diff --git a/include/opusfile.h b/include/opusfile.h index 8e7aa09..00f16f4 100644 --- a/include/opusfile.h +++ b/include/opusfile.h @@ -108,7 +108,9 @@ extern "C" { # include # include # include - +#ifdef OPUS_HAVE_OPUS_PROJECTION_H +#include +#endif /**@cond PRIVATE*/ /*Enable special features for gcc and gcc-compatible compilers.*/ @@ -209,6 +211,11 @@ typedef struct OggOpusFile OggOpusFile; /**The maximum number of channels in an Ogg Opus stream.*/ #define OPUS_CHANNEL_COUNT_MAX (255) +#ifdef OPUS_HAVE_OPUS_PROJECTION_H +/**The maximum size of projection decoder demixing matrix.*/ +#define OPUS_DEMIXING_MATRIX_SIZE_MAX (18 * 18 * 2) +#endif + /**Ogg Opus bitstream information. This contains the basic playback parameters for a stream, and corresponds to the initial ID header packet of an Ogg Opus stream.*/ @@ -266,6 +273,11 @@ struct OpusHead{ Otherwise, it refers to the output of the uncoupled stream (index-coupled_count).*/ unsigned char mapping[OPUS_CHANNEL_COUNT_MAX]; + +#ifdef OPUS_HAVE_OPUS_PROJECTION_H + /**The demixing matrix of the projection decoder.*/ + unsigned char dmatrix[OPUS_DEMIXING_MATRIX_SIZE_MAX]; +#endif }; /**The metadata from an Ogg Opus stream. diff --git a/src/info.c b/src/info.c index 000c74b..b1f2fbf 100644 --- a/src/info.c +++ b/src/info.c @@ -78,9 +78,99 @@ int opus_head_parse(OpusHead *_head,const unsigned char *_data,size_t _len){ } if(_head!=NULL)memcpy(_head->mapping,_data+21,head.channel_count); } + /*Ambisonics channel mapping*/ + else if(head.mapping_family==2) + { + size_t size; + int ci; + if (head.channel_count < 1 || head.channel_count > OP_NCHANNELS_MAX) + return OP_EBADHEADER; + size = 21 + head.channel_count; + if (_len < size || head.version <= 1 && _len > size) + return OP_EBADHEADER; + head.stream_count = _data[19]; + if (head.stream_count < 1) + return OP_EBADHEADER; + head.coupled_count = _data[20]; + if (head.coupled_count > head.stream_count) + return OP_EBADHEADER; + for (ci = 0; ci < head.channel_count; ci++) + { + if (_data[21 + ci] >= head.stream_count + head.coupled_count && + _data[21 + ci] != 255) + { + return OP_EBADHEADER; + } + } + if (_head != NULL) + memcpy(_head->mapping, _data + 21, head.channel_count); + } + else if(head.mapping_family==3) + { + /*Use channel mapping 3 for orders {1, 2, 3} with 4 to 18 channels + (including the non-diegetic stereo track). For other orders with no + demixing matrices currently available, use channel mapping 2.*/ +#ifdef OPUS_HAVE_OPUS_PROJECTION_H + size_t size; + size_t dmatrix_size; + int i; + if (head.channel_count < 1 || head.channel_count > OP_NCHANNELS_MAX) + return OP_EBADHEADER; + + head.stream_count = _data[19]; + if (head.stream_count < 1) + return OP_EBADHEADER; + + head.coupled_count = _data[20]; + if (head.coupled_count > head.stream_count) + return OP_EBADHEADER; + + size = 21 + (head.channel_count * (head.stream_count+head.coupled_count)*2); + + if (_len < size || head.version <= 1 && _len > size) + return OP_EBADHEADER; + + dmatrix_size = head.channel_count*(head.stream_count+head.coupled_count) * + sizeof(opus_int16); + if (dmatrix_size > OPUS_DEMIXING_MATRIX_SIZE_MAX) + return OP_EBADHEADER; + memcpy(_head->dmatrix, _data + 21, dmatrix_size); + if (_head != NULL){ + for (i = 0; i < head.channel_count; i++) + _head->mapping[i] = i; + } +#else + return OP_EIMPL; +#endif + } /*General purpose players should not attempt to play back content with channel mapping family 255.*/ - else if(head.mapping_family==255)return OP_EIMPL; + else if(head.mapping_family==255) + { + size_t size; + int ci; + if (head.channel_count < 1 || head.channel_count > OP_NCHANNELS_MAX) + return OP_EBADHEADER; + size = 21 + head.channel_count; + if (_len < size || head.version <= 1 && _len > size) + return OP_EBADHEADER; + head.stream_count = _data[19]; + if (head.stream_count < 1) + return OP_EBADHEADER; + head.coupled_count = _data[20]; + if (head.coupled_count > head.stream_count) + return OP_EBADHEADER; + for (ci = 0; ci < head.channel_count; ci++) + { + if (_data[21 + ci] >= head.stream_count + head.coupled_count && + _data[21 + ci] != 255) + { + return OP_EBADHEADER; + } + } + if (_head != NULL) + memcpy(_head->mapping, _data + 21, head.channel_count); + } /*No other channel mapping families are currently defined.*/ else return OP_EBADHEADER; if(_head!=NULL)memcpy(_head,&head,head.mapping-(unsigned char *)&head); diff --git a/src/internal.h b/src/internal.h index 0c2d2bb..545e27d 100644 --- a/src/internal.h +++ b/src/internal.h @@ -109,7 +109,7 @@ void op_fatal_impl(const char *_str,const char *_file,int _line); (OP_MIN(_offset,OP_INT64_MAX-(_amount))+(_amount)) /*The maximum channel count for any mapping we'll actually decode.*/ -# define OP_NCHANNELS_MAX (8) +# define OP_NCHANNELS_MAX (255) /*Initial state.*/ # define OP_NOTOPEN (0) @@ -213,6 +213,10 @@ struct OggOpusFile{ int op_count; /*Central working state for the packet-to-PCM decoder.*/ OpusMSDecoder *od; +#ifdef OPUS_HAVE_OPUS_PROJECTION_H + /*Projection decoder state.*/ + OpusProjectionDecoder *st; +#endif /*The application-provided packet decode callback.*/ op_decode_cb_func decode_cb; /*The application-provided packet decode callback context.*/ diff --git a/src/opusfile.c b/src/opusfile.c index 3c3c81e..0e4433d 100644 --- a/src/opusfile.c +++ b/src/opusfile.c @@ -1335,7 +1335,16 @@ static void op_update_gain(OggOpusFile *_of){ gain_q8=OP_CLAMP(-32768,gain_q8,32767); OP_ASSERT(_of->od!=NULL); #if defined(OPUS_SET_GAIN) +# ifdef OPUS_HAVE_OPUS_PROJECTION_H + if(_of->od==NULL){ + opus_projection_decoder_ctl(_of->st,OPUS_SET_GAIN(gain_q8)); + } + else{ + opus_multistream_decoder_ctl(_of->od,OPUS_SET_GAIN(gain_q8)); + } +# else opus_multistream_decoder_ctl(_of->od,OPUS_SET_GAIN(gain_q8)); +# endif #else /*A fallback that works with both float and fixed-point is a bunch of work, so just force people to use a sufficiently new version. @@ -1366,10 +1375,28 @@ static int op_make_decode_ready(OggOpusFile *_of){ } else{ int err; - opus_multistream_decoder_destroy(_of->od); - _of->od=opus_multistream_decoder_create(48000,channel_count, - stream_count,coupled_count,head->mapping,&err); - if(_of->od==NULL)return OP_EFAULT; + if(head->mapping_family==3){ +#ifdef OPUS_HAVE_OPUS_PROJECTION_H + OpusProjectionDecoder *st_dec; + const int dmatrix_size = (stream_count + coupled_count) * channel_count * + sizeof(opus_int16); + opus_projection_decoder_destroy(_of->st); + st_dec = opus_projection_decoder_create(48000,channel_count, + stream_count,coupled_count,(unsigned char*)head->dmatrix,dmatrix_size,&err); + /*Replace od with st*/ + opus_multistream_decoder_destroy(_of->od); + _of->st = st_dec; + if(_of->st==NULL)return OP_EFAULT; +#else + return OP_EIMPL; +#endif + } + else{ + opus_multistream_decoder_destroy(_of->od); + _of->od=opus_multistream_decoder_create(48000,channel_count, + stream_count,coupled_count,head->mapping,&err); + if(_of->od==NULL)return OP_EFAULT; + } _of->od_stream_count=stream_count; _of->od_coupled_count=coupled_count; _of->od_channel_count=channel_count; @@ -2809,8 +2836,19 @@ static int op_decode(OggOpusFile *_of,op_sample *_pcm, ret=opus_multistream_decode(_of->od, _op->packet,_op->bytes,_pcm,_nsamples,0); #else +# ifdef OPUS_HAVE_OPUS_PROJECTION_H + if(_of->st!=NULL){ + ret=opus_projection_decode_float(_of->st, + _op->packet,_op->bytes,_pcm,_nsamples,0); + } + else{ + ret=opus_multistream_decode_float(_of->od, + _op->packet,_op->bytes,_pcm,_nsamples,0); + } +# else ret=opus_multistream_decode_float(_of->od, - _op->packet,_op->bytes,_pcm,_nsamples,0); + _op->packet,_op->bytes,_pcm,_nsamples,0); +# endif #endif OP_ASSERT(ret<0||ret==_nsamples); }