summaryrefslogtreecommitdiff
path: root/lib/rbcodec/codecs/libopus/opus_decoder.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/rbcodec/codecs/libopus/opus_decoder.c')
-rw-r--r--lib/rbcodec/codecs/libopus/opus_decoder.c259
1 files changed, 191 insertions, 68 deletions
diff --git a/lib/rbcodec/codecs/libopus/opus_decoder.c b/lib/rbcodec/codecs/libopus/opus_decoder.c
index 3d42bb2..b430a4d 100644
--- a/lib/rbcodec/codecs/libopus/opus_decoder.c
+++ b/lib/rbcodec/codecs/libopus/opus_decoder.c
@@ -26,11 +26,11 @@
*/
#ifdef HAVE_CONFIG_H
-#include "opus_config.h"
+#include "config.h"
#endif
#ifndef OPUS_BUILD
-#error "OPUS_BUILD _MUST_ be defined to build Opus and you probably want a decent config.h, see README for more details."
+#error "OPUS_BUILD _MUST_ be defined to build Opus. This probably means you need other defines as well, as in a config.h. See the included build files for details."
#endif
#include <stdarg.h>
@@ -46,6 +46,7 @@
#include "structs.h"
#include "define.h"
#include "mathops.h"
+#include "cpu_support.h"
struct OpusDecoder {
int celt_dec_offset;
@@ -64,8 +65,13 @@ struct OpusDecoder {
int prev_mode;
int frame_size;
int prev_redundancy;
+ int last_packet_duration;
+#ifndef FIXED_POINT
+ opus_val16 softclip_mem[2];
+#endif
opus_uint32 rangeFinal;
+ int arch;
};
#ifdef FIXED_POINT
@@ -115,6 +121,7 @@ int opus_decoder_init(OpusDecoder *st, opus_int32 Fs, int channels)
st->Fs = Fs;
st->DecControl.API_sampleRate = st->Fs;
st->DecControl.nChannelsAPI = st->channels;
+ st->arch = opus_select_arch();
/* Reset decoder */
ret = silk_InitDecoder( silk_dec );
@@ -131,7 +138,7 @@ int opus_decoder_init(OpusDecoder *st, opus_int32 Fs, int channels)
return OPUS_OK;
}
-#define STATIC_DECODER_SIZE 26532 /* 26486 for 32bit, 26532 for 64bit environment */
+#define STATIC_DECODER_SIZE 26540 /* 26540 for 64bit environment */
static char s_dec[STATIC_DECODER_SIZE] IBSS_ATTR MEM_ALIGN_ATTR;
OpusDecoder *opus_decoder_create(opus_int32 Fs, int channels, int *error)
@@ -208,8 +215,14 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
int i, silk_ret=0, celt_ret=0;
ec_dec dec;
opus_int32 silk_frame_size;
+ int pcm_silk_size;
VARDECL(opus_int16, pcm_silk);
- VARDECL(opus_val16, pcm_transition);
+ int pcm_transition_silk_size;
+ VARDECL(opus_val16, pcm_transition_silk);
+ int pcm_transition_celt_size;
+ VARDECL(opus_val16, pcm_transition_celt);
+ opus_val16 *pcm_transition = NULL; /* Silence false positive "may be used uninitialized" warning */
+ int redundant_audio_size;
VARDECL(opus_val16, redundant_audio);
int audiosize;
@@ -265,33 +278,30 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
}
}
- /* For CELT/hybrid PLC of more than 20 ms, do multiple calls */
- if (data==NULL && frame_size > F20 && mode != MODE_SILK_ONLY)
- {
- int nb_samples = 0;
- do {
- int ret = opus_decode_frame(st, NULL, 0, pcm, F20, 0);
- if (ret != F20)
- {
- RESTORE_STACK;
- return OPUS_INTERNAL_ERROR;
- }
- pcm += F20*st->channels;
- nb_samples += F20;
- } while (nb_samples < frame_size);
- RESTORE_STACK;
- return frame_size;
- }
- ALLOC(pcm_transition, F5*st->channels, opus_val16);
+ /* For CELT/hybrid PLC of more than 20 ms, opus_decode_native() will do
+ multiple calls */
+ if (data==NULL && mode != MODE_SILK_ONLY)
+ frame_size = IMIN(frame_size, F20);
+ pcm_transition_silk_size = 0;
+ pcm_transition_celt_size = 0;
if (data!=NULL && st->prev_mode > 0 && (
(mode == MODE_CELT_ONLY && st->prev_mode != MODE_CELT_ONLY && !st->prev_redundancy)
|| (mode != MODE_CELT_ONLY && st->prev_mode == MODE_CELT_ONLY) )
)
{
transition = 1;
+ /* Decide where to allocate the stack memory for pcm_transition */
if (mode == MODE_CELT_ONLY)
- opus_decode_frame(st, NULL, 0, pcm_transition, IMIN(F5, audiosize), 0);
+ pcm_transition_celt_size = F5*st->channels;
+ else
+ pcm_transition_silk_size = F5*st->channels;
+ }
+ ALLOC(pcm_transition_celt, pcm_transition_celt_size, opus_val16);
+ if (transition && mode == MODE_CELT_ONLY)
+ {
+ pcm_transition = pcm_transition_celt;
+ opus_decode_frame(st, NULL, 0, pcm_transition, IMIN(F5, audiosize), 0);
}
if (audiosize > frame_size)
{
@@ -302,8 +312,9 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
frame_size = audiosize;
}
- ALLOC(pcm_silk, IMAX(F10, frame_size)*st->channels, opus_int16);
- ALLOC(redundant_audio, F5*st->channels, opus_val16);
+ /* Don't allocate any memory when in CELT-only mode */
+ pcm_silk_size = (mode != MODE_CELT_ONLY) ? IMAX(F10, frame_size)*st->channels : 0;
+ ALLOC(pcm_silk, pcm_silk_size, opus_int16);
/* SILK processing */
if (mode != MODE_CELT_ONLY)
@@ -417,10 +428,22 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
}
if (redundancy)
+ {
transition = 0;
+ pcm_transition_silk_size=0;
+ }
+
+ ALLOC(pcm_transition_silk, pcm_transition_silk_size, opus_val16);
if (transition && mode != MODE_CELT_ONLY)
+ {
+ pcm_transition = pcm_transition_silk;
opus_decode_frame(st, NULL, 0, pcm_transition, IMIN(F5, audiosize), 0);
+ }
+
+ /* Only allocation memory for redundancy if/when needed */
+ redundant_audio_size = redundancy ? F5*st->channels : 0;
+ ALLOC(redundant_audio, redundant_audio_size, opus_val16);
/* 5 ms redundant frame for CELT->SILK*/
if (redundancy && celt_to_silk)
@@ -534,12 +557,19 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
st->prev_mode = mode;
st->prev_redundancy = redundancy && !celt_to_silk;
+
+ if (celt_ret>=0)
+ {
+ if (OPUS_CHECK_ARRAY(pcm, audiosize*st->channels))
+ OPUS_PRINT_INT(audiosize);
+ }
+
RESTORE_STACK;
return celt_ret < 0 ? celt_ret : audiosize;
}
-static int parse_size(const unsigned char *data, opus_int32 len, short *size)
+static int parse_size(const unsigned char *data, opus_int32 len, opus_int16 *size)
{
if (len<1)
{
@@ -561,14 +591,14 @@ static int parse_size(const unsigned char *data, opus_int32 len, short *size)
static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
int self_delimited, unsigned char *out_toc,
- const unsigned char *frames[48], short size[48], int *payload_offset)
+ const unsigned char *frames[48], opus_int16 size[48], int *payload_offset)
{
int i, bytes;
int count;
int cbr;
unsigned char ch, toc;
int framesize;
- int last_size;
+ opus_int32 last_size;
const unsigned char *data0 = data;
if (size==NULL)
@@ -594,7 +624,9 @@ static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
{
if (len&0x1)
return OPUS_INVALID_PACKET;
- size[0] = last_size = len/2;
+ last_size = len/2;
+ /* If last_size doesn't fit in size[0], we'll catch it later */
+ size[0] = (opus_int16)last_size;
}
break;
/* Two VBR frames */
@@ -620,16 +652,14 @@ static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
/* Padding flag is bit 6 */
if (ch&0x40)
{
- int padding=0;
int p;
do {
if (len<=0)
return OPUS_INVALID_PACKET;
p = *data++;
len--;
- padding += p==255 ? 254: p;
+ len -= p==255 ? 254: p;
} while (p==255);
- len -= padding;
}
if (len<0)
return OPUS_INVALID_PACKET;
@@ -657,7 +687,7 @@ static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
if (last_size*count!=len)
return OPUS_INVALID_PACKET;
for (i=0;i<count-1;i++)
- size[i] = last_size;
+ size[i] = (opus_int16)last_size;
}
break;
}
@@ -685,7 +715,7 @@ static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
1275. Reject them here.*/
if (last_size > 1275)
return OPUS_INVALID_PACKET;
- size[count-1] = last_size;
+ size[count-1] = (opus_int16)last_size;
}
if (frames)
@@ -701,14 +731,14 @@ static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
*out_toc = toc;
if (payload_offset)
- *payload_offset = data-data0;
+ *payload_offset = (int)(data-data0);
return count;
}
int opus_packet_parse(const unsigned char *data, opus_int32 len,
unsigned char *out_toc, const unsigned char *frames[48],
- short size[48], int *payload_offset)
+ opus_int16 size[48], int *payload_offset)
{
return opus_packet_parse_impl(data, len, 0, out_toc,
frames, size, payload_offset);
@@ -716,50 +746,120 @@ int opus_packet_parse(const unsigned char *data, opus_int32 len,
int opus_decode_native(OpusDecoder *st, const unsigned char *data,
opus_int32 len, opus_val16 *pcm, int frame_size, int decode_fec,
- int self_delimited, int *packet_offset)
+ int self_delimited, int *packet_offset, int soft_clip)
{
int i, nb_samples;
int count, offset;
unsigned char toc;
int tot_offset;
+ int packet_frame_size, packet_bandwidth, packet_mode, packet_stream_channels;
/* 48 x 2.5 ms = 120 ms */
- short size[48];
+ opus_int16 size[48];
if (decode_fec<0 || decode_fec>1)
return OPUS_BAD_ARG;
+ /* For FEC/PLC, frame_size has to be to have a multiple of 2.5 ms */
+ if ((decode_fec || len==0 || data==NULL) && frame_size%(st->Fs/400)!=0)
+ return OPUS_BAD_ARG;
if (len==0 || data==NULL)
- return opus_decode_frame(st, NULL, 0, pcm, frame_size, 0);
- else if (len<0)
+ {
+ int pcm_count=0;
+ do {
+ int ret;
+ ret = opus_decode_frame(st, NULL, 0, pcm+pcm_count*st->channels, frame_size-pcm_count, 0);
+ if (ret<0)
+ return ret;
+ pcm_count += ret;
+ } while (pcm_count < frame_size);
+ celt_assert(pcm_count == frame_size);
+ if (OPUS_CHECK_ARRAY(pcm, pcm_count*st->channels))
+ OPUS_PRINT_INT(pcm_count);
+ st->last_packet_duration = pcm_count;
+ return pcm_count;
+ } else if (len<0)
return OPUS_BAD_ARG;
- tot_offset = 0;
- st->mode = opus_packet_get_mode(data);
- st->bandwidth = opus_packet_get_bandwidth(data);
- st->frame_size = opus_packet_get_samples_per_frame(data, st->Fs);
- st->stream_channels = opus_packet_get_nb_channels(data);
+ packet_mode = opus_packet_get_mode(data);
+ packet_bandwidth = opus_packet_get_bandwidth(data);
+ packet_frame_size = opus_packet_get_samples_per_frame(data, st->Fs);
+ packet_stream_channels = opus_packet_get_nb_channels(data);
count = opus_packet_parse_impl(data, len, self_delimited, &toc, NULL, size, &offset);
+
+ data += offset;
+
+ if (decode_fec)
+ {
+ int duration_copy;
+ int ret;
+ /* If no FEC can be present, run the PLC (recursive call) */
+ if (frame_size < packet_frame_size || packet_mode == MODE_CELT_ONLY || st->mode == MODE_CELT_ONLY)
+ return opus_decode_native(st, NULL, 0, pcm, frame_size, 0, 0, NULL, soft_clip);
+ /* Otherwise, run the PLC on everything except the size for which we might have FEC */
+ duration_copy = st->last_packet_duration;
+ if (frame_size-packet_frame_size!=0)
+ {
+ ret = opus_decode_native(st, NULL, 0, pcm, frame_size-packet_frame_size, 0, 0, NULL, soft_clip);
+ if (ret<0)
+ {
+ st->last_packet_duration = duration_copy;
+ return ret;
+ }
+ celt_assert(ret==frame_size-packet_frame_size);
+ }
+ /* Complete with FEC */
+ st->mode = packet_mode;
+ st->bandwidth = packet_bandwidth;
+ st->frame_size = packet_frame_size;
+ st->stream_channels = packet_stream_channels;
+ ret = opus_decode_frame(st, data, size[0], pcm+st->channels*(frame_size-packet_frame_size),
+ packet_frame_size, 1);
+ if (ret<0)
+ return ret;
+ else {
+ if (OPUS_CHECK_ARRAY(pcm, frame_size*st->channels))
+ OPUS_PRINT_INT(frame_size);
+ st->last_packet_duration = frame_size;
+ return frame_size;
+ }
+ }
+ tot_offset = 0;
if (count < 0)
return count;
- data += offset;
tot_offset += offset;
- if (count*st->frame_size > frame_size)
+ if (count*packet_frame_size > frame_size)
return OPUS_BUFFER_TOO_SMALL;
+
+ /* Update the state as the last step to avoid updating it on an invalid packet */
+ st->mode = packet_mode;
+ st->bandwidth = packet_bandwidth;
+ st->frame_size = packet_frame_size;
+ st->stream_channels = packet_stream_channels;
+
nb_samples=0;
for (i=0;i<count;i++)
{
int ret;
- ret = opus_decode_frame(st, data, size[i], pcm, frame_size-nb_samples, decode_fec);
+ ret = opus_decode_frame(st, data, size[i], pcm+nb_samples*st->channels, frame_size-nb_samples, 0);
if (ret<0)
return ret;
+ celt_assert(ret==packet_frame_size);
data += size[i];
tot_offset += size[i];
- pcm += ret*st->channels;
nb_samples += ret;
}
if (packet_offset != NULL)
*packet_offset = tot_offset;
+ st->last_packet_duration = nb_samples;
+ if (OPUS_CHECK_ARRAY(pcm, nb_samples*st->channels))
+ OPUS_PRINT_INT(nb_samples);
+#ifndef FIXED_POINT
+ if (soft_clip)
+ opus_pcm_soft_clip(pcm, nb_samples, st->channels, st->softclip_mem);
+ else
+ st->softclip_mem[0]=st->softclip_mem[1]=0;
+#endif
return nb_samples;
}
@@ -768,7 +868,7 @@ int opus_decode_native(OpusDecoder *st, const unsigned char *data,
int opus_decode(OpusDecoder *st, const unsigned char *data,
opus_int32 len, opus_val16 *pcm, int frame_size, int decode_fec)
{
- return opus_decode_native(st, data, len, pcm, frame_size, decode_fec, 0, NULL);
+ return opus_decode_native(st, data, len, pcm, frame_size, decode_fec, 0, NULL, 0);
}
#ifndef DISABLE_FLOAT_API
@@ -781,7 +881,7 @@ int opus_decode_float(OpusDecoder *st, const unsigned char *data,
ALLOC(out, frame_size*st->channels, opus_int16);
- ret = opus_decode_native(st, data, len, out, frame_size, decode_fec, 0, NULL);
+ ret = opus_decode_native(st, data, len, out, frame_size, decode_fec, 0, NULL, 0);
if (ret > 0)
{
for (i=0;i<ret*st->channels;i++)
@@ -809,7 +909,7 @@ int opus_decode(OpusDecoder *st, const unsigned char *data,
ALLOC(out, frame_size*st->channels, float);
- ret = opus_decode_native(st, data, len, out, frame_size, decode_fec, 0, NULL);
+ ret = opus_decode_native(st, data, len, out, frame_size, decode_fec, 0, NULL, 1);
if (ret > 0)
{
for (i=0;i<ret*st->channels;i++)
@@ -822,7 +922,7 @@ int opus_decode(OpusDecoder *st, const unsigned char *data,
int opus_decode_float(OpusDecoder *st, const unsigned char *data,
opus_int32 len, opus_val16 *pcm, int frame_size, int decode_fec)
{
- return opus_decode_native(st, data, len, pcm, frame_size, decode_fec, 0, NULL);
+ return opus_decode_native(st, data, len, pcm, frame_size, decode_fec, 0, NULL, 0);
}
#endif
@@ -845,12 +945,20 @@ int opus_decoder_ctl(OpusDecoder *st, int request, ...)
case OPUS_GET_BANDWIDTH_REQUEST:
{
opus_int32 *value = va_arg(ap, opus_int32*);
+ if (!value)
+ {
+ goto bad_arg;
+ }
*value = st->bandwidth;
}
break;
case OPUS_GET_FINAL_RANGE_REQUEST:
{
opus_uint32 *value = va_arg(ap, opus_uint32*);
+ if (!value)
+ {
+ goto bad_arg;
+ }
*value = st->rangeFinal;
}
break;
@@ -869,10 +977,9 @@ int opus_decoder_ctl(OpusDecoder *st, int request, ...)
case OPUS_GET_SAMPLE_RATE_REQUEST:
{
opus_int32 *value = va_arg(ap, opus_int32*);
- if (value==NULL)
+ if (!value)
{
- ret = OPUS_BAD_ARG;
- break;
+ goto bad_arg;
}
*value = st->Fs;
}
@@ -880,10 +987,9 @@ int opus_decoder_ctl(OpusDecoder *st, int request, ...)
case OPUS_GET_PITCH_REQUEST:
{
opus_int32 *value = va_arg(ap, opus_int32*);
- if (value==NULL)
+ if (!value)
{
- ret = OPUS_BAD_ARG;
- break;
+ goto bad_arg;
}
if (st->prev_mode == MODE_CELT_ONLY)
celt_decoder_ctl(celt_dec, OPUS_GET_PITCH(value));
@@ -894,10 +1000,9 @@ int opus_decoder_ctl(OpusDecoder *st, int request, ...)
case OPUS_GET_GAIN_REQUEST:
{
opus_int32 *value = va_arg(ap, opus_int32*);
- if (value==NULL)
+ if (!value)
{
- ret = OPUS_BAD_ARG;
- break;
+ goto bad_arg;
}
*value = st->decode_gain;
}
@@ -907,12 +1012,21 @@ int opus_decoder_ctl(OpusDecoder *st, int request, ...)
opus_int32 value = va_arg(ap, opus_int32);
if (value<-32768 || value>32767)
{
- ret = OPUS_BAD_ARG;
- break;
+ goto bad_arg;
}
st->decode_gain = value;
}
break;
+ case OPUS_GET_LAST_PACKET_DURATION_REQUEST:
+ {
+ opus_uint32 *value = va_arg(ap, opus_uint32*);
+ if (!value)
+ {
+ goto bad_arg;
+ }
+ *value = st->last_packet_duration;
+ }
+ break;
default:
/*fprintf(stderr, "unknown opus_decoder_ctl() request: %d", request);*/
ret = OPUS_UNIMPLEMENTED;
@@ -921,6 +1035,9 @@ int opus_decoder_ctl(OpusDecoder *st, int request, ...)
va_end(ap);
return ret;
+bad_arg:
+ va_end(ap);
+ return OPUS_BAD_ARG;
}
void opus_decoder_destroy(OpusDecoder *st)
@@ -989,8 +1106,8 @@ int opus_packet_get_nb_frames(const unsigned char packet[], opus_int32 len)
return packet[1]&0x3F;
}
-int opus_decoder_get_nb_samples(const OpusDecoder *dec,
- const unsigned char packet[], opus_int32 len)
+int opus_packet_get_nb_samples(const unsigned char packet[], opus_int32 len,
+ opus_int32 Fs)
{
int samples;
int count = opus_packet_get_nb_frames(packet, len);
@@ -998,10 +1115,16 @@ int opus_decoder_get_nb_samples(const OpusDecoder *dec,
if (count<0)
return count;
- samples = count*opus_packet_get_samples_per_frame(packet, dec->Fs);
+ samples = count*opus_packet_get_samples_per_frame(packet, Fs);
/* Can't have more than 120 ms */
- if (samples*25 > dec->Fs*3)
+ if (samples*25 > Fs*3)
return OPUS_INVALID_PACKET;
else
return samples;
}
+
+int opus_decoder_get_nb_samples(const OpusDecoder *dec,
+ const unsigned char packet[], opus_int32 len)
+{
+ return opus_packet_get_nb_samples(packet, len, dec->Fs);
+}