Index: src/opus_multistream_decoder.c |
diff --git a/src/opus_multistream_decoder.c b/src/opus_multistream_decoder.c |
new file mode 100644 |
index 0000000000000000000000000000000000000000..f3d2311e450b5c4131c0d00a0c2604a14bbbd1e0 |
--- /dev/null |
+++ b/src/opus_multistream_decoder.c |
@@ -0,0 +1,499 @@ |
+/* 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 COPYRIGHT OWNER |
+ 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 <stdarg.h> |
+#include "float_cast.h" |
+#include "os_support.h" |
+ |
+struct OpusMSDecoder { |
+ ChannelLayout layout; |
+ /* Decoder states go here */ |
+}; |
+ |
+ |
+ |
+ |
+/* DECODER */ |
+ |
+opus_int32 opus_multistream_decoder_get_size(int nb_streams, int nb_coupled_streams) |
+{ |
+ int coupled_size; |
+ int mono_size; |
+ |
+ if(nb_streams<1||nb_coupled_streams>nb_streams||nb_coupled_streams<0)return 0; |
+ 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, |
+ opus_int32 Fs, |
+ int channels, |
+ int streams, |
+ int coupled_streams, |
+ const unsigned char *mapping |
+) |
+{ |
+ int coupled_size; |
+ int mono_size; |
+ int i, ret; |
+ char *ptr; |
+ |
+ if ((channels>255) || (channels<1) || (coupled_streams>streams) || |
+ (coupled_streams+streams>255) || (streams<1) || (coupled_streams<0)) |
+ return OPUS_BAD_ARG; |
+ |
+ 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(OpusMSDecoder)); |
+ coupled_size = opus_decoder_get_size(2); |
+ mono_size = opus_decoder_get_size(1); |
+ |
+ for (i=0;i<st->layout.nb_coupled_streams;i++) |
+ { |
+ ret=opus_decoder_init((OpusDecoder*)ptr, Fs, 2); |
+ if(ret!=OPUS_OK)return ret; |
+ ptr += align(coupled_size); |
+ } |
+ for (;i<st->layout.nb_streams;i++) |
+ { |
+ ret=opus_decoder_init((OpusDecoder*)ptr, Fs, 1); |
+ if(ret!=OPUS_OK)return ret; |
+ ptr += align(mono_size); |
+ } |
+ return OPUS_OK; |
+} |
+ |
+ |
+OpusMSDecoder *opus_multistream_decoder_create( |
+ opus_int32 Fs, |
+ int channels, |
+ int streams, |
+ int coupled_streams, |
+ const unsigned char *mapping, |
+ int *error |
+) |
+{ |
+ int ret; |
+ OpusMSDecoder *st; |
+ if ((channels>255) || (channels<1) || (coupled_streams>streams) || |
+ (coupled_streams+streams>255) || (streams<1) || (coupled_streams<0)) |
+ { |
+ if (error) |
+ *error = OPUS_BAD_ARG; |
+ return NULL; |
+ } |
+ st = (OpusMSDecoder *)opus_alloc(opus_multistream_decoder_get_size(streams, coupled_streams)); |
+ if (st==NULL) |
+ { |
+ if (error) |
+ *error = OPUS_ALLOC_FAIL; |
+ return NULL; |
+ } |
+ ret = opus_multistream_decoder_init(st, Fs, channels, streams, coupled_streams, mapping); |
+ if (error) |
+ *error = ret; |
+ if (ret != OPUS_OK) |
+ { |
+ opus_free(st); |
+ st = NULL; |
+ } |
+ return st; |
+} |
+ |
+typedef void (*opus_copy_channel_out_func)( |
+ void *dst, |
+ int dst_stride, |
+ int dst_channel, |
+ const opus_val16 *src, |
+ int src_stride, |
+ int frame_size |
+); |
+ |
+static int opus_multistream_decode_native( |
+ OpusMSDecoder *st, |
+ const unsigned char *data, |
+ opus_int32 len, |
+ void *pcm, |
+ opus_copy_channel_out_func copy_channel_out, |
+ int frame_size, |
+ int decode_fec, |
+ int soft_clip |
+) |
+{ |
+ opus_int32 Fs; |
+ int coupled_size; |
+ int mono_size; |
+ int s, c; |
+ char *ptr; |
+ int do_plc=0; |
+ VARDECL(opus_val16, buf); |
+ ALLOC_STACK; |
+ |
+ /* Limit frame_size to avoid excessive stack allocations. */ |
+ opus_multistream_decoder_ctl(st, OPUS_GET_SAMPLE_RATE(&Fs)); |
+ frame_size = IMIN(frame_size, Fs/25*3); |
+ ALLOC(buf, 2*frame_size, opus_val16); |
+ ptr = (char*)st + align(sizeof(OpusMSDecoder)); |
+ coupled_size = opus_decoder_get_size(2); |
+ mono_size = opus_decoder_get_size(1); |
+ |
+ if (len==0) |
+ do_plc = 1; |
+ if (len < 0) |
+ return OPUS_BAD_ARG; |
+ if (!do_plc && len < 2*st->layout.nb_streams-1) |
+ return OPUS_INVALID_PACKET; |
+ 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 (!do_plc && len<=0) |
+ { |
+ RESTORE_STACK; |
+ return OPUS_INVALID_PACKET; |
+ } |
+ packet_offset = 0; |
+ ret = opus_decode_native(dec, data, len, buf, frame_size, decode_fec, s!=st->layout.nb_streams-1, &packet_offset, soft_clip); |
+ 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_INVALID_PACKET; |
+ } |
+ 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) |
+ { |
+ (*copy_channel_out)(pcm, st->layout.nb_channels, chan, |
+ buf, 2, frame_size); |
+ prev = chan; |
+ } |
+ prev = -1; |
+ /* Copy "right" audio to the channel(s) where it belongs */ |
+ while ( (chan = get_right_channel(&st->layout, s, prev)) != -1) |
+ { |
+ (*copy_channel_out)(pcm, st->layout.nb_channels, chan, |
+ buf+1, 2, frame_size); |
+ 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) |
+ { |
+ (*copy_channel_out)(pcm, st->layout.nb_channels, chan, |
+ buf, 1, frame_size); |
+ prev = chan; |
+ } |
+ } |
+ } |
+ /* Handle muted channels */ |
+ for (c=0;c<st->layout.nb_channels;c++) |
+ { |
+ if (st->layout.mapping[c] == 255) |
+ { |
+ (*copy_channel_out)(pcm, st->layout.nb_channels, c, |
+ NULL, 0, frame_size); |
+ } |
+ } |
+ RESTORE_STACK; |
+ return frame_size; |
+} |
+ |
+#if !defined(DISABLE_FLOAT_API) |
+static void opus_copy_channel_out_float( |
+ void *dst, |
+ int dst_stride, |
+ int dst_channel, |
+ const opus_val16 *src, |
+ int src_stride, |
+ int frame_size |
+) |
+{ |
+ float *float_dst; |
+ opus_int32 i; |
+ float_dst = (float*)dst; |
+ if (src != NULL) |
+ { |
+ for (i=0;i<frame_size;i++) |
+#if defined(FIXED_POINT) |
+ float_dst[i*dst_stride+dst_channel] = (1/32768.f)*src[i*src_stride]; |
+#else |
+ float_dst[i*dst_stride+dst_channel] = src[i*src_stride]; |
+#endif |
+ } |
+ else |
+ { |
+ for (i=0;i<frame_size;i++) |
+ float_dst[i*dst_stride+dst_channel] = 0; |
+ } |
+} |
+#endif |
+ |
+static void opus_copy_channel_out_short( |
+ void *dst, |
+ int dst_stride, |
+ int dst_channel, |
+ const opus_val16 *src, |
+ int src_stride, |
+ int frame_size |
+) |
+{ |
+ opus_int16 *short_dst; |
+ opus_int32 i; |
+ short_dst = (opus_int16*)dst; |
+ if (src != NULL) |
+ { |
+ for (i=0;i<frame_size;i++) |
+#if defined(FIXED_POINT) |
+ short_dst[i*dst_stride+dst_channel] = src[i*src_stride]; |
+#else |
+ short_dst[i*dst_stride+dst_channel] = FLOAT2INT16(src[i*src_stride]); |
+#endif |
+ } |
+ else |
+ { |
+ for (i=0;i<frame_size;i++) |
+ short_dst[i*dst_stride+dst_channel] = 0; |
+ } |
+} |
+ |
+ |
+ |
+#ifdef FIXED_POINT |
+int opus_multistream_decode( |
+ OpusMSDecoder *st, |
+ const unsigned char *data, |
+ opus_int32 len, |
+ opus_int16 *pcm, |
+ int frame_size, |
+ int decode_fec |
+) |
+{ |
+ return opus_multistream_decode_native(st, data, len, |
+ pcm, opus_copy_channel_out_short, frame_size, decode_fec, 0); |
+} |
+ |
+#ifndef DISABLE_FLOAT_API |
+int opus_multistream_decode_float(OpusMSDecoder *st, const unsigned char *data, |
+ opus_int32 len, float *pcm, int frame_size, int decode_fec) |
+{ |
+ return opus_multistream_decode_native(st, data, len, |
+ pcm, opus_copy_channel_out_float, frame_size, decode_fec, 0); |
+} |
+#endif |
+ |
+#else |
+ |
+int opus_multistream_decode(OpusMSDecoder *st, const unsigned char *data, |
+ opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec) |
+{ |
+ return opus_multistream_decode_native(st, data, len, |
+ pcm, opus_copy_channel_out_short, frame_size, decode_fec, 1); |
+} |
+ |
+int opus_multistream_decode_float( |
+ OpusMSDecoder *st, |
+ const unsigned char *data, |
+ opus_int32 len, |
+ float *pcm, |
+ int frame_size, |
+ int decode_fec |
+) |
+{ |
+ return opus_multistream_decode_native(st, data, len, |
+ pcm, opus_copy_channel_out_float, frame_size, decode_fec, 0); |
+} |
+#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) |
+ { |
+ case OPUS_GET_BANDWIDTH_REQUEST: |
+ case OPUS_GET_SAMPLE_RATE_REQUEST: |
+ case OPUS_GET_GAIN_REQUEST: |
+ case OPUS_GET_LAST_PACKET_DURATION_REQUEST: |
+ { |
+ OpusDecoder *dec; |
+ /* For int32* GET params, just query the first stream */ |
+ opus_int32 *value = va_arg(ap, opus_int32*); |
+ dec = (OpusDecoder*)ptr; |
+ ret = opus_decoder_ctl(dec, request, value); |
+ } |
+ break; |
+ case OPUS_GET_FINAL_RANGE_REQUEST: |
+ { |
+ int s; |
+ opus_uint32 *value = va_arg(ap, opus_uint32*); |
+ opus_uint32 tmp; |
+ if (!value) |
+ { |
+ goto bad_arg; |
+ } |
+ *value = 0; |
+ for (s=0;s<st->layout.nb_streams;s++) |
+ { |
+ OpusDecoder *dec; |
+ dec = (OpusDecoder*)ptr; |
+ if (s < st->layout.nb_coupled_streams) |
+ ptr += align(coupled_size); |
+ else |
+ ptr += align(mono_size); |
+ ret = opus_decoder_ctl(dec, request, &tmp); |
+ if (ret != OPUS_OK) break; |
+ *value ^= tmp; |
+ } |
+ } |
+ break; |
+ case OPUS_RESET_STATE: |
+ { |
+ int s; |
+ for (s=0;s<st->layout.nb_streams;s++) |
+ { |
+ OpusDecoder *dec; |
+ |
+ dec = (OpusDecoder*)ptr; |
+ if (s < st->layout.nb_coupled_streams) |
+ ptr += align(coupled_size); |
+ else |
+ ptr += align(mono_size); |
+ ret = opus_decoder_ctl(dec, OPUS_RESET_STATE); |
+ if (ret != OPUS_OK) |
+ break; |
+ } |
+ } |
+ break; |
+ case OPUS_MULTISTREAM_GET_DECODER_STATE_REQUEST: |
+ { |
+ int s; |
+ opus_int32 stream_id; |
+ OpusDecoder **value; |
+ stream_id = va_arg(ap, opus_int32); |
+ if (stream_id<0 || stream_id >= st->layout.nb_streams) |
+ ret = OPUS_BAD_ARG; |
+ value = va_arg(ap, OpusDecoder**); |
+ if (!value) |
+ { |
+ goto bad_arg; |
+ } |
+ for (s=0;s<stream_id;s++) |
+ { |
+ if (s < st->layout.nb_coupled_streams) |
+ ptr += align(coupled_size); |
+ else |
+ ptr += align(mono_size); |
+ } |
+ *value = (OpusDecoder*)ptr; |
+ } |
+ break; |
+ case OPUS_SET_GAIN_REQUEST: |
+ { |
+ int s; |
+ /* This works for int32 params */ |
+ opus_int32 value = va_arg(ap, opus_int32); |
+ for (s=0;s<st->layout.nb_streams;s++) |
+ { |
+ OpusDecoder *dec; |
+ |
+ dec = (OpusDecoder*)ptr; |
+ if (s < st->layout.nb_coupled_streams) |
+ ptr += align(coupled_size); |
+ else |
+ ptr += align(mono_size); |
+ ret = opus_decoder_ctl(dec, request, value); |
+ if (ret != OPUS_OK) |
+ break; |
+ } |
+ } |
+ break; |
+ default: |
+ ret = OPUS_UNIMPLEMENTED; |
+ break; |
+ } |
+ |
+ va_end(ap); |
+ return ret; |
+bad_arg: |
+ va_end(ap); |
+ return OPUS_BAD_ARG; |
+} |
+ |
+ |
+void opus_multistream_decoder_destroy(OpusMSDecoder *st) |
+{ |
+ opus_free(st); |
+} |