renatofilho@787: /* GStreamer renatofilho@797: * Copyright (C) <2007> Renato Araujo Oliveira Filho renatofilho@787: * renatofilho@787: * This library is free software; you can redistribute it and/or renatofilho@787: * modify it under the terms of the GNU Library General Public renatofilho@787: * License as published by the Free Software Foundation; either renatofilho@787: * version 2 of the License, or (at your option) any later version. renatofilho@787: * renatofilho@787: * This library is distributed in the hope that it will be useful, renatofilho@787: * but WITHOUT ANY WARRANTY; without even the implied warranty of renatofilho@787: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU renatofilho@787: * Library General Public License for more details. renatofilho@787: * renatofilho@787: * You should have received a copy of the GNU Library General Public renatofilho@787: * License along with this library; if not, write to the renatofilho@787: * Free Software Foundation, Inc., 59 Temple Place - Suite 330, renatofilho@787: * Boston, MA 02111-1307, USA. renatofilho@787: */ renatofilho@787: renatofilho@787: #ifdef HAVE_CONFIG_H renatofilho@787: #include "config.h" renatofilho@787: #endif renatofilho@787: renatofilho@787: #include renatofilho@787: #include renatofilho@787: #include renatofilho@787: #include renatofilho@787: #include renatofilho@787: #include renatofilho@787: #include renatofilho@787: //#include renatofilho@787: #include "gstplaybinmaemo.h" renatofilho@787: renatofilho@787: renatofilho@787: GST_DEBUG_CATEGORY_STATIC (gst_play_bin_maemo_debug); renatofilho@787: #define GST_CAT_DEFAULT gst_play_bin_maemo_debug renatofilho@787: renatofilho@787: #define DEFAULT_VOLUME 10 renatofilho@787: #define DEFAULT_XID -1 renatofilho@787: renatofilho@787: /* props */ renatofilho@787: enum renatofilho@787: { renatofilho@787: ARG_0, renatofilho@787: ARG_URI, renatofilho@787: ARG_QUEUE_SIZE, renatofilho@787: ARG_QUEUE_MIN_THRESHOLD, renatofilho@787: ARG_SOURCE, renatofilho@787: ARG_VOLUME, renatofilho@788: ARG_PARSE_METADATA, renatofilho@787: ARG_XID renatofilho@787: }; renatofilho@787: renatofilho@787: static const GstElementDetails gst_play_bin_maemo_details = leo_sobral@796: GST_ELEMENT_DETAILS("playbinmaemo", renatofilho@787: "Generic/Bin/Player", renatofilho@787: "Autoplug and play media from an uri used on maemo plataform", renatofilho@787: "Renato Araujo Oliveira Filho "); renatofilho@787: renatofilho@787: static void gst_play_bin_maemo_dispose (GObject * object); renatofilho@787: static void gst_play_bin_maemo_finalize (GObject * object); renatofilho@787: static void gst_play_bin_maemo_set_property (GObject * object, guint prop_id, renatofilho@787: const GValue * value, GParamSpec * spec); renatofilho@787: static void gst_play_bin_maemo_get_property (GObject * object, guint prop_id, renatofilho@787: GValue * value, GParamSpec * spec); renatofilho@787: static GstStateChangeReturn renatofilho@787: gst_play_bin_maemo_change_state (GstElement *element, renatofilho@787: GstStateChange transition); renatofilho@787: static gboolean factory_filter_sinks (GstPluginFeature *feature, renatofilho@787: GstPlayBinMaemo *pbm); renatofilho@787: static gint compare_ranks (GstPluginFeature * f1, renatofilho@787: GstPluginFeature * f2); renatofilho@787: static GList *find_compatibles (GstPlayBinMaemo *pbm, renatofilho@787: const GstCaps *caps); renatofilho@787: static GstPad *find_sink_pad (GstElement * element); renatofilho@787: static void update_volume (GstPlayBinMaemo *pbm); renatofilho@787: static void update_xid (GstPlayBinMaemo *pbm); renatofilho@787: static void new_decoded_pad_cb (GstElement *object, renatofilho@787: GstPad* pad, renatofilho@787: gboolean arg, renatofilho@787: gpointer user_data); renatofilho@787: static void unknown_type_cb (GstElement *object, renatofilho@787: GstPad *pad, renatofilho@787: GstCaps *casp, renatofilho@787: gpointer user_data); renatofilho@787: static gboolean autoplug_continue_cb (GstElement* object, renatofilho@787: GstCaps* caps, renatofilho@787: gpointer user_data); renatofilho@792: static gboolean add_element (GstPlayBinMaemo *pbm, renatofilho@792: GstElement *child); renatofilho@792: static void clear_elements (GstPlayBinMaemo *pbm); leo_sobral@808: static int x_error_handler (Display *display, leo_sobral@808: XErrorEvent *event); renatofilho@787: renatofilho@787: GST_BOILERPLATE(GstPlayBinMaemo, gst_play_bin_maemo, GstPipeline, GST_TYPE_PIPELINE) renatofilho@787: renatofilho@787: renatofilho@787: static void renatofilho@787: gst_play_bin_maemo_base_init (gpointer klass) renatofilho@787: { renatofilho@787: GstElementClass *element_class = GST_ELEMENT_CLASS(klass); renatofilho@787: renatofilho@787: gst_element_class_set_details (element_class, &gst_play_bin_maemo_details); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_play_bin_maemo_class_init (GstPlayBinMaemoClass * klass) renatofilho@787: { renatofilho@787: GObjectClass *gobject_klass; renatofilho@787: GstElementClass *gstelement_klass; renatofilho@787: GstBinClass *gstbin_klass; renatofilho@787: renatofilho@787: gobject_klass = (GObjectClass *) klass; renatofilho@787: gstelement_klass = (GstElementClass *) klass; renatofilho@787: gstbin_klass = (GstBinClass *) klass; renatofilho@787: renatofilho@787: parent_class = g_type_class_peek_parent (klass); renatofilho@787: renatofilho@787: gobject_klass->set_property = gst_play_bin_maemo_set_property; renatofilho@787: gobject_klass->get_property = gst_play_bin_maemo_get_property; renatofilho@787: renatofilho@787: g_object_class_install_property (gobject_klass, ARG_URI, renatofilho@787: g_param_spec_string ("uri", "URI", "URI of the media to play", renatofilho@787: NULL, G_PARAM_READWRITE)); renatofilho@787: renatofilho@787: g_object_class_install_property (gobject_klass, ARG_VOLUME, renatofilho@793: g_param_spec_double ("volume", "Audio volume", "volume", renatofilho@793: 0.0, 10.0, (gdouble) DEFAULT_VOLUME, G_PARAM_READWRITE)); renatofilho@787: renatofilho@787: g_object_class_install_property (gobject_klass, ARG_XID, renatofilho@787: g_param_spec_long ("xid", "xid", "X windown ID", renatofilho@787: -1, G_MAXLONG, DEFAULT_XID, G_PARAM_READWRITE)); renatofilho@787: renatofilho@787: g_object_class_install_property (gobject_klass, ARG_SOURCE, renatofilho@787: g_param_spec_object ("source", "Source", "Source element", renatofilho@787: GST_TYPE_ELEMENT, G_PARAM_READABLE)); renatofilho@787: renatofilho@788: g_object_class_install_property (gobject_klass, ARG_PARSE_METADATA, renatofilho@788: g_param_spec_boolean ("parse-metadata", "Parse Metadata", "Parse metadata info", renatofilho@788: TRUE, G_PARAM_READWRITE)); renatofilho@788: renatofilho@788: renatofilho@787: GST_DEBUG_CATEGORY_INIT (gst_play_bin_maemo_debug, "playbinmaemo", 0, renatofilho@787: "playbinmaemo"); renatofilho@787: renatofilho@787: gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_bin_maemo_dispose); renatofilho@787: gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_bin_maemo_finalize); renatofilho@787: renatofilho@787: gstelement_klass->change_state = renatofilho@787: GST_DEBUG_FUNCPTR (gst_play_bin_maemo_change_state); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_play_bin_maemo_init (GstPlayBinMaemo * play_bin_maemo, GstPlayBinMaemoClass *class) renatofilho@787: { renatofilho@787: GList *factories; renatofilho@787: renatofilho@787: play_bin_maemo->uri = NULL; renatofilho@787: play_bin_maemo->source = NULL; renatofilho@787: renatofilho@787: play_bin_maemo->volume = DEFAULT_VOLUME * 65535 / 10; renatofilho@787: play_bin_maemo->xid = DEFAULT_XID; renatofilho@788: play_bin_maemo->parse_metadata = TRUE; renatofilho@787: renatofilho@787: factories = gst_default_registry_feature_filter ((GstPluginFeatureFilter) factory_filter_sinks, renatofilho@787: FALSE, play_bin_maemo); renatofilho@787: renatofilho@787: play_bin_maemo->factories = g_list_sort (factories, (GCompareFunc) compare_ranks); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_play_bin_maemo_dispose (GObject * object) renatofilho@787: { renatofilho@787: GstPlayBinMaemo *play_bin_maemo; renatofilho@787: renatofilho@787: play_bin_maemo = GST_PLAY_BIN_MAEMO (object); renatofilho@787: g_free (play_bin_maemo->uri); renatofilho@787: play_bin_maemo->uri = NULL; renatofilho@787: renatofilho@787: G_OBJECT_CLASS (parent_class)->dispose (object); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_play_bin_maemo_finalize (GObject * object) renatofilho@787: { renatofilho@792: clear_elements (GST_PLAY_BIN_MAEMO (object)); renatofilho@787: G_OBJECT_CLASS (parent_class)->finalize (object); renatofilho@787: } renatofilho@787: renatofilho@787: static gboolean renatofilho@787: array_has_value (const gchar * values[], const gchar * value) renatofilho@787: { renatofilho@787: gint i; renatofilho@787: renatofilho@787: for (i = 0; values[i]; i++) { renatofilho@787: if (g_str_has_prefix (value, values[i])) renatofilho@787: return TRUE; renatofilho@787: } renatofilho@787: return FALSE; renatofilho@787: } renatofilho@787: renatofilho@787: /* list of URIs that we consider to be streams and that need buffering. renatofilho@787: * We have no mechanism yet to figure this out with a query. */ renatofilho@787: static const gchar *stream_uris[] = { "http://", "mms://", "mmsh://", renatofilho@787: "mmsu://", "mmst://", NULL renatofilho@787: }; renatofilho@787: renatofilho@787: /* blacklisted URIs, we know they will always fail. */ renatofilho@787: static const gchar *blacklisted_uris[] = { NULL }; renatofilho@787: renatofilho@787: /* mime types that we don't consider to be media types */ renatofilho@793: /* renatofilho@787: static const gchar *no_media_mimes[] = { renatofilho@787: "application/x-executable", "application/x-bzip", "application/x-gzip", renatofilho@787: "application/zip", "application/x-compress", NULL renatofilho@787: }; renatofilho@793: */ renatofilho@787: renatofilho@787: /* mime types we consider raw media */ renatofilho@787: static const gchar *raw_mimes[] = { renatofilho@787: "audio/x-raw", "video/x-raw", NULL renatofilho@787: }; renatofilho@787: renatofilho@787: #define IS_STREAM_URI(uri) (array_has_value (stream_uris, uri)) renatofilho@787: #define IS_BLACKLISTED_URI(uri) (array_has_value (blacklisted_uris, uri)) renatofilho@787: #define IS_NO_MEDIA_MIME(mime) (array_has_value (no_media_mimes, mime)) renatofilho@787: #define IS_RAW_MIME(mime) (array_has_value (raw_mimes, mime)) renatofilho@787: renatofilho@787: /* renatofilho@787: * Generate and configure a source element. renatofilho@787: */ renatofilho@787: static GstElement * renatofilho@787: gen_source_element (GstPlayBinMaemo * play_bin_maemo) renatofilho@787: { renatofilho@787: GstElement *source; renatofilho@787: renatofilho@787: if (!play_bin_maemo->uri) renatofilho@787: goto no_uri; renatofilho@787: renatofilho@787: if (!gst_uri_is_valid (play_bin_maemo->uri)) renatofilho@787: goto invalid_uri; renatofilho@787: renatofilho@787: if (IS_BLACKLISTED_URI (play_bin_maemo->uri)) renatofilho@787: goto uri_blacklisted; renatofilho@787: renatofilho@792: source = gst_element_make_from_uri (GST_URI_SRC, play_bin_maemo->uri, "source"); renatofilho@787: if (!source) renatofilho@787: goto no_source; renatofilho@787: renatofilho@787: play_bin_maemo->is_stream = IS_STREAM_URI (play_bin_maemo->uri); renatofilho@787: renatofilho@787: /* make HTTP sources send extra headers so we get icecast renatofilho@787: * metadata in case the stream is an icecast stream */ renatofilho@787: if (!strncmp (play_bin_maemo->uri, "http://", 7) && renatofilho@787: g_object_class_find_property (G_OBJECT_GET_CLASS (source), renatofilho@787: "iradio-mode")) { renatofilho@787: g_object_set (source, "iradio-mode", TRUE, NULL); renatofilho@787: } renatofilho@787: return source; renatofilho@787: renatofilho@787: /* ERRORS */ renatofilho@787: no_uri: renatofilho@787: { renatofilho@787: GST_ELEMENT_ERROR (play_bin_maemo, RESOURCE, NOT_FOUND, renatofilho@787: (_("No URI specified to play from.")), (NULL)); renatofilho@787: return NULL; renatofilho@787: } renatofilho@787: invalid_uri: renatofilho@787: { renatofilho@787: GST_ELEMENT_ERROR (play_bin_maemo, RESOURCE, NOT_FOUND, renatofilho@787: (_("Invalid URI \"%s\"."), play_bin_maemo->uri), (NULL)); renatofilho@787: return NULL; renatofilho@787: } renatofilho@787: uri_blacklisted: renatofilho@787: { renatofilho@787: GST_ELEMENT_ERROR (play_bin_maemo, RESOURCE, FAILED, renatofilho@787: (_("RTSP streams cannot be played yet.")), (NULL)); renatofilho@787: return NULL; renatofilho@787: } renatofilho@787: no_source: renatofilho@787: { renatofilho@787: gchar *prot = gst_uri_get_protocol (play_bin_maemo->uri); renatofilho@787: renatofilho@787: /* whoops, could not create the source element, dig a little deeper to renatofilho@787: * figure out what might be wrong. */ renatofilho@787: if (prot) { renatofilho@793: /* renatofilho@787: gchar *desc; renatofilho@787: renatofilho@787: gst_element_post_message (GST_ELEMENT (play_bin_maemo), renatofilho@787: gst_missing_uri_source_message_new (GST_ELEMENT (play_bin_maemo), renatofilho@787: prot)); renatofilho@787: renatofilho@787: desc = gst_pb_utils_get_source_description (prot); renatofilho@787: GST_ELEMENT_ERROR (play_bin_maemo, CORE, MISSING_PLUGIN, renatofilho@787: (_("A %s plugin is required to play this stream, but not installed."), renatofilho@787: desc), ("No URI handler for %s", prot)); renatofilho@787: g_free (desc); renatofilho@793: */ renatofilho@787: g_free (prot); renatofilho@787: } else renatofilho@787: goto invalid_uri; renatofilho@787: renatofilho@787: return NULL; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: prepare_elements (GstPlayBinMaemo *pbm) renatofilho@787: { leo_sobral@805: GstElement *decoder; leo_sobral@805: GstElement *queue; renatofilho@787: leo_sobral@805: decoder = gst_element_factory_make ("decodebin2", "decode"); leo_sobral@805: add_element (pbm, decoder); leo_sobral@805: g_signal_connect (G_OBJECT (decoder), leo_sobral@805: "autoplug-continue", leo_sobral@805: G_CALLBACK (autoplug_continue_cb), leo_sobral@805: pbm); renatofilho@787: leo_sobral@805: g_signal_connect (G_OBJECT (decoder), leo_sobral@805: "unknown-type", leo_sobral@805: G_CALLBACK (unknown_type_cb), leo_sobral@805: pbm); renatofilho@792: leo_sobral@805: g_signal_connect (G_OBJECT (decoder), leo_sobral@805: "new-decoded-pad", leo_sobral@805: G_CALLBACK (new_decoded_pad_cb), leo_sobral@805: pbm); renatofilho@792: leo_sobral@805: queue = gst_element_factory_make ("queue", NULL); leo_sobral@805: add_element (pbm, queue); renatofilho@792: leo_sobral@805: if (gst_element_link_many (pbm->source, queue, decoder, NULL) == FALSE) { leo_sobral@805: g_warning ("FAIL TO LINK SRC WITH DECODEBIN2"); leo_sobral@805: } renatofilho@787: } renatofilho@787: renatofilho@787: static gboolean renatofilho@787: setup_source (GstPlayBinMaemo *pbm) renatofilho@787: { leo_sobral@805: if (!pbm->need_rebuild) leo_sobral@805: return TRUE; renatofilho@787: leo_sobral@805: clear_elements (pbm); renatofilho@792: leo_sobral@805: GST_DEBUG_OBJECT (pbm, "setup source"); renatofilho@787: leo_sobral@805: pbm->has_metadata = FALSE; renatofilho@788: leo_sobral@805: /* create and configure an element that can handle the uri */ leo_sobral@805: if (!(pbm->source = gen_source_element (pbm))) leo_sobral@805: goto no_source; renatofilho@787: leo_sobral@805: add_element (pbm, pbm->source); renatofilho@787: renatofilho@787: renatofilho@787: #if 0 renatofilho@787: if (verify_src_have_sink (pbm)) { renatofilho@787: /* source can be linked with sinks directly */ renatofilho@787: return TRUE; renatofilho@787: } renatofilho@787: #endif renatofilho@787: leo_sobral@805: prepare_elements (pbm); renatofilho@787: leo_sobral@805: return TRUE; renatofilho@787: renatofilho@787: no_source: leo_sobral@805: return FALSE; renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_play_bin_maemo_set_property (GObject *object, renatofilho@787: guint prop_id, renatofilho@787: const GValue *value, renatofilho@787: GParamSpec *pspec) renatofilho@787: { renatofilho@787: GstPlayBinMaemo *play_bin_maemo; renatofilho@787: renatofilho@787: g_return_if_fail (GST_IS_PLAY_BIN_MAEMO (object)); renatofilho@787: renatofilho@787: play_bin_maemo = GST_PLAY_BIN_MAEMO (object); renatofilho@787: renatofilho@787: switch (prop_id) { renatofilho@787: case ARG_URI: renatofilho@787: { renatofilho@787: const gchar *uri = g_value_get_string (value); renatofilho@787: renatofilho@787: if (uri == NULL) { renatofilho@787: g_warning ("cannot set NULL uri"); renatofilho@787: return; renatofilho@787: } renatofilho@787: /* if we have no previous uri, or the new uri is different from the renatofilho@787: * old one, replug */ renatofilho@787: if (play_bin_maemo->uri == NULL || strcmp (play_bin_maemo->uri, uri) != 0) { renatofilho@787: g_free (play_bin_maemo->uri); renatofilho@787: play_bin_maemo->uri = g_strdup (uri); renatofilho@787: renatofilho@787: GST_DEBUG ("setting new uri to %s", uri); renatofilho@787: renatofilho@787: play_bin_maemo->need_rebuild = TRUE; renatofilho@787: } renatofilho@787: break; renatofilho@787: } renatofilho@787: case ARG_VOLUME: renatofilho@787: { renatofilho@793: gdouble d_volume = 0; renatofilho@793: guint u_volume = 0; renatofilho@793: d_volume = g_value_get_double (value); renatofilho@793: renatofilho@793: g_debug ("Getting : %5.2f", d_volume); renatofilho@793: if (d_volume != 0) { renatofilho@793: u_volume = (guint) (65535 * d_volume); renatofilho@787: } renatofilho@787: renatofilho@793: g_debug ("Converting : %d", u_volume); renatofilho@793: if (play_bin_maemo->volume != u_volume) { renatofilho@793: play_bin_maemo->volume = u_volume; renatofilho@787: update_volume (play_bin_maemo); renatofilho@787: } renatofilho@787: break; renatofilho@787: } renatofilho@787: case ARG_XID: renatofilho@787: { renatofilho@787: long xid; renatofilho@787: xid = g_value_get_long (value); renatofilho@792: if (play_bin_maemo->xid != xid) renatofilho@792: { renatofilho@787: play_bin_maemo->xid = xid; renatofilho@787: update_xid (play_bin_maemo); renatofilho@787: } renatofilho@787: break; renatofilho@787: } renatofilho@788: case ARG_PARSE_METADATA: renatofilho@788: play_bin_maemo->parse_metadata = g_value_get_boolean (value); renatofilho@788: break; renatofilho@787: default: renatofilho@787: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); renatofilho@787: break; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: gst_play_bin_maemo_get_property (GObject * object, guint prop_id, GValue * value, renatofilho@787: GParamSpec * pspec) renatofilho@787: { renatofilho@787: GstPlayBinMaemo *play_bin_maemo; renatofilho@787: renatofilho@787: g_return_if_fail (GST_IS_PLAY_BIN_MAEMO (object)); renatofilho@787: renatofilho@787: play_bin_maemo = GST_PLAY_BIN_MAEMO (object); renatofilho@787: renatofilho@787: switch (prop_id) { renatofilho@787: case ARG_URI: renatofilho@787: g_value_set_string (value, play_bin_maemo->uri); renatofilho@787: break; renatofilho@787: case ARG_SOURCE: renatofilho@787: g_value_set_object (value, play_bin_maemo->source); renatofilho@787: break; renatofilho@787: case ARG_VOLUME: leo_sobral@805: { renatofilho@793: gdouble volume = 0; renatofilho@792: if (play_bin_maemo->volume > 0) { renatofilho@793: volume = play_bin_maemo->volume / 65535; renatofilho@792: } renatofilho@793: g_value_set_double (value, volume); renatofilho@787: break; leo_sobral@805: } renatofilho@787: case ARG_XID: renatofilho@787: g_value_set_long (value, play_bin_maemo->xid); renatofilho@787: break; renatofilho@788: case ARG_PARSE_METADATA: renatofilho@788: g_value_set_boolean (value, play_bin_maemo->parse_metadata); renatofilho@788: break; renatofilho@787: default: renatofilho@787: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); renatofilho@787: break; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: static GstStateChangeReturn renatofilho@787: gst_play_bin_maemo_change_state (GstElement * element, GstStateChange transition) renatofilho@787: { renatofilho@787: GstStateChangeReturn ret; renatofilho@787: GstPlayBinMaemo *play_bin_maemo; renatofilho@787: renatofilho@787: play_bin_maemo = GST_PLAY_BIN_MAEMO (element); renatofilho@787: renatofilho@787: switch (transition) { renatofilho@787: case GST_STATE_CHANGE_READY_TO_PAUSED: renatofilho@787: if (!setup_source (play_bin_maemo)) renatofilho@787: goto source_failed; renatofilho@787: break; renatofilho@787: default: renatofilho@787: break; renatofilho@787: } renatofilho@787: renatofilho@787: ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); renatofilho@787: renatofilho@787: switch (transition) { renatofilho@787: case GST_STATE_CHANGE_READY_TO_PAUSED: renatofilho@787: if (ret == GST_STATE_CHANGE_FAILURE) { renatofilho@787: play_bin_maemo->need_rebuild = TRUE; renatofilho@787: return GST_STATE_CHANGE_FAILURE; renatofilho@787: } renatofilho@787: break; renatofilho@787: /* clean-up in both cases, READY=>NULL clean-up is if there was an error */ renatofilho@787: case GST_STATE_CHANGE_PAUSED_TO_READY: renatofilho@787: case GST_STATE_CHANGE_READY_TO_NULL: renatofilho@787: play_bin_maemo->need_rebuild = TRUE; renatofilho@792: clear_elements (play_bin_maemo); renatofilho@787: break; renatofilho@787: default: renatofilho@787: break; renatofilho@787: } renatofilho@787: return ret; renatofilho@787: renatofilho@787: /* ERRORS */ renatofilho@787: source_failed: renatofilho@787: { renatofilho@787: play_bin_maemo->need_rebuild = TRUE; renatofilho@787: renatofilho@787: return GST_STATE_CHANGE_FAILURE; renatofilho@787: } renatofilho@787: } renatofilho@787: renatofilho@787: static gboolean renatofilho@787: factory_filter_sinks (GstPluginFeature *feature, renatofilho@787: GstPlayBinMaemo *pbm) renatofilho@787: { leo_sobral@805: guint rank; leo_sobral@805: const gchar *klass; renatofilho@787: leo_sobral@805: if (!GST_IS_ELEMENT_FACTORY (feature)) leo_sobral@805: return FALSE; renatofilho@787: leo_sobral@805: klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature)); renatofilho@787: leo_sobral@805: if ((strstr (klass, "Sink/Video") == NULL) && (strstr (klass, "Sink/Audio") == NULL)) leo_sobral@805: return FALSE; renatofilho@787: leo_sobral@805: g_debug ("Filtered: %s", gst_element_factory_get_longname ((GST_ELEMENT_FACTORY (feature)))); leo_sobral@805: rank = gst_plugin_feature_get_rank (feature); leo_sobral@805: if (rank < GST_RANK_MARGINAL) leo_sobral@805: return FALSE; renatofilho@787: leo_sobral@805: return TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: static gint renatofilho@787: compare_ranks (GstPluginFeature * f1, GstPluginFeature * f2) renatofilho@787: { renatofilho@787: gint diff; renatofilho@787: const gchar *rname1, *rname2; renatofilho@787: renatofilho@787: diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1); renatofilho@787: if (diff != 0) renatofilho@787: return diff; renatofilho@787: renatofilho@787: rname1 = gst_plugin_feature_get_name (f1); renatofilho@787: rname2 = gst_plugin_feature_get_name (f2); renatofilho@787: renatofilho@787: diff = strcmp (rname2, rname1); renatofilho@787: renatofilho@787: return diff; renatofilho@787: } renatofilho@787: renatofilho@787: renatofilho@787: static GList * renatofilho@787: find_compatibles (GstPlayBinMaemo *pbm, const GstCaps *caps) renatofilho@787: { renatofilho@787: GList *factories; renatofilho@787: GList *to_try = NULL; renatofilho@787: renatofilho@787: /* loop over all the factories */ renatofilho@787: for (factories = pbm->factories; factories; factories = g_list_next (factories)) { renatofilho@787: GstElementFactory *factory = GST_ELEMENT_FACTORY (factories->data); renatofilho@787: const GList *templates; renatofilho@787: GList *walk; renatofilho@787: renatofilho@787: /* get the templates from the element factory */ renatofilho@787: templates = gst_element_factory_get_static_pad_templates (factory); renatofilho@787: for (walk = (GList *) templates; walk; walk = g_list_next (walk)) { renatofilho@787: GstStaticPadTemplate *templ = walk->data; renatofilho@787: renatofilho@787: /* we only care about the sink templates */ renatofilho@787: if (templ->direction == GST_PAD_SINK) { renatofilho@787: GstCaps *intersect; renatofilho@787: GstCaps *tmpl_caps; renatofilho@787: renatofilho@787: /* try to intersect the caps with the caps of the template */ renatofilho@787: tmpl_caps = gst_static_caps_get (&templ->static_caps); renatofilho@787: renatofilho@787: intersect = gst_caps_intersect (caps, tmpl_caps); renatofilho@787: gst_caps_unref (tmpl_caps); renatofilho@787: renatofilho@787: /* check if the intersection is empty */ renatofilho@787: if (!gst_caps_is_empty (intersect)) { renatofilho@787: /* non empty intersection, we can use this element */ renatofilho@787: to_try = g_list_prepend (to_try, factory); renatofilho@787: gst_caps_unref (intersect); renatofilho@787: break; renatofilho@787: } renatofilho@787: gst_caps_unref (intersect); renatofilho@787: } renatofilho@787: } renatofilho@787: } renatofilho@787: to_try = g_list_reverse (to_try); renatofilho@787: renatofilho@787: return to_try; renatofilho@787: } renatofilho@787: renatofilho@787: renatofilho@787: static gboolean renatofilho@787: autoplug_continue_cb (GstElement* object, renatofilho@787: GstCaps* caps, renatofilho@787: gpointer user_data) renatofilho@787: { renatofilho@787: GList *comp = NULL; renatofilho@787: gboolean ret = TRUE; renatofilho@788: GstPlayBinMaemo *pbm; renatofilho@788: renatofilho@788: pbm = GST_PLAY_BIN_MAEMO (user_data); renatofilho@788: renatofilho@788: //TODO: fix this for work with all metada elements renatofilho@788: if (pbm->parse_metadata) { renatofilho@788: gchar *caps_str = gst_caps_to_string (caps); renatofilho@788: if ((strstr (caps_str, "id3") != NULL) && renatofilho@788: (pbm->has_metadata == FALSE)) { renatofilho@788: renatofilho@788: g_free (caps_str); renatofilho@788: pbm->has_metadata = TRUE; renatofilho@788: return ret; renatofilho@788: } renatofilho@788: g_free (caps_str); renatofilho@788: } renatofilho@787: renatofilho@787: comp = find_compatibles (GST_PLAY_BIN_MAEMO (user_data), caps); renatofilho@787: if (comp != NULL) { renatofilho@787: g_list_free (comp); renatofilho@787: ret = FALSE; renatofilho@787: } renatofilho@787: renatofilho@787: return ret; renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: unknown_type_cb (GstElement *object, renatofilho@787: GstPad *pad, renatofilho@787: GstCaps *caps, renatofilho@787: gpointer user_data) renatofilho@787: { leo_sobral@805: g_debug ("unknown_type_cb: %s", gst_caps_to_string (caps)); renatofilho@787: } renatofilho@787: renatofilho@787: static GstPad * renatofilho@787: find_sink_pad (GstElement * element) renatofilho@787: { renatofilho@787: GstIterator *it; renatofilho@787: GstPad *pad = NULL; renatofilho@787: gpointer point; renatofilho@787: renatofilho@787: it = gst_element_iterate_sink_pads (element); renatofilho@787: renatofilho@787: if ((gst_iterator_next (it, &point)) == GST_ITERATOR_OK) renatofilho@787: pad = (GstPad *) point; renatofilho@787: renatofilho@787: gst_iterator_free (it); renatofilho@787: renatofilho@787: return pad; renatofilho@787: } renatofilho@787: renatofilho@788: static GstElement* renatofilho@788: create_element (GstPlayBinMaemo *pbm, GstElementFactory *factory) renatofilho@788: { leo_sobral@805: GstElement *queue; leo_sobral@805: GstElement *bin = NULL; leo_sobral@805: GstElement *element; leo_sobral@805: GstPad *pad; renatofilho@788: leo_sobral@805: element = gst_element_factory_create (factory, NULL); leo_sobral@805: if (element == NULL) leo_sobral@805: return NULL; renatofilho@788: renatofilho@792: leo_sobral@805: bin = gst_bin_new (NULL); renatofilho@792: leo_sobral@805: queue = gst_element_factory_make ("queue", NULL); leo_sobral@805: gst_bin_add (GST_BIN (bin), queue); renatofilho@792: leo_sobral@805: if (strstr (gst_element_factory_get_klass (factory), "Sink/Video") != NULL) { leo_sobral@805: GstElement *colorspace; renatofilho@792: leo_sobral@805: colorspace = gst_element_factory_make ("ffmpegcolorspace", NULL); renatofilho@792: leo_sobral@805: gst_bin_add (GST_BIN (bin), colorspace); leo_sobral@805: if (gst_element_link (queue, colorspace) == FALSE) { leo_sobral@805: GST_WARNING_OBJECT (pbm, "Fail to link queue and colorspace"); leo_sobral@805: gst_element_set_state (colorspace, GST_STATE_NULL); leo_sobral@805: gst_object_unref (colorspace); leo_sobral@805: goto error; renatofilho@788: } renatofilho@788: leo_sobral@805: gst_bin_add (GST_BIN (bin), element); leo_sobral@805: if (gst_element_link (colorspace, element) == FALSE) { leo_sobral@805: GST_WARNING_OBJECT (pbm, "Fail to link colorspace and sink video: %s", GST_ELEMENT_NAME (element)); leo_sobral@805: gst_element_set_state (colorspace, GST_STATE_NULL); leo_sobral@805: gst_object_unref (colorspace); leo_sobral@805: goto error; leo_sobral@805: } renatofilho@792: leo_sobral@805: pbm->video_sink = element; leo_sobral@805: update_xid (pbm); leo_sobral@805: leo_sobral@805: } else if (strstr (gst_element_factory_get_klass (factory), "Sink/Audio") != NULL) { leo_sobral@805: GParamSpec *vol_spec; leo_sobral@805: GstElement *prev; leo_sobral@805: leo_sobral@805: prev = queue; leo_sobral@805: vol_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), "volume"); leo_sobral@805: if (vol_spec == NULL) { leo_sobral@805: GstElement *volume; leo_sobral@805: leo_sobral@805: bin = gst_bin_new (NULL); leo_sobral@805: volume = gst_element_factory_make ("volume", "volume"); leo_sobral@805: gst_bin_add (GST_BIN (bin), volume); leo_sobral@805: if (gst_element_link (queue, volume) == FALSE) { leo_sobral@805: GST_WARNING_OBJECT (pbm, "Fail to link queue and volume"); leo_sobral@805: gst_element_set_state (volume, GST_STATE_NULL); leo_sobral@805: gst_object_unref (volume); leo_sobral@805: goto error; leo_sobral@805: } leo_sobral@805: leo_sobral@805: prev = volume; leo_sobral@805: g_param_spec_unref (vol_spec); leo_sobral@805: } leo_sobral@805: leo_sobral@805: gst_bin_add (GST_BIN (bin), element); leo_sobral@805: if (gst_element_link (prev, element) == FALSE) { leo_sobral@805: GST_WARNING_OBJECT (pbm, "Fail to link volume and sink audio: %s", GST_ELEMENT_NAME (element)); leo_sobral@805: if (prev != queue) { leo_sobral@805: gst_element_set_state (prev, GST_STATE_NULL); leo_sobral@805: gst_object_unref (prev); leo_sobral@805: } leo_sobral@805: goto error; leo_sobral@805: } leo_sobral@805: leo_sobral@805: pbm->volume_element = (prev != queue) ? prev : element; leo_sobral@805: update_volume (pbm); leo_sobral@805: } leo_sobral@805: leo_sobral@805: pad = gst_element_get_pad (queue, "sink"); leo_sobral@805: gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad)); leo_sobral@805: gst_object_unref (pad); leo_sobral@805: leo_sobral@805: return bin; renatofilho@792: error: renatofilho@792: leo_sobral@805: gst_element_set_state (bin, GST_STATE_NULL); leo_sobral@805: gst_object_unref (bin); renatofilho@792: leo_sobral@805: return NULL; renatofilho@788: } renatofilho@788: renatofilho@787: static void renatofilho@787: new_decoded_pad_cb (GstElement *object, renatofilho@787: GstPad* pad, renatofilho@787: gboolean arg, renatofilho@787: gpointer user_data) renatofilho@787: { leo_sobral@805: GList *comp = NULL; leo_sobral@805: GList *walk; leo_sobral@805: GstCaps *caps; leo_sobral@805: gboolean linked; leo_sobral@805: GstPlayBinMaemo *pbm; renatofilho@787: leo_sobral@805: pbm = GST_PLAY_BIN_MAEMO (user_data); leo_sobral@805: caps = gst_pad_get_caps (pad); renatofilho@787: leo_sobral@805: g_debug ("new_decoded_pad_cb: %s", gst_caps_to_string (caps)); renatofilho@787: leo_sobral@805: comp = find_compatibles (GST_PLAY_BIN_MAEMO (user_data), caps); renatofilho@787: renatofilho@787: leo_sobral@805: if (comp == NULL) { leo_sobral@805: g_warning ("flow error: dont find comaptible"); leo_sobral@805: return; leo_sobral@805: } leo_sobral@805: leo_sobral@805: GST_PAD_STREAM_LOCK (pad); leo_sobral@805: leo_sobral@805: linked = FALSE; leo_sobral@805: for (walk=comp; walk != NULL; walk = walk->next) { leo_sobral@805: GstElementFactory *factory = (GstElementFactory *) walk->data; leo_sobral@805: GstElement *element; leo_sobral@805: GstPad *sinkpad = NULL; leo_sobral@805: leo_sobral@805: if ((element = create_element (pbm, factory)) == NULL) { leo_sobral@805: GST_WARNING_OBJECT (pbm, "Could not create an element from %s", leo_sobral@805: gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); leo_sobral@805: g_debug ("Could not create an element from %s", leo_sobral@805: gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory))); leo_sobral@805: leo_sobral@805: continue; renatofilho@787: } renatofilho@787: leo_sobral@805: if (!(add_element (GST_PLAY_BIN_MAEMO (user_data), element))) { leo_sobral@805: GST_WARNING_OBJECT (pbm, "Couldn't set %s to READY", GST_ELEMENT_NAME (element)); leo_sobral@805: gst_object_unref (element); leo_sobral@805: continue; renatofilho@787: } renatofilho@787: leo_sobral@805: if ((gst_element_set_state (element, GST_STATE_READY)) leo_sobral@805: == GST_STATE_CHANGE_FAILURE) { leo_sobral@805: gst_element_set_state (element, GST_STATE_NULL); leo_sobral@805: gst_object_unref (sinkpad); leo_sobral@805: gst_bin_remove (GST_BIN (user_data), element); leo_sobral@805: continue; renatofilho@787: } leo_sobral@805: leo_sobral@805: if (!(sinkpad = find_sink_pad (element))) { leo_sobral@805: GST_WARNING_OBJECT (pbm, "Element %s doesn't have a sink pad", GST_ELEMENT_NAME (element)); leo_sobral@805: g_debug ("Element %s doesn't have a sink pad", GST_ELEMENT_NAME (element)); leo_sobral@805: gst_object_unref (element); leo_sobral@805: continue; leo_sobral@805: } leo_sobral@805: leo_sobral@805: leo_sobral@805: if ((gst_pad_link (pad, sinkpad)) != GST_PAD_LINK_OK) { leo_sobral@805: GST_WARNING_OBJECT (pbm, "Link failed on pad %s:%s", GST_DEBUG_PAD_NAME (sinkpad)); leo_sobral@805: gst_element_set_state (element, GST_STATE_NULL); leo_sobral@805: gst_object_unref (sinkpad); leo_sobral@805: gst_bin_remove (GST_BIN (user_data), element); leo_sobral@805: continue; leo_sobral@805: } leo_sobral@805: leo_sobral@805: gst_object_unref (sinkpad); leo_sobral@805: leo_sobral@805: if ((gst_element_set_state (element, GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) { leo_sobral@805: gst_element_set_state (element, GST_STATE_NULL); leo_sobral@805: gst_bin_remove (GST_BIN (user_data), element); leo_sobral@805: continue; leo_sobral@805: } leo_sobral@805: leo_sobral@805: linked = TRUE; leo_sobral@805: break; leo_sobral@805: } leo_sobral@805: leo_sobral@805: g_list_free (comp); leo_sobral@805: if (linked == FALSE) { leo_sobral@805: g_warning ("GstFlow ERROR"); leo_sobral@805: } leo_sobral@805: GST_PAD_STREAM_UNLOCK (pad); renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: update_volume (GstPlayBinMaemo *pbm) renatofilho@787: { leo_sobral@805: if (pbm->volume_element != NULL) { leo_sobral@805: if (pbm->volume > 0) { leo_sobral@805: g_object_set (G_OBJECT (pbm->volume_element), leo_sobral@805: "volume", pbm->volume, leo_sobral@805: NULL); leo_sobral@805: } else { leo_sobral@805: g_object_set (G_OBJECT (pbm->volume_element), leo_sobral@805: "mute", TRUE, leo_sobral@805: NULL); renatofilho@787: } leo_sobral@805: } renatofilho@787: } renatofilho@787: renatofilho@787: static void renatofilho@787: update_xid (GstPlayBinMaemo *pbm) renatofilho@787: { leo_sobral@805: if ((pbm->video_sink != NULL) && leo_sobral@805: (pbm->xid != -1) && leo_sobral@805: (GST_IS_X_OVERLAY (pbm->video_sink))) { leo_sobral@805: Display *display; leo_sobral@805: g_object_set (G_OBJECT (pbm->video_sink), leo_sobral@805: "force-aspect-ratio", TRUE, NULL); leo_sobral@805: display = XOpenDisplay(NULL); leo_sobral@805: XMapRaised(display, pbm->xid); leo_sobral@808: XSetErrorHandler(x_error_handler); leo_sobral@805: XSync (display, FALSE); renatofilho@792: leo_sobral@805: gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (pbm->video_sink), leo_sobral@805: pbm->xid); leo_sobral@805: } renatofilho@787: } renatofilho@787: leo_sobral@808: static int leo_sobral@808: x_error_handler(Display *display, XErrorEvent *event) leo_sobral@808: { leo_sobral@808: g_debug ("In x error handler:"); leo_sobral@808: leo_sobral@808: switch (event->error_code) { leo_sobral@808: case BadWindow: leo_sobral@808: g_debug ("got bad window"); leo_sobral@808: break; leo_sobral@808: case BadDrawable: leo_sobral@808: g_debug ("got bad drawable"); leo_sobral@808: break; leo_sobral@808: case BadGC: leo_sobral@808: g_debug ("got bad gc"); leo_sobral@808: break; leo_sobral@808: default: leo_sobral@808: g_debug ("unhandled x error = %d", event->error_code); leo_sobral@808: } leo_sobral@808: leo_sobral@808: return 0; leo_sobral@808: } leo_sobral@808: renatofilho@787: static gboolean renatofilho@792: add_element (GstPlayBinMaemo *pbm, renatofilho@792: GstElement *child) renatofilho@792: { leo_sobral@805: if (gst_bin_add (GST_BIN (pbm), child)) { leo_sobral@805: pbm->elements = g_list_append (pbm->elements, child); leo_sobral@805: return TRUE; leo_sobral@805: } leo_sobral@805: return FALSE; renatofilho@792: } renatofilho@792: renatofilho@792: static void renatofilho@792: clear_elements (GstPlayBinMaemo *pbm) renatofilho@792: { leo_sobral@805: GList *walk; renatofilho@792: leo_sobral@805: walk = pbm->elements; renatofilho@792: leo_sobral@805: for (; walk != NULL; walk = walk->next) { leo_sobral@805: GstElement *e = GST_ELEMENT (walk->data); renatofilho@792: leo_sobral@805: gst_element_set_state (e, GST_STATE_NULL); leo_sobral@805: gst_bin_remove (GST_BIN (pbm), e); leo_sobral@805: } renatofilho@792: leo_sobral@805: g_list_free (pbm->elements); leo_sobral@805: pbm->elements = NULL; leo_sobral@805: pbm->source = NULL; leo_sobral@805: pbm->volume_element = NULL; leo_sobral@805: pbm->video_sink = NULL; renatofilho@792: } renatofilho@792: renatofilho@792: static gboolean renatofilho@787: plugin_init(GstPlugin * plugin) renatofilho@787: { renatofilho@787: #ifdef ENABLE_NLS leo_sobral@805: setlocale(LC_ALL, ""); leo_sobral@805: bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); renatofilho@787: #endif /* ENABLE_NLS */ renatofilho@787: leo_sobral@805: if (!gst_element_register(plugin, "playbinmaemo", GST_RANK_SECONDARY, leo_sobral@805: GST_TYPE_PLAY_BIN_MAEMO)) { leo_sobral@805: return FALSE; leo_sobral@805: } renatofilho@787: leo_sobral@805: return TRUE; renatofilho@787: } renatofilho@787: renatofilho@787: GST_PLUGIN_DEFINE(GST_VERSION_MAJOR, renatofilho@787: GST_VERSION_MINOR, renatofilho@787: "playbinmaemo", leo_sobral@796: "A playbin element that uses decodebin2 for automatic playback of audio and video", renatofilho@787: plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, renatofilho@787: GST_PACKAGE_ORIGIN)