From f4cab18ec13548426245e5cc1ae43caa69a42317 Mon Sep 17 00:00:00 2001 From: Arun Raghavan Date: Sun, 16 Mar 2014 17:00:38 +0100 Subject: [PATCH] playback: Add video-/audio-filter properties This provides an audio-filter and video-filter property to allow applications to set filter elements/bins. The idea is that these will e 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.c | 5 +- gst/playback/gstplay-enum.h | 5 +- gst/playback/gstplaybin2.c | 28 +++ gst/playback/gstplaysink.c | 310 ++++++++++++++++++++++++++------ gst/playback/gstplaysink.h | 3 + tests/examples/playback/playback-test.c | 3 +- 6 files changed, 300 insertions(+), 54 deletions(-) diff --git a/gst/playback/gstplay-enum.c b/gst/playback/gstplay-enum.c index 4c4861f..7b34a64 100644 --- a/gst/playback/gstplay-enum.c +++ b/gst/playback/gstplay-enum.c @@ -65,8 +65,9 @@ gst_play_flags_get_type (void) "buffering"}, {C_FLAGS (GST_PLAY_FLAG_DEINTERLACE), "Deinterlace video if necessary", "deinterlace"}, - {C_FLAGS (GST_PLAY_FLAG_SOFT_COLORBALANCE), "Use software color balance", - "soft-colorbalance"}, + {C_FLAGS (GST_PLAY_FLAG_SOFT_COLORBALANCE), "Use software color balance"}, + {C_FLAGS (GST_PLAY_FLAG_FORCE_FILTERS), + "Force audio/video filter(s) to be applied", "force-filters"}, {0, NULL, NULL} }; static volatile GType id = 0; diff --git a/gst/playback/gstplay-enum.h b/gst/playback/gstplay-enum.h index f6ab5d9..0c75b53 100644 --- a/gst/playback/gstplay-enum.h +++ b/gst/playback/gstplay-enum.h @@ -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()) diff --git a/gst/playback/gstplaybin2.c b/gst/playback/gstplaybin2.c index 54dd775..5e1ee7e 100644 --- a/gst/playback/gstplaybin2.c +++ b/gst/playback/gstplaybin2.c @@ -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, diff --git a/gst/playback/gstplaysink.c b/gst/playback/gstplaysink.c index d8fe74f..1d19077 100644 --- a/gst/playback/gstplaysink.c +++ b/gst/playback/gstplaysink.c @@ -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)); diff --git a/gst/playback/gstplaysink.h b/gst/playback/gstplaysink.h index ff47a19..f102583 100644 --- a/gst/playback/gstplaysink.h +++ b/gst/playback/gstplaysink.h @@ -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); diff --git a/tests/examples/playback/playback-test.c b/tests/examples/playback/playback-test.c index 9012caa..a229a8d 100644 --- a/tests/examples/playback/playback-test.c +++ b/tests/examples/playback/playback-test.c @@ -67,7 +67,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; /* configuration */ -- 2.7.4