close #333784 unref the result of gst_pad_get_parent() by: Christophe Fergeau.
[platform/upstream/gstreamer.git] / ext / flac / gstflactag.c
1
2 /* GStreamer
3  * Copyright (C) 2003 Christophe Fergeau <teuf@gnome.org>
4  *
5  * gstflactag.c: plug-in for reading/modifying vorbis comments in flac files
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <gst/gsttagsetter.h>
27 #include <gst/tag/tag.h>
28 #include <string.h>
29
30 #define GST_TYPE_FLAC_TAG (gst_flac_tag_get_type())
31 #define GST_FLAC_TAG(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_FLAC_TAG, GstFlacTag))
32 #define GST_FLAC_TAG_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_FLAC_TAG, GstFlacTag))
33 #define GST_IS_FLAC_TAG(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_FLAC_TAG))
34 #define GST_IS_FLAC_TAG_CLASS(obj) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_FLAC_TAG))
35
36 typedef struct _GstFlacTag GstFlacTag;
37 typedef struct _GstFlacTagClass GstFlacTagClass;
38
39 static inline gint
40 min (gint a, gint b)
41 {
42   if (a < b) {
43     return a;
44   } else {
45     return b;
46   }
47 }
48
49
50 typedef enum
51 {
52   GST_FLAC_TAG_STATE_INIT,
53   GST_FLAC_TAG_STATE_METADATA_BLOCKS,
54   GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK,
55   GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK,
56   GST_FLAC_TAG_STATE_VC_METADATA_BLOCK,
57   GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT,
58   GST_FLAC_TAG_STATE_AUDIO_DATA
59 }
60 GstFlacTagState;
61
62
63 struct _GstFlacTag
64 {
65   GstElement element;
66
67   /* pads */
68   GstPad *sinkpad;
69   GstPad *srcpad;
70
71   GstFlacTagState state;
72
73   GstBuffer *buffer;
74   GstBuffer *vorbiscomment;
75   GstTagList *tags;
76
77   guint metadata_bytes_remaining;
78   gboolean metadata_last_block;
79
80   gboolean only_output_tags;
81 };
82
83 struct _GstFlacTagClass
84 {
85   GstElementClass parent_class;
86 };
87
88 /* elementfactory information */
89 static GstElementDetails gst_flac_tag_details =
90 GST_ELEMENT_DETAILS ("flac retagger",
91     "Tag",
92     "Rewrite tags in a FLAC file",
93     "Christope Fergeau <teuf@gnome.org>");
94
95
96 /* signals and args */
97 enum
98 {
99   /* FILL ME */
100   LAST_SIGNAL
101 };
102
103 enum
104 {
105   ARG_0
106       /* FILL ME */
107 };
108
109 static GstStaticPadTemplate flac_tag_src_template =
110     GST_STATIC_PAD_TEMPLATE ("src",
111     GST_PAD_SRC,
112     GST_PAD_ALWAYS,
113     GST_STATIC_CAPS ("audio/x-flac; application/x-gst-tags")
114     );
115
116 static GstStaticPadTemplate flac_tag_sink_template =
117 GST_STATIC_PAD_TEMPLATE ("sink",
118     GST_PAD_SINK,
119     GST_PAD_ALWAYS,
120     GST_STATIC_CAPS ("audio/x-flac")
121     );
122
123
124 static void gst_flac_tag_base_init (gpointer g_class);
125 static void gst_flac_tag_class_init (GstFlacTagClass * klass);
126 static void gst_flac_tag_init (GstFlacTag * tag);
127
128 static void gst_flac_tag_chain (GstPad * pad, GstData * data);
129
130 static GstStateChangeReturn gst_flac_tag_change_state (GstElement * element,
131     GstStateChange transition);
132
133
134 static GstElementClass *parent_class = NULL;
135
136 /* static guint gst_flac_tag_signals[LAST_SIGNAL] = { 0 }; */
137
138 GType
139 gst_flac_tag_get_type (void)
140 {
141   static GType flac_tag_type = 0;
142
143   if (!flac_tag_type) {
144     static const GTypeInfo flac_tag_info = {
145       sizeof (GstFlacTagClass),
146       gst_flac_tag_base_init,
147       NULL,
148       (GClassInitFunc) gst_flac_tag_class_init,
149       NULL,
150       NULL,
151       sizeof (GstFlacTag),
152       0,
153       (GInstanceInitFunc) gst_flac_tag_init,
154     };
155     static const GInterfaceInfo tag_setter_info = {
156       NULL,
157       NULL,
158       NULL
159     };
160
161     flac_tag_type =
162         g_type_register_static (GST_TYPE_ELEMENT, "GstFlacTag", &flac_tag_info,
163         0);
164
165     g_type_add_interface_static (flac_tag_type, GST_TYPE_TAG_SETTER,
166         &tag_setter_info);
167
168   }
169   return flac_tag_type;
170 }
171
172
173 static void
174 gst_flac_tag_base_init (gpointer g_class)
175 {
176   GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
177
178   gst_element_class_set_details (element_class, &gst_flac_tag_details);
179
180   gst_element_class_add_pad_template (element_class,
181       gst_static_pad_template_get (&flac_tag_sink_template));
182   gst_element_class_add_pad_template (element_class,
183       gst_static_pad_template_get (&flac_tag_src_template));
184 }
185
186
187 static void
188 send_eos (GstFlacTag * tag)
189 {
190   gst_element_set_eos (GST_ELEMENT (tag));
191   gst_pad_push (tag->srcpad, GST_DATA (gst_event_new (GST_EVENT_EOS)));
192   /* Seek to end of sink stream */
193   if (gst_pad_send_event (GST_PAD_PEER (tag->sinkpad),
194           gst_event_new_seek (GST_FORMAT_BYTES | GST_SEEK_METHOD_END |
195               GST_SEEK_FLAG_FLUSH, 0))) {
196   } else {
197     g_warning ("Couldn't seek to eos on sinkpad\n");
198   }
199 }
200
201
202 static gboolean
203 caps_nego (GstFlacTag * tag)
204 {
205   /* do caps nego */
206   GstCaps *caps;
207
208   caps = gst_caps_new_simple ("audio/x-flac", NULL);
209   if (gst_pad_try_set_caps (tag->srcpad, caps) != GST_PAD_LINK_REFUSED) {
210     tag->only_output_tags = FALSE;
211     GST_LOG_OBJECT (tag, "normal operation, using audio/x-flac output");
212   } else {
213     if (gst_pad_try_set_caps (tag->srcpad,
214             gst_caps_new_simple ("application/x-gst-tags", NULL))
215         != GST_PAD_LINK_REFUSED) {
216       tag->only_output_tags = TRUE;
217       GST_LOG_OBJECT (tag, "fast operation, just outputting tags");
218       printf ("output tags only\n");
219     } else {
220       return FALSE;
221     }
222   }
223   return TRUE;
224 }
225
226 static void
227 gst_flac_tag_class_init (GstFlacTagClass * klass)
228 {
229   GstElementClass *gstelement_class;
230   GObjectClass *gobject_class;
231
232   gstelement_class = (GstElementClass *) klass;
233   gobject_class = (GObjectClass *) klass;
234
235   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
236
237   gstelement_class->change_state = gst_flac_tag_change_state;
238 }
239
240
241 static void
242 gst_flac_tag_init (GstFlacTag * tag)
243 {
244   /* create the sink and src pads */
245   tag->sinkpad =
246       gst_pad_new_from_template (gst_static_pad_template_get
247       (&flac_tag_sink_template), "sink");
248   gst_element_add_pad (GST_ELEMENT (tag), tag->sinkpad);
249   gst_pad_set_chain_function (tag->sinkpad,
250       GST_DEBUG_FUNCPTR (gst_flac_tag_chain));
251
252   tag->srcpad =
253       gst_pad_new_from_template (gst_static_pad_template_get
254       (&flac_tag_src_template), "src");
255   gst_element_add_pad (GST_ELEMENT (tag), tag->srcpad);
256
257   tag->buffer = NULL;
258 }
259
260 #define FLAC_MAGIC "fLaC"
261 #define FLAC_MAGIC_SIZE (sizeof (FLAC_MAGIC) - 1)
262
263 static void
264 gst_flac_tag_chain (GstPad * pad, GstData * data)
265 {
266   GstBuffer *buffer;
267   GstFlacTag *tag;
268
269   if (GST_IS_EVENT (data)) {
270     g_print ("Unhandled event\n");
271     return;
272   }
273
274   buffer = GST_BUFFER (data);
275   tag = GST_FLAC_TAG (gst_pad_get_parent (pad));
276
277   if (tag->buffer) {
278     GstBuffer *merge;
279
280     merge = gst_buffer_merge (tag->buffer, buffer);
281     gst_buffer_unref (buffer);
282     gst_buffer_unref (tag->buffer);
283     tag->buffer = merge;
284   } else {
285     tag->buffer = buffer;
286   }
287
288
289   /* Initial state, we don't even know if we are dealing with a flac file */
290   if (tag->state == GST_FLAC_TAG_STATE_INIT) {
291     if (!caps_nego (tag)) {
292       goto cleanup;
293     }
294
295     if (GST_BUFFER_SIZE (tag->buffer) < sizeof (FLAC_MAGIC)) {
296       goto cleanup;
297     }
298
299     if (strncmp (GST_BUFFER_DATA (tag->buffer), FLAC_MAGIC,
300             FLAC_MAGIC_SIZE) == 0) {
301       GstBuffer *sub;
302
303       tag->state = GST_FLAC_TAG_STATE_METADATA_BLOCKS;
304       sub = gst_buffer_create_sub (tag->buffer, 0, FLAC_MAGIC_SIZE);
305
306       gst_pad_push (tag->srcpad, GST_DATA (sub));
307       sub =
308           gst_buffer_create_sub (tag->buffer, FLAC_MAGIC_SIZE,
309           GST_BUFFER_SIZE (tag->buffer) - FLAC_MAGIC_SIZE);
310       gst_buffer_unref (tag->buffer);
311       /* We do a copy because we need a writable buffer, and _create_sub
312        * sets the buffer it uses to read-only
313        */
314       tag->buffer = gst_buffer_copy (sub);
315       gst_buffer_unref (sub);
316     } else {
317       /* FIXME: does that work well with FLAC files containing ID3v2 tags ? */
318       GST_ELEMENT_ERROR (tag, STREAM, WRONG_TYPE, (NULL), (NULL));
319     }
320   }
321
322
323   /* The fLaC magic string has been skipped, try to detect the beginning
324    * of a metadata block
325    */
326   if (tag->state == GST_FLAC_TAG_STATE_METADATA_BLOCKS) {
327     guint size;
328     guint type;
329     gboolean is_last;
330
331     g_assert (tag->metadata_bytes_remaining == 0);
332     g_assert (tag->metadata_last_block == FALSE);
333
334     /* The header of a flac metadata block is 4 bytes long:
335      * 1st bit: indicates whether this is the last metadata info block
336      * 7 next bits: 4 if vorbis comment block
337      * 24 next bits: size of the metadata to follow (big endian)
338      */
339     if (GST_BUFFER_SIZE (tag->buffer) < 4) {
340       goto cleanup;
341     }
342     is_last = (((GST_BUFFER_DATA (tag->buffer)[0]) & 0x80) == 0x80);
343     /* If we have metadata set on the element, the last metadata block 
344      * will be the vorbis comment block which we will build ourselves
345      */
346     if (is_last) {
347       (GST_BUFFER_DATA (tag->buffer)[0]) &= (~0x80);
348     }
349
350     type = (GST_BUFFER_DATA (tag->buffer)[0]) & 0x7F;
351     size = ((GST_BUFFER_DATA (tag->buffer)[1]) << 16)
352         | ((GST_BUFFER_DATA (tag->buffer)[2]) << 8)
353         | (GST_BUFFER_DATA (tag->buffer)[3]);
354
355     /* The 4 bytes long header isn't included in the metadata size */
356     tag->metadata_bytes_remaining = size + 4;
357     tag->metadata_last_block = is_last;
358
359     /* Metadata blocks of type 4 are vorbis comment blocks */
360     if (type == 0x04) {
361       tag->state = GST_FLAC_TAG_STATE_VC_METADATA_BLOCK;
362     } else {
363       tag->state = GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK;
364     }
365   }
366
367
368   /* Reads a metadata block */
369   if ((tag->state == GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK) ||
370       (tag->state == GST_FLAC_TAG_STATE_VC_METADATA_BLOCK)) {
371     GstBuffer *sub;
372     guint bytes_to_push;
373
374     g_assert (tag->metadata_bytes_remaining != 0);
375
376     bytes_to_push = min (tag->metadata_bytes_remaining,
377         GST_BUFFER_SIZE (tag->buffer));
378
379     sub = gst_buffer_create_sub (tag->buffer, 0, bytes_to_push);
380
381     if (tag->state == GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK) {
382       gst_pad_push (tag->srcpad, GST_DATA (sub));
383     } else {
384       if (tag->vorbiscomment == NULL) {
385         tag->vorbiscomment = sub;
386       } else {
387         GstBuffer *merge;
388
389         merge = gst_buffer_merge (tag->vorbiscomment, sub);
390         gst_buffer_unref (tag->vorbiscomment);
391         gst_buffer_unref (sub);
392         tag->vorbiscomment = merge;
393       }
394     }
395
396     tag->metadata_bytes_remaining -= (bytes_to_push);
397
398     if (GST_BUFFER_SIZE (tag->buffer) > bytes_to_push) {
399       GstBuffer *sub;
400
401       sub = gst_buffer_create_sub (tag->buffer, bytes_to_push,
402           GST_BUFFER_SIZE (tag->buffer) - bytes_to_push);
403       gst_buffer_unref (tag->buffer);
404
405       /* We make a copy because we need a writable buffer, and _create_sub
406        * sets the buffer it uses to read-only
407        */
408       tag->buffer = gst_buffer_copy (sub);
409       gst_buffer_unref (sub);
410
411       tag->state = GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK;
412     } else if (tag->metadata_bytes_remaining == 0) {
413       gst_buffer_unref (tag->buffer);
414       tag->buffer = NULL;
415       tag->state = GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK;
416       tag->buffer = NULL;
417     } else {
418       tag->state = GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK;
419       tag->buffer = NULL;
420     }
421   }
422
423   /* This state is mainly used to be able to stop as soon as we read
424    * a vorbiscomment block from the flac file if we are in an only output
425    * tags mode
426    */
427   if (tag->state == GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK) {
428     /* Check if in the previous iteration we read a vorbis comment metadata 
429      * block, and stop now if the user only wants to read tags
430      */
431     if (tag->vorbiscomment != NULL) {
432       /* We found some tags, try to parse them and notify the other elements
433        * that we encoutered some tags
434        */
435       tag->tags = gst_tag_list_from_vorbiscomment_buffer (tag->vorbiscomment,
436           GST_BUFFER_DATA (tag->vorbiscomment), 4, NULL);
437       if (tag->tags != NULL) {
438         gst_element_found_tags (GST_ELEMENT (tag), tag->tags);
439       }
440
441       gst_buffer_unref (tag->vorbiscomment);
442       tag->vorbiscomment = NULL;
443
444       if (tag->only_output_tags) {
445         send_eos (tag);
446         goto cleanup;
447       }
448     }
449
450     /* Skip to next state */
451     if (tag->metadata_last_block == FALSE) {
452       tag->state = GST_FLAC_TAG_STATE_METADATA_BLOCKS;
453     } else {
454       if (tag->only_output_tags) {
455         /* If we finished parsing the metadata blocks, we will never find any
456          * metadata, so just stop now
457          */
458         send_eos (tag);
459         goto cleanup;
460       } else {
461         tag->state = GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT;
462       }
463     }
464   }
465
466
467   /* Creates a vorbis comment block from the metadata which was set
468    * on the gstreamer element, and add it to the flac stream
469    */
470   if (tag->state == GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT) {
471     GstBuffer *buffer;
472     gint size;
473     const GstTagList *user_tags;
474     GstTagList *merged_tags;
475
476     g_assert (tag->only_output_tags == FALSE);
477
478     user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (tag));
479     merged_tags = gst_tag_list_merge (tag->tags, user_tags,
480         gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (tag)));
481
482     if (merged_tags == NULL) {
483       /* If we get a NULL list of tags, we must generate a padding block
484        * which is marked as the last metadata block, otherwise we'll
485        * end up with a corrupted flac file.
486        */
487       g_warning ("No tags found\n");
488       buffer = gst_buffer_new_and_alloc (12);
489       if (buffer == NULL) {
490         GST_ELEMENT_ERROR (tag, CORE, TOO_LAZY, (NULL),
491             ("Error creating 12-byte buffer for padding block"));
492       }
493       memset (GST_BUFFER_DATA (buffer), 0, GST_BUFFER_SIZE (buffer));
494       GST_BUFFER_DATA (buffer)[0] = 0x81;       /* 0x80 = Last metadata block, 
495                                                  * 0x01 = padding block
496                                                  */
497     } else {
498       guchar header[4];
499
500       memset (header, 0, sizeof (header));
501       header[0] = 0x84;         /* 0x80 = Last metadata block, 
502                                  * 0x04 = vorbiscomment block
503                                  */
504       buffer = gst_tag_list_to_vorbiscomment_buffer (merged_tags, header,
505           sizeof (header), NULL);
506       gst_tag_list_free (merged_tags);
507       if (buffer == NULL) {
508         GST_ELEMENT_ERROR (tag, CORE, TAG, (NULL),
509             ("Error converting tag list to vorbiscomment buffer"));
510         goto cleanup;
511       }
512       size = GST_BUFFER_SIZE (buffer) - 4;
513       if ((size > 0xFFFFFF) || (size < 0)) {
514         /* FLAC vorbis comment blocks are limited to 2^24 bytes, 
515          * while the vorbis specs allow more than that. Shouldn't 
516          * be a real world problem though
517          */
518         GST_ELEMENT_ERROR (tag, CORE, TAG, (NULL),
519             ("Vorbis comment of size %d too long", size));
520         goto cleanup;
521       }
522
523       /* Get rid of the framing bit at the end of the vorbiscomment buffer 
524        * if it exists since libFLAC seems to lose sync because of this
525        * bit in gstflacdec
526        */
527       if (GST_BUFFER_DATA (buffer)[GST_BUFFER_SIZE (buffer) - 1] == 1) {
528         GstBuffer *sub;
529
530         sub = gst_buffer_create_sub (buffer, 0, GST_BUFFER_SIZE (buffer) - 1);
531         gst_buffer_unref (buffer);
532         buffer = sub;
533       }
534     }
535
536     /* The 4 byte metadata block header isn't accounted for in the total
537      * size of the metadata block
538      */
539     size = GST_BUFFER_SIZE (buffer) - 4;
540
541     GST_BUFFER_DATA (buffer)[1] = ((size & 0xFF0000) >> 16);
542     GST_BUFFER_DATA (buffer)[2] = ((size & 0x00FF00) >> 8);
543     GST_BUFFER_DATA (buffer)[3] = (size & 0x0000FF);
544     gst_pad_push (tag->srcpad, GST_DATA (buffer));
545     tag->state = GST_FLAC_TAG_STATE_AUDIO_DATA;
546   }
547
548   /* The metadata blocks have been read, now we are reading audio data */
549   if (tag->state == GST_FLAC_TAG_STATE_AUDIO_DATA) {
550     gst_pad_push (tag->srcpad, GST_DATA (tag->buffer));
551     tag->buffer = NULL;
552   }
553
554 cleanup:
555   gst_object_unref (tag);
556 }
557
558
559 static GstStateChangeReturn
560 gst_flac_tag_change_state (GstElement * element, GstStateChange transition)
561 {
562   GstFlacTag *tag;
563
564   tag = GST_FLAC_TAG (element);
565
566   switch (transition) {
567     case GST_STATE_CHANGE_NULL_TO_READY:
568       break;
569     case GST_STATE_CHANGE_READY_TO_PAUSED:
570       break;
571     case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
572       /* do something to get out of the chain function faster */
573       break;
574     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
575       break;
576     case GST_STATE_CHANGE_PAUSED_TO_READY:
577       if (tag->buffer) {
578         gst_buffer_unref (tag->buffer);
579         tag->buffer = NULL;
580       }
581       if (tag->vorbiscomment) {
582         gst_buffer_unref (tag->vorbiscomment);
583         tag->vorbiscomment = NULL;
584       }
585       if (tag->tags) {
586         gst_tag_list_free (tag->tags);
587       }
588       tag->state = GST_FLAC_TAG_STATE_INIT;
589       break;
590     case GST_STATE_CHANGE_READY_TO_NULL:
591       break;
592   }
593
594   return parent_class->change_state (element, transition);
595 }