From eca958be3d866591ccac7400362879b5850c4f93 Mon Sep 17 00:00:00 2001 From: Jean-Marc Valin Date: Thu, 17 Sep 2026 23:47:52 +0000 Subject: [PATCH] Extend packet size coding up to 2105535 bytes Replaces the Opus 1/2-byte frame length code (max 1275 bytes) with a 1/2/3-byte code that reaches OAC_SIZE_MAX = 2105535 bytes per frame. That's enough for a 20 ms, 256-channel, 96 kHz lossless frame that inflates instead of compressing (~34 bits per 96 kHz sample): first byte bytes size 0..191 1 p[0] (0..191) 192..223 2 32*p[1] + p[0] (192..8383) 224..255 3 32*(256*p[2] + p[1]) + p[0] + 8160 (8384..2105535) The code is bijective, so there is no redundancy. Main changes: - OAC_SIZE_MAX and OAC_MAX_FRAMES_PER_PACKET are now public, in include/oac_defines.h. oaci_encode_size()/oaci_parse_size() live out-of-line in src/oac.c; only the trivial oaci_size_bytes() is a static inline in src/oac_private.h. - All frame size arrays widen from oac_int16 to oac_int32, and every "1 + (len >= 252)" header-size computation becomes oaci_size_bytes(). - New oaci_max_frame_bytes(frame_size, Fs, channels) is the single source of truth for "largest frame this configuration can produce", and is used to size the encoder scratch buffers, the multistream scratch buffer and the demo defaults, so we do not allocate multi-MB buffers for configurations that cannot use them. - OAC_BITRATE_MAX now scales with sampling rate, frame size and channel count via oaci_max_frame_bytes(), instead of being a fixed ceiling. - OAC_MAX_BITRATE is defined once, internally, in celt/celt.h. oaci_bits_to_bitrate() saturates at it and oaci_bitrate_to_bits() uses a 64-bit intermediate. - The CELT VBR 510 kb/s ceiling is dropped; the only remaining cap is CELT_MAX_BITRATE_PER_CHANNEL * channels. - SILK keeps an explicit SILK_MAX_BYTES = 1275 cap, since the SILK bitstream format itself is unchanged. - The range coder is initialised with max_data_bytes - 1 (the TOC byte is not part of the range-coded payload) and is shrunk whenever the CELT packet size cap kicks in. New tests in tests/test_oac_api.c, all using an independent reference encoder so the test never links library internals: - test_frame_length_code(): exhaustive encode/parse round trip over all 2105536 representable sizes, tier-boundary encoder checks, and a repacketizer round trip across tiers. - test_encoder_buffer_independence(): the encoder output must not depend on the size of the caller's output buffer, across steady state, a bitrate sweep with FEC off (which is what actually reaches the SILK/hybrid/CELT transitions) and the same sweep with FEC on. --- celt/bands.c | 4 +- celt/celt.h | 13 +- celt/celt_decoder.c | 5 +- celt/celt_encoder.c | 18 +- celt/oac_custom_demo.c | 8 +- doc/trivial_example.c | 3 +- include/oac.h | 29 +- include/oac_defines.h | 19 +- silk/define.h | 4 + silk/fixed/encode_frame_FIX.c | 6 +- silk/float/encode_frame_FLP.c | 6 +- src/extensions.c | 8 +- src/oac.c | 67 ++-- src/oac_decoder.c | 10 +- src/oac_demo.c | 12 +- src/oac_encoder.c | 67 ++-- src/oac_multistream_decoder.c | 2 +- src/oac_multistream_encoder.c | 18 +- src/oac_private.h | 33 +- src/repacketizer.c | 14 +- src/repacketizer_demo.c | 14 +- tests/oac_decode_fuzzer.c | 5 +- tests/test_oac_api.c | 561 ++++++++++++++++++++++++++++++---- tests/test_oac_decode.c | 2 +- tests/test_oac_encode.c | 12 +- tests/test_oac_extensions.c | 6 +- 26 files changed, 751 insertions(+), 195 deletions(-) diff --git a/celt/bands.c b/celt/bands.c index 7b41c327..5305741e 100644 --- a/celt/bands.c +++ b/celt/bands.c @@ -1475,7 +1475,9 @@ static void quant_all_bands_twoch(int encode, const CELTMode *m, int start, int ctx.disable_inv = disable_inv; ctx.resynth = resynth; ctx.theta_round = 0; - ALLOC(bytes_save, theta_rdo ? 1275 : ALLOC_NONE, unsigned char); + /* The theta RDO path saves everything between ec->offs and ec->storage. + ec->offs only grows from here, so this is an upper bound. */ + ALLOC(bytes_save, theta_rdo ? (int)(ec->storage - ec->offs) : ALLOC_NONE, unsigned char); /* Avoid injecting noise in the first band on transients. */ ctx.avoid_split_noise = B > 1; diff --git a/celt/celt.h b/celt/celt.h index 1216ae47..63b1e7b6 100644 --- a/celt/celt.h +++ b/celt/celt.h @@ -173,13 +173,22 @@ typedef struct { #define CELT_SET_SILK_INFO_REQUEST 10028 #define CELT_SET_SILK_INFO(x) CELT_SET_SILK_INFO_REQUEST, celt_check_silkinfo_ptr(x) +/* Highest bit-rate the frame length signalling can represent: a full + OAC_SIZE_MAX payload every 20 ms. Only used to saturate the bits<->bit-rate + conversions below, so they cannot overflow oac_int32. */ +#define OAC_MAX_BITRATE ((oac_int32)OAC_SIZE_MAX*8*50) + +/* Maximum bitrate per channel the CELT layer will ever emit. Lossless coding is + handled in the OAC encoder layer and is deliberately NOT bound by this. */ +#define CELT_MAX_BITRATE_PER_CHANNEL 750000 static OAC_INLINE oac_int32 oaci_bits_to_bitrate(oac_int32 bits, oac_int32 Fs, oac_int32 frame_size) { - return bits*(6*Fs/frame_size)/6; + oac_int64 rate = (oac_int64)bits * (6 * Fs / frame_size) / 6; + return (oac_int32)IMIN(rate, OAC_MAX_BITRATE); } static OAC_INLINE oac_int32 oaci_bitrate_to_bits(oac_int32 bitrate, oac_int32 Fs, oac_int32 frame_size) { - return bitrate*6/(6*Fs/frame_size); + return (oac_int32)((oac_int64)bitrate * 6 / (6 * Fs / frame_size)); } /* Encoder stuff */ diff --git a/celt/celt_decoder.c b/celt/celt_decoder.c index 9c599e11..50b662e0 100644 --- a/celt/celt_decoder.c +++ b/celt/celt_decoder.c @@ -1191,7 +1191,10 @@ int oaci_celt_decode_with_ec_dred(CELTDecoder * OAC_RESTRICT st, const unsigned } M = 1< (st->format == OAC_FORMAT_STANDARD ? 1275 : 1275*OAC_MAX_CHANNELS) || pcm == NULL) + /* Accept any explicitly representable payload size (up to OAC_SIZE_MAX). + If a stricter per-channel or mode-specific upper bound is desired in + the future, enforce it here using st->channels and frame_size. */ + if (len < 0 || len > OAC_SIZE_MAX || pcm == NULL) return OAC_BAD_ARG; N = M*mode->shortMdctSize; diff --git a/celt/celt_encoder.c b/celt/celt_encoder.c index 43f5ca97..9ab9c833 100644 --- a/celt/celt_encoder.c +++ b/celt/celt_encoder.c @@ -1763,7 +1763,7 @@ int oaci_celt_encode_with_ec(CELTEncoder * OAC_RESTRICT st, const oac_res * pcm, oac_val16 tone_freq = -1; oac_val32 toneishness = 0; VARDECL(celt_glog, surround_dynalloc); - int packet_size_cap = (st->format == OAC_FORMAT_STANDARD) ? 1275 : 1275*OAC_MAX_AMBISONICS_CHANNELS; + oac_int32 packet_size_cap; int qext_scale = 1; ALLOC_STACK; @@ -1836,8 +1836,17 @@ int oaci_celt_encode_with_ec(CELTEncoder * OAC_RESTRICT st, const oac_res * pcm, celt_assert(st->signalling == 0); #endif - /* Can't produce more than 1275 output bytes for the main payload. */ + /* The CELT layer never emits more than CELT_MAX_BITRATE_PER_CHANNEL per + channel. This has to be a byte-level clamp rather than just the ctl + bit-rate clamp, because the OAC_BITRATE_MAX sentinel bypasses the latter + by design. frame_size is already in mode->Fs units here. */ + packet_size_cap = oaci_bitrate_to_bits(CELT_MAX_BITRATE_PER_CHANNEL*(oac_int32)CC, + mode->Fs, frame_size)/8; nbCompressedBytes = IMIN(nbCompressedBytes, packet_size_cap); + /* Keep the range coder's idea of the buffer in sync, otherwise the raw bits + written from the end would land outside the returned packet. */ + if (enc != NULL && enc->storage > (oac_uint32)nbCompressedBytes) + oaci_ec_enc_shrink(enc, nbCompressedBytes); if (st->vbr && st->bitrate != OAC_BITRATE_MAX) { vbr_rate = oaci_bitrate_to_bits(st->bitrate, mode->Fs, frame_size)<maxLM - LM; - /* Don't attempt to use more than 510 kb/s, even for frames smaller than 20 ms. - The CELT allocator will just not be able to use more than that anyway. */ - nbCompressedBytes = IMIN(nbCompressedBytes, packet_size_cap>>(3 - LM)); if (!hybrid) { base_target = vbr_rate - ((40*C + 20)<channels); + value = IMIN(value, CELT_MAX_BITRATE_PER_CHANNEL*st->channels); st->bitrate = value; } break; diff --git a/celt/oac_custom_demo.c b/celt/oac_custom_demo.c index 083576cf..db4a9e9f 100644 --- a/celt/oac_custom_demo.c +++ b/celt/oac_custom_demo.c @@ -71,7 +71,9 @@ #include #include -#define MAX_PACKET 1275 +/* Largest payload the frame length signalling can represent. The buffer is + malloc'd rather than on the stack because of its size. */ +#define MAX_PACKET OAC_SIZE_MAX static OAC_INLINE void _oac_ctl_failed(const char *file, int line) { fprintf(stderr, "\n ***************************************************\n"); @@ -145,7 +147,7 @@ int main(int argc, char *argv[]) { oac_int32 frame_size, channels, rate; int format = FORMAT_S16_LE; int bytes_per_packet = 0; - unsigned char data[MAX_PACKET]; + unsigned char *data = NULL; int complexity = -1; float percent_loss = -1; int i; @@ -292,6 +294,7 @@ int main(int argc, char *argv[]) { in = (oac_int32*)malloc(frame_size*channels*sizeof(oac_int32)); out = (oac_int32*)malloc(frame_size*channels*sizeof(oac_int32)); fbytes = (unsigned char*)malloc(frame_size*channels*4); + data = (unsigned char*)malloc(MAX_PACKET); while (!feof(fin)) { int lost = 0; @@ -488,5 +491,6 @@ int main(int argc, char *argv[]) { if (in) free(in); if (out) free(out); if (fbytes) free(fbytes); + if (data) free(data); return ret; } diff --git a/doc/trivial_example.c b/doc/trivial_example.c index e8095e6b..3b7ffa35 100644 --- a/doc/trivial_example.c +++ b/doc/trivial_example.c @@ -76,7 +76,8 @@ #define BITRATE 64000 #define MAX_FRAME_SIZE 6*960 -#define MAX_PACKET_SIZE (3*1276) +/* Enough for a 20 ms stereo frame at any bitrate the encoder will produce. */ +#define MAX_PACKET_SIZE 4000 int main(int argc, char **argv) { char *inFile; diff --git a/include/oac.h b/include/oac.h index 06174e0d..fd1013c9 100644 --- a/include/oac.h +++ b/include/oac.h @@ -86,7 +86,7 @@ extern "C" { * stereo music. Its main features are: * @li Sampling rates from 8 to 48 kHz - * @li Bit-rates from 6 kb/s to 510 kb/s + * @li Bit-rates from 6 kb/s to 750 kb/s per channel (and higher for lossless) * @li Support for both constant bit-rate (CBR) and variable bit-rate (VBR) * @li Audio bandwidth from narrowband to full-band * @li Support for speech and music @@ -752,8 +752,8 @@ OAC_EXPORT int oac_decoder_dred_decode_float(OacDecoder *st, const OacDRED *dred * @param [in] data char*: Oac packet to be parsed * @param [in] len oac_int32: size of data * @param [out] out_toc char*: TOC pointer - * @param [out] frames char*[48] encapsulated frames - * @param [out] size oac_int16[48] sizes of the encapsulated frames + * @param [out] frames char*[OAC_MAX_FRAMES_PER_PACKET] encapsulated frames + * @param [out] size oac_int32[OAC_MAX_FRAMES_PER_PACKET] sizes of the encapsulated frames * @param [out] payload_offset int*: returns the position of the payload within the packet (in bytes) * @param [in] format int: Audio format (OAC_FORMAT_STANDARD or OAC_FORMAT_AMBISONICS) * @returns number of frames @@ -762,8 +762,8 @@ OAC_EXPORT int oac_packet_parse( const unsigned char *data, oac_int32 len, unsigned char *out_toc, - const unsigned char *frames[48], - oac_int16 size[48], + const unsigned char *frames[OAC_MAX_FRAMES_PER_PACKET], + oac_int32 size[OAC_MAX_FRAMES_PER_PACKET], int *payload_offset, int format) OAC_ARG_NONNULL(1) OAC_ARG_NONNULL(5); @@ -891,7 +891,7 @@ OAC_EXPORT void oac_pcm_soft_clip(float *pcm, int frame_size, int channels, floa * int len; * while (get_next_packet(&data, &len)) * { - * unsigned char out[1276]; + * unsigned char out[OAC_SIZE_MAX + 1]; * oac_int32 out_len; * int nb_frames; * int err; @@ -927,7 +927,10 @@ OAC_EXPORT void oac_pcm_soft_clip(float *pcm, int frame_size, int channels, floa * unsigned char *data[(TARGET_DURATION_MS*2/5)+1]; * oac_int32 len[(TARGET_DURATION_MS*2/5)+1]; * int nb_packets; - * unsigned char out[1277*(TARGET_DURATION_MS*2/2)]; + * // Worst case output size: every frame may need a three-byte length field, + * // plus the TOC and frame count bytes. MAX_FRAME_BYTES is the largest frame + * // the producer emits; frames may be up to OAC_SIZE_MAX bytes long. + * unsigned char out[(MAX_FRAME_BYTES+3)*(TARGET_DURATION_MS*2/5)+2]; * oac_int32 out_len; * int prev_toc; * nb_packets = 0; @@ -1101,10 +1104,10 @@ OAC_EXPORT int oac_repacketizer_cat(OacRepacketizer *rp, const unsigned char *da * @param maxlen oac_int32: The maximum number of bytes to store in * the output buffer. In order to guarantee * success, this should be at least - * 1276 for a single frame, - * or for multiple frames, - * 1277*(end-begin). - * However, 1*(end-begin) plus + * OAC_SIZE_MAX+1 for a single + * frame, or for multiple frames, + * (OAC_SIZE_MAX+4)*(end-begin). + * However, 3*(end-begin) plus * the size of all packet data submitted to * the repacketizer since the last call to * oac_repacketizer_init() or @@ -1149,9 +1152,9 @@ OAC_EXPORT OAC_WARN_UNUSED_RESULT int oac_repacketizer_get_nb_frames(OacRepacket * @param maxlen oac_int32: The maximum number of bytes to store in * the output buffer. In order to guarantee * success, this should be at least - * 1277*oac_repacketizer_get_nb_frames(rp). + * (OAC_SIZE_MAX+4)*oac_repacketizer_get_nb_frames(rp). * However, - * 1*oac_repacketizer_get_nb_frames(rp) + * 3*oac_repacketizer_get_nb_frames(rp) * plus the size of all packet data * submitted to the repacketizer since the * last call to oac_repacketizer_init() or diff --git a/include/oac_defines.h b/include/oac_defines.h index a9a26d1f..81df2f28 100644 --- a/include/oac_defines.h +++ b/include/oac_defines.h @@ -263,6 +263,23 @@ extern "C" { #define OAC_FORMAT_STANDARD 0 /** Ambisonics format supporting orders 0 to OAC_MAX_AMBISONICS_ORDER @hideinitializer */ #define OAC_FORMAT_AMBISONICS 1 + +/** Maximum number of frames that can be packed in a single OAC packet + * (48 x 2.5 ms = 120 ms). @hideinitializer */ +#define OAC_MAX_FRAMES_PER_PACKET 48 + +/** Largest frame size (in bytes) that the frame length signalling can represent. + * + * Frame lengths are coded on 1 to 3 bytes: + * - @c p[0] in [0,191]: 1 byte, size = @c p[0] (0 - 191) + * - @c p[0] in [192,223]: 2 bytes, size = @c 32*p[1]+p[0] (192 - 8383) + * - @c p[0] in [224,255]: 3 bytes, size = @c 32*(256*p[2]+p[1])+p[0]+8160 (8384 - 2105535) + * + * The mapping is bijective: every size has exactly one representation, so there + * are no non-canonical encodings for a parser to reject. + * @hideinitializer */ +#define OAC_SIZE_MAX 2105535 + #define OAC_SIGNAL_VOICE 3001 /**< Signal being encoded is voice */ #define OAC_SIGNAL_MUSIC 3002 /**< Signal being encoded is music */ #define OAC_BANDWIDTH_NARROWBAND 1101 /**< 4 kHz bandpass @hideinitializer*/ @@ -324,7 +341,7 @@ extern "C" { #define OAC_GET_COMPLEXITY(x) OAC_GET_COMPLEXITY_REQUEST, oac_check_int_ptr(x) /** Configures the bitrate in the encoder. - * Rates from 500 to 512000 bits per second are meaningful, as well as the + * Rates from 500 to 750000 bits per second per channel for lossy coding (and higher for lossless) are meaningful, as well as the * special values #OAC_AUTO and #OAC_BITRATE_MAX. * The value #OAC_BITRATE_MAX can be used to cause the codec to use as much * rate as it can, which is useful for controlling the rate by adjusting the diff --git a/silk/define.h b/silk/define.h index 766ac2ad..900b01fa 100644 --- a/silk/define.h +++ b/silk/define.h @@ -75,6 +75,10 @@ /* Limits on bitrate */ #define MIN_TARGET_RATE_BPS 5000 #define MAX_TARGET_RATE_BPS 80000 +/* Largest payload SILK will ever be asked to produce for one frame. SILK cannot + make use of a higher rate, so its bit budget is clamped to this even when the + packet itself may be much larger. */ +#define SILK_MAX_BYTES 1275 /* LBRR thresholds */ #define LBRR_NB_MIN_RATE_BPS 12000 diff --git a/silk/fixed/encode_frame_FIX.c b/silk/fixed/encode_frame_FIX.c index 3ae3b40d..21e6dad9 100644 --- a/silk/fixed/encode_frame_FIX.c +++ b/silk/fixed/encode_frame_FIX.c @@ -217,7 +217,7 @@ oac_int oaci_silk_encode_frame_FIX( seed_copy = psEnc->sCmn.indices.Seed; ec_prevLagIndex_copy = psEnc->sCmn.ec_prevLagIndex; ec_prevSignalType_copy = psEnc->sCmn.ec_prevSignalType; - ALLOC( ec_buf_copy, 1275, oac_uint8 ); + ALLOC( ec_buf_copy, SILK_MAX_BYTES, oac_uint8 ); for (iter = 0; ; iter++) { if (gainsID == gainsID_lower) { nBits = nBits_lower; @@ -306,7 +306,7 @@ oac_int oaci_silk_encode_frame_FIX( if (found_lower && (gainsID == gainsID_lower || nBits > maxBits)) { /* Restore output state from earlier iteration that did meet the bitrate budget */ silk_memcpy( psRangeEnc, &sRangeEnc_copy2, sizeof(ec_enc)); - celt_assert( sRangeEnc_copy2.offs <= 1275 ); + celt_assert( sRangeEnc_copy2.offs <= SILK_MAX_BYTES ); silk_memcpy( psRangeEnc->buf, ec_buf_copy, sRangeEnc_copy2.offs ); silk_memcpy( &psEnc->sCmn.sNSQ, &sNSQ_copy[1], sizeof(silk_nsq_state)); psEnc->sShape.LastGainIndex = LastGainIndex_copy2; @@ -334,7 +334,7 @@ oac_int oaci_silk_encode_frame_FIX( gainsID_lower = gainsID; /* Copy part of the output state */ silk_memcpy( &sRangeEnc_copy2, psRangeEnc, sizeof(ec_enc)); - celt_assert( psRangeEnc->offs <= 1275 ); + celt_assert( psRangeEnc->offs <= SILK_MAX_BYTES ); silk_memcpy( ec_buf_copy, psRangeEnc->buf, psRangeEnc->offs ); silk_memcpy( &sNSQ_copy[1], &psEnc->sCmn.sNSQ, sizeof(silk_nsq_state)); LastGainIndex_copy2 = psEnc->sShape.LastGainIndex; diff --git a/silk/float/encode_frame_FLP.c b/silk/float/encode_frame_FLP.c index 3c3297a1..b08b62eb 100644 --- a/silk/float/encode_frame_FLP.c +++ b/silk/float/encode_frame_FLP.c @@ -216,7 +216,7 @@ oac_int oaci_silk_encode_frame_FLP( seed_copy = psEnc->sCmn.indices.Seed; ec_prevLagIndex_copy = psEnc->sCmn.ec_prevLagIndex; ec_prevSignalType_copy = psEnc->sCmn.ec_prevSignalType; - ALLOC( ec_buf_copy, 1275, oac_uint8 ); + ALLOC( ec_buf_copy, SILK_MAX_BYTES, oac_uint8 ); for (iter = 0; ; iter++) { if (gainsID == gainsID_lower) { nBits = nBits_lower; @@ -291,7 +291,7 @@ oac_int oaci_silk_encode_frame_FLP( if (found_lower && (gainsID == gainsID_lower || nBits > maxBits)) { /* Restore output state from earlier iteration that did meet the bitrate budget */ silk_memcpy( psRangeEnc, &sRangeEnc_copy2, sizeof(ec_enc)); - celt_assert( sRangeEnc_copy2.offs <= 1275 ); + celt_assert( sRangeEnc_copy2.offs <= SILK_MAX_BYTES ); silk_memcpy( psRangeEnc->buf, ec_buf_copy, sRangeEnc_copy2.offs ); silk_memcpy( &psEnc->sCmn.sNSQ, &sNSQ_copy[1], sizeof(silk_nsq_state)); psEnc->sShape.LastGainIndex = LastGainIndex_copy2; @@ -321,7 +321,7 @@ oac_int oaci_silk_encode_frame_FLP( gainsID_lower = gainsID; /* Copy part of the output state */ silk_memcpy( &sRangeEnc_copy2, psRangeEnc, sizeof(ec_enc)); - celt_assert( psRangeEnc->offs <= 1275 ); + celt_assert( psRangeEnc->offs <= SILK_MAX_BYTES ); silk_memcpy( ec_buf_copy, psRangeEnc->buf, psRangeEnc->offs ); silk_memcpy( &sNSQ_copy[1], &psEnc->sCmn.sNSQ, sizeof(silk_nsq_state)); LastGainIndex_copy2 = psEnc->sShape.LastGainIndex; diff --git a/src/extensions.c b/src/extensions.c index 2de41df2..78238875 100644 --- a/src/extensions.c +++ b/src/extensions.c @@ -493,9 +493,9 @@ static int oaci_write_extension(unsigned char *data, oac_int32 len, oac_int32 po oac_int32 oac_packet_extensions_generate(unsigned char *data, oac_int32 len, const oac_extension_data *extensions, oac_int32 nb_extensions, int nb_frames, int pad) { - oac_int32 frame_min_idx[48]; - oac_int32 frame_max_idx[48]; - oac_int32 frame_repeat_idx[48]; + oac_int32 frame_min_idx[OAC_MAX_FRAMES_PER_PACKET]; + oac_int32 frame_max_idx[OAC_MAX_FRAMES_PER_PACKET]; + oac_int32 frame_repeat_idx[OAC_MAX_FRAMES_PER_PACKET]; oac_int32 i; int f; int curr_frame = 0; @@ -503,7 +503,7 @@ oac_int32 oac_packet_extensions_generate(unsigned char *data, oac_int32 len, oac_int32 written = 0; celt_assert(len >= 0); - if (nb_frames > 48) return OAC_BAD_ARG; + if (nb_frames > OAC_MAX_FRAMES_PER_PACKET) return OAC_BAD_ARG; /* Do a little work up-front to make this O(nb_extensions) instead of O(nb_extensions*nb_frames) so long as the extensions are in frame diff --git a/src/oac.c b/src/oac.c index aff2aa70..4b2458d7 100644 --- a/src/oac.c +++ b/src/oac.c @@ -189,30 +189,49 @@ OAC_EXPORT void oac_pcm_soft_clip(float *_x, int N, int C, float *declip_mem) { #endif -int oaci_encode_size(int size, unsigned char *data) { - if (size < 252) { +/* Frame length signalling. See OAC_SIZE_MAX in oac_defines.h for the format. + The mapping is bijective, so there is no non-canonical encoding to reject. */ +int oaci_encode_size(oac_int32 size, unsigned char *data) { + celt_assert(size >= 0 && size <= OAC_SIZE_MAX); + if (size < 192) { data[0] = size; return 1; - } else { - data[0] = 252 + (size&0x3); - data[1] = (size - (int)data[0])>>2; + } else if (size < 8384) { + oac_int32 v = size - 192; /* 13 bits */ + data[0] = 192 + (v&0x1F); + data[1] = v>>5; return 2; + } else { + oac_int32 v = size - 8384; /* 21 bits */ + data[0] = 224 + (v&0x1F); + v >>= 5; + data[1] = v&0xFF; + data[2] = v>>8; + return 3; } } -static int oaci_parse_size(const unsigned char *data, oac_int32 len, oac_int16 *size) { +static int oaci_parse_size(const unsigned char *data, oac_int32 len, oac_int32 *size) { if (len < 1) { *size = -1; return -1; - } else if (data[0] < 252) { + } else if (data[0] < 192) { *size = data[0]; return 1; - } else if (len < 2) { - *size = -1; - return -1; - } else { - *size = 4*data[1] + data[0]; + } else if (data[0] < 224) { + if (len < 2) { + *size = -1; + return -1; + } + *size = 32*(oac_int32)data[1] + data[0]; return 2; + } else { + if (len < 3) { + *size = -1; + return -1; + } + *size = 32*(256*(oac_int32)data[2] + data[1]) + data[0] + 8160; + return 3; } } @@ -236,7 +255,7 @@ int oac_packet_get_samples_per_frame(const unsigned char *data, int oac_packet_parse_impl(const unsigned char *data, oac_int32 len, int self_delimited, unsigned char *out_toc, - const unsigned char *frames[48], oac_int16 size[48], + const unsigned char *frames[OAC_MAX_FRAMES_PER_PACKET], oac_int32 size[OAC_MAX_FRAMES_PER_PACKET], int *payload_offset, oac_int32 *packet_offset, const unsigned char **padding, oac_int32 *padding_len, int format) { @@ -249,6 +268,10 @@ int oac_packet_parse_impl(const unsigned char *data, oac_int32 len, oac_int32 pad = 0; const unsigned char *data0 = data; + /* The frame length codec no longer depends on the format: any length the + codec can represent is accepted. */ + (void)format; + /* Make sure we return NULL/0 on error. */ if (padding != NULL) { *padding = NULL; @@ -279,8 +302,7 @@ int oac_packet_parse_impl(const unsigned char *data, oac_int32 len, if (len&0x1) return OAC_INVALID_PACKET; last_size = len/2; - /* If last_size doesn't fit in size[0], we'll catch it later */ - size[0] = (oac_int16)last_size; + size[0] = last_size; } break; /* Two VBR frames */ @@ -340,7 +362,7 @@ int oac_packet_parse_impl(const unsigned char *data, oac_int32 len, if (last_size*count != len) return OAC_INVALID_PACKET; for (i = 0; i < count - 1; i++) - size[i] = (oac_int16)last_size; + size[i] = last_size; } break; } @@ -361,11 +383,12 @@ int oac_packet_parse_impl(const unsigned char *data, oac_int32 len, return OAC_INVALID_PACKET; } else { /* Because it's not encoded explicitly, it's possible the size of the - last packet (or all the packets, for the CBR case) is larger than - 1275. Reject them here.*/ - if (last_size > (format == OAC_FORMAT_AMBISONICS ? 1275*OAC_MAX_CHANNELS : 1275)) + last frame (or all the frames, for the CBR case) is not representable + by the frame length codec. Reject them here, so that an implicit + length is always explicitly representable. */ + if (last_size > OAC_SIZE_MAX) return OAC_INVALID_PACKET; - size[count - 1] = (oac_int16)last_size; + size[count - 1] = last_size; } if (payload_offset) @@ -391,8 +414,8 @@ int oac_packet_parse_impl(const unsigned char *data, oac_int32 len, } int oac_packet_parse(const unsigned char *data, oac_int32 len, - unsigned char *out_toc, const unsigned char *frames[48], - oac_int16 size[48], int *payload_offset, int format) { + unsigned char *out_toc, const unsigned char *frames[OAC_MAX_FRAMES_PER_PACKET], + oac_int32 size[OAC_MAX_FRAMES_PER_PACKET], int *payload_offset, int format) { return oac_packet_parse_impl(data, len, 0, out_toc, frames, size, payload_offset, NULL, NULL, NULL, format); diff --git a/src/oac_decoder.c b/src/oac_decoder.c index bf18f274..847b4e33 100644 --- a/src/oac_decoder.c +++ b/src/oac_decoder.c @@ -720,7 +720,7 @@ int oac_decode_native(OacDecoder *st, const unsigned char *data, unsigned char toc; int packet_frame_size, packet_bandwidth, packet_mode, packet_stream_channels; /* 48 x 2.5 ms = 120 ms */ - oac_int16 size[48]; + oac_int32 size[OAC_MAX_FRAMES_PER_PACKET]; const unsigned char *padding; oac_int32 padding_len; OacExtensionIterator iter; @@ -1248,8 +1248,8 @@ int oac_packet_get_nb_samples(const unsigned char packet[], oac_int32 len, int oac_packet_has_lbrr(const unsigned char packet[], oac_int32 len) { int ret; - const unsigned char *frames[48]; - oac_int16 size[48]; + const unsigned char *frames[OAC_MAX_FRAMES_PER_PACKET]; + oac_int32 size[OAC_MAX_FRAMES_PER_PACKET]; int packet_mode, packet_frame_size, packet_stream_channels; int nb_frames = 1; int lbrr; @@ -1400,8 +1400,8 @@ static int oaci_dred_find_payload(const unsigned char *data, oac_int32 len, cons const unsigned char *padding; oac_int32 padding_len; int nb_frames; - const unsigned char *frames[48]; - oac_int16 size[48]; + const unsigned char *frames[OAC_MAX_FRAMES_PER_PACKET]; + oac_int32 size[OAC_MAX_FRAMES_PER_PACKET]; int frame_size; int ret; diff --git a/src/oac_demo.c b/src/oac_demo.c index ae1893cf..a185dd0b 100644 --- a/src/oac_demo.c +++ b/src/oac_demo.c @@ -76,7 +76,7 @@ # include "lossgen.h" #endif -#define MAX_PACKET (1276*OAC_MAX_AMBISONICS_CHANNELS) +#define MAX_PACKET ((OAC_SIZE_MAX + 3)*6) #ifdef ENABLE_QEXT # define MAX_SAMPLING_RATE 96000 @@ -582,7 +582,7 @@ int main(int argc, char *argv[]) { /* defaults: */ use_vbr = 1; - max_payload_bytes = MAX_PACKET; + max_payload_bytes = -1; complexity = 10; dec_complexity = 0; use_inbandfec = 0; @@ -816,6 +816,12 @@ int main(int argc, char *argv[]) { if (sweep_max) sweep_min = bitrate_bps; + if (max_payload_bytes < 0) { + if (decode_only) + max_payload_bytes = MAX_PACKET; + else + max_payload_bytes = IMIN(MAX_PACKET, oaci_max_frame_bytes(sampling_rate/50, sampling_rate, channels) * 6 + 20); + } if (max_payload_bytes < 0 || max_payload_bytes > MAX_PACKET) { fprintf (stderr, "max_payload_bytes must be between 0 and %d\n", MAX_PACKET); @@ -1083,7 +1089,7 @@ int main(int argc, char *argv[]) { } #if 0 /* This is for testing the padding code, do not enable by default */ - if (len < 1275) { + if (len < max_payload_bytes) { int new_len = len + rand()%(max_payload_bytes - len); if ((err = oac_packet_pad(data, len, new_len)) != OAC_OK) { fprintf(stderr, "padding failed: %s\n", oac_strerror(err)); diff --git a/src/oac_encoder.c b/src/oac_encoder.c index c229346d..6f11ef27 100644 --- a/src/oac_encoder.c +++ b/src/oac_encoder.c @@ -756,9 +756,13 @@ static oac_int32 oaci_user_bitrate_to_bitrate(OacEncoder *st, int frame_size, in max_bitrate = oaci_bits_to_bitrate(max_data_bytes*8, st->Fs, frame_size); if (st->user_bitrate_bps == OAC_AUTO) user_bitrate = 60*st->Fs/frame_size + st->Fs*st->channels; - else if (st->user_bitrate_bps == OAC_BITRATE_MAX) - user_bitrate = 1500000; - else + else if (st->user_bitrate_bps == OAC_BITRATE_MAX) { + /* As much rate as this configuration could ever need, i.e. the lossless + bound. This scales with sampling rate, frame size and channel count, + and only reaches OAC_SIZE_MAX for a 20 ms, 256-channel, 96 kHz frame. */ + user_bitrate = oaci_bits_to_bitrate( + oaci_max_frame_bytes(frame_size, st->Fs, st->channels)*8, st->Fs, frame_size); + } else user_bitrate = st->user_bitrate_bps; return IMIN(user_bitrate, max_bitrate); } @@ -1139,9 +1143,13 @@ static int oaci_compute_redundancy_bytes(oac_int32 max_data_bytes, oac_int32 bit redundancy_rate = 3*redundancy_rate/2; redundancy_bytes = redundancy_rate/1600; - /* Compute the max rate we can use given CBR or VBR with cap. */ + /* Compute the max rate we can use given CBR or VBR with cap. + available_bits can now be as large as 8*(OAC_SIZE_MAX+1), so the *240 + needs a 64-bit intermediate. The result is a byte count that is then + clamped to 257, so the truncation to int is safe. */ available_bits = max_data_bytes*8 - 2*base_bits; - redundancy_bytes_cap = (available_bits*240/(240 + 48000/frame_rate) + base_bits)/8; + redundancy_bytes_cap = (int)IMIN((oac_int64)257, + ((oac_int64)available_bits*240/(240 + 48000/frame_rate) + base_bits)/8); redundancy_bytes = IMIN(redundancy_bytes, redundancy_bytes_cap); /* It we can't get enough bits for redundancy to be worth it, rely on the decoder PLC. */ if (redundancy_bytes > 4 + 8*channels) @@ -1184,7 +1192,6 @@ oac_int32 oac_encode_native(OacEncoder *st, const oac_res *pcm, int frame_size, oac_int32 cbr_bytes = -1; oac_val16 stereo_width; const CELTMode *celt_mode = NULL; - int packet_size_cap = (st->format == OAC_FORMAT_STANDARD) ? 1276 : 1276*OAC_MAX_CHANNELS; #ifndef DISABLE_FLOAT_API AnalysisInfo analysis_info; int analysis_read_pos_bak = -1; @@ -1197,7 +1204,7 @@ oac_int32 oac_encode_native(OacEncoder *st, const oac_res *pcm, int frame_size, ALLOC_STACK; /* Just avoid insane packet sizes here, but the real bounds are applied later on. */ - max_data_bytes = IMIN(packet_size_cap*6, out_data_bytes); + max_data_bytes = IMIN(OAC_SIZE_MAX*6, out_data_bytes); st->rangeFinal = 0; if (frame_size <= 0 || max_data_bytes <= 0) { @@ -1657,8 +1664,10 @@ oac_int32 oac_encode_native(OacEncoder *st, const oac_res *pcm, int frame_size, VARDECL(unsigned char, tmp_data); VARDECL(OacRepacketizer, rp); int max_header_bytes; + int size_bytes; oac_int32 repacketize_len; oac_int32 max_len_sum; + oac_int32 tmp_data_size; oac_int32 tot_size = 0; unsigned char *curr_data; int tmp_len; @@ -1686,20 +1695,36 @@ oac_int32 oac_encode_native(OacEncoder *st, const oac_res *pcm, int frame_size, } #endif - /* Worst cases: - * 2 frames: Code 2 with different compressed sizes - * >2 frames: Code 3 VBR */ - max_header_bytes = nb_frames == 2 ? 3 : (2 + (nb_frames - 1)*2); - if (st->use_vbr || st->user_bitrate_bps == OAC_BITRATE_MAX) repacketize_len = out_data_bytes; else { celt_assert(cbr_bytes >= 0); repacketize_len = IMIN(cbr_bytes, out_data_bytes); } + { + oac_int32 max_frame_bytes = oaci_max_frame_bytes(enc_frame_size, st->Fs, st->channels); + repacketize_len = IMIN(repacketize_len, nb_frames * max_frame_bytes + 2 + (nb_frames - 1)*3); + + /* Worst cases: + * 2 frames: Code 2 with different compressed sizes + * >2 frames: Code 3 VBR */ + size_bytes = oaci_size_bytes(IMIN(repacketize_len, max_frame_bytes)); + max_header_bytes = nb_frames == 2 ? (1 + size_bytes) : (2 + (nb_frames - 1)*size_bytes); + repacketize_len = IMIN(repacketize_len, nb_frames * max_frame_bytes + max_header_bytes); + } max_len_sum = nb_frames + repacketize_len - max_header_bytes; - ALLOC(tmp_data, max_len_sum, unsigned char); + /* The scratch buffer only ever holds what we actually ask the frame + encoder for, and that is curr_max below, not max_len_sum. Sizing it + from max_len_sum would put a multi-megabyte VLA on the stack as soon + as the channel count is high. Keep this in sync with curr_max. */ + tmp_data_size = nb_frames*IMIN(oaci_bitrate_to_bits(st->bitrate_bps, st->Fs, enc_frame_size)/8, + max_len_sum/nb_frames); +#ifdef ENABLE_DRED + tmp_data_size += oaci_bitrate_to_bits(dred_bitrate_bps, st->Fs, frame_size)/8; +#endif + tmp_data_size = IMAX(nb_frames, IMIN(max_len_sum, tmp_data_size)); + ALLOC(tmp_data, tmp_data_size, unsigned char); curr_data = tmp_data; ALLOC(rp, 1, OacRepacketizer); oac_repacketizer_init(rp, st->format); @@ -1829,7 +1854,7 @@ static oac_int32 oac_encode_frame_native(OacEncoder *st, const oac_res *pcm, int VARDECL(oac_res, tmp_prefill); SAVE_STACK; - max_data_bytes = IMIN(orig_max_data_bytes, (st->format == OAC_FORMAT_STANDARD) ? 1276 : 1276*OAC_MAX_CHANNELS); + max_data_bytes = IMIN(orig_max_data_bytes, OAC_SIZE_MAX + 1); st->rangeFinal = 0; if (st->application != OAC_APPLICATION_RESTRICTED_CELT) silk_enc = (char*)st + st->silk_enc_offset; @@ -1898,7 +1923,7 @@ static oac_int32 oac_encode_frame_native(OacEncoder *st, const oac_res *pcm, int data += 1; - oaci_ec_enc_init(&enc, data, orig_max_data_bytes - 1); + oaci_ec_enc_init(&enc, data, max_data_bytes - 1); ALLOC(pcm_buf, (total_buffer + frame_size)*st->channels, oac_res); OAC_COPY(pcm_buf, &st->delay_buffer[(st->encoder_buffer - total_buffer)*st->channels], total_buffer*st->channels); @@ -2072,7 +2097,7 @@ static oac_int32 oac_encode_frame_native(OacEncoder *st, const oac_res *pcm, int /* Call SILK encoder for the low band */ /* Max bits for SILK, counting ToC, redundancy bytes, and optionally redundancy. */ - st->silk_mode.maxBits = (max_data_bytes - 1)*8; + st->silk_mode.maxBits = IMIN(max_data_bytes - 1, SILK_MAX_BYTES)*8; if (redundancy && redundancy_bytes >= 2) { /* Counting 1 bit for redundancy position and 20 bits for flag+size (only for hybrid). */ st->silk_mode.maxBits -= redundancy_bytes*8 + 1; @@ -2090,7 +2115,7 @@ static oac_int32 oac_encode_frame_native(OacEncoder *st, const oac_res *pcm, int #endif { /* Allow SILK to steal up to 25% of the remaining bits */ - oac_int16 other_bits = IMAX(0, st->silk_mode.maxBits - st->silk_mode.bitRate*frame_size/st->Fs); + oac_int32 other_bits = IMAX(0, st->silk_mode.maxBits - st->silk_mode.bitRate*frame_size/st->Fs); st->silk_mode.maxBits = IMAX(0, st->silk_mode.maxBits - other_bits*3/4); st->silk_mode.useCBR = 0; } @@ -2098,7 +2123,7 @@ static oac_int32 oac_encode_frame_native(OacEncoder *st, const oac_res *pcm, int /* Constrained VBR. */ if (st->mode == MODE_HYBRID) { /* Compute SILK bitrate corresponding to the max total bits available */ - oac_int32 maxBitRate = oaci_compute_silk_rate_for_hybrid(st->silk_mode.maxBits*st->Fs/frame_size, + oac_int32 maxBitRate = oaci_compute_silk_rate_for_hybrid(oaci_bits_to_bitrate(st->silk_mode.maxBits, st->Fs, frame_size), curr_bandwidth, st->Fs == 50*frame_size, st->use_vbr, st->silk_mode.LBRR_coded, st->stream_channels); st->silk_mode.maxBits = oaci_bitrate_to_bits(maxBitRate, st->Fs, frame_size); @@ -2671,8 +2696,8 @@ int oac_encoder_ctl(OacEncoder *st, int request, ...) { goto bad_arg; else if (value <= 500) value = 500; - else if (value > (oac_int32)750000*st->channels) - value = (oac_int32)750000*st->channels; + else if (value > (oac_int32)CELT_MAX_BITRATE_PER_CHANNEL*st->channels) + value = (oac_int32)CELT_MAX_BITRATE_PER_CHANNEL*st->channels; } st->user_bitrate_bps = value; } @@ -2683,7 +2708,7 @@ int oac_encoder_ctl(OacEncoder *st, int request, ...) { if (!value) { goto bad_arg; } - *value = oaci_user_bitrate_to_bitrate(st, st->prev_framesize, 1276); + *value = oaci_user_bitrate_to_bitrate(st, st->prev_framesize, OAC_SIZE_MAX + 1); } break; case OAC_SET_FORCE_CHANNELS_REQUEST: diff --git a/src/oac_multistream_decoder.c b/src/oac_multistream_decoder.c index 79989e1f..44284147 100644 --- a/src/oac_multistream_decoder.c +++ b/src/oac_multistream_decoder.c @@ -173,7 +173,7 @@ static int oac_multistream_packet_validate(const unsigned char *data, int s; int count; unsigned char toc; - oac_int16 size[48]; + oac_int32 size[OAC_MAX_FRAMES_PER_PACKET]; int samples = 0; oac_int32 packet_offset; diff --git a/src/oac_multistream_encoder.c b/src/oac_multistream_encoder.c index 0b8ebf04..81f38bc7 100644 --- a/src/oac_multistream_encoder.c +++ b/src/oac_multistream_encoder.c @@ -698,7 +698,7 @@ static void oaci_surround_rate_allocation( if (st->bitrate_bps == OAC_AUTO) { bitrate = nb_normal*(channel_offset + Fs + 10000) + 8000*nb_lfe; } else if (st->bitrate_bps == OAC_BITRATE_MAX) { - bitrate = nb_normal*750000 + nb_lfe*128000; + bitrate = nb_normal*CELT_MAX_BITRATE_PER_CHANNEL + nb_lfe*128000; } else { bitrate = st->bitrate_bps; } @@ -749,7 +749,7 @@ static void oaci_ambisonics_rate_allocation( total_rate = (st->layout.nb_coupled_streams + st->layout.nb_streams) *(Fs + 60*Fs/frame_size) + st->layout.nb_streams*(oac_int32)15000; } else if (st->bitrate_bps == OAC_BITRATE_MAX) { - total_rate = nb_channels*750000; + total_rate = nb_channels*CELT_MAX_BITRATE_PER_CHANNEL; } else { total_rate = st->bitrate_bps; } @@ -787,9 +787,6 @@ static oac_int32 oaci_rate_allocation( return rate_sum; } -/* Max size in case the encoder decides to return six frames (6 x 20 ms = 120 ms) */ -#define MS_FRAME_TMP (6*1275 + 12) - int oac_multistream_encode_native ( OacMSEncoder *st, @@ -810,7 +807,7 @@ int oac_multistream_encode_native int tot_size; VARDECL(oac_res, buf); VARDECL(celt_glog, bandSMR); - unsigned char tmp_data[MS_FRAME_TMP]; + VARDECL(unsigned char, tmp_data); OacRepacketizer rp; oac_int32 vbr; const CELTMode *celt_mode = NULL; @@ -850,6 +847,7 @@ int oac_multistream_encode_native return OAC_BUFFER_TOO_SMALL; } ALLOC(buf, 2*frame_size, oac_res); + ALLOC(tmp_data, oaci_max_frame_bytes(frame_size, Fs, st->layout.nb_coupled_streams > 0 ? 2 : 1) + 20, unsigned char); coupled_size = oac_encoder_init(NULL, st->Fs, 2, OAC_FORMAT_STANDARD, st->application); mono_size = oac_encoder_init(NULL, st->Fs, 1, OAC_FORMAT_STANDARD, st->application); @@ -954,9 +952,9 @@ int oac_multistream_encode_native /* For 100 ms, reserve an extra byte per stream for the ToC */ if (Fs/frame_size == 10) curr_max -= st->layout.nb_streams - s - 1; - curr_max = IMIN(curr_max, MS_FRAME_TMP); - /* Repacketizer will add one or two bytes for self-delimited frames */ - if (s != st->layout.nb_streams - 1) curr_max -= curr_max > 253 ? 2 : 1; + curr_max = IMIN(curr_max, oaci_max_frame_bytes(frame_size, Fs, (s < st->layout.nb_coupled_streams) ? 2 : 1) + 20); + /* Repacketizer will add one to three bytes for self-delimited frames */ + if (s != st->layout.nb_streams - 1) curr_max -= oaci_size_bytes(curr_max); if (!vbr && s == st->layout.nb_streams - 1) oac_encoder_ctl(enc, OAC_SET_BITRATE(oaci_bits_to_bitrate(curr_max*8, Fs, frame_size))); len = oac_encode_native(enc, buf, frame_size, tmp_data, curr_max, lsb_depth, @@ -1084,7 +1082,7 @@ int oac_multistream_encoder_ctl_va_list(OacMSEncoder *st, int request, if (value != OAC_AUTO && value != OAC_BITRATE_MAX) { if (value <= 0) goto bad_arg; - value = IMIN(750000*st->layout.nb_channels, IMAX(500*st->layout.nb_channels, value)); + value = IMIN(CELT_MAX_BITRATE_PER_CHANNEL*st->layout.nb_channels, IMAX(500*st->layout.nb_channels, value)); } st->bitrate_bps = value; } diff --git a/src/oac_private.h b/src/oac_private.h index 4425e0f2..5f461c90 100644 --- a/src/oac_private.h +++ b/src/oac_private.h @@ -69,16 +69,35 @@ #include /* va_list */ #include /* offsetof */ +/** Number of bytes needed to signal a frame length of @a size bytes. + * Monotonic in @a size, so reserving space from an upper bound never + * under-reserves. See OAC_SIZE_MAX for the encoding. */ +static OAC_INLINE int oaci_size_bytes(oac_int32 size) { + if (size < 192) return 1; + if (size < 8384) return 2; + return 3; +} + +/** Largest payload (in bytes) a frame of this configuration can ever need, + * assuming lossless coding at 34 bits per 96-kHz sample per channel. Used to + * size internal scratch buffers, which must not scale with whatever buffer the + * caller happens to provide. */ +static OAC_INLINE oac_int32 oaci_max_frame_bytes(oac_int32 frame_size, oac_int32 Fs, int channels) { + oac_int64 samples_96k = (oac_int64)frame_size * 96000 / Fs; + oac_int64 bytes = (samples_96k * channels * 34 + 7) / 8; + return (oac_int32)IMIN(bytes, OAC_SIZE_MAX); +} + struct OacRepacketizer { unsigned char toc; int nb_frames; - const unsigned char *frames[48]; - oac_int16 len[48]; + const unsigned char *frames[OAC_MAX_FRAMES_PER_PACKET]; + oac_int32 len[OAC_MAX_FRAMES_PER_PACKET]; int framesize; int format; - const unsigned char *paddings[48]; - oac_int32 padding_len[48]; - unsigned char padding_nb_frames[48]; + const unsigned char *paddings[OAC_MAX_FRAMES_PER_PACKET]; + oac_int32 padding_len[OAC_MAX_FRAMES_PER_PACKET]; + unsigned char padding_nb_frames[OAC_MAX_FRAMES_PER_PACKET]; }; typedef struct OacExtensionIterator { @@ -212,7 +231,7 @@ int oaci_is_digital_silence(const oac_res* pcm, int frame_size, int channels, in void oac_pcm_soft_clip_impl(float *_x, int N, int C, float *declip_mem, int arch); -int oaci_encode_size(int size, unsigned char *data); +int oaci_encode_size(oac_int32 size, unsigned char *data); oac_int32 oaci_frame_size_select(int application, oac_int32 frame_size, int variable_duration, oac_int32 Fs); @@ -238,7 +257,7 @@ static OAC_INLINE int oaci_align(int i) { int oac_packet_parse_impl(const unsigned char *data, oac_int32 len, int self_delimited, unsigned char *out_toc, - const unsigned char *frames[48], oac_int16 size[48], + const unsigned char *frames[OAC_MAX_FRAMES_PER_PACKET], oac_int32 size[OAC_MAX_FRAMES_PER_PACKET], int *payload_offset, oac_int32 *packet_offset, const unsigned char **padding, oac_int32 *padding_len, int format); diff --git a/src/repacketizer.c b/src/repacketizer.c index a0888579..17b551af 100644 --- a/src/repacketizer.c +++ b/src/repacketizer.c @@ -142,7 +142,7 @@ oac_int32 oac_repacketizer_out_range_impl(OacRepacketizer *rp, int begin, int en const oac_extension_data *extensions, int nb_extensions) { int i, count; oac_int32 tot_size; - oac_int16 *len; + oac_int32 *len; const unsigned char **frames; unsigned char * ptr; int ones_begin = 0, ones_end = 0; @@ -161,7 +161,7 @@ oac_int32 oac_repacketizer_out_range_impl(OacRepacketizer *rp, int begin, int en len = rp->len + begin; frames = rp->frames + begin; if (self_delimited) - tot_size = 1 + (len[count - 1] >= 252); + tot_size = oaci_size_bytes(len[count - 1]); else tot_size = 0; @@ -216,7 +216,7 @@ oac_int32 oac_repacketizer_out_range_impl(OacRepacketizer *rp, int begin, int en *ptr++ = (rp->toc&0xFC)|0x1; } else { /* Code 2 */ - tot_size += len[0] + len[1] + 2 + (len[0] >= 252); + tot_size += len[0] + len[1] + 1 + oaci_size_bytes(len[0]); if (tot_size > maxlen) { RESTORE_STACK; return OAC_BUFFER_TOO_SMALL; @@ -233,7 +233,7 @@ oac_int32 oac_repacketizer_out_range_impl(OacRepacketizer *rp, int begin, int en /* Restart the process for the padding case */ ptr = data; if (self_delimited) - tot_size = 1 + (len[count - 1] >= 252); + tot_size = oaci_size_bytes(len[count - 1]); else tot_size = 0; vbr = 0; @@ -246,7 +246,7 @@ oac_int32 oac_repacketizer_out_range_impl(OacRepacketizer *rp, int begin, int en if (vbr) { tot_size += 2; for (i = 0; i < count - 1; i++) - tot_size += 1 + (len[i] >= 252) + len[i]; + tot_size += oaci_size_bytes(len[i]) + len[i]; tot_size += len[count - 1]; if (tot_size > maxlen) { @@ -391,7 +391,7 @@ int oac_multistream_packet_pad(unsigned char *data, oac_int32 len, oac_int32 new int s; int count; unsigned char toc; - oac_int16 size[48]; + oac_int32 size[OAC_MAX_FRAMES_PER_PACKET]; oac_int32 packet_offset; oac_int32 amount; @@ -420,7 +420,7 @@ int oac_multistream_packet_pad(unsigned char *data, oac_int32 len, oac_int32 new oac_int32 oac_multistream_packet_unpad(unsigned char *data, oac_int32 len, int nb_streams) { int s; unsigned char toc; - oac_int16 size[48]; + oac_int32 size[OAC_MAX_FRAMES_PER_PACKET]; oac_int32 packet_offset; OacRepacketizer rp; unsigned char *dst; diff --git a/src/repacketizer_demo.c b/src/repacketizer_demo.c index 836d1c25..b22e7e85 100644 --- a/src/repacketizer_demo.c +++ b/src/repacketizer_demo.c @@ -68,6 +68,8 @@ #include #define MAX_PACKETOUT 32000 +/* Largest input packet this demo will read. */ +#define MAX_PACKETIN 1500 void usage(char *argv0) { fprintf(stderr, "usage: %s [options] input_file output_file\n", argv0); @@ -88,9 +90,9 @@ static oac_uint32 char_to_int(unsigned char ch[4]) { int main(int argc, char *argv[]) { int i, eof = 0; FILE *fin, *fout; - unsigned char packets[48][1500]; - int len[48]; - int rng[48]; + unsigned char packets[OAC_MAX_FRAMES_PER_PACKET][MAX_PACKETIN]; + int len[OAC_MAX_FRAMES_PER_PACKET]; + int rng[OAC_MAX_FRAMES_PER_PACKET]; OacRepacketizer *rp; unsigned char output_packet[MAX_PACKETOUT]; int merge = 1, split = 0; @@ -106,8 +108,8 @@ int main(int argc, char *argv[]) { fprintf(stderr, "-merge parameter must be at least 1.\n"); return EXIT_FAILURE; } - if (merge > 48) { - fprintf(stderr, "-merge parameter must be less than 48.\n"); + if (merge > OAC_MAX_FRAMES_PER_PACKET) { + fprintf(stderr, "-merge parameter must be at most %d.\n", OAC_MAX_FRAMES_PER_PACKET); return EXIT_FAILURE; } i++; @@ -151,7 +153,7 @@ int main(int argc, char *argv[]) { } len[i] = char_to_int(ch); /*fprintf(stderr, "in len = %d\n", len[i]);*/ - if (len[i] > 1500 || len[i] < 0) { + if (len[i] > MAX_PACKETIN || len[i] < 0) { if (feof(fin)) { eof = 1; } else { diff --git a/tests/oac_decode_fuzzer.c b/tests/oac_decode_fuzzer.c index 7fe568ba..d59f88f6 100644 --- a/tests/oac_decode_fuzzer.c +++ b/tests/oac_decode_fuzzer.c @@ -68,7 +68,10 @@ #include "oac_types.h" #define MAX_FRAME_SAMP 5760 -#define MAX_PACKET 1500 +/* Resource bound on how much of the fuzz input we will turn into a single + packet, not a protocol limit. It is well above the two-byte frame length + tier so the fuzzer can reach three-byte length codes. */ +#define MAX_PACKET 65536 /* 4 bytes: packet length, 4 bytes: encoder final range */ #define SETUP_BYTE_COUNT 8 diff --git a/tests/test_oac_api.c b/tests/test_oac_api.c index 4f07ffca..726953d8 100644 --- a/tests/test_oac_api.c +++ b/tests/test_oac_api.c @@ -115,12 +115,60 @@ oac_uint32 *null_uint_ptr = (oac_uint32 *)NULL; static const oac_int32 oac_rates[5] = {48000, 24000, 16000, 12000, 8000}; +/* Scratch buffer for the encoder/decoder API tests. It only ever has to hold + a single encoded frame at a sane bitrate. */ +#define TEST_PACKET_MAX 1500 + +/* Largest size that the frame length code represents in one and in two bytes. */ +#define TEST_SIZE_1B_MAX 191 +#define TEST_SIZE_2B_MAX 8383 +/* Sweep bound for the parser tests: crosses both tier boundaries. */ +#define TEST_SIZE_SWEEP 8500 +/* Packet buffer for the parser tests. The parser never reads the payload, so + this only has to be large enough for the headers the tests write. */ +#define TEST_PACKET_BUF 4096 +/* Arbitrary packet length used where the tests only need "some plausible, + non-degenerate packet length". */ +#define TEST_PKT_LEN 1024 +/* Frame size for the jumbo-packet test. Above 32767 so that it also catches a + regression to the old oac_int16 size[] truncation. */ +#define TEST_JUMBO_FRAME 100000 + +/* Test-local reference implementation of the frame length code, written from + the specification rather than reusing the library's. test_oac_api only links + against the public API, so oaci_encode_size() is not available here. Returns + the number of bytes written. */ +static int ref_put_size(unsigned char *p, oac_int32 size) { + if (size <= TEST_SIZE_1B_MAX) { + p[0] = (unsigned char)size; + return 1; + } else if (size <= TEST_SIZE_2B_MAX) { + oac_int32 v = size - 192; + p[0] = (unsigned char)(192 + (v&0x1F)); + p[1] = (unsigned char)(v>>5); + return 2; + } else { + oac_int32 v = size - 8384; + p[0] = (unsigned char)(224 + (v&0x1F)); + p[1] = (unsigned char)((v>>5)&0xFF); + p[2] = (unsigned char)(v>>13); + return 3; + } +} + +/* Number of bytes the reference encoder above uses for a given size. */ +static int ref_size_bytes(oac_int32 size) { + if (size <= TEST_SIZE_1B_MAX) return 1; + if (size <= TEST_SIZE_2B_MAX) return 2; + return 3; +} + oac_int32 test_dec_api(void) { oac_uint32 dec_final_range; OacDecoder *dec; OacDecoder *dec2; oac_int32 i, j, cfgs; - unsigned char packet[1276]; + unsigned char packet[TEST_PACKET_MAX]; #ifndef DISABLE_FLOAT_API float fbuf[960*2]; #endif @@ -377,7 +425,7 @@ oac_int32 test_msdec_api(void) { OacMSDecoder *dec; OacDecoder *streamdec; oac_int32 i, j, cfgs; - unsigned char packet[1276]; + unsigned char packet[TEST_PACKET_MAX]; unsigned char mapping[256]; #ifndef DISABLE_FLOAT_API float fbuf[960*2]; @@ -764,15 +812,15 @@ oac_int32 test_msdec_api(void) { other implementations. */ oac_int32 test_parse(void) { oac_int32 i, j, jj, sz; - unsigned char packet[1276]; + unsigned char packet[TEST_PACKET_BUF]; oac_int32 cfgs, cfgs_total; unsigned char toc; - const unsigned char *frames[48]; - short size[48]; - int payload_offset, ret; + const unsigned char *frames[OAC_MAX_FRAMES_PER_PACKET]; + oac_int32 size[OAC_MAX_FRAMES_PER_PACKET]; + int payload_offset, ret, nb; fprintf(stdout, "\n Packet header parsing tests\n"); fprintf(stdout, " ---------------------------------------------------\n"); - memset(packet, 0, sizeof(char)*1276); + memset(packet, 0, sizeof(packet)); packet[0] = 63<<2; if (oac_packet_parse(packet, 1, &toc, frames, 0, &payload_offset, OAC_FORMAT_STANDARD) != OAC_BAD_ARG) test_failed(); cfgs_total = cfgs = 1; @@ -789,16 +837,33 @@ oac_int32 test_parse(void) { fprintf(stdout, " code 0 (%2d cases) ............................ OK.\n", cfgs); cfgs_total += cfgs; cfgs = 0; + /*code 0, the largest representable implicit length and one past it*/ + for (i = 0; i < 64; i++) { + packet[0] = i<<2; + UNDEFINE_FOR_PARSE + ret = oac_packet_parse(packet, OAC_SIZE_MAX + 1, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + cfgs++; + if (ret != 1) test_failed(); + if (size[0] != OAC_SIZE_MAX) test_failed(); + UNDEFINE_FOR_PARSE + ret = oac_packet_parse(packet, OAC_SIZE_MAX + 2, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + cfgs++; + if (ret != OAC_INVALID_PACKET) test_failed(); + } + fprintf(stdout, " code 0 size limit (%2d cases) ................ OK.\n", cfgs); + cfgs_total += cfgs; cfgs = 0; + /*code 1, two frames of the same size*/ for (i = 0; i < 64; i++) { packet[0] = (i<<2) + 1; - for (jj = 0; jj <= 1275*2 + 3; jj++) { + for (jj = 0; jj <= 2*TEST_PKT_LEN + 3; jj++) { UNDEFINE_FOR_PARSE ret = oac_packet_parse(packet, jj, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; - if ((jj&1) == 1 && jj <= 2551) { - /* Must pass if payload length even (packet length odd) and - size<=2551, must fail otherwise. */ + if ((jj&1) == 1) { + /* Must pass if the payload length is even (packet length odd). + Unlike the old fixed frame-length limit, every such split + up to OAC_SIZE_MAX per frame is representable. */ if (ret != 2) test_failed(); if (size[0] != size[1] || size[0] != ((jj - 1)>>1)) test_failed(); if (frames[0] != packet + 1) test_failed(); @@ -806,6 +871,17 @@ oac_int32 test_parse(void) { if ((toc>>2) != i) test_failed(); } else if (ret != OAC_INVALID_PACKET) test_failed(); } + /*The largest representable pair of implicit lengths must be accepted.*/ + UNDEFINE_FOR_PARSE + ret = oac_packet_parse(packet, 2*(oac_int32)OAC_SIZE_MAX + 1, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + cfgs++; + if (ret != 2) test_failed(); + if (size[0] != OAC_SIZE_MAX || size[1] != OAC_SIZE_MAX) test_failed(); + /*One byte per frame more must not be.*/ + UNDEFINE_FOR_PARSE + ret = oac_packet_parse(packet, 2*((oac_int32)OAC_SIZE_MAX + 1) + 1, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + cfgs++; + if (ret != OAC_INVALID_PACKET) test_failed(); } fprintf(stdout, " code 1 (%6d cases) ........................ OK.\n", cfgs); cfgs_total += cfgs; cfgs = 0; @@ -817,29 +893,34 @@ oac_int32 test_parse(void) { ret = oac_packet_parse(packet, 1, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); - packet[1] = 252; + /*code 2, a two-byte length code truncated by the end of the packet*/ + packet[1] = 192; UNDEFINE_FOR_PARSE ret = oac_packet_parse(packet, 2, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); - for (j = 0; j < 1275; j++) { - if (j < 252) packet[1] = j; - else { - packet[1] = 252 + (j&3); packet[2] = (j - 252)>>2; - } + /*code 2, a three-byte length code truncated by the end of the packet*/ + packet[1] = 224; + packet[2] = 0; + UNDEFINE_FOR_PARSE + ret = oac_packet_parse(packet, 3, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + cfgs++; + if (ret != OAC_INVALID_PACKET) test_failed(); + for (j = 0; j < TEST_SIZE_SWEEP; j++) { + nb = ref_put_size(&packet[1], j); /*Code 2, one too short*/ UNDEFINE_FOR_PARSE - ret = oac_packet_parse(packet, j + (j < 252?2:3) - 1, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + ret = oac_packet_parse(packet, 1 + nb + j - 1, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); - /*Code 2, one too long*/ + /*Code 2, the second frame one byte past what can be represented*/ UNDEFINE_FOR_PARSE - ret = oac_packet_parse(packet, j + (j < 252?2:3) + 1276, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + ret = oac_packet_parse(packet, 1 + nb + j + (oac_int32)OAC_SIZE_MAX + 1, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); /*Code 2, second zero*/ UNDEFINE_FOR_PARSE - ret = oac_packet_parse(packet, j + (j < 252?2:3), &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + ret = oac_packet_parse(packet, 1 + nb + j, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != 2) test_failed(); if (size[0] != j || size[1] != 0) test_failed(); @@ -847,10 +928,10 @@ oac_int32 test_parse(void) { if ((toc>>2) != i) test_failed(); /*Code 2, normal*/ UNDEFINE_FOR_PARSE - ret = oac_packet_parse(packet, (j<<1) + 4, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + ret = oac_packet_parse(packet, (j<<1) + nb + 2, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != 2) test_failed(); - if (size[0] != j || size[1] != (j<<1) + 3 - j - (j < 252?1:2)) test_failed(); + if (size[0] != j || size[1] != j + 1) test_failed(); if (frames[1] != frames[0] + size[0]) test_failed(); if ((toc>>2) != i) test_failed(); } @@ -875,22 +956,22 @@ oac_int32 test_parse(void) { for (jj = 49; jj <= 64; jj++) { packet[1] = 0 + (jj&63); /*CBR, no padding*/ UNDEFINE_FOR_PARSE - ret = oac_packet_parse(packet, 1275, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + ret = oac_packet_parse(packet, TEST_PKT_LEN, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); packet[1] = 128 + (jj&63); /*VBR, no padding*/ UNDEFINE_FOR_PARSE - ret = oac_packet_parse(packet, 1275, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + ret = oac_packet_parse(packet, TEST_PKT_LEN, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); packet[1] = 64 + (jj&63); /*CBR, padding*/ UNDEFINE_FOR_PARSE - ret = oac_packet_parse(packet, 1275, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + ret = oac_packet_parse(packet, TEST_PKT_LEN, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); packet[1] = 128 + 64 + (jj&63); /*VBR, padding*/ UNDEFINE_FOR_PARSE - ret = oac_packet_parse(packet, 1275, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + ret = oac_packet_parse(packet, TEST_PKT_LEN, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); } @@ -902,7 +983,7 @@ oac_int32 test_parse(void) { packet[0] = (i<<2) + 3; /*code 3, m is one, cbr*/ packet[1] = 1; - for (j = 0; j < 1276; j++) { + for (j = 0; j < TEST_SIZE_SWEEP; j++) { UNDEFINE_FOR_PARSE ret = oac_packet_parse(packet, j + 2, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; @@ -910,12 +991,19 @@ oac_int32 test_parse(void) { if (size[0] != j) test_failed(); if ((toc>>2) != i) test_failed(); } + /*The largest representable implicit length must be accepted...*/ + UNDEFINE_FOR_PARSE + ret = oac_packet_parse(packet, (oac_int32)OAC_SIZE_MAX + 2, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + cfgs++; + if (ret != 1) test_failed(); + if (size[0] != OAC_SIZE_MAX) test_failed(); + /*...and one byte more must not.*/ UNDEFINE_FOR_PARSE - ret = oac_packet_parse(packet, 1276 + 2, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + ret = oac_packet_parse(packet, (oac_int32)OAC_SIZE_MAX + 3, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); } - fprintf(stdout, " code 3 m=1 CBR (%2d cases) ................. OK.\n", cfgs); + fprintf(stdout, " code 3 m=1 CBR (%6d cases) ............... OK.\n", cfgs); cfgs_total += cfgs; cfgs = 0; for (i = 0; i < 64; i++) { @@ -925,25 +1013,27 @@ oac_int32 test_parse(void) { frame_samp = oac_packet_get_samples_per_frame(packet, 48000); for (j = 2; j < 49; j++) { packet[1] = j; - for (sz = 2; sz < ((j + 2)*1275); sz++) { + for (sz = 2; sz < ((j + 2)*TEST_PKT_LEN); sz++) { UNDEFINE_FOR_PARSE ret = oac_packet_parse(packet, sz, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; - /*Must be <=120ms, must be evenly divisible, can't have frames>1275 bytes*/ - if (frame_samp*j <= 5760 && (sz - 2)%j == 0 && (sz - 2)/j < 1276) { + /*Must be <=120ms and must be evenly divisible. There is no + longer a per-frame byte limit below OAC_SIZE_MAX.*/ + if (frame_samp*j <= 5760 && (sz - 2)%j == 0) { if (ret != j) test_failed(); for (jj = 1; jj < ret; jj++) if (frames[jj] != frames[jj - 1] + size[jj - 1]) test_failed(); if ((toc>>2) != i) test_failed(); } else if (ret != OAC_INVALID_PACKET) test_failed(); } } - /*Super jumbo packets*/ + /*Super jumbo packets. The frame size here is deliberately above 32767, + which the old oac_int16 size[] silently truncated.*/ packet[1] = 5760/frame_samp; UNDEFINE_FOR_PARSE - ret = oac_packet_parse(packet, 1275*packet[1] + 2, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + ret = oac_packet_parse(packet, TEST_JUMBO_FRAME*packet[1] + 2, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != packet[1]) test_failed(); - for (jj = 0; jj < ret; jj++) if (size[jj] != 1275) test_failed(); + for (jj = 0; jj < ret; jj++) if (size[jj] != TEST_JUMBO_FRAME) test_failed(); } fprintf(stdout, " code 3 m=1-48 CBR (%2d cases) .......... OK.\n", cfgs); cfgs_total += cfgs; cfgs = 0; @@ -954,7 +1044,7 @@ oac_int32 test_parse(void) { packet[0] = (i<<2) + 3; packet[1] = 128 + 1; frame_samp = oac_packet_get_samples_per_frame(packet, 48000); - for (jj = 0; jj < 1276; jj++) { + for (jj = 0; jj < TEST_SIZE_SWEEP; jj++) { UNDEFINE_FOR_PARSE ret = oac_packet_parse(packet, 2 + jj, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; @@ -963,7 +1053,12 @@ oac_int32 test_parse(void) { if ((toc>>2) != i) test_failed(); } UNDEFINE_FOR_PARSE - ret = oac_packet_parse(packet, 2 + 1276, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + ret = oac_packet_parse(packet, 2 + (oac_int32)OAC_SIZE_MAX, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + cfgs++; + if (ret != 1) test_failed(); + if (size[0] != OAC_SIZE_MAX) test_failed(); + UNDEFINE_FOR_PARSE + ret = oac_packet_parse(packet, 2 + (oac_int32)OAC_SIZE_MAX + 1, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); for (j = 2; j < 49; j++) { @@ -973,9 +1068,9 @@ oac_int32 test_parse(void) { ret = oac_packet_parse(packet, 2 + j - 2, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); - packet[2] = 252; - packet[3] = 0; - for (jj = 4; jj < 2 + j; jj++) packet[jj] = 0; + /*A three-byte length code that does not fit in the packet*/ + nb = ref_put_size(&packet[2], TEST_SIZE_2B_MAX + 1); + for (jj = 2 + nb; jj < 2 + j; jj++) packet[jj] = 0; UNDEFINE_FOR_PARSE ret = oac_packet_parse(packet, 2 + j, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; @@ -987,13 +1082,22 @@ oac_int32 test_parse(void) { cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); /*One byte too short thanks to length coding*/ - packet[2] = 252; - packet[3] = 0; - for (jj = 4; jj < 2 + j; jj++) packet[jj] = 0; + nb = ref_put_size(&packet[2], TEST_SIZE_2B_MAX + 1); + for (jj = 2 + nb; jj < 2 + nb + j - 2; jj++) packet[jj] = 0; UNDEFINE_FOR_PARSE - ret = oac_packet_parse(packet, 2 + j + 252 - 1, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + ret = oac_packet_parse(packet, 2 + nb + (j - 2) + TEST_SIZE_2B_MAX + 1 - 1, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; if (ret != OAC_INVALID_PACKET) test_failed(); + /*...and exactly long enough parses, with a three-byte length code*/ + UNDEFINE_FOR_PARSE + ret = oac_packet_parse(packet, 2 + nb + (j - 2) + TEST_SIZE_2B_MAX + 1, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + cfgs++; + if (frame_samp*j <= 5760) { + if (ret != j) test_failed(); + if (size[0] != TEST_SIZE_2B_MAX + 1) test_failed(); + for (jj = 1; jj < j; jj++) if (size[jj] != 0) test_failed(); + if ((toc>>2) != i) test_failed(); + } else if (ret != OAC_INVALID_PACKET) test_failed(); /*Most expensive way of coding zeros*/ for (jj = 2; jj < 2 + j; jj++) packet[jj] = 0; UNDEFINE_FOR_PARSE @@ -1004,22 +1108,17 @@ oac_int32 test_parse(void) { for (jj = 0; jj < j; jj++) if (size[jj] != 0) test_failed(); if ((toc>>2) != i) test_failed(); } else if (ret != OAC_INVALID_PACKET) test_failed(); - /*Quasi-CBR use of mode 3*/ + /*Quasi-CBR use of mode 3. The larger entries of tsz[] put the + per-frame length in the two- and three-byte tiers.*/ for (sz = 0; sz < 8; sz++) { const int tsz[8] = {50, 201, 403, 700, 1472, 5110, 20400, 61298}; int pos = 0; int as = (tsz[sz] + i - j - 2)/j; - for (jj = 0; jj < j - 1; jj++) { - if (as < 252) { - packet[2 + pos] = as; pos++; - } else { - packet[2 + pos] = 252 + (as&3); packet[3 + pos] = (as - 252)>>2; pos += 2; - } - } + for (jj = 0; jj < j - 1; jj++) pos += ref_put_size(&packet[2 + pos], as); UNDEFINE_FOR_PARSE ret = oac_packet_parse(packet, tsz[sz] + i, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; - if (frame_samp*j <= 5760 && as < 1276 && (tsz[sz] + i - 2 - pos - as*(j - 1)) < 1276) { + if (frame_samp*j <= 5760) { if (ret != j) test_failed(); for (jj = 0; jj < j - 1; jj++) if (size[jj] != as) test_failed(); if (size[j - 1] != (tsz[sz] + i - 2 - pos - as*(j - 1))) test_failed(); @@ -1043,7 +1142,7 @@ oac_int32 test_parse(void) { if (ret != OAC_INVALID_PACKET) test_failed(); for (sz = 0; sz < 4; sz++) { - const int tsz[4] = {0, 72, 512, 1275}; + const int tsz[4] = {0, 72, 512, TEST_PKT_LEN}; for (jj = sz; jj < 65025; jj += 11) { int pos; for (pos = 0; pos < jj/254; pos++) packet[2 + pos] = 255; @@ -1059,11 +1158,10 @@ oac_int32 test_parse(void) { UNDEFINE_FOR_PARSE ret = oac_packet_parse(packet, 2 + jj + tsz[sz] + i + pos, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); cfgs++; - if (tsz[sz] + i < 1276) { - if (ret != 1) test_failed(); - if (size[0] != tsz[sz] + i) test_failed(); - if ((toc>>2) != i) test_failed(); - } else if (ret != OAC_INVALID_PACKET) test_failed(); + /*Every size used here is representable, so this always parses.*/ + if (ret != 1) test_failed(); + if (size[0] != tsz[sz] + i) test_failed(); + if ((toc>>2) != i) test_failed(); } } } @@ -1103,7 +1201,7 @@ oac_int32 test_enc_api(void) { oac_uint32 enc_final_range; OacEncoder *enc; oac_int32 i, j; - unsigned char packet[1276]; + unsigned char packet[TEST_PACKET_MAX]; #ifndef DISABLE_FLOAT_API float fbuf[960*2]; #endif @@ -1483,7 +1581,11 @@ oac_int32 test_enc_api(void) { return cfgs; } -#define max_out (1276*48 + 48*2 + 2) +/* Largest input payload the repacketizer test feeds in. */ +#define TEST_REPACK_MAX 1350 +/* Worst case output: OAC_MAX_FRAMES_PER_PACKET frames, each of which may need + a three-byte length code, plus the TOC and frame count bytes. */ +#define max_out ((TEST_REPACK_MAX + 3)*OAC_MAX_FRAMES_PER_PACKET + 2) int test_repacketizer_api(void) { int ret, cfgs, i, j, k; OacRepacketizer *rp; @@ -1569,17 +1671,19 @@ int test_repacketizer_api(void) { if (i > 1) packet[0] += i == 2?1:3; packet[1] = i > 2?i:0; maxp = 960/(i*oac_packet_get_samples_per_frame(packet, 8000)); - for (k = 0; k <= (1275 + 75); k += 3) { + for (k = 0; k <= TEST_REPACK_MAX; k += 3) { /*Payload size*/ oac_int32 cnt, rcnt; if (k%i != 0) continue; /* Only testing CBR here, payload must be a multiple of the count */ for (cnt = 0; cnt < maxp + 2; cnt++) { if (cnt > 0) { ret = oac_repacketizer_cat(rp, packet, k + (i > 2?2:1)); - if ((cnt <= maxp && k <= (1275*i))?ret != OAC_OK:ret != OAC_INVALID_PACKET) test_failed(); + /* Only the 120 ms limit can reject now: there is no + longer a per-frame byte limit at this scale. */ + if ((cnt <= maxp)?ret != OAC_OK:ret != OAC_INVALID_PACKET) test_failed(); cfgs++; } - rcnt = k <= (1275*i)?(cnt < maxp?cnt:maxp):0; + rcnt = cnt < maxp?cnt:maxp; if (oac_repacketizer_get_nb_frames(rp) != rcnt*i) test_failed(); cfgs++; ret = oac_repacketizer_out_range(rp, 0, rcnt*i, po, max_out); @@ -1891,6 +1995,331 @@ return cfgs; # endif #endif +/* Exhaustively checks the frame length code. The reference encoder above is + written from the specification, so this validates the library's parser + against an independent implementation rather than against itself. */ +oac_int32 test_frame_length_code(void) { + oac_int32 s, cfgs; + unsigned char *pkt; + unsigned char toc; + const unsigned char *frames[OAC_MAX_FRAMES_PER_PACKET]; + oac_int32 size[OAC_MAX_FRAMES_PER_PACKET]; + int payload_offset, nb, ret; + + cfgs = 0; + fprintf(stdout, "\n Frame length code tests\n"); + fprintf(stdout, " ---------------------------------------------------\n"); + + /* The parser never reads the payload, so a single buffer large enough for + the longest packet we claim to have is enough for every size. */ + pkt = (unsigned char *)calloc((size_t)OAC_SIZE_MAX + 8, 1); + if (pkt == NULL) test_failed(); + + /* Every size in [0, OAC_SIZE_MAX] must survive a reference-encode followed + by a library parse, and must be rejected when the packet is one byte + short of what the length claims. */ + pkt[0] = (31<<2) + 2; + for (s = 0; s <= OAC_SIZE_MAX; s++) { + nb = ref_put_size(&pkt[1], s); + if (nb != (s < 192 ? 1 : (s < 8384 ? 2 : 3))) test_failed(); + UNDEFINE_FOR_PARSE + ret = oac_packet_parse(pkt, 1 + nb + s, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + if (ret != 2) test_failed(); + if (size[0] != s || size[1] != 0) test_failed(); + if (frames[0] != pkt + 1 + nb) test_failed(); + if (frames[1] != frames[0] + s) test_failed(); + UNDEFINE_FOR_PARSE + ret = oac_packet_parse(pkt, 1 + nb + s - 1, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + if (ret != OAC_INVALID_PACKET) test_failed(); + cfgs += 2; + } + fprintf(stdout, " exhaustive parse 0..%d ................. OK.\n", OAC_SIZE_MAX); + + /* Now the other direction: the length bytes the library writes must match + the reference at every tier boundary. The repacketizer is the only public + way to make it emit an explicit length. */ + { + const oac_int32 boundaries[9] = {0, 191, 192, 223, 224, 8383, 8384, 100000, OAC_SIZE_MAX}; + unsigned char *out; + unsigned char ref[3]; + OacRepacketizer *rp; + oac_int32 out_len; + int i; + + out = (unsigned char *)calloc((size_t)OAC_SIZE_MAX + 16, 1); + if (out == NULL) test_failed(); + rp = oac_repacketizer_create(OAC_FORMAT_STANDARD); + if (rp == NULL) test_failed(); + + for (i = 0; i < 9; i++) { + s = boundaries[i]; + /* Two code 0 frames of different sizes, so the output uses code 2 + and has to signal the length of the first one explicitly. The + same buffer backs both frames; the repacketizer only keeps + pointers into it. */ + pkt[0] = (31<<2) + 0; + oac_repacketizer_init(rp, OAC_FORMAT_STANDARD); + if (oac_repacketizer_cat(rp, pkt, 1 + s) != OAC_OK) test_failed(); + if (oac_repacketizer_cat(rp, pkt, 1 + 1) != OAC_OK) test_failed(); + out_len = oac_repacketizer_out(rp, out, OAC_SIZE_MAX + 16); + nb = ref_put_size(ref, s); + if (out_len != 1 + nb + s + 1) test_failed(); + if (memcmp(out + 1, ref, nb) != 0) test_failed(); + UNDEFINE_FOR_PARSE + ret = oac_packet_parse(out, out_len, &toc, frames, size, &payload_offset, OAC_FORMAT_STANDARD); + if (ret != 2) test_failed(); + if (size[0] != s || size[1] != 1) test_failed(); + cfgs += 4; + } + oac_repacketizer_destroy(rp); + free(out); + } + fprintf(stdout, " length bytes written at tier boundaries ..... OK.\n"); + + /* Round trip real payloads of assorted sizes through the repacketizer, in + both directions and across all three length tiers. Two equal frames are + re-coded as code 1, which drops the explicit length; anything else keeps + code 2. Getting that accounting wrong is what would overrun a caller's + buffer. */ + { + const oac_int32 sizes[8] = {1, 50, 191, 192, 300, 8383, 8384, 20000}; + const oac_int32 cap = 2*20000 + 16; + unsigned char *in; + unsigned char *out; + OacRepacketizer *rp; + int i, j; + + in = (unsigned char *)malloc(cap); + out = (unsigned char *)malloc(cap); + if (in == NULL || out == NULL) test_failed(); + rp = oac_repacketizer_create(OAC_FORMAT_STANDARD); + if (rp == NULL) test_failed(); + + for (i = 0; i < 8; i++) { + for (j = 0; j < 8; j++) { + oac_int32 s0 = sizes[i]; + oac_int32 s1 = sizes[j]; + oac_int32 k, len, expect; + + /* Build a code 2 packet by hand: 20 ms CELT-only stereo ToC. */ + in[0] = (15<<3)|(1<<2)|2; + len = 1; + len += ref_put_size(in + len, s0); + for (k = 0; k < s0; k++) in[len + k] = (unsigned char)(k + i); + len += s0; + for (k = 0; k < s1; k++) in[len + k] = (unsigned char)(k + j + 7); + len += s1; + + if (oac_repacketizer_init(rp, OAC_FORMAT_STANDARD) == NULL) test_failed(); + if (oac_repacketizer_cat(rp, in, len) != OAC_OK) test_failed(); + if (oac_repacketizer_get_nb_frames(rp) != 2) test_failed(); + ret = oac_repacketizer_out(rp, out, cap); + expect = (s0 == s1) ? 1 + s0 + s1 : 1 + ref_size_bytes(s0) + s0 + s1; + if (ret != expect) test_failed(); + + UNDEFINE_FOR_PARSE + if (oac_packet_parse(out, ret, &toc, frames, size, &payload_offset, + OAC_FORMAT_STANDARD) != 2) test_failed(); + if (size[0] != s0 || size[1] != s1) test_failed(); + if (memcmp(frames[0], in + 1 + ref_size_bytes(s0), s0) != 0) test_failed(); + if (memcmp(frames[1], in + len - s1, s1) != 0) test_failed(); + + /* Splitting back into single frames must also work. */ + if (oac_repacketizer_out_range(rp, 0, 1, out, cap) != 1 + s0) test_failed(); + if (oac_repacketizer_out_range(rp, 1, 2, out, cap) != 1 + s1) test_failed(); + cfgs += 6; + } + } + oac_repacketizer_destroy(rp); + free(in); + free(out); + } + fprintf(stdout, " repacketizer round trip across tiers ........ OK.\n"); + + + free(pkt); + fprintf(stdout, " All frame length code tests passed\n"); + fprintf(stdout, " (%d API invocations)\n", cfgs); + return cfgs; +} + + + +/* Room kept aside for the reference bitstream of one configuration. */ +#define TEST_REF_FRAMES 6 +#define TEST_REF_FRAME_BYTES 65536 + +/* Encoding the same audio at the same bitrate must give the exact same + bitstream no matter how much spare room the caller left in the output + buffer. Before the length signalling change, max_data_bytes could never + exceed 1276, so a number of derived rate computations silently assumed a + small value; with big buffers they overflow and quietly change the coding + decisions (bandwidth collapse, redundancy turning itself off, ...). + Comparing whole packets rather than just the length catches all of those. */ +oac_int32 test_encoder_buffer_independence(void) { + const int fsz[4] = {120, 480, 960, 2880}; + const oac_int32 rates[3] = {24000, 64000, 256000}; + const oac_int32 bufs[3] = {8000, 100000, 4000000}; + const int apps[2] = {OAC_APPLICATION_AUDIO, OAC_APPLICATION_VOIP}; + /* Sweeping the bitrate walks the encoder through SILK/hybrid/CELT and the + mode-switching redundancy, which a steady-state encode never reaches. */ + const oac_int32 sweep[TEST_REF_FRAMES] = {12000, 40000, 128000, 12000, 96000, 16000}; + oac_int32 cfgs; + int fi, ri, ai, ch, bi, f, i, pass; + unsigned int seed; + short *pcm; + short *out; + unsigned char *ref; + int ref_len[TEST_REF_FRAMES]; + + cfgs = 0; + fprintf(stdout, "\n Encoder output buffer independence tests\n"); + fprintf(stdout, " ---------------------------------------------------\n"); + + pcm = (short *)malloc(sizeof(short)*2880*2); + out = (short *)malloc(sizeof(short)*5760*2); + ref = (unsigned char *)malloc((size_t)TEST_REF_FRAMES*TEST_REF_FRAME_BYTES); + if (pcm == NULL || out == NULL || ref == NULL) test_failed(); + + /* pass 0: steady state at a fixed bitrate. pass 1: bitrate sweep, which + walks through SILK/hybrid/CELT and the mode-switch redundancy. pass 2: + same sweep with FEC on. FEC has to be a separate pass rather than part of + pass 1, because it biases the encoder towards SILK hard enough that the + mode transitions never happen. */ + for (pass = 0; pass < 3; pass++) + for (fi = 0; fi < 4; fi++) + for (ri = 0; ri < 3; ri++) + for (ai = 0; ai < 2; ai++) + for (ch = 1; ch <= 2; ch++) { + if (pass > 0 && (fi != 2 || ri != 0)) continue; /* sweep only needs 20 ms */ + for (bi = 0; bi < 3; bi++) { + OacEncoder *enc; + OacDecoder *dec; + unsigned char *data; + int err; + + /* --enable-fuzzing makes the encoder take random coding decisions, + and oac_select_arch() randomly downgrades the SIMD path on every + create, so the three runs would diverge for reasons that have + nothing to do with the output buffer. Restarting the generator + from the same seed before each run gives all three the same + sequence of random decisions, so any remaining difference really + is caused by the buffer size. Builds that never call rand() are + unaffected. */ + srand((unsigned)((((pass*4 + fi)*3 + ri)*2 + ai)*2 + ch)); + + enc = oac_encoder_create(48000, ch, OAC_FORMAT_STANDARD, apps[ai], &err); + if (err != OAC_OK || enc == NULL) test_failed(); + dec = oac_decoder_create(48000, ch, OAC_FORMAT_STANDARD, &err); + if (err != OAC_OK || dec == NULL) test_failed(); + if (oac_encoder_ctl(enc, OAC_SET_BITRATE(rates[ri])) != OAC_OK) test_failed(); + if (oac_encoder_ctl(enc, OAC_SET_VBR(1)) != OAC_OK) test_failed(); + if (oac_encoder_ctl(enc, OAC_SET_COMPLEXITY(3)) != OAC_OK) test_failed(); + if (pass == 2) { + if (oac_encoder_ctl(enc, OAC_SET_INBAND_FEC(1)) != OAC_OK) test_failed(); + if (oac_encoder_ctl(enc, OAC_SET_PACKET_LOSS_PERC(20)) != OAC_OK) test_failed(); + } + data = (unsigned char *)malloc(bufs[bi]); + if (data == NULL) test_failed(); + seed = 1234u; + for (f = 0; f < TEST_REF_FRAMES; f++) { + oac_uint32 erange, drange; + int dlen, len; + for (i = 0; i < fsz[fi]*ch; i++) { + seed = 1664525u*seed + 1013904223u; + pcm[i] = (short)((int)(seed>>20) - 2048); + } + if (pass > 0 && oac_encoder_ctl(enc, OAC_SET_BITRATE(sweep[f])) != OAC_OK) + test_failed(); + len = oac_encode(enc, pcm, fsz[fi], data, bufs[bi]); + if (len < 0 || len > bufs[bi]) test_failed(); + if (len > TEST_REF_FRAME_BYTES) test_failed(); + if (oac_encoder_ctl(enc, OAC_GET_FINAL_RANGE(&erange)) != OAC_OK) test_failed(); + if (bi == 0) { + ref_len[f] = len; + memcpy(ref + (size_t)f*TEST_REF_FRAME_BYTES, data, len); + } else { + /* The only difference between the runs is the buffer size. */ + if (len != ref_len[f]) test_failed(); + if (memcmp(ref + (size_t)f*TEST_REF_FRAME_BYTES, data, len) != 0) test_failed(); + } + dlen = oac_decode(dec, data, len, out, 5760, 0); + if (dlen != fsz[fi]) test_failed(); + if (oac_decoder_ctl(dec, OAC_GET_FINAL_RANGE(&drange)) != OAC_OK) test_failed(); + if (erange != drange) test_failed(); + cfgs += 4; + } + free(data); + oac_encoder_destroy(enc); + oac_decoder_destroy(dec); + } + } + free(pcm); + free(out); + free(ref); + fprintf(stdout, " encoder ignores spare output room ........... OK.\n"); + + /* Frame sizes above 20 ms are coded as several 20 ms frames in one packet. + That path sizes a scratch buffer on the stack; if it were sized from the + caller's output buffer rather than from what the frame encoder can + actually be asked for, simply offering a large buffer would crash the + encoder. 9 MB is above the usual 8 MB stack limit, which is the point. */ + { + const int big_fsz[4] = {1920, 2880, 4800, 5760}; + const oac_int32 bufsize = 9000000; + unsigned char *data; + int vbr; + + pcm = (short *)malloc(sizeof(short)*5760*2); + out = (short *)malloc(sizeof(short)*5760*2); + data = (unsigned char *)malloc(bufsize); + if (pcm == NULL || out == NULL || data == NULL) test_failed(); + for (fi = 0; fi < 4; fi++) + for (ch = 1; ch <= 2; ch++) + for (vbr = 0; vbr <= 1; vbr++) { + OacEncoder *enc; + OacDecoder *dec; + int err; + + enc = oac_encoder_create(48000, ch, OAC_FORMAT_STANDARD, OAC_APPLICATION_AUDIO, &err); + if (err != OAC_OK || enc == NULL) test_failed(); + dec = oac_decoder_create(48000, ch, OAC_FORMAT_STANDARD, &err); + if (err != OAC_OK || dec == NULL) test_failed(); + if (oac_encoder_ctl(enc, OAC_SET_BITRATE(96000)) != OAC_OK) test_failed(); + if (oac_encoder_ctl(enc, OAC_SET_VBR(vbr)) != OAC_OK) test_failed(); + if (oac_encoder_ctl(enc, OAC_SET_COMPLEXITY(3)) != OAC_OK) test_failed(); + seed = 777u; + for (f = 0; f < 2; f++) { + oac_uint32 erange, drange; + oac_int32 len; + int dlen; + for (i = 0; i < big_fsz[fi]*ch; i++) { + seed = 1664525u*seed + 1013904223u; + pcm[i] = (short)((int)(seed>>20) - 2048); + } + len = oac_encode(enc, pcm, big_fsz[fi], data, bufsize); + if (len < 0 || len > bufsize) test_failed(); + if (oac_encoder_ctl(enc, OAC_GET_FINAL_RANGE(&erange)) != OAC_OK) test_failed(); + dlen = oac_decode(dec, data, len, out, 5760, 0); + if (dlen != big_fsz[fi]) test_failed(); + if (oac_decoder_ctl(dec, OAC_GET_FINAL_RANGE(&drange)) != OAC_OK) test_failed(); + if (erange != drange) test_failed(); + cfgs += 4; + } + oac_encoder_destroy(enc); + oac_decoder_destroy(dec); + } + free(pcm); + free(out); + free(data); + } + fprintf(stdout, " large buffers do not blow the stack ......... OK.\n"); + + fprintf(stdout, " All encoder buffer independence tests passed\n"); + fprintf(stdout, " (%d API invocations)\n", cfgs); + return cfgs; +} + int main(int _argc, char **_argv) { oac_int32 total; const char * oversion; @@ -1911,7 +2340,9 @@ int main(int _argc, char **_argv) { total += test_dec_api(); total += test_msdec_api(); total += test_parse(); + total += test_frame_length_code(); total += test_enc_api(); + total += test_encoder_buffer_independence(); total += test_repacketizer_api(); total += test_malloc_fail(); diff --git a/tests/test_oac_decode.c b/tests/test_oac_decode.c index 3238a516..84caa8ee 100644 --- a/tests/test_oac_decode.c +++ b/tests/test_oac_decode.c @@ -321,7 +321,7 @@ int test_decoder_code0(int no_fuzz) { int j, expected[5*2]; packet[0] = i<<2; for (t = 0; t < 5*2; t++) expected[t] = oac_decoder_get_nb_samples(dec[t], packet, 1); - for (j = 2 + skip; j < 1275; j += 4) { + for (j = 2 + skip; j < MAX_PACKET; j += 4) { int jj; for (jj = 0; jj < j; jj++) packet[jj + 1] = fast_rand()&255; for (t = 0; t < 5*2; t++) { diff --git a/tests/test_oac_encode.c b/tests/test_oac_encode.c index aa20d3c4..36d24779 100644 --- a/tests/test_oac_encode.c +++ b/tests/test_oac_encode.c @@ -80,7 +80,7 @@ #include "../src/oac_private.h" #include "test_oac_common.h" -#define MAX_PACKET (1500) +#define MAX_PACKET (30000) #define SAMPLES (48000*30) #define SSAMPLES (SAMPLES/3) #define MAX_FRAME_SAMP (5760) @@ -244,7 +244,7 @@ void fuzz_encoder_settings(const int num_encoders, const int num_setting_changes int sampling_rates[5] = {8000, 12000, 16000, 24000, 48000}; int channels[2] = {1, 2}; int applications[3] = {OAC_APPLICATION_AUDIO, OAC_APPLICATION_VOIP, OAC_APPLICATION_RESTRICTED_LOWDELAY}; - int bitrates[11] = {6000, 12000, 16000, 24000, 32000, 48000, 64000, 96000, 510000, OAC_AUTO, OAC_BITRATE_MAX}; + int bitrates[11] = {6000, 12000, 16000, 24000, 32000, 48000, 64000, 96000, 750000, OAC_AUTO, OAC_BITRATE_MAX}; int force_channels[4] = {OAC_AUTO, OAC_AUTO, 1, 2}; int use_vbr[3] = {0, 1, 1}; int vbr_constraints[3] = {0, 1, 1}; @@ -453,7 +453,7 @@ int run_test1(int no_fuzz) { for (j = 0; j < 13; j++) { int rate; int modes[13] = {0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2}; - int rates[13] = {6000, 12000, 48000, 16000, 32000, 48000, 64000, 512000, 13000, 24000, 48000, 64000, 96000}; + int rates[13] = {6000, 12000, 48000, 16000, 32000, 48000, 64000, 1500000, 13000, 24000, 48000, 64000, 96000}; int frame[13] = {960*2, 960, 480, 960, 960, 960, 480, 960*3, 960*3, 960, 480, 240, 120}; rate = rates[j] + fast_rand()%rates[j]; count = i = 0; @@ -598,7 +598,7 @@ int run_test1(int no_fuzz) { } } - bitrate_bps = 512000; + bitrate_bps = 1500000; fsize = fast_rand()%31; fswitch = 100; @@ -606,8 +606,8 @@ int run_test1(int no_fuzz) { count = i = 0; do { unsigned char toc; - const unsigned char *frames[48]; - short size[48]; + const unsigned char *frames[OAC_MAX_FRAMES_PER_PACKET]; + oac_int32 size[OAC_MAX_FRAMES_PER_PACKET]; int payload_offset; oac_uint32 dec_final_range2; int jj, dec2; diff --git a/tests/test_oac_extensions.c b/tests/test_oac_extensions.c index 7076b186..babf7c48 100644 --- a/tests/test_oac_extensions.c +++ b/tests/test_oac_extensions.c @@ -514,7 +514,7 @@ void test_extensions_repeating(void) { oac_int32 nb_ext; for (nb_ext = 0; nb_ext <= NB_EXT; nb_ext++) { oac_extension_data ext_out[NB_EXT]; - oac_int32 nb_frame_exts[48]; + oac_int32 nb_frame_exts[OAC_MAX_FRAMES_PER_PACKET]; oac_int32 nb_ext_out; int len, result; unsigned char packet[64]; @@ -628,7 +628,7 @@ void test_random_extensions_parse(void) { if (result == OAC_OK) { oac_extension_data ext_out2[MAX_NB_EXTENSIONS]; unsigned char payload2[MAX_EXTENSION_SIZE + 1]; - oac_int32 nb_frame_exts[48]; + oac_int32 nb_frame_exts[OAC_MAX_FRAMES_PER_PACKET]; oac_int32 nb_ext_out; len = oac_packet_extensions_generate(payload2, sizeof(payload2), ext_out, nb_ext, nb_frames, 0); @@ -652,7 +652,7 @@ void test_oac_repacketizer_out_range_impl(void) { OacRepacketizer rp; unsigned char packet[1024]; unsigned char packet_out[1024]; - oac_int16 size[48]; + oac_int32 size[OAC_MAX_FRAMES_PER_PACKET]; const unsigned char *padding; oac_int32 padding_len; oac_extension_data ext_out[10];