From 8ced1b35ff4080df103333b58caa27ae86c78f2a Mon Sep 17 00:00:00 2001 From: Jonathan Matthew Date: Wed, 6 Aug 2008 15:34:55 +0000 Subject: ext/flac/: Port flactag to 0.10, add documentation for it and clean it up a bit. Original commit message from CVS: Based on a patch by: Jonathan Matthew * ext/flac/Makefile.am: * ext/flac/gstflac.c: (plugin_init): * ext/flac/gstflactag.c: (gst_flac_tag_setup_interfaces), (gst_flac_tag_base_init), (gst_flac_tag_class_init), (gst_flac_tag_dispose), (gst_flac_tag_init), (gst_flac_tag_sink_setcaps), (gst_flac_tag_chain), (gst_flac_tag_change_state): * ext/flac/gstflactag.h: Port flactag to 0.10, add documentation for it and clean it up a bit. Fixes bug #413841. * docs/plugins/Makefile.am: * docs/plugins/gst-plugins-good-plugins-docs.sgml: * docs/plugins/gst-plugins-good-plugins-sections.txt: * docs/plugins/gst-plugins-good-plugins.hierarchy: * docs/plugins/gst-plugins-good-plugins.interfaces: * docs/plugins/gst-plugins-good-plugins.prerequisites: * docs/plugins/inspect/plugin-flac.xml: * ext/flac/gstflacdec.c: (gst_flac_dec_base_init): * ext/flac/gstflacdec.h: * ext/flac/gstflacenc.c: (gst_flac_enc_base_init): * ext/flac/gstflacenc.h: Add flactag and flacenc to the documentation and mark the private parts of the flacdec instance structure as private. Also use gst_element_class_set_details_simple() in flacdec and flacenc. --- ext/flac/Makefile.am | 4 +- ext/flac/gstflac.c | 4 +- ext/flac/gstflacdec.c | 12 +- ext/flac/gstflacdec.h | 2 + ext/flac/gstflacenc.c | 11 +- ext/flac/gstflacenc.h | 2 + ext/flac/gstflactag.c | 460 +++++++++++++++++++------------------------------- ext/flac/gstflactag.h | 72 ++++++++ 8 files changed, 261 insertions(+), 306 deletions(-) (limited to 'ext/flac') diff --git a/ext/flac/Makefile.am b/ext/flac/Makefile.am index 2723680e..0e7d41a2 100644 --- a/ext/flac/Makefile.am +++ b/ext/flac/Makefile.am @@ -1,6 +1,6 @@ plugin_LTLIBRARIES = libgstflac.la -libgstflac_la_SOURCES = gstflac.c gstflacdec.c gstflacenc.c +libgstflac_la_SOURCES = gstflac.c gstflacdec.c gstflacenc.c gstflactag.c libgstflac_la_CFLAGS = $(GST_PLUGINS_BASE_CFLAGS) $(GST_BASE_CFLAGS) $(GST_CFLAGS) libgstflac_la_LIBADD = \ $(GST_PLUGINS_BASE_LIBS) -lgsttag-$(GST_MAJORMINOR) \ @@ -8,4 +8,4 @@ libgstflac_la_LIBADD = \ $(GST_BASE_LIBS) $(GST_LIBS) $(FLAC_LIBS) libgstflac_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) -noinst_HEADERS = gstflacenc.h gstflacdec.h +noinst_HEADERS = gstflacenc.h gstflacdec.h gstflactag.h diff --git a/ext/flac/gstflac.c b/ext/flac/gstflac.c index d07ec479..7d76a41c 100644 --- a/ext/flac/gstflac.c +++ b/ext/flac/gstflac.c @@ -23,7 +23,7 @@ #include "gstflacenc.h" #include "gstflacdec.h" -/* #include "gstflactag.h" */ +#include "gstflactag.h" #include #include @@ -43,11 +43,9 @@ plugin_init (GstPlugin * plugin) if (!gst_element_register (plugin, "flacdec", GST_RANK_PRIMARY, GST_TYPE_FLAC_DEC)) return FALSE; -#if 0 if (!gst_element_register (plugin, "flactag", GST_RANK_PRIMARY, gst_flac_tag_get_type ())) return FALSE; -#endif gst_tag_register_musicbrainz_tags (); diff --git a/ext/flac/gstflacdec.c b/ext/flac/gstflacdec.c index 7b8e381f..66d6c14c 100644 --- a/ext/flac/gstflacdec.c +++ b/ext/flac/gstflacdec.c @@ -105,15 +105,7 @@ GST_DEBUG_CATEGORY_STATIC (flacdec_debug); static GstPadTemplate *src_template, *sink_template; -static const GstElementDetails flacdec_details = -GST_ELEMENT_DETAILS ("FLAC audio decoder", - "Codec/Decoder/Audio", - "Decodes FLAC lossless audio streams", - "Wim Taymans "); - - static void gst_flac_dec_finalize (GObject * object); - static void gst_flac_dec_loop (GstPad * pad); static GstStateChangeReturn gst_flac_dec_change_state (GstElement * element, @@ -237,7 +229,9 @@ gst_flac_dec_base_init (gpointer g_class) GST_PAD_ALWAYS, raw_caps); gst_element_class_add_pad_template (element_class, sink_template); gst_element_class_add_pad_template (element_class, src_template); - gst_element_class_set_details (element_class, &flacdec_details); + gst_element_class_set_details_simple (element_class, "FLAC audio decoder", + "Codec/Decoder/Audio", + "Decodes FLAC lossless audio streams", "Wim Taymans "); GST_DEBUG_CATEGORY_INIT (flacdec_debug, "flacdec", 0, "flac decoder"); } diff --git a/ext/flac/gstflacdec.h b/ext/flac/gstflacdec.h index b68986d2..100c4e0b 100644 --- a/ext/flac/gstflacdec.h +++ b/ext/flac/gstflacdec.h @@ -47,6 +47,8 @@ typedef struct _GstFlacDecClass GstFlacDecClass; struct _GstFlacDec { GstElement element; + /* < private > */ + #if !defined(FLAC_API_VERSION_CURRENT) || FLAC_API_VERSION_CURRENT < 8 FLAC__SeekableStreamDecoder *seekable_decoder; /* for pull-based operation */ #else diff --git a/ext/flac/gstflacenc.c b/ext/flac/gstflacenc.c index 1a2a34b6..1e29343c 100644 --- a/ext/flac/gstflacenc.c +++ b/ext/flac/gstflacenc.c @@ -77,12 +77,6 @@ static const GstAudioChannelPosition channel_positions[8][8] = { GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT} }; -static const GstElementDetails flacenc_details = -GST_ELEMENT_DETAILS ("FLAC audio encoder", - "Codec/Encoder/Audio", - "Encodes audio with the FLAC lossless audio encoder", - "Wim Taymans "); - #define FLAC_SINK_CAPS \ "audio/x-raw-int, " \ "endianness = (int) BYTE_ORDER, " \ @@ -261,7 +255,10 @@ gst_flac_enc_base_init (gpointer g_class) gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&sink_factory)); - gst_element_class_set_details (element_class, &flacenc_details); + gst_element_class_set_details_simple (element_class, "FLAC audio encoder", + "Codec/Encoder/Audio", + "Encodes audio with the FLAC lossless audio encoder", + "Wim Taymans "); GST_DEBUG_CATEGORY_INIT (flacenc_debug, "flacenc", 0, "Flac encoding element"); diff --git a/ext/flac/gstflacenc.h b/ext/flac/gstflacenc.h index 016a56b6..1c6a6c66 100644 --- a/ext/flac/gstflacenc.h +++ b/ext/flac/gstflacenc.h @@ -45,6 +45,8 @@ typedef struct _GstFlacEncClass GstFlacEncClass; struct _GstFlacEnc { GstElement element; + /* < private > */ + GstPad *sinkpad; GstPad *srcpad; diff --git a/ext/flac/gstflactag.c b/ext/flac/gstflactag.c index 247e1dea..d7ca61cc 100644 --- a/ext/flac/gstflactag.c +++ b/ext/flac/gstflactag.c @@ -1,6 +1,7 @@ - /* GStreamer * Copyright (C) 2003 Christophe Fergeau + * Copyright (C) 2008 Jonathan Matthew + * Copyright (C) 2008 Sebastian Dröge * * gstflactag.c: plug-in for reading/modifying vorbis comments in flac files * @@ -20,78 +21,58 @@ * Boston, MA 02111-1307, USA. */ +/** + * SECTION:element-flactag + * @see_also: #flacenc, #flacdec, #GstTagSetter + * + * The flactag element can change the tag contained within a raw + * FLAC stream. Specifically, it modifies the comments header packet + * of the FLAC stream. + * + * Applications can set the tags to write using the #GstTagSetter interface. + * Tags contained withing the FLAC bitstream will be picked up + * automatically (and merged according to the merge mode set via the tag + * setter interface). + * + * + * Example pipelines + * |[ + * gst-launch -v filesrc location=foo.flac ! flactag ! filesink location=bar.flac + * ]| This element is not useful with gst-launch, because it does not support + * setting the tags on a #GstTagSetter interface. Conceptually, the element + * will usually be used in this order though. + * + */ + #ifdef HAVE_CONFIG_H #include "config.h" #endif + +#include #include +#include #include #include -#define GST_TYPE_FLAC_TAG (gst_flac_tag_get_type()) -#define GST_FLAC_TAG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FLAC_TAG, GstFlacTag)) -#define GST_FLAC_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FLAC_TAG, GstFlacTag)) -#define GST_IS_FLAC_TAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FLAC_TAG)) -#define GST_IS_FLAC_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FLAC_TAG)) +#include "gstflactag.h" -typedef struct _GstFlacTag GstFlacTag; -typedef struct _GstFlacTagClass GstFlacTagClass; - -static inline gint -min (gint a, gint b) -{ - if (a < b) { - return a; - } else { - return b; - } -} - - -typedef enum -{ - GST_FLAC_TAG_STATE_INIT, - GST_FLAC_TAG_STATE_METADATA_BLOCKS, - GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK, - GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK, - GST_FLAC_TAG_STATE_VC_METADATA_BLOCK, - GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT, - GST_FLAC_TAG_STATE_AUDIO_DATA -} -GstFlacTagState; - - -struct _GstFlacTag -{ - GstElement element; - - /* pads */ - GstPad *sinkpad; - GstPad *srcpad; - - GstFlacTagState state; - - GstBuffer *buffer; - GstBuffer *vorbiscomment; - GstTagList *tags; - - guint metadata_bytes_remaining; - gboolean metadata_last_block; - - gboolean only_output_tags; -}; - -struct _GstFlacTagClass -{ - GstElementClass parent_class; -}; +GST_DEBUG_CATEGORY_STATIC (flactag_debug); +#define GST_CAT_DEFAULT flactag_debug /* elementfactory information */ -static const GstElementDetails gst_flac_tag_details = -GST_ELEMENT_DETAILS ("FLAC tagger", - "Tag", - "Rewrite tags in a FLAC file", - "Christope Fergeau "); +static GstStaticPadTemplate flac_tag_src_template = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-flac") + ); +static GstStaticPadTemplate flac_tag_sink_template = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS ("audio/x-flac") + ); /* signals and args */ enum @@ -106,121 +87,42 @@ enum /* FILL ME */ }; -static GstStaticPadTemplate flac_tag_src_template = - GST_STATIC_PAD_TEMPLATE ("src", - GST_PAD_SRC, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-flac; application/x-gst-tags") - ); - -static GstStaticPadTemplate flac_tag_sink_template = -GST_STATIC_PAD_TEMPLATE ("sink", - GST_PAD_SINK, - GST_PAD_ALWAYS, - GST_STATIC_CAPS ("audio/x-flac") - ); - - -static void gst_flac_tag_base_init (gpointer g_class); -static void gst_flac_tag_class_init (GstFlacTagClass * klass); -static void gst_flac_tag_init (GstFlacTag * tag); +static void gst_flac_tag_dispose (GObject * object); -static void gst_flac_tag_chain (GstPad * pad, GstData * data); +static GstFlowReturn gst_flac_tag_chain (GstPad * pad, GstBuffer * buffer); static GstStateChangeReturn gst_flac_tag_change_state (GstElement * element, GstStateChange transition); +static gboolean gst_flac_tag_sink_setcaps (GstPad * pad, GstCaps * caps); -static GstElementClass *parent_class = NULL; - -/* static guint gst_flac_tag_signals[LAST_SIGNAL] = { 0 }; */ - -GType -gst_flac_tag_get_type (void) +static void +gst_flac_tag_setup_interfaces (GType flac_tag_type) { - static GType flac_tag_type = 0; - - if (!flac_tag_type) { - static const GTypeInfo flac_tag_info = { - sizeof (GstFlacTagClass), - gst_flac_tag_base_init, - NULL, - (GClassInitFunc) gst_flac_tag_class_init, - NULL, - NULL, - sizeof (GstFlacTag), - 0, - (GInstanceInitFunc) gst_flac_tag_init, - }; - static const GInterfaceInfo tag_setter_info = { - NULL, - NULL, - NULL - }; - - flac_tag_type = - g_type_register_static (GST_TYPE_ELEMENT, "GstFlacTag", &flac_tag_info, - 0); - - g_type_add_interface_static (flac_tag_type, GST_TYPE_TAG_SETTER, - &tag_setter_info); + static const GInterfaceInfo tag_setter_info = { NULL, NULL, NULL }; - } - return flac_tag_type; + g_type_add_interface_static (flac_tag_type, GST_TYPE_TAG_SETTER, + &tag_setter_info); } +GST_BOILERPLATE_FULL (GstFlacTag, gst_flac_tag, GstElement, GST_TYPE_ELEMENT, + gst_flac_tag_setup_interfaces); static void gst_flac_tag_base_init (gpointer g_class) { GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); - gst_element_class_set_details (element_class, &gst_flac_tag_details); + gst_element_class_set_details_simple (element_class, "FLAC tagger", + "Formatter/Metadata", + "Rewrite tags in a FLAC file", "Christophe Fergeau "); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&flac_tag_sink_template)); gst_element_class_add_pad_template (element_class, gst_static_pad_template_get (&flac_tag_src_template)); -} - -static void -send_eos (GstFlacTag * tag) -{ - gst_element_set_eos (GST_ELEMENT (tag)); - gst_pad_push (tag->srcpad, GST_DATA (gst_event_new (GST_EVENT_EOS))); - /* Seek to end of sink stream */ - if (gst_pad_send_event (GST_PAD_PEER (tag->sinkpad), - gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_END | - GST_SEEK_FLAG_FLUSH, 0))) { - } else { - g_warning ("Couldn't seek to eos on sinkpad\n"); - } -} - - -static gboolean -caps_nego (GstFlacTag * tag) -{ - /* do caps nego */ - GstCaps *caps; - - caps = gst_caps_new_simple ("audio/x-flac", NULL); - if (gst_pad_try_set_caps (tag->srcpad, caps) != GST_PAD_LINK_REFUSED) { - tag->only_output_tags = FALSE; - GST_LOG_OBJECT (tag, "normal operation, using audio/x-flac output"); - } else { - if (gst_pad_try_set_caps (tag->srcpad, - gst_caps_new_simple ("application/x-gst-tags", NULL)) - != GST_PAD_LINK_REFUSED) { - tag->only_output_tags = TRUE; - GST_LOG_OBJECT (tag, "fast operation, just outputting tags"); - printf ("output tags only\n"); - } else { - return FALSE; - } - } - return TRUE; + GST_DEBUG_CATEGORY_INIT (flactag_debug, "flactag", 0, "flac tag rewriter"); } static void @@ -234,86 +136,96 @@ gst_flac_tag_class_init (GstFlacTagClass * klass) parent_class = g_type_class_peek_parent (klass); + gobject_class->dispose = gst_flac_tag_dispose; gstelement_class->change_state = gst_flac_tag_change_state; } +static void +gst_flac_tag_dispose (GObject * object) +{ + GstFlacTag *tag = GST_FLAC_TAG (object); + + if (tag->adapter) { + gst_object_unref (tag->adapter); + tag->adapter = NULL; + } + if (tag->vorbiscomment) { + gst_buffer_unref (tag->vorbiscomment); + tag->vorbiscomment = NULL; + } + if (tag->tags) { + gst_tag_list_free (tag->tags); + tag->tags = NULL; + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + static void -gst_flac_tag_init (GstFlacTag * tag) +gst_flac_tag_init (GstFlacTag * tag, GstFlacTagClass * klass) { /* create the sink and src pads */ tag->sinkpad = gst_pad_new_from_static_template (&flac_tag_sink_template, "sink"); - gst_element_add_pad (GST_ELEMENT (tag), tag->sinkpad); gst_pad_set_chain_function (tag->sinkpad, GST_DEBUG_FUNCPTR (gst_flac_tag_chain)); + gst_pad_set_setcaps_function (tag->sinkpad, + GST_DEBUG_FUNCPTR (gst_flac_tag_sink_setcaps)); + gst_element_add_pad (GST_ELEMENT (tag), tag->sinkpad); tag->srcpad = gst_pad_new_from_static_template (&flac_tag_src_template, "src"); gst_element_add_pad (GST_ELEMENT (tag), tag->srcpad); - tag->buffer = NULL; + tag->adapter = gst_adapter_new (); +} + +static gboolean +gst_flac_tag_sink_setcaps (GstPad * pad, GstCaps * caps) +{ + GstFlacTag *tag = GST_FLAC_TAG (GST_PAD_PARENT (pad)); + + return gst_pad_set_caps (tag->srcpad, caps); } #define FLAC_MAGIC "fLaC" #define FLAC_MAGIC_SIZE (sizeof (FLAC_MAGIC) - 1) -static void -gst_flac_tag_chain (GstPad * pad, GstData * data) +static GstFlowReturn +gst_flac_tag_chain (GstPad * pad, GstBuffer * buffer) { - GstBuffer *buffer; GstFlacTag *tag; + GstFlowReturn ret; - if (GST_IS_EVENT (data)) { - g_print ("Unhandled event\n"); - return; - } - - buffer = GST_BUFFER (data); + ret = GST_FLOW_OK; tag = GST_FLAC_TAG (gst_pad_get_parent (pad)); - if (tag->buffer) { - GstBuffer *merge; - - merge = gst_buffer_merge (tag->buffer, buffer); - gst_buffer_unref (buffer); - gst_buffer_unref (tag->buffer); - tag->buffer = merge; - } else { - tag->buffer = buffer; - } - + gst_adapter_push (tag->adapter, buffer); /* Initial state, we don't even know if we are dealing with a flac file */ if (tag->state == GST_FLAC_TAG_STATE_INIT) { - if (!caps_nego (tag)) { - goto cleanup; - } + GstBuffer *id_buffer; - if (GST_BUFFER_SIZE (tag->buffer) < sizeof (FLAC_MAGIC)) { + if (gst_adapter_available (tag->adapter) < sizeof (FLAC_MAGIC)) goto cleanup; - } - if (strncmp (GST_BUFFER_DATA (tag->buffer), FLAC_MAGIC, - FLAC_MAGIC_SIZE) == 0) { - GstBuffer *sub; + id_buffer = gst_adapter_take_buffer (tag->adapter, FLAC_MAGIC_SIZE); + GST_DEBUG_OBJECT (tag, "looking for " FLAC_MAGIC " identifier"); + if (memcmp (GST_BUFFER_DATA (id_buffer), FLAC_MAGIC, FLAC_MAGIC_SIZE) == 0) { + + GST_DEBUG_OBJECT (tag, "pushing " FLAC_MAGIC " identifier buffer"); + gst_buffer_set_caps (id_buffer, GST_PAD_CAPS (tag->srcpad)); + ret = gst_pad_push (tag->srcpad, id_buffer); + if (ret != GST_FLOW_OK) + goto cleanup; tag->state = GST_FLAC_TAG_STATE_METADATA_BLOCKS; - sub = gst_buffer_create_sub (tag->buffer, 0, FLAC_MAGIC_SIZE); - - gst_pad_push (tag->srcpad, GST_DATA (sub)); - sub = - gst_buffer_create_sub (tag->buffer, FLAC_MAGIC_SIZE, - GST_BUFFER_SIZE (tag->buffer) - FLAC_MAGIC_SIZE); - gst_buffer_unref (tag->buffer); - /* We do a copy because we need a writable buffer, and _create_sub - * sets the buffer it uses to read-only - */ - tag->buffer = gst_buffer_copy (sub); - gst_buffer_unref (sub); } else { /* FIXME: does that work well with FLAC files containing ID3v2 tags ? */ + gst_buffer_unref (id_buffer); GST_ELEMENT_ERROR (tag, STREAM, WRONG_TYPE, (NULL), (NULL)); + ret = GST_FLOW_ERROR; } } @@ -325,8 +237,9 @@ gst_flac_tag_chain (GstPad * pad, GstData * data) guint size; guint type; gboolean is_last; + const guint8 *block_header; - g_assert (tag->metadata_bytes_remaining == 0); + g_assert (tag->metadata_block_size == 0); g_assert (tag->metadata_last_block == FALSE); /* The header of a flac metadata block is 4 bytes long: @@ -334,26 +247,25 @@ gst_flac_tag_chain (GstPad * pad, GstData * data) * 7 next bits: 4 if vorbis comment block * 24 next bits: size of the metadata to follow (big endian) */ - if (GST_BUFFER_SIZE (tag->buffer) < 4) { + if (gst_adapter_available (tag->adapter) < 4) goto cleanup; - } - is_last = (((GST_BUFFER_DATA (tag->buffer)[0]) & 0x80) == 0x80); - /* If we have metadata set on the element, the last metadata block - * will be the vorbis comment block which we will build ourselves - */ - if (is_last) { - (GST_BUFFER_DATA (tag->buffer)[0]) &= (~0x80); - } - type = (GST_BUFFER_DATA (tag->buffer)[0]) & 0x7F; - size = ((GST_BUFFER_DATA (tag->buffer)[1]) << 16) - | ((GST_BUFFER_DATA (tag->buffer)[2]) << 8) - | (GST_BUFFER_DATA (tag->buffer)[3]); + block_header = gst_adapter_peek (tag->adapter, 4); + + is_last = ((block_header[0] & 0x80) == 0x80); + type = block_header[0] & 0x7F; + size = (block_header[1] << 16) + | (block_header[2] << 8) + | block_header[3]; /* The 4 bytes long header isn't included in the metadata size */ - tag->metadata_bytes_remaining = size + 4; + tag->metadata_block_size = size + 4; tag->metadata_last_block = is_last; + GST_DEBUG_OBJECT (tag, + "got metadata block: %d bytes, type %d, is vorbiscomment: %d, is last: %d", + size, type, (type == 0x04), is_last); + /* Metadata blocks of type 4 are vorbis comment blocks */ if (type == 0x04) { tag->state = GST_FLAC_TAG_STATE_VC_METADATA_BLOCK; @@ -366,56 +278,29 @@ gst_flac_tag_chain (GstPad * pad, GstData * data) /* Reads a metadata block */ if ((tag->state == GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK) || (tag->state == GST_FLAC_TAG_STATE_VC_METADATA_BLOCK)) { - GstBuffer *sub; - guint bytes_to_push; - - g_assert (tag->metadata_bytes_remaining != 0); + GstBuffer *metadata_buffer; - bytes_to_push = min (tag->metadata_bytes_remaining, - GST_BUFFER_SIZE (tag->buffer)); + if (gst_adapter_available (tag->adapter) < tag->metadata_block_size) + goto cleanup; - sub = gst_buffer_create_sub (tag->buffer, 0, bytes_to_push); + metadata_buffer = gst_adapter_take_buffer (tag->adapter, + tag->metadata_block_size); + /* clear the is-last flag, as the last metadata block will + * be the vorbis comment block which we will build ourselves. + */ + GST_BUFFER_DATA (metadata_buffer)[0] &= (~0x80); if (tag->state == GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK) { - gst_pad_push (tag->srcpad, GST_DATA (sub)); - } else { - if (tag->vorbiscomment == NULL) { - tag->vorbiscomment = sub; - } else { - GstBuffer *merge; - - merge = gst_buffer_merge (tag->vorbiscomment, sub); - gst_buffer_unref (tag->vorbiscomment); - gst_buffer_unref (sub); - tag->vorbiscomment = merge; - } - } - - tag->metadata_bytes_remaining -= (bytes_to_push); - - if (GST_BUFFER_SIZE (tag->buffer) > bytes_to_push) { - GstBuffer *sub; - - sub = gst_buffer_create_sub (tag->buffer, bytes_to_push, - GST_BUFFER_SIZE (tag->buffer) - bytes_to_push); - gst_buffer_unref (tag->buffer); - - /* We make a copy because we need a writable buffer, and _create_sub - * sets the buffer it uses to read-only - */ - tag->buffer = gst_buffer_copy (sub); - gst_buffer_unref (sub); - - tag->state = GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK; - } else if (tag->metadata_bytes_remaining == 0) { - gst_buffer_unref (tag->buffer); - tag->buffer = NULL; - tag->state = GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK; - tag->buffer = NULL; + GST_DEBUG_OBJECT (tag, "pushing metadata block buffer"); + gst_buffer_set_caps (metadata_buffer, GST_PAD_CAPS (tag->srcpad)); + ret = gst_pad_push (tag->srcpad, metadata_buffer); + if (ret != GST_FLOW_OK) + goto cleanup; } else { - tag->state = GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK; - tag->buffer = NULL; + tag->vorbiscomment = metadata_buffer; } + tag->metadata_block_size = 0; + tag->state = GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK; } /* This state is mainly used to be able to stop as soon as we read @@ -428,36 +313,25 @@ gst_flac_tag_chain (GstPad * pad, GstData * data) */ if (tag->vorbiscomment != NULL) { /* We found some tags, try to parse them and notify the other elements - * that we encoutered some tags + * that we encountered some tags */ + GST_DEBUG_OBJECT (tag, "emitting vorbiscomment tags"); tag->tags = gst_tag_list_from_vorbiscomment_buffer (tag->vorbiscomment, GST_BUFFER_DATA (tag->vorbiscomment), 4, NULL); if (tag->tags != NULL) { - gst_element_found_tags (GST_ELEMENT (tag), tag->tags); + gst_element_found_tags (GST_ELEMENT (tag), + gst_tag_list_copy (tag->tags)); } gst_buffer_unref (tag->vorbiscomment); tag->vorbiscomment = NULL; - - if (tag->only_output_tags) { - send_eos (tag); - goto cleanup; - } } /* Skip to next state */ if (tag->metadata_last_block == FALSE) { tag->state = GST_FLAC_TAG_STATE_METADATA_BLOCKS; } else { - if (tag->only_output_tags) { - /* If we finished parsing the metadata blocks, we will never find any - * metadata, so just stop now - */ - send_eos (tag); - goto cleanup; - } else { - tag->state = GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT; - } + tag->state = GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT; } } @@ -471,18 +345,21 @@ gst_flac_tag_chain (GstPad * pad, GstData * data) const GstTagList *user_tags; GstTagList *merged_tags; - g_assert (tag->only_output_tags == FALSE); - + /* merge the tag lists */ user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (tag)); - merged_tags = gst_tag_list_merge (tag->tags, user_tags, - gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (tag))); + if (user_tags != NULL) { + merged_tags = gst_tag_list_merge (user_tags, tag->tags, + gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (tag))); + } else { + merged_tags = gst_tag_list_copy (tag->tags); + } if (merged_tags == NULL) { /* If we get a NULL list of tags, we must generate a padding block * which is marked as the last metadata block, otherwise we'll * end up with a corrupted flac file. */ - g_warning ("No tags found\n"); + GST_WARNING_OBJECT (tag, "No tags found"); buffer = gst_buffer_new_and_alloc (12); if (buffer == NULL) { GST_ELEMENT_ERROR (tag, CORE, TOO_LAZY, (NULL), @@ -501,6 +378,7 @@ gst_flac_tag_chain (GstPad * pad, GstData * data) */ buffer = gst_tag_list_to_vorbiscomment_buffer (merged_tags, header, sizeof (header), NULL); + GST_DEBUG_OBJECT (tag, "Writing tags %" GST_PTR_FORMAT, merged_tags); gst_tag_list_free (merged_tags); if (buffer == NULL) { GST_ELEMENT_ERROR (tag, CORE, TAG, (NULL), @@ -539,18 +417,30 @@ gst_flac_tag_chain (GstPad * pad, GstData * data) GST_BUFFER_DATA (buffer)[1] = ((size & 0xFF0000) >> 16); GST_BUFFER_DATA (buffer)[2] = ((size & 0x00FF00) >> 8); GST_BUFFER_DATA (buffer)[3] = (size & 0x0000FF); - gst_pad_push (tag->srcpad, GST_DATA (buffer)); + GST_DEBUG_OBJECT (tag, "pushing %d byte vorbiscomment buffer", + GST_BUFFER_SIZE (buffer)); + gst_buffer_set_caps (buffer, GST_PAD_CAPS (tag->srcpad)); + ret = gst_pad_push (tag->srcpad, buffer); + if (ret != GST_FLOW_OK) { + goto cleanup; + } tag->state = GST_FLAC_TAG_STATE_AUDIO_DATA; } /* The metadata blocks have been read, now we are reading audio data */ if (tag->state == GST_FLAC_TAG_STATE_AUDIO_DATA) { - gst_pad_push (tag->srcpad, GST_DATA (tag->buffer)); - tag->buffer = NULL; + GstBuffer *buffer; + buffer = + gst_adapter_take_buffer (tag->adapter, + gst_adapter_available (tag->adapter)); + gst_buffer_set_caps (buffer, GST_PAD_CAPS (tag->srcpad)); + ret = gst_pad_push (tag->srcpad, buffer); } cleanup: gst_object_unref (tag); + + return ret; } @@ -572,17 +462,17 @@ gst_flac_tag_change_state (GstElement * element, GstStateChange transition) case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: - if (tag->buffer) { - gst_buffer_unref (tag->buffer); - tag->buffer = NULL; - } + gst_adapter_clear (tag->adapter); if (tag->vorbiscomment) { gst_buffer_unref (tag->vorbiscomment); tag->vorbiscomment = NULL; } if (tag->tags) { gst_tag_list_free (tag->tags); + tag->tags = NULL; } + tag->metadata_block_size = 0; + tag->metadata_last_block = FALSE; tag->state = GST_FLAC_TAG_STATE_INIT; break; case GST_STATE_CHANGE_READY_TO_NULL: diff --git a/ext/flac/gstflactag.h b/ext/flac/gstflactag.h index 5107d1c3..a6f90f5e 100644 --- a/ext/flac/gstflactag.h +++ b/ext/flac/gstflactag.h @@ -1,6 +1,78 @@ +/* GStreamer + * Copyright (C) 2003 Christophe Fergeau + * Copyright (C) 2008 Jonathan Matthew + * Copyright (C) 2008 Sebastian Dröge + * + * gstflactag.c: plug-in for reading/modifying vorbis comments in flac files + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + #ifndef GST_FLAC_TAG_H #define GST_FLAC_TAG_H +#include +#include + +#define GST_TYPE_FLAC_TAG (gst_flac_tag_get_type()) +#define GST_FLAC_TAG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FLAC_TAG, GstFlacTag)) +#define GST_FLAC_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FLAC_TAG, GstFlacTag)) +#define GST_IS_FLAC_TAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FLAC_TAG)) +#define GST_IS_FLAC_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FLAC_TAG)) + +typedef struct _GstFlacTag GstFlacTag; +typedef struct _GstFlacTagClass GstFlacTagClass; + +typedef enum +{ + GST_FLAC_TAG_STATE_INIT, + GST_FLAC_TAG_STATE_METADATA_BLOCKS, + GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK, + GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK, + GST_FLAC_TAG_STATE_VC_METADATA_BLOCK, + GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT, + GST_FLAC_TAG_STATE_AUDIO_DATA +} +GstFlacTagState; + +struct _GstFlacTag +{ + GstElement element; + + /* < private > */ + + /* pads */ + GstPad *sinkpad; + GstPad *srcpad; + + GstFlacTagState state; + + GstAdapter *adapter; + GstBuffer *vorbiscomment; + GstTagList *tags; + + guint metadata_block_size; + gboolean metadata_last_block; +}; + +struct _GstFlacTagClass +{ + GstElementClass parent_class; +}; + GType gst_flac_tag_get_type (void); #endif /* GST_FLAC_TAG_H */ -- cgit