ref: 58bf8b413c064c41913ac1a5ad734971c811892c
parent: 5c13840b53e6124fa52ba3e83a42c4f528626211
author: Timothy B. Terriberry <tterribe@xiph.org>
date: Wed Jul 24 10:28:45 EDT 2024
Decoder implementation of Repeat These Extensions. Right now, opus_packet_extensions_parse() returns the extensions in the order they appear in the packet, which is no longer necessarily in frame order. This adds a new (still private) API that returns parsed extensions in frame order, even when repeated extensions are used. Nothing has been converted to use this new API yet. Signed-off-by: Jean-Marc Valin <jeanmarcv@google.com>
--- a/src/extensions.c
+++ b/src/extensions.c
@@ -36,80 +36,121 @@
#include "opus_private.h"
-/* Given an extension payload, advance data to the next extension and return the
- length of the remaining extensions. */
-static opus_int32 skip_extension(const unsigned char **data, opus_int32 len,
- opus_int32 *header_size)
+/* Given an extension payload (i.e., excluding the initial ID byte), advance
+ data to the next extension and return the length of the remaining
+ extensions.
+ N.B., a "Repeat These Extensions" extension (ID==2) does not advance past
+ the repeated extension payloads.
+ That requires higher-level logic. */
+static opus_int32 skip_extension_payload(const unsigned char **pdata,
+ opus_int32 len, opus_int32 *pheader_size, int id_byte)
{
+ const unsigned char *data;
+ opus_int32 header_size;
int id, L;
- if (len==0)
- return 0;
- id = **data>>1;
- L = **data&1;
- if (id == 0 && L == 1)
+ data = *pdata;
+ header_size = 0;
+ id = id_byte>>1;
+ L = id_byte&1;
+ if ((id == 0 && L == 1) || id == 2)
{
- *header_size = 1;
- if (len < 1)
- return -1;
- (*data)++;
- len--;
- return len;
+ /* Nothing to do. */
} else if (id > 0 && id < 32)
{
- if (len < 1+L)
+ if (len < L)
return -1;
- *data += 1+L;
- len -= 1+L;
- *header_size = 1;
- return len;
+ data += L;
+ len -= L;
} else {
if (L==0)
{
- *data += len;
- *header_size = 1;
- return 0;
+ data += len;
+ len = 0;
} else {
opus_int32 bytes=0;
opus_int32 lacing;
- *header_size = 1;
do {
- (*data)++;
- len--;
if (len < 1)
return -1;
- lacing = **data;
+ lacing = *data++;
bytes += lacing;
- (*header_size)++;
- len -= lacing;
+ header_size++;
+ len -= lacing + 1;
} while (lacing == 255);
- if (len < 1)
+ if (len < 0)
return -1;
- (*data)++;
- len--;
- *data += bytes;
- return len;
+ data += bytes;
}
}
+ *pdata = data;
+ *pheader_size = header_size;
+ return len;
}
+/* Given an extension, advance data to the next extension and return the
+ length of the remaining extensions.
+ N.B., a "Repeat These Extensions" extension (ID==2) only advances past the
+ extension ID byte.
+ Higher-level logic is required to skip the extension payloads that come
+ after it.*/
+static opus_int32 skip_extension(const unsigned char **pdata, opus_int32 len,
+ opus_int32 *pheader_size)
+{
+ const unsigned char *data;
+ int id_byte;
+ if (len == 0) {
+ *pheader_size = 0;
+ return 0;
+ }
+ if (len < 1)
+ return -1;
+ data = *pdata;
+ id_byte = *data++;
+ len--;
+ len = skip_extension_payload(&data, len, pheader_size, id_byte);
+ if (len >= 0) {
+ *pdata = data;
+ (*pheader_size)++;
+ }
+ return len;
+}
+
void opus_extension_iterator_init(OpusExtensionIterator *iter,
- const unsigned char *data, opus_int32 len) {
+ const unsigned char *data, opus_int32 len, opus_int32 nb_frames) {
celt_assert(len >= 0);
celt_assert(data != NULL || len == 0);
- iter->curr_data = iter->data = data;
+ celt_assert(nb_frames >= 0 && nb_frames <= 48);
+ iter->repeat_data_end = iter->repeat_data = iter->curr_data = iter->data =
+ data;
+ iter->src_data = NULL;
iter->curr_len = iter->len = len;
- iter->curr_frame = 0;
+ iter->repeat_len = iter->src_len = 0;
+ iter->frame_max = iter->nb_frames = nb_frames;
+ iter->repeat_frame = iter->curr_frame = 0;
+ iter->repeat_l = 0;
}
/* Reset the iterator so it can start iterating again from the first
extension. */
void opus_extension_iterator_reset(OpusExtensionIterator *iter) {
- iter->curr_data = iter->data;
+ iter->repeat_data_end = iter->repeat_data = iter->curr_data = iter->data;
iter->curr_len = iter->len;
- iter->curr_frame = 0;
+ iter->repeat_frame = iter->curr_frame = 0;
}
-/* Return the next extension (excluding real padding and separators). */
+/* Tell the iterator not to return any extensions for frames of index
+ frame_max or larger.
+ This can allow it to stop iterating early if these extensions are not
+ needed. */
+void opus_extension_iterator_set_frame_max(OpusExtensionIterator *iter,
+ int frame_max) {
+ iter->frame_max = frame_max;
+}
+
+/* Return the next extension (excluding real padding, separators, and repeat
+ indicators, but including the repeated extensions) in bitstream order.
+ Due to the extension repetition mechanism, extensions are not necessarily
+ returned in frame order. */
int opus_extension_iterator_next(OpusExtensionIterator *iter,
opus_extension_data *ext) {
opus_int32 header_size;
@@ -116,6 +157,75 @@
if (iter->curr_len < 0) {
return OPUS_INVALID_PACKET;
}
+ /* Checking this here allows opus_extension_iterator_set_frame_max() to be
+ called at any point. */
+ if (iter->curr_frame >= iter->frame_max) {
+ return 0;
+ }
+ if (iter->repeat_frame > 0) {
+ /* We are in the process of repeating some extensions. */
+ for (;iter->repeat_frame < iter->nb_frames; iter->repeat_frame++) {
+ while (iter->src_len > 0) {
+ const unsigned char *curr_data0;
+ int repeat_id_byte;
+ repeat_id_byte = *iter->src_data;
+ iter->src_len = skip_extension(&iter->src_data, iter->src_len,
+ &header_size);
+ /* We skipped this extension earlier, so it should not fail now. */
+ celt_assert(iter->src_len >= 0);
+ /* Don't repeat padding or frame separators with a 0 increment. */
+ if (repeat_id_byte <= 3) continue;
+ /* If the "Repeat These Extensions" extension had L == 0 and this
+ is the last repeated extension, and it is a long extension,
+ then force decoding the payload with L = 0. */
+ if (iter->repeat_l == 0
+ && iter->repeat_frame + 1 >= iter->nb_frames
+ && iter->src_data == iter->repeat_data_end
+ && repeat_id_byte >= 64) {
+ repeat_id_byte &= ~1;
+ }
+ curr_data0 = iter->curr_data;
+ iter->curr_len = skip_extension_payload(&iter->curr_data,
+ iter->curr_len, &header_size, repeat_id_byte);
+ if (iter->curr_len < 0) {
+ return OPUS_INVALID_PACKET;
+ }
+ celt_assert(iter->curr_data - iter->data
+ == iter->len - iter->curr_len);
+ /* If we were asked to stop at frame_max, skip extensions for later
+ frames. */
+ if (iter->repeat_frame >= iter->frame_max) {
+ if (iter->repeat_l == 0) {
+ /* If L == 0, there will be no more extensions after these
+ repeats, so we can just stop. */
+ iter->repeat_frame = 0;
+ iter->curr_len = 0;
+ return 0;
+ }
+ continue;
+ }
+ if (ext != NULL) {
+ ext->id = repeat_id_byte >> 1;
+ ext->frame = iter->repeat_frame;
+ ext->data = curr_data0 + header_size;
+ ext->len = iter->curr_data - curr_data0 - header_size;
+ }
+ return 1;
+ }
+ /* We finished repeating the extensions for this frame. */
+ iter->src_data = iter->repeat_data;
+ iter->src_len = iter->repeat_len;
+ }
+ /* We finished repeating extensions. */
+ iter->repeat_data_end = iter->repeat_data = iter->curr_data;
+ /* Even if there is more data (because there was nothing to repeat or
+ because the last extension was a short extension and we did not use
+ all the data), when L == 0 we are done decoding extensions. */
+ if (iter->repeat_l == 0) {
+ iter->curr_len = 0;
+ }
+ iter->repeat_frame = 0;
+ }
while (iter->curr_len > 0) {
const unsigned char *curr_data0;
int id;
@@ -134,14 +244,38 @@
iter->curr_frame++;
}
else {
+ /* A frame increment of 0 is a no-op. */
+ if (!curr_data0[1]) continue;
iter->curr_frame += curr_data0[1];
}
- if (iter->curr_frame >= 48) {
+ if (iter->curr_frame >= iter->nb_frames) {
iter->curr_len = -1;
return OPUS_INVALID_PACKET;
}
+ /* If we were asked to stop at frame_max, skip extensions for later
+ frames. */
+ if (iter->curr_frame >= iter->frame_max) {
+ iter->curr_len = 0;
+ }
+ iter->repeat_data_end = iter->repeat_data = iter->curr_data;
}
- else if (id > 1) {
+ else if (id == 2) {
+ iter->repeat_l = L;
+ iter->repeat_frame = iter->curr_frame + 1;
+ iter->repeat_len = curr_data0 - iter->repeat_data;
+ iter->src_data = iter->repeat_data;
+ iter->src_len = iter->repeat_len;
+ return opus_extension_iterator_next(iter, ext);
+ }
+ else if (id > 2) {
+ /* Update the stopping point for repeating extension data.
+ This lets us detect when we have hit the last repeated extension,
+ for purposes of modifying the L flag if it is a long extension.
+ Only extensions which can have a non-empty payload count, as we can
+ still use L=0 to code a final long extension if there cannot be
+ any more payload data, even if there are more short L=0
+ extensions (or padding). */
+ if (L || id >= 32) iter->repeat_data_end = iter->curr_data;
if (ext != NULL) {
ext->id = id;
ext->frame = iter->curr_frame;
@@ -170,25 +304,47 @@
}
}
-/* Count the number of extensions, excluding real padding and separators. */
-opus_int32 opus_packet_extensions_count(const unsigned char *data, opus_int32 len)
+/* Count the number of extensions, excluding real padding, separators, and
+ repeat indicators, but including the repeated extensions. */
+opus_int32 opus_packet_extensions_count(const unsigned char *data,
+ opus_int32 len, int nb_frames)
{
OpusExtensionIterator iter;
int count;
- opus_extension_iterator_init(&iter, data, len);
+ opus_extension_iterator_init(&iter, data, len, nb_frames);
for (count=0; opus_extension_iterator_next(&iter, NULL) > 0; count++);
return count;
}
-/* Extract extensions from Opus padding (excluding real padding and separators) */
-opus_int32 opus_packet_extensions_parse(const unsigned char *data, opus_int32 len, opus_extension_data *extensions, opus_int32 *nb_extensions)
-{
+/* Count the number of extensions for each frame, excluding real padding and
+ separators and repeat indicators, but including the repeated extensions. */
+opus_int32 opus_packet_extensions_count_ext(const unsigned char *data,
+ opus_int32 len, opus_int32 *nb_frame_exts, int nb_frames) {
OpusExtensionIterator iter;
+ opus_extension_data ext;
int count;
+ opus_extension_iterator_init(&iter, data, len, nb_frames);
+ OPUS_CLEAR(nb_frame_exts, nb_frames);
+ for (count=0; opus_extension_iterator_next(&iter, &ext) > 0; count++) {
+ nb_frame_exts[ext.frame]++;
+ }
+ return count;
+}
+
+/* Extract extensions from Opus padding (excluding real padding, separators,
+ and repeat indicators, but including the repeated extensions) in bitstream
+ order.
+ Due to the extension repetition mechanism, extensions are not necessarily
+ returned in frame order. */
+opus_int32 opus_packet_extensions_parse(const unsigned char *data,
+ opus_int32 len, opus_extension_data *extensions, opus_int32 *nb_extensions,
+ int nb_frames) {
+ OpusExtensionIterator iter;
+ int count;
int ret;
celt_assert(nb_extensions != NULL);
celt_assert(extensions != NULL || *nb_extensions == 0);
- opus_extension_iterator_init(&iter, data, len);
+ opus_extension_iterator_init(&iter, data, len, nb_frames);
for (count=0;; count++) {
opus_extension_data ext;
ret = opus_extension_iterator_next(&iter, &ext);
@@ -202,6 +358,48 @@
return ret;
}
+/* Extract extensions from Opus padding (excluding real padding, separators,
+ and repeat indicators, but including the repeated extensions) in frame
+ order.
+ nb_frame_exts must be filled with the output of
+ opus_packet_extensions_count_ext(). */
+opus_int32 opus_packet_extensions_parse_ext(const unsigned char *data,
+ opus_int32 len, opus_extension_data *extensions, opus_int32 *nb_extensions,
+ const opus_int32 *nb_frame_exts, int nb_frames) {
+ OpusExtensionIterator iter;
+ opus_extension_data ext;
+ opus_int32 nb_frames_cum[49];
+ int count;
+ int prev_total;
+ int ret;
+ celt_assert(nb_extensions != NULL);
+ celt_assert(extensions != NULL || *nb_extensions == 0);
+ celt_assert(nb_frames <= 48);
+ /* Convert the frame extension count array to a cumulative sum. */
+ prev_total = 0;
+ for (count=0; count<nb_frames; count++) {
+ int total;
+ total = nb_frame_exts[count] + prev_total;
+ nb_frames_cum[count] = prev_total;
+ prev_total = total;
+ }
+ nb_frames_cum[count] = prev_total;
+ opus_extension_iterator_init(&iter, data, len, nb_frames);
+ for (count=0;; count++) {
+ opus_int32 idx;
+ ret = opus_extension_iterator_next(&iter, &ext);
+ if (ret <= 0) break;
+ idx = nb_frames_cum[ext.frame]++;
+ if (idx >= *nb_extensions) {
+ return OPUS_BUFFER_TOO_SMALL;
+ }
+ celt_assert(idx < nb_frames_cum[ext.frame + 1]);
+ extensions[idx] = ext;
+ }
+ *nb_extensions = count;
+ return ret;
+}
+
opus_int32 opus_packet_extensions_generate(unsigned char *data, opus_int32 len, const opus_extension_data *extensions, opus_int32 nb_extensions, int pad)
{
int max_frame=0;
@@ -216,7 +414,7 @@
for (i=0;i<nb_extensions;i++)
{
max_frame = IMAX(max_frame, extensions[i].frame);
- if (extensions[i].id < 2 || extensions[i].id > 127)
+ if (extensions[i].id < 3 || extensions[i].id > 127)
return OPUS_BAD_ARG;
}
if (max_frame >= 48) return OPUS_BAD_ARG;
--- a/src/opus_decoder.c
+++ b/src/opus_decoder.c
@@ -1314,13 +1314,11 @@
return ret;
nb_frames = ret;
frame_size = opus_packet_get_samples_per_frame(data, 48000);
- opus_extension_iterator_init(&iter, padding, padding_len);
+ opus_extension_iterator_init(&iter, padding, padding_len, nb_frames);
for (;;) {
ret = opus_extension_iterator_find(&iter, &ext, DRED_EXTENSION_ID);
if (ret <= 0)
return ret;
- if (ext.frame >= nb_frames)
- break;
/* DRED position in the packet, in units of 2.5 ms like for the signaled DRED offset. */
*dred_frame_offset = ext.frame*frame_size/120;
#ifdef DRED_EXPERIMENTAL_VERSION
--- a/src/opus_private.h
+++ b/src/opus_private.h
@@ -44,14 +44,24 @@
int framesize;
const unsigned char *paddings[48];
opus_int32 padding_len[48];
+ unsigned char padding_nb_frames[48];
};
typedef struct OpusExtensionIterator {
const unsigned char *data;
const unsigned char *curr_data;
+ const unsigned char *repeat_data;
+ const unsigned char *repeat_data_end;
+ const unsigned char *src_data;
opus_int32 len;
opus_int32 curr_len;
+ opus_int32 repeat_len;
+ opus_int32 src_len;
+ int nb_frames;
+ int frame_max;
int curr_frame;
+ int repeat_frame;
+ unsigned char repeat_l;
} OpusExtensionIterator;
typedef struct {
@@ -62,9 +72,11 @@
} opus_extension_data;
void opus_extension_iterator_init(OpusExtensionIterator *iter,
- const unsigned char *data, opus_int32 len);
+ const unsigned char *data, opus_int32 len, opus_int32 nb_frames);
void opus_extension_iterator_reset(OpusExtensionIterator *iter);
+void opus_extension_iterator_set_frame_max(OpusExtensionIterator *iter,
+ int frame_max);
int opus_extension_iterator_next(OpusExtensionIterator *iter,
opus_extension_data *ext);
int opus_extension_iterator_find(OpusExtensionIterator *iter,
@@ -226,11 +238,21 @@
void *user_data
);
-opus_int32 opus_packet_extensions_parse(const unsigned char *data, opus_int32 len, opus_extension_data *extensions, opus_int32 *nb_extensions);
+opus_int32 opus_packet_extensions_parse(const unsigned char *data,
+ opus_int32 len, opus_extension_data *extensions, opus_int32 *nb_extensions,
+ int nb_frames);
+opus_int32 opus_packet_extensions_parse_ext(const unsigned char *data,
+ opus_int32 len, opus_extension_data *extensions, opus_int32 *nb_extensions,
+ const opus_int32 *nb_frame_exts, int nb_frames);
+
opus_int32 opus_packet_extensions_generate(unsigned char *data, opus_int32 len, const opus_extension_data *extensions, opus_int32 nb_extensions, int pad);
-opus_int32 opus_packet_extensions_count(const unsigned char *data, opus_int32 len);
+opus_int32 opus_packet_extensions_count(const unsigned char *data,
+ opus_int32 len, int nb_frames);
+
+opus_int32 opus_packet_extensions_count_ext(const unsigned char *data,
+ opus_int32 len, opus_int32 *nb_frame_exts, int nb_frames);
opus_int32 opus_packet_pad_impl(unsigned char *data, opus_int32 len, opus_int32 new_len, int pad, const opus_extension_data *extensions, int nb_extensions);
--- a/src/repacketizer.c
+++ b/src/repacketizer.c
@@ -86,6 +86,7 @@
ret=opus_packet_parse_impl(data, len, self_delimited, &tmp_toc, &rp->frames[rp->nb_frames], &rp->len[rp->nb_frames],
NULL, NULL, &rp->paddings[rp->nb_frames], &rp->padding_len[rp->nb_frames]);
if(ret<1)return ret;
+ rp->padding_nb_frames[rp->nb_frames]=ret;
/* set padding length to zero for all but the first frame */
while (curr_nb_frames > 1)
@@ -92,6 +93,7 @@
{
rp->nb_frames++;
rp->padding_len[rp->nb_frames] = 0;
+ rp->padding_nb_frames[rp->nb_frames] = 0;
rp->paddings[rp->nb_frames] = NULL;
curr_nb_frames--;
}
@@ -142,7 +144,8 @@
total_ext_count = nb_extensions;
for (i=begin;i<end;i++)
{
- int n = opus_packet_extensions_count(rp->paddings[i], rp->padding_len[i]);
+ int n = opus_packet_extensions_count(rp->paddings[i], rp->padding_len[i],
+ rp->padding_nb_frames[i]);
if (n > 0) total_ext_count += n;
}
ALLOC(all_extensions, total_ext_count ? total_ext_count : ALLOC_NONE, opus_extension_data);
@@ -159,7 +162,7 @@
opus_int32 frame_ext_count;
frame_ext_count = total_ext_count - ext_count;
int ret = opus_packet_extensions_parse(rp->paddings[i], rp->padding_len[i],
- &all_extensions[ext_count], &frame_ext_count);
+ &all_extensions[ext_count], &frame_ext_count, rp->padding_nb_frames[i]);
if (ret<0)
{
RESTORE_STACK;
--- a/tests/test_opus_extensions.c
+++ b/tests/test_opus_extensions.c
@@ -46,10 +46,10 @@
void test_extensions_generate_success(void)
{
static const opus_extension_data ext[] = {
- {2, 0, (const unsigned char *)"a", 1},
+ {3, 0, (const unsigned char *)"a", 1},
{32, 10, (const unsigned char *)"DRED", 4},
{33, 1, (const unsigned char *)"NOT DRED", 8},
- {3, 4, (const unsigned char *)NULL, 0}
+ {4, 4, (const unsigned char *)NULL, 0}
};
int result;
@@ -62,8 +62,8 @@
expect_true(p[0] == 1 && p[1] == 1 && p[2] == 1 && p[3] == 1, "expected padding");
p += 4;
- /* extension ID=2 */
- expect_true((p[0] >> 1) == 2, "expected extension id 2");
+ /* extension ID=3 */
+ expect_true((p[0] >> 1) == 3, "expected extension id 3");
/* For extension IDs 1 through 31, L=0 means that no data follows the
extension, whereas L=1 means that exactly one byte of extension data follows. */
expect_true((p[0] & 0x01) == 1, "expected L-bit set");
@@ -90,8 +90,8 @@
expect_true(p[0] == 0x03, "bad frame separator");
expect_true(p[1] == 0x03, "bad frame increment");
p += 2;
- /* extension ID=3 */
- expect_true((p[0] >> 1) == 3, "expected extension id 3");
+ /* extension ID=4 */
+ expect_true((p[0] >> 1) == 4, "expected extension id 4");
/* For extension IDs 1 through 31, L=0 means that no data follows the
extension, whereas L=1 means that exactly one byte of extension data follows. */
expect_true((p[0] & 0x01) == 0, "expected L-bit unset");
@@ -124,10 +124,10 @@
void test_extensions_generate_no_padding(void)
{
static const opus_extension_data ext[] = {
- {2, 0, (const unsigned char *)"a", 1},
+ {3, 0, (const unsigned char *)"a", 1},
{32, 10, (const unsigned char *)"DRED", 4},
{33, 1, (const unsigned char *)"NOT DRED", 8},
- {3, 4, (const unsigned char *)NULL, 0}
+ {4, 4, (const unsigned char *)NULL, 0}
};
int result;
@@ -139,10 +139,10 @@
void test_extensions_generate_fail(void)
{
static const opus_extension_data ext[] = {
- {2, 0, (const unsigned char *)"a", 1},
+ {3, 0, (const unsigned char *)"a", 1},
{32, 10, (const unsigned char *)"DRED", 4},
{33, 1, (const unsigned char *)"NOT DRED", 8},
- {3, 4, (const unsigned char *)NULL, 0}
+ {4, 4, (const unsigned char *)NULL, 0}
};
int result;
@@ -180,7 +180,7 @@
/* invalid id */
{
static const opus_extension_data id_too_small[] = {
- {1, 0, (const unsigned char *)"a", 1},
+ {2, 0, (const unsigned char *)"a", 1},
};
result = opus_packet_extensions_generate(packet, sizeof(packet), id_too_small, 1, 1);
expect_true(result == OPUS_BAD_ARG, "expected OPUS_BAD_ARG");
@@ -198,7 +198,7 @@
/* size too big for extension IDs 1 through 31 */
{
static const opus_extension_data size_too_big[] = {
- {2, 0, (const unsigned char *)"abcd", 4},
+ {3, 0, (const unsigned char *)"abcd", 4},
};
result = opus_packet_extensions_generate(packet, sizeof(packet), size_too_big, 1, 1);
expect_true(result == OPUS_BAD_ARG, "expected OPUS_BAD_ARG");
@@ -207,7 +207,7 @@
/* negative size for extension IDs 1 through 31 */
{
static const opus_extension_data neg_size[] = {
- {2, 0, NULL, -4},
+ {3, 0, NULL, -4},
};
result = opus_packet_extensions_generate(packet, sizeof(packet), neg_size, 1, 1);
expect_true(result == OPUS_BAD_ARG, "expected OPUS_BAD_ARG");
@@ -226,25 +226,28 @@
void test_extensions_parse_success(void)
{
static const opus_extension_data ext[] = {
- {2, 0, (const unsigned char *)"a", 1},
+ {3, 0, (const unsigned char *)"a", 1},
{32, 10, (const unsigned char *)"DRED", 4},
{33, 1, (const unsigned char *)"NOT DRED", 8},
- {3, 4, (const unsigned char *)NULL, 0}
+ {4, 4, (const unsigned char *)NULL, 0}
};
opus_extension_data ext_out[10];
int nb_ext;
+ int nb_frames;
int len, result;
unsigned char packet[32];
nb_ext = 10;
+ nb_frames = 11;
len = opus_packet_extensions_generate(packet, 32, ext, 4, 1);
expect_true(len == 32, "expected length 32");
- result = opus_packet_extensions_count(packet, len);
+ result = opus_packet_extensions_count(packet, len, nb_frames);
expect_true(result == 4, "expected opus_packet_extensions_count 4");
- result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext);
+ result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext,
+ nb_frames);
expect_true(nb_ext == 4, "expected 4 extensions");
- expect_true(ext_out[0].id == 2, "expected id 2");
+ expect_true(ext_out[0].id == 3, "expected id 3");
expect_true(ext_out[0].frame == 0, "expected frame 0");
expect_true(ext_out[0].len == 1, "expected len 1");
expect_true(0 == memcmp(ext_out[0].data, ext[0].data, 1), "expected data");
@@ -254,7 +257,7 @@
expect_true(ext_out[1].len == 8, "expected len 8");
expect_true(0 == memcmp(ext_out[1].data, ext[2].data, 8), "expected data");
- expect_true(ext_out[2].id == 3, "expected id 3");
+ expect_true(ext_out[2].id == 4, "expected id 4");
expect_true(ext_out[2].frame == 4, "expected frame 4");
expect_true(ext_out[2].len == 0, "expected len 0");
@@ -270,6 +273,7 @@
{32, 1, (const unsigned char *)"DRED", 4},
};
int nb_ext;
+ int nb_frames;
int len, result;
unsigned char packet[32];
@@ -277,7 +281,8 @@
expect_true(len == 32, "expected length 32");
nb_ext = 0;
- result = opus_packet_extensions_parse(packet, len, NULL, &nb_ext);
+ nb_frames = 2;
+ result = opus_packet_extensions_parse(packet, len, NULL, &nb_ext, nb_frames);
expect_true(result == OPUS_BUFFER_TOO_SMALL, "expected OPUS_BUFFER_TOO_SMALL");
}
@@ -284,13 +289,14 @@
void test_extensions_parse_fail(void)
{
static const opus_extension_data ext[] = {
- {2, 0, (const unsigned char *)"a", 1},
+ {3, 0, (const unsigned char *)"a", 1},
{33, 1, (const unsigned char *)"NOT DRED", 8},
- {3, 4, (const unsigned char *)NULL, 0},
+ {4, 4, (const unsigned char *)NULL, 0},
{32, 10, (const unsigned char *)"DRED", 4}
};
opus_extension_data ext_out[10];
int nb_ext;
+ int nb_frames;
int len, result;
unsigned char packet[32];
@@ -298,28 +304,43 @@
len = opus_packet_extensions_generate(packet, sizeof(packet), ext, 4, 0);
packet[4] = 255;
nb_ext = 10;
- result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext);
+ nb_frames = 11;
+ result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext,
+ nb_frames);
expect_true(result == OPUS_INVALID_PACKET, "expected OPUS_INVALID_PACKET");
/* note, opus_packet_extensions_count stops at the invalid frame increment
and tells us that we have 1 extension */
- result = opus_packet_extensions_count(packet, len);
+ result = opus_packet_extensions_count(packet, len, nb_frames);
expect_true(result == 1, "expected opus_packet_extensions_count to return 1");
/* create invalid frame increment */
nb_ext = 10;
len = opus_packet_extensions_generate(packet, sizeof(packet), ext, 4, 0);
+ /* first by reducing the number of frames */
+ result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext, 5);
+ expect_true(result == OPUS_INVALID_PACKET, "expected OPUS_INVALID_PACKET");
+
+ /* note, opus_packet_extensions_count stops at the invalid frame increment
+ and tells us that we have 3 extensions */
+ result = opus_packet_extensions_count(packet, len, 5);
+ expect_true(result == 3, "expected opus_packet_extensions_count to return 3");
+
+ /* then by increasing the increment */
packet[14] = 255;
- result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext);
+ result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext,
+ nb_frames);
expect_true(result == OPUS_INVALID_PACKET, "expected OPUS_INVALID_PACKET");
+
/* note, opus_packet_extensions_count stops at the invalid frame increment
and tells us that we have 2 extensions */
- result = opus_packet_extensions_count(packet, len);
+ result = opus_packet_extensions_count(packet, len, nb_frames);
expect_true(result == 2, "expected opus_packet_extensions_count to return 2");
/* not enough space */
nb_ext = 1;
len = opus_packet_extensions_generate(packet, sizeof(packet), ext, 4, 0);
- result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext);
+ result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext,
+ nb_frames);
expect_true(result == OPUS_BUFFER_TOO_SMALL, "expected OPUS_BUFFER_TOO_SMALL");
/* overflow for long extension length */
@@ -331,7 +352,7 @@
buf[0] = 33<<1 | 1;
memset(buf + 1, 0xFF, LENSIZE - 1);
buf[LENSIZE] = 0xFE;
- result = opus_packet_extensions_parse(buf, len, ext_out, &nb_ext);
+ result = opus_packet_extensions_parse(buf, len, ext_out, &nb_ext, 1);
expect_true(result == OPUS_INVALID_PACKET, "expected OPUS_INVALID_PACKET");
free(buf);
}
@@ -339,7 +360,9 @@
#define NB_RANDOM_EXTENSIONS 100000000
#define MAX_EXTENSION_SIZE 200
-#define MAX_NB_EXTENSIONS 100
+/* The worst case is a 48-frame packet filled entirely with 0-byte short
+ extensions followed by an RTE. */
+#define MAX_NB_EXTENSIONS ((MAX_EXTENSION_SIZE - 1)*48)
void test_random_extensions_parse(void)
{
@@ -356,7 +379,7 @@
for (j=0;j<len;j++)
payload[j] = fast_rand()&0xFF;
nb_ext = fast_rand()%(MAX_NB_EXTENSIONS+1);
- result = opus_packet_extensions_parse(payload, len, ext_out, &nb_ext);
+ result = opus_packet_extensions_parse(payload, len, ext_out, &nb_ext, 48);
expect_true(result == OPUS_OK || result == OPUS_BUFFER_TOO_SMALL || result == OPUS_INVALID_PACKET, "expected OPUS_OK, OPUS_BUFFER_TOO_SMALL or OPUS_INVALID_PACKET");
/* Even if parsing fails, check that the extensions that got extracted make sense. */
for (j=0;j<nb_ext;j++)
@@ -425,7 +448,8 @@
res = opus_packet_parse_impl(packet_out, res, 0, NULL, NULL, size,
NULL, NULL, &padding, &padding_len);
nb_ext = 10;
- res = opus_packet_extensions_parse(padding, padding_len, ext_out, &nb_ext);
+ res = opus_packet_extensions_parse(padding, padding_len, ext_out, &nb_ext,
+ 3);
expect_true(nb_ext == 4, "Expected 4 extensions");
for (i = 0 ; i < nb_ext; i++)
{
--
⑨