gst-gmyth/playbinmaemo/gstplaybinmaemo.c
author melunko
Tue Sep 18 14:09:39 2007 +0100 (2007-09-18)
branchtrunk
changeset 850 b457f7ba9b15
parent 808 215c45290ce3
child 852 2498e4f8c758
permissions -rw-r--r--
[svn r856] debian/changelog updated
     1 /* GStreamer
     2  * Copyright (C) <2007> Renato Araujo Oliveira Filho <renato.filho@indt.org.br>
     3  *
     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.
     8  *
     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.
    13  *
    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.
    18  */
    19 
    20 #ifdef HAVE_CONFIG_H
    21 #include "config.h"
    22 #endif
    23 
    24 #include <glib/gi18n.h>
    25 #include <string.h>
    26 #include <gst/gst.h>
    27 #include <gst/gsterror.h>
    28 #include <gst/gstplugin.h>
    29 #include <gst/interfaces/xoverlay.h>
    30 #include <X11/Xlib.h>
    31 //#include <gst/pbutils/pbutils.h>
    32 #include "gstplaybinmaemo.h"
    33 
    34 
    35 GST_DEBUG_CATEGORY_STATIC (gst_play_bin_maemo_debug);
    36 #define GST_CAT_DEFAULT gst_play_bin_maemo_debug
    37 
    38 #define DEFAULT_VOLUME               1.0
    39 #define DEFAULT_XID                 -1
    40 
    41 /* props */
    42 enum
    43 {
    44   ARG_0,
    45   ARG_URI,
    46   ARG_QUEUE_SIZE,
    47   ARG_QUEUE_MIN_THRESHOLD,
    48   ARG_SOURCE,
    49   ARG_VOLUME,
    50   ARG_PARSE_METADATA,
    51   ARG_XID
    52 };
    53 
    54 static const GstElementDetails gst_play_bin_maemo_details =
    55         GST_ELEMENT_DETAILS("playbinmaemo",
    56                             "Generic/Bin/Player",
    57                             "Autoplug and play media from an uri used on maemo plataform",
    58                             "Renato Araujo Oliveira Filho <renato.filho@indt.org.br>");
    59 
    60 static void     gst_play_bin_maemo_dispose          (GObject * object);
    61 static void     gst_play_bin_maemo_finalize         (GObject * object);
    62 static void     gst_play_bin_maemo_set_property     (GObject * object, guint prop_id,
    63                                                     const GValue * value, GParamSpec * spec);
    64 static void     gst_play_bin_maemo_get_property     (GObject * object, guint prop_id,
    65                                                     GValue * value, GParamSpec * spec);
    66 static GstStateChangeReturn
    67                 gst_play_bin_maemo_change_state     (GstElement *element,
    68                                                     GstStateChange transition);
    69 static gboolean factory_filter_sinks                (GstPluginFeature *feature,
    70                                                     GstPlayBinMaemo *pbm);
    71 static gint     compare_ranks                       (GstPluginFeature * f1,
    72                                                     GstPluginFeature * f2);
    73 static GList    *find_compatibles                   (GstPlayBinMaemo *pbm,
    74                                                      const GstCaps *caps);
    75 static GstPad   *find_sink_pad                      (GstElement * element);
    76 static void     update_volume                       (GstPlayBinMaemo *pbm,
    77                                                      gfloat volume);
    78 static void     update_xid                          (GstPlayBinMaemo *pbm);
    79 static void     new_decoded_pad_cb                  (GstElement *object,
    80                                                      GstPad* pad,
    81                                                      gboolean arg,
    82                                                      gpointer user_data);
    83 static void     removed_decoded_pad_cb              (GstElement *object,
    84                                                      GstPad* pad,
    85                                                      gpointer user_data);
    86 static void     unknown_type_cb                     (GstElement *object,
    87                                                      GstPad *pad,
    88                                                      GstCaps *casp,
    89                                                      gpointer user_data);
    90 static gboolean autoplug_continue_cb                (GstElement* object,
    91                                                      GstCaps* caps,
    92                                                      gpointer user_data);
    93 static gboolean add_element                         (GstPlayBinMaemo *pbm,
    94                                                      GstElement *child);
    95 static void     clear_elements                      (GstPlayBinMaemo *pbm);
    96 static int      x_error_handler                     (Display *display,
    97                                                      XErrorEvent *event);
    98 
    99 GST_BOILERPLATE(GstPlayBinMaemo, gst_play_bin_maemo, GstPipeline, GST_TYPE_PIPELINE)
   100 
   101 
   102 static void
   103 gst_play_bin_maemo_base_init (gpointer klass)
   104 {
   105     GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
   106 
   107     gst_element_class_set_details (element_class, &gst_play_bin_maemo_details);
   108 }
   109 
   110 static void
   111 gst_play_bin_maemo_class_init (GstPlayBinMaemoClass * klass)
   112 {
   113   GObjectClass *gobject_klass;
   114   GstElementClass *gstelement_klass;
   115   GstBinClass *gstbin_klass;
   116 
   117   gobject_klass = (GObjectClass *) klass;
   118   gstelement_klass = (GstElementClass *) klass;
   119   gstbin_klass = (GstBinClass *) klass;
   120 
   121   parent_class = g_type_class_peek_parent (klass);
   122 
   123   gobject_klass->set_property = gst_play_bin_maemo_set_property;
   124   gobject_klass->get_property = gst_play_bin_maemo_get_property;
   125 
   126   g_object_class_install_property (gobject_klass, ARG_URI,
   127       g_param_spec_string ("uri", "URI", "URI of the media to play",
   128           NULL, G_PARAM_READWRITE));
   129 
   130   g_object_class_install_property (gobject_klass, ARG_VOLUME,
   131       g_param_spec_double ("volume", "Audio volume", "volume",
   132                             0.0, 10.0, DEFAULT_VOLUME, G_PARAM_READWRITE));
   133 
   134   g_object_class_install_property (gobject_klass, ARG_XID,
   135       g_param_spec_long ("xid", "xid", "X windown ID",
   136                          -1, G_MAXLONG, DEFAULT_XID, G_PARAM_READWRITE));
   137 
   138   g_object_class_install_property (gobject_klass, ARG_SOURCE,
   139       g_param_spec_object ("source", "Source", "Source element",
   140           GST_TYPE_ELEMENT, G_PARAM_READABLE));
   141 
   142   g_object_class_install_property (gobject_klass, ARG_PARSE_METADATA,
   143       g_param_spec_boolean ("parse-metadata", "Parse Metadata", "Parse metadata info",
   144           TRUE, G_PARAM_READWRITE));
   145 
   146 
   147   GST_DEBUG_CATEGORY_INIT (gst_play_bin_maemo_debug, "playbinmaemo", 0,
   148       "playbinmaemo");
   149 
   150   gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_bin_maemo_dispose);
   151   gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_bin_maemo_finalize);
   152 
   153   gstelement_klass->change_state =
   154       GST_DEBUG_FUNCPTR (gst_play_bin_maemo_change_state);
   155 }
   156 
   157 static void
   158 gst_play_bin_maemo_init (GstPlayBinMaemo * play_bin_maemo, GstPlayBinMaemoClass *class)
   159 {
   160   GList *factories;
   161 
   162   play_bin_maemo->uri = NULL;
   163   play_bin_maemo->source = NULL;
   164 
   165   play_bin_maemo->volume = DEFAULT_VOLUME;
   166   play_bin_maemo->xid = DEFAULT_XID;
   167   play_bin_maemo->parse_metadata = TRUE;
   168 
   169   factories = gst_default_registry_feature_filter (
   170                 (GstPluginFeatureFilter) factory_filter_sinks,
   171                  FALSE, play_bin_maemo);
   172 
   173   play_bin_maemo->factories = g_list_sort (factories,
   174                                            (GCompareFunc) compare_ranks);
   175 }
   176 
   177 static void
   178 gst_play_bin_maemo_dispose (GObject * object)
   179 {
   180   GstPlayBinMaemo *play_bin_maemo;
   181 
   182   play_bin_maemo = GST_PLAY_BIN_MAEMO (object);
   183   g_free (play_bin_maemo->uri);
   184   play_bin_maemo->uri = NULL;
   185 
   186   G_OBJECT_CLASS (parent_class)->dispose (object);
   187 }
   188 
   189 static void
   190 gst_play_bin_maemo_finalize (GObject * object)
   191 {
   192   clear_elements (GST_PLAY_BIN_MAEMO (object));
   193   G_OBJECT_CLASS (parent_class)->finalize (object);
   194 }
   195 
   196 static gboolean
   197 array_has_value (const gchar * values[], const gchar * value)
   198 {
   199   gint i;
   200 
   201   for (i = 0; values[i]; i++) {
   202     if (g_str_has_prefix (value, values[i]))
   203       return TRUE;
   204   }
   205   return FALSE;
   206 }
   207 
   208 /* list of URIs that we consider to be streams and that need buffering.
   209  * We have no mechanism yet to figure this out with a query. */
   210 static const gchar *stream_uris[] = { "http://", "mms://", "mmsh://",
   211   "mmsu://", "mmst://", NULL
   212 };
   213 
   214 /* blacklisted URIs, we know they will always fail. */
   215 static const gchar *blacklisted_uris[] = { NULL };
   216 
   217 /* mime types that we don't consider to be media types */
   218 /*
   219 static const gchar *no_media_mimes[] = {
   220   "application/x-executable", "application/x-bzip", "application/x-gzip",
   221   "application/zip", "application/x-compress", NULL
   222 };
   223 */
   224 
   225 /* mime types we consider raw media */
   226 static const gchar *raw_mimes[] = {
   227   "audio/x-raw", "video/x-raw", NULL
   228 };
   229 
   230 #define IS_STREAM_URI(uri)          (array_has_value (stream_uris, uri))
   231 #define IS_BLACKLISTED_URI(uri)     (array_has_value (blacklisted_uris, uri))
   232 #define IS_NO_MEDIA_MIME(mime)      (array_has_value (no_media_mimes, mime))
   233 #define IS_RAW_MIME(mime)           (array_has_value (raw_mimes, mime))
   234 
   235 /*
   236  * Generate and configure a source element.
   237  */
   238 static GstElement *
   239 gen_source_element (GstPlayBinMaemo * play_bin_maemo)
   240 {
   241   GstElement *source;
   242 
   243   if (!play_bin_maemo->uri)
   244     goto no_uri;
   245 
   246   if (!gst_uri_is_valid (play_bin_maemo->uri))
   247     goto invalid_uri;
   248 
   249   if (IS_BLACKLISTED_URI (play_bin_maemo->uri))
   250     goto uri_blacklisted;
   251 
   252   source = gst_element_make_from_uri (GST_URI_SRC, play_bin_maemo->uri, "source");
   253   if (!source)
   254     goto no_source;
   255 
   256   play_bin_maemo->is_stream = IS_STREAM_URI (play_bin_maemo->uri);
   257 
   258   /* make HTTP sources send extra headers so we get icecast
   259    * metadata in case the stream is an icecast stream */
   260   if (!strncmp (play_bin_maemo->uri, "http://", 7) &&
   261       g_object_class_find_property (G_OBJECT_GET_CLASS (source),
   262           "iradio-mode")) {
   263     g_object_set (source, "iradio-mode", TRUE, NULL);
   264   }
   265   return source;
   266 
   267   /* ERRORS */
   268 no_uri:
   269   {
   270     GST_ELEMENT_ERROR (play_bin_maemo, RESOURCE, NOT_FOUND,
   271         (_("No URI specified to play from.")), (NULL));
   272     return NULL;
   273   }
   274 invalid_uri:
   275   {
   276     GST_ELEMENT_ERROR (play_bin_maemo, RESOURCE, NOT_FOUND,
   277         (_("Invalid URI \"%s\"."), play_bin_maemo->uri), (NULL));
   278     return NULL;
   279   }
   280 uri_blacklisted:
   281   {
   282     GST_ELEMENT_ERROR (play_bin_maemo, RESOURCE, FAILED,
   283         (_("RTSP streams cannot be played yet.")), (NULL));
   284     return NULL;
   285   }
   286 no_source:
   287   {
   288     gchar *prot = gst_uri_get_protocol (play_bin_maemo->uri);
   289 
   290     /* whoops, could not create the source element, dig a little deeper to
   291      * figure out what might be wrong. */
   292     if (prot) {
   293        /*
   294       gchar *desc;
   295 
   296       gst_element_post_message (GST_ELEMENT (play_bin_maemo),
   297           gst_missing_uri_source_message_new (GST_ELEMENT (play_bin_maemo),
   298               prot));
   299 
   300       desc = gst_pb_utils_get_source_description (prot);
   301       GST_ELEMENT_ERROR (play_bin_maemo, CORE, MISSING_PLUGIN,
   302           (_("A %s plugin is required to play this stream, but not installed."),
   303               desc), ("No URI handler for %s", prot));
   304       g_free (desc);
   305       */
   306       g_free (prot);
   307     } else
   308       goto invalid_uri;
   309 
   310     return NULL;
   311   }
   312 }
   313 
   314 static void
   315 prepare_elements (GstPlayBinMaemo *pbm)
   316 {
   317   GstElement *decoder;
   318   GstElement *queue;
   319 
   320   decoder = gst_element_factory_make ("decodebin2", "decode");
   321   add_element (pbm, decoder);
   322   g_signal_connect (G_OBJECT (decoder),
   323                     "autoplug-continue",
   324                     G_CALLBACK (autoplug_continue_cb),
   325                     pbm);
   326 
   327   g_signal_connect (G_OBJECT (decoder),
   328                     "unknown-type",
   329                     G_CALLBACK (unknown_type_cb),
   330                     pbm);
   331 
   332   g_signal_connect (G_OBJECT (decoder),
   333                     "new-decoded-pad",
   334                     G_CALLBACK (new_decoded_pad_cb),
   335                     pbm);
   336 
   337   g_signal_connect (G_OBJECT (decoder),
   338                     "removed-decoded-pad",
   339                     G_CALLBACK (removed_decoded_pad_cb),
   340                     pbm);
   341 
   342 
   343   queue = gst_element_factory_make ("queue", NULL);
   344   add_element (pbm, queue);
   345 
   346   if (gst_element_link_many (pbm->source, queue, decoder, NULL) == FALSE) {
   347     GST_WARNING ("FAIL TO LINK SRC WITH DECODEBIN2");
   348   }
   349 }
   350 
   351 static gboolean
   352 setup_source (GstPlayBinMaemo *pbm)
   353 {
   354   if (!pbm->need_rebuild)
   355     return TRUE;
   356 
   357   clear_elements (pbm);
   358 
   359   GST_DEBUG_OBJECT (pbm, "setup source");
   360 
   361   pbm->has_metadata = FALSE;
   362 
   363   /* create and configure an element that can handle the uri */
   364   if (!(pbm->source = gen_source_element (pbm)))
   365     goto no_source;
   366 
   367   add_element (pbm, pbm->source);
   368 
   369 
   370 #if 0
   371     if (verify_src_have_sink (pbm)) {
   372         /* source can be linked with sinks directly */
   373         return TRUE;
   374     }
   375 #endif
   376 
   377   prepare_elements (pbm);
   378 
   379   return TRUE;
   380 
   381 no_source:
   382   return FALSE;
   383 }
   384 
   385 static void
   386 gst_play_bin_maemo_set_property (GObject *object,
   387                                  guint prop_id,
   388                                  const GValue *value,
   389                                  GParamSpec *pspec)
   390 {
   391   GstPlayBinMaemo *play_bin_maemo;
   392 
   393   g_return_if_fail (GST_IS_PLAY_BIN_MAEMO (object));
   394 
   395   play_bin_maemo = GST_PLAY_BIN_MAEMO (object);
   396 
   397   switch (prop_id) {
   398     case ARG_URI:
   399     {
   400       const gchar *uri = g_value_get_string (value);
   401 
   402       if (uri == NULL) {
   403         GST_WARNING ("cannot set NULL uri");
   404         return;
   405       }
   406       /* if we have no previous uri, or the new uri is different from the
   407        * old one, replug */
   408       if (play_bin_maemo->uri == NULL || strcmp (play_bin_maemo->uri, uri) != 0) {
   409         g_free (play_bin_maemo->uri);
   410         play_bin_maemo->uri = g_strdup (uri);
   411 
   412         GST_DEBUG ("setting new uri to %s", uri);
   413 
   414         play_bin_maemo->need_rebuild = TRUE;
   415       }
   416       break;
   417     }
   418     case ARG_VOLUME:
   419       update_volume(play_bin_maemo, g_value_get_double (value));
   420       break;
   421     case ARG_XID:
   422     {
   423       long xid;
   424       xid = g_value_get_long (value);
   425       if (play_bin_maemo->xid != xid)
   426       {
   427           play_bin_maemo->xid = xid;
   428           update_xid (play_bin_maemo);
   429       }
   430       break;
   431     }
   432     case ARG_PARSE_METADATA:
   433         play_bin_maemo->parse_metadata = g_value_get_boolean (value);
   434         break;
   435     default:
   436       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
   437       break;
   438   }
   439 }
   440 
   441 static void
   442 gst_play_bin_maemo_get_property (GObject * object,
   443                                  guint prop_id,
   444                                  GValue * value,
   445                                  GParamSpec * pspec)
   446 {
   447   GstPlayBinMaemo *play_bin_maemo;
   448 
   449   g_return_if_fail (GST_IS_PLAY_BIN_MAEMO (object));
   450 
   451   play_bin_maemo = GST_PLAY_BIN_MAEMO (object);
   452 
   453   switch (prop_id) {
   454     case ARG_URI:
   455       g_value_set_string (value, play_bin_maemo->uri);
   456       break;
   457     case ARG_SOURCE:
   458       g_value_set_object (value, play_bin_maemo->source);
   459       break;
   460     case ARG_VOLUME:
   461       g_value_set_double (value, play_bin_maemo->volume);
   462       break;
   463     case ARG_XID:
   464       g_value_set_long (value, play_bin_maemo->xid);
   465       break;
   466     case ARG_PARSE_METADATA:
   467       g_value_set_boolean (value, play_bin_maemo->parse_metadata);
   468       break;
   469     default:
   470       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
   471       break;
   472   }
   473 }
   474 
   475 static GstStateChangeReturn
   476 gst_play_bin_maemo_change_state (GstElement * element,
   477                                  GstStateChange transition)
   478 {
   479   GstStateChangeReturn ret;
   480   GstPlayBinMaemo *play_bin_maemo;
   481 
   482   play_bin_maemo = GST_PLAY_BIN_MAEMO (element);
   483 
   484   switch (transition) {
   485     case GST_STATE_CHANGE_READY_TO_PAUSED:
   486       if (!setup_source (play_bin_maemo))
   487         goto source_failed;
   488       break;
   489     default:
   490       break;
   491   }
   492 
   493   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
   494 
   495   switch (transition) {
   496     case GST_STATE_CHANGE_READY_TO_PAUSED:
   497       if (ret == GST_STATE_CHANGE_FAILURE) {
   498         play_bin_maemo->need_rebuild = TRUE;
   499         return GST_STATE_CHANGE_FAILURE;
   500       }
   501       break;
   502       /* clean-up in both cases, READY=>NULL clean-up is if there was an error */
   503     case GST_STATE_CHANGE_PAUSED_TO_READY:
   504     case GST_STATE_CHANGE_READY_TO_NULL:
   505       play_bin_maemo->need_rebuild = TRUE;
   506       clear_elements (play_bin_maemo);
   507       break;
   508     default:
   509       break;
   510   }
   511   return ret;
   512 
   513   /* ERRORS */
   514 source_failed:
   515   {
   516     play_bin_maemo->need_rebuild = TRUE;
   517 
   518     return GST_STATE_CHANGE_FAILURE;
   519   }
   520 }
   521 
   522 static gboolean
   523 factory_filter_sinks (GstPluginFeature *feature,
   524                       GstPlayBinMaemo *pbm)
   525 {
   526   guint rank;
   527   const gchar *klass;
   528 
   529   if (!GST_IS_ELEMENT_FACTORY (feature))
   530     return FALSE;
   531 
   532   klass = gst_element_factory_get_klass (GST_ELEMENT_FACTORY (feature));
   533 
   534   if ((strstr (klass, "Sink/Video") == NULL) && (strstr (klass, "Sink/Audio") == NULL))
   535     return FALSE;
   536 
   537   GST_DEBUG_OBJECT (pbm, "Filtered: %s",
   538         gst_element_factory_get_longname ((GST_ELEMENT_FACTORY (feature))));
   539   rank = gst_plugin_feature_get_rank (feature);
   540   if (rank < GST_RANK_MARGINAL)
   541     return FALSE;
   542 
   543   return TRUE;
   544 }
   545 
   546 static gint
   547 compare_ranks (GstPluginFeature * f1,
   548                GstPluginFeature * f2)
   549 {
   550   gint diff;
   551   const gchar *rname1, *rname2;
   552 
   553   diff = gst_plugin_feature_get_rank (f2) - gst_plugin_feature_get_rank (f1);
   554   if (diff != 0)
   555     return diff;
   556 
   557   rname1 = gst_plugin_feature_get_name (f1);
   558   rname2 = gst_plugin_feature_get_name (f2);
   559 
   560   diff = strcmp (rname2, rname1);
   561 
   562   return diff;
   563 }
   564 
   565 
   566 static GList *
   567 find_compatibles (GstPlayBinMaemo *pbm,
   568                   const GstCaps *caps)
   569 {
   570   GList *factories;
   571   GList *to_try = NULL;
   572 
   573   /* loop over all the factories */
   574   for (factories = pbm->factories; factories; factories = g_list_next (factories)) {
   575     GstElementFactory *factory = GST_ELEMENT_FACTORY (factories->data);
   576     const GList *templates;
   577     GList *walk;
   578 
   579     /* get the templates from the element factory */
   580     templates = gst_element_factory_get_static_pad_templates (factory);
   581     for (walk = (GList *) templates; walk; walk = g_list_next (walk)) {
   582       GstStaticPadTemplate *templ = walk->data;
   583 
   584       /* we only care about the sink templates */
   585       if (templ->direction == GST_PAD_SINK) {
   586         GstCaps *intersect;
   587         GstCaps *tmpl_caps;
   588 
   589         /* try to intersect the caps with the caps of the template */
   590         tmpl_caps = gst_static_caps_get (&templ->static_caps);
   591 
   592         intersect = gst_caps_intersect (caps, tmpl_caps);
   593 
   594         gst_caps_unref (tmpl_caps);
   595 
   596         /* check if the intersection is empty */
   597         if (!gst_caps_is_empty (intersect)) {
   598           /* non empty intersection, we can use this element */
   599           to_try = g_list_prepend (to_try, factory);
   600           gst_caps_unref (intersect);
   601           break;
   602         }
   603         gst_caps_unref (intersect);
   604       }
   605     }
   606   }
   607   to_try = g_list_reverse (to_try);
   608 
   609   return to_try;
   610 }
   611 
   612 static gboolean
   613 autoplug_continue_cb (GstElement* object,
   614                       GstCaps* caps,
   615                       gpointer user_data)
   616 {
   617     GList *comp = NULL;
   618     gboolean ret = TRUE;
   619     GstPlayBinMaemo *pbm;
   620 
   621     pbm = GST_PLAY_BIN_MAEMO (user_data);
   622 
   623     //TODO: fix this to work with all metadata elements
   624     if (pbm->parse_metadata) {
   625         gchar *caps_str = gst_caps_to_string (caps);
   626         if ((strstr (caps_str, "id3") != NULL) &&
   627             (pbm->has_metadata == FALSE)) {
   628 
   629             g_free (caps_str);
   630             pbm->has_metadata = TRUE;
   631             return ret;
   632         }
   633         g_free (caps_str);
   634     }
   635 
   636     comp = find_compatibles (GST_PLAY_BIN_MAEMO (user_data), caps);
   637     if (comp != NULL) {
   638         g_list_free (comp);
   639         ret = FALSE;
   640     }
   641 
   642     return ret;
   643 }
   644 
   645 static void
   646 unknown_type_cb (GstElement *object,
   647                  GstPad *pad,
   648                  GstCaps *caps,
   649                  gpointer user_data)
   650 {
   651   GST_DEBUG ("unknown_type_cb: %s", gst_caps_to_string (caps));
   652 }
   653 
   654 static GstPad*
   655 find_sink_pad (GstElement * element)
   656 {
   657   GstIterator *it;
   658   GstPad *pad = NULL;
   659   gpointer point;
   660 
   661   it = gst_element_iterate_sink_pads (element);
   662 
   663   if ((gst_iterator_next (it, &point)) == GST_ITERATOR_OK)
   664     pad = (GstPad *) point;
   665 
   666   gst_iterator_free (it);
   667 
   668   return pad;
   669 }
   670 
   671 static GstElement*
   672 create_element (GstPlayBinMaemo *pbm,
   673                 GstElementFactory *factory)
   674 {
   675   GstElement *queue;
   676   GstElement *bin = NULL;
   677   GstElement *element;
   678   GstPad *pad;
   679 
   680   element = gst_element_factory_create (factory, NULL);
   681   if (element == NULL)
   682     goto error;
   683 
   684   bin = gst_bin_new (NULL);
   685   if (bin == NULL)
   686     goto error;
   687 
   688   queue = gst_element_factory_make ("queue", NULL);
   689   gst_bin_add (GST_BIN (bin), queue);
   690 
   691   if (strstr (gst_element_factory_get_klass (factory), "Sink/Video") != NULL) {
   692     GstElement *colorspace;
   693 
   694     colorspace = gst_element_factory_make ("ffmpegcolorspace", NULL);
   695     if (colorspace == NULL)
   696       goto error;
   697 
   698     gst_bin_add (GST_BIN (bin), colorspace);
   699     if (gst_element_link (queue, colorspace) == FALSE) {
   700       GST_WARNING_OBJECT (pbm, "Failed to link queue and colorspace");
   701       gst_element_set_state (colorspace, GST_STATE_NULL);
   702       gst_object_unref (colorspace);
   703       goto error;
   704     }
   705 
   706     gst_bin_add (GST_BIN (bin), element);
   707     if (gst_element_link (colorspace, element) == FALSE) {
   708       GST_WARNING_OBJECT (pbm, "Failed to link colorspace and sink video: %s",
   709                           GST_ELEMENT_NAME (element));
   710       gst_element_set_state (colorspace, GST_STATE_NULL);
   711       gst_object_unref (colorspace);
   712       goto error;
   713     }
   714 
   715     pbm->video_sink = element;
   716     update_xid (pbm);
   717 
   718   } else if (strstr (gst_element_factory_get_klass (factory), "Sink/Audio") != NULL) {
   719     GParamSpec *vol_spec;
   720     GstElement *prev;
   721 
   722     prev = queue;
   723     vol_spec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), "volume");
   724 
   725     if (vol_spec == NULL) {
   726       GstElement *conv;
   727       GstElement *volume;
   728 
   729       conv = gst_element_factory_make ("audioconvert", "aconv");
   730       if (conv == NULL)
   731         goto error;
   732 
   733       gst_bin_add (GST_BIN_CAST (bin), conv);
   734 
   735       gst_element_link (queue, conv);
   736 
   737       volume = gst_element_factory_make ("volume", "volume");
   738       if (volume == NULL)
   739         goto error;
   740 
   741       gst_bin_add (GST_BIN (bin), volume);
   742       if (gst_element_link (conv, volume) == FALSE) {
   743         GST_WARNING_OBJECT (pbm, "Failed to link queue and volume");
   744         gst_element_set_state (volume, GST_STATE_NULL);
   745         gst_object_unref (volume);
   746         goto error;
   747       }
   748 
   749       prev = volume;
   750     } else {
   751       g_param_spec_unref (vol_spec);
   752     }
   753 
   754     gst_bin_add (GST_BIN (bin), element);
   755     if (gst_element_link (prev, element) == FALSE) {
   756       GST_WARNING_OBJECT (pbm, "Failed to link volume and sink audio: %s", GST_ELEMENT_NAME (element));
   757       if (prev != queue) {
   758         gst_element_set_state (prev, GST_STATE_NULL);
   759         gst_object_unref (prev);
   760       }
   761       goto error;
   762     }
   763     pbm->volume_element = (prev != queue) ? prev : element;
   764     update_volume (pbm, pbm->volume);
   765   }
   766 
   767   pad = gst_element_get_pad (queue, "sink");
   768   gst_element_add_pad (bin, gst_ghost_pad_new ("sink", pad));
   769   gst_object_unref (pad);
   770 
   771   return bin;
   772 
   773 error:
   774   GST_WARNING_OBJECT (pbm, "Error creating pipeline");
   775 
   776   gst_element_set_state (bin, GST_STATE_NULL);
   777   gst_object_unref (bin);
   778 
   779   return NULL;
   780 }
   781 
   782 static void
   783 new_decoded_pad_cb (GstElement *object,
   784                     GstPad* pad,
   785                     gboolean arg,
   786                     gpointer user_data)
   787 {
   788   GList *comp = NULL;
   789   GList *walk;
   790   GstCaps *caps;
   791   gboolean linked;
   792   GstPlayBinMaemo *pbm;
   793 
   794   pbm = GST_PLAY_BIN_MAEMO (user_data);
   795   caps = gst_pad_get_caps (pad);
   796 
   797   GST_DEBUG_OBJECT (pbm, "new_decoded_pad_cb: %s", gst_caps_to_string (caps));
   798 
   799   comp = find_compatibles (GST_PLAY_BIN_MAEMO (user_data), caps);
   800 
   801   if (comp == NULL) {
   802     GST_WARNING ("flow error: dont find comaptible");
   803     return;
   804   }
   805 
   806   GST_PAD_STREAM_LOCK (pad);
   807 
   808   linked = FALSE;
   809   for (walk=comp; walk != NULL; walk = walk->next) {
   810     GstElementFactory *factory = (GstElementFactory *) walk->data;
   811     GstElement *element;
   812     GstPad *sinkpad = NULL;
   813     gint result;
   814 
   815     if ((element = create_element (pbm, factory)) == NULL) {
   816       GST_WARNING_OBJECT (pbm, "Could not create an element from %s",
   817           gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
   818       continue;
   819     }
   820 
   821     if (!(sinkpad = find_sink_pad (element))) {
   822       GST_WARNING_OBJECT (pbm, "Element %s doesn't have a sink pad", GST_ELEMENT_NAME (element));
   823       gst_object_unref (element);
   824       continue;
   825     }
   826 
   827     if (!(add_element (GST_PLAY_BIN_MAEMO (user_data), element))) {
   828       GST_WARNING_OBJECT (pbm, "Couldn't add element %s to bin", GST_ELEMENT_NAME (element));
   829       gst_object_unref (sinkpad);
   830       gst_object_unref (element);
   831       continue;
   832     }
   833 
   834     if ((gst_element_set_state (element, GST_STATE_READY))
   835                    == GST_STATE_CHANGE_FAILURE) {
   836       GST_WARNING_OBJECT (pbm, "Couldn't set %s to READY", GST_ELEMENT_NAME (element));
   837       gst_object_unref (sinkpad);
   838       gst_bin_remove (GST_BIN (user_data), element);
   839       continue;
   840     }
   841 
   842     if (gst_pad_link (pad, sinkpad) != GST_PAD_LINK_OK) {
   843       GST_WARNING_OBJECT (pbm, "Link failed on pad %s:%s", GST_DEBUG_PAD_NAME (sinkpad));
   844       gst_element_set_state (element, GST_STATE_NULL);
   845       gst_object_unref (sinkpad);
   846       gst_bin_remove (GST_BIN (user_data), element);
   847       continue;
   848     }
   849 
   850     gst_object_unref (sinkpad);
   851 
   852     if ((gst_element_set_state (element, GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE) {
   853       gst_element_set_state (element, GST_STATE_NULL);
   854       gst_bin_remove (GST_BIN (user_data), element);
   855       continue;
   856     }
   857 
   858     linked = TRUE;
   859     break;
   860   }
   861 
   862   g_list_free (comp);
   863   if (linked == FALSE) {
   864     g_debug ("GstFlow Error");
   865     GST_WARNING ("GstFlow ERROR");
   866   }
   867   GST_PAD_STREAM_UNLOCK (pad);
   868 }
   869 
   870 static void
   871 removed_decoded_pad_cb (GstElement *object,
   872                         GstPad* pad,
   873                         gpointer user_data)
   874 {
   875   GstElement *sink;
   876 
   877   GST_DEBUG("removed_decoded_pad_cb");
   878 
   879   sink = gst_pad_get_parent_element (pad);
   880 
   881   if (sink)
   882     gst_bin_remove (GST_BIN (user_data), sink);
   883 }
   884 
   885 static GValue*
   886 convert_volume_base (GstPlayBinMaemo *pbm, gfloat volume)
   887 {
   888   GValue value = { 0, };
   889   GValue *converted_vol = g_new0(GValue, 1);
   890 
   891   GParamSpec* vol_spec = g_object_class_find_property (
   892     G_OBJECT_GET_CLASS (pbm->volume_element), "volume");
   893 
   894   g_value_init (&value, vol_spec->value_type);
   895   g_value_init (converted_vol, vol_spec->value_type);
   896 
   897   g_object_get_property (G_OBJECT (pbm->volume_element), "volume", &value);
   898 
   899   /* convert volume from double to int range if needed */
   900   switch (G_VALUE_TYPE (&value)) {
   901     case G_TYPE_UINT:
   902     {
   903       GParamSpecUInt *puint = G_PARAM_SPEC_UINT (vol_spec);
   904       guint scale = puint->maximum - puint->minimum;
   905       guint vol_guint = (guint) ((scale * volume) + puint->minimum);
   906 
   907       GST_WARNING ("Range: %u - %u, Converted: %u",
   908           puint->minimum, puint->maximum, vol_guint);
   909       g_value_set_uint (converted_vol, vol_guint);
   910       break;
   911     }
   912     case G_TYPE_INT:
   913     {
   914       GParamSpecInt *pint = G_PARAM_SPEC_INT (vol_spec);
   915       gint scale = pint->maximum - pint->minimum;
   916       gint vol_gint = (gint) ((scale * volume) + pint->minimum);
   917 
   918       GST_WARNING ("Range: %d - %d, Converted: %d",
   919           pint->minimum, pint->maximum, vol_gint);
   920       g_value_set_int (converted_vol, vol_gint);
   921       break;
   922     }
   923     case G_TYPE_DOUBLE:
   924     case G_TYPE_FLOAT:
   925     {
   926       GST_WARNING ("Default converted to float: %f", volume);
   927       g_value_set_double (converted_vol, volume);
   928       break;
   929     }
   930     default:
   931       GST_WARNING ("Dont know how to convert volume");
   932   }
   933 
   934   return converted_vol;
   935 }
   936 
   937 static void
   938 update_volume (GstPlayBinMaemo *pbm, gfloat volume)
   939 {
   940   pbm->volume = volume;
   941   if (pbm->volume_element) {
   942     GValue *converted_vol = convert_volume_base (pbm, volume);
   943     g_object_set_property (G_OBJECT (pbm->volume_element), "volume",
   944                            converted_vol);
   945     g_value_unset (converted_vol);
   946   }
   947 }
   948 
   949 static void
   950 update_xid (GstPlayBinMaemo *pbm)
   951 {
   952   if ((pbm->video_sink != NULL) && (pbm->xid != -1) &&
   953       (GST_IS_X_OVERLAY (pbm->video_sink))) {
   954     Display *display;
   955     g_object_set (G_OBJECT (pbm->video_sink),
   956                   "force-aspect-ratio", TRUE, NULL);
   957 
   958     display = XOpenDisplay(NULL);
   959 
   960     XMapRaised(display, pbm->xid);
   961     XSync (display, FALSE);
   962 
   963     XSetErrorHandler(x_error_handler);
   964 
   965     gst_x_overlay_set_xwindow_id (GST_X_OVERLAY (pbm->video_sink),
   966                                   pbm->xid);
   967   }
   968 }
   969 
   970 static int
   971 x_error_handler(Display *display,
   972                 XErrorEvent *event)
   973 {
   974     GST_DEBUG ("In x error handler:");
   975 
   976     switch (event->error_code) {
   977         case BadWindow:
   978             GST_DEBUG ("got bad window");
   979             break;
   980         case BadDrawable:
   981             GST_DEBUG ("got bad drawable");
   982             break;
   983         case BadGC:
   984             GST_DEBUG ("got bad gc");
   985             break;
   986         default:
   987             GST_DEBUG ("unhandled x error = %d", event->error_code);
   988     }
   989 
   990     return 0;
   991 }
   992 
   993 static gboolean
   994 add_element (GstPlayBinMaemo *pbm,
   995              GstElement *child)
   996 {
   997   if (gst_bin_add (GST_BIN (pbm), child)) {
   998     pbm->elements = g_list_append (pbm->elements, child);
   999     return TRUE;
  1000   }
  1001   return FALSE;
  1002 }
  1003 
  1004 static void
  1005 clear_elements (GstPlayBinMaemo *pbm)
  1006 {
  1007   GList *walk;
  1008 
  1009   walk = pbm->elements;
  1010 
  1011   for (; walk != NULL; walk = walk->next) {
  1012     GstElement *e = GST_ELEMENT (walk->data);
  1013 
  1014     gst_element_set_state (e, GST_STATE_NULL);
  1015     gst_bin_remove (GST_BIN (pbm), e);
  1016   }
  1017 
  1018   g_list_free (pbm->elements);
  1019   pbm->elements = NULL;
  1020   pbm->source = NULL;
  1021   pbm->volume_element = NULL;
  1022   pbm->video_sink = NULL;
  1023 }
  1024 
  1025 static gboolean
  1026 plugin_init(GstPlugin * plugin)
  1027 {
  1028 #ifdef ENABLE_NLS
  1029   setlocale(LC_ALL, "");
  1030   bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
  1031 #endif                          /* ENABLE_NLS */
  1032 
  1033   if (!gst_element_register(plugin, "playbinmaemo", GST_RANK_SECONDARY,
  1034                           GST_TYPE_PLAY_BIN_MAEMO)) {
  1035     return FALSE;
  1036   }
  1037 
  1038   return TRUE;
  1039 }
  1040 
  1041 GST_PLUGIN_DEFINE(GST_VERSION_MAJOR,
  1042                   GST_VERSION_MINOR,
  1043                   "playbinmaemo",
  1044                   "A playbin element that uses decodebin2 for automatic playback of audio and video",
  1045                   plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME,
  1046                   GST_PACKAGE_ORIGIN)