• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
3  *           2006 Michael Smith <msmith@fluendo.com>
4  * Copyright (C) 2003-2004 Benjamin Otte <otte@gnome.org>
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., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21 
22 /**
23  * SECTION:element-icydemux
24  * @title: icydemux
25  *
26  * icydemux accepts data streams with ICY metadata at known intervals, as
27  * transmitted from an upstream element (usually read as response headers from
28  * an HTTP stream). The mime type of the data between the tag blocks is
29  * detected using typefind functions, and the appropriate output mime type set
30  * on outgoing buffers.
31  *
32  * ## Example launch line
33  * |[
34  * gst-launch-1.0 souphttpsrc location=http://some.server/ iradio-mode=true ! icydemux ! fakesink -t
35  * ]| This pipeline should read any available ICY tag information and output it.
36  * The contents of the stream should be detected, and the appropriate mime
37  * type set on buffers produced from icydemux. (Using gnomevfssrc, neonhttpsrc
38  * or giosrc instead of souphttpsrc should also work.)
39  *
40  */
41 #ifdef HAVE_CONFIG_H
42 #include "config.h"
43 #endif
44 #include <gst/gst.h>
45 #include <gst/gst-i18n-plugin.h>
46 #include <gst/tag/tag.h>
47 
48 #include "gsticydemux.h"
49 
50 #include <string.h>
51 
52 #define ICY_TYPE_FIND_MAX_SIZE (40*1024)
53 
54 GST_DEBUG_CATEGORY_STATIC (icydemux_debug);
55 #define GST_CAT_DEFAULT (icydemux_debug)
56 
57 static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE ("sink",
58     GST_PAD_SINK,
59     GST_PAD_ALWAYS,
60     GST_STATIC_CAPS ("application/x-icy, metadata-interval = (int)[0, MAX]")
61     );
62 
63 static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE ("src",
64     GST_PAD_SRC,
65     GST_PAD_SOMETIMES,
66     GST_STATIC_CAPS ("ANY")
67     );
68 
69 static void gst_icydemux_dispose (GObject * object);
70 
71 static GstFlowReturn gst_icydemux_chain (GstPad * pad, GstObject * parent,
72     GstBuffer * buf);
73 static gboolean gst_icydemux_handle_event (GstPad * pad, GstObject * parent,
74     GstEvent * event);
75 
76 static gboolean gst_icydemux_add_srcpad (GstICYDemux * icydemux,
77     GstCaps * new_caps);
78 static gboolean gst_icydemux_remove_srcpad (GstICYDemux * icydemux);
79 
80 static GstStateChangeReturn gst_icydemux_change_state (GstElement * element,
81     GstStateChange transition);
82 static gboolean gst_icydemux_sink_setcaps (GstPad * pad, GstCaps * caps);
83 
84 static gboolean gst_icydemux_send_tag_event (GstICYDemux * icydemux,
85     GstTagList * taglist);
86 
87 
88 #define gst_icydemux_parent_class parent_class
89 G_DEFINE_TYPE (GstICYDemux, gst_icydemux, GST_TYPE_ELEMENT);
90 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (icydemux, "icydemux",
91     GST_RANK_PRIMARY, GST_TYPE_ICYDEMUX,
92     GST_DEBUG_CATEGORY_INIT (icydemux_debug, "icydemux", 0,
93         "GStreamer ICY tag demuxer");
94     );
95 static void
gst_icydemux_class_init(GstICYDemuxClass * klass)96 gst_icydemux_class_init (GstICYDemuxClass * klass)
97 {
98   GObjectClass *gobject_class;
99   GstElementClass *gstelement_class;
100 
101   gobject_class = (GObjectClass *) klass;
102   gstelement_class = (GstElementClass *) klass;
103 
104   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
105 
106   gobject_class->dispose = gst_icydemux_dispose;
107 
108   gstelement_class->change_state = gst_icydemux_change_state;
109 
110   gst_element_class_add_static_pad_template (gstelement_class, &src_factory);
111   gst_element_class_add_static_pad_template (gstelement_class, &sink_factory);
112 
113   gst_element_class_set_static_metadata (gstelement_class, "ICY tag demuxer",
114       "Codec/Demuxer/Metadata",
115       "Read and output ICY tags while demuxing the contents",
116       "Jan Schmidt <thaytan@mad.scientist.com>, "
117       "Michael Smith <msmith@fluendo.com>");
118 }
119 
120 static void
gst_icydemux_reset(GstICYDemux * icydemux)121 gst_icydemux_reset (GstICYDemux * icydemux)
122 {
123   /* Unknown at the moment (this is a fatal error if don't have a value by the
124    * time we get to our chain function)
125    */
126   icydemux->meta_interval = -1;
127   icydemux->remaining = 0;
128 
129   icydemux->typefinding = TRUE;
130 
131   gst_caps_replace (&(icydemux->src_caps), NULL);
132 
133   gst_icydemux_remove_srcpad (icydemux);
134 
135   if (icydemux->cached_tags) {
136     gst_tag_list_unref (icydemux->cached_tags);
137     icydemux->cached_tags = NULL;
138   }
139 
140   if (icydemux->cached_events) {
141     g_list_foreach (icydemux->cached_events,
142         (GFunc) gst_mini_object_unref, NULL);
143     g_list_free (icydemux->cached_events);
144     icydemux->cached_events = NULL;
145   }
146 
147   if (icydemux->meta_adapter) {
148     gst_adapter_clear (icydemux->meta_adapter);
149     g_object_unref (icydemux->meta_adapter);
150     icydemux->meta_adapter = NULL;
151   }
152 
153   if (icydemux->typefind_buf) {
154     gst_buffer_unref (icydemux->typefind_buf);
155     icydemux->typefind_buf = NULL;
156   }
157 
158   if (icydemux->content_type) {
159     g_free (icydemux->content_type);
160     icydemux->content_type = NULL;
161   }
162 }
163 
164 static void
gst_icydemux_init(GstICYDemux * icydemux)165 gst_icydemux_init (GstICYDemux * icydemux)
166 {
167   GstElementClass *klass = GST_ELEMENT_GET_CLASS (icydemux);
168 
169   icydemux->sinkpad =
170       gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
171           "sink"), "sink");
172   gst_pad_set_chain_function (icydemux->sinkpad,
173       GST_DEBUG_FUNCPTR (gst_icydemux_chain));
174   gst_pad_set_event_function (icydemux->sinkpad,
175       GST_DEBUG_FUNCPTR (gst_icydemux_handle_event));
176   gst_element_add_pad (GST_ELEMENT (icydemux), icydemux->sinkpad);
177 
178   gst_icydemux_reset (icydemux);
179 }
180 
181 static gboolean
gst_icydemux_sink_setcaps(GstPad * pad,GstCaps * caps)182 gst_icydemux_sink_setcaps (GstPad * pad, GstCaps * caps)
183 {
184   GstICYDemux *icydemux = GST_ICYDEMUX (GST_PAD_PARENT (pad));
185   GstStructure *structure = gst_caps_get_structure (caps, 0);
186   const gchar *tmp;
187 
188   if (!gst_structure_get_int (structure, "metadata-interval",
189           &icydemux->meta_interval))
190     return FALSE;
191 
192   /* If incoming caps have the HTTP Content-Type, copy that over */
193   if ((tmp = gst_structure_get_string (structure, "content-type")))
194     icydemux->content_type = g_strdup (tmp);
195 
196   /* We have a meta interval, so initialise the rest */
197   icydemux->remaining = icydemux->meta_interval;
198   icydemux->meta_remaining = 0;
199   return TRUE;
200 }
201 
202 static void
gst_icydemux_dispose(GObject * object)203 gst_icydemux_dispose (GObject * object)
204 {
205   GstICYDemux *icydemux = GST_ICYDEMUX (object);
206 
207   gst_icydemux_reset (icydemux);
208 
209   G_OBJECT_CLASS (parent_class)->dispose (object);
210 }
211 
212 typedef struct
213 {
214   GstCaps *caps;
215   GstPad *pad;
216 } CopyStickyEventsData;
217 
218 static gboolean
copy_sticky_events(GstPad * pad,GstEvent ** event,gpointer user_data)219 copy_sticky_events (GstPad * pad, GstEvent ** event, gpointer user_data)
220 {
221   CopyStickyEventsData *data = user_data;
222 
223   if (GST_EVENT_TYPE (*event) >= GST_EVENT_CAPS && data->caps) {
224     gst_pad_set_caps (data->pad, data->caps);
225     data->caps = NULL;
226   }
227 
228   if (GST_EVENT_TYPE (*event) != GST_EVENT_CAPS)
229     gst_pad_push_event (data->pad, gst_event_ref (*event));
230 
231   return TRUE;
232 }
233 
234 static gboolean
gst_icydemux_add_srcpad(GstICYDemux * icydemux,GstCaps * new_caps)235 gst_icydemux_add_srcpad (GstICYDemux * icydemux, GstCaps * new_caps)
236 {
237   if (icydemux->src_caps == NULL ||
238       !gst_caps_is_equal (new_caps, icydemux->src_caps)) {
239     gst_caps_replace (&(icydemux->src_caps), new_caps);
240     if (icydemux->srcpad != NULL) {
241       GST_DEBUG_OBJECT (icydemux, "Changing src pad caps to %" GST_PTR_FORMAT,
242           icydemux->src_caps);
243 
244       gst_pad_set_caps (icydemux->srcpad, icydemux->src_caps);
245     }
246   } else {
247     /* Caps never changed */
248     gst_caps_unref (new_caps);
249   }
250 
251   if (icydemux->srcpad == NULL) {
252     CopyStickyEventsData data;
253 
254     icydemux->srcpad =
255         gst_pad_new_from_template (gst_element_class_get_pad_template
256         (GST_ELEMENT_GET_CLASS (icydemux), "src"), "src");
257     g_return_val_if_fail (icydemux->srcpad != NULL, FALSE);
258 
259     gst_pad_use_fixed_caps (icydemux->srcpad);
260     gst_pad_set_active (icydemux->srcpad, TRUE);
261 
262     data.pad = icydemux->srcpad;
263     data.caps = icydemux->src_caps;
264     gst_pad_sticky_events_foreach (icydemux->sinkpad, copy_sticky_events,
265         &data);
266     if (data.caps)
267       gst_pad_set_caps (data.pad, data.caps);
268 
269     GST_DEBUG_OBJECT (icydemux, "Adding src pad with caps %" GST_PTR_FORMAT,
270         icydemux->src_caps);
271 
272     if (!(gst_element_add_pad (GST_ELEMENT (icydemux), icydemux->srcpad)))
273       return FALSE;
274     gst_element_no_more_pads (GST_ELEMENT (icydemux));
275   }
276 
277   return TRUE;
278 }
279 
280 static gboolean
gst_icydemux_remove_srcpad(GstICYDemux * icydemux)281 gst_icydemux_remove_srcpad (GstICYDemux * icydemux)
282 {
283   gboolean res = TRUE;
284 
285   if (icydemux->srcpad != NULL) {
286     res = gst_element_remove_pad (GST_ELEMENT (icydemux), icydemux->srcpad);
287     g_return_val_if_fail (res != FALSE, FALSE);
288     icydemux->srcpad = NULL;
289   }
290 
291   return res;
292 };
293 
294 static gchar *
gst_icydemux_unicodify(const gchar * str)295 gst_icydemux_unicodify (const gchar * str)
296 {
297   const gchar *env_vars[] = { "GST_ICY_TAG_ENCODING",
298     "GST_TAG_ENCODING", NULL
299   };
300 
301   return gst_tag_freeform_string_to_utf8 (str, -1, env_vars);
302 }
303 
304 /* takes ownership of tag list */
305 static gboolean
gst_icydemux_tag_found(GstICYDemux * icydemux,GstTagList * tags)306 gst_icydemux_tag_found (GstICYDemux * icydemux, GstTagList * tags)
307 {
308   /* send the tag event if we have finished typefinding and have a src pad */
309   if (icydemux->srcpad)
310     return gst_icydemux_send_tag_event (icydemux, tags);
311 
312   /* if we haven't a source pad yet, cache the tags */
313   if (!icydemux->cached_tags) {
314     icydemux->cached_tags = tags;
315   } else {
316     gst_tag_list_insert (icydemux->cached_tags, tags,
317         GST_TAG_MERGE_REPLACE_ALL);
318     gst_tag_list_unref (tags);
319   }
320 
321   return TRUE;
322 }
323 
324 static void
gst_icydemux_parse_and_send_tags(GstICYDemux * icydemux)325 gst_icydemux_parse_and_send_tags (GstICYDemux * icydemux)
326 {
327   GstTagList *tags;
328   const guint8 *data;
329   int length, i;
330   gboolean tags_found = FALSE;
331   gchar *buffer;
332   gchar **strings;
333 
334   length = gst_adapter_available (icydemux->meta_adapter);
335 
336   data = gst_adapter_map (icydemux->meta_adapter, length);
337 
338   /* Now, copy this to a buffer where we can NULL-terminate it to make things
339    * a bit easier, then do that parsing. */
340   buffer = g_strndup ((const gchar *) data, length);
341 
342   tags = gst_tag_list_new_empty ();
343   strings = g_strsplit (buffer, "';", 0);
344 
345   for (i = 0; strings[i]; i++) {
346     if (!g_ascii_strncasecmp (strings[i], "StreamTitle=", 12)) {
347       char *title = gst_icydemux_unicodify (strings[i] + 13);
348       tags_found = TRUE;
349 
350       if (title && *title) {
351         gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE,
352             title, NULL);
353         g_free (title);
354       }
355     } else if (!g_ascii_strncasecmp (strings[i], "StreamUrl=", 10)) {
356       char *url = gst_icydemux_unicodify (strings[i] + 11);
357       tags_found = TRUE;
358 
359       if (url && *url) {
360         gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_HOMEPAGE,
361             url, NULL);
362         g_free (url);
363       }
364     }
365   }
366 
367   g_strfreev (strings);
368   g_free (buffer);
369   gst_adapter_unmap (icydemux->meta_adapter);
370   gst_adapter_flush (icydemux->meta_adapter, length);
371 
372   if (tags_found)
373     gst_icydemux_tag_found (icydemux, tags);
374   else
375     gst_tag_list_unref (tags);
376 }
377 
378 static gboolean
gst_icydemux_handle_event(GstPad * pad,GstObject * parent,GstEvent * event)379 gst_icydemux_handle_event (GstPad * pad, GstObject * parent, GstEvent * event)
380 {
381   GstICYDemux *icydemux = GST_ICYDEMUX (parent);
382   gboolean result;
383 
384   switch (GST_EVENT_TYPE (event)) {
385     case GST_EVENT_TAG:
386     {
387       GstTagList *tags;
388 
389       gst_event_parse_tag (event, &tags);
390       result = gst_icydemux_tag_found (icydemux, gst_tag_list_copy (tags));
391       gst_event_unref (event);
392       return result;
393     }
394     case GST_EVENT_CAPS:
395     {
396       GstCaps *caps;
397 
398       gst_event_parse_caps (event, &caps);
399       result = gst_icydemux_sink_setcaps (pad, caps);
400       gst_event_unref (event);
401       return result;
402     }
403     default:
404       break;
405   }
406 
407   if (icydemux->typefinding) {
408     switch (GST_EVENT_TYPE (event)) {
409       case GST_EVENT_FLUSH_STOP:
410         g_list_foreach (icydemux->cached_events,
411             (GFunc) gst_mini_object_unref, NULL);
412         g_list_free (icydemux->cached_events);
413         icydemux->cached_events = NULL;
414 
415         return gst_pad_event_default (pad, parent, event);
416       default:
417         if (!GST_EVENT_IS_STICKY (event)) {
418           icydemux->cached_events =
419               g_list_append (icydemux->cached_events, event);
420         } else {
421           gst_event_unref (event);
422         }
423         return TRUE;
424     }
425   } else {
426     return gst_pad_event_default (pad, parent, event);
427   }
428 }
429 
430 static void
gst_icydemux_send_cached_events(GstICYDemux * icydemux)431 gst_icydemux_send_cached_events (GstICYDemux * icydemux)
432 {
433   GList *l;
434 
435   for (l = icydemux->cached_events; l != NULL; l = l->next) {
436     GstEvent *event = GST_EVENT (l->data);
437 
438     gst_pad_push_event (icydemux->srcpad, event);
439   }
440   g_list_free (icydemux->cached_events);
441   icydemux->cached_events = NULL;
442 }
443 
444 static GstFlowReturn
gst_icydemux_typefind_or_forward(GstICYDemux * icydemux,GstBuffer * buf)445 gst_icydemux_typefind_or_forward (GstICYDemux * icydemux, GstBuffer * buf)
446 {
447   if (icydemux->typefinding) {
448     GstBuffer *tf_buf;
449     GstCaps *caps = NULL;
450     GstTypeFindProbability prob;
451 
452     /* If we have a content-type from upstream, let's see if we can shortcut
453      * typefinding */
454     if (G_UNLIKELY (icydemux->content_type)) {
455       if (!g_ascii_strcasecmp (icydemux->content_type, "video/nsv")) {
456         GST_DEBUG ("We have a NSV stream");
457         caps = gst_caps_new_empty_simple ("video/x-nsv");
458       } else {
459         GST_DEBUG ("Upstream Content-Type isn't supported");
460         g_free (icydemux->content_type);
461         icydemux->content_type = NULL;
462       }
463     }
464 
465     if (icydemux->typefind_buf) {
466       icydemux->typefind_buf = gst_buffer_append (icydemux->typefind_buf, buf);
467     } else {
468       icydemux->typefind_buf = buf;
469     }
470 
471     /* Only typefind if we haven't already got some caps */
472     if (caps == NULL) {
473       caps = gst_type_find_helper_for_buffer (GST_OBJECT (icydemux),
474           icydemux->typefind_buf, &prob);
475 
476       if (caps == NULL) {
477         if (gst_buffer_get_size (icydemux->typefind_buf) <
478             ICY_TYPE_FIND_MAX_SIZE) {
479           /* Just break for more data */
480           return GST_FLOW_OK;
481         }
482 
483         /* We failed typefind */
484         GST_ELEMENT_ERROR (icydemux, STREAM, TYPE_NOT_FOUND, (NULL),
485             ("No caps found for contents within an ICY stream"));
486         gst_buffer_unref (icydemux->typefind_buf);
487         icydemux->typefind_buf = NULL;
488         return GST_FLOW_ERROR;
489       }
490     }
491 
492     if (!gst_icydemux_add_srcpad (icydemux, caps)) {
493       GST_DEBUG_OBJECT (icydemux, "Failed to add srcpad");
494       gst_caps_unref (caps);
495       gst_buffer_unref (icydemux->typefind_buf);
496       icydemux->typefind_buf = NULL;
497       return GST_FLOW_ERROR;
498     }
499     gst_caps_unref (caps);
500 
501     if (icydemux->cached_events) {
502       gst_icydemux_send_cached_events (icydemux);
503     }
504 
505     if (icydemux->cached_tags) {
506       gst_icydemux_send_tag_event (icydemux, icydemux->cached_tags);
507       icydemux->cached_tags = NULL;
508     }
509 
510     /* Move onto streaming: call ourselves recursively with the typefind buffer
511      * to get that forwarded. */
512     icydemux->typefinding = FALSE;
513 
514     tf_buf = icydemux->typefind_buf;
515     icydemux->typefind_buf = NULL;
516     return gst_icydemux_typefind_or_forward (icydemux, tf_buf);
517   } else {
518     if (G_UNLIKELY (icydemux->srcpad == NULL)) {
519       gst_buffer_unref (buf);
520       return GST_FLOW_ERROR;
521     }
522 
523     buf = gst_buffer_make_writable (buf);
524 
525     /* Most things don't care, and it's a pain to track (we should preserve a
526      * 0 offset on the first buffer though if it's there, for id3demux etc.) */
527     if (GST_BUFFER_OFFSET (buf) != 0) {
528       GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE;
529     }
530 
531     return gst_pad_push (icydemux->srcpad, buf);
532   }
533 }
534 
535 static void
gst_icydemux_add_meta(GstICYDemux * icydemux,GstBuffer * buf)536 gst_icydemux_add_meta (GstICYDemux * icydemux, GstBuffer * buf)
537 {
538   if (!icydemux->meta_adapter)
539     icydemux->meta_adapter = gst_adapter_new ();
540 
541   gst_adapter_push (icydemux->meta_adapter, buf);
542 }
543 
544 static GstFlowReturn
gst_icydemux_chain(GstPad * pad,GstObject * parent,GstBuffer * buf)545 gst_icydemux_chain (GstPad * pad, GstObject * parent, GstBuffer * buf)
546 {
547   GstICYDemux *icydemux;
548   guint size, chunk, offset;
549   GstBuffer *sub;
550   GstFlowReturn ret = GST_FLOW_OK;
551 
552   icydemux = GST_ICYDEMUX (parent);
553 
554   if (G_UNLIKELY (icydemux->meta_interval < 0))
555     goto not_negotiated;
556 
557   if (icydemux->meta_interval == 0) {
558     ret = gst_icydemux_typefind_or_forward (icydemux, buf);
559     buf = NULL;
560     goto done;
561   }
562 
563   /* Go through the buffer, chopping it into appropriate chunks. Forward as
564    * tags or buffers, as appropriate
565    */
566   size = gst_buffer_get_size (buf);
567   offset = 0;
568   while (size) {
569     if (icydemux->remaining) {
570       chunk = (size <= icydemux->remaining) ? size : icydemux->remaining;
571       if (offset == 0 && chunk == size) {
572         sub = buf;
573         buf = NULL;
574       } else {
575         sub = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset, chunk);
576       }
577       offset += chunk;
578       icydemux->remaining -= chunk;
579       size -= chunk;
580 
581       /* This buffer goes onto typefinding, and/or directly pushed out */
582       ret = gst_icydemux_typefind_or_forward (icydemux, sub);
583       if (ret != GST_FLOW_OK)
584         goto done;
585     } else if (icydemux->meta_remaining) {
586       chunk = (size <= icydemux->meta_remaining) ?
587           size : icydemux->meta_remaining;
588       sub = gst_buffer_copy_region (buf, GST_BUFFER_COPY_ALL, offset, chunk);
589       gst_icydemux_add_meta (icydemux, sub);
590 
591       offset += chunk;
592       icydemux->meta_remaining -= chunk;
593       size -= chunk;
594 
595       if (icydemux->meta_remaining == 0) {
596         /* Parse tags from meta_adapter, send off as tag messages */
597         GST_DEBUG_OBJECT (icydemux, "No remaining metadata, parsing for tags");
598         gst_icydemux_parse_and_send_tags (icydemux);
599 
600         icydemux->remaining = icydemux->meta_interval;
601       }
602     } else {
603       guint8 byte;
604       /* We need to read a single byte (always safe at this point in the loop)
605        * to figure out how many bytes of metadata exist.
606        * The 'spec' tells us to read 16 * (byte_value) bytes of metadata after
607        * this (zero is common, and means the metadata hasn't changed).
608        */
609       gst_buffer_extract (buf, offset, &byte, 1);
610       icydemux->meta_remaining = 16 * byte;
611       if (icydemux->meta_remaining == 0)
612         icydemux->remaining = icydemux->meta_interval;
613 
614       offset += 1;
615       size -= 1;
616     }
617   }
618 
619 done:
620   if (buf)
621     gst_buffer_unref (buf);
622 
623   return ret;
624 
625   /* ERRORS */
626 not_negotiated:
627   {
628     GST_WARNING_OBJECT (icydemux, "meta_interval not set, buffer probably had "
629         "no caps set. Try enabling iradio-mode on the http source element");
630     gst_buffer_unref (buf);
631     return GST_FLOW_NOT_NEGOTIATED;
632   }
633 }
634 
635 static GstStateChangeReturn
gst_icydemux_change_state(GstElement * element,GstStateChange transition)636 gst_icydemux_change_state (GstElement * element, GstStateChange transition)
637 {
638   GstStateChangeReturn ret;
639   GstICYDemux *icydemux = GST_ICYDEMUX (element);
640 
641   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
642 
643   switch (transition) {
644     case GST_STATE_CHANGE_PAUSED_TO_READY:
645       gst_icydemux_reset (icydemux);
646       break;
647     default:
648       break;
649   }
650   return ret;
651 }
652 
653 /* takes ownership of tag list */
654 static gboolean
gst_icydemux_send_tag_event(GstICYDemux * icydemux,GstTagList * tags)655 gst_icydemux_send_tag_event (GstICYDemux * icydemux, GstTagList * tags)
656 {
657   GstEvent *event;
658 
659   event = gst_event_new_tag (tags);
660 
661   GST_DEBUG_OBJECT (icydemux, "Sending tag event on src pad");
662   return gst_pad_push_event (icydemux->srcpad, event);
663 
664 }
665 
666 static gboolean
plugin_init(GstPlugin * plugin)667 plugin_init (GstPlugin * plugin)
668 {
669   return GST_ELEMENT_REGISTER (icydemux, plugin);
670 
671 }
672 
673 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
674     GST_VERSION_MINOR,
675     icydemux,
676     "Demux ICY tags from a stream",
677     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)
678