1 From 1e0ddb12aa1c2b13c4bc4a13712ebd7f06a6346e Mon Sep 17 00:00:00 2001
2 From: Sebastian Dröge <sebastian.droege@collabora.co.uk>
3 Date: Fri, 25 Mar 2011 07:26:00 +0000
4 Subject: playsink: Add audio and video converter convenience bins
6 These reconfigure based on the caps and plugin in converters if
7 necessary. This also makes switching between compressed and raw
8 streams work flawlessly without loosing the states of any element
9 somewhere or having running time problems.
11 diff --git a/gst/playback/Makefile.am b/gst/playback/Makefile.am
12 index 3adb56d..2fe90e6 100644
13 --- a/gst/playback/Makefile.am
14 +++ b/gst/playback/Makefile.am
15 @@ -18,6 +18,8 @@ libgstplaybin_la_SOURCES = \
18 gstsubtitleoverlay.c \
19 + gstplaysinkvideoconvert.c \
20 + gstplaysinkaudioconvert.c \
21 gststreamsynchronizer.c
23 nodist_libgstplaybin_la_SOURCES = $(built_sources)
24 @@ -57,6 +59,8 @@ noinst_HEADERS = \
27 gstsubtitleoverlay.h \
28 + gstplaysinkvideoconvert.h \
29 + gstplaysinkaudioconvert.h \
30 gststreamsynchronizer.h
32 BUILT_SOURCES = $(built_headers) $(built_sources)
33 diff --git a/gst/playback/gstplaysink.c b/gst/playback/gstplaysink.c
34 index 3661af3..1449d00 100644
35 --- a/gst/playback/gstplaysink.c
36 +++ b/gst/playback/gstplaysink.c
39 #include "gstplaysink.h"
40 #include "gststreamsynchronizer.h"
41 +#include "gstplaysinkvideoconvert.h"
42 +#include "gstplaysinkaudioconvert.h"
44 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
45 #define GST_CAT_DEFAULT gst_play_sink_debug
46 @@ -59,7 +61,6 @@ typedef struct
50 - GstElement *resample;
51 GstElement *volume; /* element with the volume property */
52 gboolean sink_volume; /* if the volume was provided by the sink */
53 GstElement *mute; /* element with the mute property */
54 @@ -81,7 +82,6 @@ typedef struct
61 GstElement *ts_offset;
62 @@ -1278,46 +1278,19 @@ gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
63 head = prev = chain->queue;
66 - if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
67 - GST_DEBUG_OBJECT (playsink, "creating ffmpegcolorspace");
68 - chain->conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
69 - if (chain->conv == NULL) {
70 - post_missing_element_message (playsink, "ffmpegcolorspace");
71 - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
72 - (_("Missing element '%s' - check your GStreamer installation."),
73 - "ffmpegcolorspace"), ("video rendering might fail"));
74 + if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
75 + GST_DEBUG_OBJECT (playsink, "creating videoconverter");
77 + g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
78 + gst_bin_add (bin, chain->conv);
80 + if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
81 + GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
84 - gst_bin_add (bin, chain->conv);
86 - if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
87 - GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
95 - GST_DEBUG_OBJECT (playsink, "creating videoscale");
96 - chain->scale = gst_element_factory_make ("videoscale", "vscale");
97 - if (chain->scale == NULL) {
98 - post_missing_element_message (playsink, "videoscale");
99 - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
100 - (_("Missing element '%s' - check your GStreamer installation."),
101 - "videoscale"), ("possibly a liboil version mismatch?"));
103 - /* Add black borders if necessary to keep the DAR */
104 - g_object_set (chain->scale, "add-borders", TRUE, NULL);
105 - gst_bin_add (bin, chain->scale);
107 - if (!gst_element_link_pads_full (prev, "src", chain->scale, "sink",
108 - GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
111 - head = chain->scale;
113 - prev = chain->scale;
114 + head = chain->conv;
116 + prev = chain->conv;
120 @@ -1388,8 +1361,7 @@ setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
122 chain = playsink->videochain;
124 - if (chain->chain.raw != raw)
126 + chain->chain.raw = raw;
128 /* if the chain was active we don't do anything */
129 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
130 @@ -1768,54 +1740,32 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw)
131 chain->sink_volume = FALSE;
134 - if (raw && !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
135 + if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
136 + && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
137 GST_DEBUG_OBJECT (playsink, "creating audioconvert");
138 - chain->conv = gst_element_factory_make ("audioconvert", "aconv");
139 - if (chain->conv == NULL) {
140 - post_missing_element_message (playsink, "audioconvert");
141 - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
142 - (_("Missing element '%s' - check your GStreamer installation."),
143 - "audioconvert"), ("possibly a liboil version mismatch?"));
145 + g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv", NULL);
146 + gst_bin_add (bin, chain->conv);
148 + if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
149 + GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
152 - gst_bin_add (bin, chain->conv);
154 - if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
155 - GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
158 - head = chain->conv;
160 - prev = chain->conv;
161 + head = chain->conv;
163 + prev = chain->conv;
165 - GST_DEBUG_OBJECT (playsink, "creating audioresample");
166 - chain->resample = gst_element_factory_make ("audioresample", "aresample");
167 - if (chain->resample == NULL) {
168 - post_missing_element_message (playsink, "audioresample");
169 - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
170 - (_("Missing element '%s' - check your GStreamer installation."),
171 - "audioresample"), ("possibly a liboil version mismatch?"));
173 - gst_bin_add (bin, chain->resample);
175 - if (!gst_element_link_pads_full (prev, "src", chain->resample, "sink",
176 - GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
179 - head = chain->resample;
181 - prev = chain->resample;
183 + GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_converters =
184 + !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
185 + GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = (!have_volume
186 + && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
188 if (!have_volume && playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME) {
189 - GST_DEBUG_OBJECT (playsink, "creating volume");
190 - chain->volume = gst_element_factory_make ("volume", "volume");
191 - if (chain->volume == NULL) {
192 - post_missing_element_message (playsink, "volume");
193 - GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
194 - (_("Missing element '%s' - check your GStreamer installation."),
195 - "volume"), ("possibly a liboil version mismatch?"));
197 + GstPlaySinkAudioConvert *conv =
198 + GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
200 + if (conv->volume) {
201 + chain->volume = conv->volume;
204 g_signal_connect (chain->volume, "notify::volume",
205 @@ -1830,16 +1780,6 @@ gen_audio_chain (GstPlaySink * playsink, gboolean raw)
206 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
208 g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
209 - gst_bin_add (bin, chain->volume);
212 - if (!gst_element_link_pads_full (prev, "src", chain->volume, "sink",
213 - GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
216 - head = chain->volume;
218 - prev = chain->volume;
222 @@ -1921,8 +1861,7 @@ setup_audio_chain (GstPlaySink * playsink, gboolean raw)
224 chain = playsink->audiochain;
226 - if (chain->chain.raw != raw)
228 + chain->chain.raw = raw;
230 /* if the chain was active we don't do anything */
231 if (GST_PLAY_CHAIN (chain)->activated == TRUE)
232 @@ -1967,29 +1906,35 @@ setup_audio_chain (GstPlaySink * playsink, gboolean raw)
233 g_signal_connect (chain->mute, "notify::mute",
234 G_CALLBACK (notify_mute_cb), playsink);
237 + GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = FALSE;
239 + GstPlaySinkAudioConvert *conv =
240 + GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
242 /* no volume, we need to add a volume element when we can */
243 + conv->use_volume = TRUE;
244 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
246 - GST_LOG_OBJECT (playsink, "non-raw format, can't do soft volume control");
248 - disconnect_chain (chain, playsink);
249 - chain->volume = NULL;
250 - chain->mute = NULL;
252 - /* both last and current chain are raw audio, there should be a volume
253 - * element already, unless the sink changed from one with a volume
254 - * property to one that hasn't got a volume property, in which case we
255 - * re-generate the chain */
256 - if (chain->volume == NULL) {
257 - GST_DEBUG_OBJECT (playsink, "no existing volume element to re-use");
258 - /* undo background state change done earlier */
259 - gst_element_set_state (chain->sink, GST_STATE_NULL);
262 + /* Disconnect signals */
263 + disconnect_chain (chain, playsink);
265 + if (conv->volume) {
266 + chain->volume = conv->volume;
267 + chain->mute = chain->volume;
269 - GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
270 + g_signal_connect (chain->volume, "notify::volume",
271 + G_CALLBACK (notify_volume_cb), playsink);
273 + g_signal_connect (chain->mute, "notify::mute",
274 + G_CALLBACK (notify_mute_cb), playsink);
276 + /* configure with the latest volume and mute */
277 + g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
278 + g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
281 + GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
285 diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c
287 index 0000000..43c5272
289 +++ b/gst/playback/gstplaysinkaudioconvert.c
292 + * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
294 + * This library is free software; you can redistribute it and/or
295 + * modify it under the terms of the GNU Library General Public
296 + * License as published by the Free Software Foundation; either
297 + * version 2 of the License, or (at your option) any later version.
299 + * This library is distributed in the hope that it will be useful,
300 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
301 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
302 + * Library General Public License for more details.
304 + * You should have received a copy of the GNU Library General Public
305 + * License along with this library; if not, write to the
306 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
307 + * Boston, MA 02111-1307, USA.
310 +#ifdef HAVE_CONFIG_H
314 +#include "gstplaysinkaudioconvert.h"
316 +#include <gst/pbutils/pbutils.h>
317 +#include <gst/gst-i18n-plugin.h>
319 +GST_DEBUG_CATEGORY_STATIC (gst_play_sink_audio_convert_debug);
320 +#define GST_CAT_DEFAULT gst_play_sink_audio_convert_debug
322 +#define parent_class gst_play_sink_audio_convert_parent_class
324 +G_DEFINE_TYPE (GstPlaySinkAudioConvert, gst_play_sink_audio_convert,
327 +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
330 + GST_STATIC_CAPS_ANY);
332 +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
335 + GST_STATIC_CAPS_ANY);
338 +is_raw_caps (GstCaps * caps)
344 + n = gst_caps_get_size (caps);
345 + for (i = 0; i < n; i++) {
346 + s = gst_caps_get_structure (caps, i);
347 + name = gst_structure_get_name (s);
348 + if (!g_str_has_prefix (name, "audio/x-raw"))
356 +post_missing_element_message (GstPlaySinkAudioConvert * self,
357 + const gchar * name)
361 + msg = gst_missing_element_message_new (GST_ELEMENT_CAST (self), name);
362 + gst_element_post_message (GST_ELEMENT_CAST (self), msg);
366 +distribute_running_time (GstElement * element, const GstSegment * segment)
371 + pad = gst_element_get_static_pad (element, "sink");
373 + if (segment->accum) {
374 + event = gst_event_new_new_segment_full (FALSE, segment->rate,
375 + segment->applied_rate, segment->format, 0, segment->accum, 0);
376 + gst_pad_push_event (pad, event);
379 + event = gst_event_new_new_segment_full (FALSE, segment->rate,
380 + segment->applied_rate, segment->format,
381 + segment->start, segment->stop, segment->time);
382 + gst_pad_push_event (pad, event);
384 + gst_object_unref (pad);
388 +pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkAudioConvert * self)
394 + GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
395 + self->sink_proxypad_blocked = blocked;
396 + GST_DEBUG_OBJECT (self, "Pad blocked: %d", blocked);
400 + /* There must be a peer at this point */
401 + peer = gst_pad_get_peer (self->sinkpad);
402 + caps = gst_pad_get_negotiated_caps (peer);
404 + caps = gst_pad_get_caps_reffed (peer);
405 + gst_object_unref (peer);
407 + raw = is_raw_caps (caps);
408 + GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw);
409 + gst_caps_unref (caps);
411 + if (raw == self->raw)
416 + GstBin *bin = GST_BIN_CAST (self);
417 + GstElement *head = NULL, *prev = NULL;
420 + GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline");
422 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
423 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
425 + if (self->use_converters) {
426 + self->conv = gst_element_factory_make ("audioconvert", "conv");
427 + if (self->conv == NULL) {
428 + post_missing_element_message (self, "audioconvert");
429 + GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
430 + (_("Missing element '%s' - check your GStreamer installation."),
431 + "audioconvert"), ("audio rendering might fail"));
433 + gst_bin_add (bin, self->conv);
434 + gst_element_sync_state_with_parent (self->conv);
435 + distribute_running_time (self->conv, &self->segment);
436 + prev = head = self->conv;
439 + self->resample = gst_element_factory_make ("audioresample", "resample");
440 + if (self->resample == NULL) {
441 + post_missing_element_message (self, "audioresample");
442 + GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
443 + (_("Missing element '%s' - check your GStreamer installation."),
444 + "audioresample"), ("possibly a liboil version mismatch?"));
446 + gst_bin_add (bin, self->resample);
447 + gst_element_sync_state_with_parent (self->resample);
448 + distribute_running_time (self->resample, &self->segment);
450 + if (!gst_element_link_pads_full (prev, "src", self->resample, "sink",
451 + GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
454 + head = self->resample;
456 + prev = self->resample;
460 + if (self->use_volume && self->volume) {
461 + gst_bin_add (bin, gst_object_ref (self->volume));
462 + gst_element_sync_state_with_parent (self->volume);
463 + distribute_running_time (self->volume, &self->segment);
465 + if (!gst_element_link_pads_full (prev, "src", self->volume, "sink",
466 + GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
469 + head = self->volume;
471 + prev = self->volume;
475 + pad = gst_element_get_static_pad (head, "sink");
476 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad);
477 + gst_object_unref (pad);
481 + pad = gst_element_get_static_pad (prev, "src");
482 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad);
483 + gst_object_unref (pad);
486 + if (!head && !prev) {
487 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
488 + self->sink_proxypad);
491 + GST_DEBUG_OBJECT (self, "Raw conversion pipeline created");
493 + GstBin *bin = GST_BIN_CAST (self);
495 + GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline");
497 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
498 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
501 + gst_element_set_state (self->conv, GST_STATE_NULL);
502 + gst_bin_remove (bin, self->conv);
505 + if (self->resample) {
506 + gst_element_set_state (self->resample, GST_STATE_NULL);
507 + gst_bin_remove (bin, self->resample);
508 + self->resample = NULL;
510 + if (self->volume) {
511 + gst_element_set_state (self->volume, GST_STATE_NULL);
512 + if (GST_OBJECT_PARENT (self->volume) == GST_OBJECT_CAST (self)) {
513 + gst_bin_remove (GST_BIN_CAST (self), self->volume);
517 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
518 + self->sink_proxypad);
520 + GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed");
524 + gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
525 + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
526 + (GDestroyNotify) gst_object_unref);
529 + GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
534 + GST_ELEMENT_ERROR (self, CORE, PAD,
535 + (NULL), ("Failed to configure the audio converter."));
536 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
537 + self->sink_proxypad);
538 + gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
539 + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
540 + (GDestroyNotify) gst_object_unref);
546 +gst_play_sink_audio_convert_sink_event (GstPad * pad, GstEvent * event)
548 + GstPlaySinkAudioConvert *self =
549 + GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad));
552 + ret = self->sink_event (pad, gst_event_ref (event));
554 + if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
556 + gdouble rate, applied_rate;
558 + gint64 start, stop, position;
560 + GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
561 + gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
562 + &format, &start, &stop, &position);
564 + GST_DEBUG_OBJECT (self, "Segment before %" GST_SEGMENT_FORMAT,
566 + gst_segment_set_newsegment_full (&self->segment, update, rate, applied_rate,
567 + format, start, stop, position);
568 + GST_DEBUG_OBJECT (self, "Segment after %" GST_SEGMENT_FORMAT,
570 + GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
571 + } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
572 + GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
573 + GST_DEBUG_OBJECT (self, "Resetting segment");
574 + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
575 + GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
578 + gst_event_unref (event);
579 + gst_object_unref (self);
585 +gst_play_sink_audio_convert_sink_setcaps (GstPad * pad, GstCaps * caps)
587 + GstPlaySinkAudioConvert *self =
588 + GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad));
592 + gboolean reconfigure = FALSE;
594 + GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
595 + s = gst_caps_get_structure (caps, 0);
596 + name = gst_structure_get_name (s);
598 + if (g_str_has_prefix (name, "audio/x-raw-")) {
599 + if (!self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
600 + GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw");
601 + reconfigure = TRUE;
602 + gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
603 + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
604 + (GDestroyNotify) gst_object_unref);
607 + if (self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
608 + GST_DEBUG_OBJECT (self, "Changing caps from raw to non-raw");
609 + reconfigure = TRUE;
610 + gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
611 + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
612 + (GDestroyNotify) gst_object_unref);
616 + /* Otherwise the setcaps below fails */
618 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
619 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
622 + GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
623 + ret = self->sink_setcaps (pad, caps);
625 + GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT ": %d", caps,
628 + gst_object_unref (self);
634 +gst_play_sink_audio_convert_getcaps (GstPad * pad)
636 + GstPlaySinkAudioConvert *self =
637 + GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad));
639 + GstPad *otherpad, *peer;
641 + GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
642 + if (pad == self->srcpad)
643 + otherpad = gst_object_ref (self->sinkpad);
645 + otherpad = gst_object_ref (self->srcpad);
646 + GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
648 + peer = gst_pad_get_peer (otherpad);
650 + ret = gst_pad_get_caps_reffed (peer);
651 + gst_object_unref (peer);
653 + ret = gst_caps_new_any ();
656 + gst_object_unref (otherpad);
657 + gst_object_unref (self);
663 +gst_play_sink_audio_convert_finalize (GObject * object)
665 + GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (object);
668 + gst_object_unref (self->volume);
670 + gst_object_unref (self->sink_proxypad);
671 + g_mutex_free (self->lock);
673 + G_OBJECT_CLASS (parent_class)->finalize (object);
676 +static GstStateChangeReturn
677 +gst_play_sink_audio_convert_change_state (GstElement * element,
678 + GstStateChange transition)
680 + GstStateChangeReturn ret;
681 + GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (element);
683 + switch (transition) {
684 + case GST_STATE_CHANGE_PAUSED_TO_READY:
685 + GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
686 + if (gst_pad_is_blocked (self->sink_proxypad))
687 + gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
688 + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
689 + (GDestroyNotify) gst_object_unref);
690 + GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
696 + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
697 + if (ret == GST_STATE_CHANGE_FAILURE)
700 + switch (transition) {
701 + case GST_STATE_CHANGE_PAUSED_TO_READY:
702 + GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
703 + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
705 + gst_element_set_state (self->conv, GST_STATE_NULL);
706 + gst_bin_remove (GST_BIN_CAST (self), self->conv);
709 + if (self->resample) {
710 + gst_element_set_state (self->resample, GST_STATE_NULL);
711 + gst_bin_remove (GST_BIN_CAST (self), self->resample);
712 + self->resample = NULL;
714 + if (self->volume) {
715 + gst_element_set_state (self->volume, GST_STATE_NULL);
716 + if (GST_OBJECT_PARENT (self->volume) == GST_OBJECT_CAST (self)) {
717 + gst_bin_remove (GST_BIN_CAST (self), self->volume);
720 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
721 + self->sink_proxypad);
723 + GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
725 + case GST_STATE_CHANGE_READY_TO_PAUSED:
726 + GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
727 + if (!gst_pad_is_blocked (self->sink_proxypad))
728 + gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
729 + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
730 + (GDestroyNotify) gst_object_unref);
731 + GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
740 +gst_play_sink_audio_convert_class_init (GstPlaySinkAudioConvertClass * klass)
742 + GObjectClass *gobject_class;
743 + GstElementClass *gstelement_class;
745 + GST_DEBUG_CATEGORY_INIT (gst_play_sink_audio_convert_debug,
746 + "playsinkaudioconvert", 0, "play bin");
748 + gobject_class = (GObjectClass *) klass;
749 + gstelement_class = (GstElementClass *) klass;
751 + gobject_class->finalize = gst_play_sink_audio_convert_finalize;
753 + gst_element_class_add_pad_template (gstelement_class,
754 + gst_static_pad_template_get (&srctemplate));
755 + gst_element_class_add_pad_template (gstelement_class,
756 + gst_static_pad_template_get (&sinktemplate));
757 + gst_element_class_set_details_simple (gstelement_class,
758 + "Player Sink Audio Converter", "Audio/Bin/Converter",
759 + "Convenience bin for audio conversion",
760 + "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
762 + gstelement_class->change_state =
763 + GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_change_state);
767 +gst_play_sink_audio_convert_init (GstPlaySinkAudioConvert * self)
769 + GstPadTemplate *templ;
772 + self->lock = g_mutex_new ();
773 + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
775 + templ = gst_static_pad_template_get (&sinktemplate);
776 + self->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", templ);
777 + self->sink_event = GST_PAD_EVENTFUNC (self->sinkpad);
778 + gst_pad_set_event_function (self->sinkpad,
779 + GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_sink_event));
780 + self->sink_setcaps = GST_PAD_SETCAPSFUNC (self->sinkpad);
781 + gst_pad_set_setcaps_function (self->sinkpad,
782 + GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_sink_setcaps));
783 + gst_pad_set_getcaps_function (self->sinkpad,
784 + GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_getcaps));
786 + it = gst_pad_iterate_internal_links (self->sinkpad);
788 + gst_iterator_next (it, (gpointer *) & self->sink_proxypad);
789 + g_assert (self->sink_proxypad);
790 + gst_iterator_free (it);
792 + gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad);
793 + gst_object_unref (templ);
795 + templ = gst_static_pad_template_get (&srctemplate);
796 + self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ);
797 + gst_pad_set_getcaps_function (self->srcpad,
798 + GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_getcaps));
799 + gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
800 + gst_object_unref (templ);
802 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
803 + self->sink_proxypad);
805 + /* FIXME: Only create this on demand but for now we need
806 + * it to always exist because of playsink's volume proxying
809 + self->volume = gst_element_factory_make ("volume", "volume");
811 + gst_object_ref_sink (self->volume);
813 diff --git a/gst/playback/gstplaysinkaudioconvert.h b/gst/playback/gstplaysinkaudioconvert.h
815 index 0000000..b0016b9
817 +++ b/gst/playback/gstplaysinkaudioconvert.h
820 + * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
822 + * This library is free software; you can redistribute it and/or
823 + * modify it under the terms of the GNU Library General Public
824 + * License as published by the Free Software Foundation; either
825 + * version 2 of the License, or (at your option) any later version.
827 + * This library is distributed in the hope that it will be useful,
828 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
829 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
830 + * Library General Public License for more details.
832 + * You should have received a copy of the GNU Library General Public
833 + * License along with this library; if not, write to the
834 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
835 + * Boston, MA 02111-1307, USA.
838 +#include <gst/gst.h>
840 +#ifndef __GST_PLAY_SINK_AUDIO_CONVERT_H__
841 +#define __GST_PLAY_SINK_AUDIO_CONVERT_H__
844 +#define GST_TYPE_PLAY_SINK_AUDIO_CONVERT \
845 + (gst_play_sink_audio_convert_get_type())
846 +#define GST_PLAY_SINK_AUDIO_CONVERT(obj) \
847 + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PLAY_SINK_AUDIO_CONVERT, GstPlaySinkAudioConvert))
848 +#define GST_PLAY_SINK_AUDIO_CONVERT_CAST(obj) \
849 + ((GstPlaySinkAudioConvert *) obj)
850 +#define GST_PLAY_SINK_AUDIO_CONVERT_CLASS(klass) \
851 + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PLAY_SINK_AUDIO_CONVERT, GstPlaySinkAudioConvertClass))
852 +#define GST_IS_PLAY_SINK_AUDIO_CONVERT(obj) \
853 + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PLAY_SINK_AUDIO_CONVERT))
854 +#define GST_IS_PLAY_SINK_AUDIO_CONVERT_CLASS(klass) \
855 + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLAY_SINK_AUDIO_CONVERT))
857 +#define GST_PLAY_SINK_AUDIO_CONVERT_LOCK(obj) G_STMT_START { \
858 + GST_LOG_OBJECT (obj, \
859 + "locking from thread %p", \
860 + g_thread_self ()); \
861 + g_mutex_lock (GST_PLAY_SINK_AUDIO_CONVERT_CAST(obj)->lock); \
862 + GST_LOG_OBJECT (obj, \
863 + "locked from thread %p", \
864 + g_thread_self ()); \
867 +#define GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK(obj) G_STMT_START { \
868 + GST_LOG_OBJECT (obj, \
869 + "unlocking from thread %p", \
870 + g_thread_self ()); \
871 + g_mutex_unlock (GST_PLAY_SINK_AUDIO_CONVERT_CAST(obj)->lock); \
874 +typedef struct _GstPlaySinkAudioConvert GstPlaySinkAudioConvert;
875 +typedef struct _GstPlaySinkAudioConvertClass GstPlaySinkAudioConvertClass;
877 +struct _GstPlaySinkAudioConvert
884 + GstPad *sinkpad, *sink_proxypad;
885 + GstPadEventFunction sink_event;
886 + GstPadSetCapsFunction sink_setcaps;
887 + gboolean sink_proxypad_blocked;
888 + GstSegment segment;
893 + GstElement *conv, *resample;
895 + /* < pseudo public > */
896 + GstElement *volume;
897 + gboolean use_volume;
898 + gboolean use_converters;
901 +struct _GstPlaySinkAudioConvertClass
903 + GstBinClass parent;
906 +GType gst_play_sink_audio_convert_get_type (void);
909 +#endif /* __GST_PLAY_SINK_AUDIO_CONVERT_H__ */
910 diff --git a/gst/playback/gstplaysinkvideoconvert.c b/gst/playback/gstplaysinkvideoconvert.c
912 index 0000000..c544462
914 +++ b/gst/playback/gstplaysinkvideoconvert.c
917 + * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
919 + * This library is free software; you can redistribute it and/or
920 + * modify it under the terms of the GNU Library General Public
921 + * License as published by the Free Software Foundation; either
922 + * version 2 of the License, or (at your option) any later version.
924 + * This library is distributed in the hope that it will be useful,
925 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
926 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
927 + * Library General Public License for more details.
929 + * You should have received a copy of the GNU Library General Public
930 + * License along with this library; if not, write to the
931 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
932 + * Boston, MA 02111-1307, USA.
935 +#ifdef HAVE_CONFIG_H
939 +#include "gstplaysinkvideoconvert.h"
941 +#include <gst/pbutils/pbutils.h>
942 +#include <gst/gst-i18n-plugin.h>
944 +GST_DEBUG_CATEGORY_STATIC (gst_play_sink_video_convert_debug);
945 +#define GST_CAT_DEFAULT gst_play_sink_video_convert_debug
947 +#define parent_class gst_play_sink_video_convert_parent_class
949 +G_DEFINE_TYPE (GstPlaySinkVideoConvert, gst_play_sink_video_convert,
952 +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
955 + GST_STATIC_CAPS_ANY);
957 +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
960 + GST_STATIC_CAPS_ANY);
963 +is_raw_caps (GstCaps * caps)
969 + n = gst_caps_get_size (caps);
970 + for (i = 0; i < n; i++) {
971 + s = gst_caps_get_structure (caps, i);
972 + name = gst_structure_get_name (s);
973 + if (!g_str_has_prefix (name, "video/x-raw"))
981 +post_missing_element_message (GstPlaySinkVideoConvert * self,
982 + const gchar * name)
986 + msg = gst_missing_element_message_new (GST_ELEMENT_CAST (self), name);
987 + gst_element_post_message (GST_ELEMENT_CAST (self), msg);
991 +distribute_running_time (GstElement * element, const GstSegment * segment)
996 + pad = gst_element_get_static_pad (element, "sink");
998 + if (segment->accum) {
999 + event = gst_event_new_new_segment_full (FALSE, segment->rate,
1000 + segment->applied_rate, segment->format, 0, segment->accum, 0);
1001 + gst_pad_push_event (pad, event);
1004 + event = gst_event_new_new_segment_full (FALSE, segment->rate,
1005 + segment->applied_rate, segment->format,
1006 + segment->start, segment->stop, segment->time);
1007 + gst_pad_push_event (pad, event);
1009 + gst_object_unref (pad);
1013 +pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkVideoConvert * self)
1019 + GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
1020 + self->sink_proxypad_blocked = blocked;
1021 + GST_DEBUG_OBJECT (self, "Pad blocked: %d", blocked);
1025 + /* There must be a peer at this point */
1026 + peer = gst_pad_get_peer (self->sinkpad);
1027 + caps = gst_pad_get_negotiated_caps (peer);
1029 + caps = gst_pad_get_caps_reffed (peer);
1030 + gst_object_unref (peer);
1032 + raw = is_raw_caps (caps);
1033 + GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw);
1034 + gst_caps_unref (caps);
1036 + if (raw == self->raw)
1041 + GstBin *bin = GST_BIN_CAST (self);
1042 + GstElement *head = NULL, *prev = NULL;
1045 + GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline");
1047 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
1048 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
1050 + self->conv = gst_element_factory_make ("ffmpegcolorspace", "conv");
1051 + if (self->conv == NULL) {
1052 + post_missing_element_message (self, "ffmpegcolorspace");
1053 + GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
1054 + (_("Missing element '%s' - check your GStreamer installation."),
1055 + "ffmpegcolorspace"), ("video rendering might fail"));
1057 + gst_bin_add (bin, self->conv);
1058 + gst_element_sync_state_with_parent (self->conv);
1059 + distribute_running_time (self->conv, &self->segment);
1060 + prev = head = self->conv;
1063 + self->scale = gst_element_factory_make ("videoscale", "scale");
1064 + if (self->scale == NULL) {
1065 + post_missing_element_message (self, "videoscale");
1066 + GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
1067 + (_("Missing element '%s' - check your GStreamer installation."),
1068 + "videoscale"), ("possibly a liboil version mismatch?"));
1070 + /* Add black borders if necessary to keep the DAR */
1071 + g_object_set (self->scale, "add-borders", TRUE, NULL);
1072 + gst_bin_add (bin, self->scale);
1073 + gst_element_sync_state_with_parent (self->scale);
1074 + distribute_running_time (self->scale, &self->segment);
1076 + if (!gst_element_link_pads_full (prev, "src", self->scale, "sink",
1077 + GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1080 + head = self->scale;
1082 + prev = self->scale;
1086 + pad = gst_element_get_static_pad (head, "sink");
1087 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad);
1088 + gst_object_unref (pad);
1092 + pad = gst_element_get_static_pad (prev, "src");
1093 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad);
1094 + gst_object_unref (pad);
1097 + if (!head && !prev) {
1098 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
1099 + self->sink_proxypad);
1102 + GST_DEBUG_OBJECT (self, "Raw conversion pipeline created");
1104 + GstBin *bin = GST_BIN_CAST (self);
1106 + GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline");
1108 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
1109 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
1112 + gst_element_set_state (self->conv, GST_STATE_NULL);
1113 + gst_bin_remove (bin, self->conv);
1114 + self->conv = NULL;
1116 + if (self->scale) {
1117 + gst_element_set_state (self->scale, GST_STATE_NULL);
1118 + gst_bin_remove (bin, self->scale);
1119 + self->scale = NULL;
1122 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
1123 + self->sink_proxypad);
1125 + GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed");
1129 + gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
1130 + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
1131 + (GDestroyNotify) gst_object_unref);
1134 + GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
1139 + GST_ELEMENT_ERROR (self, CORE, PAD,
1140 + (NULL), ("Failed to configure the video converter."));
1141 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
1142 + self->sink_proxypad);
1143 + gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
1144 + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
1145 + (GDestroyNotify) gst_object_unref);
1151 +gst_play_sink_video_convert_sink_event (GstPad * pad, GstEvent * event)
1153 + GstPlaySinkVideoConvert *self =
1154 + GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
1157 + ret = self->sink_event (pad, gst_event_ref (event));
1159 + if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
1161 + gdouble rate, applied_rate;
1163 + gint64 start, stop, position;
1165 + GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
1166 + gst_event_parse_new_segment_full (event, &update, &rate, &applied_rate,
1167 + &format, &start, &stop, &position);
1169 + GST_DEBUG_OBJECT (self, "Segment before %" GST_SEGMENT_FORMAT,
1171 + gst_segment_set_newsegment_full (&self->segment, update, rate, applied_rate,
1172 + format, start, stop, position);
1173 + GST_DEBUG_OBJECT (self, "Segment after %" GST_SEGMENT_FORMAT,
1175 + GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
1176 + } else if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
1177 + GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
1178 + GST_DEBUG_OBJECT (self, "Resetting segment");
1179 + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
1180 + GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
1183 + gst_event_unref (event);
1184 + gst_object_unref (self);
1190 +gst_play_sink_video_convert_sink_setcaps (GstPad * pad, GstCaps * caps)
1192 + GstPlaySinkVideoConvert *self =
1193 + GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
1196 + const gchar *name;
1197 + gboolean reconfigure = FALSE;
1199 + GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
1200 + s = gst_caps_get_structure (caps, 0);
1201 + name = gst_structure_get_name (s);
1203 + if (g_str_has_prefix (name, "video/x-raw-")) {
1204 + if (!self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
1205 + GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw");
1206 + reconfigure = TRUE;
1207 + gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
1208 + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
1209 + (GDestroyNotify) gst_object_unref);
1212 + if (self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
1213 + GST_DEBUG_OBJECT (self, "Changing caps from raw to non-raw");
1214 + reconfigure = TRUE;
1215 + gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
1216 + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
1217 + (GDestroyNotify) gst_object_unref);
1221 + /* Otherwise the setcaps below fails */
1222 + if (reconfigure) {
1223 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
1224 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
1226 + GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
1228 + ret = self->sink_setcaps (pad, caps);
1230 + GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT ": %d", caps,
1233 + gst_object_unref (self);
1239 +gst_play_sink_video_convert_getcaps (GstPad * pad)
1241 + GstPlaySinkVideoConvert *self =
1242 + GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
1244 + GstPad *otherpad, *peer;
1246 + GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
1247 + if (pad == self->srcpad)
1248 + otherpad = gst_object_ref (self->sinkpad);
1250 + otherpad = gst_object_ref (self->srcpad);
1251 + GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
1253 + peer = gst_pad_get_peer (otherpad);
1255 + ret = gst_pad_get_caps_reffed (peer);
1256 + gst_object_unref (peer);
1258 + ret = gst_caps_new_any ();
1261 + gst_object_unref (otherpad);
1262 + gst_object_unref (self);
1268 +gst_play_sink_video_convert_finalize (GObject * object)
1270 + GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (object);
1272 + gst_object_unref (self->sink_proxypad);
1273 + g_mutex_free (self->lock);
1275 + G_OBJECT_CLASS (parent_class)->finalize (object);
1278 +static GstStateChangeReturn
1279 +gst_play_sink_video_convert_change_state (GstElement * element,
1280 + GstStateChange transition)
1282 + GstStateChangeReturn ret;
1283 + GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (element);
1285 + switch (transition) {
1286 + case GST_STATE_CHANGE_PAUSED_TO_READY:
1287 + GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
1288 + if (gst_pad_is_blocked (self->sink_proxypad))
1289 + gst_pad_set_blocked_async_full (self->sink_proxypad, FALSE,
1290 + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
1291 + (GDestroyNotify) gst_object_unref);
1292 + GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
1298 + ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1299 + if (ret == GST_STATE_CHANGE_FAILURE)
1302 + switch (transition) {
1303 + case GST_STATE_CHANGE_PAUSED_TO_READY:
1304 + GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
1305 + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
1307 + gst_element_set_state (self->conv, GST_STATE_NULL);
1308 + gst_bin_remove (GST_BIN_CAST (self), self->conv);
1309 + self->conv = NULL;
1311 + if (self->scale) {
1312 + gst_element_set_state (self->scale, GST_STATE_NULL);
1313 + gst_bin_remove (GST_BIN_CAST (self), self->scale);
1314 + self->scale = NULL;
1316 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
1317 + self->sink_proxypad);
1318 + self->raw = FALSE;
1319 + GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
1321 + case GST_STATE_CHANGE_READY_TO_PAUSED:
1322 + GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
1323 + if (!gst_pad_is_blocked (self->sink_proxypad))
1324 + gst_pad_set_blocked_async_full (self->sink_proxypad, TRUE,
1325 + (GstPadBlockCallback) pad_blocked_cb, gst_object_ref (self),
1326 + (GDestroyNotify) gst_object_unref);
1327 + GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
1336 +gst_play_sink_video_convert_class_init (GstPlaySinkVideoConvertClass * klass)
1338 + GObjectClass *gobject_class;
1339 + GstElementClass *gstelement_class;
1341 + GST_DEBUG_CATEGORY_INIT (gst_play_sink_video_convert_debug,
1342 + "playsinkvideoconvert", 0, "play bin");
1344 + gobject_class = (GObjectClass *) klass;
1345 + gstelement_class = (GstElementClass *) klass;
1347 + gobject_class->finalize = gst_play_sink_video_convert_finalize;
1349 + gst_element_class_add_pad_template (gstelement_class,
1350 + gst_static_pad_template_get (&srctemplate));
1351 + gst_element_class_add_pad_template (gstelement_class,
1352 + gst_static_pad_template_get (&sinktemplate));
1353 + gst_element_class_set_details_simple (gstelement_class,
1354 + "Player Sink Video Converter", "Video/Bin/Converter",
1355 + "Convenience bin for video conversion",
1356 + "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
1358 + gstelement_class->change_state =
1359 + GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_change_state);
1363 +gst_play_sink_video_convert_init (GstPlaySinkVideoConvert * self)
1365 + GstPadTemplate *templ;
1368 + self->lock = g_mutex_new ();
1369 + gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
1371 + templ = gst_static_pad_template_get (&sinktemplate);
1372 + self->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", templ);
1373 + self->sink_event = GST_PAD_EVENTFUNC (self->sinkpad);
1374 + gst_pad_set_event_function (self->sinkpad,
1375 + GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_sink_event));
1376 + self->sink_setcaps = GST_PAD_SETCAPSFUNC (self->sinkpad);
1377 + gst_pad_set_setcaps_function (self->sinkpad,
1378 + GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_sink_setcaps));
1379 + gst_pad_set_getcaps_function (self->sinkpad,
1380 + GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_getcaps));
1382 + it = gst_pad_iterate_internal_links (self->sinkpad);
1384 + gst_iterator_next (it, (gpointer *) & self->sink_proxypad);
1385 + g_assert (self->sink_proxypad);
1386 + gst_iterator_free (it);
1388 + gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad);
1389 + gst_object_unref (templ);
1391 + templ = gst_static_pad_template_get (&srctemplate);
1392 + self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ);
1393 + gst_pad_set_getcaps_function (self->srcpad,
1394 + GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_getcaps));
1395 + gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
1396 + gst_object_unref (templ);
1398 + gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
1399 + self->sink_proxypad);
1401 diff --git a/gst/playback/gstplaysinkvideoconvert.h b/gst/playback/gstplaysinkvideoconvert.h
1402 new file mode 100644
1403 index 0000000..be15dd2
1405 +++ b/gst/playback/gstplaysinkvideoconvert.h
1408 + * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
1410 + * This library is free software; you can redistribute it and/or
1411 + * modify it under the terms of the GNU Library General Public
1412 + * License as published by the Free Software Foundation; either
1413 + * version 2 of the License, or (at your option) any later version.
1415 + * This library is distributed in the hope that it will be useful,
1416 + * but WITHOUT ANY WARRANTY; without even the implied warranty of
1417 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1418 + * Library General Public License for more details.
1420 + * You should have received a copy of the GNU Library General Public
1421 + * License along with this library; if not, write to the
1422 + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
1423 + * Boston, MA 02111-1307, USA.
1426 +#include <gst/gst.h>
1428 +#ifndef __GST_PLAY_SINK_VIDEO_CONVERT_H__
1429 +#define __GST_PLAY_SINK_VIDEO_CONVERT_H__
1432 +#define GST_TYPE_PLAY_SINK_VIDEO_CONVERT \
1433 + (gst_play_sink_video_convert_get_type())
1434 +#define GST_PLAY_SINK_VIDEO_CONVERT(obj) \
1435 + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_PLAY_SINK_VIDEO_CONVERT, GstPlaySinkVideoConvert))
1436 +#define GST_PLAY_SINK_VIDEO_CONVERT_CAST(obj) \
1437 + ((GstPlaySinkVideoConvert *) obj)
1438 +#define GST_PLAY_SINK_VIDEO_CONVERT_CLASS(klass) \
1439 + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_PLAY_SINK_VIDEO_CONVERT, GstPlaySinkVideoConvertClass))
1440 +#define GST_IS_PLAY_SINK_VIDEO_CONVERT(obj) \
1441 + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_PLAY_SINK_VIDEO_CONVERT))
1442 +#define GST_IS_PLAY_SINK_VIDEO_CONVERT_CLASS(klass) \
1443 + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_PLAY_SINK_VIDEO_CONVERT))
1445 +#define GST_PLAY_SINK_VIDEO_CONVERT_LOCK(obj) G_STMT_START { \
1446 + GST_LOG_OBJECT (obj, \
1447 + "locking from thread %p", \
1448 + g_thread_self ()); \
1449 + g_mutex_lock (GST_PLAY_SINK_VIDEO_CONVERT_CAST(obj)->lock); \
1450 + GST_LOG_OBJECT (obj, \
1451 + "locked from thread %p", \
1452 + g_thread_self ()); \
1455 +#define GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK(obj) G_STMT_START { \
1456 + GST_LOG_OBJECT (obj, \
1457 + "unlocking from thread %p", \
1458 + g_thread_self ()); \
1459 + g_mutex_unlock (GST_PLAY_SINK_VIDEO_CONVERT_CAST(obj)->lock); \
1462 +typedef struct _GstPlaySinkVideoConvert GstPlaySinkVideoConvert;
1463 +typedef struct _GstPlaySinkVideoConvertClass GstPlaySinkVideoConvertClass;
1465 +struct _GstPlaySinkVideoConvert
1472 + GstPad *sinkpad, *sink_proxypad;
1473 + GstPadEventFunction sink_event;
1474 + GstPadSetCapsFunction sink_setcaps;
1475 + gboolean sink_proxypad_blocked;
1476 + GstSegment segment;
1481 + GstElement *conv, *scale;
1484 +struct _GstPlaySinkVideoConvertClass
1486 + GstBinClass parent;
1489 +GType gst_play_sink_video_convert_get_type (void);
1492 +#endif /* __GST_PLAY_SINK_VIDEO_CONVERT_H__ */