diff options
-rw-r--r-- | opus_headers.txt | 1 | ||||
-rw-r--r-- | opus_sources.mk | 1 | ||||
-rw-r--r-- | src/opus.h | 6 | ||||
-rw-r--r-- | src/opus_decoder.c | 35 | ||||
-rw-r--r-- | src/opus_multistream.c | 713 | ||||
-rw-r--r-- | src/opus_multistream.h | 121 | ||||
-rw-r--r-- | src/opus_private.h | 6 |
7 files changed, 874 insertions, 9 deletions
diff --git a/opus_headers.txt b/opus_headers.txt index 0e381852..4fc5b659 100644 --- a/opus_headers.txt +++ b/opus_headers.txt @@ -1,3 +1,4 @@ OPUS_HEAD = \ src/opus.h \ +src/opus_multistream.h \ src/opus_private.h diff --git a/opus_sources.mk b/opus_sources.mk index ee6e5740..384b036a 100644 --- a/opus_sources.mk +++ b/opus_sources.mk @@ -1,4 +1,5 @@ OPUS_SOURCES = src/opus.c \ src/opus_decoder.c \ src/opus_encoder.c \ +src/opus_multistream.c \ src/repacketizer.c @@ -169,6 +169,8 @@ extern "C" { typedef struct OpusEncoder OpusEncoder; typedef struct OpusDecoder OpusDecoder; +OPUS_EXPORT int opus_encoder_get_size(int channels); + /* * There are two coding modes: * OPUS_APPLICATION_VOIP gives best quality at a given bitrate for voice @@ -219,6 +221,10 @@ OPUS_EXPORT void opus_encoder_destroy(OpusEncoder *st); OPUS_EXPORT int opus_encoder_ctl(OpusEncoder *st, int request, ...); + + +OPUS_EXPORT int opus_decoder_get_size(int channels); + OPUS_EXPORT OpusDecoder *opus_decoder_create( int Fs, /* Sampling rate of output signal (Hz) */ int channels /* Number of channels (1/2) in output signal */ diff --git a/src/opus_decoder.c b/src/opus_decoder.c index 6b937f89..e3b12e2b 100644 --- a/src/opus_decoder.c +++ b/src/opus_decoder.c @@ -626,23 +626,21 @@ int opus_packet_parse(const unsigned char *data, int len, out_toc, frames, size, payload_offset); } -#ifdef FIXED_POINT -int opus_decode(OpusDecoder *st, const unsigned char *data, - int len, opus_val16 *pcm, int frame_size, int decode_fec) -#else -int opus_decode_float(OpusDecoder *st, const unsigned char *data, - int len, opus_val16 *pcm, int frame_size, int decode_fec) -#endif +int opus_decode_native(OpusDecoder *st, const unsigned char *data, + int len, opus_val16 *pcm, int frame_size, int decode_fec, int self_delimited, int *packet_offset) { int i, nb_samples; int count, offset; unsigned char toc; + int tot_offset; /* 48 x 2.5 ms = 120 ms */ short size[48]; if (len==0 || data==NULL) return opus_decode_frame(st, NULL, 0, pcm, frame_size, 0); 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); @@ -653,6 +651,8 @@ int opus_decode_float(OpusDecoder *st, const unsigned char *data, return count; data += offset; + tot_offset += offset; + if (count*st->frame_size > frame_size) return OPUS_BAD_ARG; nb_samples=0; @@ -663,14 +663,23 @@ int opus_decode_float(OpusDecoder *st, const unsigned char *data, if (ret<0) return ret; data += size[i]; + tot_offset += size[i]; pcm += ret*st->channels; nb_samples += ret; } + if (packet_offset != NULL) + *packet_offset = tot_offset; return nb_samples; } #ifdef FIXED_POINT +int opus_decode(OpusDecoder *st, const unsigned char *data, + int len, opus_val16 *pcm, int frame_size, int decode_fec) +{ + return opus_decode_native(st, data, len, pcm, frame_size, decode_fec, 0, NULL); +} + #ifndef DISABLE_FLOAT_API int opus_decode_float(OpusDecoder *st, const unsigned char *data, int len, float *pcm, int frame_size, int decode_fec) @@ -681,7 +690,7 @@ int opus_decode_float(OpusDecoder *st, const unsigned char *data, ALLOC(out, frame_size*st->channels, opus_int16); - ret = opus_decode(st, data, len, out, frame_size, decode_fec); + ret = opus_decode_native(st, data, len, out, frame_size, decode_fec, 0, NULL); if (ret > 0) { for (i=0;i<ret*st->channels;i++) @@ -692,6 +701,7 @@ int opus_decode_float(OpusDecoder *st, const unsigned char *data, } #endif + #else int opus_decode(OpusDecoder *st, const unsigned char *data, int len, opus_int16 *pcm, int frame_size, int decode_fec) @@ -702,7 +712,7 @@ int opus_decode(OpusDecoder *st, const unsigned char *data, ALLOC(out, frame_size*st->channels, float); - ret = opus_decode_float(st, data, len, out, frame_size, decode_fec); + ret = opus_decode_native(st, data, len, out, frame_size, decode_fec, 0, NULL); if (ret > 0) { for (i=0;i<ret*st->channels;i++) @@ -711,6 +721,13 @@ int opus_decode(OpusDecoder *st, const unsigned char *data, RESTORE_STACK; return ret; } + +int opus_decode_float(OpusDecoder *st, const unsigned char *data, + int len, opus_val16 *pcm, int frame_size, int decode_fec) +{ + return opus_decode_native(st, data, len, pcm, frame_size, decode_fec, 0, NULL); +} + #endif int opus_decoder_ctl(OpusDecoder *st, int request, ...) diff --git a/src/opus_multistream.c b/src/opus_multistream.c new file mode 100644 index 00000000..ca12fb07 --- /dev/null +++ b/src/opus_multistream.c @@ -0,0 +1,713 @@ +/* Copyright (c) 2011 Xiph.Org Foundation + Written by Jean-Marc Valin */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "opus_multistream.h" +#include "opus.h" +#include "opus_private.h" +#include "stack_alloc.h" +#include "stdio.h" +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include "float_cast.h" + +typedef struct ChannelLayout { + int nb_channels; + int nb_streams; + int nb_coupled_streams; + unsigned char mapping[256]; +} ChannelLayout; + +struct OpusMSEncoder { + ChannelLayout layout; + int bitrate; + /* Encoder states go here */ +}; + +struct OpusMSDecoder { + ChannelLayout layout; + /* Decoder states go here */ +}; + + +#ifdef FIXED_POINT +#define opus_encode_native opus_encode +#else +#define opus_encode_native opus_encode_float +#endif + +static int validate_layout(const ChannelLayout *layout) +{ + int i, max_channel; + + max_channel = layout->nb_streams+layout->nb_coupled_streams; + if (max_channel>255) + return 0; + for (i=0;i<layout->nb_channels;i++) + { + if (layout->mapping[i] >= max_channel && layout->mapping[i] != 255) + return 0; + } + return 1; +} + + +static int get_left_channel(const ChannelLayout *layout, int stream_id, int prev) +{ + int i; + i = (prev<0) ? 0 : prev+1; + for (;i<layout->nb_channels;i++) + { + if (layout->mapping[i]==stream_id*2) + return i; + } + return -1; +} + +static int get_right_channel(const ChannelLayout *layout, int stream_id, int prev) +{ + int i; + i = (prev<0) ? 0 : prev+1; + for (;i<layout->nb_channels;i++) + { + if (layout->mapping[i]==stream_id*2+1) + return i; + } + return -1; +} + +static int get_mono_channel(const ChannelLayout *layout, int stream_id, int prev) +{ + int i; + i = (prev<0) ? 0 : prev+1; + for (;i<layout->nb_channels;i++) + { + if (layout->mapping[i]==2*layout->nb_coupled_streams+stream_id) + return i; + } + return -1; +} + +static int validate_encoder_layout(const ChannelLayout *layout) +{ + int s; + for (s=0;s<layout->nb_streams;s++) + { + if (s < layout->nb_coupled_streams) + { + if (get_left_channel(layout, s, -1)==-1) + return 0; + if (get_right_channel(layout, s, -1)==-1) + return 0; + } else { + if (get_mono_channel(layout, s, -1)==-1) + return 0; + } + } + return 1; +} + +int opus_multistream_encoder_get_size(int nb_streams, int nb_coupled_streams) +{ + int coupled_size; + int mono_size; + + coupled_size = opus_encoder_get_size(2); + mono_size = opus_encoder_get_size(1); + return align(sizeof(OpusMSEncoder)) + + nb_coupled_streams * align(coupled_size) + + (nb_streams-nb_coupled_streams) * align(mono_size); +} + + + +int opus_multistream_encoder_init( + OpusMSEncoder *st, /* Encoder state */ + int Fs, /* Sampling rate of input signal (Hz) */ + int channels, /* Number of channels (1/2) in input signal */ + int streams, + int coupled_streams, + unsigned char *mapping, + int application /* Coding mode (OPUS_APPLICATION_VOIP/OPUS_APPLICATION_AUDIO) */ +) +{ + int coupled_size; + int mono_size; + int i; + char *ptr; + + st->layout.nb_channels = channels; + st->layout.nb_streams = streams; + st->layout.nb_coupled_streams = coupled_streams; + + for (i=0;i<st->layout.nb_channels;i++) + st->layout.mapping[i] = mapping[i]; + if (!validate_layout(&st->layout) || !validate_encoder_layout(&st->layout)) + return OPUS_BAD_ARG; + ptr = (char*)st + align(sizeof(OpusMSEncoder)); + coupled_size = opus_encoder_get_size(2); + mono_size = opus_encoder_get_size(1); + + for (i=0;i<st->layout.nb_coupled_streams;i++) + { + opus_encoder_init((OpusEncoder*)ptr, Fs, 2, application); + ptr += align(coupled_size); + } + for (;i<st->layout.nb_streams;i++) + { + opus_encoder_init((OpusEncoder*)ptr, Fs, 1, application); + ptr += align(mono_size); + } + return OPUS_OK; +} + +OpusMSEncoder *opus_multistream_encoder_create( + int Fs, /* Sampling rate of input signal (Hz) */ + int channels, /* Number of channels (1/2) in input signal */ + int streams, + int coupled_streams, + unsigned char *mapping, + int application /* Coding mode (OPUS_APPLICATION_VOIP/OPUS_APPLICATION_AUDIO) */ +) +{ + OpusMSEncoder *st = malloc(opus_multistream_encoder_get_size(streams, coupled_streams)); + if (st!=NULL) + opus_multistream_encoder_init(st, Fs, channels, streams, coupled_streams, mapping, application); + return st; +} + + +#ifdef FIXED_POINT +int opus_multistream_encode( +#else +int opus_multistream_encode_float( +#endif + OpusMSEncoder *st, /* Encoder state */ + const float *pcm, /* Input signal (interleaved if 2 channels). length is frame_size*channels */ + int frame_size, /* Number of samples per frame of input signal */ + unsigned char *data, /* Output payload (no more than max_data_bytes long) */ + int max_data_bytes /* Allocated memory for payload; don't use for controlling bitrate */ +) +{ + int coupled_size; + int mono_size; + int s, i; + char *ptr; + int tot_size; + VARDECL(opus_val16, buf); + unsigned char tmp_data[1276]; + ALLOC_STACK; + + ALLOC(buf, 2*frame_size, opus_val16); + ptr = (char*)st + align(sizeof(OpusMSEncoder)); + coupled_size = opus_encoder_get_size(2); + mono_size = opus_encoder_get_size(1); + + if (max_data_bytes < 2*st->layout.nb_streams-1) + { + RESTORE_STACK; + return OPUS_BUFFER_TOO_SMALL; + } + /* Counting ToC */ + tot_size = 0; + for (s=0;s<st->layout.nb_streams;s++) + { + OpusEncoder *enc; + int len; + int curr_max; + + enc = (OpusEncoder*)ptr; + if (s < st->layout.nb_coupled_streams) + { + int left, right; + left = get_left_channel(&st->layout, s, -1); + right = get_right_channel(&st->layout, s, -1); + for (i=0;i<frame_size;i++) + { + buf[2*i] = pcm[st->layout.nb_channels*i+left]; + buf[2*i+1] = pcm[st->layout.nb_channels*i+right]; + } + ptr += align(coupled_size); + } else { + int chan = get_mono_channel(&st->layout, s, -1); + for (i=0;i<frame_size;i++) + buf[i] = pcm[st->layout.nb_channels*i+chan]; + ptr += align(mono_size); + } + /* number of bytes left (+Toc) */ + curr_max = max_data_bytes - tot_size; + /* Reserve one byte for the last stream and 2 for the others */ + curr_max -= 2*(st->layout.nb_streams-s)-1; + len = opus_encode_native(enc, buf, frame_size, tmp_data, curr_max); + if (len<0) + { + RESTORE_STACK; + return len; + } + /* ToC first */ + *data++ = tmp_data[0]; + if (s != st->layout.nb_streams-1) + { + int tmp = encode_size(len-1, data); + data += tmp; + tot_size += tmp; + } + /* IMPORTANT: Here we assume that the encoder only returned one frame */ + tot_size += len; + memcpy(data, &tmp_data[1], len-1); + } + RESTORE_STACK; + return tot_size; + +} + +#ifdef FIXED_POINT + +#ifndef DISABLE_FLOAT_API +int opus_multistream_encode_float( + OpusMSEncoder *st, /* Encoder state */ + const float *pcm, /* Input signal (interleaved if 2 channels). length is frame_size*channels */ + int frame_size, /* Number of samples per frame of input signal */ + unsigned char *data, /* Output payload (no more than max_data_bytes long) */ + int max_data_bytes /* Allocated memory for payload; don't use for controlling bitrate */ +) +{ + int i, ret; + VARDECL(opus_int16, in); + ALLOC_STACK; + + ALLOC(in, frame_size*st->layout.nb_channels, opus_int16); + + for (i=0;i<frame_size*st->layout.nb_channels;i++) + in[i] = FLOAT2INT16(pcm[i]); + ret = opus_multistream_encode(st, in, frame_size, data, max_data_bytes); + RESTORE_STACK; + return ret; +} +#endif + +#else + +int opus_multistream_encode( + OpusMSEncoder *st, /* Encoder state */ + const opus_int16 *pcm, /* Input signal (interleaved if 2 channels). length is frame_size*channels */ + int frame_size, /* Number of samples per frame of input signal */ + unsigned char *data, /* Output payload (no more than max_data_bytes long) */ + int max_data_bytes /* Allocated memory for payload; don't use for controlling bitrate */ +) +{ + int i, ret; + VARDECL(float, in); + ALLOC_STACK; + + ALLOC(in, frame_size*st->layout.nb_channels, float); + + for (i=0;i<frame_size*st->layout.nb_channels;i++) + in[i] = (1./32768)*pcm[i]; + ret = opus_multistream_encode_float(st, in, frame_size, data, max_data_bytes); + RESTORE_STACK; + return ret; +} + +#endif + +int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...) +{ + va_list ap; + int coupled_size, mono_size; + char *ptr; + int ret = OPUS_OK; + + va_start(ap, request); + + coupled_size = opus_encoder_get_size(2); + mono_size = opus_encoder_get_size(1); + ptr = (char*)st + align(sizeof(OpusMSEncoder)); + switch (request) + { + case OPUS_SET_BITRATE_REQUEST: + { + int chan, s; + opus_uint32 value = va_arg(ap, opus_uint32); + chan = st->layout.nb_streams + st->layout.nb_coupled_streams; + value /= chan; + for (s=0;s<st->layout.nb_streams;s++) + { + OpusEncoder *enc; + enc = (OpusEncoder*)ptr; + opus_encoder_ctl(enc, request, value * (s < st->layout.nb_coupled_streams ? 2 : 1)); + } + } + break; + /* FIXME: Add missing ones */ + case OPUS_GET_BITRATE_REQUEST: + case OPUS_GET_VBR_REQUEST: + case OPUS_GET_APPLICATION_REQUEST: + case OPUS_GET_BANDWIDTH_REQUEST: + case OPUS_GET_COMPLEXITY_REQUEST: + case OPUS_GET_PACKET_LOSS_PERC_REQUEST: + case OPUS_GET_DTX_REQUEST: + case OPUS_GET_VOICE_RATIO_REQUEST: + case OPUS_GET_VBR_CONSTRAINT_REQUEST: + { + int s; + /* This works for int32* params */ + opus_uint32 value = va_arg(ap, opus_uint32); + for (s=0;s<st->layout.nb_streams;s++) + { + OpusEncoder *enc; + + enc = (OpusEncoder*)ptr; + if (s < st->layout.nb_coupled_streams) + ptr += align(coupled_size); + else + ptr += align(mono_size); + ret = opus_encoder_ctl(enc, request, value); + if (ret < 0) + break; + } + } + break; + default: + { + int s; + /* This works for int32 params */ + opus_uint32 value = va_arg(ap, opus_uint32); + for (s=0;s<st->layout.nb_streams;s++) + { + OpusEncoder *enc; + + enc = (OpusEncoder*)ptr; + if (s < st->layout.nb_coupled_streams) + ptr += align(coupled_size); + else + ptr += align(mono_size); + ret = opus_encoder_ctl(enc, request, value); + if (ret < 0) + break; + } + } + break; + } + + va_end(ap); + return ret; +} + +void opus_multistream_encoder_destroy(OpusMSEncoder *st) +{ + free(st); +} + + +/* DECODER */ + +int opus_multistream_decoder_get_size(int nb_streams, int nb_coupled_streams) +{ + int coupled_size; + int mono_size; + + coupled_size = opus_decoder_get_size(2); + mono_size = opus_decoder_get_size(1); + return align(sizeof(OpusMSDecoder)) + + nb_coupled_streams * align(coupled_size) + + (nb_streams-nb_coupled_streams) * align(mono_size); +} + +int opus_multistream_decoder_init( + OpusMSDecoder *st, /* Encoder state */ + int Fs, /* Sampling rate of input signal (Hz) */ + int channels, /* Number of channels (1/2) in input signal */ + int streams, + int coupled_streams, + unsigned char *mapping +) +{ + int coupled_size; + int mono_size; + int i; + char *ptr; + + st->layout.nb_channels = channels; + st->layout.nb_streams = streams; + st->layout.nb_coupled_streams = coupled_streams; + + for (i=0;i<st->layout.nb_channels;i++) + st->layout.mapping[i] = mapping[i]; + if (!validate_layout(&st->layout)) + return OPUS_BAD_ARG; + + ptr = (char*)st + align(sizeof(OpusMSEncoder)); + coupled_size = opus_decoder_get_size(2); + mono_size = opus_decoder_get_size(1); + + for (i=0;i<st->layout.nb_coupled_streams;i++) + { + opus_decoder_init((OpusDecoder*)ptr, Fs, 2); + ptr += align(coupled_size); + } + for (;i<st->layout.nb_streams;i++) + { + opus_decoder_init((OpusDecoder*)ptr, Fs, 1); + ptr += align(mono_size); + } + return OPUS_OK; +} + + +OpusMSDecoder *opus_multistream_decoder_create( + int Fs, /* Sampling rate of input signal (Hz) */ + int channels, /* Number of channels (1/2) in input signal */ + int streams, + int coupled_streams, + unsigned char *mapping +) +{ + OpusMSDecoder *st = malloc(opus_multistream_decoder_get_size(streams, coupled_streams)); + if (st!=NULL) + opus_multistream_decoder_init(st, Fs, channels, streams, coupled_streams, mapping); + return st; + + +} + +static int opus_multistream_decode_native( + OpusMSDecoder *st, /* Encoder state */ + const unsigned char *data, + int len, + opus_val16 *pcm, + int frame_size, + int decode_fec +) +{ + int coupled_size; + int mono_size; + int s, i, c; + char *ptr; + VARDECL(opus_val16, buf); + ALLOC_STACK; + + ALLOC(buf, 2*frame_size, opus_val16); + ptr = (char*)st + align(sizeof(OpusMSEncoder)); + coupled_size = opus_decoder_get_size(2); + mono_size = opus_decoder_get_size(1); + + if (len < 2*st->layout.nb_streams-1) + return OPUS_BUFFER_TOO_SMALL; + for (s=0;s<st->layout.nb_streams;s++) + { + OpusDecoder *dec; + int packet_offset, ret; + + dec = (OpusDecoder*)ptr; + ptr += (s < st->layout.nb_coupled_streams) ? align(coupled_size) : align(mono_size); + + if (len<=0) + { + RESTORE_STACK; + return OPUS_CORRUPTED_DATA; + } + ret = opus_decode_native(dec, data, len, buf, frame_size, decode_fec, 1, &packet_offset); + data += packet_offset; + len -= packet_offset; + if (ret > frame_size) + { + RESTORE_STACK; + return OPUS_BUFFER_TOO_SMALL; + } + if (s>0 && ret != frame_size) + { + RESTORE_STACK; + return OPUS_CORRUPTED_DATA; + } + if (ret <= 0) + { + RESTORE_STACK; + return ret; + } + frame_size = ret; + if (s < st->layout.nb_coupled_streams) + { + int chan, prev; + prev = -1; + /* Copy "left" audio to the channel(s) where it belongs */ + while ( (chan = get_left_channel(&st->layout, s, prev)) != -1) + { + for (i=0;i<frame_size;i++) + pcm[st->layout.nb_channels*i+chan] = buf[2*i]; + prev = chan; + } + prev = -1; + /* Copy "right" audio to the channel(s) where it belongs */ + while ( (chan = get_right_channel(&st->layout, s, prev)) != -1) + { + for (i=0;i<frame_size;i++) + pcm[st->layout.nb_channels*i+chan] = buf[2*i+1]; + prev = chan; + } + } else { + int chan, prev; + prev = -1; + /* Copy audio to the channel(s) where it belongs */ + while ( (chan = get_mono_channel(&st->layout, s, prev)) != -1) + { + for (i=0;i<frame_size;i++) + pcm[st->layout.nb_channels*i+chan] = buf[i]; + prev = chan; + } + } + } + /* Handle muted channels */ + for (c=0;c<st->layout.nb_channels;c++) + { + if (st->layout.mapping[c] == 255) + { + for (i=0;i<frame_size;i++) + pcm[st->layout.nb_channels*i+c] = 0; + } + } + RESTORE_STACK; + return frame_size; +} + +#ifdef FIXED_POINT +int opus_multistream_decode( + OpusMSDecoder *st, /* Encoder state */ + const unsigned char *data, + int len, + opus_int16 *pcm, + int frame_size, + int decode_fec +) +{ + return opus_multistream_decode_native(st, data, len, pcm, frame_size, decode_fec); +} + +#ifndef DISABLE_FLOAT_API +int opus_multistream_decode_float(OpusMSDecoder *st, const unsigned char *data, + int len, float *pcm, int frame_size, int decode_fec) +{ + VARDECL(opus_int16, out); + int ret, i; + ALLOC_STACK; + + ALLOC(out, frame_size*st->layout.nb_channels, opus_int16); + + ret = opus_multistream_decode_native(st, data, len, out, frame_size, decode_fec); + if (ret > 0) + { + for (i=0;i<ret*st->layout.nb_channels;i++) + pcm[i] = (1./32768.)*(out[i]); + } + RESTORE_STACK; + return ret; +} +#endif + +#else + +int opus_multistream_decode(OpusMSDecoder *st, const unsigned char *data, + int len, opus_int16 *pcm, int frame_size, int decode_fec) +{ + VARDECL(float, out); + int ret, i; + ALLOC_STACK; + + ALLOC(out, frame_size*st->layout.nb_channels, float); + + ret = opus_multistream_decode_native(st, data, len, out, frame_size, decode_fec); + if (ret > 0) + { + for (i=0;i<ret*st->layout.nb_channels;i++) + pcm[i] = FLOAT2INT16(out[i]); + } + RESTORE_STACK; + return ret; +} + +int opus_multistream_decode_float( + OpusMSDecoder *st, /* Encoder state */ + const unsigned char *data, + int len, + float *pcm, + int frame_size, + int decode_fec +) +{ + return opus_multistream_decode_native(st, data, len, pcm, frame_size, decode_fec); +} +#endif + +int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...) +{ + va_list ap; + int coupled_size, mono_size; + char *ptr; + int ret = OPUS_OK; + + va_start(ap, request); + + coupled_size = opus_decoder_get_size(2); + mono_size = opus_decoder_get_size(1); + ptr = (char*)st + align(sizeof(OpusMSDecoder)); + switch (request) + { + default: + { + int s; + /* This only works for int32* params, but that's all we have right now */ + opus_uint32 *value = va_arg(ap, opus_uint32*); + for (s=0;s<st->layout.nb_streams;s++) + { + OpusDecoder *enc; + + enc = (OpusDecoder*)ptr; + if (s < st->layout.nb_coupled_streams) + ptr += align(coupled_size); + else + ptr += align(mono_size); + ret = opus_decoder_ctl(enc, request, value); + if (ret < 0) + break; + } + } + break; + } + + va_end(ap); + return ret; +} + + +void opus_multistream_decoder_destroy(OpusMSDecoder *st) +{ + free(st); +} diff --git a/src/opus_multistream.h b/src/opus_multistream.h new file mode 100644 index 00000000..32adb2bf --- /dev/null +++ b/src/opus_multistream.h @@ -0,0 +1,121 @@ +/* Copyright (c) 2011 Xiph.Org Foundation + Written by Jean-Marc Valin */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + + +#ifndef OPUS_MULTISTREAM_H +#define OPUS_MULTISTREAM_H + +#include "opus.h" + +typedef struct OpusMSEncoder OpusMSEncoder; +typedef struct OpusMSDecoder OpusMSDecoder; + +OPUS_EXPORT OpusMSEncoder *opus_multistream_encoder_create( + int Fs, /* Sampling rate of input signal (Hz) */ + int channels, /* Number of channels (1/2) in input signal */ + int streams, + int coupled_streams, + unsigned char *mapping, + int application /* Coding mode (OPUS_APPLICATION_VOIP/OPUS_APPLICATION_AUDIO) */ +); + +OPUS_EXPORT int opus_multistream_encoder_init( + OpusMSEncoder *st, /* Encoder state */ + int Fs, /* Sampling rate of input signal (Hz) */ + int channels, /* Number of channels (1/2) in input signal */ + int streams, + int coupled_streams, + unsigned char *mapping, + int application /* Coding mode (OPUS_APPLICATION_VOIP/OPUS_APPLICATION_AUDIO) */ +); + +/* Returns length of the data payload (in bytes) */ +OPUS_EXPORT int opus_multistream_encode( + OpusMSEncoder *st, /* Encoder state */ + const opus_int16 *pcm, /* Input signal (interleaved if 2 channels). length is frame_size*channels */ + int frame_size, /* Number of samples per frame of input signal */ + unsigned char *data, /* Output payload (no more than max_data_bytes long) */ + int max_data_bytes /* Allocated memory for payload; don't use for controlling bitrate */ +); + +/* Returns length of the data payload (in bytes) */ +OPUS_EXPORT int opus_multistream_encode_float( + OpusMSEncoder *st, /* Encoder state */ + const float *pcm, /* Input signal (interleaved if 2 channels). length is frame_size*channels */ + int frame_size, /* Number of samples per frame of input signal */ + unsigned char *data, /* Output payload (no more than max_data_bytes long) */ + int max_data_bytes /* Allocated memory for payload; don't use for controlling bitrate */ + ); + +OPUS_EXPORT void opus_multistream_encoder_destroy(OpusMSEncoder *st); + +OPUS_EXPORT int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...); + +OPUS_EXPORT OpusMSDecoder *opus_multistream_decoder_create( + int Fs, /* Sampling rate of input signal (Hz) */ + int channels, /* Number of channels (1/2) in input signal */ + int streams, + int coupled_streams, + unsigned char *mapping +); + +OPUS_EXPORT int opus_multistream_decoder_init( + OpusMSDecoder *st, /* Encoder state */ + int Fs, /* Sampling rate of input signal (Hz) */ + int channels, /* Number of channels (1/2) in input signal */ + int streams, + int coupled_streams, + unsigned char *mapping +); + +/* Returns the number of samples decoded or a negative error code */ +OPUS_EXPORT int opus_multistream_decode( + OpusMSDecoder *st, /* Decoder state */ + const unsigned char *data, /* Input payload. Use a NULL pointer to indicate packet loss */ + int len, /* Number of bytes in payload */ + opus_int16 *pcm, /* Output signal (interleaved if 2 channels). length is frame_size*channels */ + int frame_size, /* Number of samples per frame of input signal */ + int decode_fec /* Flag (0/1) to request that any in-band forward error correction data be */ + /* decoded. If no such data is available the frame is decoded as if it were lost. */ +); + +/* Returns the number of samples decoded or a negative error code */ +OPUS_EXPORT int opus_multistream_decode_float( + OpusMSDecoder *st, /* Decoder state */ + const unsigned char *data, /* Input payload. Use a NULL pointer to indicate packet loss */ + int len, /* Number of bytes in payload */ + float *pcm, /* Output signal (interleaved if 2 channels). length is frame_size*channels */ + int frame_size, /* Number of samples per frame of input signal */ + int decode_fec /* Flag (0/1) to request that any in-band forward error correction data be */ + /* decoded. If no such data is available the frame is decoded as if it were lost. */ +); + +OPUS_EXPORT int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...); + +OPUS_EXPORT void opus_multistream_decoder_destroy(OpusMSDecoder *st); + +#endif /* OPUS_MULTISTREAM_H */ diff --git a/src/opus_private.h b/src/opus_private.h index 9015e389..6f47866a 100644 --- a/src/opus_private.h +++ b/src/opus_private.h @@ -29,8 +29,14 @@ #ifndef OPUS_PRIVATE_H #define OPUS_PRIVATE_H +#include "arch.h" +#include "opus.h" + int encode_size(int size, unsigned char *data); +int opus_decode_native(OpusDecoder *st, const unsigned char *data, int len, + opus_val16 *pcm, int frame_size, int decode_fec, int self_delimited, int *packet_offset); + /* Make sure everything's aligned to 4 bytes (this may need to be increased on really weird architectures) */ static inline int align(int i) |