Skip to content

Commit 6dabc64

Browse files
author
Timothy B. Terriberry
committed
Add extension iterator.
Rather than repeating the code to iterate through extensions in three different places, each with slight differences, different edge cases, different error handling, etc., create an iterator that can be used everywhere.
1 parent a73caa7 commit 6dabc64

File tree

4 files changed

+145
-130
lines changed

4 files changed

+145
-130
lines changed

src/extensions.c

Lines changed: 93 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@
3838

3939
/* Given an extension payload, advance data to the next extension and return the
4040
length of the remaining extensions. */
41-
opus_int32 skip_extension(const unsigned char **data, opus_int32 len, opus_int32 *header_size)
41+
static opus_int32 skip_extension(const unsigned char **data, opus_int32 len,
42+
opus_int32 *header_size)
4243
{
4344
int id, L;
4445
if (len==0)
@@ -91,95 +92,114 @@ opus_int32 skip_extension(const unsigned char **data, opus_int32 len, opus_int32
9192
}
9293
}
9394

94-
/* Count the number of extensions, excluding real padding and separators. */
95-
opus_int32 opus_packet_extensions_count(const unsigned char *data, opus_int32 len)
96-
{
97-
opus_int32 curr_len;
98-
opus_int32 count=0;
99-
const unsigned char *curr_data = data;
100-
95+
void opus_extension_iterator_init(OpusExtensionIterator *iter,
96+
const unsigned char *data, opus_int32 len) {
10197
celt_assert(len >= 0);
10298
celt_assert(data != NULL || len == 0);
99+
iter->curr_data = iter->data = data;
100+
iter->curr_len = iter->len = len;
101+
iter->curr_frame = 0;
102+
}
103103

104-
curr_len = len;
105-
while (curr_len > 0)
106-
{
104+
/* Reset the iterator so it can start iterating again from the first
105+
extension. */
106+
void opus_extension_iterator_reset(OpusExtensionIterator *iter) {
107+
iter->curr_data = iter->data;
108+
iter->curr_len = iter->len;
109+
iter->curr_frame = 0;
110+
}
111+
112+
/* Return the next extension (excluding real padding and separators). */
113+
int opus_extension_iterator_next(OpusExtensionIterator *iter,
114+
opus_extension_data *ext) {
115+
opus_int32 header_size;
116+
if (iter->curr_len < 0) {
117+
return OPUS_INVALID_PACKET;
118+
}
119+
while (iter->curr_len > 0) {
120+
const unsigned char *curr_data0;
107121
int id;
108-
opus_int32 header_size;
109-
id = *curr_data>>1;
110-
curr_len = skip_extension(&curr_data, curr_len, &header_size);
111-
if (curr_len < 0)
122+
int L;
123+
curr_data0 = iter->curr_data;
124+
id = *curr_data0>>1;
125+
L = *curr_data0&1;
126+
iter->curr_len = skip_extension(&iter->curr_data, iter->curr_len,
127+
&header_size);
128+
if (iter->curr_len < 0) {
112129
return OPUS_INVALID_PACKET;
113-
if (id > 1)
114-
count++;
130+
}
131+
celt_assert(iter->curr_data - iter->data == iter->len - iter->curr_len);
132+
if (id == 1) {
133+
if (L == 0) {
134+
iter->curr_frame++;
135+
}
136+
else {
137+
iter->curr_frame += curr_data0[1];
138+
}
139+
if (iter->curr_frame >= 48) {
140+
iter->curr_len = -1;
141+
return OPUS_INVALID_PACKET;
142+
}
143+
}
144+
else if (id > 1) {
145+
if (ext != NULL) {
146+
ext->id = id;
147+
ext->frame = iter->curr_frame;
148+
ext->data = curr_data0 + header_size;
149+
ext->len = iter->curr_data - curr_data0 - header_size;
150+
}
151+
return 1;
152+
}
115153
}
154+
return 0;
155+
}
156+
157+
int opus_extension_iterator_find(OpusExtensionIterator *iter,
158+
opus_extension_data *ext, int id) {
159+
opus_extension_data curr_ext;
160+
int ret;
161+
for(;;) {
162+
ret = opus_extension_iterator_next(iter, &curr_ext);
163+
if (ret <= 0) {
164+
return ret;
165+
}
166+
if (curr_ext.id == id) {
167+
*ext = curr_ext;
168+
return ret;
169+
}
170+
}
171+
}
172+
173+
/* Count the number of extensions, excluding real padding and separators. */
174+
opus_int32 opus_packet_extensions_count(const unsigned char *data, opus_int32 len)
175+
{
176+
OpusExtensionIterator iter;
177+
int count;
178+
opus_extension_iterator_init(&iter, data, len);
179+
for (count=0; opus_extension_iterator_next(&iter, NULL) > 0; count++);
116180
return count;
117181
}
118182

119183
/* Extract extensions from Opus padding (excluding real padding and separators) */
120184
opus_int32 opus_packet_extensions_parse(const unsigned char *data, opus_int32 len, opus_extension_data *extensions, opus_int32 *nb_extensions)
121185
{
122-
const unsigned char *curr_data;
123-
opus_int32 curr_len;
124-
int curr_frame=0;
125-
opus_int32 count=0;
126-
127-
celt_assert(len >= 0);
128-
celt_assert(data != NULL || len == 0);
186+
OpusExtensionIterator iter;
187+
int count;
188+
int ret;
129189
celt_assert(nb_extensions != NULL);
130190
celt_assert(extensions != NULL || *nb_extensions == 0);
131-
132-
curr_data = data;
133-
curr_len = len;
134-
while (curr_len > 0)
135-
{
136-
int id;
137-
opus_int32 header_size;
138-
opus_extension_data curr_ext;
139-
id = *curr_data>>1;
140-
if (id > 1)
141-
{
142-
curr_ext.id = id;
143-
curr_ext.frame = curr_frame;
144-
curr_ext.data = curr_data;
145-
} else if (id == 1)
146-
{
147-
int L = *curr_data&1;
148-
if (L==0)
149-
curr_frame++;
150-
else {
151-
if (curr_len >= 2)
152-
curr_frame += curr_data[1];
153-
/* Else we're at the end and it doesn't matter. */
154-
}
155-
if (curr_frame >= 48)
156-
{
157-
*nb_extensions = count;
158-
return OPUS_INVALID_PACKET;
159-
}
160-
}
161-
curr_len = skip_extension(&curr_data, curr_len, &header_size);
162-
/* printf("curr_len = %d, header_size = %d\n", curr_len, header_size); */
163-
if (curr_len < 0)
164-
{
165-
*nb_extensions = count;
166-
return OPUS_INVALID_PACKET;
167-
}
168-
celt_assert(curr_data - data == len - curr_len);
169-
if (id > 1)
170-
{
171-
if (count == *nb_extensions)
172-
{
173-
return OPUS_BUFFER_TOO_SMALL;
174-
}
175-
curr_ext.len = curr_data - curr_ext.data - header_size;
176-
curr_ext.data += header_size;
177-
extensions[count++] = curr_ext;
191+
opus_extension_iterator_init(&iter, data, len);
192+
for (count=0;; count++) {
193+
opus_extension_data ext;
194+
ret = opus_extension_iterator_next(&iter, &ext);
195+
if (ret <= 0) break;
196+
if (count == *nb_extensions) {
197+
return OPUS_BUFFER_TOO_SMALL;
178198
}
199+
extensions[count] = ext;
179200
}
180-
celt_assert(curr_len == 0);
181201
*nb_extensions = count;
182-
return OPUS_OK;
202+
return ret;
183203
}
184204

185205
opus_int32 opus_packet_extensions_generate(unsigned char *data, opus_int32 len, const opus_extension_data *extensions, opus_int32 nb_extensions, int pad)

src/opus_decoder.c

Lines changed: 29 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1296,69 +1296,48 @@ int opus_dred_decoder_ctl(OpusDREDDecoder *dred_dec, int request, ...)
12961296
#ifdef ENABLE_DRED
12971297
static int dred_find_payload(const unsigned char *data, opus_int32 len, const unsigned char **payload, int *dred_frame_offset)
12981298
{
1299-
const unsigned char *data0;
1300-
opus_int32 len0;
1301-
int frame = 0;
1299+
OpusExtensionIterator iter;
1300+
opus_extension_data ext;
1301+
const unsigned char *padding;
1302+
opus_int32 padding_len;
13021303
int nb_frames;
13031304
const unsigned char *frames[48];
13041305
opus_int16 size[48];
13051306
int frame_size;
1307+
int ret;
13061308

13071309
*payload = NULL;
13081310
/* Get the padding section of the packet. */
1309-
nb_frames = opus_packet_parse_impl(data, len, 0, NULL, frames, size, NULL, NULL, &data0, &len0);
1310-
if (nb_frames < 0)
1311-
return nb_frames;
1311+
ret = opus_packet_parse_impl(data, len, 0, NULL, frames, size, NULL, NULL,
1312+
&padding, &padding_len);
1313+
if (ret < 0)
1314+
return ret;
1315+
nb_frames = ret;
13121316
frame_size = opus_packet_get_samples_per_frame(data, 48000);
1313-
data = data0;
1314-
len = len0;
1315-
/* Scan extensions in order until we find the earliest frame with DRED data. */
1316-
while (len > 0)
1317-
{
1318-
opus_int32 header_size;
1319-
int id, L;
1320-
len0 = len;
1321-
data0 = data;
1322-
id = *data0 >> 1;
1323-
L = *data0 & 0x1;
1324-
len = skip_extension(&data, len, &header_size);
1325-
if (len < 0)
1317+
opus_extension_iterator_init(&iter, padding, padding_len);
1318+
for (;;) {
1319+
ret = opus_extension_iterator_find(&iter, &ext, DRED_EXTENSION_ID);
1320+
if (ret <= 0)
1321+
return ret;
1322+
if (ext.frame >= nb_frames)
13261323
break;
1327-
if (id == 1)
1328-
{
1329-
if (L==0)
1330-
{
1331-
frame++;
1332-
} else {
1333-
frame += data0[1];
1334-
}
1335-
if (frame >= nb_frames) {
1336-
break;
1337-
}
1338-
} else if (id == DRED_EXTENSION_ID)
1339-
{
1340-
const unsigned char *curr_payload;
1341-
opus_int32 curr_payload_len;
1342-
curr_payload = data0+header_size;
1343-
curr_payload_len = (data-data0)-header_size;
1344-
/* DRED position in the packet, in units of 2.5 ms like for the signaled DRED offset. */
1345-
*dred_frame_offset = frame*frame_size/120;
1324+
/* DRED position in the packet, in units of 2.5 ms like for the signaled DRED offset. */
1325+
*dred_frame_offset = ext.frame*frame_size/120;
13461326
#ifdef DRED_EXPERIMENTAL_VERSION
1347-
/* Check that temporary extension type and version match.
1348-
This check will be removed once extension is finalized. */
1349-
if (curr_payload_len > DRED_EXPERIMENTAL_BYTES && curr_payload[0] == 'D' && curr_payload[1] == DRED_EXPERIMENTAL_VERSION) {
1350-
*payload = curr_payload+2;
1351-
return curr_payload_len-2;
1352-
}
1327+
/* Check that temporary extension type and version match.
1328+
This check will be removed once extension is finalized. */
1329+
if (ext.len > DRED_EXPERIMENTAL_BYTES && ext.data[0] == 'D'
1330+
&& ext.data[1] == DRED_EXPERIMENTAL_VERSION) {
1331+
*payload = ext.data+2;
1332+
return ext.len-2;
1333+
}
13531334
#else
1354-
if (curr_payload_len > 0) {
1355-
*payload = curr_payload;
1356-
return curr_payload_len;
1357-
}
1358-
#endif
1335+
if (ext.len > 0) {
1336+
*payload = ext.data;
1337+
return ext.len;
13591338
}
1339+
#endif
13601340
}
1361-
return 0;
13621341
}
13631342
#endif
13641343

src/opus_private.h

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,30 @@ struct OpusRepacketizer {
4646
opus_int32 padding_len[48];
4747
};
4848

49+
typedef struct OpusExtensionIterator {
50+
const unsigned char *data;
51+
const unsigned char *curr_data;
52+
opus_int32 len;
53+
opus_int32 curr_len;
54+
int curr_frame;
55+
} OpusExtensionIterator;
56+
4957
typedef struct {
5058
int id;
5159
int frame;
5260
const unsigned char *data;
5361
opus_int32 len;
5462
} opus_extension_data;
5563

64+
void opus_extension_iterator_init(OpusExtensionIterator *iter,
65+
const unsigned char *data, opus_int32 len);
66+
67+
void opus_extension_iterator_reset(OpusExtensionIterator *iter);
68+
int opus_extension_iterator_next(OpusExtensionIterator *iter,
69+
opus_extension_data *ext);
70+
int opus_extension_iterator_find(OpusExtensionIterator *iter,
71+
opus_extension_data *ext, int id);
72+
5673
typedef struct ChannelLayout {
5774
int nb_channels;
5875
int nb_streams;
@@ -171,9 +188,6 @@ static OPUS_INLINE int align(int i)
171188
return ((i + alignment - 1) / alignment) * alignment;
172189
}
173190

174-
/* More than that is ridiculous for now (3 * max frames per packet)*/
175-
opus_int32 skip_extension(const unsigned char **data, opus_int32 len, opus_int32 *header_size);
176-
177191
int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
178192
int self_delimited, unsigned char *out_toc,
179193
const unsigned char *frames[48], opus_int16 size[48],

tests/test_opus_extensions.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,19 +300,21 @@ void test_extensions_parse_fail(void)
300300
nb_ext = 10;
301301
result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext);
302302
expect_true(result == OPUS_INVALID_PACKET, "expected OPUS_INVALID_PACKET");
303+
/* note, opus_packet_extensions_count stops at the invalid frame increment
304+
and tells us that we have 1 extension */
303305
result = opus_packet_extensions_count(packet, len);
304-
expect_true(result == OPUS_INVALID_PACKET, "expected OPUS_INVALID_PACKET");
306+
expect_true(result == 1, "expected opus_packet_extensions_count to return 1");
305307

306308
/* create invalid frame increment */
307309
nb_ext = 10;
308310
len = opus_packet_extensions_generate(packet, sizeof(packet), ext, 4, 0);
309311
packet[14] = 255;
310312
result = opus_packet_extensions_parse(packet, len, ext_out, &nb_ext);
311313
expect_true(result == OPUS_INVALID_PACKET, "expected OPUS_INVALID_PACKET");
312-
/* note, opus_packet_extensions_count does not read the invalid frame increment
313-
and tells us that we have 4 extensions */
314+
/* note, opus_packet_extensions_count stops at the invalid frame increment
315+
and tells us that we have 2 extensions */
314316
result = opus_packet_extensions_count(packet, len);
315-
expect_true(result == 4, "expected opus_packet_extensions_count to return 4");
317+
expect_true(result == 2, "expected opus_packet_extensions_count to return 2");
316318

317319
/* not enough space */
318320
nb_ext = 1;

0 commit comments

Comments
 (0)