]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - glsdk/gst-plugins-ugly0-10.git/blob - ext/mad/gstid3tag.c
Update for gst_tag_setter API changes.
[glsdk/gst-plugins-ugly0-10.git] / ext / mad / gstid3tag.c
1 /* GStreamer
2  * Copyright (C) 2003-2004 Benjamin Otte <otte@gnome.org>
3  *
4  * gstid3tag.c: plugin for reading / modifying id3 tags
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 #include "gstmad.h"
26 #include <stdlib.h>
27 #include <string.h>
28 #include <gst/gsttagsetter.h>
30 #define ID3_TYPE_FIND_SIZE 40960
31 GST_DEBUG_CATEGORY_STATIC (gst_id3_tag_debug);
32 #define GST_CAT_DEFAULT gst_id3_tag_debug
34 #define GST_TYPE_ID3_TAG (gst_id3_tag_get_type(GST_ID3_TAG_PARSE_BASE ))
35 #define GST_ID3_TAG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_ID3_TAG, GstID3Tag))
36 #define GST_ID3_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_ID3_TAG, GstID3TagClass))
37 #define GST_IS_ID3_TAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_ID3_TAG))
38 #define GST_IS_ID3_TAG_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_ID3_TAG))
39 #define GST_ID3_TAG_GET_CLASS(obj)   (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_ID3_TAG, GstID3TagClass))
41 typedef struct _GstID3Tag GstID3Tag;
42 typedef struct _GstID3TagClass GstID3TagClass;
44 typedef enum
45 {
46   GST_ID3_TAG_STATE_READING_V2_TAG,
47   GST_ID3_TAG_STATE_SEEKING_TO_V1_TAG,
48   GST_ID3_TAG_STATE_READING_V1_TAG,
49   GST_ID3_TAG_STATE_SEEKING_TO_NORMAL,
50   GST_ID3_TAG_STATE_NORMAL_START,
51   GST_ID3_TAG_STATE_NORMAL
52 }
53 GstID3TagState;
55 typedef enum
56 {
57   GST_ID3_TAG_PARSE_BASE = 0,
58   GST_ID3_TAG_PARSE_DEMUX = 1,
59   GST_ID3_TAG_PARSE_MUX = 2,
60   GST_ID3_TAG_PARSE_ANY = 3
61 }
62 GstID3ParseMode;
64 #define IS_DEMUXER(tag) ((tag)->parse_mode & GST_ID3_TAG_PARSE_DEMUX)
65 #define IS_MUXER(tag) ((tag)->parse_mode & GST_ID3_TAG_PARSE_MUX)
66 #define CAN_BE_DEMUXER(tag) (GST_ID3_TAG_GET_CLASS(tag)->type & GST_ID3_TAG_PARSE_DEMUX)
67 #define CAN_BE_MUXER(tag) (GST_ID3_TAG_GET_CLASS(tag)->type & GST_ID3_TAG_PARSE_MUX)
69 struct _GstID3Tag
70 {
71   GstElement element;
73   /* pads */
74   GstPad *sinkpad;
75   GstPad *srcpad;
77   /* caps */
78   GstID3ParseMode parse_mode;
79   GstCaps *found_caps;
81   /* tags */
82   GstTagList *event_tags;
83   GstTagList *parsed_tags;
85   /* state */
86   GstID3TagState state;
88   GstEvent *segment;
89   GstBuffer *buffer;
90   gboolean prefer_v1tag;
91   glong v1tag_size;
92   glong v1tag_size_new;
93   guint64 v1tag_offset;
94   gboolean v1tag_render;
95   glong v2tag_size;
96   glong v2tag_size_new;
97   gboolean v2tag_render;
98 };
100 struct _GstID3TagClass
102   GstElementClass parent_class;
104   GstID3ParseMode type;
105 };
107 /* signals and args */
108 enum
110   /* FILL ME */
111   LAST_SIGNAL
112 };
114 enum
116   ARG_0,
117   ARG_V1_TAG,
118   ARG_V2_TAG,
119   ARG_PREFER_V1
120       /* FILL ME */
121 };
123 GST_DEBUG_CATEGORY_EXTERN (mad_debug);
125 static GstStaticPadTemplate id3_tag_src_any_template_factory =
126 GST_STATIC_PAD_TEMPLATE ("src",
127     GST_PAD_SRC,
128   /* FIXME: for spider - should be GST_PAD_ALWAYS, */
129     GST_PAD_SOMETIMES,
130     GST_STATIC_CAPS ("ANY")
131     );
133 static GstStaticPadTemplate id3_tag_src_id3_template_factory =
134 GST_STATIC_PAD_TEMPLATE ("src",
135     GST_PAD_SRC,
136     GST_PAD_ALWAYS,
137     GST_STATIC_CAPS ("application/x-id3")
138     );
140 static GstStaticPadTemplate id3_tag_sink_any_template_factory =
141     GST_STATIC_PAD_TEMPLATE ("sink",
142     GST_PAD_SINK,
143     GST_PAD_ALWAYS,
144     /* FIXME: find a way to extend this generically */
145     GST_STATIC_CAPS ("audio/mpeg, mpegversion=(int)1; audio/x-flac")
146     );
148 static GstStaticPadTemplate id3_tag_sink_id3_template_factory =
149 GST_STATIC_PAD_TEMPLATE ("sink",
150     GST_PAD_SINK,
151     GST_PAD_ALWAYS,
152     GST_STATIC_CAPS ("application/x-id3")
153     );
156 static void gst_id3_tag_class_init (gpointer g_class, gpointer class_data);
157 static void gst_id3_tag_init (GTypeInstance * instance, gpointer g_class);
158 static void gst_id3_tag_set_property (GObject * object,
159     guint prop_id, const GValue * value, GParamSpec * pspec);
160 static void gst_id3_tag_get_property (GObject * object,
161     guint prop_id, GValue * value, GParamSpec * pspec);
163 static gboolean gst_id3_tag_src_event (GstPad * pad, GstEvent * event);
164 static const GstQueryType *gst_id3_tag_get_query_types (GstPad * pad);
166 static gboolean gst_id3_tag_src_query (GstPad * pad, GstQuery * query);
168 static gboolean gst_id3_tag_sink_event (GstPad * pad, GstEvent * event);
169 static GstFlowReturn gst_id3_tag_chain (GstPad * pad, GstBuffer * buffer);
170 static GstPadLinkReturn gst_id3_tag_src_link (GstPad * pad, GstPad * peer);
172 static GstStateChangeReturn gst_id3_tag_change_state (GstElement * element,
173     GstStateChange transition);
175 static GstElementClass *parent_class = NULL;
177 /* static guint gst_id3_tag_signals[LAST_SIGNAL] = { 0 }; */
179 GType
180 gst_id3_tag_get_type (guint type)
182   static GType id3_tag_type[4] = { 0, 0, 0, 0 };
183   static gchar *name[4] = { "GstID3TagBase", "GstID3Demux", "GstID3Mux",
184     "GstID3Tag"
185   };
187   g_assert (type < 4);
189   if (!id3_tag_type[type]) {
190     GTypeInfo id3_tag_info = {
191       sizeof (GstID3TagClass),
192       NULL,
193       NULL,
194       gst_id3_tag_class_init,
195       NULL,
196       GUINT_TO_POINTER (type),
197       sizeof (GstID3Tag),
198       0,
199       gst_id3_tag_init,
200     };
201     static const GInterfaceInfo tag_setter_info = {
202       NULL,
203       NULL,
204       NULL
205     };
207     id3_tag_type[type] = g_type_register_static (
208         (type == GST_ID3_TAG_PARSE_BASE) ? GST_TYPE_ELEMENT :
209         GST_TYPE_ID3_TAG, name[type], &id3_tag_info, 0);
211     if (type & GST_ID3_TAG_PARSE_MUX) {
212       g_type_add_interface_static (id3_tag_type[type], GST_TYPE_TAG_SETTER,
213           &tag_setter_info);
214     }
215   }
216   return id3_tag_type[type];
219 /* elementfactory information */
220 GstElementDetails gst_id3_tag_details[3] = {
221   GST_ELEMENT_DETAILS ("id3 tag extractor",
222       "Codec/Demuxer/Audio",
223       "Extract ID3 tagging information",
224       "Benjamin Otte <otte@gnome.org>"),
225   GST_ELEMENT_DETAILS ("id3 muxer",
226       "Codec/Muxer/Audio",
227       "Add ID3 tagging information",
228       "Benjamin Otte <otte@gnome.org>"),
229   GST_ELEMENT_DETAILS ("id3 tag extractor",
230       "Tag",
231       "Extract tagging information from mp3s",
232       "Benjamin Otte <otte@gnome.org>")
233 };
235 static void
236 gst_id3_tag_class_init (gpointer g_class, gpointer class_data)
238   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
239   GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
240   GstID3TagClass *tag_class = GST_ID3_TAG_CLASS (g_class);
242   tag_class->type = GPOINTER_TO_UINT (class_data);
244   if (tag_class->type == GST_ID3_TAG_PARSE_BASE) {
245     parent_class = g_type_class_peek_parent (g_class);
246     gstelement_class->change_state = gst_id3_tag_change_state;
247   } else {
248     gst_element_class_set_details (gstelement_class,
249         &gst_id3_tag_details[tag_class->type - 1]);
250   }
252   gobject_class->set_property = GST_DEBUG_FUNCPTR (gst_id3_tag_set_property);
253   gobject_class->get_property = GST_DEBUG_FUNCPTR (gst_id3_tag_get_property);
255   if (tag_class->type & GST_ID3_TAG_PARSE_DEMUX) {
256     g_object_class_install_property (gobject_class, ARG_PREFER_V1,
257         g_param_spec_boolean ("prefer-v1", "prefer version 1 tag",
258             "Prefer tags from tag at end of file", FALSE,
259             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
260     gst_element_class_add_pad_template (gstelement_class,
261         gst_static_pad_template_get (&id3_tag_src_any_template_factory));
262   } else {
263     gst_element_class_add_pad_template (gstelement_class,
264         gst_static_pad_template_get (&id3_tag_src_id3_template_factory));
265   }
267   if (tag_class->type & GST_ID3_TAG_PARSE_MUX) {
268     g_object_class_install_property (gobject_class, ARG_V2_TAG,
269         g_param_spec_boolean ("v2-tag", "add version 2 tag",
270             "Add version 2 tag at start of file", TRUE,
271             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
272     g_object_class_install_property (gobject_class, ARG_V1_TAG,
273         g_param_spec_boolean ("v1-tag", "add version 1 tag",
274             "Add version 1 tag at end of file", FALSE,
275             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
276   }
277   if (tag_class->type == GST_ID3_TAG_PARSE_MUX) {
278     gst_element_class_add_pad_template (gstelement_class,
279         gst_static_pad_template_get (&id3_tag_sink_any_template_factory));
280   } else {
281     gst_element_class_add_pad_template (gstelement_class,
282         gst_static_pad_template_get (&id3_tag_sink_id3_template_factory));
283   }
286 static GstCaps *
287 gst_id3_tag_get_caps (GstPad * pad)
289   GstID3Tag *tag = GST_ID3_TAG (gst_pad_get_parent (pad));
291   if (tag->found_caps) {
292     GstCaps *caps = gst_caps_copy (tag->found_caps);
294     if (CAN_BE_MUXER (tag)) {
295       gst_caps_append (caps,
296           gst_caps_from_string ("application/x-gst-tags; application/x-id3"));
297     }
298     return caps;
299   } else {
300     return gst_caps_copy (gst_pad_get_pad_template_caps (pad));
301   }
304 static void
305 gst_id3_tag_add_src_pad (GstID3Tag * tag)
307   g_assert (tag->srcpad == NULL);
308   tag->srcpad =
309       gst_pad_new_from_template (gst_element_class_get_pad_template
310       (GST_ELEMENT_GET_CLASS (tag), "src"), "src");
311   gst_pad_set_event_function (tag->srcpad,
312       GST_DEBUG_FUNCPTR (gst_id3_tag_src_event));
313   gst_pad_set_query_function (tag->srcpad,
314       GST_DEBUG_FUNCPTR (gst_id3_tag_src_query));
315   gst_pad_set_query_type_function (tag->srcpad,
316       GST_DEBUG_FUNCPTR (gst_id3_tag_get_query_types));
317   gst_pad_set_getcaps_function (tag->srcpad,
318       GST_DEBUG_FUNCPTR (gst_id3_tag_get_caps));
319   gst_pad_set_link_function (tag->srcpad,
320       GST_DEBUG_FUNCPTR (gst_id3_tag_src_link));
321   gst_element_add_pad (GST_ELEMENT (tag), tag->srcpad);
324 static void
325 gst_id3_tag_init (GTypeInstance * instance, gpointer g_class)
327   GstID3Tag *tag = GST_ID3_TAG (instance);
329   if (GST_ID3_TAG_GET_CLASS (tag)->type != GST_ID3_TAG_PARSE_BASE) {
330     /* create the sink and src pads */
331     tag->sinkpad =
332         gst_pad_new_from_template (gst_element_class_get_pad_template
333         (GST_ELEMENT_CLASS (g_class), "sink"), "sink");
334     gst_element_add_pad (GST_ELEMENT (tag), tag->sinkpad);
335     gst_pad_set_event_function (tag->sinkpad,
336         GST_DEBUG_FUNCPTR (gst_id3_tag_sink_event));
337     gst_pad_set_chain_function (tag->sinkpad,
338         GST_DEBUG_FUNCPTR (gst_id3_tag_chain));
339   }
340   if (GST_ID3_TAG_GET_CLASS (tag)->type == GST_ID3_TAG_PARSE_MUX) {
341     /* only the muxer class here, all other use sometimes pads */
342     gst_id3_tag_add_src_pad (tag);
343   }
344   /* FIXME: for the alli^H^H^H^Hspider - gst_id3_tag_add_src_pad (tag); */
345   tag->parse_mode = GST_ID3_TAG_PARSE_BASE;
346   tag->buffer = NULL;
347   tag->segment = NULL;
350 static void
351 gst_id3_tag_set_property (GObject * object, guint prop_id, const GValue * value,
352     GParamSpec * pspec)
354   GstID3Tag *tag;
356   tag = GST_ID3_TAG (object);
358   switch (prop_id) {
359     case ARG_V1_TAG:
360       tag->v1tag_render = g_value_get_boolean (value);
361       break;
362     case ARG_V2_TAG:
363       tag->v2tag_render = g_value_get_boolean (value);
364       break;
365     case ARG_PREFER_V1:
366       tag->prefer_v1tag = g_value_get_boolean (value);
367       break;
368     default:
369       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
370       break;
371   }
373   /* make sure we render at least one tag */
374   if (GST_ID3_TAG_GET_CLASS (tag)->type == GST_ID3_TAG_PARSE_MUX &&
375       !tag->v1tag_render && !tag->v2tag_render) {
376     g_object_set (object, prop_id == ARG_V1_TAG ? "v2-tag" : "v1-tag", TRUE,
377         NULL);
378   }
380 static void
381 gst_id3_tag_get_property (GObject * object, guint prop_id, GValue * value,
382     GParamSpec * pspec)
384   GstID3Tag *tag;
386   tag = GST_ID3_TAG (object);
388   switch (prop_id) {
389     case ARG_V1_TAG:
390       g_value_set_boolean (value, tag->v1tag_render);
391       break;
392     case ARG_V2_TAG:
393       g_value_set_boolean (value, tag->v2tag_render);
394       break;
395     case ARG_PREFER_V1:
396       g_value_set_boolean (value, tag->prefer_v1tag);
397       break;
398     default:
399       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
400       break;
401   }
404 #define gst_id3_tag_set_state(tag,new_state) G_STMT_START {                             \
405   GST_LOG_OBJECT (tag, "setting state to %s", #new_state );                             \
406   tag->state = new_state;                                                               \
407 }G_STMT_END
409 static const GstQueryType *
410 gst_id3_tag_get_query_types (GstPad * pad)
412   static const GstQueryType gst_id3_tag_src_query_types[] = {
413     GST_QUERY_POSITION,
414     0
415   };
417   return gst_id3_tag_src_query_types;
420 static gboolean
421 gst_id3_tag_src_query (GstPad * pad, GstQuery * query)
423   gboolean res = FALSE;
424   GstID3Tag *tag;
426   tag = GST_ID3_TAG (gst_pad_get_parent (pad));
428   switch (GST_QUERY_TYPE (query)) {
429     case GST_QUERY_POSITION:{
430       GstFormat format;
431       gint64 current;
433       gst_query_parse_position (query, &format, NULL);
434       switch (format) {
435         case GST_FORMAT_BYTES:{
436           GstPad *peer;
438           if ((peer = gst_pad_get_peer (tag->sinkpad)) == NULL)
439             break;
441           if (tag->state == GST_ID3_TAG_STATE_NORMAL &&
442               gst_pad_query_position (peer, &format, &current)) {
443             if (tag->state == GST_ID3_TAG_STATE_NORMAL) {
444               current -= tag->v2tag_size + tag->v2tag_size_new;
445             } else {
446               current = 0;
447             }
448             gst_query_set_position (query, format, current);
450             res = TRUE;
451           }
452           gst_object_unref (peer);
453           break;
454         }
455         default:
456           break;
457       }
458       break;
459     }
460     case GST_QUERY_DURATION:{
461       GstFormat format;
462       gint64 total;
464       gst_query_parse_duration (query, &format, NULL);
465       switch (format) {
466         case GST_FORMAT_BYTES:{
467           GstPad *peer;
469           if ((peer = gst_pad_get_peer (tag->sinkpad)) == NULL)
470             break;
472           if (tag->state == GST_ID3_TAG_STATE_NORMAL &&
473               gst_pad_query_duration (peer, &format, &total)) {
474             total -= tag->v2tag_size + tag->v1tag_size;
475             total += tag->v2tag_size_new + tag->v1tag_size_new;
477             gst_query_set_duration (query, format, total);
479             res = TRUE;
480           }
481           gst_object_unref (peer);
482           break;
483         }
484         default:
485           break;
486       }
487       break;
488     }
489     default:
490       break;
491   }
492   return res;
495 static gboolean
496 gst_id3_tag_src_event (GstPad * pad, GstEvent * event)
498   GstID3Tag *tag;
499   gboolean res = FALSE;
501   tag = GST_ID3_TAG (gst_pad_get_parent (pad));
503   switch (GST_EVENT_TYPE (event)) {
504     case GST_EVENT_SEEK:
505     {
506       gdouble rate;
507       GstFormat format;
508       GstSeekType cur_type, stop_type;
509       GstSeekFlags flags;
510       gint64 cur, stop;
512       gst_event_parse_seek (event, &rate, &format, &flags,
513           &cur_type, &cur, &stop_type, &stop);
515       if (format == GST_FORMAT_BYTES &&
516           tag->state == GST_ID3_TAG_STATE_NORMAL &&
517           gst_pad_is_linked (tag->sinkpad)) {
518         GstEvent *new;
519         gint diff = 0;
521         switch (cur_type) {
522           case GST_SEEK_TYPE_SET:
523             diff = tag->v2tag_size - tag->v2tag_size_new;
524             break;
525           case GST_SEEK_TYPE_CUR:
526             break;
527           case GST_SEEK_TYPE_END:
528             diff = cur ? tag->v1tag_size_new - tag->v1tag_size : 0;
529             break;
530           default:
531             g_assert_not_reached ();
532             break;
533         }
534         new = gst_event_new_seek (rate, format, flags,
535             cur_type, cur + diff, stop_type, stop);
536         gst_pad_push_event (tag->sinkpad, new);
537         res = TRUE;
538       }
539       break;
540     }
541     default:
542       break;
543   }
545   gst_event_unref (event);
546   gst_object_unref (tag);
548   return res;
551 GstTagList *
552 gst_mad_id3_to_tag_list (const struct id3_tag * tag)
554   const struct id3_frame *frame;
555   const id3_ucs4_t *ucs4;
556   id3_utf8_t *utf8;
557   GstTagList *tag_list;
558   GType tag_type;
559   guint i = 0;
561   tag_list = gst_tag_list_new ();
563   while ((frame = id3_tag_findframe (tag, NULL, i++)) != NULL) {
564     const union id3_field *field;
565     unsigned int nstrings, j;
566     const gchar *tag_name;
568     /* find me the function to query the frame id */
569     gchar *id = g_strndup (frame->id, 5);
571     tag_name = gst_tag_from_id3_tag (id);
572     if (tag_name == NULL) {
573       g_free (id);
574       continue;
575     }
577     if (strcmp (id, "COMM") == 0) {
578       ucs4 = id3_field_getfullstring (&frame->fields[3]);
579       g_assert (ucs4);
581       utf8 = id3_ucs4_utf8duplicate (ucs4);
582       if (utf8 == 0)
583         continue;
585       if (!g_utf8_validate ((char *) utf8, -1, NULL)) {
586         g_warning ("converted string is not valid utf-8");
587         g_free (utf8);
588         continue;
589       }
591       gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
592           GST_TAG_COMMENT, utf8, NULL);
594       g_free (utf8);
595       continue;
596     }
598     field = &frame->fields[1];
599     nstrings = id3_field_getnstrings (field);
601     for (j = 0; j < nstrings; ++j) {
602       ucs4 = id3_field_getstrings (field, j);
603       g_assert (ucs4);
605       if (strcmp (id, ID3_FRAME_GENRE) == 0)
606         ucs4 = id3_genre_name (ucs4);
608       utf8 = id3_ucs4_utf8duplicate (ucs4);
609       if (utf8 == 0)
610         continue;
612       if (!g_utf8_validate ((char *) utf8, -1, NULL)) {
613         g_warning ("converted string is not valid utf-8");
614         free (utf8);
615         continue;
616       }
618       tag_type = gst_tag_get_type (tag_name);
620       /* be sure to add non-string tags here */
621       switch (tag_type) {
622         case G_TYPE_UINT:
623         {
624           guint tmp;
625           gchar *check;
627           tmp = strtoul ((char *) utf8, &check, 10);
629           if (strcmp (tag_name, GST_TAG_DATE) == 0) {
630             GDate *d;
632             if (*check != '\0')
633               break;
634             if (tmp == 0)
635               break;
636             d = g_date_new_dmy (1, 1, tmp);
637             tmp = g_date_get_julian (d);
638             g_date_free (d);
639           } else if (strcmp (tag_name, GST_TAG_TRACK_NUMBER) == 0) {
640             if (*check == '/') {
641               guint total;
643               check++;
644               total = strtoul (check, &check, 10);
645               if (*check != '\0')
646                 break;
648               gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
649                   GST_TAG_TRACK_COUNT, total, NULL);
650             }
651           } else if (strcmp (tag_name, GST_TAG_ALBUM_VOLUME_NUMBER) == 0) {
652             if (*check == '/') {
653               guint total;
655               check++;
656               total = strtoul (check, &check, 10);
657               if (*check != '\0')
658                 break;
660               gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
661                   GST_TAG_ALBUM_VOLUME_COUNT, total, NULL);
662             }
663           }
665           if (*check != '\0')
666             break;
667           gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND, tag_name, tmp,
668               NULL);
669           break;
670         }
671         case G_TYPE_UINT64:
672         {
673           guint64 tmp;
675           g_assert (strcmp (tag_name, GST_TAG_DURATION) == 0);
676           tmp = strtoul ((char *) utf8, NULL, 10);
677           if (tmp == 0) {
678             break;
679           }
680           gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
681               GST_TAG_DURATION, tmp * 1000 * 1000, NULL);
682           break;
683         }
684         case G_TYPE_STRING:{
685           gst_tag_list_add (tag_list, GST_TAG_MERGE_APPEND,
686               tag_name, (const gchar *) utf8, NULL);
687           break;
688         }
689           /* handles GST_TYPE_DATE and anything else */
690         default:{
691           GValue src = { 0, };
692           GValue dest = { 0, };
694           g_value_init (&src, G_TYPE_STRING);
695           g_value_set_string (&src, (const gchar *) utf8);
697           g_value_init (&dest, tag_type);
698           if (g_value_transform (&src, &dest)) {
699             gst_tag_list_add_values (tag_list, GST_TAG_MERGE_APPEND,
700                 tag_name, &dest, NULL);
701           } else {
702             GST_WARNING ("Failed to transform tag from string to type '%s'",
703                 g_type_name (tag_type));
704           }
705           g_value_unset (&src);
706           g_value_unset (&dest);
707           break;
708         }
709       }
710       free (utf8);
711     }
712     g_free (id);
713   }
715   return tag_list;
717 static void
718 tag_list_to_id3_tag_foreach (const GstTagList * list, const gchar * tag_name,
719     gpointer user_data)
721   struct id3_frame *frame;
722   union id3_field *field;
723   guint values = gst_tag_list_get_tag_size (list, tag_name);
724   const gchar *id = gst_tag_to_id3_tag (tag_name);
725   struct id3_tag *tag = (struct id3_tag *) user_data;
727   if (id == NULL)
728     return;
730   if (values == 0)
731     return;
733   frame = id3_frame_new (id);
734   if (id3_tag_attachframe (tag, frame) != 0) {
735     GST_WARNING ("could not attach frame (%s) to id3 tag", id);
736     return;
737   }
738   /* encode in UTF-8 - libid3tag uses Latin1 by default... */
739   field = id3_frame_field (frame, 0);
740   id3_field_settextencoding (field, ID3_FIELD_TEXTENCODING_UTF_8);
741   field = id3_frame_field (frame, 1);
742   g_assert (field);
743   while (values-- > 0) {
744     gunichar *put;
746     if (strcmp (tag_name, GST_TAG_DATE) == 0) {
747       gchar *str;
748       guint u;
749       GDate *d;
751       if (!gst_tag_list_get_uint_index (list, tag_name, values, &u))
752         g_assert_not_reached ();
753       d = g_date_new_julian (u);
754       str = g_strdup_printf ("%u", (guint) (g_date_get_year (d)));
755       put = g_utf8_to_ucs4_fast (str, -1, NULL);
756       g_date_free (d);
757       g_free (str);
758     } else if (strcmp (tag_name, GST_TAG_TRACK_NUMBER) == 0) {
759       gchar *str;
760       guint u;
762       if (!gst_tag_list_get_uint_index (list, tag_name, values, &u))
763         g_assert_not_reached ();
764       str = g_strdup_printf ("%u", u);
765       put = g_utf8_to_ucs4_fast (str, -1, NULL);
766       g_free (str);
767     } else if (strcmp (tag_name, GST_TAG_COMMENT) == 0) {
768       gchar *str;
769       id3_ucs4_t ucs4_empty[] = { 0 };
771       if (!gst_tag_list_get_string_index (list, tag_name, values, &str))
772         g_assert_not_reached ();
773       put = g_utf8_to_ucs4_fast (str, -1, NULL);
774       g_free (str);
776       if (id3_field_setlanguage (&frame->fields[1], "XXX") == -1 ||
777           id3_field_setstring (&frame->fields[2], ucs4_empty) == -1 ||
778           id3_field_setfullstring (&frame->fields[3], (id3_ucs4_t *) put) == -1)
779         GST_WARNING ("could not add a string to the id3 COMM field");
781       g_free (put);
782       return;
783     } else {
784       gchar *str;
786       if (gst_tag_get_type (tag_name) != G_TYPE_STRING) {
787         GST_WARNING ("unhandled GStreamer tag %s", tag_name);
788         return;
789       }
790       if (!gst_tag_list_get_string_index (list, tag_name, values, &str))
791         g_assert_not_reached ();
792       put = g_utf8_to_ucs4_fast (str, -1, NULL);
793       g_free (str);
794     }
795     if (id3_field_addstring (field, (id3_ucs4_t *) put) != 0) {
796       GST_WARNING ("could not add a string to id3 tag field");
797       g_free (put);
798       return;
799     }
800   }
801   id3_field_settextencoding (field, ID3_FIELD_TEXTENCODING_UTF_8);
803 struct id3_tag *
804 gst_mad_tag_list_to_id3_tag (GstTagList * list)
806   struct id3_tag *tag;
808   tag = id3_tag_new ();
810   gst_tag_list_foreach (list, tag_list_to_id3_tag_foreach, tag);
811   return tag;
813 static GstTagList *
814 gst_id3_tag_get_tag_to_render (GstID3Tag * tag)
816   GstTagList *ret = NULL;
818   if (tag->event_tags)
819     ret = gst_tag_list_copy (tag->event_tags);
820   if (ret) {
821     if (tag->parsed_tags)
822       gst_tag_list_insert (ret, tag->parsed_tags, GST_TAG_MERGE_KEEP);
823   } else if (tag->parsed_tags) {
824     ret = gst_tag_list_copy (tag->parsed_tags);
825   }
826   if (ret && gst_tag_setter_get_tag_list (GST_TAG_SETTER (tag))) {
827     gst_tag_list_insert (ret,
828         gst_tag_setter_get_tag_list (GST_TAG_SETTER (tag)),
829         gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (tag)));
830   } else if (gst_tag_setter_get_tag_list (GST_TAG_SETTER (tag))) {
831     ret =
832         gst_tag_list_copy (gst_tag_setter_get_tag_list (GST_TAG_SETTER (tag)));
833   }
834   return ret;
837 static gboolean
838 gst_id3_tag_sink_event (GstPad * pad, GstEvent * event)
840   GstID3Tag *tag = GST_ID3_TAG (gst_pad_get_parent (pad));
842   switch (GST_EVENT_TYPE (event)) {
843     case GST_EVENT_NEWSEGMENT:
844       GST_DEBUG_OBJECT (tag, "Have new segment event in mode %d", tag->state);
845       switch (tag->state) {
846         case GST_ID3_TAG_STATE_READING_V2_TAG:{
847           GstFormat format = GST_FORMAT_UNDEFINED;
848           gint64 value, end_value;
850           gst_event_parse_new_segment (event, NULL, NULL, &format, &value,
851               &end_value, NULL);
853           if (format == GST_FORMAT_BYTES || format == GST_FORMAT_DEFAULT) {
854             if (value !=
855                 (tag->buffer ? GST_BUFFER_OFFSET (tag->buffer) +
856                     GST_BUFFER_SIZE (tag->buffer)
857                     : 0))
858               GST_ELEMENT_ERROR (tag, CORE, EVENT, (NULL),
859                   ("Got seek to %" G_GINT64_FORMAT " during ID3v2 tag reading"
860                       " (allowed was %" G_GINT64_FORMAT ")", value,
861                       (guint64) (tag->buffer ? GST_BUFFER_OFFSET (tag->buffer)
862                           + GST_BUFFER_SIZE (tag->buffer) : 0)));
863           }
864           tag->segment = event;
865           break;
866         }
867         case GST_ID3_TAG_STATE_SEEKING_TO_V1_TAG:
868           /* just assume it's the right seek for now */
869           gst_id3_tag_set_state (tag, GST_ID3_TAG_STATE_READING_V1_TAG);
870           gst_event_unref (event);
871           break;
872         case GST_ID3_TAG_STATE_READING_V1_TAG:
873           GST_ELEMENT_ERROR (tag, CORE, EVENT, (NULL),
874               ("Seek during ID3v1 tag reading"));
875           gst_event_unref (event);
876           break;
877         case GST_ID3_TAG_STATE_SEEKING_TO_NORMAL:
878           /* just assume it's the right seek for now */
879           gst_id3_tag_set_state (tag, GST_ID3_TAG_STATE_NORMAL_START);
880           if (tag->segment != NULL)
881             gst_event_unref (tag->segment);
882           tag->segment = event;
883           break;
884         case GST_ID3_TAG_STATE_NORMAL_START:
885           if (!CAN_BE_DEMUXER (tag)) {
886             /* initial discont, ignore */
887             GST_LOG_OBJECT (tag, "Ignoring initial newsegment");
888             gst_event_unref (event);
889             break;
890           } else {
891             GST_ERROR_OBJECT (tag, "tag event not sent, FIXME");
892             gst_id3_tag_set_state (tag, GST_ID3_TAG_STATE_NORMAL);
893             /* fall through */
894           }
895         case GST_ID3_TAG_STATE_NORMAL:{
896           GstFormat format = GST_FORMAT_UNDEFINED;
897           gdouble rate;
898           gint64 value, end_value, base;
900           gst_event_parse_new_segment (event, NULL, &rate, &format, &value,
901               &end_value, &base);
902           if (format == GST_FORMAT_BYTES || format == GST_FORMAT_DEFAULT) {
903             if (value > tag->v2tag_size) {
904               value -= tag->v2tag_size;
905               gst_event_unref (event);
906               event =
907                   gst_event_new_new_segment (FALSE, rate, format, value,
908                   end_value, base);
909             }
910           }
911           if (tag->srcpad)
912             gst_pad_push_event (tag->srcpad, event);
913           else
914             gst_event_unref (event);
915           break;
916         }
917         default:
918           g_assert_not_reached ();
919       }
920       break;
921     case GST_EVENT_TAG:
922     {
923       GstTagList *list;
925       GST_DEBUG_OBJECT (tag, "Have tags event in mode %d", tag->state);
926       gst_event_parse_tag (event, &list);
928       if (tag->event_tags) {
929         gst_tag_list_insert (tag->event_tags, list, GST_TAG_MERGE_PREPEND);
930       } else {
931         tag->event_tags = gst_tag_list_copy (list);
932       }
933       gst_event_unref (event);
934       break;
935     }
936     case GST_EVENT_EOS:
937       GST_DEBUG_OBJECT (tag, "Have EOS in mode %d", tag->state);
938       if (tag->v1tag_render && IS_MUXER (tag)) {
939         GstTagList *merged;
940         struct id3_tag *id3;
942         GST_LOG_OBJECT (tag, "rendering v1 tag after eos event");
943         merged = gst_id3_tag_get_tag_to_render (tag);
944         if (merged) {
945           id3 = gst_mad_tag_list_to_id3_tag (merged);
946           if (id3) {
947             GstBuffer *tag_buffer;
949             id3_tag_options (id3, ID3_TAG_OPTION_ID3V1, ID3_TAG_OPTION_ID3V1);
950             tag_buffer = gst_buffer_new_and_alloc (128);
951             if (128 != id3_tag_render (id3, tag_buffer->data))
952               g_assert_not_reached ();
953             gst_pad_push (tag->srcpad, tag_buffer);
954             id3_tag_delete (id3);
955           }
956           gst_tag_list_free (merged);
957         }
958       }
959       if (tag->state == GST_ID3_TAG_STATE_SEEKING_TO_NORMAL) {
960         /* Absorb EOS while finishing reading V1 TAG */
961         GST_LOG_OBJECT (tag, "Ignoring EOS event after reading id3v1");
962         gst_event_unref (event);
963         break;
964       }
965       /* fall through */
966     default:
967       gst_pad_event_default (pad, event);
968       break;
969   }
970   return TRUE;
972 typedef struct
974   guint best_probability;
975   GstCaps *caps;
976   GstBuffer *buffer;
978 SimpleTypeFind;
979 guint8 *
980 simple_find_peek (gpointer data, gint64 offset, guint size)
982   SimpleTypeFind *find = (SimpleTypeFind *) data;
984   if (offset < 0)
985     return NULL;
987   if (GST_BUFFER_SIZE (find->buffer) >= offset + size) {
988     return GST_BUFFER_DATA (find->buffer) + offset;
989   }
990   return NULL;
992 static void
993 simple_find_suggest (gpointer data, guint probability, const GstCaps * caps)
995   SimpleTypeFind *find = (SimpleTypeFind *) data;
997   if (probability > find->best_probability) {
998     GstCaps *copy = gst_caps_copy (caps);
1000     gst_caps_replace (&find->caps, copy);
1001     gst_caps_unref (copy);
1002     find->best_probability = probability;
1003   }
1005 static GstCaps *
1006 gst_id3_tag_do_typefind (GstID3Tag * tag, GstBuffer * buffer)
1008   GList *walk, *type_list;
1009   SimpleTypeFind find;
1010   GstTypeFind gst_find;
1012   /* this will help us detecting the media stream type after
1013    * this id3 thingy... Please note that this is a cruel hack
1014    * for as long as spider doesn't support multi-type-finding.
1015    */
1016   walk = type_list = gst_type_find_factory_get_list ();
1018   find.buffer = buffer;
1019   find.best_probability = 0;
1020   find.caps = NULL;
1021   gst_find.data = &find;
1022   gst_find.peek = simple_find_peek;
1023   gst_find.get_length = NULL;
1024   gst_find.suggest = simple_find_suggest;
1025   while (walk) {
1026     GstTypeFindFactory *factory = GST_TYPE_FIND_FACTORY (walk->data);
1028     gst_type_find_factory_call_function (factory, &gst_find);
1029     if (find.best_probability >= GST_TYPE_FIND_MAXIMUM)
1030       break;
1031     walk = g_list_next (walk);
1032   }
1033   g_list_free (type_list);
1034   if (find.best_probability > 0) {
1035     return find.caps;
1036   } else {
1037     GST_ELEMENT_ERROR (tag, CORE, CAPS, (NULL), ("no caps found"));
1038     return NULL;
1039   }
1041 static gboolean
1042 gst_id3_tag_do_caps_nego (GstID3Tag * tag, GstBuffer * buffer)
1044   if (buffer != NULL && CAN_BE_DEMUXER (tag)) {
1045     tag->found_caps = gst_id3_tag_do_typefind (tag, buffer);
1046     if (!tag->found_caps) {
1047       return FALSE;
1048     }
1049   }
1050   if (tag->srcpad == NULL) {
1051     gst_id3_tag_add_src_pad (tag);
1052     gst_element_no_more_pads (GST_ELEMENT (tag));
1053   }
1054   if (!gst_pad_is_linked (tag->srcpad)) {
1055     GST_DEBUG_OBJECT (tag, "srcpad not linked, not proceeding");
1056     tag->parse_mode = GST_ID3_TAG_GET_CLASS (tag)->type;
1057     return TRUE;
1058   } else {
1059     GST_DEBUG_OBJECT (tag, "renegotiating");
1060     //return gst_pad_renegotiate (tag->srcpad) != GST_PAD_LINK_REFUSED;
1061     return TRUE;
1062   }
1065 static GstPadLinkReturn
1066 gst_id3_tag_src_link (GstPad * pad, GstPad * peer)
1068   GstID3Tag *tag;
1070   //const gchar *mimetype;
1072   tag = GST_ID3_TAG (gst_pad_get_parent (pad));
1074 #if 0
1075   if (!tag->found_caps && CAN_BE_DEMUXER (tag))
1076     return GST_PAD_LINK_DELAYED;
1077   if (!CAN_BE_MUXER (tag) || !CAN_BE_DEMUXER (tag)) {
1078     tag->parse_mode = GST_ID3_TAG_GET_CLASS (tag)->type;
1079     return GST_PAD_LINK_OK;
1080   }
1082   mimetype = gst_structure_get_name (gst_caps_get_structure (caps, 0));
1084   if (strcmp (mimetype, "application/x-id3") == 0) {
1085     tag->parse_mode = GST_ID3_TAG_PARSE_MUX;
1086     GST_LOG_OBJECT (tag, "normal operation, using application/x-id3 output");
1087   } else if (strcmp (mimetype, "application/x-gst-tags") == 0) {
1088     tag->parse_mode = GST_ID3_TAG_PARSE_ANY;
1089     GST_LOG_OBJECT (tag, "fast operation, just outputting tags");
1090   } else {
1091     tag->parse_mode = GST_ID3_TAG_PARSE_DEMUX;
1092     GST_LOG_OBJECT (tag, "parsing operation, extracting tags");
1093   }
1094 #endif
1095   if (GST_PAD_LINKFUNC (peer))
1096     return GST_PAD_LINKFUNC (peer) (peer, pad);
1097   else
1098     return GST_PAD_LINK_OK;
1100 static void
1101 gst_id3_tag_send_tag_event (GstID3Tag * tag)
1103   /* FIXME: what's the correct merge mode? Docs need to tell... */
1104   GstTagList *merged = gst_tag_list_merge (tag->event_tags, tag->parsed_tags,
1105       GST_TAG_MERGE_KEEP);
1107   if (tag->parsed_tags)
1108     gst_element_post_message (GST_ELEMENT (tag),
1109         gst_message_new_tag (GST_OBJECT (tag),
1110             gst_tag_list_copy (tag->parsed_tags)));
1112   if (merged) {
1113     GstEvent *event = gst_event_new_tag (merged);
1115     GST_EVENT_TIMESTAMP (event) = 0;
1116     gst_pad_push_event (tag->srcpad, event);
1117   }
1119 static GstFlowReturn
1120 gst_id3_tag_chain (GstPad * pad, GstBuffer * buffer)
1122   GstID3Tag *tag;
1124   tag = GST_ID3_TAG (gst_pad_get_parent (pad));
1125   GST_DEBUG_OBJECT (tag, "Chain, state = %d", tag->state);
1127   switch (tag->state) {
1128     case GST_ID3_TAG_STATE_SEEKING_TO_V1_TAG:
1129     case GST_ID3_TAG_STATE_SEEKING_TO_NORMAL:
1130       /* we're waiting for the seek to finish, just discard all the stuff */
1131       gst_buffer_unref (buffer);
1132       return GST_FLOW_OK;
1133     case GST_ID3_TAG_STATE_READING_V1_TAG:
1134       if (tag->buffer) {
1135         GstBuffer *temp;
1137         temp = gst_buffer_merge (tag->buffer, buffer);
1138         gst_buffer_unref (tag->buffer);
1139         tag->buffer = temp;
1140         gst_buffer_unref (buffer);
1141       } else {
1142         tag->buffer = buffer;
1143         tag->v1tag_offset = buffer->offset;
1144       }
1145       if (GST_BUFFER_SIZE (tag->buffer) < 128)
1146         return GST_FLOW_OK;
1147       g_assert (tag->v1tag_size == 0);
1148       tag->v1tag_size = id3_tag_query (GST_BUFFER_DATA (tag->buffer),
1149           GST_BUFFER_SIZE (tag->buffer));
1150       if (tag->v1tag_size == 128) {
1151         GstTagList *newtag;
1153         newtag = gst_tag_list_new_from_id3v1 (GST_BUFFER_DATA (tag->buffer));
1154         GST_LOG_OBJECT (tag, "have read ID3v1 tag");
1155         if (newtag) {
1156           if (tag->parsed_tags) {
1157             /* FIXME: use append/prepend here ? */
1158             gst_tag_list_insert (tag->parsed_tags, newtag,
1159                 tag->prefer_v1tag ? GST_TAG_MERGE_REPLACE : GST_TAG_MERGE_KEEP);
1160             gst_tag_list_free (newtag);
1161           } else {
1162             tag->parsed_tags = newtag;
1163           }
1164         } else {
1165           GST_WARNING_OBJECT (tag, "detected ID3v1 tag, but couldn't parse it");
1166         }
1167       } else {
1168         if (tag->v1tag_size != 0) {
1169           GST_WARNING_OBJECT (tag, "bad non-ID3v1 tag at end of file");
1170         } else {
1171           GST_LOG_OBJECT (tag, "no ID3v1 tag (%" G_GUINT64_FORMAT ")",
1172               GST_BUFFER_OFFSET (tag->buffer));
1173           tag->v1tag_offset = G_MAXUINT64;
1174         }
1175       }
1176       gst_buffer_unref (tag->buffer);
1177       tag->buffer = NULL;
1178       if (tag->parse_mode != GST_ID3_TAG_PARSE_ANY) {
1179         /* seek to beginning */
1180         GST_LOG_OBJECT (tag, "seeking back to beginning");
1181         gst_id3_tag_set_state (tag, GST_ID3_TAG_STATE_SEEKING_TO_NORMAL);
1182         if (!gst_pad_push_event (tag->sinkpad,
1183                 gst_event_new_seek (1.0, GST_FORMAT_BYTES, GST_SEEK_FLAG_FLUSH,
1184                     GST_SEEK_TYPE_SET, tag->v2tag_size, GST_SEEK_TYPE_NONE,
1185                     0))) {
1186           GST_ELEMENT_ERROR (tag, CORE, SEEK, (NULL),
1187               ("can't seek back to beginning from reading ID3v1 tag"));
1188         }
1189       } else {
1190         gst_id3_tag_send_tag_event (tag);
1191         /* set eos, we're done parsing tags */
1192         GST_LOG_OBJECT (tag, "Finished reading ID3v1 tag");
1193         gst_id3_tag_set_state (tag, GST_ID3_TAG_STATE_NORMAL_START);
1194         //gst_element_set_eos (GST_ELEMENT (tag));
1195         gst_pad_push_event (tag->srcpad, gst_event_new_eos ());
1196       }
1197       return GST_FLOW_OK;
1198     case GST_ID3_TAG_STATE_READING_V2_TAG:
1199       if (tag->buffer) {
1200         GstBuffer *temp;
1202         temp = gst_buffer_merge (tag->buffer, buffer);
1203         gst_buffer_unref (tag->buffer);
1204         tag->buffer = temp;
1205         gst_buffer_unref (buffer);
1206       } else {
1207         tag->buffer = buffer;
1208       }
1209       if (GST_BUFFER_SIZE (tag->buffer) < 10)
1210         return GST_FLOW_OK;
1211       if (tag->v2tag_size == 0) {
1212         tag->v2tag_size = id3_tag_query (GST_BUFFER_DATA (tag->buffer),
1213             GST_BUFFER_SIZE (tag->buffer));
1214         /* no footers supported */
1215         if (tag->v2tag_size < 0)
1216           tag->v2tag_size = 0;
1217       }
1218       if (GST_BUFFER_SIZE (tag->buffer) < tag->v2tag_size + ID3_TYPE_FIND_SIZE)
1219         return GST_FLOW_OK;
1220       if (tag->v2tag_size != 0) {
1221         struct id3_tag *v2tag;
1223         v2tag = id3_tag_parse (GST_BUFFER_DATA (tag->buffer),
1224             GST_BUFFER_SIZE (tag->buffer));
1225         if (v2tag) {
1226           GstTagList *list;
1228           list = gst_mad_id3_to_tag_list (v2tag);
1229           id3_tag_delete (v2tag);
1230           GST_LOG_OBJECT (tag, "parsed ID3v2 tag");
1231           /* no other tag parsed yet */
1232           g_assert (tag->parsed_tags == NULL);
1233           tag->parsed_tags = list;
1234         } else {
1235           GST_WARNING_OBJECT (tag, "detected ID3v2 tag, but couldn't parse it");
1236         }
1237       }
1239       /* seek to ID3v1 tag */
1240       gst_id3_tag_set_state (tag, GST_ID3_TAG_STATE_SEEKING_TO_V1_TAG);
1242       if (gst_pad_push_event (tag->sinkpad,
1243               gst_event_new_seek (1.0, GST_FORMAT_BYTES, GST_SEEK_FLAG_FLUSH,
1244                   GST_SEEK_TYPE_END, -128, GST_SEEK_TYPE_NONE, 0))) {
1245         gst_buffer_unref (tag->buffer);
1246         tag->buffer = NULL;
1247         return GST_FLOW_OK;
1248       } else {
1249         GST_DEBUG_OBJECT (tag, "Can't seek to read ID3v1 tag");
1250       }
1252       /* Can't seek, strip off the id3v2 tag and send buffer */
1253       GST_LOG_OBJECT (tag,
1254           "removing first %ld bytes, because they're the ID3v2 tag",
1255           tag->v2tag_size);
1256       buffer =
1257           gst_buffer_create_sub (tag->buffer, tag->v2tag_size,
1258           GST_BUFFER_SIZE (tag->buffer) - tag->v2tag_size);
1259       /* the offsets will be corrected further down, we just copy them */
1260       if (GST_BUFFER_OFFSET_IS_VALID (tag->buffer))
1261         GST_BUFFER_OFFSET (buffer) =
1262             GST_BUFFER_OFFSET (tag->buffer) + tag->v2tag_size;
1263       if (GST_BUFFER_OFFSET_END_IS_VALID (tag->buffer))
1264         GST_BUFFER_OFFSET_END (buffer) =
1265             GST_BUFFER_OFFSET_END (tag->buffer) + tag->v2tag_size;
1266       gst_buffer_unref (tag->buffer);
1267       tag->buffer = NULL;
1269       gst_id3_tag_set_state (tag, GST_ID3_TAG_STATE_NORMAL_START);
1270       /* fall through */
1271     case GST_ID3_TAG_STATE_NORMAL_START:
1272       g_assert (tag->buffer == NULL);
1274       if (!IS_MUXER (tag) && (tag->found_caps == NULL))
1275         if (!gst_id3_tag_do_caps_nego (tag, buffer)) {
1276           gst_buffer_unref (buffer);
1277           return GST_FLOW_OK;
1278         }
1280       /* If we didn't get a segment event to pass on, someone
1281        * downstream is going to complain */
1282       if (tag->segment != NULL) {
1283         gst_pad_push_event (tag->srcpad, tag->segment);
1284         tag->segment = NULL;
1285       }
1287       gst_id3_tag_send_tag_event (tag);
1289       if (IS_MUXER (tag) && tag->v2tag_render) {
1290         struct id3_tag *id3;
1291         GstTagList *merged;
1292         GstBuffer *tag_buffer;
1294         /* render tag */
1295         tag->v2tag_size_new = 0;
1296         merged = gst_id3_tag_get_tag_to_render (tag);
1297         if (merged) {
1298           id3 = gst_mad_tag_list_to_id3_tag (merged);
1299           if (id3) {
1300             glong estimated;
1302             estimated = id3_tag_render (id3, NULL);
1303             tag_buffer = gst_buffer_new_and_alloc (estimated);
1304             tag->v2tag_size_new =
1305                 id3_tag_render (id3, GST_BUFFER_DATA (tag_buffer));
1306             g_assert (estimated >= tag->v2tag_size_new);
1307             GST_BUFFER_SIZE (tag_buffer) = tag->v2tag_size_new;
1308             gst_pad_push (tag->srcpad, tag_buffer);
1309             id3_tag_delete (id3);
1310           }
1311           gst_tag_list_free (merged);
1312         }
1313       }
1315       gst_id3_tag_set_state (tag, GST_ID3_TAG_STATE_NORMAL);
1316       tag->v1tag_size_new = (tag->v1tag_render &&
1317           IS_MUXER (tag) &&
1318           (tag->parsed_tags != NULL ||
1319               gst_tag_setter_get_tag_list (GST_TAG_SETTER (tag)) !=
1320               NULL)) ? 128 : 0;
1321       /* fall through */
1322     case GST_ID3_TAG_STATE_NORMAL:
1323       if (tag->parse_mode == GST_ID3_TAG_PARSE_ANY) {
1324         gst_buffer_unref (buffer);
1325         //gst_element_set_eos (GST_ELEMENT (tag));
1326         gst_pad_push_event (tag->srcpad, gst_event_new_eos ());
1327       } else {
1328         if (GST_BUFFER_OFFSET_IS_VALID (buffer)) {
1329           if (buffer->offset >= tag->v1tag_offset) {
1330             gst_buffer_unref (buffer);
1331             return GST_FLOW_OK;
1332           } else if (buffer->offset + buffer->size > tag->v1tag_offset) {
1333             GstBuffer *sub = gst_buffer_create_sub (buffer, 0,
1334                 buffer->size - 128);
1336             gst_buffer_unref (buffer);
1337             buffer = sub;
1338           }
1339         }
1340         if (tag->v2tag_size) {
1341           GstBuffer *sub =
1342               gst_buffer_create_sub (buffer, 0, GST_BUFFER_SIZE (buffer));
1343           if (GST_BUFFER_OFFSET_IS_VALID (buffer))
1344             GST_BUFFER_OFFSET (sub) =
1345                 GST_BUFFER_OFFSET (buffer) - tag->v2tag_size +
1346                 tag->v2tag_size_new;
1347           if (GST_BUFFER_OFFSET_END_IS_VALID (buffer))
1348             GST_BUFFER_OFFSET_END (sub) =
1349                 GST_BUFFER_OFFSET_END (buffer) - tag->v2tag_size +
1350                 tag->v2tag_size_new;
1351           gst_buffer_unref (buffer);
1352           buffer = sub;
1353         }
1354         gst_pad_push (tag->srcpad, buffer);
1355       }
1356       return GST_FLOW_OK;
1357   }
1358   return GST_FLOW_OK;
1361 static GstStateChangeReturn
1362 gst_id3_tag_change_state (GstElement * element, GstStateChange transition)
1364   GstID3Tag *tag;
1365   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
1367   tag = GST_ID3_TAG (element);
1369   switch (transition) {
1370     case GST_STATE_CHANGE_READY_TO_PAUSED:
1371       g_assert (tag->parsed_tags == NULL);
1372       g_assert (tag->buffer == NULL);
1373       tag->v1tag_size = 0;
1374       tag->v1tag_offset = G_MAXUINT64;
1375       tag->v2tag_size = 0;
1376       if (CAN_BE_DEMUXER (tag)) {
1377         gst_id3_tag_set_state (tag, GST_ID3_TAG_STATE_READING_V2_TAG);
1378       } else {
1379         gst_id3_tag_set_state (tag, GST_ID3_TAG_STATE_NORMAL_START);
1380       }
1381       break;
1382     default:
1383       break;
1384   }
1386   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1388   switch (transition) {
1389     case GST_STATE_CHANGE_PAUSED_TO_READY:
1390       if (tag->parsed_tags) {
1391         gst_tag_list_free (tag->parsed_tags);
1392         tag->parsed_tags = NULL;
1393       }
1394       if (tag->event_tags) {
1395         gst_tag_list_free (tag->event_tags);
1396         tag->event_tags = NULL;
1397       }
1398       if (tag->buffer) {
1399         gst_buffer_unref (tag->buffer);
1400         tag->buffer = NULL;
1401       }
1402       if (tag->found_caps) {
1403         gst_caps_unref (tag->found_caps);
1404         tag->found_caps = NULL;
1405       }
1406       if (tag->segment) {
1407         gst_event_unref (tag->segment);
1408         tag->segment = NULL;
1409       }
1410       tag->parse_mode = GST_ID3_TAG_PARSE_BASE;
1411       break;
1412     default:
1413       break;
1414   }
1417   return ret;
1420 /*** PLUGIN INITIALIZATION ****************************************************/
1422 static gboolean
1423 plugin_init (GstPlugin * plugin)
1426   if (!gst_element_register (plugin, "mad", GST_RANK_SECONDARY,
1427           gst_mad_get_type ())
1428       || !gst_element_register (plugin, "id3demux", GST_RANK_PRIMARY,
1429           gst_id3_tag_get_type (GST_ID3_TAG_PARSE_DEMUX))
1430       || !gst_element_register (plugin, "id3mux", GST_RANK_NONE,        /* removed for spider */
1431           gst_id3_tag_get_type (GST_ID3_TAG_PARSE_MUX))
1432       /* FIXME 0.9: remove this element */
1433       || !gst_element_register (plugin, "id3tag", GST_RANK_NONE,
1434           gst_id3_tag_get_type (GST_ID3_TAG_PARSE_ANY))) {
1435     return FALSE;
1436   }
1438   GST_DEBUG_CATEGORY_INIT (gst_id3_tag_debug, "id3tag", 0,
1439       "id3 tag reader / setter");
1441   return TRUE;
1444 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
1445     GST_VERSION_MINOR,
1446     "mad",
1447     "id3 tag manipulation and mp3 decoding based on the mad library",
1448     plugin_init, VERSION, "GPL", GST_PACKAGE, GST_ORIGIN)