1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
3 * Copyright (C) 2003-2004 Benjamin Otte <otte@gnome.org>
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 * Boston, MA 02111-1307, USA.
22 * SECTION:element-id3demux
23 * @short_description: reads tag information from ID3v1 and ID3v2 (<= 2.4.0) data blocks and outputs them as GStreamer tag messages and events.
27 * id3demux accepts data streams with either (or both) ID3v2 regions at the start, or ID3v1 at the end. The mime type of the data between the tag blocks is detected using typefind functions, and the appropriate output mime type set on outgoing buffers.
29 * The element is only able to read ID3v1 tags from a seekable stream, because they are at the end of the stream. That is, when get_range mode is supported by the upstream elements. If get_range operation is available, id3demux makes it available downstream. This means that elements which require get_range mode, such as wavparse, can operate on files containing ID3 tag information.
31 * <title>Example launch line</title>
34 * gst-launch filesrc location=file.mp3 ! id3demux ! fakesink -t
36 * This pipeline should read any available ID3 tag information and output it. The contents of the file inside the ID3 tag regions should be detected, and the appropriate mime type set on buffers produced from id3demux.
38 * This id3demux element replaced an older element with the same name which relied on libid3tag from the MAD project.
46 #include <gst/base/gsttypefindhelper.h>
47 #include <gst/gst-i18n-plugin.h>
48 #include <gst/tag/tag.h>
50 #include "gstid3demux.h"
53 static const GstElementDetails gst_id3demux_details =
54 GST_ELEMENT_DETAILS ("ID3 tag demuxer",
55 "Codec/Demuxer/Metadata",
56 "Read and output ID3v1 and ID3v2 tags while demuxing the contents",
57 "Jan Schmidt <thaytan@mad.scientist.com>");
65 /* Require at least 4kB of data before we attempt typefind.
66 * Seems a decent value based on test files
67 * 40kB is massive overkill for the maximum, I think, but it
68 * doesn't do any harm */
69 #define ID3_TYPE_FIND_MIN_SIZE 4096
70 #define ID3_TYPE_FIND_MAX_SIZE 40960
72 GST_DEBUG_CATEGORY (id3demux_debug);
73 #define GST_CAT_DEFAULT (id3demux_debug)
75 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
78 GST_STATIC_CAPS ("application/x-id3")
81 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
84 GST_STATIC_CAPS ("ANY")
87 static void gst_id3demux_class_init (GstID3DemuxClass * klass);
88 static void gst_id3demux_base_init (GstID3DemuxClass * klass);
89 static void gst_id3demux_init (GstID3Demux * id3demux);
90 static void gst_id3demux_dispose (GObject * object);
92 static void gst_id3demux_set_property (GObject * object, guint prop_id,
93 const GValue * value, GParamSpec * pspec);
94 static void gst_id3demux_get_property (GObject * object, guint prop_id,
95 GValue * value, GParamSpec * pspec);
97 static GstFlowReturn gst_id3demux_chain (GstPad * pad, GstBuffer * buf);
98 static gboolean gst_id3demux_sink_event (GstPad * pad, GstEvent * event);
99 static gboolean gst_id3demux_src_activate_pull (GstPad * pad, gboolean active);
100 static GstFlowReturn gst_id3demux_read_range (GstID3Demux * id3demux,
101 guint64 offset, guint length, GstBuffer ** buffer);
103 static gboolean gst_id3demux_src_checkgetrange (GstPad * srcpad);
104 static GstFlowReturn gst_id3demux_src_getrange (GstPad * srcpad,
105 guint64 offset, guint length, GstBuffer ** buffer);
107 static gboolean gst_id3demux_add_srcpad (GstID3Demux * id3demux,
109 static gboolean gst_id3demux_remove_srcpad (GstID3Demux * id3demux);
111 static gboolean gst_id3demux_srcpad_event (GstPad * pad, GstEvent * event);
112 static gboolean gst_id3demux_sink_activate (GstPad * sinkpad);
113 static GstStateChangeReturn gst_id3demux_change_state (GstElement * element,
114 GstStateChange transition);
115 static gboolean gst_id3demux_pad_query (GstPad * pad, GstQuery * query);
116 static const GstQueryType *gst_id3demux_get_query_types (GstPad * pad);
117 static gboolean id3demux_get_upstream_size (GstID3Demux * id3demux);
118 static void gst_id3demux_send_tag_event (GstID3Demux * id3demux);
120 static GstElementClass *parent_class = NULL;
123 gst_id3demux_get_type (void)
125 static GType plugin_type = 0;
128 static const GTypeInfo plugin_info = {
129 sizeof (GstID3DemuxClass),
130 (GBaseInitFunc) gst_id3demux_base_init,
132 (GClassInitFunc) gst_id3demux_class_init,
135 sizeof (GstID3Demux),
137 (GInstanceInitFunc) gst_id3demux_init,
139 plugin_type = g_type_register_static (GST_TYPE_ELEMENT,
140 "GstID3Demux", &plugin_info, 0);
146 gst_id3demux_base_init (GstID3DemuxClass * klass)
148 GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
150 gst_element_class_add_pad_template (element_class,
151 gst_static_pad_template_get (&src_factory));
152 gst_element_class_add_pad_template (element_class,
153 gst_static_pad_template_get (&sink_factory));
154 gst_element_class_set_details (element_class, &gst_id3demux_details);
158 gst_id3demux_class_init (GstID3DemuxClass * klass)
160 GObjectClass *gobject_class;
161 GstElementClass *gstelement_class;
163 gobject_class = (GObjectClass *) klass;
164 gstelement_class = (GstElementClass *) klass;
166 parent_class = g_type_class_peek_parent (klass);
168 gobject_class->dispose = gst_id3demux_dispose;
170 gobject_class->set_property = gst_id3demux_set_property;
171 gobject_class->get_property = gst_id3demux_get_property;
173 gstelement_class->change_state = gst_id3demux_change_state;
175 g_object_class_install_property (gobject_class, ARG_PREFER_V1,
176 g_param_spec_boolean ("prefer-v1", "Prefer version 1 tag",
177 "Prefer tags from ID3v1 tag at end of file when both ID3v1 "
178 "and ID3v2 tags are present", FALSE,
179 G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
183 gst_id3demux_reset (GstID3Demux * id3demux)
185 GstBuffer **buffer_p = &id3demux->collect;
186 GstCaps **caps_p = &id3demux->src_caps;
188 id3demux->strip_start = 0;
189 id3demux->strip_end = 0;
190 id3demux->upstream_size = -1;
191 id3demux->state = GST_ID3DEMUX_READID3V2;
192 id3demux->send_tag_event = FALSE;
194 gst_buffer_replace (buffer_p, NULL);
195 gst_caps_replace (caps_p, NULL);
197 gst_id3demux_remove_srcpad (id3demux);
199 if (id3demux->event_tags) {
200 gst_tag_list_free (id3demux->event_tags);
201 id3demux->event_tags = NULL;
203 if (id3demux->parsed_tags) {
204 gst_tag_list_free (id3demux->parsed_tags);
205 id3demux->parsed_tags = NULL;
211 gst_id3demux_init (GstID3Demux * id3demux)
213 GstElementClass *klass = GST_ELEMENT_GET_CLASS (id3demux);
216 gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
218 gst_pad_set_activate_function (id3demux->sinkpad,
219 GST_DEBUG_FUNCPTR (gst_id3demux_sink_activate));
220 gst_pad_set_event_function (id3demux->sinkpad,
221 GST_DEBUG_FUNCPTR (gst_id3demux_sink_event));
222 gst_pad_set_chain_function (id3demux->sinkpad,
223 GST_DEBUG_FUNCPTR (gst_id3demux_chain));
224 gst_element_add_pad (GST_ELEMENT (id3demux), id3demux->sinkpad);
226 id3demux->prefer_v1 = FALSE;
227 gst_id3demux_reset (id3demux);
231 gst_id3demux_dispose (GObject * object)
233 GstID3Demux *id3demux = GST_ID3DEMUX (object);
235 gst_id3demux_reset (id3demux);
237 G_OBJECT_CLASS (parent_class)->dispose (object);
241 gst_id3demux_add_srcpad (GstID3Demux * id3demux, GstCaps * new_caps)
243 if (id3demux->src_caps == NULL ||
244 !gst_caps_is_equal (new_caps, id3demux->src_caps)) {
246 gst_caps_replace (&(id3demux->src_caps), new_caps);
247 if (id3demux->srcpad != NULL) {
248 GST_DEBUG_OBJECT (id3demux, "Changing src pad caps to %" GST_PTR_FORMAT,
251 gst_pad_set_caps (id3demux->srcpad, id3demux->src_caps);
254 /* Caps never changed */
255 gst_caps_unref (new_caps);
258 if (id3demux->srcpad == NULL) {
259 id3demux->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
260 g_return_val_if_fail (id3demux->srcpad != NULL, FALSE);
262 gst_pad_set_query_type_function (id3demux->srcpad,
263 GST_DEBUG_FUNCPTR (gst_id3demux_get_query_types));
264 gst_pad_set_query_function (id3demux->srcpad,
265 GST_DEBUG_FUNCPTR (gst_id3demux_pad_query));
266 gst_pad_set_event_function (id3demux->srcpad,
267 GST_DEBUG_FUNCPTR (gst_id3demux_srcpad_event));
268 gst_pad_set_activatepull_function (id3demux->srcpad,
269 GST_DEBUG_FUNCPTR (gst_id3demux_src_activate_pull));
270 gst_pad_set_checkgetrange_function (id3demux->srcpad,
271 GST_DEBUG_FUNCPTR (gst_id3demux_src_checkgetrange));
272 gst_pad_set_getrange_function (id3demux->srcpad,
273 GST_DEBUG_FUNCPTR (gst_id3demux_src_getrange));
275 gst_pad_use_fixed_caps (id3demux->srcpad);
277 if (id3demux->src_caps)
278 gst_pad_set_caps (id3demux->srcpad, id3demux->src_caps);
280 GST_DEBUG_OBJECT (id3demux, "Adding src pad with caps %" GST_PTR_FORMAT,
283 gst_object_ref (id3demux->srcpad);
284 if (!(gst_element_add_pad (GST_ELEMENT (id3demux), id3demux->srcpad)))
286 gst_element_no_more_pads (GST_ELEMENT (id3demux));
293 gst_id3demux_remove_srcpad (GstID3Demux * id3demux)
297 if (id3demux->srcpad != NULL) {
298 GST_DEBUG_OBJECT (id3demux, "Removing src pad");
299 res = gst_element_remove_pad (GST_ELEMENT (id3demux), id3demux->srcpad);
300 g_return_val_if_fail (res != FALSE, FALSE);
301 gst_object_unref (id3demux->srcpad);
302 id3demux->srcpad = NULL;
309 gst_id3demux_trim_buffer (GstID3Demux * id3demux, GstBuffer ** buf_ref)
311 GstBuffer *buf = *buf_ref;
313 guint trim_start = 0;
314 guint out_size = GST_BUFFER_SIZE (buf);
315 guint64 out_offset = GST_BUFFER_OFFSET (buf);
316 gboolean need_sub = FALSE;
318 /* Adjust offset and length */
319 if (!GST_BUFFER_OFFSET_IS_VALID (buf)) {
320 /* Can't change anything without an offset */
324 /* If the buffer crosses the ID3v1 tag at the end of file, trim it */
325 if (id3demux->strip_end > 0) {
326 if (id3demux_get_upstream_size (id3demux)) {
327 guint64 v1tag_offset = id3demux->upstream_size - id3demux->strip_end;
329 if (out_offset >= v1tag_offset) {
330 GST_DEBUG_OBJECT (id3demux, "Buffer is past the end of the data");
334 if (out_offset + out_size > v1tag_offset) {
335 out_size = v1tag_offset - out_offset;
341 if (id3demux->strip_start > 0) {
342 /* If the buffer crosses the ID3v2 tag at the start of file, trim it */
343 if (out_offset <= id3demux->strip_start) {
344 if (out_offset + out_size <= id3demux->strip_start) {
345 GST_DEBUG_OBJECT (id3demux, "Buffer is before the start of the data");
349 trim_start = id3demux->strip_start - out_offset;
350 out_size -= trim_start;
353 out_offset -= id3demux->strip_start;
358 g_assert (out_size > 0);
360 if (need_sub == TRUE) {
361 if (out_size != GST_BUFFER_SIZE (buf) || !gst_buffer_is_writable (buf)) {
364 GST_DEBUG_OBJECT (id3demux, "Sub-buffering to trim size %d offset %"
365 G_GINT64_FORMAT " to %d offset %" G_GINT64_FORMAT,
366 GST_BUFFER_SIZE (buf), GST_BUFFER_OFFSET (buf), out_size, out_offset);
368 sub = gst_buffer_create_sub (buf, trim_start, out_size);
369 g_return_val_if_fail (sub != NULL, FALSE);
370 gst_buffer_unref (buf);
371 *buf_ref = buf = sub;
373 GST_DEBUG_OBJECT (id3demux, "Adjusting buffer from size %d offset %"
374 G_GINT64_FORMAT " to %d offset %" G_GINT64_FORMAT,
375 GST_BUFFER_SIZE (buf), GST_BUFFER_OFFSET (buf), out_size, out_offset);
378 GST_BUFFER_OFFSET (buf) = out_offset;
379 GST_BUFFER_OFFSET_END (buf) = out_offset + out_size;
380 gst_buffer_set_caps (buf, id3demux->src_caps);
385 gst_buffer_unref (buf);
391 gst_id3demux_chain (GstPad * pad, GstBuffer * buf)
393 GstID3Demux *id3demux;
395 id3demux = GST_ID3DEMUX (GST_PAD_PARENT (pad));
396 g_return_val_if_fail (GST_IS_ID3DEMUX (id3demux), GST_FLOW_ERROR);
398 if (id3demux->collect == NULL) {
399 id3demux->collect = buf;
401 id3demux->collect = gst_buffer_join (id3demux->collect, buf);
405 switch (id3demux->state) {
406 case GST_ID3DEMUX_READID3V2:
407 /* If we receive a buffer that's from the middle of the file,
408 * we can't read tags so move to typefinding */
409 if (GST_BUFFER_OFFSET (id3demux->collect) != 0) {
410 GST_DEBUG_OBJECT (id3demux,
411 "Received buffer from non-zero offset. Can't read tags");
413 ID3TagsResult tag_result;
415 tag_result = id3demux_read_id3v2_tag (id3demux->collect,
416 &id3demux->strip_start, &id3demux->parsed_tags);
417 if (tag_result == ID3TAGS_MORE_DATA)
418 break; /* Go get more data and try again */
419 else if (tag_result == ID3TAGS_BROKEN_TAG)
420 GST_WARNING_OBJECT (id3demux,
421 "Ignoring broken ID3v2 tag of size %d", id3demux->strip_start);
423 GST_DEBUG_OBJECT (id3demux, "Found an ID3v2 tag of size %d\n",
424 id3demux->strip_start);
426 id3demux->send_tag_event = TRUE;
428 id3demux->state = GST_ID3DEMUX_TYPEFINDING;
431 case GST_ID3DEMUX_TYPEFINDING:{
432 GstTypeFindProbability probability = 0;
433 GstBuffer *typefind_buf = NULL;
436 if (GST_BUFFER_SIZE (id3demux->collect) <
437 ID3_TYPE_FIND_MIN_SIZE + id3demux->strip_start)
438 break; /* Go get more data first */
440 GST_DEBUG_OBJECT (id3demux, "Typefinding with size %d",
441 GST_BUFFER_SIZE (id3demux->collect));
443 /* Trim the buffer and adjust offset for typefinding */
444 typefind_buf = id3demux->collect;
445 gst_buffer_ref (typefind_buf);
446 if (!gst_id3demux_trim_buffer (id3demux, &typefind_buf))
447 return GST_FLOW_ERROR;
449 if (typefind_buf == NULL)
450 break; /* Still need more data */
452 caps = gst_type_find_helper_for_buffer (GST_OBJECT (id3demux),
453 typefind_buf, &probability);
456 if (GST_BUFFER_SIZE (typefind_buf) < ID3_TYPE_FIND_MAX_SIZE) {
457 /* Just break for more data */
458 gst_buffer_unref (typefind_buf);
462 /* We failed typefind */
463 GST_ELEMENT_ERROR (id3demux, STREAM, TYPE_NOT_FOUND, (NULL),
464 ("Could not detect type for contents within an ID3 tag"));
465 gst_buffer_unref (typefind_buf);
466 gst_buffer_unref (id3demux->collect);
467 id3demux->collect = NULL;
468 return GST_FLOW_ERROR;
471 GST_DEBUG_OBJECT (id3demux, "Found type %" GST_PTR_FORMAT " with a "
472 "probability of %u, buf size was %u", caps, probability,
473 GST_BUFFER_SIZE (typefind_buf));
475 gst_buffer_unref (typefind_buf);
477 if (!gst_id3demux_add_srcpad (id3demux, caps)) {
478 GST_DEBUG_OBJECT (id3demux, "Failed to add srcpad");
479 gst_caps_unref (caps);
482 gst_caps_unref (caps);
484 /* Move onto streaming and fall-through to push out existing
486 id3demux->state = GST_ID3DEMUX_STREAMING;
488 /* Now that typefinding is complete, post the
490 if (id3demux->parsed_tags != NULL) {
491 gst_element_post_message (GST_ELEMENT (id3demux),
492 gst_message_new_tag (GST_OBJECT (id3demux),
493 gst_tag_list_copy (id3demux->parsed_tags)));
497 case GST_ID3DEMUX_STREAMING:{
498 GstBuffer *outbuf = NULL;
500 if (id3demux->send_tag_event) {
501 gst_id3demux_send_tag_event (id3demux);
502 id3demux->send_tag_event = FALSE;
505 /* Trim the buffer and adjust offset */
506 if (id3demux->collect) {
507 outbuf = id3demux->collect;
508 id3demux->collect = NULL;
509 if (!gst_id3demux_trim_buffer (id3demux, &outbuf))
510 return GST_FLOW_ERROR;
513 if (G_UNLIKELY (id3demux->srcpad == NULL)) {
514 gst_buffer_unref (outbuf);
515 return GST_FLOW_ERROR;
518 GST_DEBUG_OBJECT (id3demux, "Pushing buffer %p", outbuf);
520 /* Ensure the caps are set correctly */
521 outbuf = gst_buffer_make_metadata_writable (outbuf);
522 gst_buffer_set_caps (outbuf, GST_PAD_CAPS (id3demux->srcpad));
524 return gst_pad_push (id3demux->srcpad, outbuf);
531 GST_DEBUG_OBJECT (id3demux, "error in chain function");
533 return GST_FLOW_ERROR;
537 gst_id3demux_sink_event (GstPad * pad, GstEvent * event)
542 demux = GST_ID3DEMUX (gst_pad_get_parent (pad));
544 switch (GST_EVENT_TYPE (event)) {
546 if (demux->srcpad == NULL) {
547 GST_WARNING_OBJECT (demux, "EOS before we found a type");
548 GST_ELEMENT_ERROR (demux, STREAM, TYPE_NOT_FOUND, (NULL), (NULL));
550 ret = gst_pad_event_default (pad, event);
553 ret = gst_pad_event_default (pad, event);
557 gst_object_unref (demux);
563 gst_id3demux_set_property (GObject * object, guint prop_id,
564 const GValue * value, GParamSpec * pspec)
566 GstID3Demux *id3demux;
568 g_return_if_fail (GST_IS_ID3DEMUX (object));
569 id3demux = GST_ID3DEMUX (object);
573 id3demux->prefer_v1 = g_value_get_boolean (value);
576 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
582 gst_id3demux_get_property (GObject * object, guint prop_id,
583 GValue * value, GParamSpec * pspec)
585 GstID3Demux *id3demux;
587 g_return_if_fail (GST_IS_ID3DEMUX (object));
588 id3demux = GST_ID3DEMUX (object);
592 g_value_set_boolean (value, id3demux->prefer_v1);
595 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
601 id3demux_get_upstream_size (GstID3Demux * id3demux)
606 /* Short-cut if we already queried upstream */
607 if (id3demux->upstream_size > 0)
610 format = GST_FORMAT_BYTES;
611 if (!gst_pad_query_peer_duration (id3demux->sinkpad, &format, &result) ||
616 id3demux->upstream_size = result;
621 gst_id3demux_srcpad_event (GstPad * pad, GstEvent * event)
623 gboolean res = FALSE;
624 GstID3Demux *id3demux = GST_ID3DEMUX (GST_PAD_PARENT (pad));
626 /* Handle SEEK events, with adjusted byte offsets and sizes. */
628 switch (GST_EVENT_TYPE (event)) {
633 GstSeekType cur_type, stop_type;
637 gst_event_parse_seek (event, &rate, &format, &flags,
638 &cur_type, &cur, &stop_type, &stop);
640 if (format == GST_FORMAT_BYTES &&
641 id3demux->state == GST_ID3DEMUX_STREAMING &&
642 gst_pad_is_linked (id3demux->sinkpad)) {
646 case GST_SEEK_TYPE_SET:
647 cur += id3demux->strip_start;
649 case GST_SEEK_TYPE_CUR:
651 case GST_SEEK_TYPE_END:
652 cur += id3demux->strip_end;
655 g_assert_not_reached ();
659 case GST_SEEK_TYPE_SET:
660 stop += id3demux->strip_start;
662 case GST_SEEK_TYPE_CUR:
664 case GST_SEEK_TYPE_END:
665 stop += id3demux->strip_end;
670 upstream = gst_event_new_seek (rate, format, flags,
671 cur_type, cur, stop_type, stop);
672 res = gst_pad_push_event (id3demux->sinkpad, upstream);
680 gst_event_unref (event);
684 /* Read and interpret any ID3v1 tag when activating in pull_range */
686 gst_id3demux_read_id3v1 (GstID3Demux * id3demux, GstTagList ** tags)
688 GstBuffer *buffer = NULL;
689 gboolean res = FALSE;
690 ID3TagsResult tag_res;
691 GstFlowReturn flow_ret;
692 guint64 id3v1_offset;
694 if (id3demux->upstream_size < ID3V1_TAG_SIZE)
696 id3v1_offset = id3demux->upstream_size - ID3V1_TAG_SIZE;
698 flow_ret = gst_pad_pull_range (id3demux->sinkpad, id3v1_offset,
699 ID3V1_TAG_SIZE, &buffer);
700 if (flow_ret != GST_FLOW_OK) {
701 GST_DEBUG_OBJECT (id3demux,
702 "Could not read data from start of file ret=%d", flow_ret);
706 if (GST_BUFFER_SIZE (buffer) != ID3V1_TAG_SIZE) {
707 GST_DEBUG_OBJECT (id3demux,
708 "Only managed to read %u bytes from file - not an ID3 file",
709 GST_BUFFER_SIZE (buffer));
713 tag_res = id3demux_read_id3v1_tag (buffer, &id3demux->strip_end, tags);
714 if (tag_res == ID3TAGS_READ_TAG) {
715 GST_DEBUG_OBJECT (id3demux,
716 "Read ID3v1 tag - trimming %d bytes from end of file",
717 id3demux->strip_end);
719 } else if (tag_res == ID3TAGS_BROKEN_TAG) {
720 GST_WARNING_OBJECT (id3demux, "Ignoring broken ID3v1 tag");
725 gst_buffer_unref (buffer);
729 /* Read and interpret any ID3v2 tag when activating in pull_range */
731 gst_id3demux_read_id3v2 (GstID3Demux * id3demux, GstTagList ** tags)
733 GstBuffer *buffer = NULL;
734 gboolean res = FALSE;
735 ID3TagsResult tag_res;
736 GstFlowReturn flow_ret;
738 /* Handle ID3V2 tag. Try with 4kB to start with */
739 flow_ret = gst_pad_pull_range (id3demux->sinkpad, 0, 4096, &buffer);
740 if (flow_ret != GST_FLOW_OK) {
741 GST_DEBUG_OBJECT (id3demux,
742 "Could not read data from start of file ret=%d", flow_ret);
746 if (GST_BUFFER_SIZE (buffer) < ID3V2_HDR_SIZE) {
747 GST_DEBUG_OBJECT (id3demux,
748 "Only managed to read %u bytes from file - not an ID3 file",
749 GST_BUFFER_SIZE (buffer));
753 tag_res = id3demux_read_id3v2_tag (buffer, &id3demux->strip_start, tags);
754 if (tag_res == ID3TAGS_MORE_DATA) {
755 /* Need more data to interpret the tag */
757 gst_buffer_unref (buffer);
760 g_assert (id3demux->strip_start > ID3V2_HDR_SIZE);
762 GST_DEBUG_OBJECT (id3demux, "Reading %u bytes to decode ID3v2",
763 id3demux->strip_start);
764 flow_ret = gst_pad_pull_range (id3demux->sinkpad, 0, id3demux->strip_start,
766 if (flow_ret != GST_FLOW_OK) {
767 GST_DEBUG_OBJECT (id3demux,
768 "Could not read data from start of file ret=%d", flow_ret);
771 tag_res = id3demux_read_id3v2_tag (buffer, &id3demux->strip_start, tags);
774 if (tag_res == ID3TAGS_READ_TAG) {
776 GST_DEBUG_OBJECT (id3demux, "Read ID3v2 tag of size %d",
777 id3demux->strip_start);
778 } else if (tag_res == ID3TAGS_BROKEN_TAG) {
780 GST_WARNING_OBJECT (id3demux, "Ignoring broken ID3v2 tag of size %d",
781 id3demux->strip_start);
785 gst_buffer_unref (buffer);
789 /* This function operates similarly to gst_type_find_element_activate
790 * in the typefind element
791 * 1. try to activate in pull mode. if not, switch to push and succeed.
792 * 2. try to read tags in pull mode
793 * 3. typefind the contents
794 * 4. deactivate pull mode.
795 * 5. if we didn't find any caps, fail.
797 * 7. if the sink pad is activated, we are in pull mode. succeed.
798 * otherwise activate both pads in push mode and succeed.
801 gst_id3demux_sink_activate (GstPad * sinkpad)
803 GstTypeFindProbability probability = 0;
804 GstID3Demux *id3demux = GST_ID3DEMUX (GST_PAD_PARENT (sinkpad));
805 gboolean ret = FALSE;
806 GstCaps *caps = NULL;
809 /* If we can activate pull_range upstream, then read any ID3v1 and ID3v2
810 * tags, otherwise activate in push mode and the chain function will
811 * collect buffers, read the ID3v2 tag and output a buffer to end
814 if (!gst_pad_check_pull_range (sinkpad) ||
815 !gst_pad_activate_pull (sinkpad, TRUE)) {
816 GST_DEBUG_OBJECT (id3demux,
817 "No pull mode. Changing to push, but won't be able to read ID3v1 tags");
818 id3demux->state = GST_ID3DEMUX_READID3V2;
819 return gst_pad_activate_push (sinkpad, TRUE);
822 /* Look for tags at start and end of file */
823 GST_DEBUG_OBJECT (id3demux, "Activated pull mode. Looking for tags");
824 if (!id3demux_get_upstream_size (id3demux))
827 id3demux->strip_start = 0;
828 id3demux->strip_end = 0;
831 if (id3demux->prefer_v1) {
832 if (!gst_id3demux_read_id3v2 (id3demux, &(id3demux->parsed_tags)))
834 if (!gst_id3demux_read_id3v1 (id3demux, &(id3demux->parsed_tags)))
837 if (!gst_id3demux_read_id3v1 (id3demux, &(id3demux->parsed_tags)))
839 if (!gst_id3demux_read_id3v2 (id3demux, &(id3demux->parsed_tags)))
842 if (id3demux->parsed_tags != NULL) {
843 id3demux->send_tag_event = TRUE;
846 /* 3 - Do typefinding on data */
847 caps = gst_type_find_helper_get_range (GST_OBJECT (id3demux),
848 (GstTypeFindHelperGetRangeFunction) gst_id3demux_read_range,
849 id3demux->upstream_size - id3demux->strip_start - id3demux->strip_end,
852 GST_DEBUG_OBJECT (id3demux, "Found type %" GST_PTR_FORMAT " with a "
853 "probability of %u", caps, probability);
855 /* 4 - Deactivate pull mode */
856 if (!gst_pad_activate_pull (sinkpad, FALSE)) {
858 gst_caps_unref (caps);
859 GST_DEBUG_OBJECT (id3demux,
860 "Could not deactivate sinkpad after reading tags");
864 /* 5 - If we didn't find the caps, fail */
866 GST_ELEMENT_ERROR (id3demux, STREAM, TYPE_NOT_FOUND, (NULL),
867 ("Could not detect type for contents within an ID3 tag"));
871 /* Now that we've finished typefinding, post tag message on bus */
872 if (id3demux->parsed_tags != NULL) {
873 gst_element_post_message (GST_ELEMENT (id3demux),
874 gst_message_new_tag (GST_OBJECT (id3demux),
875 gst_tag_list_copy (id3demux->parsed_tags)));
878 /* tag reading and typefinding were already done, don't do them again in
879 the chain function if we end up in push mode */
880 id3demux->state = GST_ID3DEMUX_STREAMING;
882 /* 6 Add the srcpad for output now we know caps. */
883 /* add_srcpad takes ownership of the caps */
884 if (!gst_id3demux_add_srcpad (id3demux, caps)) {
885 GST_DEBUG_OBJECT (id3demux, "Could not add source pad");
889 /* 7 - if the sinkpad is active, it was done by downstream so we're
890 * done, otherwise switch to push */
892 if (!gst_pad_is_active (sinkpad)) {
893 ret = gst_pad_activate_push (id3demux->srcpad, TRUE);
894 ret &= gst_pad_activate_push (sinkpad, TRUE);
903 gst_id3demux_src_activate_pull (GstPad * pad, gboolean active)
905 GstID3Demux *id3demux = GST_ID3DEMUX (GST_PAD_PARENT (pad));
907 return gst_pad_activate_pull (id3demux->sinkpad, active);
911 gst_id3demux_src_checkgetrange (GstPad * srcpad)
913 GstID3Demux *id3demux = GST_ID3DEMUX (GST_PAD_PARENT (srcpad));
915 return gst_pad_check_pull_range (id3demux->sinkpad);
919 gst_id3demux_read_range (GstID3Demux * id3demux,
920 guint64 offset, guint length, GstBuffer ** buffer)
926 g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
928 /* Adjust offset and length of the request to trim off ID3 information.
929 * For the returned buffer, adjust the output offset to match what downstream
931 in_offset = offset + id3demux->strip_start;
933 if (!id3demux_get_upstream_size (id3demux))
934 return GST_FLOW_ERROR;
936 if (in_offset + length >= id3demux->upstream_size - id3demux->strip_end)
937 in_length = id3demux->upstream_size - id3demux->strip_end - in_offset;
941 ret = gst_pad_pull_range (id3demux->sinkpad, in_offset, in_length, buffer);
943 if (ret == GST_FLOW_OK && *buffer) {
944 if (!gst_id3demux_trim_buffer (id3demux, buffer))
950 if (*buffer != NULL) {
951 gst_buffer_unref (buffer);
954 return GST_FLOW_ERROR;
958 gst_id3demux_src_getrange (GstPad * srcpad,
959 guint64 offset, guint length, GstBuffer ** buffer)
961 GstID3Demux *id3demux = GST_ID3DEMUX (GST_PAD_PARENT (srcpad));
963 if (id3demux->send_tag_event) {
964 gst_id3demux_send_tag_event (id3demux);
965 id3demux->send_tag_event = FALSE;
967 return gst_id3demux_read_range (id3demux, offset, length, buffer);
970 static GstStateChangeReturn
971 gst_id3demux_change_state (GstElement * element, GstStateChange transition)
973 GstStateChangeReturn ret;
974 GstID3Demux *id3demux = GST_ID3DEMUX (element);
976 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
978 switch (transition) {
979 case GST_STATE_CHANGE_PAUSED_TO_READY:
980 gst_id3demux_reset (id3demux);
989 gst_id3demux_pad_query (GstPad * pad, GstQuery * query)
991 /* For a position or duration query, adjust the returned
992 * bytes to strip off the id3v1 and id3v2 areas */
994 GstID3Demux *id3demux = GST_ID3DEMUX (GST_PAD_PARENT (pad));
999 if ((peer = gst_pad_get_peer (id3demux->sinkpad)) == NULL)
1002 if (!gst_pad_query (peer, query)) {
1003 gst_object_unref (peer);
1007 gst_object_unref (peer);
1009 switch (GST_QUERY_TYPE (query)) {
1010 case GST_QUERY_POSITION:
1012 gst_query_parse_position (query, &format, &result);
1013 if (format == GST_FORMAT_BYTES) {
1014 result -= id3demux->strip_start;
1015 gst_query_set_position (query, format, result);
1019 case GST_QUERY_DURATION:
1021 gst_query_parse_duration (query, &format, &result);
1022 if (format == GST_FORMAT_BYTES) {
1023 result -= id3demux->strip_start + id3demux->strip_end;
1024 gst_query_set_duration (query, format, result);
1035 static const GstQueryType *
1036 gst_id3demux_get_query_types (GstPad * pad)
1038 static const GstQueryType types[] = {
1048 gst_id3demux_send_tag_event (GstID3Demux * id3demux)
1050 /* FIXME: what's the correct merge mode? Docs need to tell... */
1051 GstTagList *merged = gst_tag_list_merge (id3demux->event_tags,
1052 id3demux->parsed_tags, GST_TAG_MERGE_KEEP);
1055 GstEvent *event = gst_event_new_tag (merged);
1057 GST_EVENT_TIMESTAMP (event) = 0;
1058 GST_DEBUG_OBJECT (id3demux, "Sending tag event on src pad");
1059 gst_pad_push_event (id3demux->srcpad, event);
1064 plugin_init (GstPlugin * plugin)
1066 GST_DEBUG_CATEGORY_INIT (id3demux_debug, "id3demux", 0,
1067 "GStreamer ID3 tag demuxer");
1069 gst_tag_register_musicbrainz_tags ();
1071 return gst_element_register (plugin, "id3demux",
1072 GST_RANK_PRIMARY, GST_TYPE_ID3DEMUX);
1075 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1078 "Demux ID3v1 and ID3v2 tags from a file",
1079 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)