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];