gst/: Use gst_pad_query_peer_duration() utility function here.
[platform/upstream/gstreamer.git] / gst / id3demux / gstid3demux.c
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>
4  *
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.
9  *
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.
14  *
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.
19  */
20
21 /**
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.
24  *
25  * <refsect2>
26  * <para>
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. 
28  * </para><para>
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.
30  * </para>
31  * <title>Example launch line</title>
32  * <para>
33  * <programlisting>
34  * gst-launch filesrc location=file.mp3 ! id3demux ! fakesink -t
35  * </programlisting>
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.
37  * </para><para>
38  * This id3demux element replaced an older element with the same name which relied on libid3tag from the MAD project.
39  * </para>
40  * </refsect2>
41  */
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45 #include <gst/gst.h>
46 #include <gst/base/gsttypefindhelper.h>
47 #include <gst/gst-i18n-plugin.h>
48 #include <gst/tag/tag.h>
49
50 #include "gstid3demux.h"
51 #include "id3tags.h"
52
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>");
58
59 enum
60 {
61   ARG_0,
62   ARG_PREFER_V1
63 };
64
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
71
72 GST_DEBUG_CATEGORY (id3demux_debug);
73 #define GST_CAT_DEFAULT (id3demux_debug)
74
75 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
76     GST_PAD_SINK,
77     GST_PAD_ALWAYS,
78     GST_STATIC_CAPS ("application/x-id3")
79     );
80
81 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
82     GST_PAD_SRC,
83     GST_PAD_SOMETIMES,
84     GST_STATIC_CAPS ("ANY")
85     );
86
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);
91
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);
96
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);
102
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);
106
107 static gboolean gst_id3demux_add_srcpad (GstID3Demux * id3demux,
108     GstCaps * new_caps);
109 static gboolean gst_id3demux_remove_srcpad (GstID3Demux * id3demux);
110
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);
119
120 static GstElementClass *parent_class = NULL;
121
122 GType
123 gst_id3demux_get_type (void)
124 {
125   static GType plugin_type = 0;
126
127   if (!plugin_type) {
128     static const GTypeInfo plugin_info = {
129       sizeof (GstID3DemuxClass),
130       (GBaseInitFunc) gst_id3demux_base_init,
131       NULL,
132       (GClassInitFunc) gst_id3demux_class_init,
133       NULL,
134       NULL,
135       sizeof (GstID3Demux),
136       0,
137       (GInstanceInitFunc) gst_id3demux_init,
138     };
139     plugin_type = g_type_register_static (GST_TYPE_ELEMENT,
140         "GstID3Demux", &plugin_info, 0);
141   }
142   return plugin_type;
143 }
144
145 static void
146 gst_id3demux_base_init (GstID3DemuxClass * klass)
147 {
148   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
149
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);
155 }
156
157 static void
158 gst_id3demux_class_init (GstID3DemuxClass * klass)
159 {
160   GObjectClass *gobject_class;
161   GstElementClass *gstelement_class;
162
163   gobject_class = (GObjectClass *) klass;
164   gstelement_class = (GstElementClass *) klass;
165
166   parent_class = g_type_class_peek_parent (klass);
167
168   gobject_class->dispose = gst_id3demux_dispose;
169
170   gobject_class->set_property = gst_id3demux_set_property;
171   gobject_class->get_property = gst_id3demux_get_property;
172
173   gstelement_class->change_state = gst_id3demux_change_state;
174
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));
180 }
181
182 static void
183 gst_id3demux_reset (GstID3Demux * id3demux)
184 {
185   GstBuffer **buffer_p = &id3demux->collect;
186   GstCaps **caps_p = &id3demux->src_caps;
187
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;
193
194   gst_buffer_replace (buffer_p, NULL);
195   gst_caps_replace (caps_p, NULL);
196
197   gst_id3demux_remove_srcpad (id3demux);
198
199   if (id3demux->event_tags) {
200     gst_tag_list_free (id3demux->event_tags);
201     id3demux->event_tags = NULL;
202   }
203   if (id3demux->parsed_tags) {
204     gst_tag_list_free (id3demux->parsed_tags);
205     id3demux->parsed_tags = NULL;
206   }
207
208 }
209
210 static void
211 gst_id3demux_init (GstID3Demux * id3demux)
212 {
213   GstElementClass *klass = GST_ELEMENT_GET_CLASS (id3demux);
214
215   id3demux->sinkpad =
216       gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
217           "sink"), "sink");
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);
225
226   id3demux->prefer_v1 = FALSE;
227   gst_id3demux_reset (id3demux);
228 }
229
230 static void
231 gst_id3demux_dispose (GObject * object)
232 {
233   GstID3Demux *id3demux = GST_ID3DEMUX (object);
234
235   gst_id3demux_reset (id3demux);
236
237   G_OBJECT_CLASS (parent_class)->dispose (object);
238 }
239
240 static gboolean
241 gst_id3demux_add_srcpad (GstID3Demux * id3demux, GstCaps * new_caps)
242 {
243   if (id3demux->src_caps == NULL ||
244       !gst_caps_is_equal (new_caps, id3demux->src_caps)) {
245
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,
249           id3demux->src_caps);
250
251       gst_pad_set_caps (id3demux->srcpad, id3demux->src_caps);
252     }
253   } else {
254     /* Caps never changed */
255     gst_caps_unref (new_caps);
256   }
257
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);
261
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));
274
275     gst_pad_use_fixed_caps (id3demux->srcpad);
276
277     if (id3demux->src_caps)
278       gst_pad_set_caps (id3demux->srcpad, id3demux->src_caps);
279
280     GST_DEBUG_OBJECT (id3demux, "Adding src pad with caps %" GST_PTR_FORMAT,
281         id3demux->src_caps);
282
283     gst_object_ref (id3demux->srcpad);
284     if (!(gst_element_add_pad (GST_ELEMENT (id3demux), id3demux->srcpad)))
285       return FALSE;
286     gst_element_no_more_pads (GST_ELEMENT (id3demux));
287   }
288
289   return TRUE;
290 }
291
292 static gboolean
293 gst_id3demux_remove_srcpad (GstID3Demux * id3demux)
294 {
295   gboolean res = TRUE;
296
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;
303   }
304
305   return res;
306 };
307
308 static gboolean
309 gst_id3demux_trim_buffer (GstID3Demux * id3demux, GstBuffer ** buf_ref)
310 {
311   GstBuffer *buf = *buf_ref;
312
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;
317
318   /* Adjust offset and length */
319   if (!GST_BUFFER_OFFSET_IS_VALID (buf)) {
320     /* Can't change anything without an offset */
321     return TRUE;
322   }
323
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;
328
329       if (out_offset >= v1tag_offset) {
330         GST_DEBUG_OBJECT (id3demux, "Buffer is past the end of the data");
331         goto no_out_buffer;
332       }
333
334       if (out_offset + out_size > v1tag_offset) {
335         out_size = v1tag_offset - out_offset;
336         need_sub = TRUE;
337       }
338     }
339   }
340
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");
346         goto no_out_buffer;
347       }
348
349       trim_start = id3demux->strip_start - out_offset;
350       out_size -= trim_start;
351       out_offset = 0;
352     } else {
353       out_offset -= id3demux->strip_start;
354     }
355     need_sub = TRUE;
356   }
357
358   g_assert (out_size > 0);
359
360   if (need_sub == TRUE) {
361     if (out_size != GST_BUFFER_SIZE (buf) || !gst_buffer_is_writable (buf)) {
362       GstBuffer *sub;
363
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);
367
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;
372     } else {
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);
376     }
377
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);
381   }
382
383   return TRUE;
384 no_out_buffer:
385   gst_buffer_unref (buf);
386   *buf_ref = NULL;
387   return TRUE;
388 }
389
390 static GstFlowReturn
391 gst_id3demux_chain (GstPad * pad, GstBuffer * buf)
392 {
393   GstID3Demux *id3demux;
394
395   id3demux = GST_ID3DEMUX (GST_PAD_PARENT (pad));
396   g_return_val_if_fail (GST_IS_ID3DEMUX (id3demux), GST_FLOW_ERROR);
397
398   if (id3demux->collect == NULL) {
399     id3demux->collect = buf;
400   } else {
401     id3demux->collect = gst_buffer_join (id3demux->collect, buf);
402   }
403   buf = NULL;
404
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");
412       } else {
413         ID3TagsResult tag_result;
414
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);
422         else
423           GST_DEBUG_OBJECT (id3demux, "Found an ID3v2 tag of size %d\n",
424               id3demux->strip_start);
425
426         id3demux->send_tag_event = TRUE;
427       }
428       id3demux->state = GST_ID3DEMUX_TYPEFINDING;
429
430       /* Fall-through */
431     case GST_ID3DEMUX_TYPEFINDING:{
432       GstTypeFindProbability probability = 0;
433       GstBuffer *typefind_buf = NULL;
434       GstCaps *caps;
435
436       if (GST_BUFFER_SIZE (id3demux->collect) <
437           ID3_TYPE_FIND_MIN_SIZE + id3demux->strip_start)
438         break;                  /* Go get more data first */
439
440       GST_DEBUG_OBJECT (id3demux, "Typefinding with size %d",
441           GST_BUFFER_SIZE (id3demux->collect));
442
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;
448
449       if (typefind_buf == NULL)
450         break;                  /* Still need more data */
451
452       caps = gst_type_find_helper_for_buffer (GST_OBJECT (id3demux),
453           typefind_buf, &probability);
454
455       if (caps == NULL) {
456         if (GST_BUFFER_SIZE (typefind_buf) < ID3_TYPE_FIND_MAX_SIZE) {
457           /* Just break for more data */
458           gst_buffer_unref (typefind_buf);
459           return GST_FLOW_OK;
460         }
461
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;
469       }
470
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));
474
475       gst_buffer_unref (typefind_buf);
476
477       if (!gst_id3demux_add_srcpad (id3demux, caps)) {
478         GST_DEBUG_OBJECT (id3demux, "Failed to add srcpad");
479         gst_caps_unref (caps);
480         goto error;
481       }
482       gst_caps_unref (caps);
483
484       /* Move onto streaming and fall-through to push out existing
485        * data */
486       id3demux->state = GST_ID3DEMUX_STREAMING;
487
488       /* Now that typefinding is complete, post the
489        * tags message */
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)));
494       }
495       /* fall-through */
496     }
497     case GST_ID3DEMUX_STREAMING:{
498       GstBuffer *outbuf = NULL;
499
500       if (id3demux->send_tag_event) {
501         gst_id3demux_send_tag_event (id3demux);
502         id3demux->send_tag_event = FALSE;
503       }
504
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;
511       }
512       if (outbuf) {
513         if (G_UNLIKELY (id3demux->srcpad == NULL)) {
514           gst_buffer_unref (outbuf);
515           return GST_FLOW_ERROR;
516         }
517
518         GST_DEBUG_OBJECT (id3demux, "Pushing buffer %p", outbuf);
519
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));
523
524         return gst_pad_push (id3demux->srcpad, outbuf);
525       }
526     }
527   }
528   return GST_FLOW_OK;
529
530 error:
531   GST_DEBUG_OBJECT (id3demux, "error in chain function");
532
533   return GST_FLOW_ERROR;
534 }
535
536 static gboolean
537 gst_id3demux_sink_event (GstPad * pad, GstEvent * event)
538 {
539   GstID3Demux *demux;
540   gboolean ret;
541
542   demux = GST_ID3DEMUX (gst_pad_get_parent (pad));
543
544   switch (GST_EVENT_TYPE (event)) {
545     case GST_EVENT_EOS:
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));
549       }
550       ret = gst_pad_event_default (pad, event);
551       break;
552     default:
553       ret = gst_pad_event_default (pad, event);
554       break;
555   }
556
557   gst_object_unref (demux);
558   return ret;
559 }
560
561
562 static void
563 gst_id3demux_set_property (GObject * object, guint prop_id,
564     const GValue * value, GParamSpec * pspec)
565 {
566   GstID3Demux *id3demux;
567
568   g_return_if_fail (GST_IS_ID3DEMUX (object));
569   id3demux = GST_ID3DEMUX (object);
570
571   switch (prop_id) {
572     case ARG_PREFER_V1:
573       id3demux->prefer_v1 = g_value_get_boolean (value);
574       break;
575     default:
576       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
577       break;
578   }
579 }
580
581 static void
582 gst_id3demux_get_property (GObject * object, guint prop_id,
583     GValue * value, GParamSpec * pspec)
584 {
585   GstID3Demux *id3demux;
586
587   g_return_if_fail (GST_IS_ID3DEMUX (object));
588   id3demux = GST_ID3DEMUX (object);
589
590   switch (prop_id) {
591     case ARG_PREFER_V1:
592       g_value_set_boolean (value, id3demux->prefer_v1);
593       break;
594     default:
595       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
596       break;
597   }
598 }
599
600 static gboolean
601 id3demux_get_upstream_size (GstID3Demux * id3demux)
602 {
603   GstFormat format;
604   gint64 result;
605
606   /* Short-cut if we already queried upstream */
607   if (id3demux->upstream_size > 0)
608     return TRUE;
609
610   format = GST_FORMAT_BYTES;
611   if (!gst_pad_query_peer_duration (id3demux->sinkpad, &format, &result) ||
612       result < 0) {
613     return FALSE;
614   }
615
616   id3demux->upstream_size = result;
617   return TRUE;
618 }
619
620 static gboolean
621 gst_id3demux_srcpad_event (GstPad * pad, GstEvent * event)
622 {
623   gboolean res = FALSE;
624   GstID3Demux *id3demux = GST_ID3DEMUX (GST_PAD_PARENT (pad));
625
626   /* Handle SEEK events, with adjusted byte offsets and sizes. */
627
628   switch (GST_EVENT_TYPE (event)) {
629     case GST_EVENT_SEEK:
630     {
631       gdouble rate;
632       GstFormat format;
633       GstSeekType cur_type, stop_type;
634       GstSeekFlags flags;
635       gint64 cur, stop;
636
637       gst_event_parse_seek (event, &rate, &format, &flags,
638           &cur_type, &cur, &stop_type, &stop);
639
640       if (format == GST_FORMAT_BYTES &&
641           id3demux->state == GST_ID3DEMUX_STREAMING &&
642           gst_pad_is_linked (id3demux->sinkpad)) {
643         GstEvent *upstream;
644
645         switch (cur_type) {
646           case GST_SEEK_TYPE_SET:
647             cur += id3demux->strip_start;
648             break;
649           case GST_SEEK_TYPE_CUR:
650             break;
651           case GST_SEEK_TYPE_END:
652             cur += id3demux->strip_end;
653             break;
654           default:
655             g_assert_not_reached ();
656             break;
657         }
658         switch (stop_type) {
659           case GST_SEEK_TYPE_SET:
660             stop += id3demux->strip_start;
661             break;
662           case GST_SEEK_TYPE_CUR:
663             break;
664           case GST_SEEK_TYPE_END:
665             stop += id3demux->strip_end;
666             break;
667           default:
668             break;
669         }
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);
673       }
674       break;
675     }
676     default:
677       break;
678   }
679
680   gst_event_unref (event);
681   return res;
682 }
683
684 /* Read and interpret any ID3v1 tag when activating in pull_range */
685 static gboolean
686 gst_id3demux_read_id3v1 (GstID3Demux * id3demux, GstTagList ** tags)
687 {
688   GstBuffer *buffer = NULL;
689   gboolean res = FALSE;
690   ID3TagsResult tag_res;
691   GstFlowReturn flow_ret;
692   guint64 id3v1_offset;
693
694   if (id3demux->upstream_size < ID3V1_TAG_SIZE)
695     return TRUE;
696   id3v1_offset = id3demux->upstream_size - ID3V1_TAG_SIZE;
697
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);
703     goto beach;
704   }
705
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));
710     goto beach;
711   }
712
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);
718     res = TRUE;
719   } else if (tag_res == ID3TAGS_BROKEN_TAG) {
720     GST_WARNING_OBJECT (id3demux, "Ignoring broken ID3v1 tag");
721     res = TRUE;
722   }
723 beach:
724   if (buffer)
725     gst_buffer_unref (buffer);
726   return res;
727 }
728
729 /* Read and interpret any ID3v2 tag when activating in pull_range */
730 static gboolean
731 gst_id3demux_read_id3v2 (GstID3Demux * id3demux, GstTagList ** tags)
732 {
733   GstBuffer *buffer = NULL;
734   gboolean res = FALSE;
735   ID3TagsResult tag_res;
736   GstFlowReturn flow_ret;
737
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);
743     goto beach;
744   }
745
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));
750     goto beach;
751   }
752
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 */
756     if (buffer) {
757       gst_buffer_unref (buffer);
758       buffer = NULL;
759     }
760     g_assert (id3demux->strip_start > ID3V2_HDR_SIZE);
761
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,
765         &buffer);
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);
769       goto beach;
770     }
771     tag_res = id3demux_read_id3v2_tag (buffer, &id3demux->strip_start, tags);
772   }
773
774   if (tag_res == ID3TAGS_READ_TAG) {
775     res = TRUE;
776     GST_DEBUG_OBJECT (id3demux, "Read ID3v2 tag of size %d",
777         id3demux->strip_start);
778   } else if (tag_res == ID3TAGS_BROKEN_TAG) {
779     res = TRUE;
780     GST_WARNING_OBJECT (id3demux, "Ignoring broken ID3v2 tag of size %d",
781         id3demux->strip_start);
782   }
783 beach:
784   if (buffer)
785     gst_buffer_unref (buffer);
786   return res;
787 }
788
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.
796  * 6. Add the srcpad
797  * 7. if the sink pad is activated, we are in pull mode. succeed.
798  *    otherwise activate both pads in push mode and succeed.
799  */
800 static gboolean
801 gst_id3demux_sink_activate (GstPad * sinkpad)
802 {
803   GstTypeFindProbability probability = 0;
804   GstID3Demux *id3demux = GST_ID3DEMUX (GST_PAD_PARENT (sinkpad));
805   gboolean ret = FALSE;
806   GstCaps *caps = NULL;
807
808   /* 1: */
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
812    * preroll.
813    */
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);
820   }
821
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))
825     return FALSE;
826
827   id3demux->strip_start = 0;
828   id3demux->strip_end = 0;
829
830
831   if (id3demux->prefer_v1) {
832     if (!gst_id3demux_read_id3v2 (id3demux, &(id3demux->parsed_tags)))
833       return FALSE;
834     if (!gst_id3demux_read_id3v1 (id3demux, &(id3demux->parsed_tags)))
835       return FALSE;
836   } else {
837     if (!gst_id3demux_read_id3v1 (id3demux, &(id3demux->parsed_tags)))
838       return FALSE;
839     if (!gst_id3demux_read_id3v2 (id3demux, &(id3demux->parsed_tags)))
840       return FALSE;
841   }
842   if (id3demux->parsed_tags != NULL) {
843     id3demux->send_tag_event = TRUE;
844   }
845
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,
850       &probability);
851
852   GST_DEBUG_OBJECT (id3demux, "Found type %" GST_PTR_FORMAT " with a "
853       "probability of %u", caps, probability);
854
855   /* 4 - Deactivate pull mode */
856   if (!gst_pad_activate_pull (sinkpad, FALSE)) {
857     if (caps)
858       gst_caps_unref (caps);
859     GST_DEBUG_OBJECT (id3demux,
860         "Could not deactivate sinkpad after reading tags");
861     return FALSE;
862   }
863
864   /* 5 - If we didn't find the caps, fail */
865   if (caps == NULL) {
866     GST_ELEMENT_ERROR (id3demux, STREAM, TYPE_NOT_FOUND, (NULL),
867         ("Could not detect type for contents within an ID3 tag"));
868     goto done_activate;
869   }
870
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)));
876   }
877
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;
881
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");
886     goto done_activate;
887   }
888
889   /* 7 - if the sinkpad is active, it was done by downstream so we're 
890    * done, otherwise switch to push */
891   ret = TRUE;
892   if (!gst_pad_is_active (sinkpad)) {
893     ret = gst_pad_activate_push (id3demux->srcpad, TRUE);
894     ret &= gst_pad_activate_push (sinkpad, TRUE);
895   }
896
897 done_activate:
898
899   return ret;
900 }
901
902 static gboolean
903 gst_id3demux_src_activate_pull (GstPad * pad, gboolean active)
904 {
905   GstID3Demux *id3demux = GST_ID3DEMUX (GST_PAD_PARENT (pad));
906
907   return gst_pad_activate_pull (id3demux->sinkpad, active);
908 }
909
910 static gboolean
911 gst_id3demux_src_checkgetrange (GstPad * srcpad)
912 {
913   GstID3Demux *id3demux = GST_ID3DEMUX (GST_PAD_PARENT (srcpad));
914
915   return gst_pad_check_pull_range (id3demux->sinkpad);
916 }
917
918 static GstFlowReturn
919 gst_id3demux_read_range (GstID3Demux * id3demux,
920     guint64 offset, guint length, GstBuffer ** buffer)
921 {
922   GstFlowReturn ret;
923   guint64 in_offset;
924   guint in_length;
925
926   g_return_val_if_fail (buffer != NULL, GST_FLOW_ERROR);
927
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
930    * should see */
931   in_offset = offset + id3demux->strip_start;
932
933   if (!id3demux_get_upstream_size (id3demux))
934     return GST_FLOW_ERROR;
935
936   if (in_offset + length >= id3demux->upstream_size - id3demux->strip_end)
937     in_length = id3demux->upstream_size - id3demux->strip_end - in_offset;
938   else
939     in_length = length;
940
941   ret = gst_pad_pull_range (id3demux->sinkpad, in_offset, in_length, buffer);
942
943   if (ret == GST_FLOW_OK && *buffer) {
944     if (!gst_id3demux_trim_buffer (id3demux, buffer))
945       goto error;
946   }
947
948   return ret;
949 error:
950   if (*buffer != NULL) {
951     gst_buffer_unref (buffer);
952     *buffer = NULL;
953   }
954   return GST_FLOW_ERROR;
955 }
956
957 static GstFlowReturn
958 gst_id3demux_src_getrange (GstPad * srcpad,
959     guint64 offset, guint length, GstBuffer ** buffer)
960 {
961   GstID3Demux *id3demux = GST_ID3DEMUX (GST_PAD_PARENT (srcpad));
962
963   if (id3demux->send_tag_event) {
964     gst_id3demux_send_tag_event (id3demux);
965     id3demux->send_tag_event = FALSE;
966   }
967   return gst_id3demux_read_range (id3demux, offset, length, buffer);
968 }
969
970 static GstStateChangeReturn
971 gst_id3demux_change_state (GstElement * element, GstStateChange transition)
972 {
973   GstStateChangeReturn ret;
974   GstID3Demux *id3demux = GST_ID3DEMUX (element);
975
976   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
977
978   switch (transition) {
979     case GST_STATE_CHANGE_PAUSED_TO_READY:
980       gst_id3demux_reset (id3demux);
981       break;
982     default:
983       break;
984   }
985   return ret;
986 }
987
988 static gboolean
989 gst_id3demux_pad_query (GstPad * pad, GstQuery * query)
990 {
991   /* For a position or duration query, adjust the returned
992    * bytes to strip off the id3v1 and id3v2 areas */
993
994   GstID3Demux *id3demux = GST_ID3DEMUX (GST_PAD_PARENT (pad));
995   GstPad *peer = NULL;
996   GstFormat format;
997   gint64 result;
998
999   if ((peer = gst_pad_get_peer (id3demux->sinkpad)) == NULL)
1000     return FALSE;
1001
1002   if (!gst_pad_query (peer, query)) {
1003     gst_object_unref (peer);
1004     return FALSE;
1005   }
1006
1007   gst_object_unref (peer);
1008
1009   switch (GST_QUERY_TYPE (query)) {
1010     case GST_QUERY_POSITION:
1011     {
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);
1016       }
1017       break;
1018     }
1019     case GST_QUERY_DURATION:
1020     {
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);
1025       }
1026       break;
1027     }
1028     default:
1029       break;
1030   }
1031
1032   return TRUE;
1033 }
1034
1035 static const GstQueryType *
1036 gst_id3demux_get_query_types (GstPad * pad)
1037 {
1038   static const GstQueryType types[] = {
1039     GST_QUERY_POSITION,
1040     GST_QUERY_DURATION,
1041     0
1042   };
1043
1044   return types;
1045 }
1046
1047 static void
1048 gst_id3demux_send_tag_event (GstID3Demux * id3demux)
1049 {
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);
1053
1054   if (merged) {
1055     GstEvent *event = gst_event_new_tag (merged);
1056
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);
1060   }
1061 }
1062
1063 static gboolean
1064 plugin_init (GstPlugin * plugin)
1065 {
1066   GST_DEBUG_CATEGORY_INIT (id3demux_debug, "id3demux", 0,
1067       "GStreamer ID3 tag demuxer");
1068
1069   gst_tag_register_musicbrainz_tags ();
1070
1071   return gst_element_register (plugin, "id3demux",
1072       GST_RANK_PRIMARY, GST_TYPE_ID3DEMUX);
1073 }
1074
1075 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1076     GST_VERSION_MINOR,
1077     "id3demux",
1078     "Demux ID3v1 and ID3v2 tags from a file",
1079     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)