flacdec: naive port to GstAudioDecoder

This would probably have been too invasive to do in the 0.10
branch, with all the pull-mode and parser handling code in
there.
This commit is contained in:
Tim-Philipp Müller 2011-10-09 16:18:09 +01:00
parent 9cd17092d8
commit 92361863e6
2 changed files with 122 additions and 632 deletions

View file

@ -45,8 +45,6 @@
#include "gstflacdec.h" #include "gstflacdec.h"
#include <gst/gst-i18n-plugin.h> #include <gst/gst-i18n-plugin.h>
#include <gst/gsttagsetter.h>
#include <gst/base/gsttypefindhelper.h>
#include <gst/audio/multichannel.h> #include <gst/audio/multichannel.h>
#include <gst/tag/tag.h> #include <gst/tag/tag.h>
@ -95,25 +93,6 @@ static const GstAudioChannelPosition channel_positions[8][8] = {
GST_DEBUG_CATEGORY_STATIC (flacdec_debug); GST_DEBUG_CATEGORY_STATIC (flacdec_debug);
#define GST_CAT_DEFAULT flacdec_debug #define GST_CAT_DEFAULT flacdec_debug
static void gst_flac_dec_finalize (GObject * object);
static GstStateChangeReturn gst_flac_dec_change_state (GstElement * element,
GstStateChange transition);
static const GstQueryType *gst_flac_dec_get_src_query_types (GstPad * pad);
static const GstQueryType *gst_flac_dec_get_sink_query_types (GstPad * pad);
static gboolean gst_flac_dec_sink_query (GstPad * pad, GstQuery * query);
static gboolean gst_flac_dec_src_query (GstPad * pad, GstQuery * query);
static gboolean gst_flac_dec_convert_src (GstPad * pad, GstFormat src_format,
gint64 src_value, GstFormat * dest_format, gint64 * dest_value);
static gboolean gst_flac_dec_sink_activate (GstPad * sinkpad);
static gboolean gst_flac_dec_sink_activate_push (GstPad * sinkpad,
gboolean active);
static gboolean gst_flac_dec_sink_event (GstPad * pad, GstEvent * event);
static GstFlowReturn gst_flac_dec_chain (GstPad * pad, GstBuffer * buf);
static void gst_flac_dec_reset_decoders (GstFlacDec * flacdec);
static void gst_flac_dec_setup_decoder (GstFlacDec * flacdec);
static FLAC__StreamDecoderReadStatus static FLAC__StreamDecoderReadStatus
gst_flac_dec_read_stream (const FLAC__StreamDecoder * decoder, gst_flac_dec_read_stream (const FLAC__StreamDecoder * decoder,
FLAC__byte buffer[], size_t * bytes, void *client_data); FLAC__byte buffer[], size_t * bytes, void *client_data);
@ -126,8 +105,14 @@ static void gst_flac_dec_metadata_cb (const FLAC__StreamDecoder *
static void gst_flac_dec_error_cb (const FLAC__StreamDecoder * static void gst_flac_dec_error_cb (const FLAC__StreamDecoder *
decoder, FLAC__StreamDecoderErrorStatus status, void *client_data); decoder, FLAC__StreamDecoderErrorStatus status, void *client_data);
#define gst_flac_dec_parent_class parent_class static void gst_flac_dec_flush (GstAudioDecoder * audio_dec, gboolean hard);
G_DEFINE_TYPE (GstFlacDec, gst_flac_dec, GST_TYPE_ELEMENT); static gboolean gst_flac_dec_set_format (GstAudioDecoder * dec, GstCaps * caps);
static gboolean gst_flac_dec_start (GstAudioDecoder * dec);
static gboolean gst_flac_dec_stop (GstAudioDecoder * dec);
static GstFlowReturn gst_flac_dec_handle_frame (GstAudioDecoder * audio_dec,
GstBuffer * buf);
G_DEFINE_TYPE (GstFlacDec, gst_flac_dec, GST_TYPE_AUDIO_DECODER);
#if G_BYTE_ORDER == G_LITTLE_ENDIAN #if G_BYTE_ORDER == G_LITTLE_ENDIAN
#define FORMATS "{ S8LE, S16LE, S32LE } " #define FORMATS "{ S8LE, S16LE, S32LE } "
@ -144,6 +129,12 @@ G_DEFINE_TYPE (GstFlacDec, gst_flac_dec, GST_TYPE_ELEMENT);
"rate = (int) [ 1, 655350 ], " \ "rate = (int) [ 1, 655350 ], " \
"channels = (int) [ 1, 8 ]" "channels = (int) [ 1, 8 ]"
#define GST_FLAC_DEC_SINK_CAPS \
"audio/x-flac, " \
"framed = (boolean) true, " \
"rate = (int) [ 1, 655350 ], " \
"channels = (int) [ 1, 8 ]"
static GstStaticPadTemplate flac_dec_src_factory = static GstStaticPadTemplate flac_dec_src_factory =
GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_PAD_TEMPLATE ("src",
GST_PAD_SRC, GST_PAD_SRC,
@ -153,24 +144,25 @@ static GstStaticPadTemplate flac_dec_sink_factory =
GST_STATIC_PAD_TEMPLATE ("sink", GST_STATIC_PAD_TEMPLATE ("sink",
GST_PAD_SINK, GST_PAD_SINK,
GST_PAD_ALWAYS, GST_PAD_ALWAYS,
GST_STATIC_CAPS ("audio/x-flac") GST_STATIC_CAPS (GST_FLAC_DEC_SINK_CAPS));
);
static void static void
gst_flac_dec_class_init (GstFlacDecClass * klass) gst_flac_dec_class_init (GstFlacDecClass * klass)
{ {
GstAudioDecoderClass *audiodecoder_class;
GstElementClass *gstelement_class; GstElementClass *gstelement_class;
GObjectClass *gobject_class;
audiodecoder_class = (GstAudioDecoderClass *) klass;
gstelement_class = (GstElementClass *) klass; gstelement_class = (GstElementClass *) klass;
gobject_class = (GObjectClass *) klass;
GST_DEBUG_CATEGORY_INIT (flacdec_debug, "flacdec", 0, "flac decoder"); GST_DEBUG_CATEGORY_INIT (flacdec_debug, "flacdec", 0, "flac decoder");
gobject_class->finalize = gst_flac_dec_finalize; audiodecoder_class->stop = GST_DEBUG_FUNCPTR (gst_flac_dec_stop);
audiodecoder_class->start = GST_DEBUG_FUNCPTR (gst_flac_dec_start);
gstelement_class->change_state = audiodecoder_class->flush = GST_DEBUG_FUNCPTR (gst_flac_dec_flush);
GST_DEBUG_FUNCPTR (gst_flac_dec_change_state); audiodecoder_class->set_format = GST_DEBUG_FUNCPTR (gst_flac_dec_set_format);
audiodecoder_class->handle_frame =
GST_DEBUG_FUNCPTR (gst_flac_dec_handle_frame);
gst_element_class_add_pad_template (gstelement_class, gst_element_class_add_pad_template (gstelement_class,
gst_static_pad_template_get (&flac_dec_src_factory)); gst_static_pad_template_get (&flac_dec_src_factory));
@ -178,45 +170,60 @@ gst_flac_dec_class_init (GstFlacDecClass * klass)
gst_static_pad_template_get (&flac_dec_sink_factory)); gst_static_pad_template_get (&flac_dec_sink_factory));
gst_element_class_set_details_simple (gstelement_class, "FLAC audio decoder", gst_element_class_set_details_simple (gstelement_class, "FLAC audio decoder",
"Codec/Decoder/Audio", "Codec/Decoder/Audio", "Decodes FLAC lossless audio streams",
"Decodes FLAC lossless audio streams", "Wim Taymans <wim@fluendo.com>"); "Tim-Philipp Müller <tim@centricular.net>, "
"Wim Taymans <wim.taymans@gmail.com>");
} }
static void static void
gst_flac_dec_init (GstFlacDec * flacdec) gst_flac_dec_init (GstFlacDec * flacdec)
{ {
flacdec->sinkpad = /* nothing to do here */
gst_pad_new_from_static_template (&flac_dec_sink_factory, "sink");
gst_pad_set_activate_function (flacdec->sinkpad,
GST_DEBUG_FUNCPTR (gst_flac_dec_sink_activate));
gst_pad_set_activatepush_function (flacdec->sinkpad,
GST_DEBUG_FUNCPTR (gst_flac_dec_sink_activate_push));
gst_pad_set_query_type_function (flacdec->sinkpad,
GST_DEBUG_FUNCPTR (gst_flac_dec_get_sink_query_types));
gst_pad_set_query_function (flacdec->sinkpad,
GST_DEBUG_FUNCPTR (gst_flac_dec_sink_query));
gst_pad_set_event_function (flacdec->sinkpad,
GST_DEBUG_FUNCPTR (gst_flac_dec_sink_event));
gst_pad_set_chain_function (flacdec->sinkpad,
GST_DEBUG_FUNCPTR (gst_flac_dec_chain));
gst_element_add_pad (GST_ELEMENT (flacdec), flacdec->sinkpad);
flacdec->srcpad =
gst_pad_new_from_static_template (&flac_dec_src_factory, "src");
gst_pad_set_query_type_function (flacdec->srcpad,
GST_DEBUG_FUNCPTR (gst_flac_dec_get_src_query_types));
gst_pad_set_query_function (flacdec->srcpad,
GST_DEBUG_FUNCPTR (gst_flac_dec_src_query));
gst_pad_use_fixed_caps (flacdec->srcpad);
gst_element_add_pad (GST_ELEMENT (flacdec), flacdec->srcpad);
gst_flac_dec_reset_decoders (flacdec);
} }
static void static gboolean
gst_flac_dec_reset_decoders (GstFlacDec * flacdec) gst_flac_dec_start (GstAudioDecoder * audio_dec)
{ {
/* Clean up the decoder */ FLAC__StreamDecoderInitStatus s;
GstFlacDec *dec;
dec = GST_FLAC_DEC (audio_dec);
///////////// FIXME:
dec->tags = gst_tag_list_new (GST_TAG_AUDIO_CODEC, "FLAC", NULL);
dec->adapter = gst_adapter_new ();
dec->decoder = FLAC__stream_decoder_new ();
/* no point calculating MD5 since it's never checked here */
FLAC__stream_decoder_set_md5_checking (dec->decoder, false);
FLAC__stream_decoder_set_metadata_respond (dec->decoder,
FLAC__METADATA_TYPE_VORBIS_COMMENT);
FLAC__stream_decoder_set_metadata_respond (dec->decoder,
FLAC__METADATA_TYPE_PICTURE);
GST_DEBUG_OBJECT (dec, "initializing decoder");
s = FLAC__stream_decoder_init_stream (dec->decoder,
gst_flac_dec_read_stream, NULL, NULL, NULL, NULL,
gst_flac_dec_write_stream, gst_flac_dec_metadata_cb,
gst_flac_dec_error_cb, dec);
if (s != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
GST_ELEMENT_ERROR (GST_ELEMENT (dec), LIBRARY, INIT, (NULL), (NULL));
return FALSE;
}
dec->got_headers = FALSE;
return TRUE;
}
static gboolean
gst_flac_dec_stop (GstAudioDecoder * dec)
{
GstFlacDec *flacdec = GST_FLAC_DEC (dec);
if (flacdec->decoder) { if (flacdec->decoder) {
FLAC__stream_decoder_delete (flacdec->decoder); FLAC__stream_decoder_delete (flacdec->decoder);
flacdec->decoder = NULL; flacdec->decoder = NULL;
@ -233,42 +240,17 @@ gst_flac_dec_reset_decoders (GstFlacDec * flacdec)
flacdec->tags = NULL; flacdec->tags = NULL;
} }
flacdec->segment.position = 0; return TRUE;
flacdec->init = TRUE;
} }
static void static gboolean
gst_flac_dec_setup_decoder (GstFlacDec * dec) gst_flac_dec_set_format (GstAudioDecoder * dec, GstCaps * caps)
{ {
gst_flac_dec_reset_decoders (dec); /* if stream headers are present we could process them here already */
GST_LOG_OBJECT (dec, "sink caps: %" GST_PTR_FORMAT, caps);
dec->tags = gst_tag_list_new (GST_TAG_AUDIO_CODEC, "FLAC", NULL); return TRUE;
dec->adapter = gst_adapter_new ();
dec->decoder = FLAC__stream_decoder_new ();
/* no point calculating since it's never checked here */
FLAC__stream_decoder_set_md5_checking (dec->decoder, false);
FLAC__stream_decoder_set_metadata_respond (dec->decoder,
FLAC__METADATA_TYPE_VORBIS_COMMENT);
FLAC__stream_decoder_set_metadata_respond (dec->decoder,
FLAC__METADATA_TYPE_PICTURE);
} }
static void
gst_flac_dec_finalize (GObject * object)
{
GstFlacDec *flacdec;
flacdec = GST_FLAC_DEC (object);
gst_flac_dec_reset_decoders (flacdec);
G_OBJECT_CLASS (parent_class)->finalize (object);
}
static gboolean static gboolean
gst_flac_dec_update_metadata (GstFlacDec * flacdec, gst_flac_dec_update_metadata (GstFlacDec * flacdec,
const FLAC__StreamMetadata * metadata) const FLAC__StreamMetadata * metadata)
@ -437,6 +419,7 @@ gst_flac_dec_scan_got_frame (GstFlacDec * flacdec, guint8 * data, guint size,
return TRUE; return TRUE;
} }
/* FIXME: let parser extract tags */
static void static void
gst_flac_extract_picture_buffer (GstFlacDec * dec, gst_flac_extract_picture_buffer (GstFlacDec * dec,
const FLAC__StreamMetadata * metadata) const FLAC__StreamMetadata * metadata)
@ -459,7 +442,8 @@ gst_flac_extract_picture_buffer (GstFlacDec * dec,
picture.data_length, picture.type); picture.data_length, picture.type);
if (!gst_tag_list_is_empty (tags)) { if (!gst_tag_list_is_empty (tags)) {
gst_element_found_tags_for_pad (GST_ELEMENT (dec), dec->srcpad, tags); gst_element_found_tags_for_pad (GST_ELEMENT (dec),
GST_AUDIO_DECODER_SRC_PAD (dec), tags);
} else { } else {
GST_DEBUG_OBJECT (dec, "problem parsing PICTURE block, skipping"); GST_DEBUG_OBJECT (dec, "problem parsing PICTURE block, skipping");
gst_tag_list_free (tags); gst_tag_list_free (tags);
@ -502,26 +486,6 @@ gst_flac_dec_metadata_cb (const FLAC__StreamDecoder * decoder,
flacdec->width); flacdec->width);
GST_DEBUG_OBJECT (flacdec, "total samples = %" G_GINT64_FORMAT, samples); GST_DEBUG_OBJECT (flacdec, "total samples = %" G_GINT64_FORMAT, samples);
/* in framed mode the demuxer/parser upstream has already pushed a
* newsegment event in TIME format which we've passed on */
if (samples > 0 && !flacdec->framed) {
gint64 duration;
GstSegment seg;
flacdec->segment.duration = samples;
/* convert duration to time */
duration = gst_util_uint64_scale_int (samples, GST_SECOND,
flacdec->sample_rate);
gst_segment_init (&seg, GST_FORMAT_TIME);
seg.rate = flacdec->segment.rate;
seg.applied_rate = flacdec->segment.applied_rate;
seg.start = 0;
seg.stop = duration;
seg.time = 0;
}
break; break;
} }
case FLAC__METADATA_TYPE_PICTURE:{ case FLAC__METADATA_TYPE_PICTURE:{
@ -600,24 +564,12 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
guint channels = frame->header.channels; guint channels = frame->header.channels;
guint samples = frame->header.blocksize; guint samples = frame->header.blocksize;
guint j, i; guint j, i;
GstClockTime next;
gpointer data; gpointer data;
gsize size; gsize size;
const gchar *format; const gchar *format;
GST_LOG_OBJECT (flacdec, "samples in frame header: %d", samples); GST_LOG_OBJECT (flacdec, "samples in frame header: %d", samples);
/* if a DEFAULT segment is configured, don't send samples past the end
* of the segment */
if (flacdec->segment.format == GST_FORMAT_DEFAULT &&
flacdec->segment.stop != -1 &&
flacdec->segment.position >= 0 &&
flacdec->segment.position + samples > flacdec->segment.stop) {
samples = flacdec->segment.stop - flacdec->segment.position;
GST_DEBUG_OBJECT (flacdec,
"clipping last buffer to %d samples because of segment", samples);
}
if (depth == 0) { if (depth == 0) {
if (flacdec->depth < 4 || flacdec->depth > 32) { if (flacdec->depth < 4 || flacdec->depth > 32) {
GST_ERROR_OBJECT (flacdec, "unsupported depth %d from STREAMINFO", GST_ERROR_OBJECT (flacdec, "unsupported depth %d from STREAMINFO",
@ -667,7 +619,7 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
} }
} }
if (!gst_pad_has_current_caps (flacdec->srcpad)) { if (!gst_pad_has_current_caps (GST_AUDIO_DECODER_SRC_PAD (flacdec))) {
GstCaps *caps; GstCaps *caps;
GST_DEBUG_OBJECT (flacdec, "Negotiating %d Hz @ %d channels", GST_DEBUG_OBJECT (flacdec, "Negotiating %d Hz @ %d channels",
@ -689,31 +641,19 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
flacdec->channels = channels; flacdec->channels = channels;
flacdec->sample_rate = sample_rate; flacdec->sample_rate = sample_rate;
gst_pad_set_caps (flacdec->srcpad, caps); gst_audio_decoder_set_outcaps (GST_AUDIO_DECODER (flacdec), caps);
gst_caps_unref (caps); gst_caps_unref (caps);
} }
if (flacdec->tags) { if (flacdec->tags) {
gst_element_found_tags_for_pad (GST_ELEMENT (flacdec), flacdec->srcpad, gst_element_found_tags_for_pad (GST_ELEMENT (flacdec),
flacdec->tags); GST_AUDIO_DECODER_SRC_PAD (flacdec), flacdec->tags);
flacdec->tags = NULL; flacdec->tags = NULL;
} }
GST_LOG_OBJECT (flacdec, "alloc_buffer_and_set_caps"); GST_LOG_OBJECT (flacdec, "alloc_buffer_and_set_caps");
outbuf = gst_buffer_new_allocate (NULL, samples * channels * (width / 8), 0); outbuf = gst_buffer_new_allocate (NULL, samples * channels * (width / 8), 0);
GST_BUFFER_OFFSET (outbuf) = flacdec->segment.position;
GST_BUFFER_TIMESTAMP (outbuf) =
gst_util_uint64_scale_int (flacdec->segment.position, GST_SECOND,
frame->header.sample_rate);
/* get next timestamp to calculate the duration */
next = gst_util_uint64_scale_int (flacdec->segment.position + samples,
GST_SECOND, frame->header.sample_rate);
GST_BUFFER_DURATION (outbuf) = next - GST_BUFFER_TIMESTAMP (outbuf);
data = gst_buffer_map (outbuf, &size, NULL, GST_MAP_WRITE); data = gst_buffer_map (outbuf, &size, NULL, GST_MAP_WRITE);
if (width == 8) { if (width == 8) {
gint8 *outbuffer = (gint8 *) data; gint8 *outbuffer = (gint8 *) data;
@ -750,21 +690,17 @@ gst_flac_dec_write (GstFlacDec * flacdec, const FLAC__Frame * frame,
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)), GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (outbuf)),
GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf))); GST_TIME_ARGS (GST_BUFFER_DURATION (outbuf)));
ret = gst_pad_push (flacdec->srcpad, outbuf); ret = gst_audio_decoder_finish_frame (GST_AUDIO_DECODER (flacdec), outbuf, 1);
GST_DEBUG_OBJECT (flacdec, "returned %s", gst_flow_get_name (ret));
flacdec->segment.position += samples;
if (ret != GST_FLOW_OK) { if (G_UNLIKELY (ret != GST_FLOW_OK)) {
GST_DEBUG_OBJECT (flacdec, "gst_pad_push() returned %s", GST_DEBUG_OBJECT (flacdec, "finish_frame flow %s", gst_flow_get_name (ret));
gst_flow_get_name (ret));
} }
done: done:
/* we act on the flow return value later in the handle_frame function, as we
/* we act on the flow return value later in the loop function, as we don't * don't want to mess up the internal decoder state by returning ABORT when
* want to mess up the internal decoder state by returning ABORT when the * the error is in fact non-fatal (like a pad in flushing mode) and we want
* error is in fact non-fatal (like a pad in flushing mode) and we want
* to continue later. So just pretend everything's dandy and act later. */ * to continue later. So just pretend everything's dandy and act later. */
flacdec->last_flow = ret; flacdec->last_flow = ret;
@ -779,82 +715,22 @@ gst_flac_dec_write_stream (const FLAC__StreamDecoder * decoder,
return gst_flac_dec_write (GST_FLAC_DEC (client_data), frame, buffer); return gst_flac_dec_write (GST_FLAC_DEC (client_data), frame, buffer);
} }
static gboolean static void
gst_flac_dec_sink_event (GstPad * pad, GstEvent * event) gst_flac_dec_flush (GstAudioDecoder * audio_dec, gboolean hard)
{ {
GstFlacDec *dec; GstFlacDec *dec = GST_FLAC_DEC (audio_dec);
gboolean res;
dec = GST_FLAC_DEC (gst_pad_get_parent (pad)); if (!hard) {
guint available = gst_adapter_available (dec->adapter);
switch (GST_EVENT_TYPE (event)) { if (available > 0) {
case GST_EVENT_CAPS: GST_INFO_OBJECT (dec, "draining, %u bytes left in adapter", available);
res = TRUE;
gst_event_unref (event);
break;
case GST_EVENT_FLUSH_STOP:{
if (dec->init == FALSE) {
FLAC__stream_decoder_flush (dec->decoder);
gst_adapter_clear (dec->adapter);
}
res = gst_pad_push_event (dec->srcpad, event);
break;
}
case GST_EVENT_SEGMENT:{
GstSegment seg;
gint64 start, stop;
gst_event_copy_segment (event, &seg);
if (seg.format == GST_FORMAT_TIME) {
GstFormat dformat = GST_FORMAT_DEFAULT;
GST_DEBUG_OBJECT (dec, "newsegment event in TIME format => framed");
dec->framed = TRUE;
res = gst_pad_push_event (dec->srcpad, event);
/* this won't work for the first newsegment event though ... */
if (gst_flac_dec_convert_src (dec->srcpad, GST_FORMAT_TIME, seg.start,
&dformat, &start) && start != -1 &&
gst_flac_dec_convert_src (dec->srcpad, GST_FORMAT_TIME, seg.stop,
&dformat, &stop) && stop != -1) {
seg.start = start;
seg.stop = stop;
dec->segment = seg;
GST_DEBUG_OBJECT (dec, "segment %" GST_SEGMENT_FORMAT, &dec->segment);
} else {
GST_WARNING_OBJECT (dec, "couldn't convert time => samples");
}
} else if (seg.format == GST_FORMAT_BYTES || TRUE) {
/* FIXME: error out or post warning, we require parsed input */
gst_event_unref (event);
res = FALSE;
}
break;
}
case GST_EVENT_EOS:{
GST_LOG_OBJECT (dec, "EOS, with %u bytes available in adapter",
gst_adapter_available (dec->adapter));
if (dec->init == FALSE) {
if (gst_adapter_available (dec->adapter) > 0) {
FLAC__stream_decoder_process_until_end_of_stream (dec->decoder); FLAC__stream_decoder_process_until_end_of_stream (dec->decoder);
} }
}
FLAC__stream_decoder_flush (dec->decoder); FLAC__stream_decoder_flush (dec->decoder);
}
gst_adapter_clear (dec->adapter); gst_adapter_clear (dec->adapter);
res = gst_pad_push_event (dec->srcpad, event);
break;
}
default:
res = gst_pad_event_default (pad, event);
break;
}
gst_object_unref (dec);
return res;
} }
static gboolean static gboolean
@ -905,39 +781,29 @@ gst_flac_dec_chain_parse_headers (GstFlacDec * dec)
} }
static GstFlowReturn static GstFlowReturn
gst_flac_dec_chain (GstPad * pad, GstBuffer * buf) gst_flac_dec_handle_frame (GstAudioDecoder * audio_dec, GstBuffer * buf)
{ {
FLAC__StreamDecoderInitStatus s;
GstFlacDec *dec; GstFlacDec *dec;
gboolean got_audio_frame; gboolean got_audio_frame;
dec = GST_FLAC_DEC (GST_PAD_PARENT (pad)); dec = GST_FLAC_DEC (audio_dec);
GST_LOG_OBJECT (dec, /* drain remaining data? */
"buffer with ts=%" GST_TIME_FORMAT ", offset=%" G_GINT64_FORMAT if (G_UNLIKELY (buf == NULL)) {
", end_offset=%" G_GINT64_FORMAT ", size=%" G_GSIZE_FORMAT, gst_flac_dec_flush (audio_dec, FALSE);
GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_OFFSET (buf), return GST_FLOW_OK;
GST_BUFFER_OFFSET_END (buf), gst_buffer_get_size (buf));
if (dec->init) {
GST_DEBUG_OBJECT (dec, "initializing decoder");
s = FLAC__stream_decoder_init_stream (dec->decoder,
gst_flac_dec_read_stream, NULL, NULL, NULL, NULL,
gst_flac_dec_write_stream, gst_flac_dec_metadata_cb,
gst_flac_dec_error_cb, dec);
if (s != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
GST_ELEMENT_ERROR (GST_ELEMENT (dec), LIBRARY, INIT, (NULL), (NULL));
return GST_FLOW_ERROR;
} }
GST_DEBUG_OBJECT (dec, "initialized (framed=%d)", dec->framed);
dec->init = FALSE; GST_LOG_OBJECT (dec, "frame: ts %" GST_TIME_FORMAT ", flags 0x%04x, %u bytes",
} else if (GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DISCONT)) { GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)), GST_BUFFER_FLAGS (buf),
/* Clear the adapter and the decoder */ gst_buffer_get_size (buf));
gst_adapter_clear (dec->adapter);
FLAC__stream_decoder_flush (dec->decoder); if (G_UNLIKELY (!dec->got_headers)) {
// FIXME
} }
/* FIXME: should always be framed */ /* FIXME: should always be framed */
dec->framed = TRUE;
if (dec->framed) { if (dec->framed) {
gint64 unused; gint64 unused;
guint8 *data; guint8 *data;
@ -947,22 +813,11 @@ gst_flac_dec_chain (GstPad * pad, GstBuffer * buf)
data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ); data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
got_audio_frame = gst_flac_dec_scan_got_frame (dec, data, size, &unused); got_audio_frame = gst_flac_dec_scan_got_frame (dec, data, size, &unused);
gst_buffer_unmap (buf, data, size); gst_buffer_unmap (buf, data, size);
if (G_LIKELY (got_audio_frame)) {
GstFormat dformat = GST_FORMAT_DEFAULT; /* FIXME: remove var */
gint64 position;
/* upstream (e.g. demuxer) presents us time,
* convert to default samples */
gst_flac_dec_convert_src (dec->srcpad, GST_FORMAT_TIME,
GST_BUFFER_TIMESTAMP (buf), &dformat, &position);
dec->segment.position = position;
}
} else { } else {
got_audio_frame = TRUE; got_audio_frame = TRUE;
} }
gst_adapter_push (dec->adapter, buf); gst_adapter_push (dec->adapter, gst_buffer_ref (buf));
buf = NULL; buf = NULL;
dec->last_flow = GST_FLOW_OK; dec->last_flow = GST_FLOW_OK;
@ -1029,358 +884,3 @@ out:
return dec->last_flow; return dec->last_flow;
} }
static gboolean
gst_flac_dec_convert_sink (GstFlacDec * dec, GstFormat src_format,
gint64 src_value, GstFormat * dest_format, gint64 * dest_value)
{
gboolean res = TRUE;
if (dec->width == 0 || dec->channels == 0 || dec->sample_rate == 0) {
/* no frame decoded yet */
GST_DEBUG_OBJECT (dec, "cannot convert: not set up yet");
return FALSE;
}
switch (src_format) {
case GST_FORMAT_BYTES:{
res = FALSE;
break;
}
case GST_FORMAT_DEFAULT:
switch (*dest_format) {
case GST_FORMAT_BYTES:
res = FALSE;
break;
case GST_FORMAT_TIME:
/* granulepos = sample */
*dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
dec->sample_rate);
break;
default:
res = FALSE;
break;
}
break;
case GST_FORMAT_TIME:
switch (*dest_format) {
case GST_FORMAT_BYTES:
res = FALSE;
break;
case GST_FORMAT_DEFAULT:
*dest_value = gst_util_uint64_scale_int (src_value,
dec->sample_rate, GST_SECOND);
break;
default:
res = FALSE;
break;
}
break;
default:
res = FALSE;
break;
}
return res;
}
static const GstQueryType *
gst_flac_dec_get_sink_query_types (GstPad * pad)
{
static const GstQueryType types[] = {
GST_QUERY_CONVERT,
0,
};
return types;
}
static gboolean
gst_flac_dec_sink_query (GstPad * pad, GstQuery * query)
{
GstFlacDec *dec;
gboolean res = FALSE;
dec = GST_FLAC_DEC (gst_pad_get_parent (pad));
GST_LOG_OBJECT (dec, "%s query", GST_QUERY_TYPE_NAME (query));
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CONVERT:{
GstFormat src_fmt, dest_fmt;
gint64 src_val, dest_val;
gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, NULL);
res = gst_flac_dec_convert_sink (dec, src_fmt, src_val, &dest_fmt,
&dest_val);
if (res) {
gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
}
GST_LOG_OBJECT (dec, "conversion %s", (res) ? "ok" : "FAILED");
break;
}
default:{
res = gst_pad_query_default (pad, query);
break;
}
}
gst_object_unref (dec);
return res;
}
static gboolean
gst_flac_dec_convert_src (GstPad * pad, GstFormat src_format, gint64 src_value,
GstFormat * dest_format, gint64 * dest_value)
{
GstFlacDec *flacdec = GST_FLAC_DEC (GST_PAD_PARENT (pad));
gboolean res = TRUE;
guint bytes_per_sample;
guint scale = 1;
if (flacdec->width == 0 || flacdec->channels == 0 ||
flacdec->sample_rate == 0) {
/* no frame decoded yet */
GST_DEBUG_OBJECT (flacdec, "cannot convert: not set up yet");
return FALSE;
}
bytes_per_sample = flacdec->channels * (flacdec->width / 8);
switch (src_format) {
case GST_FORMAT_BYTES:{
switch (*dest_format) {
case GST_FORMAT_DEFAULT:
*dest_value =
gst_util_uint64_scale_int (src_value, 1, bytes_per_sample);
break;
case GST_FORMAT_TIME:
{
gint byterate = bytes_per_sample * flacdec->sample_rate;
*dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
byterate);
break;
}
default:
res = FALSE;
}
break;
}
case GST_FORMAT_DEFAULT:
switch (*dest_format) {
case GST_FORMAT_BYTES:
*dest_value = src_value * bytes_per_sample;
break;
case GST_FORMAT_TIME:
*dest_value = gst_util_uint64_scale_int (src_value, GST_SECOND,
flacdec->sample_rate);
break;
default:
res = FALSE;
}
break;
case GST_FORMAT_TIME:
switch (*dest_format) {
case GST_FORMAT_BYTES:
scale = bytes_per_sample;
case GST_FORMAT_DEFAULT:
*dest_value = gst_util_uint64_scale_int_round (src_value,
scale * flacdec->sample_rate, GST_SECOND);
break;
default:
res = FALSE;
}
break;
default:
res = FALSE;
}
return res;
}
static const GstQueryType *
gst_flac_dec_get_src_query_types (GstPad * pad)
{
static const GstQueryType types[] = {
GST_QUERY_POSITION,
GST_QUERY_DURATION,
GST_QUERY_CONVERT,
GST_QUERY_SEEKING,
0,
};
return types;
}
static gboolean
gst_flac_dec_src_query (GstPad * pad, GstQuery * query)
{
GstFlacDec *flacdec;
gboolean res = TRUE;
GstPad *peer;
flacdec = GST_FLAC_DEC (gst_pad_get_parent (pad));
peer = gst_pad_get_peer (flacdec->sinkpad);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_POSITION:{
GstFormat fmt;
gint64 pos;
gst_query_parse_position (query, &fmt, NULL);
/* there might be a demuxer in front of us who can handle this */
if (fmt == GST_FORMAT_TIME && (res = gst_pad_query (peer, query)))
break;
if (fmt != GST_FORMAT_DEFAULT) {
if (!gst_flac_dec_convert_src (flacdec->srcpad, GST_FORMAT_DEFAULT,
flacdec->segment.position, &fmt, &pos)) {
GST_DEBUG_OBJECT (flacdec, "failed to convert position into %s "
"format", gst_format_get_name (fmt));
res = FALSE;
goto done;
}
} else {
pos = flacdec->segment.position;
}
gst_query_set_position (query, fmt, pos);
GST_DEBUG_OBJECT (flacdec, "returning position %" G_GUINT64_FORMAT
" (format: %s)", pos, gst_format_get_name (fmt));
res = TRUE;
break;
}
case GST_QUERY_DURATION:{
GstFormat fmt;
gint64 len;
gst_query_parse_duration (query, &fmt, NULL);
/* try any demuxers or parsers before us first */
if ((fmt == GST_FORMAT_TIME || fmt == GST_FORMAT_DEFAULT) &&
peer != NULL && gst_pad_query (peer, query)) {
gst_query_parse_duration (query, NULL, &len);
GST_DEBUG_OBJECT (flacdec, "peer returned duration %" GST_TIME_FORMAT,
GST_TIME_ARGS (len));
res = TRUE;
goto done;
}
if (flacdec->segment.duration == 0 || flacdec->segment.duration == -1) {
GST_DEBUG_OBJECT (flacdec, "duration not known yet");
res = FALSE;
goto done;
}
/* convert total number of samples to request format */
if (fmt != GST_FORMAT_DEFAULT) {
if (!gst_flac_dec_convert_src (flacdec->srcpad, GST_FORMAT_DEFAULT,
flacdec->segment.duration, &fmt, &len)) {
GST_DEBUG_OBJECT (flacdec, "failed to convert duration into %s "
"format", gst_format_get_name (fmt));
res = FALSE;
goto done;
}
} else {
len = flacdec->segment.duration;
}
gst_query_set_duration (query, fmt, len);
GST_DEBUG_OBJECT (flacdec, "returning duration %" G_GUINT64_FORMAT
" (format: %s)", len, gst_format_get_name (fmt));
res = TRUE;
break;
}
case GST_QUERY_CONVERT:{
GstFormat src_fmt, dest_fmt;
gint64 src_val, dest_val;
gst_query_parse_convert (query, &src_fmt, &src_val, &dest_fmt, NULL);
res = gst_flac_dec_convert_src (pad, src_fmt, src_val, &dest_fmt,
&dest_val);
if (res) {
gst_query_set_convert (query, src_fmt, src_val, dest_fmt, dest_val);
}
break;
}
default:{
res = gst_pad_query_default (pad, query);
break;
}
}
done:
if (peer)
gst_object_unref (peer);
gst_object_unref (flacdec);
return res;
}
static gboolean
gst_flac_dec_sink_activate (GstPad * sinkpad)
{
GST_DEBUG_OBJECT (sinkpad, "activating push");
return gst_pad_activate_push (sinkpad, TRUE);
}
static gboolean
gst_flac_dec_sink_activate_push (GstPad * sinkpad, gboolean active)
{
GstFlacDec *dec = GST_FLAC_DEC (GST_OBJECT_PARENT (sinkpad));
if (active) {
gst_flac_dec_setup_decoder (dec);
dec->got_headers = FALSE;
}
return TRUE;
}
static GstStateChangeReturn
gst_flac_dec_change_state (GstElement * element, GstStateChange transition)
{
GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
GstFlacDec *flacdec = GST_FLAC_DEC (element);
switch (transition) {
case GST_STATE_CHANGE_READY_TO_PAUSED:
flacdec->channels = 0;
flacdec->depth = 0;
flacdec->width = 0;
flacdec->sample_rate = 0;
gst_segment_init (&flacdec->segment, GST_FORMAT_DEFAULT);
break;
default:
break;
}
ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
if (ret == GST_STATE_CHANGE_FAILURE)
return ret;
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_READY:
gst_segment_init (&flacdec->segment, GST_FORMAT_UNDEFINED);
gst_flac_dec_reset_decoders (flacdec);
break;
default:
break;
}
return ret;
}

View file

@ -1,5 +1,6 @@
/* GStreamer /* GStreamer
* Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu> * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
* Copyright (C) <2011> Tim-Philipp Müller <tim centricular net>
* *
* This library is free software; you can redistribute it and/or * This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU Library General Public
@ -21,9 +22,8 @@
#ifndef __GST_FLAC_DEC_H__ #ifndef __GST_FLAC_DEC_H__
#define __GST_FLAC_DEC_H__ #define __GST_FLAC_DEC_H__
#include <gst/gst.h> #include <gst/gst.h>
#include <gst/base/gstadapter.h> #include <gst/audio/gstaudiodecoder.h>
#include <FLAC/all.h> #include <FLAC/all.h>
@ -39,27 +39,19 @@ typedef struct _GstFlacDec GstFlacDec;
typedef struct _GstFlacDecClass GstFlacDecClass; typedef struct _GstFlacDecClass GstFlacDecClass;
struct _GstFlacDec { struct _GstFlacDec {
GstElement element; GstAudioDecoder audiodecoder;
/* < private > */
/*< private >*/
FLAC__StreamDecoder *decoder; FLAC__StreamDecoder *decoder;
GstAdapter *adapter; GstAdapter *adapter;
gboolean framed; gboolean framed; // FIXME
gboolean got_headers; /* if we've parsed the headers (unframed push mode only) */ gboolean got_headers; /* if we've parsed the headers */
GstPad *sinkpad;
GstPad *srcpad;
gboolean init;
GstSegment segment; /* the currently configured segment, in
* samples/audio frames (DEFAULT format) */
GstTagList *tags; GstTagList *tags;
GstFlowReturn last_flow; /* the last flow return received from either GstFlowReturn last_flow; /* to marshal flow return from finis_frame to
* gst_pad_push or gst_pad_buffer_alloc */ * handle_frame via flac callbacks */
gint channels; gint channels;
gint depth; gint depth;
@ -69,12 +61,10 @@ struct _GstFlacDec {
/* from the stream info, needed for scanning */ /* from the stream info, needed for scanning */
guint16 min_blocksize; guint16 min_blocksize;
guint16 max_blocksize; guint16 max_blocksize;
gint64 cur_granulepos; /* only used in framed mode (flac-in-ogg) */
}; };
struct _GstFlacDecClass { struct _GstFlacDecClass {
GstElementClass parent_class; GstAudioDecoderClass audiodecoder;
}; };
GType gst_flac_dec_get_type (void); GType gst_flac_dec_get_type (void);