/* Given an extension payload (i.e., excluding the initial ID byte), advance datatothenextextensionandreturnthelengthoftheremaining extensions. N.B.,a"RepeatTheseExtensions"extension(ID==2)doesnotadvancepast therepeatedextensionpayloads.
That requires higher-level logic. */ static opus_int32 skip_extension_payload(constunsignedchar **pdata,
opus_int32 len, opus_int32 *pheader_size, int id_byte,
opus_int32 trailing_short_len)
{ constunsignedchar *data;
opus_int32 header_size; int id, L;
data = *pdata;
header_size = 0;
id = id_byte>>1;
L = id_byte&1; if ((id == 0 && L == 1) || id == 2)
{ /* Nothing to do. */
} elseif (id > 0 && id < 32)
{ if (len < L) return -1;
data += L;
len -= L;
} else { if (L==0)
{ if (len < trailing_short_len) return -1;
data += len - trailing_short_len;
len = trailing_short_len;
} else {
opus_int32 bytes=0;
opus_int32 lacing; do { if (len < 1) return -1;
lacing = *data++;
bytes += lacing;
header_size++;
len -= lacing + 1;
} while (lacing == 255); if (len < 0) return -1;
data += bytes;
}
}
*pdata = data;
*pheader_size = header_size; return len;
}
/* Given an extension, advance data to the next extension and return the lengthoftheremainingextensions. N.B.,a"RepeatTheseExtensions"extension(ID==2)onlyadvancespastthe extensionIDbyte. Higher-levellogicisrequiredtoskiptheextensionpayloadsthatcome
after it.*/ static opus_int32 skip_extension(constunsignedchar **pdata, opus_int32 len,
opus_int32 *pheader_size)
{ constunsignedchar *data; int id_byte; if (len == 0) {
*pheader_size = 0; return0;
} if (len < 1) return -1;
data = *pdata;
id_byte = *data++;
len--;
len = skip_extension_payload(&data, len, pheader_size, id_byte, 0); if (len >= 0) {
*pdata = data;
(*pheader_size)++;
} return len;
}
/* Reset the iterator so it can start iterating again from the first
extension. */ void opus_extension_iterator_reset(OpusExtensionIterator *iter) {
iter->repeat_data = iter->curr_data = iter->data;
iter->last_long = NULL;
iter->curr_len = iter->len;
iter->repeat_frame = iter->curr_frame = 0;
iter->trailing_short_len = 0;
}
/* Tell the iterator not to return any extensions for frames of index frame_maxorlarger. Thiscanallowittostopiteratingearlyiftheseextensionsarenot
needed. */ void opus_extension_iterator_set_frame_max(OpusExtensionIterator *iter, int frame_max) {
iter->frame_max = frame_max;
}
/* Return the next repeated extension. Thereturnvalueisnon-zeroifoneisfound,negativeonerror,or0ifwe
have finished repeating extensions. */ staticint opus_extension_iterator_next_repeat(OpusExtensionIterator *iter,
opus_extension_data *ext) {
opus_int32 header_size;
celt_assert(iter->repeat_frame > 0); for (;iter->repeat_frame < iter->nb_frames; iter->repeat_frame++) { while (iter->src_len > 0) { constunsignedchar *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 isthelastrepeatedlongextension,thenforcedecodingthe
payload with L = 0. */ if (iter->repeat_l == 0
&& iter->repeat_frame + 1 >= iter->nb_frames
&& iter->src_data == iter->last_long) {
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,
iter->trailing_short_len); 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) { 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;
} return1;
} /* 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 = iter->curr_data;
iter->last_long = NULL; /* If L == 0, advance the frame number to handle the case where we did
not consume all of the data with an L == 0 long extension. */ if (iter->repeat_l == 0) {
iter->curr_frame++; /* Ignore additional padding if this was already the last frame. */ if (iter->curr_frame >= iter->nb_frames) {
iter->curr_len = 0;
}
}
iter->repeat_frame = 0; return0;
}
/* Return the next extension (excluding real padding, separators, and repeat indicators,butincludingtherepeatedextensions)inbitstreamorder. Duetotheextensionrepetitionmechanism,extensionsarenotnecessarily
returned in frame order. */ int opus_extension_iterator_next(OpusExtensionIterator *iter,
opus_extension_data *ext) {
opus_int32 header_size; if (iter->curr_len < 0) { return OPUS_INVALID_PACKET;
} if (iter->repeat_frame > 0) { int ret; /* We are in the process of repeating some extensions. */
ret = opus_extension_iterator_next_repeat(iter, ext); if (ret) return ret;
} /* Checking this here allows opus_extension_iterator_set_frame_max() to be
called at any point. */ if (iter->curr_frame >= iter->frame_max) { return0;
} while (iter->curr_len > 0) { constunsignedchar *curr_data0; int id; int L;
curr_data0 = iter->curr_data;
id = *curr_data0>>1;
L = *curr_data0&1;
iter->curr_len = skip_extension(&iter->curr_data, iter->curr_len,
&header_size); if (iter->curr_len < 0) { return OPUS_INVALID_PACKET;
}
celt_assert(iter->curr_data - iter->data == iter->len - iter->curr_len); if (id == 1) { if (L == 0) {
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 >= 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 = iter->curr_data;
iter->last_long = NULL;
iter->trailing_short_len = 0;
} elseif (id == 2) { int ret;
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;
ret = opus_extension_iterator_next_repeat(iter, ext); if (ret) return ret;
} elseif (id > 2) { /* Update the location of the last long extension. ThisletsusknowwhenweneedtomodifythelastLflagifwe
repeat these extensions with L=0. */ if (id >= 32) {
iter->last_long = iter->curr_data;
iter->trailing_short_len = 0;
} /* Otherwise, keep track of how many payload bytes follow the last
long extension. */ else iter->trailing_short_len += L; if (ext != NULL) {
ext->id = id;
ext->frame = iter->curr_frame;
ext->data = curr_data0 + header_size;
ext->len = iter->curr_data - curr_data0 - header_size;
} return1;
}
} return0;
}
int opus_extension_iterator_find(OpusExtensionIterator *iter,
opus_extension_data *ext, int id) {
opus_extension_data curr_ext; int ret; for(;;) {
ret = opus_extension_iterator_next(iter, &curr_ext); if (ret <= 0) { return ret;
} if (curr_ext.id == id) {
*ext = curr_ext; return ret;
}
}
}
/* Count the number of extensions, excluding real padding, separators, and
repeat indicators, but including the repeated extensions. */
opus_int32 opus_packet_extensions_count(constunsignedchar *data,
opus_int32 len, int nb_frames)
{
OpusExtensionIterator iter; int count;
opus_extension_iterator_init(&iter, data, len, nb_frames); for (count=0; opus_extension_iterator_next(&iter, NULL) > 0; count++); return count;
}
/* 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(constunsignedchar *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, andrepeatindicators,butincludingtherepeatedextensions)inbitstream order. Duetotheextensionrepetitionmechanism,extensionsarenotnecessarily
returned in frame order. */
opus_int32 opus_packet_extensions_parse(constunsignedchar *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, nb_frames); for (count=0;; count++) {
opus_extension_data ext;
ret = opus_extension_iterator_next(&iter, &ext); if (ret <= 0) break; if (count == *nb_extensions) { return OPUS_BUFFER_TOO_SMALL;
}
extensions[count] = ext;
}
*nb_extensions = count; return ret;
}
/* Extract extensions from Opus padding (excluding real padding, separators, andrepeatindicators,butincludingtherepeatedextensions)inframe order. nb_frame_extsmustbefilledwiththeoutputof
opus_packet_extensions_count_ext(). */
opus_int32 opus_packet_extensions_parse_ext(constunsignedchar *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;
}
staticint write_extension_payload(unsignedchar *data, opus_int32 len,
opus_int32 pos, const opus_extension_data *ext, int last) {
celt_assert(ext->id >= 3 && ext->id <= 127); if (ext->id < 32)
{ if (ext->len < 0 || ext->len > 1) return OPUS_BAD_ARG; if (ext->len > 0) { if (len-pos < ext->len) return OPUS_BUFFER_TOO_SMALL; if (data) data[pos] = ext->data[0];
pos++;
}
} else {
opus_int32 length_bytes; if (ext->len < 0) return OPUS_BAD_ARG;
length_bytes = 1 + ext->len/255; if (last)
length_bytes = 0; if (len-pos < length_bytes + ext->len) return OPUS_BUFFER_TOO_SMALL; if (!last)
{
opus_int32 j; for (j=0;j<ext->len/255;j++) { if (data) data[pos] = 255;
pos++;
} if (data) data[pos] = ext->len % 255;
pos++;
} if (data) OPUS_COPY(&data[pos], ext->data, ext->len);
pos += ext->len;
} return pos;
}
opus_int32 opus_packet_extensions_generate(unsignedchar *data, opus_int32 len, const opus_extension_data *extensions, opus_int32 nb_extensions, int nb_frames, int pad)
{
opus_int32 frame_min_idx[48];
opus_int32 frame_max_idx[48];
opus_int32 frame_repeat_idx[48];
opus_int32 i; int f; int curr_frame = 0;
opus_int32 pos = 0;
opus_int32 written = 0;
celt_assert(len >= 0); if (nb_frames > 48) return OPUS_BAD_ARG;
/* Do a little work up-front to make this O(nb_extensions) instead of O(nb_extensions*nb_frames)solongastheextensionsareinframe
order (without requiring that they be in frame order). */ for (f=0;f<nb_frames;f++) frame_min_idx[f] = nb_extensions;
OPUS_CLEAR(frame_max_idx, nb_frames); for (i=0;i<nb_extensions;i++)
{
f = extensions[i].frame; if (f < 0 || f >= nb_frames) return OPUS_BAD_ARG; if (extensions[i].id < 3 || extensions[i].id > 127) return OPUS_BAD_ARG;
frame_min_idx[f] = IMIN(frame_min_idx[f], i);
frame_max_idx[f] = IMAX(frame_max_idx[f], i+1);
} for (f=0;f<nb_frames;f++) frame_repeat_idx[f] = frame_min_idx[f]; for (f=0;f<nb_frames;f++)
{
opus_int32 last_long_idx; int repeat_count;
repeat_count = 0;
last_long_idx = -1; if (f + 1 < nb_frames)
{ for (i=frame_min_idx[f];i<frame_max_idx[f];i++)
{ if (extensions[i].frame == f)
{ int g; /* Test if we can repeat this extension in future frames. */ for (g=f+1;g<nb_frames;g++)
{ if (frame_repeat_idx[g] >= frame_max_idx[g]) break;
celt_assert(extensions[frame_repeat_idx[g]].frame == g); if (extensions[frame_repeat_idx[g]].id != extensions[i].id)
{ break;
} if (extensions[frame_repeat_idx[g]].id < 32
&& extensions[frame_repeat_idx[g]].len
!= extensions[i].len)
{ break;
}
} if (g < nb_frames) break; /* We can! */ /* If this is a long extension, save the index of the last
instance, so we can modify its L flag. */ if (extensions[i].id >= 32) {
last_long_idx = frame_repeat_idx[nb_frames-1];
} /* Using the repeat mechanism almost always makes the encodingsmaller(oratleastnolarger). However,there'sonecasewherethatmightnotbetrue:if thelastrepeatedlongextensioninthelastframewas previouslythelastextension,butusingtherepeat mechanismmakesthatnolongertrue(becausethereareother non-repeatedextensionsinearlierframesthatmustnowbe codedafterit),andcodingitslengthrequiresmorebytes thantherepeatmechanismsaves. Thiscanonlybetrueifitslengthisatleast255bytes (althoughsometimesitrequiresevenmore). Currentlywedonotcheckforthat,andjustalwaysusethe repeatmechanismifwecan.
See git history for code that does the check. */ /* Advance the repeat pointers. */ for (g=f+1; g<nb_frames; g++)
{ int j; for (j=frame_repeat_idx[g]+1; j<frame_max_idx[g]
&& extensions[j].frame != g; j++);
frame_repeat_idx[g] = j;
}
repeat_count++; /* Point the repeat pointer for this frame to the current
extension, so we know when to trigger the repeats. */
frame_repeat_idx[f] = i;
}
}
} for (i=frame_min_idx[f];i<frame_max_idx[f];i++)
{ if (extensions[i].frame == f)
{ /* Insert separator when needed. */ if (f != curr_frame) { int diff = f - curr_frame; if (len-pos < 2) return OPUS_BUFFER_TOO_SMALL; if (diff == 1) { if (data) data[pos] = 0x02;
pos++;
} else { if (data) data[pos] = 0x03;
pos++; if (data) data[pos] = diff;
pos++;
}
curr_frame = f;
}
pos = write_extension(data, len, pos, extensions + i,
written == nb_extensions - 1); if (pos < 0) return pos;
written++;
if (repeat_count > 0 && frame_repeat_idx[f] == i) { int nb_repeated; int last; int g; /* Add the repeat indicator. */
nb_repeated = repeat_count*(nb_frames - (f + 1));
last = written + nb_repeated == nb_extensions
|| (last_long_idx < 0 && i+1 >= frame_max_idx[f]); if (len-pos < 1) return OPUS_BUFFER_TOO_SMALL; if (data) data[pos] = 0x04 + !last;
pos++; for (g=f+1;g<nb_frames;g++)
{ int j; for (j=frame_min_idx[g];j<frame_repeat_idx[g];j++)
{ if (extensions[j].frame == g)
{
pos = write_extension_payload(data, len, pos,
extensions + j, last && j == last_long_idx); if (pos < 0) return pos;
written++;
}
}
frame_min_idx[g] = j;
} if (last) curr_frame++;
}
}
}
}
celt_assert(written == nb_extensions); /* If we need to pad, just prepend 0x01 bytes. Even better would be to fill the endwithzeros,butthatrequirescheckingthatturningthelastextensioninto
an L=1 case still fits. */ if (pad && pos < len)
{
opus_int32 padding = len - pos; if (data) {
OPUS_MOVE(data+padding, data, pos); for (i=0;i<padding;i++)
data[i] = 0x01;
}
pos += padding;
} return pos;
}
#if0 #include <stdio.h> int main()
{
opus_extension_data ext[] = {{2, 0, (constunsignedchar *)"a", 1},
{32, 10, (constunsignedchar *)"DRED", 4},
{33, 1, (constunsignedchar *)"NOT DRED", 8},
{3, 4, (constunsignedchar *)NULL, 0}
};
opus_extension_data ext2[10]; int i, len; int nb_ext = 10; unsignedchar packet[10000];
len = opus_packet_extensions_generate(packet, 32, ext, 4, 1); for (i=0;i<len;i++)
{
printf("%#04x ", packet[i]); if (i%16 == 15)
printf("\n");
}
printf("\n");
printf("count = %d\n", opus_packet_extensions_count(packet, len));
opus_packet_extensions_parse(packet, len, ext2, &nb_ext); for (i=0;i<nb_ext;i++)
{ int j;
printf("%d %d {", ext2[i].id, ext2[i].frame); for (j=0;j<ext2[i].len;j++) printf("%#04x ", ext2[i].data[j]);
printf("} %d\n", ext2[i].len);
}
} #endif
Messung V0.5 in Prozent
¤ Dauer der Verarbeitung: 0.19 Sekunden
(vorverarbeitet am 2026-09-27)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.