Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Channel mapping 255 and Ambisonics mapping 2 and 3 #45

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
Open
14 changes: 13 additions & 1 deletion include/opusfile.h
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,9 @@ extern "C" {
# include <stdio.h>
# include <ogg/ogg.h>
# include <opus_multistream.h>

#ifdef OPUS_HAVE_OPUS_PROJECTION_H
#include <opus_projection.h>
#endif
/**@cond PRIVATE*/

/*Enable special features for gcc and gcc-compatible compilers.*/
Expand Down Expand Up @@ -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.*/
Expand Down Expand Up @@ -266,6 +273,11 @@ struct OpusHead{
Otherwise, it refers to the output of the uncoupled stream
<code>(index-coupled_count)</code>.*/
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.
Expand Down
92 changes: 91 additions & 1 deletion src/info.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 5 additions & 1 deletion src/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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.*/
Expand Down
48 changes: 43 additions & 5 deletions src/opusfile.c
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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);
}
Expand Down