gst_plugins_base/gst/playback/gstplaysink.c
changeset 0 0e761a78d257
child 8 4a7fac7dd34a
equal deleted inserted replaced
-1:000000000000 0:0e761a78d257
       
     1 /* GStreamer
       
     2  * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
       
     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 <string.h>
       
    25 #include <gst/gst.h>
       
    26 
       
    27 #include <gst/gst-i18n-plugin.h>
       
    28 #include <gst/pbutils/pbutils.h>
       
    29 
       
    30 #include "gstplaysink.h"
       
    31 
       
    32 #ifdef __SYMBIAN32__
       
    33 #include <glib_global.h>
       
    34 #endif
       
    35 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
       
    36 #define GST_CAT_DEFAULT gst_play_sink_debug
       
    37 
       
    38 #define VOLUME_MAX_DOUBLE 10.0
       
    39 
       
    40 /* holds the common data fields for the audio and video pipelines. We keep them
       
    41  * in a structure to more easily have all the info available. */
       
    42 typedef struct
       
    43 {
       
    44   GstPlaySink *playsink;
       
    45   GstPad *sinkpad;
       
    46   GstElement *bin;
       
    47   gboolean added;
       
    48   gboolean activated;
       
    49 } GstPlayChain;
       
    50 
       
    51 typedef struct
       
    52 {
       
    53   GstPlayChain chain;
       
    54   GstElement *queue;
       
    55   GstElement *conv;
       
    56   GstElement *resample;
       
    57   GstElement *volume;           /* element with the volume property */
       
    58   GstElement *mute;             /* element with the mute property */
       
    59   GstElement *sink;
       
    60 } GstPlayAudioChain;
       
    61 
       
    62 typedef struct
       
    63 {
       
    64   GstPlayChain chain;
       
    65   GstElement *queue;
       
    66   GstElement *conv;
       
    67   GstElement *scale;
       
    68   GstElement *sink;
       
    69   gboolean async;
       
    70 } GstPlayVideoChain;
       
    71 
       
    72 typedef struct
       
    73 {
       
    74   GstPlayChain chain;
       
    75   GstElement *queue;
       
    76   GstElement *conv;
       
    77   GstElement *resample;
       
    78   GstPad *blockpad;             /* srcpad of resample, used for switching the vis */
       
    79   GstPad *vissinkpad;           /* visualisation sinkpad, */
       
    80   GstElement *vis;
       
    81   GstPad *vissrcpad;            /* visualisation srcpad, */
       
    82   GstPad *srcpad;               /* outgoing srcpad, used to connect to the next
       
    83                                  * chain */
       
    84 } GstPlayVisChain;
       
    85 
       
    86 #define GST_PLAY_SINK_GET_LOCK(playsink) (((GstPlaySink *)playsink)->lock)
       
    87 #define GST_PLAY_SINK_LOCK(playsink)     g_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink))
       
    88 #define GST_PLAY_SINK_UNLOCK(playsink)   g_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink))
       
    89 
       
    90 struct _GstPlaySink
       
    91 {
       
    92   GstBin bin;
       
    93 
       
    94   GMutex *lock;
       
    95 
       
    96   GstPlayFlags flags;
       
    97 
       
    98   GstPlayChain *audiochain;
       
    99   GstPlayChain *videochain;
       
   100   GstPlayChain *vischain;
       
   101 
       
   102   GstPad *audio_pad;
       
   103   gboolean audio_pad_raw;
       
   104   GstElement *audio_tee;
       
   105   GstPad *audio_tee_sink;
       
   106   GstPad *audio_tee_asrc;
       
   107   GstPad *audio_tee_vissrc;
       
   108 
       
   109   GstPad *video_pad;
       
   110   gboolean video_pad_raw;
       
   111 
       
   112   GstPad *text_pad;
       
   113 
       
   114   /* properties */
       
   115   GstElement *audio_sink;
       
   116   GstElement *video_sink;
       
   117   GstElement *visualisation;
       
   118   gfloat volume;
       
   119   gboolean mute;
       
   120   gchar *font_desc;             /* font description */
       
   121   guint connection_speed;       /* connection speed in bits/sec (0 = unknown) */
       
   122 
       
   123   /* internal elements */
       
   124   GstElement *textoverlay_element;
       
   125 };
       
   126 
       
   127 struct _GstPlaySinkClass
       
   128 {
       
   129   GstBinClass parent_class;
       
   130 };
       
   131 
       
   132 
       
   133 /* props */
       
   134 enum
       
   135 {
       
   136   PROP_0,
       
   137   PROP_AUDIO_SINK,
       
   138   PROP_VIDEO_SINK,
       
   139   PROP_VIS_PLUGIN,
       
   140   PROP_VOLUME,
       
   141   PROP_FRAME,
       
   142   PROP_FONT_DESC,
       
   143   PROP_LAST
       
   144 };
       
   145 
       
   146 /* signals */
       
   147 enum
       
   148 {
       
   149   LAST_SIGNAL
       
   150 };
       
   151 
       
   152 static void gst_play_sink_class_init (GstPlaySinkClass * klass);
       
   153 static void gst_play_sink_init (GstPlaySink * playsink);
       
   154 static void gst_play_sink_dispose (GObject * object);
       
   155 static void gst_play_sink_finalize (GObject * object);
       
   156 
       
   157 static void gst_play_sink_set_property (GObject * object, guint prop_id,
       
   158     const GValue * value, GParamSpec * spec);
       
   159 static void gst_play_sink_get_property (GObject * object, guint prop_id,
       
   160     GValue * value, GParamSpec * spec);
       
   161 
       
   162 static gboolean gst_play_sink_send_event (GstElement * element,
       
   163     GstEvent * event);
       
   164 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
       
   165     GstStateChange transition);
       
   166 
       
   167 static GstElementClass *parent_class;
       
   168 
       
   169 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
       
   170 
       
   171 static const GstElementDetails gst_play_sink_details =
       
   172 GST_ELEMENT_DETAILS ("Player Sink",
       
   173     "Generic/Bin/Player",
       
   174     "Autoplug and play media from an uri",
       
   175     "Wim Taymans <wim.taymans@gmail.com>");
       
   176 #ifdef __SYMBIAN32__
       
   177 EXPORT_C
       
   178 #endif
       
   179 
       
   180 
       
   181 GType
       
   182 gst_play_sink_get_type (void)
       
   183 {
       
   184   static GType gst_play_sink_type = 0;
       
   185 
       
   186   if (!gst_play_sink_type) {
       
   187     static const GTypeInfo gst_play_sink_info = {
       
   188       sizeof (GstPlaySinkClass),
       
   189       NULL,
       
   190       NULL,
       
   191       (GClassInitFunc) gst_play_sink_class_init,
       
   192       NULL,
       
   193       NULL,
       
   194       sizeof (GstPlaySink),
       
   195       0,
       
   196       (GInstanceInitFunc) gst_play_sink_init,
       
   197       NULL
       
   198     };
       
   199 
       
   200     gst_play_sink_type = g_type_register_static (GST_TYPE_BIN,
       
   201         "GstPlaySink", &gst_play_sink_info, 0);
       
   202   }
       
   203 
       
   204   return gst_play_sink_type;
       
   205 }
       
   206 
       
   207 static void
       
   208 gst_play_sink_class_init (GstPlaySinkClass * klass)
       
   209 {
       
   210   GObjectClass *gobject_klass;
       
   211   GstElementClass *gstelement_klass;
       
   212   GstBinClass *gstbin_klass;
       
   213 
       
   214   gobject_klass = (GObjectClass *) klass;
       
   215   gstelement_klass = (GstElementClass *) klass;
       
   216   gstbin_klass = (GstBinClass *) klass;
       
   217 
       
   218   parent_class = g_type_class_peek_parent (klass);
       
   219 
       
   220   gobject_klass->set_property = gst_play_sink_set_property;
       
   221   gobject_klass->get_property = gst_play_sink_get_property;
       
   222 
       
   223   gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_sink_dispose);
       
   224   gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_sink_finalize);
       
   225 
       
   226   g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
       
   227       g_param_spec_object ("video-sink", "Video Sink",
       
   228           "the video output element to use (NULL = default sink)",
       
   229           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
       
   230   g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
       
   231       g_param_spec_object ("audio-sink", "Audio Sink",
       
   232           "the audio output element to use (NULL = default sink)",
       
   233           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
       
   234   g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
       
   235       g_param_spec_object ("vis-plugin", "Vis plugin",
       
   236           "the visualization element to use (NULL = none)",
       
   237           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
       
   238   g_object_class_install_property (gobject_klass, PROP_VOLUME,
       
   239       g_param_spec_double ("volume", "volume", "volume",
       
   240           0.0, VOLUME_MAX_DOUBLE, 1.0, G_PARAM_READWRITE));
       
   241   g_object_class_install_property (gobject_klass, PROP_FRAME,
       
   242       gst_param_spec_mini_object ("frame", "Frame",
       
   243           "The last video frame (NULL = no video available)",
       
   244           GST_TYPE_BUFFER, G_PARAM_READABLE));
       
   245   g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
       
   246       g_param_spec_string ("subtitle-font-desc",
       
   247           "Subtitle font description",
       
   248           "Pango font description of font "
       
   249           "to be used for subtitle rendering", NULL, G_PARAM_WRITABLE));
       
   250 
       
   251   gst_element_class_set_details (gstelement_klass, &gst_play_sink_details);
       
   252 
       
   253   gstelement_klass->change_state =
       
   254       GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
       
   255   gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
       
   256 
       
   257   GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play bin");
       
   258 }
       
   259 
       
   260 static void
       
   261 gst_play_sink_init (GstPlaySink * playsink)
       
   262 {
       
   263   /* init groups */
       
   264   playsink->video_sink = NULL;
       
   265   playsink->audio_sink = NULL;
       
   266   playsink->visualisation = NULL;
       
   267   playsink->textoverlay_element = NULL;
       
   268   playsink->volume = 1.0;
       
   269   playsink->font_desc = NULL;
       
   270   playsink->flags = GST_PLAY_FLAG_SOFT_VOLUME;
       
   271 
       
   272   playsink->lock = g_mutex_new ();
       
   273 }
       
   274 
       
   275 static void
       
   276 gst_play_sink_dispose (GObject * object)
       
   277 {
       
   278   GstPlaySink *playsink;
       
   279 
       
   280   playsink = GST_PLAY_SINK (object);
       
   281 
       
   282   if (playsink->audio_sink != NULL) {
       
   283     gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
       
   284     gst_object_unref (playsink->audio_sink);
       
   285     playsink->audio_sink = NULL;
       
   286   }
       
   287   if (playsink->video_sink != NULL) {
       
   288     gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
       
   289     gst_object_unref (playsink->video_sink);
       
   290     playsink->video_sink = NULL;
       
   291   }
       
   292   if (playsink->visualisation != NULL) {
       
   293     gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
       
   294     gst_object_unref (playsink->visualisation);
       
   295     playsink->visualisation = NULL;
       
   296   }
       
   297   if (playsink->textoverlay_element != NULL) {
       
   298     gst_object_unref (playsink->textoverlay_element);
       
   299     playsink->textoverlay_element = NULL;
       
   300   }
       
   301   g_free (playsink->font_desc);
       
   302   playsink->font_desc = NULL;
       
   303 
       
   304   G_OBJECT_CLASS (parent_class)->dispose (object);
       
   305 }
       
   306 
       
   307 static void
       
   308 gst_play_sink_finalize (GObject * object)
       
   309 {
       
   310   GstPlaySink *playsink;
       
   311 
       
   312   playsink = GST_PLAY_SINK (object);
       
   313 
       
   314   g_mutex_free (playsink->lock);
       
   315 
       
   316   G_OBJECT_CLASS (parent_class)->finalize (object);
       
   317 }
       
   318 #ifdef __SYMBIAN32__
       
   319 EXPORT_C
       
   320 #endif
       
   321 
       
   322 
       
   323 void
       
   324 gst_play_sink_set_video_sink (GstPlaySink * playsink, GstElement * sink)
       
   325 {
       
   326   GST_OBJECT_LOCK (playsink);
       
   327   if (playsink->video_sink)
       
   328     gst_object_unref (playsink->video_sink);
       
   329 
       
   330   if (sink) {
       
   331     gst_object_ref (sink);
       
   332     gst_object_sink (sink);
       
   333   }
       
   334   playsink->video_sink = sink;
       
   335   GST_OBJECT_UNLOCK (playsink);
       
   336 }
       
   337 #ifdef __SYMBIAN32__
       
   338 EXPORT_C
       
   339 #endif
       
   340 
       
   341 
       
   342 void
       
   343 gst_play_sink_set_audio_sink (GstPlaySink * playsink, GstElement * sink)
       
   344 {
       
   345   GST_OBJECT_LOCK (playsink);
       
   346   if (playsink->audio_sink)
       
   347     gst_object_unref (playsink->audio_sink);
       
   348 
       
   349   if (sink) {
       
   350     gst_object_ref (sink);
       
   351     gst_object_sink (sink);
       
   352   }
       
   353   playsink->audio_sink = sink;
       
   354   GST_OBJECT_UNLOCK (playsink);
       
   355 }
       
   356 
       
   357 static void
       
   358 gst_play_sink_vis_unblocked (GstPad * tee_pad, gboolean blocked,
       
   359     gpointer user_data)
       
   360 {
       
   361   GstPlaySink *playsink;
       
   362 
       
   363   playsink = GST_PLAY_SINK (user_data);
       
   364   /* nothing to do here, we need a dummy callback here to make the async call
       
   365    * non-blocking. */
       
   366   GST_DEBUG_OBJECT (playsink, "vis pad unblocked");
       
   367 }
       
   368 
       
   369 static void
       
   370 gst_play_sink_vis_blocked (GstPad * tee_pad, gboolean blocked,
       
   371     gpointer user_data)
       
   372 {
       
   373   GstPlaySink *playsink;
       
   374   GstPlayVisChain *chain;
       
   375 
       
   376   playsink = GST_PLAY_SINK (user_data);
       
   377 
       
   378   GST_PLAY_SINK_LOCK (playsink);
       
   379   GST_DEBUG_OBJECT (playsink, "vis pad blocked");
       
   380   /* now try to change the plugin in the running vis chain */
       
   381   if (!(chain = (GstPlayVisChain *) playsink->vischain))
       
   382     goto done;
       
   383 
       
   384   /* unlink the old plugin and unghost the pad */
       
   385   gst_pad_unlink (chain->blockpad, chain->vissinkpad);
       
   386   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
       
   387 
       
   388   /* set the old plugin to NULL and remove */
       
   389   gst_element_set_state (chain->vis, GST_STATE_NULL);
       
   390   gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
       
   391 
       
   392   /* add new plugin and set state to playing */
       
   393   chain->vis = gst_object_ref (playsink->visualisation);
       
   394   gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
       
   395   gst_element_set_state (chain->vis, GST_STATE_PLAYING);
       
   396 
       
   397   /* get pads */
       
   398   chain->vissinkpad = gst_element_get_pad (chain->vis, "sink");
       
   399   chain->vissrcpad = gst_element_get_pad (chain->vis, "src");
       
   400 
       
   401   /* link pads */
       
   402   gst_pad_link (chain->blockpad, chain->vissinkpad);
       
   403   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
       
   404       chain->vissrcpad);
       
   405 
       
   406 done:
       
   407   /* Unblock the pad */
       
   408   gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_sink_vis_unblocked,
       
   409       playsink);
       
   410   GST_PLAY_SINK_UNLOCK (playsink);
       
   411 }
       
   412 #ifdef __SYMBIAN32__
       
   413 EXPORT_C
       
   414 #endif
       
   415 
       
   416 
       
   417 void
       
   418 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
       
   419 {
       
   420   GstPlayVisChain *chain;
       
   421 
       
   422   GST_PLAY_SINK_LOCK (playsink);
       
   423   /* first store the new vis */
       
   424   if (playsink->visualisation)
       
   425     gst_object_unref (playsink->visualisation);
       
   426   playsink->visualisation = gst_object_ref (vis);
       
   427 
       
   428   /* now try to change the plugin in the running vis chain, if we have no chain,
       
   429    * we don't bother, any future vis chain will be created with the new vis
       
   430    * plugin. */
       
   431   if (!(chain = (GstPlayVisChain *) playsink->vischain))
       
   432     goto done;
       
   433 
       
   434   /* block the pad, the next time the callback is called we can change the
       
   435    * visualisation. It's possible that this never happens or that the pad was
       
   436    * already blocked. */
       
   437   GST_DEBUG_OBJECT (playsink, "blocking vis pad");
       
   438   gst_pad_set_blocked_async (chain->blockpad, TRUE, gst_play_sink_vis_blocked,
       
   439       playsink);
       
   440 done:
       
   441   GST_PLAY_SINK_UNLOCK (playsink);
       
   442 
       
   443   return;
       
   444 }
       
   445 #ifdef __SYMBIAN32__
       
   446 EXPORT_C
       
   447 #endif
       
   448 
       
   449 
       
   450 void
       
   451 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
       
   452 {
       
   453   GstPlayAudioChain *chain;
       
   454 
       
   455   GST_PLAY_SINK_LOCK (playsink);
       
   456   playsink->volume = volume;
       
   457   chain = (GstPlayAudioChain *) playsink->audiochain;
       
   458   if (chain && chain->volume) {
       
   459     g_object_set (chain->volume, "volume", volume, NULL);
       
   460   }
       
   461   GST_PLAY_SINK_UNLOCK (playsink);
       
   462 }
       
   463 #ifdef __SYMBIAN32__
       
   464 EXPORT_C
       
   465 #endif
       
   466 
       
   467 
       
   468 gdouble
       
   469 gst_play_sink_get_volume (GstPlaySink * playsink)
       
   470 {
       
   471   gdouble result;
       
   472   GstPlayAudioChain *chain;
       
   473 
       
   474   GST_PLAY_SINK_LOCK (playsink);
       
   475   chain = (GstPlayAudioChain *) playsink->audiochain;
       
   476   if (chain && chain->volume) {
       
   477     g_object_get (chain->volume, "volume", &result, NULL);
       
   478     playsink->volume = result;
       
   479   } else {
       
   480     result = playsink->volume;
       
   481   }
       
   482   GST_PLAY_SINK_UNLOCK (playsink);
       
   483 
       
   484   return result;
       
   485 }
       
   486 #ifdef __SYMBIAN32__
       
   487 EXPORT_C
       
   488 #endif
       
   489 
       
   490 
       
   491 void
       
   492 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
       
   493 {
       
   494   GstPlayAudioChain *chain;
       
   495 
       
   496   GST_PLAY_SINK_LOCK (playsink);
       
   497   playsink->mute = mute;
       
   498   chain = (GstPlayAudioChain *) playsink->audiochain;
       
   499   if (chain && chain->mute) {
       
   500     g_object_set (chain->mute, "mute", mute, NULL);
       
   501   }
       
   502   GST_PLAY_SINK_UNLOCK (playsink);
       
   503 }
       
   504 #ifdef __SYMBIAN32__
       
   505 EXPORT_C
       
   506 #endif
       
   507 
       
   508 
       
   509 gboolean
       
   510 gst_play_sink_get_mute (GstPlaySink * playsink)
       
   511 {
       
   512   gboolean result;
       
   513   GstPlayAudioChain *chain;
       
   514 
       
   515   GST_PLAY_SINK_LOCK (playsink);
       
   516   chain = (GstPlayAudioChain *) playsink->audiochain;
       
   517   if (chain && chain->mute) {
       
   518     g_object_get (chain->mute, "mute", &result, NULL);
       
   519     playsink->mute = result;
       
   520   } else {
       
   521     result = playsink->mute;
       
   522   }
       
   523   GST_PLAY_SINK_UNLOCK (playsink);
       
   524 
       
   525   return result;
       
   526 }
       
   527 
       
   528 static void
       
   529 gst_play_sink_set_property (GObject * object, guint prop_id,
       
   530     const GValue * value, GParamSpec * pspec)
       
   531 {
       
   532   GstPlaySink *playsink;
       
   533 
       
   534   playsink = GST_PLAY_SINK (object);
       
   535 
       
   536   switch (prop_id) {
       
   537     case PROP_VIDEO_SINK:
       
   538       gst_play_sink_set_video_sink (playsink, g_value_get_object (value));
       
   539       break;
       
   540     case PROP_AUDIO_SINK:
       
   541       gst_play_sink_set_audio_sink (playsink, g_value_get_object (value));
       
   542       break;
       
   543     case PROP_VIS_PLUGIN:
       
   544       gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
       
   545       break;
       
   546     case PROP_VOLUME:
       
   547       gst_play_sink_set_volume (playsink, g_value_get_double (value));
       
   548       break;
       
   549     case PROP_FONT_DESC:
       
   550       GST_OBJECT_LOCK (playsink);
       
   551       g_free (playsink->font_desc);
       
   552       playsink->font_desc = g_strdup (g_value_get_string (value));
       
   553       if (playsink->textoverlay_element) {
       
   554         g_object_set (G_OBJECT (playsink->textoverlay_element),
       
   555             "font-desc", g_value_get_string (value), NULL);
       
   556       }
       
   557       GST_OBJECT_UNLOCK (playsink);
       
   558       break;
       
   559     default:
       
   560       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   561       break;
       
   562   }
       
   563 }
       
   564 
       
   565 static void
       
   566 gst_play_sink_get_property (GObject * object, guint prop_id, GValue * value,
       
   567     GParamSpec * pspec)
       
   568 {
       
   569   GstPlaySink *playsink;
       
   570 
       
   571   playsink = GST_PLAY_SINK (object);
       
   572 
       
   573   switch (prop_id) {
       
   574     case PROP_VIDEO_SINK:
       
   575       GST_OBJECT_LOCK (playsink);
       
   576       g_value_set_object (value, playsink->video_sink);
       
   577       GST_OBJECT_UNLOCK (playsink);
       
   578       break;
       
   579     case PROP_AUDIO_SINK:
       
   580       GST_OBJECT_LOCK (playsink);
       
   581       g_value_set_object (value, playsink->audio_sink);
       
   582       GST_OBJECT_UNLOCK (playsink);
       
   583       break;
       
   584     case PROP_VIS_PLUGIN:
       
   585       GST_OBJECT_LOCK (playsink);
       
   586       g_value_set_object (value, playsink->visualisation);
       
   587       GST_OBJECT_UNLOCK (playsink);
       
   588       break;
       
   589     case PROP_VOLUME:
       
   590       g_value_set_double (value, gst_play_sink_get_volume (playsink));
       
   591       break;
       
   592     case PROP_FRAME:
       
   593     {
       
   594       break;
       
   595     }
       
   596     default:
       
   597       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   598       break;
       
   599   }
       
   600 }
       
   601 
       
   602 static void
       
   603 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
       
   604 {
       
   605   GstMessage *msg;
       
   606 
       
   607   msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
       
   608   gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
       
   609 }
       
   610 
       
   611 static void
       
   612 free_chain (GstPlayChain * chain)
       
   613 {
       
   614   if (chain->bin)
       
   615     gst_object_unref (chain->bin);
       
   616   gst_object_unref (chain->playsink);
       
   617   g_free (chain);
       
   618 }
       
   619 
       
   620 static gboolean
       
   621 add_chain (GstPlayChain * chain, gboolean add)
       
   622 {
       
   623   if (chain->added == add)
       
   624     return TRUE;
       
   625 
       
   626   if (add)
       
   627     gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
       
   628   else
       
   629     gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
       
   630 
       
   631   chain->added = add;
       
   632 
       
   633   return TRUE;
       
   634 }
       
   635 
       
   636 static gboolean
       
   637 activate_chain (GstPlayChain * chain, gboolean activate)
       
   638 {
       
   639   if (chain->activated == activate)
       
   640     return TRUE;
       
   641 
       
   642   if (activate)
       
   643     gst_element_set_state (chain->bin, GST_STATE_PAUSED);
       
   644   else
       
   645     gst_element_set_state (chain->bin, GST_STATE_NULL);
       
   646 
       
   647   chain->activated = activate;
       
   648 
       
   649   return TRUE;
       
   650 }
       
   651 
       
   652 static gint
       
   653 find_property (GstElement * element, const gchar * name)
       
   654 {
       
   655   gint res;
       
   656 
       
   657   if (g_object_class_find_property (G_OBJECT_GET_CLASS (element), name)) {
       
   658     res = 0;
       
   659     GST_DEBUG_OBJECT (element, "found %s property", name);
       
   660   } else {
       
   661     GST_DEBUG_OBJECT (element, "did not find %s property", name);
       
   662     res = 1;
       
   663     gst_object_unref (element);
       
   664   }
       
   665   return res;
       
   666 }
       
   667 
       
   668 /* find an object in the hierarchy with a property named @name */
       
   669 static GstElement *
       
   670 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
       
   671     const gchar * name)
       
   672 {
       
   673   GstElement *result = NULL;
       
   674   GstIterator *it;
       
   675 
       
   676   if (GST_IS_BIN (obj)) {
       
   677     it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
       
   678     result = gst_iterator_find_custom (it,
       
   679         (GCompareFunc) find_property, (gpointer) name);
       
   680     gst_iterator_free (it);
       
   681   } else {
       
   682     if (g_object_class_find_property (G_OBJECT_GET_CLASS (obj), name)) {
       
   683       result = obj;
       
   684       gst_object_ref (obj);
       
   685     }
       
   686   }
       
   687   return result;
       
   688 }
       
   689 
       
   690 /* make the element (bin) that contains the elements needed to perform
       
   691  * video display. 
       
   692  *
       
   693  *  +------------------------------------------------------------+
       
   694  *  | vbin                                                       |
       
   695  *  |      +-------+   +----------+   +----------+   +---------+ |
       
   696  *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
       
   697  *  |   +-sink    src-sink       src-sink       src-sink       | |
       
   698  *  |   |  +-------+   +----------+   +----------+   +---------+ |
       
   699  * sink-+                                                        |
       
   700  *  +------------------------------------------------------------+
       
   701  *           
       
   702  */
       
   703 static GstPlayChain *
       
   704 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
       
   705 {
       
   706   GstPlayVideoChain *chain;
       
   707   GstBin *bin;
       
   708   GstPad *pad;
       
   709 
       
   710   chain = g_new0 (GstPlayVideoChain, 1);
       
   711   chain->chain.playsink = gst_object_ref (playsink);
       
   712 
       
   713   if (playsink->video_sink) {
       
   714     chain->sink = playsink->video_sink;
       
   715   } else {
       
   716     chain->sink = gst_element_factory_make ("autovideosink", "videosink");
       
   717     if (chain->sink == NULL) {
       
   718       chain->sink = gst_element_factory_make ("xvimagesink", "videosink");
       
   719     }
       
   720     if (chain->sink == NULL)
       
   721       goto no_sinks;
       
   722   }
       
   723 
       
   724   /* if we can disable async behaviour of the sink, we can avoid adding a
       
   725    * queue for the audio chain. We can't use the deep property here because the
       
   726    * sink might change it's internal sink element later. */
       
   727   if (g_object_class_find_property (G_OBJECT_GET_CLASS (chain->sink), "async")) {
       
   728     GST_DEBUG_OBJECT (playsink, "setting async property to %d on video sink",
       
   729         async);
       
   730     g_object_set (chain->sink, "async", async, NULL);
       
   731     chain->async = async;
       
   732   } else
       
   733     chain->async = TRUE;
       
   734 
       
   735   /* create a bin to hold objects, as we create them we add them to this bin so
       
   736    * that when something goes wrong we only need to unref the bin */
       
   737   chain->chain.bin = gst_bin_new ("vbin");
       
   738   bin = GST_BIN_CAST (chain->chain.bin);
       
   739   gst_object_ref (bin);
       
   740   gst_object_sink (bin);
       
   741   gst_bin_add (bin, chain->sink);
       
   742 
       
   743   if (raw) {
       
   744     chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
       
   745     if (chain->conv == NULL)
       
   746       goto no_colorspace;
       
   747     gst_bin_add (bin, chain->conv);
       
   748 
       
   749     chain->scale = gst_element_factory_make ("videoscale", "vscale");
       
   750     if (chain->scale == NULL)
       
   751       goto no_videoscale;
       
   752     gst_bin_add (bin, chain->scale);
       
   753   }
       
   754 
       
   755   /* decouple decoder from sink, this improves playback quite a lot since the
       
   756    * decoder can continue while the sink blocks for synchronisation. We don't
       
   757    * need a lot of buffers as this consumes a lot of memory and we don't want
       
   758    * too little because else we would be context switching too quickly. */
       
   759   chain->queue = gst_element_factory_make ("queue", "vqueue");
       
   760   g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
       
   761       "max-size-bytes", 0, "max-size-time", (gint64) 0, NULL);
       
   762   gst_bin_add (bin, chain->queue);
       
   763 
       
   764   if (raw) {
       
   765     gst_element_link_pads (chain->queue, "src", chain->conv, "sink");
       
   766     gst_element_link_pads (chain->conv, "src", chain->scale, "sink");
       
   767     /* be more careful with the pad from the custom sink element, it might not
       
   768      * be named 'sink' */
       
   769     if (!gst_element_link_pads (chain->scale, "src", chain->sink, NULL))
       
   770       goto link_failed;
       
   771 
       
   772     pad = gst_element_get_pad (chain->queue, "sink");
       
   773   } else {
       
   774     if (!gst_element_link_pads (chain->queue, "src", chain->sink, NULL))
       
   775       goto link_failed;
       
   776     pad = gst_element_get_pad (chain->queue, "sink");
       
   777   }
       
   778 
       
   779   chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad);
       
   780   gst_object_unref (pad);
       
   781   gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad);
       
   782 
       
   783   return (GstPlayChain *) chain;
       
   784 
       
   785   /* ERRORS */
       
   786 no_sinks:
       
   787   {
       
   788     post_missing_element_message (playsink, "autovideosink");
       
   789     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
       
   790         (_("Both autovideosink and xvimagesink elements are missing.")),
       
   791         (NULL));
       
   792     free_chain ((GstPlayChain *) chain);
       
   793     return NULL;
       
   794   }
       
   795 no_colorspace:
       
   796   {
       
   797     post_missing_element_message (playsink, "ffmpegcolorspace");
       
   798     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
       
   799         (_("Missing element '%s' - check your GStreamer installation."),
       
   800             "ffmpegcolorspace"), (NULL));
       
   801     free_chain ((GstPlayChain *) chain);
       
   802     return NULL;
       
   803   }
       
   804 
       
   805 no_videoscale:
       
   806   {
       
   807     post_missing_element_message (playsink, "videoscale");
       
   808     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
       
   809         (_("Missing element '%s' - check your GStreamer installation."),
       
   810             "videoscale"), ("possibly a liboil version mismatch?"));
       
   811     free_chain ((GstPlayChain *) chain);
       
   812     return NULL;
       
   813   }
       
   814 link_failed:
       
   815   {
       
   816     GST_ELEMENT_ERROR (playsink, CORE, PAD,
       
   817         (NULL), ("Failed to configure the video sink."));
       
   818     free_chain ((GstPlayChain *) chain);
       
   819     return NULL;
       
   820   }
       
   821 }
       
   822 
       
   823 #if 0
       
   824 /* make an element for playback of video with subtitles embedded.
       
   825  *
       
   826  *  +--------------------------------------------------+
       
   827  *  | tbin                  +-------------+            |
       
   828  *  |          +-----+      | textoverlay |   +------+ |
       
   829  *  |          | csp | +--video_sink      |   | vbin | |
       
   830  * video_sink-sink  src+ +-text_sink     src-sink    | |
       
   831  *  |          +-----+   |  +-------------+   +------+ |
       
   832  * text_sink-------------+                             |
       
   833  *  +--------------------------------------------------+
       
   834  *
       
   835  *  If there is no subtitle renderer this function will simply return the
       
   836  *  videosink without the text_sink pad.
       
   837  */
       
   838 static GstElement *
       
   839 gen_text_element (GstPlaySink * playsink)
       
   840 {
       
   841   GstElement *element, *csp, *overlay, *vbin;
       
   842   GstPad *pad;
       
   843 
       
   844   /* Create the video rendering bin, error is posted when this fails. */
       
   845   vbin = gen_video_element (playsink);
       
   846   if (!vbin)
       
   847     return NULL;
       
   848 
       
   849   /* Text overlay */
       
   850   overlay = gst_element_factory_make ("textoverlay", "overlay");
       
   851 
       
   852   /* If no overlay return the video bin without subtitle support. */
       
   853   if (!overlay)
       
   854     goto no_overlay;
       
   855 
       
   856   /* Create our bin */
       
   857   element = gst_bin_new ("textbin");
       
   858 
       
   859   /* Set some parameters */
       
   860   g_object_set (G_OBJECT (overlay),
       
   861       "halign", "center", "valign", "bottom", NULL);
       
   862   if (playsink->font_desc) {
       
   863     g_object_set (G_OBJECT (overlay), "font-desc", playsink->font_desc, NULL);
       
   864   }
       
   865 
       
   866   /* Take a ref */
       
   867   playsink->textoverlay_element = GST_ELEMENT_CAST (gst_object_ref (overlay));
       
   868 
       
   869   /* we know this will succeed, as the video bin already created one before */
       
   870   csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp");
       
   871 
       
   872   /* Add our elements */
       
   873   gst_bin_add_many (GST_BIN_CAST (element), csp, overlay, vbin, NULL);
       
   874 
       
   875   /* Link */
       
   876   gst_element_link_pads (csp, "src", overlay, "video_sink");
       
   877   gst_element_link_pads (overlay, "src", vbin, "sink");
       
   878 
       
   879   /* Add ghost pads on the subtitle bin */
       
   880   pad = gst_element_get_pad (overlay, "text_sink");
       
   881   gst_element_add_pad (element, gst_ghost_pad_new ("text_sink", pad));
       
   882   gst_object_unref (pad);
       
   883 
       
   884   pad = gst_element_get_pad (csp, "sink");
       
   885   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
       
   886   gst_object_unref (pad);
       
   887 
       
   888   /* Set state to READY */
       
   889   gst_element_set_state (element, GST_STATE_READY);
       
   890 
       
   891   return element;
       
   892 
       
   893   /* ERRORS */
       
   894 no_overlay:
       
   895   {
       
   896     post_missing_element_message (playsink, "textoverlay");
       
   897     GST_WARNING_OBJECT (playsink,
       
   898         "No overlay (pango) element, subtitles disabled");
       
   899     return vbin;
       
   900   }
       
   901 }
       
   902 #endif
       
   903 
       
   904 
       
   905 /* make the chain that contains the elements needed to perform
       
   906  * audio playback. 
       
   907  *
       
   908  * We add a tee as the first element so that we can link the visualisation chain
       
   909  * to it when requested.
       
   910  *
       
   911  *  +-------------------------------------------------------------+
       
   912  *  | abin                                                        |
       
   913  *  |      +---------+   +----------+   +---------+   +---------+ |
       
   914  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
       
   915  *  |   +-srck      src-sink       src-sink      src-sink       | |
       
   916  *  |   |  +---------+   +----------+   +---------+   +---------+ |
       
   917  * sink-+                                                         |
       
   918  *  +-------------------------------------------------------------+
       
   919  */
       
   920 static GstPlayChain *
       
   921 gen_audio_chain (GstPlaySink * playsink, gboolean raw, gboolean queue)
       
   922 {
       
   923   GstPlayAudioChain *chain;
       
   924   GstBin *bin;
       
   925   gboolean res;
       
   926   GstPad *pad;
       
   927 
       
   928   chain = g_new0 (GstPlayAudioChain, 1);
       
   929   chain->chain.playsink = gst_object_ref (playsink);
       
   930 
       
   931   if (playsink->audio_sink) {
       
   932     chain->sink = playsink->audio_sink;
       
   933   } else {
       
   934     chain->sink = gst_element_factory_make ("autoaudiosink", "audiosink");
       
   935     if (chain->sink == NULL) {
       
   936       chain->sink = gst_element_factory_make ("alsasink", "audiosink");
       
   937     }
       
   938     if (chain->sink == NULL)
       
   939       goto no_sinks;
       
   940   }
       
   941   chain->chain.bin = gst_bin_new ("abin");
       
   942   bin = GST_BIN_CAST (chain->chain.bin);
       
   943   gst_object_ref (bin);
       
   944   gst_object_sink (bin);
       
   945   gst_bin_add (bin, chain->sink);
       
   946 
       
   947   if (queue) {
       
   948     /* we have to add a queue when we need to decouple for the video sink in
       
   949      * visualisations */
       
   950     GST_DEBUG_OBJECT (playsink, "adding audio queue");
       
   951     chain->queue = gst_element_factory_make ("queue", "aqueue");
       
   952     gst_bin_add (bin, chain->queue);
       
   953   }
       
   954 
       
   955   if (raw) {
       
   956     chain->conv = gst_element_factory_make ("audioconvert", "aconv");
       
   957     if (chain->conv == NULL)
       
   958       goto no_audioconvert;
       
   959     gst_bin_add (bin, chain->conv);
       
   960 
       
   961     chain->resample = gst_element_factory_make ("audioresample", "aresample");
       
   962     if (chain->resample == NULL)
       
   963       goto no_audioresample;
       
   964     gst_bin_add (bin, chain->resample);
       
   965 
       
   966     res = gst_element_link_pads (chain->conv, "src", chain->resample, "sink");
       
   967 
       
   968     /* FIXME check if the sink has the volume property */
       
   969 
       
   970     if (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
       
   971       chain->volume = gst_element_factory_make ("volume", "volume");
       
   972       if (chain->volume == NULL)
       
   973         goto no_volume;
       
   974 
       
   975       /* volume also has the mute property */
       
   976       chain->mute = gst_object_ref (chain->volume);
       
   977 
       
   978       /* configure with the latest volume */
       
   979       g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
       
   980       gst_bin_add (bin, chain->volume);
       
   981 
       
   982       res &=
       
   983           gst_element_link_pads (chain->resample, "src", chain->volume, "sink");
       
   984       res &= gst_element_link_pads (chain->volume, "src", chain->sink, NULL);
       
   985     } else {
       
   986       res &= gst_element_link_pads (chain->resample, "src", chain->sink, NULL);
       
   987     }
       
   988     if (!res)
       
   989       goto link_failed;
       
   990 
       
   991     if (queue) {
       
   992       res = gst_element_link_pads (chain->queue, "src", chain->conv, "sink");
       
   993       pad = gst_element_get_pad (chain->queue, "sink");
       
   994     } else {
       
   995       pad = gst_element_get_pad (chain->conv, "sink");
       
   996     }
       
   997   } else {
       
   998     if (queue) {
       
   999       res = gst_element_link_pads (chain->queue, "src", chain->sink, "sink");
       
  1000       pad = gst_element_get_pad (chain->queue, "sink");
       
  1001     } else {
       
  1002       pad = gst_element_get_pad (chain->sink, "sink");
       
  1003     }
       
  1004   }
       
  1005   chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad);
       
  1006   gst_object_unref (pad);
       
  1007   gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad);
       
  1008 
       
  1009   return (GstPlayChain *) chain;
       
  1010 
       
  1011   /* ERRORS */
       
  1012 no_sinks:
       
  1013   {
       
  1014     post_missing_element_message (playsink, "autoaudiosink");
       
  1015     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
       
  1016         (_("Both autoaudiosink and alsasink elements are missing.")), (NULL));
       
  1017     free_chain ((GstPlayChain *) chain);
       
  1018     return NULL;
       
  1019   }
       
  1020 no_audioconvert:
       
  1021   {
       
  1022     post_missing_element_message (playsink, "audioconvert");
       
  1023     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
       
  1024         (_("Missing element '%s' - check your GStreamer installation."),
       
  1025             "audioconvert"), ("possibly a liboil version mismatch?"));
       
  1026     free_chain ((GstPlayChain *) chain);
       
  1027     return NULL;
       
  1028   }
       
  1029 no_audioresample:
       
  1030   {
       
  1031     post_missing_element_message (playsink, "audioresample");
       
  1032     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
       
  1033         (_("Missing element '%s' - check your GStreamer installation."),
       
  1034             "audioresample"), ("possibly a liboil version mismatch?"));
       
  1035     free_chain ((GstPlayChain *) chain);
       
  1036     return NULL;
       
  1037   }
       
  1038 no_volume:
       
  1039   {
       
  1040     post_missing_element_message (playsink, "volume");
       
  1041     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
       
  1042         (_("Missing element '%s' - check your GStreamer installation."),
       
  1043             "volume"), ("possibly a liboil version mismatch?"));
       
  1044     free_chain ((GstPlayChain *) chain);
       
  1045     return NULL;
       
  1046   }
       
  1047 link_failed:
       
  1048   {
       
  1049     GST_ELEMENT_ERROR (playsink, CORE, PAD,
       
  1050         (NULL), ("Failed to configure the audio sink."));
       
  1051     free_chain ((GstPlayChain *) chain);
       
  1052     return NULL;
       
  1053   }
       
  1054 }
       
  1055 
       
  1056 /*
       
  1057  *  +-------------------------------------------------------------------+
       
  1058  *  | visbin                                                            |
       
  1059  *  |      +----------+   +------------+   +----------+   +-------+     |
       
  1060  *  |      | visqueue |   | audioconv  |   | audiores |   |  vis  |     |
       
  1061  *  |   +-sink       src-sink + samp  src-sink       src-sink    src-+  |
       
  1062  *  |   |  +----------+   +------------+   +----------+   +-------+  |  |
       
  1063  * sink-+                                                            +-src
       
  1064  *  +-------------------------------------------------------------------+
       
  1065  *           
       
  1066  */
       
  1067 static GstPlayChain *
       
  1068 gen_vis_chain (GstPlaySink * playsink)
       
  1069 {
       
  1070   GstPlayVisChain *chain;
       
  1071   GstBin *bin;
       
  1072   gboolean res;
       
  1073   GstPad *pad;
       
  1074 
       
  1075   chain = g_new0 (GstPlayVisChain, 1);
       
  1076   chain->chain.playsink = gst_object_ref (playsink);
       
  1077 
       
  1078   chain->chain.bin = gst_bin_new ("visbin");
       
  1079   bin = GST_BIN_CAST (chain->chain.bin);
       
  1080   gst_object_ref (bin);
       
  1081   gst_object_sink (bin);
       
  1082 
       
  1083   /* we're queuing raw audio here, we can remove this queue when we can disable
       
  1084    * async behaviour in the video sink. */
       
  1085   chain->queue = gst_element_factory_make ("queue", "visqueue");
       
  1086   gst_bin_add (bin, chain->queue);
       
  1087 
       
  1088   chain->conv = gst_element_factory_make ("audioconvert", "aconv");
       
  1089   if (chain->conv == NULL)
       
  1090     goto no_audioconvert;
       
  1091   gst_bin_add (bin, chain->conv);
       
  1092 
       
  1093   chain->resample = gst_element_factory_make ("audioresample", "aresample");
       
  1094   if (chain->resample == NULL)
       
  1095     goto no_audioresample;
       
  1096   gst_bin_add (bin, chain->resample);
       
  1097 
       
  1098   /* this pad will be used for blocking the dataflow and switching the vis
       
  1099    * plugin */
       
  1100   chain->blockpad = gst_element_get_pad (chain->resample, "src");
       
  1101 
       
  1102   if (playsink->visualisation) {
       
  1103     chain->vis = gst_object_ref (playsink->visualisation);
       
  1104   } else {
       
  1105     chain->vis = gst_element_factory_make ("goom", "vis");
       
  1106     if (!chain->vis)
       
  1107       goto no_goom;
       
  1108   }
       
  1109   gst_bin_add (bin, chain->vis);
       
  1110 
       
  1111   res = gst_element_link_pads (chain->queue, "src", chain->conv, "sink");
       
  1112   res &= gst_element_link_pads (chain->conv, "src", chain->resample, "sink");
       
  1113   res &= gst_element_link_pads (chain->resample, "src", chain->vis, "sink");
       
  1114   if (!res)
       
  1115     goto link_failed;
       
  1116 
       
  1117   chain->vissinkpad = gst_element_get_pad (chain->vis, "sink");
       
  1118   chain->vissrcpad = gst_element_get_pad (chain->vis, "src");
       
  1119 
       
  1120   pad = gst_element_get_pad (chain->queue, "sink");
       
  1121   chain->chain.sinkpad = gst_ghost_pad_new ("sink", pad);
       
  1122   gst_object_unref (pad);
       
  1123   gst_element_add_pad (chain->chain.bin, chain->chain.sinkpad);
       
  1124 
       
  1125   chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
       
  1126   gst_element_add_pad (chain->chain.bin, chain->srcpad);
       
  1127 
       
  1128   return (GstPlayChain *) chain;
       
  1129 
       
  1130   /* ERRORS */
       
  1131 no_audioconvert:
       
  1132   {
       
  1133     post_missing_element_message (playsink, "audioconvert");
       
  1134     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
       
  1135         (_("Missing element '%s' - check your GStreamer installation."),
       
  1136             "audioconvert"), ("possibly a liboil version mismatch?"));
       
  1137     free_chain ((GstPlayChain *) chain);
       
  1138     return NULL;
       
  1139   }
       
  1140 no_audioresample:
       
  1141   {
       
  1142     post_missing_element_message (playsink, "audioresample");
       
  1143     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
       
  1144         (_("Missing element '%s' - check your GStreamer installation."),
       
  1145             "audioresample"), (NULL));
       
  1146     free_chain ((GstPlayChain *) chain);
       
  1147     return NULL;
       
  1148   }
       
  1149 no_goom:
       
  1150   {
       
  1151     post_missing_element_message (playsink, "goom");
       
  1152     GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
       
  1153         (_("Missing element '%s' - check your GStreamer installation."),
       
  1154             "goom"), (NULL));
       
  1155     free_chain ((GstPlayChain *) chain);
       
  1156     return NULL;
       
  1157   }
       
  1158 link_failed:
       
  1159   {
       
  1160     GST_ELEMENT_ERROR (playsink, CORE, PAD,
       
  1161         (NULL), ("Failed to configure the visualisation element."));
       
  1162     free_chain ((GstPlayChain *) chain);
       
  1163     return NULL;
       
  1164   }
       
  1165 }
       
  1166 
       
  1167 #if 0
       
  1168 static gboolean
       
  1169 activate_vis (GstPlaySink * playsink, gboolean activate)
       
  1170 {
       
  1171   /* need to have an audio chain */
       
  1172   if (!playsink->audiochain || !playsink->vischain)
       
  1173     return FALSE;
       
  1174 
       
  1175   if (playsink->vischain->activated == activate)
       
  1176     return TRUE;
       
  1177 
       
  1178   if (activate) {
       
  1179     /* activation: Add the vis chain to the sink bin . Take a new srcpad from
       
  1180      * the tee of the audio chain and link it to the sinkpad of the vis chain.
       
  1181      */
       
  1182 
       
  1183   } else {
       
  1184     /* deactivation: release the srcpad from the tee of the audio chain. Set the
       
  1185      * vis chain to NULL and remove it from the sink bin */
       
  1186 
       
  1187   }
       
  1188   return TRUE;
       
  1189 }
       
  1190 #endif
       
  1191 
       
  1192 /* this function is called when all the request pads are requested and when we
       
  1193  * have to construct the final pipeline.
       
  1194  */
       
  1195 #ifdef __SYMBIAN32__
       
  1196 EXPORT_C
       
  1197 #endif
       
  1198 
       
  1199 gboolean
       
  1200 gst_play_sink_reconfigure (GstPlaySink * playsink)
       
  1201 {
       
  1202   GstPlayFlags flags;
       
  1203   gboolean need_audio, need_video, need_vis;
       
  1204 
       
  1205   GST_DEBUG_OBJECT (playsink, "reconfiguring");
       
  1206 
       
  1207   /* assume we need nothing */
       
  1208   need_audio = need_video = need_vis = FALSE;
       
  1209 
       
  1210   GST_PLAY_SINK_LOCK (playsink);
       
  1211   GST_OBJECT_LOCK (playsink);
       
  1212   /* get flags, there are protected with the object lock */
       
  1213   flags = playsink->flags;
       
  1214   GST_OBJECT_UNLOCK (playsink);
       
  1215 
       
  1216   /* figure out which components we need */
       
  1217   if (flags & GST_PLAY_FLAG_VIDEO && playsink->video_pad) {
       
  1218     /* we have video and we are requested to show it */
       
  1219     need_video = TRUE;
       
  1220   }
       
  1221   if (playsink->audio_pad) {
       
  1222     if (flags & GST_PLAY_FLAG_AUDIO) {
       
  1223       need_audio = TRUE;
       
  1224     }
       
  1225     if (flags & GST_PLAY_FLAG_VIS && !need_video) {
       
  1226       /* also add video when we add visualisation */
       
  1227       need_video = TRUE;
       
  1228       need_vis = TRUE;
       
  1229     }
       
  1230   }
       
  1231 
       
  1232   if (need_video) {
       
  1233     GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
       
  1234         playsink->video_pad_raw);
       
  1235     if (!playsink->videochain) {
       
  1236       gboolean raw, async;
       
  1237 
       
  1238       /* we need a raw sink when we do vis or when we have a raw pad */
       
  1239       raw = need_vis ? TRUE : playsink->video_pad_raw;
       
  1240       /* we try to set the sink async=FALSE when we need vis, this way we can
       
  1241        * avoid a queue in the audio chain. */
       
  1242       async = !need_vis;
       
  1243 
       
  1244       playsink->videochain = gen_video_chain (playsink, raw, async);
       
  1245     }
       
  1246     add_chain (playsink->videochain, TRUE);
       
  1247     activate_chain (playsink->videochain, TRUE);
       
  1248     if (!need_vis)
       
  1249       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
       
  1250           playsink->videochain->sinkpad);
       
  1251   } else {
       
  1252     if (playsink->videochain) {
       
  1253       add_chain (playsink->videochain, FALSE);
       
  1254       activate_chain (playsink->videochain, FALSE);
       
  1255     }
       
  1256     if (playsink->video_pad)
       
  1257       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
       
  1258   }
       
  1259 
       
  1260   if (need_audio) {
       
  1261     GST_DEBUG_OBJECT (playsink, "adding audio");
       
  1262     if (!playsink->audiochain) {
       
  1263       gboolean raw, queue;
       
  1264 
       
  1265       /* get a raw sink if we are asked for a raw pad */
       
  1266       raw = playsink->audio_pad_raw;
       
  1267       if (need_vis) {
       
  1268         /* If we are dealing with visualisations, we need to add a queue to
       
  1269          * decouple the audio from the video part. We only have to do this when
       
  1270          * the video part is async=true */
       
  1271         queue = ((GstPlayVideoChain *) playsink->videochain)->async;
       
  1272         GST_DEBUG_OBJECT (playsink, "need audio queue for vis: %d", queue);
       
  1273       } else {
       
  1274         /* no vis, we can avoid a queue */
       
  1275         GST_DEBUG_OBJECT (playsink, "don't need audio queue");
       
  1276         queue = FALSE;
       
  1277       }
       
  1278 
       
  1279       playsink->audiochain = gen_audio_chain (playsink, raw, queue);
       
  1280     }
       
  1281     add_chain (playsink->audiochain, TRUE);
       
  1282     gst_pad_link (playsink->audio_tee_asrc, playsink->audiochain->sinkpad);
       
  1283     activate_chain (playsink->audiochain, TRUE);
       
  1284   } else {
       
  1285     /* we have no audio or we are requested to not play audio */
       
  1286     if (playsink->audiochain) {
       
  1287       gst_pad_unlink (playsink->audio_tee_asrc, playsink->audiochain->sinkpad);
       
  1288       add_chain (playsink->audiochain, FALSE);
       
  1289       activate_chain (playsink->audiochain, FALSE);
       
  1290     }
       
  1291   }
       
  1292 
       
  1293   if (need_vis) {
       
  1294     GstPad *srcpad;
       
  1295 
       
  1296     if (!playsink->vischain)
       
  1297       playsink->vischain = gen_vis_chain (playsink);
       
  1298 
       
  1299     GST_DEBUG_OBJECT (playsink, "adding visualisation");
       
  1300 
       
  1301     srcpad =
       
  1302         gst_element_get_pad (GST_ELEMENT_CAST (playsink->vischain->bin), "src");
       
  1303     add_chain (playsink->vischain, TRUE);
       
  1304     gst_pad_link (playsink->audio_tee_vissrc, playsink->vischain->sinkpad);
       
  1305     gst_pad_link (srcpad, playsink->videochain->sinkpad);
       
  1306     gst_object_unref (srcpad);
       
  1307     activate_chain (playsink->vischain, TRUE);
       
  1308   } else {
       
  1309     if (playsink->vischain) {
       
  1310       add_chain (playsink->vischain, FALSE);
       
  1311       activate_chain (playsink->vischain, FALSE);
       
  1312     }
       
  1313   }
       
  1314   GST_PLAY_SINK_UNLOCK (playsink);
       
  1315 
       
  1316   return TRUE;
       
  1317 }
       
  1318 #ifdef __SYMBIAN32__
       
  1319 EXPORT_C
       
  1320 #endif
       
  1321 
       
  1322 
       
  1323 gboolean
       
  1324 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
       
  1325 {
       
  1326   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
       
  1327 
       
  1328   GST_OBJECT_LOCK (playsink);
       
  1329   playsink->flags = flags;
       
  1330   GST_OBJECT_UNLOCK (playsink);
       
  1331 
       
  1332   return TRUE;
       
  1333 }
       
  1334 #ifdef __SYMBIAN32__
       
  1335 EXPORT_C
       
  1336 #endif
       
  1337 
       
  1338 
       
  1339 GstPlayFlags
       
  1340 gst_play_sink_get_flags (GstPlaySink * playsink)
       
  1341 {
       
  1342   GstPlayFlags res;
       
  1343 
       
  1344   g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
       
  1345 
       
  1346   GST_OBJECT_LOCK (playsink);
       
  1347   res = playsink->flags;
       
  1348   GST_OBJECT_UNLOCK (playsink);
       
  1349 
       
  1350   return res;
       
  1351 }
       
  1352 #ifdef __SYMBIAN32__
       
  1353 EXPORT_C
       
  1354 #endif
       
  1355 
       
  1356 
       
  1357 GstBuffer *
       
  1358 gst_play_sink_get_last_frame (GstPlaySink * playsink)
       
  1359 {
       
  1360   GstBuffer *result = NULL;
       
  1361   GstPlayVideoChain *chain;
       
  1362 
       
  1363   GST_PLAY_SINK_LOCK (playsink);
       
  1364   GST_DEBUG_OBJECT (playsink, "taking last frame");
       
  1365   /* get the video chain if we can */
       
  1366   if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
       
  1367     GST_DEBUG_OBJECT (playsink, "found video chain");
       
  1368     /* see if the chain is active */
       
  1369     if (chain->chain.activated && chain->sink) {
       
  1370       GstElement *elem;
       
  1371 
       
  1372       GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
       
  1373 
       
  1374       /* find and get the last-buffer property now */
       
  1375       if ((elem =
       
  1376               gst_play_sink_find_property (playsink, chain->sink,
       
  1377                   "last-buffer"))) {
       
  1378         GST_DEBUG_OBJECT (playsink, "getting last-buffer property");
       
  1379         g_object_get (elem, "last-buffer", &result, NULL);
       
  1380         gst_object_unref (elem);
       
  1381       }
       
  1382     }
       
  1383   }
       
  1384   GST_PLAY_SINK_UNLOCK (playsink);
       
  1385 
       
  1386   return result;
       
  1387 }
       
  1388 #ifdef __SYMBIAN32__
       
  1389 EXPORT_C
       
  1390 #endif
       
  1391 
       
  1392 
       
  1393 GstPad *
       
  1394 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
       
  1395 {
       
  1396   GstPad *res = NULL;
       
  1397   gboolean created = FALSE;
       
  1398   gboolean raw = FALSE;
       
  1399 
       
  1400   GST_PLAY_SINK_LOCK (playsink);
       
  1401   switch (type) {
       
  1402     case GST_PLAY_SINK_TYPE_AUDIO_RAW:
       
  1403       raw = TRUE;
       
  1404     case GST_PLAY_SINK_TYPE_AUDIO:
       
  1405       if (!playsink->audio_tee) {
       
  1406         /* create tee when needed. This element will feed the audio sink chain
       
  1407          * and the vis chain. */
       
  1408         playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
       
  1409         playsink->audio_tee_sink =
       
  1410             gst_element_get_pad (playsink->audio_tee, "sink");
       
  1411         /* get two request pads */
       
  1412         playsink->audio_tee_vissrc =
       
  1413             gst_element_get_request_pad (playsink->audio_tee, "src%d");
       
  1414         playsink->audio_tee_asrc =
       
  1415             gst_element_get_request_pad (playsink->audio_tee, "src%d");
       
  1416         gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
       
  1417         gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
       
  1418       }
       
  1419       if (!playsink->audio_pad) {
       
  1420         playsink->audio_pad =
       
  1421             gst_ghost_pad_new ("audio_sink", playsink->audio_tee_sink);
       
  1422         created = TRUE;
       
  1423       }
       
  1424       playsink->audio_pad_raw = raw;
       
  1425       res = playsink->audio_pad;
       
  1426       break;
       
  1427     case GST_PLAY_SINK_TYPE_VIDEO_RAW:
       
  1428       raw = TRUE;
       
  1429     case GST_PLAY_SINK_TYPE_VIDEO:
       
  1430       if (!playsink->video_pad) {
       
  1431         playsink->video_pad =
       
  1432             gst_ghost_pad_new_no_target ("video_sink", GST_PAD_SINK);
       
  1433         created = TRUE;
       
  1434       }
       
  1435       playsink->video_pad_raw = raw;
       
  1436       res = playsink->video_pad;
       
  1437       break;
       
  1438     case GST_PLAY_SINK_TYPE_TEXT:
       
  1439       if (!playsink->text_pad) {
       
  1440         playsink->text_pad =
       
  1441             gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
       
  1442         created = TRUE;
       
  1443       }
       
  1444       res = playsink->text_pad;
       
  1445       break;
       
  1446     default:
       
  1447       res = NULL;
       
  1448       break;
       
  1449   }
       
  1450   GST_PLAY_SINK_UNLOCK (playsink);
       
  1451 
       
  1452   if (created && res) {
       
  1453     gst_pad_set_active (res, TRUE);
       
  1454     gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
       
  1455   }
       
  1456 
       
  1457   return res;
       
  1458 }
       
  1459 #ifdef __SYMBIAN32__
       
  1460 EXPORT_C
       
  1461 #endif
       
  1462 
       
  1463 
       
  1464 void
       
  1465 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
       
  1466 {
       
  1467   GstPad **res = NULL;
       
  1468 
       
  1469   GST_PLAY_SINK_LOCK (playsink);
       
  1470   if (pad == playsink->video_pad) {
       
  1471     res = &playsink->video_pad;
       
  1472   } else if (pad == playsink->audio_pad) {
       
  1473     res = &playsink->audio_pad;
       
  1474   } else if (pad == playsink->text_pad) {
       
  1475     res = &playsink->text_pad;
       
  1476   }
       
  1477   GST_PLAY_SINK_UNLOCK (playsink);
       
  1478 
       
  1479   if (*res) {
       
  1480     gst_pad_set_active (*res, FALSE);
       
  1481     gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
       
  1482     *res = NULL;
       
  1483   }
       
  1484 }
       
  1485 
       
  1486 /* Send an event to our sinks until one of them works; don't then send to the
       
  1487  * remaining sinks (unlike GstBin)
       
  1488  */
       
  1489 static gboolean
       
  1490 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event)
       
  1491 {
       
  1492   gboolean res = TRUE;
       
  1493 
       
  1494   if (playsink->audiochain) {
       
  1495     gst_event_ref (event);
       
  1496     if ((res = gst_element_send_event (playsink->audiochain->bin, event))) {
       
  1497       GST_DEBUG_OBJECT (playsink, "Sent event succesfully to audio sink");
       
  1498       goto done;
       
  1499     }
       
  1500     GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
       
  1501   }
       
  1502   if (playsink->videochain) {
       
  1503     gst_event_ref (event);
       
  1504     if ((res = gst_element_send_event (playsink->videochain->bin, event))) {
       
  1505       GST_DEBUG_OBJECT (playsink, "Sent event succesfully to video sink");
       
  1506       goto done;
       
  1507     }
       
  1508     GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
       
  1509   }
       
  1510 done:
       
  1511   gst_event_unref (event);
       
  1512   return res;
       
  1513 }
       
  1514 
       
  1515 /* We only want to send the event to a single sink (overriding GstBin's
       
  1516  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
       
  1517  * events appropriately. So, this is a messy duplication of code. */
       
  1518 static gboolean
       
  1519 gst_play_sink_send_event (GstElement * element, GstEvent * event)
       
  1520 {
       
  1521   gboolean res = FALSE;
       
  1522   GstEventType event_type = GST_EVENT_TYPE (event);
       
  1523 
       
  1524   switch (event_type) {
       
  1525     case GST_EVENT_SEEK:
       
  1526       GST_DEBUG_OBJECT (element, "Sending seek event to a sink");
       
  1527       res = gst_play_sink_send_event_to_sink (GST_PLAY_SINK (element), event);
       
  1528       break;
       
  1529     default:
       
  1530       res = parent_class->send_event (element, event);
       
  1531       break;
       
  1532   }
       
  1533   return res;
       
  1534 }
       
  1535 
       
  1536 static GstStateChangeReturn
       
  1537 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
       
  1538 {
       
  1539   GstStateChangeReturn ret;
       
  1540   GstPlaySink *playsink;
       
  1541 
       
  1542   playsink = GST_PLAY_SINK (element);
       
  1543 
       
  1544   switch (transition) {
       
  1545     case GST_STATE_CHANGE_READY_TO_PAUSED:
       
  1546       break;
       
  1547     default:
       
  1548       break;
       
  1549   }
       
  1550 
       
  1551   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
       
  1552   if (ret == GST_STATE_CHANGE_FAILURE)
       
  1553     return ret;
       
  1554 
       
  1555   switch (transition) {
       
  1556     case GST_STATE_CHANGE_READY_TO_PAUSED:
       
  1557       break;
       
  1558     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
       
  1559       /* FIXME Release audio device when we implement that */
       
  1560       break;
       
  1561     case GST_STATE_CHANGE_PAUSED_TO_READY:
       
  1562       /* remove sinks we added */
       
  1563       if (playsink->videochain) {
       
  1564         activate_chain (playsink->videochain, FALSE);
       
  1565         add_chain (playsink->videochain, FALSE);
       
  1566       }
       
  1567       if (playsink->audiochain) {
       
  1568         activate_chain (playsink->audiochain, FALSE);
       
  1569         add_chain (playsink->audiochain, FALSE);
       
  1570       }
       
  1571       break;
       
  1572     default:
       
  1573       break;
       
  1574   }
       
  1575 
       
  1576   return ret;
       
  1577 }