#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "opus_types.h"
#include "opus_defines.h"
#include "arch.h"
#include "os_support.h"
#include "opus_private.h"
static opus_int32 skip_extension_payload(const unsigned char **pdata,
opus_int32 len, opus_int32 *pheader_size, int id_byte,
opus_int32 trailing_short_len)
{
const unsigned char *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)
{
} else if (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;
}
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, 0);
if (len >= 0) {
*pdata = data;
(*pheader_size)++;
}
return len;
}
void opus_extension_iterator_init(OpusExtensionIterator *iter,
const unsigned char *data, opus_int32 len, opus_int32 nb_frames) {
celt_assert(len >= 0);
celt_assert(data != NULL || len == 0);
celt_assert(nb_frames >= 0 && nb_frames <= 48);
iter->repeat_data = iter->curr_data = iter->data = data;
iter->last_long = iter->src_data = NULL;
iter->curr_len = iter->len = len;
iter->repeat_len = iter->src_len = 0;
iter->trailing_short_len = 0;
iter->frame_max = iter->nb_frames = nb_frames;
iter->repeat_frame = iter->curr_frame = 0;
iter->repeat_l = 0;
}
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;
}
void opus_extension_iterator_set_frame_max(OpusExtensionIterator *iter,
int frame_max) {
iter->frame_max = frame_max;
}
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) {
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);
celt_assert(iter->src_len >= 0);
if (repeat_id_byte <= 3) continue;
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 (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;
}
return 1;
}
iter->src_data = iter->repeat_data;
iter->src_len = iter->repeat_len;
}
iter->repeat_data = iter->curr_data;
iter->last_long = NULL;
if (iter->repeat_l == 0) {
iter->curr_frame++;
if (iter->curr_frame >= iter->nb_frames) {
iter->curr_len = 0;
}
}
iter->repeat_frame = 0;
}
if (iter->curr_frame >= iter->frame_max) {
return 0;
}
while (iter->curr_len > 0) {
const unsigned char *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 {
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 (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;
}
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) {
if (id >= 32) {
iter->last_long = iter->curr_data;
iter->trailing_short_len = 0;
}
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;
}
return 1;
}
}
return 0;
}
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;
}
}
}
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, nb_frames);
for (count=0; opus_extension_iterator_next(&iter, NULL) > 0; count++);
return count;
}
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;
}
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, 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;
}
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);
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;
}
static int write_extension_payload(unsigned char *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;
}
static int write_extension(unsigned char *data, opus_int32 len, opus_int32 pos,
const opus_extension_data *ext, int last) {
if (len-pos < 1)
return OPUS_BUFFER_TOO_SMALL;
celt_assert(ext->id >= 3 && ext->id <= 127);
if (data) data[pos] = (ext->id<<1) + (ext->id < 32 ? ext->len : !last);
pos++;
return write_extension_payload(data, len, pos, ext, last);
}
opus_int32 opus_packet_extensions_generate(unsigned char *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;
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;
opus_int32 trailing_short_len;
int repeat_count;
repeat_count = 0;
last_long_idx = -1;
trailing_short_len = 0;
if (f + 1 < nb_frames)
{
for (i=frame_min_idx[f];i<frame_max_idx[f];i++)
{
if (extensions[i].frame == f)
{
int g;
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;
if (extensions[i].id >= 32) {
last_long_idx = frame_repeat_idx[nb_frames-1];
trailing_short_len = 0;
}
else trailing_short_len += extensions[i].len;
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++;
frame_repeat_idx[f] = i;
}
}
}
for (i=frame_min_idx[f];i<frame_max_idx[f];i++)
{
if (extensions[i].frame == f)
{
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;
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 (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;
}
#if 0#endif