2 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
14 * You should have received a copy of the GNU Library General Public
15 * License along with this library; if not, write to the
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 * Boston, MA 02111-1307, USA.
24 #include <glib/gi18n.h>
27 #include <gst/gsterror.h>
28 #include <gst/gstplugin.h>
29 #include <gst/interfaces/xoverlay.h>
31 //#include <gst/pbutils/pbutils.h>
32 #include "gstplaybinmaemo.h"
35 GST_DEBUG_CATEGORY_STATIC (gst_play_bin_maemo_debug);
36 #define GST_CAT_DEFAULT gst_play_bin_maemo_debug
38 #define DEFAULT_VOLUME 10
39 #define DEFAULT_XID -1
47 ARG_QUEUE_MIN_THRESHOLD,
53 static const GstElementDetails gst_play_bin_maemo_details =
54 GST_ELEMENT_DETAILS("Nuv demuxer",
56 "Autoplug and play media from an uri used on maemo plataform",
57 "Renato Araujo Oliveira Filho <renato.filho@indt.org.br>");
59 static void gst_play_bin_maemo_dispose (GObject * object);
60 static void gst_play_bin_maemo_finalize (GObject * object);
61 static void gst_play_bin_maemo_set_property (GObject * object, guint prop_id,
62 const GValue * value, GParamSpec * spec);
63 static void gst_play_bin_maemo_get_property (GObject * object, guint prop_id,
64 GValue * value, GParamSpec * spec);
65 static GstStateChangeReturn
66 gst_play_bin_maemo_change_state (GstElement *element,
67 GstStateChange transition);
68 static gboolean factory_filter_sinks (GstPluginFeature *feature,
69 GstPlayBinMaemo *pbm);
70 static gint compare_ranks (GstPluginFeature * f1,
71 GstPluginFeature * f2);
72 static GList *find_compatibles (GstPlayBinMaemo *pbm,
74 static GstPad *find_sink_pad (GstElement * element);
75 static void update_volume (GstPlayBinMaemo *pbm);
76 static void update_xid (GstPlayBinMaemo *pbm);
77 static void new_decoded_pad_cb (GstElement *object,
81 static void unknown_type_cb (GstElement *object,
85 static gboolean autoplug_continue_cb (GstElement* object,
88 static void decode_new_pad_cb (GstElement *element,
91 static void queue_underrun_cb (GstElement* queue,
93 static void queue_sink_underrun_cb (GstElement* queue,
95 static void queue_sink_overrun_cb (GstElement* queue,
102 GST_BOILERPLATE(GstPlayBinMaemo, gst_play_bin_maemo, GstPipeline, GST_TYPE_PIPELINE)
106 gst_play_bin_maemo_base_init (gpointer klass)
108 GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
110 gst_element_class_set_details (element_class, &gst_play_bin_maemo_details);
114 gst_play_bin_maemo_class_init (GstPlayBinMaemoClass * klass)
116 GObjectClass *gobject_klass;
117 GstElementClass *gstelement_klass;
118 GstBinClass *gstbin_klass;
120 gobject_klass = (GObjectClass *) klass;
121 gstelement_klass = (GstElementClass *) klass;
122 gstbin_klass = (GstBinClass *) klass;
124 parent_class = g_type_class_peek_parent (klass);
126 gobject_klass->set_property = gst_play_bin_maemo_set_property;
127 gobject_klass->get_property = gst_play_bin_maemo_get_property;
129 g_object_class_install_property (gobject_klass, ARG_URI,
130 g_param_spec_string ("uri", "URI", "URI of the media to play",
131 NULL, G_PARAM_READWRITE));
133 g_object_class_install_property (gobject_klass, ARG_VOLUME,
134 g_param_spec_uint ("volume", "Audio volume", "volume",
135 0, 10, (guint) DEFAULT_VOLUME, G_PARAM_READWRITE));
137 g_object_class_install_property (gobject_klass, ARG_XID,
138 g_param_spec_long ("xid", "xid", "X windown ID",
139 -1, G_MAXLONG, DEFAULT_XID, G_PARAM_READWRITE));
141 g_object_class_install_property (gobject_klass, ARG_SOURCE,
142 g_param_spec_object ("source", "Source", "Source element",
143 GST_TYPE_ELEMENT, G_PARAM_READABLE));
145 GST_DEBUG_CATEGORY_INIT (gst_play_bin_maemo_debug, "playbinmaemo", 0,
148 gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_bin_maemo_dispose);
149 gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_bin_maemo_finalize);
151 gstelement_klass->change_state =
152 GST_DEBUG_FUNCPTR (gst_play_bin_maemo_change_state);
156 gst_play_bin_maemo_init (GstPlayBinMaemo * play_bin_maemo, GstPlayBinMaemoClass *class)
160 play_bin_maemo->uri = NULL;
161 play_bin_maemo->source = NULL;
163 play_bin_maemo->volume = DEFAULT_VOLUME * 65535 / 10;
164 play_bin_maemo->xid = DEFAULT_XID;
166 factories = gst_default_registry_feature_filter ((GstPluginFeatureFilter) factory_filter_sinks,
167 FALSE, play_bin_maemo);
169 play_bin_maemo->factories = g_list_sort (factories, (GCompareFunc) compare_ranks);
173 gst_play_bin_maemo_dispose (GObject * object)
175 GstPlayBinMaemo *play_bin_maemo;
177 play_bin_maemo = GST_PLAY_BIN_MAEMO (object);
178 g_free (play_bin_maemo->uri);
179 play_bin_maemo->uri = NULL;
181 G_OBJECT_CLASS (parent_class)->dispose (object);
185 gst_play_bin_maemo_finalize (GObject * object)
187 G_OBJECT_CLASS (parent_class)->finalize (object);
191 array_has_value (const gchar * values[], const gchar * value)
195 for (i = 0; values[i]; i++) {
196 if (g_str_has_prefix (value, values[i]))
202 /* list of URIs that we consider to be streams and that need buffering.
203 * We have no mechanism yet to figure this out with a query. */
204 static const gchar *stream_uris[] = { "http://", "mms://", "mmsh://",
205 "mmsu://", "mmst://", NULL
208 /* blacklisted URIs, we know they will always fail. */
209 static const gchar *blacklisted_uris[] = { NULL };
211 /* mime types that we don't consider to be media types */
212 static const gchar *no_media_mimes[] = {
213 "application/x-executable", "application/x-bzip", "application/x-gzip",
214 "application/zip", "application/x-compress", NULL
217 /* mime types we consider raw media */
218 static const gchar *raw_mimes[] = {
219 "audio/x-raw", "video/x-raw", NULL
222 #define IS_STREAM_URI(uri) (array_has_value (stream_uris, uri))
223 #define IS_BLACKLISTED_URI(uri) (array_has_value (blacklisted_uris, uri))
224 #define IS_NO_MEDIA_MIME(mime) (array_has_value (no_media_mimes, mime))
225 #define IS_RAW_MIME(mime) (array_has_value (raw_mimes, mime))
228 * Generate and configure a source element.
231 gen_source_element (GstPlayBinMaemo * play_bin_maemo)
235 if (!play_bin_maemo->uri)
238 if (!gst_uri_is_valid (play_bin_maemo->uri))
241 if (IS_BLACKLISTED_URI (play_bin_maemo->uri))
242 goto uri_blacklisted;
244 source = gst_element_make_from_uri (GST_URI_SRC, play_bin_maemo->uri,
249 play_bin_maemo->is_stream = IS_STREAM_URI (play_bin_maemo->uri);
251 /* make HTTP sources send extra headers so we get icecast
252 * metadata in case the stream is an icecast stream */
253 if (!strncmp (play_bin_maemo->uri, "http://", 7) &&
254 g_object_class_find_property (G_OBJECT_GET_CLASS (source),
256 g_object_set (source, "iradio-mode", TRUE, NULL);
263 GST_ELEMENT_ERROR (play_bin_maemo, RESOURCE, NOT_FOUND,
264 (_("No URI specified to play from.")), (NULL));
269 GST_ELEMENT_ERROR (play_bin_maemo, RESOURCE, NOT_FOUND,
270 (_("Invalid URI \"%s\"."), play_bin_maemo->uri), (NULL));
275 GST_ELEMENT_ERROR (play_bin_maemo, RESOURCE, FAILED,
276 (_("RTSP streams cannot be played yet.")), (NULL));
281 gchar *prot = gst_uri_get_protocol (play_bin_maemo->uri);
283 /* whoops, could not create the source element, dig a little deeper to
284 * figure out what might be wrong. */
289 gst_element_post_message (GST_ELEMENT (play_bin_maemo),
290 gst_missing_uri_source_message_new (GST_ELEMENT (play_bin_maemo),
293 desc = gst_pb_utils_get_source_description (prot);
294 GST_ELEMENT_ERROR (play_bin_maemo, CORE, MISSING_PLUGIN,
295 (_("A %s plugin is required to play this stream, but not installed."),
296 desc), ("No URI handler for %s", prot));
308 remove_source (GstPlayBinMaemo *pbm)
310 GstElement *source = pbm->source;
313 GST_DEBUG_OBJECT (pbm, "removing old src element");
314 gst_element_set_state (source, GST_STATE_NULL);
315 gst_bin_remove (GST_BIN_CAST (pbm), source);
321 remove_decoders (GstPlayBinMaemo *pbm)
323 if (pbm->queue != NULL) {
324 gst_element_set_state (pbm->queue, GST_STATE_NULL);
325 gst_bin_remove (GST_BIN_CAST (pbm), pbm->queue);
329 if (pbm->decoder != NULL) {
330 gst_element_set_state (pbm->decoder, GST_STATE_NULL);
331 gst_bin_remove (GST_BIN_CAST (pbm), pbm->decoder);
337 remove_sinks (GstPlayBinMaemo *pbm)
341 for(walk=pbm->sinks; walk != NULL; walk = walk->next) {
342 GstElement *element = (GstElement *) walk->data;
344 gst_element_set_state (element, GST_STATE_NULL);
345 gst_bin_remove (GST_BIN_CAST (pbm), element);
348 g_slist_free (pbm->sinks);
353 prepare_elements (GstPlayBinMaemo *pbm)
355 if (pbm->decoder == NULL) {
356 pbm->decoder = gst_element_factory_make ("decodebin2", "decode");
357 gst_bin_add (GST_BIN (pbm), pbm->decoder);
358 g_signal_connect (G_OBJECT (pbm->decoder),
360 G_CALLBACK (autoplug_continue_cb),
362 g_signal_connect (G_OBJECT (pbm->decoder),
364 G_CALLBACK (unknown_type_cb),
366 g_signal_connect (G_OBJECT (pbm->decoder),
368 G_CALLBACK (new_decoded_pad_cb),
372 if (pbm->queue == NULL) {
373 pbm->queue = gst_element_factory_make ("queue", NULL);
374 gst_bin_add (GST_BIN (pbm), pbm->queue);
377 if (gst_element_link_many (pbm->source, pbm->queue, pbm->decoder, NULL) == FALSE) {
378 g_warning ("FAIL TO LINK SRC WITH DECODEBIN2");
383 setup_source (GstPlayBinMaemo *pbm)
385 if (!pbm->need_rebuild)
388 GST_DEBUG_OBJECT (pbm, "setup source");
393 /* create and configure an element that can handle the uri */
394 if (!(pbm->source = gen_source_element (pbm)))
398 gst_bin_add (GST_BIN_CAST (pbm), pbm->source);
400 remove_decoders (pbm);
405 if (verify_src_have_sink (pbm)) {
406 /* source can be linked with sinks directly */
411 prepare_elements (pbm);
420 gst_play_bin_maemo_set_property (GObject *object,
425 GstPlayBinMaemo *play_bin_maemo;
427 g_return_if_fail (GST_IS_PLAY_BIN_MAEMO (object));
429 play_bin_maemo = GST_PLAY_BIN_MAEMO (object);
434 const gchar *uri = g_value_get_string (value);
437 g_warning ("cannot set NULL uri");
440 /* if we have no previous uri, or the new uri is different from the
442 if (play_bin_maemo->uri == NULL || strcmp (play_bin_maemo->uri, uri) != 0) {
443 g_free (play_bin_maemo->uri);
444 play_bin_maemo->uri = g_strdup (uri);
446 GST_DEBUG ("setting new uri to %s", uri);
448 play_bin_maemo->need_rebuild = TRUE;
455 volume = g_value_get_uint (value);
457 volume = (guint) (65535 * volume / 10);
460 if (play_bin_maemo->volume != volume) {
461 play_bin_maemo->volume = volume;
462 update_volume (play_bin_maemo);
469 xid = g_value_get_long (value);
470 if (play_bin_maemo->xid != xid) {
471 play_bin_maemo->xid = xid;
472 update_xid (play_bin_maemo);
477 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
483 gst_play_bin_maemo_get_property (GObject * object, guint prop_id, GValue * value,
486 GstPlayBinMaemo *play_bin_maemo;
488 g_return_if_fail (GST_IS_PLAY_BIN_MAEMO (object));
490 play_bin_maemo = GST_PLAY_BIN_MAEMO (object);
494 g_value_set_string (value, play_bin_maemo->uri);
497 g_value_set_object (value, play_bin_maemo->source);
500 g_value_set_uint (value, play_bin_maemo->volume);
503 g_value_set_long (value, play_bin_maemo->xid);
506 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
511 static GstStateChangeReturn
512 gst_play_bin_maemo_change_state (GstElement * element, GstStateChange transition)
514 GstStateChangeReturn ret;
515 GstPlayBinMaemo *play_bin_maemo;
517 play_bin_maemo = GST_PLAY_BIN_MAEMO (element);
519 switch (transition) {
520 case GST_STATE_CHANGE_READY_TO_PAUSED:
521 if (!setup_source (play_bin_maemo))
528 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
530 switch (transition) {
531 case GST_STATE_CHANGE_READY_TO_PAUSED:
532 if (ret == GST_STATE_CHANGE_FAILURE) {
533 play_bin_maemo->need_rebuild = TRUE;
534 return GST_STATE_CHANGE_FAILURE;
537 /* clean-up in both cases, READY=>NULL clean-up is if there was an error */
538 case GST_STATE_CHANGE_PAUSED_TO_READY:
539 case GST_STATE_CHANGE_READY_TO_NULL:
540 play_bin_maemo->need_rebuild = TRUE;
541 remove_decoders (play_bin_maemo);
542 remove_source (play_bin_maemo);
552 play_bin_maemo->need_rebuild = TRUE;
554 return GST_STATE_CHANGE_FAILURE;
559 factory_filter_sinks (GstPluginFeature *feature,
560 GstPlayBinMaemo *pbm)
565 if (!GST_IS_ELEMENT_FACTORY (feature))
568 klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature));
570 if ((strstr (klass, "Sink/Video") == NULL) && (strstr (klass, "Sink/Audio") == NULL))
573 g_debug ("Fitered: %s", gst_element_factory_get_longname ((GST_ELEMENT_FACTORY (feature))));
574 rank = gst_plugin_feature_get_rank (feature);
575 if (rank < GST_RANK_MARGINAL)
582 compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
585 const gchar *rname1, *rname2;
587 diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
591 rname1 = gst_plugin_feature_get_name (f1);
592 rname2 = gst_plugin_feature_get_name (f2);
594 diff = strcmp (rname2, rname1);
601 find_compatibles (GstPlayBinMaemo *pbm, const GstCaps *caps)
604 GList *to_try = NULL;
606 /* loop over all the factories */
607 for (factories = pbm->factories; factories; factories = g_list_next (factories)) {
608 GstElementFactory *factory = GST_ELEMENT_FACTORY (factories->data);
609 const GList *templates;
612 /* get the templates from the element factory */
613 templates = gst_element_factory_get_static_pad_templates (factory);
614 for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
615 GstStaticPadTemplate *templ = walk->data;
617 /* we only care about the sink templates */
618 if (templ->direction == GST_PAD_SINK) {
622 /* try to intersect the caps with the caps of the template */
623 tmpl_caps = gst_static_caps_get (&templ->static_caps);
625 intersect = gst_caps_intersect (caps, tmpl_caps);
626 gst_caps_unref (tmpl_caps);
628 /* check if the intersection is empty */
629 if (!gst_caps_is_empty (intersect)) {
630 /* non empty intersection, we can use this element */
631 to_try = g_list_prepend (to_try, factory);
632 gst_caps_unref (intersect);
635 gst_caps_unref (intersect);
639 to_try = g_list_reverse (to_try);
646 autoplug_continue_cb (GstElement* object,
653 comp = find_compatibles (GST_PLAY_BIN_MAEMO (user_data), caps);
663 unknown_type_cb (GstElement *object,
668 g_debug ("unknown_type_cb: %s", gst_caps_to_string (caps));
672 find_sink_pad (GstElement * element)
678 it = gst_element_iterate_sink_pads (element);
680 if ((gst_iterator_next (it, &point)) == GST_ITERATOR_OK)
681 pad = (GstPad *) point;
683 gst_iterator_free (it);
689 new_decoded_pad_cb (GstElement *object,
698 GstPlayBinMaemo *pbm;
700 pbm = GST_PLAY_BIN_MAEMO (user_data);
701 caps = gst_pad_get_caps (pad);
703 g_debug ("new_decoded_pad_cb: %s", gst_caps_to_string (caps));
705 comp = find_compatibles (GST_PLAY_BIN_MAEMO (user_data), caps);
709 g_warning ("flow error: dont find comaptible");
713 GST_PAD_STREAM_LOCK (pad);
716 for (walk=comp; walk != NULL; walk = walk->next) {
717 GstElementFactory *factory = (GstElementFactory *) walk->data;
721 if ((element = gst_element_factory_create (factory, NULL)) == NULL) {
722 GST_WARNING_OBJECT (pbm, "Could not create an element from %s",
723 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
727 if (strstr (gst_element_factory_get_klass (factory), "Sink/Video") != NULL) {
728 pbm->sink_video = element;
730 } else if (strstr (gst_element_factory_get_klass (factory), "Sink/Audio") != NULL) {
731 pbm->volume_element = element;
735 if (!(gst_bin_add (GST_BIN (user_data), element))) {
736 GST_WARNING_OBJECT (pbm, "Couldn't set %s to READY",
737 GST_ELEMENT_NAME (element));
738 gst_object_unref (element);
742 if ((gst_element_set_state (element, GST_STATE_READY))
743 == GST_STATE_CHANGE_FAILURE) {
744 gst_element_set_state (element, GST_STATE_NULL);
745 gst_object_unref (sinkpad);
746 gst_bin_remove (GST_BIN (user_data), element);
750 if (!(sinkpad = find_sink_pad (element))) {
751 GST_WARNING_OBJECT (pbm, "Element %s doesn't have a sink pad", GST_ELEMENT_NAME (element));
752 gst_object_unref (element);
757 if ((gst_pad_link (pad, sinkpad)) != GST_PAD_LINK_OK) {
758 GST_WARNING_OBJECT (pbm, "Link failed on pad %s:%s",
759 GST_DEBUG_PAD_NAME (sinkpad));
760 gst_element_set_state (element, GST_STATE_NULL);
761 gst_object_unref (sinkpad);
762 gst_bin_remove (GST_BIN (user_data), element);
766 gst_object_unref (sinkpad);
768 if ((gst_element_set_state (element, GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) {
769 gst_element_set_state (element, GST_STATE_NULL);
770 gst_bin_remove (GST_BIN (user_data), element);
775 pbm->sinks = g_slist_append (pbm->sinks, element);
781 if (linked == FALSE) {
782 g_warning ("GstFlow ERROR");
784 GST_PAD_STREAM_UNLOCK (pad);
788 update_volume (GstPlayBinMaemo *pbm)
790 if (pbm->volume_element != NULL) {
791 if (pbm->volume > 0) {
792 g_object_set (G_OBJECT (pbm->volume_element),
793 "volume", pbm->volume,
796 g_object_set (G_OBJECT (pbm->volume_element),
804 update_xid (GstPlayBinMaemo *pbm)
806 if ((pbm->sink_video != NULL) &&
808 (GST_IS_X_OVERLAY (pbm->sink_video))) {
811 g_object_set (G_OBJECT (pbm->sink_video),
812 "force-aspect-ratio", TRUE, NULL);
813 g_debug ("Update XID to %ld", pbm->xid);
815 gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (pbm->sink_video),
821 plugin_init(GstPlugin * plugin)
824 setlocale(LC_ALL, "");
825 bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
826 #endif /* ENABLE_NLS */
828 if (!gst_element_register(plugin, "playbinmaemo", GST_RANK_SECONDARY,
829 GST_TYPE_PLAY_BIN_MAEMO)) {
836 GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
839 "Demuxes and muxes audio and video",
840 plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,