ref: 1b114a82b93eb18f8cc87331236660a5f4bf3992
parent: 07a813ed87eda4d20745f4960d71c6a7805d982d
author: Timothy B. Terriberry <tterribe@xiph.org>
date: Wed Aug 28 19:37:31 EDT 2013
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.
--- a/include/opusfile.h
+++ b/include/opusfile.h
@@ -1684,6 +1684,66 @@
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.*/
--- a/src/internal.h
+++ b/src/internal.h
@@ -203,6 +203,10 @@
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.*/
--- a/src/opusfile.c
+++ b/src/opusfile.c
@@ -2548,6 +2548,12 @@
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 @@
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 @@
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 @@
}
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);