playback: Add video-/audio-filter properties
authorArun Raghavan <arun.raghavan@collabora.co.uk>
Sat, 15 Mar 2014 15:05:22 +0000 (16:05 +0100)
committerSebastian Dröge <sebastian@centricular.com>
Sun, 16 Mar 2014 17:35:57 +0000 (18:35 +0100)
This provides an audio-filter and video-filter property to allow
applications to set filter elements/bins. The idea is that these will be
applied if possible -- for non-raw sinks, the filters will be skipped.

If the application wishes to force the application of the filters, this
can be done by setting the new flag introduced on playsink -
GST_PLAY_FLAG_FORCE_FILTERS.

https://bugzilla.gnome.org/show_bug.cgi?id=679031

gst/playback/gstplay-enum.h
gst/playback/gstplaybin2.c
gst/playback/gstplaysink.c
gst/playback/gstplaysink.h

index f6ab5d9..0c75b53 100644 (file)
@@ -57,6 +57,8 @@ GType gst_autoplug_select_result_get_type (void);
  *   formats.
  * @GST_PLAY_FLAG_BUFFERING: enable buffering of the demuxed or parsed data.
  * @GST_PLAY_FLAG_DEINTERLACE: deinterlace raw video (if native not forced).
+ * @GST_PLAY_FLAG_FORCE_FILTERS: force audio/video filters to be applied if
+ *   set.
  *
  * Extra flags to configure the behaviour of the sinks.
  */
@@ -71,7 +73,8 @@ typedef enum {
   GST_PLAY_FLAG_DOWNLOAD      = (1 << 7),
   GST_PLAY_FLAG_BUFFERING     = (1 << 8),
   GST_PLAY_FLAG_DEINTERLACE   = (1 << 9),
-  GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10)
+  GST_PLAY_FLAG_SOFT_COLORBALANCE = (1 << 10),
+  GST_PLAY_FLAG_FORCE_FILTERS = (1 << 11),
 } GstPlayFlags;
 
 #define GST_TYPE_PLAY_FLAGS (gst_play_flags_get_type())
index 54dd775..5e1ee7e 100644 (file)
@@ -565,6 +565,8 @@ enum
   PROP_AV_OFFSET,
   PROP_RING_BUFFER_MAX_SIZE,
   PROP_FORCE_ASPECT_RATIO,
+  PROP_AUDIO_FILTER,
+  PROP_VIDEO_FILTER,
   PROP_LAST
 };
 
@@ -837,6 +839,14 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
           "ISO-8859-15 will be assumed.", NULL,
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
+  g_object_class_install_property (gobject_klass, PROP_VIDEO_FILTER,
+      g_param_spec_object ("video-filter", "Video filter",
+          "the video filter(s) to apply, if possible",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (gobject_klass, PROP_AUDIO_FILTER,
+      g_param_spec_object ("audio-filter", "Audio filter",
+          "the audio filter(s) to apply, if possible",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
       g_param_spec_object ("video-sink", "Video Sink",
           "the video output element to use (NULL = default sink)",
@@ -2252,6 +2262,14 @@ gst_play_bin_set_property (GObject * object, guint prop_id,
     case PROP_SUBTITLE_ENCODING:
       gst_play_bin_set_encoding (playbin, g_value_get_string (value));
       break;
+    case PROP_VIDEO_FILTER:
+      gst_play_sink_set_filter (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO,
+          GST_ELEMENT (g_value_get_object (value)));
+      break;
+    case PROP_AUDIO_FILTER:
+      gst_play_sink_set_filter (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO,
+          GST_ELEMENT (g_value_get_object (value)));
+      break;
     case PROP_VIDEO_SINK:
       gst_play_bin_set_sink (playbin, GST_PLAY_SINK_TYPE_VIDEO, "video",
           &playbin->video_sink, g_value_get_object (value));
@@ -2469,6 +2487,16 @@ gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
           gst_play_sink_get_subtitle_encoding (playbin->playsink));
       GST_PLAY_BIN_UNLOCK (playbin);
       break;
+    case PROP_VIDEO_FILTER:
+      g_value_take_object (value,
+          gst_play_sink_get_filter (playbin->playsink,
+              GST_PLAY_SINK_TYPE_VIDEO));
+      break;
+    case PROP_AUDIO_FILTER:
+      g_value_take_object (value,
+          gst_play_sink_get_filter (playbin->playsink,
+              GST_PLAY_SINK_TYPE_AUDIO));
+      break;
     case PROP_VIDEO_SINK:
       g_value_take_object (value,
           gst_play_bin_get_current_sink (playbin, &playbin->video_sink,
index d8fe74f..1d19077 100644 (file)
@@ -99,6 +99,7 @@ typedef struct
   GstPlayChain chain;
   GstPad *sinkpad;
   GstElement *queue;
+  GstElement *filter;
   GstElement *conv;
   GstElement *volume;           /* element with the volume property */
   gboolean sink_volume;         /* if the volume was provided by the sink */
@@ -122,6 +123,7 @@ typedef struct
   GstPlayChain chain;
   GstPad *sinkpad;
   GstElement *queue;
+  GstElement *filter;
   GstElement *conv;
   GstElement *sink;
   gboolean async;
@@ -237,6 +239,8 @@ struct _GstPlaySink
   /* properties */
   GstElement *audio_sink;
   GstElement *video_sink;
+  GstElement *audio_filter;
+  GstElement *video_filter;
   GstElement *visualisation;
   GstElement *text_sink;
   gdouble volume;
@@ -338,6 +342,8 @@ enum
   PROP_TEXT_SINK,
   PROP_SEND_EVENT_MODE,
   PROP_FORCE_ASPECT_RATIO,
+  PROP_VIDEO_FILTER,
+  PROP_AUDIO_FILTER,
   PROP_LAST
 };
 
@@ -511,6 +517,29 @@ gst_play_sink_class_init (GstPlaySinkClass * klass)
           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   /**
+   * GstPlaySink:video-filter:
+   *
+   * Set the video filter element/bin to use. Will apply on a best-effort basis
+   * unless GST_PLAY_FLAG_FORCE_FILTERS is set. playsink must be in
+   * %GST_STATE_NULL
+   */
+  g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
+      g_param_spec_object ("video-filter", "Video filter",
+          "the video filter(s) to apply, if possible",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+  /**
+   * GstPlaySink:audio-filter:
+   *
+   * Set the audio filter element/bin to use. Will apply on a best-effort basis
+   * unless GST_PLAY_FLAG_FORCE_FILTERS is set. playsink must be in
+   * %GST_STATE_NULL
+   */
+  g_object_class_install_property (gobject_klass, PROP_AUDIO_FILTER,
+      g_param_spec_object ("audio-filter", "Audio filter",
+          "the audio filter(s) to apply, if possible",
+          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  /**
    * GstPlaySink:video-sink:
    *
    * Set the used video sink element. NULL will use the default sink. playsink
@@ -720,6 +749,16 @@ gst_play_sink_dispose (GObject * object)
 
   playsink = GST_PLAY_SINK (object);
 
+  if (playsink->audio_filter != NULL) {
+    gst_element_set_state (playsink->audio_filter, GST_STATE_NULL);
+    gst_object_unref (playsink->audio_filter);
+    playsink->audio_filter = NULL;
+  }
+  if (playsink->video_filter != NULL) {
+    gst_element_set_state (playsink->video_filter, GST_STATE_NULL);
+    gst_object_unref (playsink->video_filter);
+    playsink->video_filter = NULL;
+  }
   if (playsink->audio_sink != NULL) {
     gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
     gst_object_unref (playsink->audio_sink);
@@ -888,6 +927,83 @@ gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
   return result;
 }
 
+void
+gst_play_sink_set_filter (GstPlaySink * playsink, GstPlaySinkType type,
+    GstElement * filter)
+{
+  GstElement **elem = NULL, *old = NULL;
+
+  GST_LOG_OBJECT (playsink,
+      "Setting filter %" GST_PTR_FORMAT " as filter type %d", filter, type);
+
+  GST_PLAY_SINK_LOCK (playsink);
+  switch (type) {
+    case GST_PLAY_SINK_TYPE_AUDIO:
+    case GST_PLAY_SINK_TYPE_AUDIO_RAW:
+      elem = &playsink->audio_filter;
+      break;
+    case GST_PLAY_SINK_TYPE_VIDEO:
+    case GST_PLAY_SINK_TYPE_VIDEO_RAW:
+      elem = &playsink->video_filter;
+      break;
+    default:
+      break;
+  }
+  if (elem) {
+    old = *elem;
+    if (filter)
+      gst_object_ref_sink (filter);
+    *elem = filter;
+  }
+  GST_PLAY_SINK_UNLOCK (playsink);
+
+  if (old) {
+    /* Set the old filter to NULL if it is not used any longer */
+    if (old != filter && !GST_OBJECT_PARENT (old))
+      gst_element_set_state (old, GST_STATE_NULL);
+    gst_object_unref (old);
+  }
+}
+
+GstElement *
+gst_play_sink_get_filter (GstPlaySink * playsink, GstPlaySinkType type)
+{
+  GstElement *result = NULL;
+  GstElement *elem = NULL, *chainp = NULL;
+
+  GST_PLAY_SINK_LOCK (playsink);
+  switch (type) {
+    case GST_PLAY_SINK_TYPE_AUDIO_RAW:
+    {
+      GstPlayAudioChain *chain;
+      if ((chain = (GstPlayAudioChain *) playsink->audiochain))
+        chainp = chain->filter;
+      elem = playsink->audio_filter;
+      break;
+    }
+    case GST_PLAY_SINK_TYPE_VIDEO_RAW:
+    {
+      GstPlayVideoChain *chain;
+      if ((chain = (GstPlayVideoChain *) playsink->videochain))
+        chainp = chain->filter;
+      elem = playsink->video_filter;
+      break;
+    }
+    default:
+      break;
+  }
+  if (chainp) {
+    /* we have an active chain with a filter, get the filter */
+    result = gst_object_ref (chainp);
+  }
+  /* nothing found, return last configured filter */
+  if (result == NULL && elem)
+    result = gst_object_ref (elem);
+  GST_PLAY_SINK_UNLOCK (playsink);
+
+  return result;
+}
+
 static GstPadProbeReturn
 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
     gpointer user_data)
@@ -1571,14 +1687,14 @@ update_colorbalance (GstPlaySink * playsink)
 /* make the element (bin) that contains the elements needed to perform
  * video display.
  *
- *  +------------------------------------------------------------+
- *  | vbin                                                       |
- *  |      +-------+   +----------+   +----------+   +---------+ |
- *  |      | queue |   |colorspace|   |videoscale|   |videosink| |
- *  |   +-sink    src-sink       src-sink       src-sink       | |
- *  |   |  +-------+   +----------+   +----------+   +---------+ |
- * sink-+                                                        |
- *  +------------------------------------------------------------+
+ *  +------------------------------------------------------------------------+
+ *  | vbin                                                                   |
+ *  |      +--------+  +-------+   +----------+   +----------+   +---------+ |
+ *  |      | filter |  | queue |   |colorspace|   |videoscale|   |videosink| |
+ *  |   +-sink    src-sink    src-sink       src-sink       src-sink       | |
+ *  |   |  +--------+  +-------+   +----------+   +----------+   +---------+ |
+ * sink-+                                                                    |
+ *  +------------------------------------------------------------------------+
  *
  */
 static GstPlayVideoChain *
@@ -1683,6 +1799,28 @@ gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
     }
   }
 
+  head = chain->sink;
+  prev = NULL;
+
+  /* add the video filter first, so everything is working with post-filter
+   * samples */
+  chain->filter = gst_play_sink_get_filter (playsink,
+      GST_PLAY_SINK_TYPE_VIDEO_RAW);
+  if (chain->filter) {
+    if (!raw) {
+      if (playsink->flags & GST_PLAY_FLAG_FORCE_FILTERS) {
+        goto filter_with_nonraw;
+      } else {
+        GST_DEBUG_OBJECT (playsink,
+            "skipping video filter since we're not raw");
+      }
+    } else {
+      GST_DEBUG_OBJECT (playsink, "adding video filter");
+      gst_bin_add (bin, chain->filter);
+      head = prev = chain->filter;
+    }
+  }
+
   /* decouple decoder from sink, this improves playback quite a lot since the
    * decoder can continue while the sink blocks for synchronisation. We don't
    * need a lot of buffers as this consumes a lot of memory and we don't want
@@ -1693,13 +1831,18 @@ gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
         (_("Missing element '%s' - check your GStreamer installation."),
             "queue"), ("video rendering might be suboptimal"));
-    head = chain->sink;
-    prev = NULL;
   } else {
     g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
         "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
     gst_bin_add (bin, chain->queue);
-    head = prev = chain->queue;
+    if (prev) {
+      if (!gst_element_link_pads_full (prev, "src", chain->queue, "sink",
+              GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
+        goto link_failed;
+    } else {
+      head = chain->queue;
+    }
+    prev = chain->queue;
   }
 
   GST_OBJECT_LOCK (playsink);
@@ -1813,13 +1956,21 @@ link_failed:
   {
     GST_ELEMENT_ERROR (playsink, CORE, PAD,
         (NULL), ("Failed to configure the video sink."));
-    /* checking sink made it READY */
-    gst_element_set_state (chain->sink, GST_STATE_NULL);
-    /* Remove chain from the bin to allow reuse later */
-    gst_bin_remove (bin, chain->sink);
-    free_chain ((GstPlayChain *) chain);
-    return NULL;
+    goto cleanup;
   }
+filter_with_nonraw:
+  {
+    GST_ELEMENT_ERROR (playsink, CORE, NEGOTIATION,
+        (NULL), ("Cannot apply video-filter on non-raw stream"));
+    goto cleanup;
+  }
+cleanup:
+  /* checking sink made it READY */
+  gst_element_set_state (chain->sink, GST_STATE_NULL);
+  /* Remove chain from the bin to allow reuse later */
+  gst_bin_remove (bin, chain->sink);
+  free_chain ((GstPlayChain *) chain);
+  return NULL;
 }
 
 static gboolean
@@ -1831,6 +1982,10 @@ setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
 
   chain = playsink->videochain;
 
+  /* if we have a filter, and raw-ness changed, we have to force a rebuild */
+  if (chain->filter && chain->chain.raw != raw)
+    return FALSE;
+
   chain->chain.raw = raw;
 
   /* if the chain was active we don't do anything */
@@ -2444,14 +2599,14 @@ notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
  * We add a tee as the first element so that we can link the visualisation chain
  * to it when requested.
  *
- *  +------------------------------------------------+
- *  | abin                                           |
- *  |      +---------+   +---------+   +-----------+ |
- *  |      |  queue  |   | convbin |   | audiosink | |
- *  |   +-sink      src-sink       src-sink        | |
- *  |   |  +---------+   +---------+   +-----------+ |
- * sink-+                                            |
- *  +------------------------------------------------+
+ *  +--------------------------------------------------------------+
+ *  | abin                                                         |
+ *  |      +----------+   +--------+   +---------+   +-----------+ |
+ *  |      |  filter  |   | queue  |   | convbin |   | audiosink | |
+ *  |   +-sink       src-sink     src-sink      src-sink         | |
+ *  |   |  +----------+   +--------+   +---------+   +-----------+ |
+ * sink-+                                                          |
+ *  +--------------------------------------------------------------+
  */
 static GstPlayAudioChain *
 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
@@ -2498,6 +2653,28 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw)
   gst_object_ref_sink (bin);
   gst_bin_add (bin, chain->sink);
 
+  head = chain->sink;
+  prev = NULL;
+
+  /* add the audio filter first, so everything is working with post-filter
+   * samples */
+  chain->filter = gst_play_sink_get_filter (playsink,
+      GST_PLAY_SINK_TYPE_AUDIO_RAW);
+  if (chain->filter) {
+    if (!raw) {
+      if (playsink->flags & GST_PLAY_FLAG_FORCE_FILTERS) {
+        goto filter_with_nonraw;
+      } else {
+        GST_DEBUG_OBJECT (playsink,
+            "skipping video filter since we're not raw");
+      }
+    } else {
+      GST_DEBUG_OBJECT (playsink, "adding video filter");
+      gst_bin_add (bin, chain->filter);
+      head = prev = chain->filter;
+    }
+  }
+
   /* we have to add a queue when we need to decouple for the video sink in
    * visualisations and for streamsynchronizer */
   GST_DEBUG_OBJECT (playsink, "adding audio queue");
@@ -2507,12 +2684,17 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw)
     GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
         (_("Missing element '%s' - check your GStreamer installation."),
             "queue"), ("audio playback and visualizations might not work"));
-    head = chain->sink;
-    prev = NULL;
   } else {
     g_object_set (chain->queue, "silent", TRUE, NULL);
     gst_bin_add (bin, chain->queue);
-    prev = head = chain->queue;
+    if (prev) {
+      if (!gst_element_link_pads_full (prev, "src", chain->queue, "sink",
+              GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
+        goto link_failed;
+    } else {
+      head = chain->queue;
+    }
+    prev = chain->queue;
   }
 
   /* find ts-offset element */
@@ -2682,13 +2864,21 @@ link_failed:
   {
     GST_ELEMENT_ERROR (playsink, CORE, PAD,
         (NULL), ("Failed to configure the audio sink."));
-    /* checking sink made it READY */
-    gst_element_set_state (chain->sink, GST_STATE_NULL);
-    /* Remove chain from the bin to allow reuse later */
-    gst_bin_remove (bin, chain->sink);
-    free_chain ((GstPlayChain *) chain);
-    return NULL;
+    goto cleanup;
   }
+filter_with_nonraw:
+  {
+    GST_ELEMENT_ERROR (playsink, CORE, NEGOTIATION,
+        (NULL), ("Cannot apply video-filter on non-raw stream"));
+    goto cleanup;
+  }
+cleanup:
+  /* checking sink made it READY */
+  gst_element_set_state (chain->sink, GST_STATE_NULL);
+  /* Remove chain from the bin to allow reuse later */
+  gst_bin_remove (bin, chain->sink);
+  free_chain ((GstPlayChain *) chain);
+  return NULL;
 }
 
 static gboolean
@@ -2702,6 +2892,10 @@ setup_audio_chain (GstPlaySink * playsink, gboolean raw)
   chain = playsink->audiochain;
   conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
 
+  /* if we have a filter, and raw-ness changed, we have to force a rebuild */
+  if (chain->filter && chain->chain.raw != raw)
+    return FALSE;
+
   chain->chain.raw = raw;
 
   /* if the chain was active we don't do anything */
@@ -3342,8 +3536,8 @@ gst_play_sink_do_reconfigure (GstPlaySink * playsink)
         /* unlink the old plugin and unghost the pad */
         gst_pad_unlink (playsink->vischain->vispeerpad,
             playsink->vischain->vissinkpad);
-        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->
-                vischain->srcpad), NULL);
+        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->vischain->
+                srcpad), NULL);
 
         /* set the old plugin to NULL and remove */
         gst_element_set_state (playsink->vischain->vis, GST_STATE_NULL);
@@ -3365,8 +3559,8 @@ gst_play_sink_do_reconfigure (GstPlaySink * playsink)
         /* link pads */
         gst_pad_link_full (playsink->vischain->vispeerpad,
             playsink->vischain->vissinkpad, GST_PAD_LINK_CHECK_NOTHING);
-        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->
-                vischain->srcpad), playsink->vischain->vissrcpad);
+        gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->vischain->
+                srcpad), playsink->vischain->vissrcpad);
       } else {
         srcpad =
             gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
@@ -3836,8 +4030,8 @@ video_set_blocked (GstPlaySink * playsink, gboolean blocked)
             (playsink->video_pad)));
     if (blocked && playsink->video_block_id == 0) {
       if (playsink->vis_pad_block_id)
-        gst_pad_remove_probe (((GstPlayVisChain *) playsink->
-                vischain)->blockpad, playsink->vis_pad_block_id);
+        gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->
+            blockpad, playsink->vis_pad_block_id);
       playsink->vis_pad_block_id = 0;
 
       playsink->video_block_id =
@@ -3863,8 +4057,8 @@ audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
             (playsink->audio_pad)));
     if (blocked && playsink->audio_block_id == 0) {
       if (playsink->vis_pad_block_id)
-        gst_pad_remove_probe (((GstPlayVisChain *) playsink->
-                vischain)->blockpad, playsink->vis_pad_block_id);
+        gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->
+            blockpad, playsink->vis_pad_block_id);
       playsink->vis_pad_block_id = 0;
 
       playsink->audio_block_id =
@@ -3872,8 +4066,8 @@ audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
           sinkpad_blocked_cb, playsink, NULL);
     } else if (!blocked && playsink->audio_block_id) {
       if (playsink->vis_pad_block_id)
-        gst_pad_remove_probe (((GstPlayVisChain *) playsink->
-                vischain)->blockpad, playsink->vis_pad_block_id);
+        gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->
+            blockpad, playsink->vis_pad_block_id);
       playsink->vis_pad_block_id = 0;
 
       gst_pad_remove_probe (opad, playsink->audio_block_id);
@@ -3895,8 +4089,8 @@ text_set_blocked (GstPlaySink * playsink, gboolean blocked)
             (playsink->text_pad)));
     if (blocked && playsink->text_block_id == 0) {
       if (playsink->vis_pad_block_id)
-        gst_pad_remove_probe (((GstPlayVisChain *) playsink->
-                vischain)->blockpad, playsink->vis_pad_block_id);
+        gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->
+            blockpad, playsink->vis_pad_block_id);
       playsink->vis_pad_block_id = 0;
 
       playsink->text_block_id =
@@ -4187,8 +4381,8 @@ gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
           GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
 
       if (playsink->vis_pad_block_id)
-        gst_pad_remove_probe (((GstPlayVisChain *) playsink->
-                vischain)->blockpad, playsink->vis_pad_block_id);
+        gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->
+            blockpad, playsink->vis_pad_block_id);
       playsink->vis_pad_block_id = 0;
 
       *block_id =
@@ -4497,8 +4691,8 @@ gst_play_sink_change_state (GstElement * element, GstStateChange transition)
       audio_set_blocked (playsink, FALSE);
       text_set_blocked (playsink, FALSE);
       if (playsink->vis_pad_block_id)
-        gst_pad_remove_probe (((GstPlayVisChain *) playsink->
-                vischain)->blockpad, playsink->vis_pad_block_id);
+        gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->
+            blockpad, playsink->vis_pad_block_id);
       playsink->vis_pad_block_id = 0;
 
       GST_PLAY_SINK_UNLOCK (playsink);
@@ -4706,6 +4900,14 @@ gst_play_sink_set_property (GObject * object, guint prop_id,
     case PROP_AV_OFFSET:
       gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
       break;
+    case PROP_VIDEO_FILTER:
+      gst_play_sink_set_filter (playsink, GST_PLAY_SINK_TYPE_VIDEO,
+          g_value_get_object (value));
+      break;
+    case PROP_AUDIO_FILTER:
+      gst_play_sink_set_filter (playsink, GST_PLAY_SINK_TYPE_AUDIO,
+          g_value_get_object (value));
+      break;
     case PROP_VIDEO_SINK:
       gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
           g_value_get_object (value));
@@ -4781,6 +4983,14 @@ gst_play_sink_get_property (GObject * object, guint prop_id,
     case PROP_AV_OFFSET:
       g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
       break;
+    case PROP_VIDEO_FILTER:
+      g_value_take_object (value, gst_play_sink_get_filter (playsink,
+              GST_PLAY_SINK_TYPE_VIDEO));
+      break;
+    case PROP_AUDIO_FILTER:
+      g_value_take_object (value, gst_play_sink_get_filter (playsink,
+              GST_PLAY_SINK_TYPE_AUDIO));
+      break;
     case PROP_VIDEO_SINK:
       g_value_take_object (value, gst_play_sink_get_sink (playsink,
               GST_PLAY_SINK_TYPE_VIDEO));
index ff47a19..f102583 100644 (file)
@@ -72,6 +72,9 @@ GstPad *         gst_play_sink_request_pad    (GstPlaySink *playsink, GstPlaySin
 void             gst_play_sink_release_pad    (GstPlaySink *playsink, GstPad *pad);
 void             gst_play_sink_refresh_pad    (GstPlaySink *playsink, GstPad *pad, GstPlaySinkType type);
 
+void             gst_play_sink_set_filter     (GstPlaySink * playsink, GstPlaySinkType type, GstElement * filter);
+GstElement *     gst_play_sink_get_filter     (GstPlaySink * playsink, GstPlaySinkType type);
+
 void             gst_play_sink_set_sink       (GstPlaySink * playsink, GstPlaySinkType type, GstElement * sink);
 GstElement *     gst_play_sink_get_sink       (GstPlaySink * playsink, GstPlaySinkType type);