upload tizen1.0 source
[framework/multimedia/gst-plugins-good0.10.git] / gst / icydemux / gsticydemux.c
1 /* -*- Mode: C; tab-width: 2; indent-tabs-mode: t; c-basic-offset: 2 -*- */
2 /* Copyright 2005 Jan Schmidt <thaytan@mad.scientist.com>
3  *           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., 59 Temple Place - Suite 330,
19  * Boston, MA 02111-1307, USA.
20  */
21
22 /**
23  * SECTION:element-icydemux
24  *
25  * icydemux accepts data streams with ICY metadata at known intervals, as
26  * transmitted from an upstream element (usually read as response headers from
27  * an HTTP stream). The mime type of the data between the tag blocks is
28  * detected using typefind functions, and the appropriate output mime type set
29  * on outgoing buffers. 
30  *
31  * <refsect2>
32  * <title>Example launch line</title>
33  * |[
34  * gst-launch 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  * </refsect2>
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_class_init (GstICYDemuxClass * klass);
70 static void gst_icydemux_base_init (GstICYDemuxClass * klass);
71 static void gst_icydemux_init (GstICYDemux * icydemux);
72 static void gst_icydemux_dispose (GObject * object);
73
74 static GstFlowReturn gst_icydemux_chain (GstPad * pad, GstBuffer * buf);
75 static gboolean gst_icydemux_handle_event (GstPad * pad, GstEvent * event);
76
77 static gboolean gst_icydemux_add_srcpad (GstICYDemux * icydemux,
78     GstCaps * new_caps);
79 static gboolean gst_icydemux_remove_srcpad (GstICYDemux * icydemux);
80
81 static GstStateChangeReturn gst_icydemux_change_state (GstElement * element,
82     GstStateChange transition);
83 static gboolean gst_icydemux_sink_setcaps (GstPad * pad, GstCaps * caps);
84
85 static gboolean gst_icydemux_send_tag_event (GstICYDemux * icydemux,
86     GstTagList * taglist);
87
88 static GstElementClass *parent_class = NULL;
89
90 GType
91 gst_icydemux_get_type (void)
92 {
93   static GType plugin_type = 0;
94
95   if (!plugin_type) {
96     static const GTypeInfo plugin_info = {
97       sizeof (GstICYDemuxClass),
98       (GBaseInitFunc) gst_icydemux_base_init,
99       NULL,
100       (GClassInitFunc) gst_icydemux_class_init,
101       NULL,
102       NULL,
103       sizeof (GstICYDemux),
104       0,
105       (GInstanceInitFunc) gst_icydemux_init,
106     };
107     plugin_type = g_type_register_static (GST_TYPE_ELEMENT,
108         "GstICYDemux", &plugin_info, 0);
109   }
110   return plugin_type;
111 }
112
113 static void
114 gst_icydemux_base_init (GstICYDemuxClass * klass)
115 {
116   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
117
118   gst_element_class_add_pad_template (element_class,
119       gst_static_pad_template_get (&src_factory));
120   gst_element_class_add_pad_template (element_class,
121       gst_static_pad_template_get (&sink_factory));
122   gst_element_class_set_details_simple (element_class, "ICY tag demuxer",
123       "Codec/Demuxer/Metadata",
124       "Read and output ICY tags while demuxing the contents",
125       "Jan Schmidt <thaytan@mad.scientist.com>, "
126       "Michael Smith <msmith@fluendo.com>");
127 }
128
129 static void
130 gst_icydemux_class_init (GstICYDemuxClass * klass)
131 {
132   GObjectClass *gobject_class;
133   GstElementClass *gstelement_class;
134
135   gobject_class = (GObjectClass *) klass;
136   gstelement_class = (GstElementClass *) klass;
137
138   parent_class = g_type_class_ref (GST_TYPE_ELEMENT);
139
140   gobject_class->dispose = gst_icydemux_dispose;
141
142   gstelement_class->change_state = gst_icydemux_change_state;
143
144 }
145
146 static void
147 gst_icydemux_reset (GstICYDemux * icydemux)
148 {
149   /* Unknown at the moment (this is a fatal error if don't have a value by the
150    * time we get to our chain function)
151    */
152   icydemux->meta_interval = -1;
153   icydemux->remaining = 0;
154
155   icydemux->typefinding = TRUE;
156
157   gst_caps_replace (&(icydemux->src_caps), NULL);
158
159   gst_icydemux_remove_srcpad (icydemux);
160
161   if (icydemux->cached_tags) {
162     gst_tag_list_free (icydemux->cached_tags);
163     icydemux->cached_tags = NULL;
164   }
165
166   if (icydemux->cached_events) {
167     g_list_foreach (icydemux->cached_events,
168         (GFunc) gst_mini_object_unref, NULL);
169     g_list_free (icydemux->cached_events);
170     icydemux->cached_events = NULL;
171   }
172
173   if (icydemux->meta_adapter) {
174     gst_adapter_clear (icydemux->meta_adapter);
175     g_object_unref (icydemux->meta_adapter);
176     icydemux->meta_adapter = NULL;
177   }
178
179   if (icydemux->typefind_buf) {
180     gst_buffer_unref (icydemux->typefind_buf);
181     icydemux->typefind_buf = NULL;
182   }
183
184   if (icydemux->content_type) {
185     g_free (icydemux->content_type);
186     icydemux->content_type = NULL;
187   }
188 }
189
190 static void
191 gst_icydemux_init (GstICYDemux * icydemux)
192 {
193   GstElementClass *klass = GST_ELEMENT_GET_CLASS (icydemux);
194
195   icydemux->sinkpad =
196       gst_pad_new_from_template (gst_element_class_get_pad_template (klass,
197           "sink"), "sink");
198   gst_pad_set_chain_function (icydemux->sinkpad,
199       GST_DEBUG_FUNCPTR (gst_icydemux_chain));
200   gst_pad_set_event_function (icydemux->sinkpad,
201       GST_DEBUG_FUNCPTR (gst_icydemux_handle_event));
202   gst_pad_set_setcaps_function (icydemux->sinkpad,
203       GST_DEBUG_FUNCPTR (gst_icydemux_sink_setcaps));
204   gst_element_add_pad (GST_ELEMENT (icydemux), icydemux->sinkpad);
205
206   gst_icydemux_reset (icydemux);
207 }
208
209 static gboolean
210 gst_icydemux_sink_setcaps (GstPad * pad, GstCaps * caps)
211 {
212   GstICYDemux *icydemux = GST_ICYDEMUX (GST_PAD_PARENT (pad));
213   GstStructure *structure = gst_caps_get_structure (caps, 0);
214   const gchar *tmp;
215
216   if (!gst_structure_get_int (structure, "metadata-interval",
217           &icydemux->meta_interval))
218     return FALSE;
219
220   /* If incoming caps have the HTTP Content-Type, copy that over */
221   if ((tmp = gst_structure_get_string (structure, "content-type")))
222     icydemux->content_type = g_strdup (tmp);
223
224   /* We have a meta interval, so initialise the rest */
225   icydemux->remaining = icydemux->meta_interval;
226   icydemux->meta_remaining = 0;
227   return TRUE;
228 }
229
230 static void
231 gst_icydemux_dispose (GObject * object)
232 {
233   GstICYDemux *icydemux = GST_ICYDEMUX (object);
234
235   gst_icydemux_reset (icydemux);
236
237   G_OBJECT_CLASS (parent_class)->dispose (object);
238 }
239
240 static gboolean
241 gst_icydemux_add_srcpad (GstICYDemux * icydemux, GstCaps * new_caps)
242 {
243   if (icydemux->src_caps == NULL ||
244       !gst_caps_is_equal (new_caps, icydemux->src_caps)) {
245     gst_caps_replace (&(icydemux->src_caps), new_caps);
246     if (icydemux->srcpad != NULL) {
247       GST_DEBUG_OBJECT (icydemux, "Changing src pad caps to %" GST_PTR_FORMAT,
248           icydemux->src_caps);
249
250       gst_pad_set_caps (icydemux->srcpad, icydemux->src_caps);
251     }
252   } else {
253     /* Caps never changed */
254     gst_caps_unref (new_caps);
255   }
256
257   if (icydemux->srcpad == NULL) {
258     icydemux->srcpad =
259         gst_pad_new_from_template (gst_element_class_get_pad_template
260         (GST_ELEMENT_GET_CLASS (icydemux), "src"), "src");
261     g_return_val_if_fail (icydemux->srcpad != NULL, FALSE);
262
263     gst_pad_use_fixed_caps (icydemux->srcpad);
264
265     if (icydemux->src_caps)
266       gst_pad_set_caps (icydemux->srcpad, icydemux->src_caps);
267
268     GST_DEBUG_OBJECT (icydemux, "Adding src pad with caps %" GST_PTR_FORMAT,
269         icydemux->src_caps);
270
271     gst_pad_set_active (icydemux->srcpad, TRUE);
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
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 *
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
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_free (tags);
319   }
320
321   return TRUE;
322 }
323
324 static void
325 gst_icydemux_parse_and_send_tags (GstICYDemux * icydemux)
326 {
327   GstTagList *tags;
328   const guint8 *data;
329   int length, i;
330   gchar *buffer;
331   gchar **strings;
332
333   length = gst_adapter_available (icydemux->meta_adapter);
334
335   data = gst_adapter_peek (icydemux->meta_adapter, length);
336
337   /* Now, copy this to a buffer where we can NULL-terminate it to make things
338    * a bit easier, then do that parsing. */
339   buffer = g_strndup ((const gchar *) data, length);
340
341   tags = gst_tag_list_new ();
342   strings = g_strsplit (buffer, "';", 0);
343
344   for (i = 0; strings[i]; i++) {
345     if (!g_ascii_strncasecmp (strings[i], "StreamTitle=", 12)) {
346       char *title = gst_icydemux_unicodify (strings[i] + 13);
347
348       if (title && *title) {
349         gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_TITLE,
350             title, NULL);
351         g_free (title);
352       }
353     } else if (!g_ascii_strncasecmp (strings[i], "StreamUrl=", 10)) {
354       char *url = gst_icydemux_unicodify (strings[i] + 11);
355
356       if (url && *url) {
357         gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_HOMEPAGE,
358             url, NULL);
359         g_free (url);
360       }
361     }
362   }
363
364   g_strfreev (strings);
365   g_free (buffer);
366   gst_adapter_clear (icydemux->meta_adapter);
367
368   if (!gst_tag_list_is_empty (tags))
369     gst_icydemux_tag_found (icydemux, tags);
370   else
371     gst_tag_list_free (tags);
372 }
373
374 static gboolean
375 gst_icydemux_handle_event (GstPad * pad, GstEvent * event)
376 {
377   GstICYDemux *icydemux = GST_ICYDEMUX (GST_PAD_PARENT (pad));
378   gboolean result;
379
380   if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) {
381     GstTagList *tags;
382
383     gst_event_parse_tag (event, &tags);
384     result = gst_icydemux_tag_found (icydemux, gst_tag_list_copy (tags));
385     gst_event_unref (event);
386     return result;
387   }
388
389   if (icydemux->typefinding) {
390     switch (GST_EVENT_TYPE (event)) {
391       case GST_EVENT_FLUSH_STOP:
392         g_list_foreach (icydemux->cached_events,
393             (GFunc) gst_mini_object_unref, NULL);
394         g_list_free (icydemux->cached_events);
395         icydemux->cached_events = NULL;
396
397         return gst_pad_event_default (pad, event);
398       default:
399         icydemux->cached_events = g_list_append (icydemux->cached_events,
400             event);
401         return TRUE;
402     }
403   } else {
404     return gst_pad_event_default (pad, event);
405   }
406 }
407
408 static void
409 gst_icydemux_send_cached_events (GstICYDemux * icydemux)
410 {
411   GList *l;
412
413   for (l = icydemux->cached_events; l != NULL; l = l->next) {
414     GstEvent *event = GST_EVENT (l->data);
415
416     gst_pad_push_event (icydemux->srcpad, event);
417   }
418   g_list_free (icydemux->cached_events);
419   icydemux->cached_events = NULL;
420 }
421
422 static GstFlowReturn
423 gst_icydemux_typefind_or_forward (GstICYDemux * icydemux, GstBuffer * buf)
424 {
425   if (icydemux->typefinding) {
426     GstBuffer *tf_buf;
427     GstCaps *caps = NULL;
428     GstTypeFindProbability prob;
429
430     /* If we have a content-type from upstream, let's see if we can shortcut
431      * typefinding */
432     if (G_UNLIKELY (icydemux->content_type)) {
433       if (!g_ascii_strcasecmp (icydemux->content_type, "video/nsv")) {
434         GST_DEBUG ("We have a NSV stream");
435         caps = gst_caps_new_simple ("video/x-nsv", NULL);
436       } else {
437         GST_DEBUG ("Upstream Content-Type isn't supported");
438         g_free (icydemux->content_type);
439         icydemux->content_type = NULL;
440       }
441     }
442
443     if (icydemux->typefind_buf) {
444       icydemux->typefind_buf = gst_buffer_join (icydemux->typefind_buf, buf);
445     } else {
446       icydemux->typefind_buf = buf;
447     }
448
449     /* Only typefind if we haven't already got some caps */
450     if (caps == NULL) {
451       caps = gst_type_find_helper_for_buffer (GST_OBJECT (icydemux),
452           icydemux->typefind_buf, &prob);
453
454       if (caps == NULL) {
455         if (GST_BUFFER_SIZE (icydemux->typefind_buf) < ICY_TYPE_FIND_MAX_SIZE) {
456           /* Just break for more data */
457           return GST_FLOW_OK;
458         }
459
460         /* We failed typefind */
461         GST_ELEMENT_ERROR (icydemux, STREAM, TYPE_NOT_FOUND, (NULL),
462             ("No caps found for contents within an ICY stream"));
463         gst_buffer_unref (icydemux->typefind_buf);
464         icydemux->typefind_buf = NULL;
465         return GST_FLOW_ERROR;
466       }
467     }
468
469     if (!gst_icydemux_add_srcpad (icydemux, caps)) {
470       GST_DEBUG_OBJECT (icydemux, "Failed to add srcpad");
471       gst_caps_unref (caps);
472       gst_buffer_unref (icydemux->typefind_buf);
473       icydemux->typefind_buf = NULL;
474       return GST_FLOW_ERROR;
475     }
476     gst_caps_unref (caps);
477
478     if (icydemux->cached_events) {
479       gst_icydemux_send_cached_events (icydemux);
480     }
481
482     if (icydemux->cached_tags) {
483       gst_icydemux_send_tag_event (icydemux, icydemux->cached_tags);
484       icydemux->cached_tags = NULL;
485     }
486
487     /* Move onto streaming: call ourselves recursively with the typefind buffer
488      * to get that forwarded. */
489     icydemux->typefinding = FALSE;
490
491     tf_buf = icydemux->typefind_buf;
492     icydemux->typefind_buf = NULL;
493     return gst_icydemux_typefind_or_forward (icydemux, tf_buf);
494   } else {
495     if (G_UNLIKELY (icydemux->srcpad == NULL)) {
496       gst_buffer_unref (buf);
497       return GST_FLOW_ERROR;
498     }
499
500     buf = gst_buffer_make_metadata_writable (buf);
501     gst_buffer_set_caps (buf, icydemux->src_caps);
502
503     /* Most things don't care, and it's a pain to track (we should preserve a
504      * 0 offset on the first buffer though if it's there, for id3demux etc.) */
505     if (GST_BUFFER_OFFSET (buf) != 0) {
506       GST_BUFFER_OFFSET (buf) = GST_BUFFER_OFFSET_NONE;
507     }
508
509     return gst_pad_push (icydemux->srcpad, buf);
510   }
511 }
512
513 static void
514 gst_icydemux_add_meta (GstICYDemux * icydemux, GstBuffer * buf)
515 {
516   if (!icydemux->meta_adapter)
517     icydemux->meta_adapter = gst_adapter_new ();
518
519   gst_adapter_push (icydemux->meta_adapter, buf);
520 }
521
522 static GstFlowReturn
523 gst_icydemux_chain (GstPad * pad, GstBuffer * buf)
524 {
525   GstICYDemux *icydemux;
526   guint size, chunk, offset;
527   GstBuffer *sub;
528   GstFlowReturn ret = GST_FLOW_OK;
529
530   icydemux = GST_ICYDEMUX (GST_PAD_PARENT (pad));
531
532   if (G_UNLIKELY (icydemux->meta_interval < 0))
533     goto not_negotiated;
534
535   if (icydemux->meta_interval == 0) {
536     ret = gst_icydemux_typefind_or_forward (icydemux, buf);
537     goto done;
538   }
539
540   /* Go through the buffer, chopping it into appropriate chunks. Forward as
541    * tags or buffers, as appropriate
542    */
543   size = GST_BUFFER_SIZE (buf);
544   offset = 0;
545   while (size) {
546     if (icydemux->remaining) {
547       chunk = (size <= icydemux->remaining) ? size : icydemux->remaining;
548       sub = gst_buffer_create_sub (buf, offset, chunk);
549       offset += chunk;
550       icydemux->remaining -= chunk;
551       size -= chunk;
552
553       /* This buffer goes onto typefinding, and/or directly pushed out */
554       ret = gst_icydemux_typefind_or_forward (icydemux, sub);
555       if (ret != GST_FLOW_OK)
556         goto done;
557     } else if (icydemux->meta_remaining) {
558       chunk = (size <= icydemux->meta_remaining) ?
559           size : icydemux->meta_remaining;
560       sub = gst_buffer_create_sub (buf, offset, chunk);
561       gst_icydemux_add_meta (icydemux, sub);
562
563       offset += chunk;
564       icydemux->meta_remaining -= chunk;
565       size -= chunk;
566
567       if (icydemux->meta_remaining == 0) {
568         /* Parse tags from meta_adapter, send off as tag messages */
569         GST_DEBUG_OBJECT (icydemux, "No remaining metadata, parsing for tags");
570         gst_icydemux_parse_and_send_tags (icydemux);
571
572         icydemux->remaining = icydemux->meta_interval;
573       }
574     } else {
575       /* We need to read a single byte (always safe at this point in the loop)
576        * to figure out how many bytes of metadata exist. 
577        * The 'spec' tells us to read 16 * (byte_value) bytes of metadata after
578        * this (zero is common, and means the metadata hasn't changed).
579        */
580       icydemux->meta_remaining = 16 * GST_BUFFER_DATA (buf)[offset];
581       if (icydemux->meta_remaining == 0)
582         icydemux->remaining = icydemux->meta_interval;
583
584       offset += 1;
585       size -= 1;
586     }
587   }
588
589 done:
590   gst_buffer_unref (buf);
591
592   return ret;
593
594   /* ERRORS */
595 not_negotiated:
596   {
597     GST_WARNING_OBJECT (icydemux, "meta_interval not set, buffer probably had "
598         "no caps set. Try enabling iradio-mode on the http source element");
599     gst_buffer_unref (buf);
600     return GST_FLOW_NOT_NEGOTIATED;
601   }
602 }
603
604 static GstStateChangeReturn
605 gst_icydemux_change_state (GstElement * element, GstStateChange transition)
606 {
607   GstStateChangeReturn ret;
608   GstICYDemux *icydemux = GST_ICYDEMUX (element);
609
610   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
611
612   switch (transition) {
613     case GST_STATE_CHANGE_PAUSED_TO_READY:
614       gst_icydemux_reset (icydemux);
615       break;
616     default:
617       break;
618   }
619   return ret;
620 }
621
622 /* takes ownership of tag list */
623 static gboolean
624 gst_icydemux_send_tag_event (GstICYDemux * icydemux, GstTagList * tags)
625 {
626   GstEvent *event;
627
628   gst_element_post_message (GST_ELEMENT (icydemux),
629       gst_message_new_tag (GST_OBJECT (icydemux), gst_tag_list_copy (tags)));
630
631   event = gst_event_new_tag (tags);
632   GST_EVENT_TIMESTAMP (event) = 0;
633
634   GST_DEBUG_OBJECT (icydemux, "Sending tag event on src pad");
635   return gst_pad_push_event (icydemux->srcpad, event);
636
637 }
638
639 static gboolean
640 plugin_init (GstPlugin * plugin)
641 {
642   GST_DEBUG_CATEGORY_INIT (icydemux_debug, "icydemux", 0,
643       "GStreamer ICY tag demuxer");
644
645   return gst_element_register (plugin, "icydemux",
646       GST_RANK_PRIMARY, GST_TYPE_ICYDEMUX);
647 }
648
649 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
650     GST_VERSION_MINOR,
651     "icydemux",
652     "Demux ICY tags from a stream",
653     plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN)