From 004377b0b526182c2c24f374d88c038d9b99800d Mon Sep 17 00:00:00 2001 From: Mark Nauwelaerts Date: Mon, 27 Feb 2012 23:46:03 +0100 Subject: [PATCH] wavpackdec: port to audiodecoder --- ext/wavpack/gstwavpackdec.c | 296 +++++++++++++----------------------- ext/wavpack/gstwavpackdec.h | 12 +- 2 files changed, 110 insertions(+), 198 deletions(-) diff --git a/ext/wavpack/gstwavpackdec.c b/ext/wavpack/gstwavpackdec.c index 4ba242d875..133b51b60a 100644 --- a/ext/wavpack/gstwavpackdec.c +++ b/ext/wavpack/gstwavpackdec.c @@ -55,8 +55,6 @@ #include "gstwavpackstreamreader.h" -#define WAVPACK_DEC_MAX_ERRORS 16 - GST_DEBUG_CATEGORY_STATIC (gst_wavpack_dec_debug); #define GST_CAT_DEFAULT gst_wavpack_dec_debug @@ -80,15 +78,18 @@ static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src", "endianness = (int) BYTE_ORDER, " "signed = (boolean) true") ); -static GstFlowReturn gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buffer); -static gboolean gst_wavpack_dec_sink_set_caps (GstPad * pad, GstCaps * caps); -static gboolean gst_wavpack_dec_sink_event (GstPad * pad, GstEvent * event); +static gboolean gst_wavpack_dec_start (GstAudioDecoder * dec); +static gboolean gst_wavpack_dec_stop (GstAudioDecoder * dec); +static gboolean gst_wavpack_dec_set_format (GstAudioDecoder * dec, + GstCaps * caps); +static GstFlowReturn gst_wavpack_dec_handle_frame (GstAudioDecoder * dec, + GstBuffer * buffer); + static void gst_wavpack_dec_finalize (GObject * object); -static GstStateChangeReturn gst_wavpack_dec_change_state (GstElement * element, - GstStateChange transition); static void gst_wavpack_dec_post_tags (GstWavpackDec * dec); -GST_BOILERPLATE (GstWavpackDec, gst_wavpack_dec, GstElement, GST_TYPE_ELEMENT); +GST_BOILERPLATE (GstWavpackDec, gst_wavpack_dec, GstAudioDecoder, + GST_TYPE_AUDIO_DECODER); static void gst_wavpack_dec_base_init (gpointer klass) @@ -108,11 +109,14 @@ static void gst_wavpack_dec_class_init (GstWavpackDecClass * klass) { GObjectClass *gobject_class = (GObjectClass *) klass; - GstElementClass *gstelement_class = (GstElementClass *) klass; + GstAudioDecoderClass *base_class = (GstAudioDecoderClass *) (klass); - gstelement_class->change_state = - GST_DEBUG_FUNCPTR (gst_wavpack_dec_change_state); gobject_class->finalize = gst_wavpack_dec_finalize; + + base_class->start = GST_DEBUG_FUNCPTR (gst_wavpack_dec_start); + base_class->stop = GST_DEBUG_FUNCPTR (gst_wavpack_dec_stop); + base_class->set_format = GST_DEBUG_FUNCPTR (gst_wavpack_dec_set_format); + base_class->handle_frame = GST_DEBUG_FUNCPTR (gst_wavpack_dec_handle_frame); } static void @@ -121,33 +125,15 @@ gst_wavpack_dec_reset (GstWavpackDec * dec) dec->wv_id.buffer = NULL; dec->wv_id.position = dec->wv_id.length = 0; - dec->error_count = 0; - dec->channels = 0; dec->channel_mask = 0; dec->sample_rate = 0; dec->depth = 0; - - gst_segment_init (&dec->segment, GST_FORMAT_TIME); - dec->next_block_index = 0; } static void gst_wavpack_dec_init (GstWavpackDec * dec, GstWavpackDecClass * gklass) { - dec->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink"); - gst_pad_set_chain_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (gst_wavpack_dec_chain)); - gst_pad_set_setcaps_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (gst_wavpack_dec_sink_set_caps)); - gst_pad_set_event_function (dec->sinkpad, - GST_DEBUG_FUNCPTR (gst_wavpack_dec_sink_event)); - gst_element_add_pad (GST_ELEMENT (dec), dec->sinkpad); - - dec->srcpad = gst_pad_new_from_static_template (&src_factory, "src"); - gst_pad_use_fixed_caps (dec->srcpad); - gst_element_add_pad (GST_ELEMENT (dec), dec->srcpad); - dec->context = NULL; dec->stream_reader = gst_wavpack_stream_reader_new (); @@ -166,26 +152,73 @@ gst_wavpack_dec_finalize (GObject * object) } static gboolean -gst_wavpack_dec_sink_set_caps (GstPad * pad, GstCaps * caps) +gst_wavpack_dec_start (GstAudioDecoder * dec) { - GstWavpackDec *dec = GST_WAVPACK_DEC (gst_pad_get_parent (pad)); + GST_DEBUG_OBJECT (dec, "start"); + + /* never mind a few errors */ + gst_audio_decoder_set_max_errors (dec, 16); + /* don't bother us with flushing */ + gst_audio_decoder_set_drainable (dec, FALSE); + + return TRUE; +} + +static gboolean +gst_wavpack_dec_stop (GstAudioDecoder * dec) +{ + GstWavpackDec *wpdec = GST_WAVPACK_DEC (dec); + + GST_DEBUG_OBJECT (dec, "stop"); + + if (wpdec->context) { + WavpackCloseFile (wpdec->context); + wpdec->context = NULL; + } + + gst_wavpack_dec_reset (wpdec); + + return TRUE; +} + +static void +gst_wavpack_dec_negotiate (GstWavpackDec * dec) +{ + GstCaps *caps; + + caps = gst_caps_new_simple ("audio/x-raw-int", + "rate", G_TYPE_INT, dec->sample_rate, + "channels", G_TYPE_INT, dec->channels, + "depth", G_TYPE_INT, dec->depth, + "width", G_TYPE_INT, 32, + "endianness", G_TYPE_INT, G_BYTE_ORDER, + "signed", G_TYPE_BOOLEAN, TRUE, NULL); + + /* Only set the channel layout for more than two channels + * otherwise things break unfortunately */ + if (dec->channel_mask != 0 && dec->channels > 2) + if (!gst_wavpack_set_channel_layout (caps, dec->channel_mask)) + GST_WARNING_OBJECT (dec, "Failed to set channel layout"); + + GST_DEBUG_OBJECT (dec, "setting caps %" GST_PTR_FORMAT, caps); + + /* should always succeed */ + gst_pad_set_caps (GST_AUDIO_DECODER_SRC_PAD (dec), caps); + gst_caps_unref (caps); +} + +static gboolean +gst_wavpack_dec_set_format (GstAudioDecoder * bdec, GstCaps * caps) +{ + GstWavpackDec *dec = GST_WAVPACK_DEC (bdec); GstStructure *structure = gst_caps_get_structure (caps, 0); /* Check if we can set the caps here already */ if (gst_structure_get_int (structure, "channels", &dec->channels) && gst_structure_get_int (structure, "rate", &dec->sample_rate) && gst_structure_get_int (structure, "width", &dec->depth)) { - GstCaps *caps; GstAudioChannelPosition *pos; - caps = gst_caps_new_simple ("audio/x-raw-int", - "rate", G_TYPE_INT, dec->sample_rate, - "channels", G_TYPE_INT, dec->channels, - "depth", G_TYPE_INT, dec->depth, - "width", G_TYPE_INT, 32, - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "signed", G_TYPE_BOOLEAN, TRUE, NULL); - /* If we already have the channel layout set from upstream * take this */ if (gst_structure_has_field (structure, "channel-positions")) { @@ -202,19 +235,13 @@ gst_wavpack_dec_sink_set_caps (GstPad * pad, GstCaps * caps) g_free (pos); } - GST_DEBUG_OBJECT (dec, "setting caps %" GST_PTR_FORMAT, caps); - - /* should always succeed */ - gst_pad_set_caps (dec->srcpad, caps); - gst_caps_unref (caps); + gst_wavpack_dec_negotiate (dec); /* send GST_TAG_AUDIO_CODEC and GST_TAG_BITRATE tags before something * is decoded or after the format has changed */ gst_wavpack_dec_post_tags (dec); } - gst_object_unref (dec); - return TRUE; } @@ -225,28 +252,26 @@ gst_wavpack_dec_post_tags (GstWavpackDec * dec) GstFormat format_time = GST_FORMAT_TIME, format_bytes = GST_FORMAT_BYTES; gint64 duration, size; - list = gst_tag_list_new (); - - gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, - GST_TAG_AUDIO_CODEC, "Wavpack", NULL); - /* try to estimate the average bitrate */ - if (gst_pad_query_peer_duration (dec->sinkpad, &format_bytes, &size) && - gst_pad_query_peer_duration (dec->sinkpad, &format_time, &duration) && - size > 0 && duration > 0) { + if (gst_pad_query_peer_duration (GST_AUDIO_DECODER_SINK_PAD (dec), + &format_bytes, &size) && + gst_pad_query_peer_duration (GST_AUDIO_DECODER_SINK_PAD (dec), + &format_time, &duration) && size > 0 && duration > 0) { guint64 bitrate; + list = gst_tag_list_new (); + bitrate = gst_util_uint64_scale (size, 8 * GST_SECOND, duration); gst_tag_list_add (list, GST_TAG_MERGE_REPLACE, GST_TAG_BITRATE, (guint) bitrate, NULL); - } - gst_element_post_message (GST_ELEMENT (dec), - gst_message_new_tag (GST_OBJECT (dec), list)); + gst_element_post_message (GST_ELEMENT (dec), + gst_message_new_tag (GST_OBJECT (dec), list)); + } } static GstFlowReturn -gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buf) +gst_wavpack_dec_handle_frame (GstAudioDecoder * bdec, GstBuffer * buf) { GstWavpackDec *dec; GstBuffer *outbuf = NULL; @@ -255,7 +280,9 @@ gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buf) int32_t decoded, unpacked_size; gboolean format_changed; - dec = GST_WAVPACK_DEC (GST_PAD_PARENT (pad)); + dec = GST_WAVPACK_DEC (bdec); + + g_return_val_if_fail (buf != NULL, GST_FLOW_ERROR); /* check input, we only accept framed input with complete chunks */ if (GST_BUFFER_SIZE (buf) < sizeof (WavpackHeader)) @@ -283,21 +310,15 @@ gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buf) dec->context = WavpackOpenFileInputEx (dec->stream_reader, &dec->wv_id, NULL, error_msg, OPEN_STREAMING, 0); + /* expect this to work */ if (!dec->context) { - GST_WARNING ("Couldn't decode buffer: %s", error_msg); - dec->error_count++; - if (dec->error_count <= WAVPACK_DEC_MAX_ERRORS) { - goto out; /* just return OK for now */ - } else { - goto decode_error; - } + GST_WARNING_OBJECT (dec, "Couldn't decode buffer: %s", error_msg); + goto context_failed; } } g_assert (dec->context != NULL); - dec->error_count = 0; - format_changed = (dec->sample_rate != WavpackGetSampleRate (dec->context)) || (dec->channels != WavpackGetNumChannels (dec->context)) || @@ -308,22 +329,13 @@ gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buf) (dec->channel_mask != WavpackGetChannelMask (dec->context)); #endif - if (!GST_PAD_CAPS (dec->srcpad) || format_changed) { - GstCaps *caps; + if (!GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (dec)) || format_changed) { gint channel_mask; dec->sample_rate = WavpackGetSampleRate (dec->context); dec->channels = WavpackGetNumChannels (dec->context); dec->depth = WavpackGetBitsPerSample (dec->context); - caps = gst_caps_new_simple ("audio/x-raw-int", - "rate", G_TYPE_INT, dec->sample_rate, - "channels", G_TYPE_INT, dec->channels, - "depth", G_TYPE_INT, dec->depth, - "width", G_TYPE_INT, 32, - "endianness", G_TYPE_INT, G_BYTE_ORDER, - "signed", G_TYPE_BOOLEAN, TRUE, NULL); - #ifdef WAVPACK_OLD_API channel_mask = dec->context->config.channel_mask; #else @@ -334,17 +346,7 @@ gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buf) dec->channel_mask = channel_mask; - /* Only set the channel layout for more than two channels - * otherwise things break unfortunately */ - if (channel_mask != 0 && dec->channels > 2) - if (!gst_wavpack_set_channel_layout (caps, channel_mask)) - GST_WARNING_OBJECT (dec, "Failed to set channel layout"); - - GST_DEBUG_OBJECT (dec, "setting caps %" GST_PTR_FORMAT, caps); - - /* should always succeed */ - gst_pad_set_caps (dec->srcpad, caps); - gst_caps_unref (caps); + gst_wavpack_dec_negotiate (dec); /* send GST_TAG_AUDIO_CODEC and GST_TAG_BITRATE tags before something * is decoded or after the format has changed */ @@ -353,34 +355,20 @@ gst_wavpack_dec_chain (GstPad * pad, GstBuffer * buf) /* alloc output buffer */ unpacked_size = 4 * wph.block_samples * dec->channels; - ret = gst_pad_alloc_buffer (dec->srcpad, GST_BUFFER_OFFSET (buf), - unpacked_size, GST_PAD_CAPS (dec->srcpad), &outbuf); + ret = gst_pad_alloc_buffer (GST_AUDIO_DECODER_SRC_PAD (dec), + GST_BUFFER_OFFSET (buf), unpacked_size, + GST_PAD_CAPS (GST_AUDIO_DECODER_SRC_PAD (dec)), &outbuf); if (ret != GST_FLOW_OK) goto out; - gst_buffer_copy_metadata (outbuf, buf, GST_BUFFER_COPY_TIMESTAMPS); - - /* If we got a DISCONT buffer forward the flag. Nothing else - * has to be done as libwavpack doesn't store state between - * Wavpack blocks */ - if (GST_BUFFER_IS_DISCONT (buf) || dec->next_block_index != wph.block_index) - GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); - - dec->next_block_index = wph.block_index + wph.block_samples; - /* decode */ decoded = WavpackUnpackSamples (dec->context, (int32_t *) GST_BUFFER_DATA (outbuf), wph.block_samples); if (decoded != wph.block_samples) goto decode_error; - if ((outbuf = gst_audio_buffer_clip (outbuf, &dec->segment, - dec->sample_rate, 4 * dec->channels))) { - GST_LOG_OBJECT (dec, "pushing buffer with time %" GST_TIME_FORMAT, - GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf))); - ret = gst_pad_push (dec->srcpad, outbuf); - } + ret = gst_audio_decoder_finish_frame (bdec, outbuf, 1); out: @@ -388,23 +376,25 @@ out: GST_DEBUG_OBJECT (dec, "flow: %s", gst_flow_get_name (ret)); } - gst_buffer_unref (buf); - return ret; /* ERRORS */ input_not_framed: { GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Expected framed input")); - gst_buffer_unref (buf); return GST_FLOW_ERROR; } invalid_header: { GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), ("Invalid wavpack header")); - gst_buffer_unref (buf); return GST_FLOW_ERROR; } +context_failed: + { + GST_AUDIO_DECODER_ERROR (bdec, 1, LIBRARY, INIT, (NULL), + ("error creating Wavpack context"), ret); + goto out; + } decode_error: { const gchar *reason = "unknown"; @@ -418,88 +408,16 @@ decode_error: } else { reason = "couldn't create decoder context"; } - GST_ELEMENT_ERROR (dec, STREAM, DECODE, (NULL), - ("Failed to decode wavpack stream: %s", reason)); + GST_AUDIO_DECODER_ERROR (bdec, 1, STREAM, DECODE, (NULL), + ("decoding error: %s", reason), ret); if (outbuf) gst_buffer_unref (outbuf); - gst_buffer_unref (buf); - return GST_FLOW_ERROR; + if (ret == GST_FLOW_OK) + gst_audio_decoder_finish_frame (bdec, NULL, 1); + return ret; } } -static gboolean -gst_wavpack_dec_sink_event (GstPad * pad, GstEvent * event) -{ - GstWavpackDec *dec = GST_WAVPACK_DEC (gst_pad_get_parent (pad)); - - GST_LOG_OBJECT (dec, "Received %s event", GST_EVENT_TYPE_NAME (event)); - switch (GST_EVENT_TYPE (event)) { - case GST_EVENT_NEWSEGMENT:{ - GstFormat fmt; - gboolean is_update; - gint64 start, end, base; - gdouble rate; - - gst_event_parse_new_segment (event, &is_update, &rate, &fmt, &start, - &end, &base); - if (fmt == GST_FORMAT_TIME) { - GST_DEBUG ("Got NEWSEGMENT event in GST_FORMAT_TIME, passing on (%" - GST_TIME_FORMAT " - %" GST_TIME_FORMAT ")", GST_TIME_ARGS (start), - GST_TIME_ARGS (end)); - gst_segment_set_newsegment (&dec->segment, is_update, rate, fmt, - start, end, base); - } else { - gst_segment_init (&dec->segment, GST_FORMAT_TIME); - } - break; - } - default: - break; - } - - gst_object_unref (dec); - return gst_pad_event_default (pad, event); -} - -static GstStateChangeReturn -gst_wavpack_dec_change_state (GstElement * element, GstStateChange transition) -{ - GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS; - GstWavpackDec *dec = GST_WAVPACK_DEC (element); - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - break; - default: - break; - } - - ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - switch (transition) { - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - if (dec->context) { - WavpackCloseFile (dec->context); - dec->context = NULL; - } - - gst_wavpack_dec_reset (dec); - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - default: - break; - } - - return ret; -} - gboolean gst_wavpack_dec_plugin_init (GstPlugin * plugin) { diff --git a/ext/wavpack/gstwavpackdec.h b/ext/wavpack/gstwavpackdec.h index eb6e4c3bb8..4b8b057987 100644 --- a/ext/wavpack/gstwavpackdec.h +++ b/ext/wavpack/gstwavpackdec.h @@ -24,6 +24,7 @@ #define __GST_WAVPACK_DEC_H__ #include +#include #include @@ -45,31 +46,24 @@ typedef struct _GstWavpackDecClass GstWavpackDecClass; struct _GstWavpackDec { - GstElement element; + GstAudioDecoder element; /*< private > */ - GstPad *sinkpad; - GstPad *srcpad; WavpackContext *context; WavpackStreamReader *stream_reader; read_id wv_id; - GstSegment segment; /* used for clipping, TIME format */ - guint32 next_block_index; - gint sample_rate; gint depth; gint channels; gint channel_mask; - - gint error_count; }; struct _GstWavpackDecClass { - GstElementClass parent; + GstAudioDecoderClass parent; }; GType gst_wavpack_dec_get_type (void);