Commit 1b114a82 authored by Timothy B. Terriberry's avatar Timothy B. Terriberry
Browse files

Add an application decoding callback API.

This is needed to allow advanced usage, like that of opusdec in
 opus-tools, which can simulate packet loss or save the range coder
 state for decoder verification.
It could also be used in a pinch to use libopusfile for access to
 the raw Ogg packets, though this is somewhat of a hack.
parent 07a813ed
......@@ -1684,6 +1684,66 @@ int op_pcm_seek(OggOpusFile *_of,ogg_int64_t _pcm_offset) OP_ARG_NONNULL(1);
appropriately.*/
/*@{*/
/**Indicates that the decoding callback should produce signed 16-bit
native-endian output samples.*/
#define OP_DEC_FORMAT_SHORT (7008)
/**Indicates that the decoding callback should produce 32-bit native-endian
float samples.*/
#define OP_DEC_FORMAT_FLOAT (7040)
/**Indicates that the decoding callback did not decode anything, and that
<tt>libopusfile</tt> should decode normally instead.*/
#define OP_DEC_USE_DEFAULT (6720)
/**Called to decode an Opus packet.
This should invoke the functional equivalent of opus_multistream_decode() or
opus_multistream_decode_float(), except that it returns 0 on success
instead of the number of decoded samples (which is known a priori).
\param _ctx The application-provided callback context.
\param _decoder The decoder to use to decode the packet.
\param[out] _pcm The buffer to decode into.
This will always have enough room for \a _nchannels of
\a _nsamples samples, which should be placed into this
buffer interleaved.
\param _op The packet to decode.
This will always have its granule position set to a valid
value.
\param _nsamples The number of samples expected from the packet.
\param _nchannels The number of channels expected from the packet.
\param _format The desired sample output format.
This is either #OP_DEC_FORMAT_SHORT or
#OP_DEC_FORMAT_FLOAT.
\param _li The index of the link from which this packet was decoded.
\return A non-negative value on success, or a negative value on error.
The error codes should be the same as those returned by
opus_multistream_decode() or opus_multistream_decode_float().
\retval 0 Decoding was successful.
The application has filled the buffer with
exactly <code>\a _nsamples*\a
_nchannels</code> samples in the requested
format.
\retval #OP_DEC_USE_DEFAULT No decoding was done.
<tt>libopusfile</tt> should decode normally
instead.*/
typedef int (*op_decode_cb_func)(void *_ctx,OpusMSDecoder *_decoder,void *_pcm,
const ogg_packet *_op,int _nsamples,int _nchannels,int _format,int _li);
/**Sets the packet decode callback function.
This is called once for each packet that needs to be decoded.
A call to this function is no guarantee that the audio will eventually be
delivered to the application.
Some or all of the data from the packet may be discarded (i.e., at the
beginning or end of a link, or after a seek), however the callback is
required to provide all of it.
\param _of The \c OggOpusFile on which to set the decode callback.
\param _decode_cb The callback function to call.
This may be <code>NULL</code> to disable calling the
callback.
\param _ctx The application-provided context pointer to pass to the
callback on each call.*/
void op_set_decode_callback(OggOpusFile *_of,
op_decode_cb_func _decode_cb,void *_ctx) OP_ARG_NONNULL(1);
/**Gain offset type that indicates that the provided offset is relative to the
header gain.
This is the default.*/
......
......@@ -203,6 +203,10 @@ struct OggOpusFile{
int op_count;
/*Central working state for the packet-to-PCM decoder.*/
OpusMSDecoder *od;
/*The application-provided packet decode callback.*/
op_decode_cb_func decode_cb;
/*The application-provided packet decode callback context.*/
void *decode_cb_ctx;
/*The stream count used to initialize the decoder.*/
int od_stream_count;
/*The coupled stream count used to initialize the decoder.*/
......
......@@ -2548,6 +2548,12 @@ ogg_int64_t op_pcm_tell(OggOpusFile *_of){
return op_get_pcm_offset(_of,gp,li);
}
void op_set_decode_callback(OggOpusFile *_of,
op_decode_cb_func _decode_cb,void *_ctx){
_of->decode_cb=_decode_cb;
_of->decode_cb_ctx=_ctx;
}
int op_set_gain_offset(OggOpusFile *_of,
int _gain_type,opus_int32 _gain_offset_q8){
if(_gain_type!=OP_HEADER_GAIN&&_gain_type!=OP_TRACK_GAIN
......@@ -2586,6 +2592,39 @@ static int op_init_buffer(OggOpusFile *_of){
return 0;
}
/*Decode a single packet into the target buffer.*/
static int op_decode(OggOpusFile *_of,op_sample *_pcm,
const ogg_packet *_op,int _nsamples,int _nchannels){
int ret;
/*First we try using the application-provided decode callback.*/
if(_of->decode_cb!=NULL){
#if defined(OP_FIXED_POINT)
ret=(*_of->decode_cb)(_of->decode_cb_ctx,_of->od,_pcm,_op,
_nsamples,_nchannels,OP_DEC_FORMAT_SHORT,_of->cur_link);
#else
ret=(*_of->decode_cb)(_of->decode_cb_ctx,_of->od,_pcm,_op,
_nsamples,_nchannels,OP_DEC_FORMAT_FLOAT,_of->cur_link);
#endif
}
else ret=OP_DEC_USE_DEFAULT;
/*If the application didn't want to handle decoding, do it ourselves.*/
if(ret==OP_DEC_USE_DEFAULT){
#if defined(OP_FIXED_POINT)
ret=opus_multistream_decode(_of->od,
_op->packet,_op->bytes,_pcm,_nsamples,0);
#else
ret=opus_multistream_decode_float(_of->od,
_op->packet,_op->bytes,_pcm,_nsamples,0);
#endif
OP_ASSERT(ret<0||ret==_nsamples);
}
/*If the application returned a positive value other than 0 or
OP_DEC_USE_DEFAULT, fail.*/
else if(OP_UNLIKELY(ret>0))return OP_EBADPACKET;
if(OP_UNLIKELY(ret<0))return OP_EBADPACKET;
return ret;
}
/*Read more samples from the stream, using the same API as op_read() or
op_read_float().*/
static int op_read_native(OggOpusFile *_of,
......@@ -2649,15 +2688,8 @@ static int op_read_native(OggOpusFile *_of,
if(OP_UNLIKELY(ret<0))return ret;
buf=_of->od_buffer;
}
#if defined(OP_FIXED_POINT)
ret=opus_multistream_decode(_of->od,
pop->packet,pop->bytes,buf,120*48,0);
#else
ret=opus_multistream_decode_float(_of->od,
pop->packet,pop->bytes,buf,120*48,0);
#endif
if(OP_UNLIKELY(ret<0))return OP_EBADPACKET;
OP_ASSERT(ret==duration);
ret=op_decode(_of,buf,pop,duration,nchannels);
if(OP_UNLIKELY(ret<0))return ret;
/*Perform pre-skip/pre-roll.*/
od_buffer_pos=(int)OP_MIN(trimmed_duration,cur_discard_count);
cur_discard_count-=od_buffer_pos;
......@@ -2673,15 +2705,8 @@ static int op_read_native(OggOpusFile *_of,
}
else{
/*Otherwise decode directly into the user's buffer.*/
#if defined(OP_FIXED_POINT)
ret=opus_multistream_decode(_of->od,pop->packet,pop->bytes,
_pcm,_buf_size/nchannels,0);
#else
ret=opus_multistream_decode_float(_of->od,pop->packet,pop->bytes,
_pcm,_buf_size/nchannels,0);
#endif
if(OP_UNLIKELY(ret<0))return OP_EBADPACKET;
OP_ASSERT(ret==duration);
ret=op_decode(_of,_pcm,pop,duration,nchannels);
if(OP_UNLIKELY(ret<0))return ret;
if(OP_LIKELY(trimmed_duration>0)){
/*Perform pre-skip/pre-roll.*/
od_buffer_pos=(int)OP_MIN(trimmed_duration,cur_discard_count);
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment