Skip to content

Channel mapping 255 and Ambisonics mapping 2 and 3 #45

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

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion include/opusfile.h
Original file line number Diff line number Diff line change
@@ -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.*/
@@ -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
<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.
92 changes: 91 additions & 1 deletion src/info.c
Original file line number Diff line number Diff line change
@@ -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);
6 changes: 5 additions & 1 deletion src/internal.h
Original file line number Diff line number Diff line change
@@ -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.*/
48 changes: 43 additions & 5 deletions src/opusfile.c
Original file line number Diff line number Diff line change
@@ -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);
}