Merge branch 'vuplus-1.6' of 192.168.102.66:/var/ikseong/repo/openembedded into test_0921
[vuplus_openembedded] / recipes / gstreamer / gst-plugins-base / 1e0ddb12aa1c2b13c4bc4a13712ebd7f06a6346e.patch
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
5
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.
10 ---
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 = \
16         gststreaminfo.c \
17         gststreamselector.c \
18         gstsubtitleoverlay.c \
19 +       gstplaysinkvideoconvert.c \
20 +       gstplaysinkaudioconvert.c \
21         gststreamsynchronizer.c
22  
23  nodist_libgstplaybin_la_SOURCES = $(built_sources)
24 @@ -57,6 +59,8 @@ noinst_HEADERS = \
25         gststreamselector.h \
26         gstrawcaps.h \
27         gstsubtitleoverlay.h \
28 +       gstplaysinkvideoconvert.h \
29 +       gstplaysinkaudioconvert.h \
30         gststreamsynchronizer.h
31  
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
37 @@ -31,6 +31,8 @@
38  
39  #include "gstplaysink.h"
40  #include "gststreamsynchronizer.h"
41 +#include "gstplaysinkvideoconvert.h"
42 +#include "gstplaysinkaudioconvert.h"
43  
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
47    GstPad *sinkpad;
48    GstElement *queue;
49    GstElement *conv;
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
55    GstPad *sinkpad;
56    GstElement *queue;
57    GstElement *conv;
58 -  GstElement *scale;
59    GstElement *sink;
60    gboolean async;
61    GstElement *ts_offset;
62 @@ -1278,46 +1278,19 @@ gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
63      head = prev = chain->queue;
64    }
65  
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");
76 +    chain->conv =
77 +        g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv", NULL);
78 +    gst_bin_add (bin, chain->conv);
79 +    if (prev) {
80 +      if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
81 +              GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
82 +        goto link_failed;
83      } else {
84 -      gst_bin_add (bin, chain->conv);
85 -      if (prev) {
86 -        if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
87 -                GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
88 -          goto link_failed;
89 -      } else {
90 -        head = chain->conv;
91 -      }
92 -      prev = chain->conv;
93 -    }
94 -
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?"));
102 -    } else {
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);
106 -      if (prev) {
107 -        if (!gst_element_link_pads_full (prev, "src", chain->scale, "sink",
108 -                GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
109 -          goto link_failed;
110 -      } else {
111 -        head = chain->scale;
112 -      }
113 -      prev = chain->scale;
114 +      head = chain->conv;
115      }
116 +    prev = chain->conv;
117    }
118  
119    if (prev) {
120 @@ -1388,8 +1361,7 @@ setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
121  
122    chain = playsink->videochain;
123  
124 -  if (chain->chain.raw != raw)
125 -    return FALSE;
126 +  chain->chain.raw = raw;
127  
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;
132    }
133  
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?"));
144 +    chain->conv =
145 +        g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv", NULL);
146 +    gst_bin_add (bin, chain->conv);
147 +    if (prev) {
148 +      if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
149 +              GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
150 +        goto link_failed;
151      } else {
152 -      gst_bin_add (bin, chain->conv);
153 -      if (prev) {
154 -        if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
155 -                GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
156 -          goto link_failed;
157 -      } else {
158 -        head = chain->conv;
159 -      }
160 -      prev = chain->conv;
161 +      head = chain->conv;
162      }
163 +    prev = chain->conv;
164  
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?"));
172 -    } else {
173 -      gst_bin_add (bin, chain->resample);
174 -      if (prev) {
175 -        if (!gst_element_link_pads_full (prev, "src", chain->resample, "sink",
176 -                GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
177 -          goto link_failed;
178 -      } else {
179 -        head = chain->resample;
180 -      }
181 -      prev = chain->resample;
182 -    }
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);
187  
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?"));
196 -      } else {
197 +      GstPlaySinkAudioConvert *conv =
198 +          GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
199 +
200 +      if (conv->volume) {
201 +        chain->volume = conv->volume;
202          have_volume = TRUE;
203  
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,
207              NULL);
208          g_object_set (G_OBJECT (chain->mute), "mute", playsink->mute, NULL);
209 -        gst_bin_add (bin, chain->volume);
210 -
211 -        if (prev) {
212 -          if (!gst_element_link_pads_full (prev, "src", chain->volume, "sink",
213 -                  GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
214 -            goto link_failed;
215 -        } else {
216 -          head = chain->volume;
217 -        }
218 -        prev = chain->volume;
219        }
220      }
221    }
222 @@ -1921,8 +1861,7 @@ setup_audio_chain (GstPlaySink * playsink, gboolean raw)
223  
224    chain = playsink->audiochain;
225  
226 -  if (chain->chain.raw != raw)
227 -    return FALSE;
228 +  chain->chain.raw = raw;
229  
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);
235      }
236 +
237 +    GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv)->use_volume = FALSE;
238    } else {
239 +    GstPlaySinkAudioConvert *conv =
240 +        GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
241 +
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");
245 -    if (!raw) {
246 -      GST_LOG_OBJECT (playsink, "non-raw format, can't do soft volume control");
247  
248 -      disconnect_chain (chain, playsink);
249 -      chain->volume = NULL;
250 -      chain->mute = NULL;
251 -    } else {
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);
260 -        return FALSE;
261 -      }
262 +    /* Disconnect signals */
263 +    disconnect_chain (chain, playsink);
264 +
265 +    if (conv->volume) {
266 +      chain->volume = conv->volume;
267 +      chain->mute = chain->volume;
268  
269 -      GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
270 +      g_signal_connect (chain->volume, "notify::volume",
271 +          G_CALLBACK (notify_volume_cb), playsink);
272 +
273 +      g_signal_connect (chain->mute, "notify::mute",
274 +          G_CALLBACK (notify_mute_cb), playsink);
275 +
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);
279      }
280 +
281 +    GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
282    }
283    return TRUE;
284  }
285 diff --git a/gst/playback/gstplaysinkaudioconvert.c b/gst/playback/gstplaysinkaudioconvert.c
286 new file mode 100644
287 index 0000000..43c5272
288 --- a/dev/null
289 +++ b/gst/playback/gstplaysinkaudioconvert.c
290 @@ -0,0 +1,522 @@
291 +/* GStreamer
292 + * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
293 + *
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.
298 + *
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.
303 + *
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.
308 + */
309 +
310 +#ifdef HAVE_CONFIG_H
311 +#include "config.h"
312 +#endif
313 +
314 +#include "gstplaysinkaudioconvert.h"
315 +
316 +#include <gst/pbutils/pbutils.h>
317 +#include <gst/gst-i18n-plugin.h>
318 +
319 +GST_DEBUG_CATEGORY_STATIC (gst_play_sink_audio_convert_debug);
320 +#define GST_CAT_DEFAULT gst_play_sink_audio_convert_debug
321 +
322 +#define parent_class gst_play_sink_audio_convert_parent_class
323 +
324 +G_DEFINE_TYPE (GstPlaySinkAudioConvert, gst_play_sink_audio_convert,
325 +    GST_TYPE_BIN);
326 +
327 +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
328 +    GST_PAD_SRC,
329 +    GST_PAD_ALWAYS,
330 +    GST_STATIC_CAPS_ANY);
331 +
332 +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
333 +    GST_PAD_SINK,
334 +    GST_PAD_ALWAYS,
335 +    GST_STATIC_CAPS_ANY);
336 +
337 +static gboolean
338 +is_raw_caps (GstCaps * caps)
339 +{
340 +  gint i, n;
341 +  GstStructure *s;
342 +  const gchar *name;
343 +
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"))
349 +      return FALSE;
350 +  }
351 +
352 +  return TRUE;
353 +}
354 +
355 +static void
356 +post_missing_element_message (GstPlaySinkAudioConvert * self,
357 +    const gchar * name)
358 +{
359 +  GstMessage *msg;
360 +
361 +  msg = gst_missing_element_message_new (GST_ELEMENT_CAST (self), name);
362 +  gst_element_post_message (GST_ELEMENT_CAST (self), msg);
363 +}
364 +
365 +static void
366 +distribute_running_time (GstElement * element, const GstSegment * segment)
367 +{
368 +  GstEvent *event;
369 +  GstPad *pad;
370 +
371 +  pad = gst_element_get_static_pad (element, "sink");
372 +
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);
377 +  }
378 +
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);
383 +
384 +  gst_object_unref (pad);
385 +}
386 +
387 +static void
388 +pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkAudioConvert * self)
389 +{
390 +  GstPad *peer;
391 +  GstCaps *caps;
392 +  gboolean raw;
393 +
394 +  GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
395 +  self->sink_proxypad_blocked = blocked;
396 +  GST_DEBUG_OBJECT (self, "Pad blocked: %d", blocked);
397 +  if (!blocked)
398 +    goto done;
399 +
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);
403 +  if (!caps)
404 +    caps = gst_pad_get_caps_reffed (peer);
405 +  gst_object_unref (peer);
406 +
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);
410 +
411 +  if (raw == self->raw)
412 +    goto unblock;
413 +  self->raw = raw;
414 +
415 +  if (raw) {
416 +    GstBin *bin = GST_BIN_CAST (self);
417 +    GstElement *head = NULL, *prev = NULL;
418 +    GstPad *pad;
419 +
420 +    GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline");
421 +
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);
424 +
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"));
432 +      } else {
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;
437 +      }
438 +
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?"));
445 +      } else {
446 +        gst_bin_add (bin, self->resample);
447 +        gst_element_sync_state_with_parent (self->resample);
448 +        distribute_running_time (self->resample, &self->segment);
449 +        if (prev) {
450 +          if (!gst_element_link_pads_full (prev, "src", self->resample, "sink",
451 +                  GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
452 +            goto link_failed;
453 +        } else {
454 +          head = self->resample;
455 +        }
456 +        prev = self->resample;
457 +      }
458 +    }
459 +
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);
464 +      if (prev) {
465 +        if (!gst_element_link_pads_full (prev, "src", self->volume, "sink",
466 +                GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
467 +          goto link_failed;
468 +      } else {
469 +        head = self->volume;
470 +      }
471 +      prev = self->volume;
472 +    }
473 +
474 +    if (head) {
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);
478 +    }
479 +
480 +    if (prev) {
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);
484 +    }
485 +
486 +    if (!head && !prev) {
487 +      gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
488 +          self->sink_proxypad);
489 +    }
490 +
491 +    GST_DEBUG_OBJECT (self, "Raw conversion pipeline created");
492 +  } else {
493 +    GstBin *bin = GST_BIN_CAST (self);
494 +
495 +    GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline");
496 +
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);
499 +
500 +    if (self->conv) {
501 +      gst_element_set_state (self->conv, GST_STATE_NULL);
502 +      gst_bin_remove (bin, self->conv);
503 +      self->conv = NULL;
504 +    }
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;
509 +    }
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);
514 +      }
515 +    }
516 +
517 +    gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
518 +        self->sink_proxypad);
519 +
520 +    GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed");
521 +  }
522 +
523 +unblock:
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);
527 +
528 +done:
529 +  GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
530 +  return;
531 +
532 +link_failed:
533 +  {
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);
541 +    return;
542 +  }
543 +}
544 +
545 +static gboolean
546 +gst_play_sink_audio_convert_sink_event (GstPad * pad, GstEvent * event)
547 +{
548 +  GstPlaySinkAudioConvert *self =
549 +      GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad));
550 +  gboolean ret;
551 +
552 +  ret = self->sink_event (pad, gst_event_ref (event));
553 +
554 +  if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
555 +    gboolean update;
556 +    gdouble rate, applied_rate;
557 +    GstFormat format;
558 +    gint64 start, stop, position;
559 +
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);
563 +
564 +    GST_DEBUG_OBJECT (self, "Segment before %" GST_SEGMENT_FORMAT,
565 +        &self->segment);
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,
569 +        &self->segment);
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);
576 +  }
577 +
578 +  gst_event_unref (event);
579 +  gst_object_unref (self);
580 +
581 +  return ret;
582 +}
583 +
584 +static gboolean
585 +gst_play_sink_audio_convert_sink_setcaps (GstPad * pad, GstCaps * caps)
586 +{
587 +  GstPlaySinkAudioConvert *self =
588 +      GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad));
589 +  gboolean ret;
590 +  GstStructure *s;
591 +  const gchar *name;
592 +  gboolean reconfigure = FALSE;
593 +
594 +  GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
595 +  s = gst_caps_get_structure (caps, 0);
596 +  name = gst_structure_get_name (s);
597 +
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);
605 +    }
606 +  } else {
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);
613 +    }
614 +  }
615 +
616 +  /* Otherwise the setcaps below fails */
617 +  if (reconfigure) {
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);
620 +  }
621 +
622 +  GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
623 +  ret = self->sink_setcaps (pad, caps);
624 +
625 +  GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT ": %d", caps,
626 +      ret);
627 +
628 +  gst_object_unref (self);
629 +
630 +  return ret;
631 +}
632 +
633 +static GstCaps *
634 +gst_play_sink_audio_convert_getcaps (GstPad * pad)
635 +{
636 +  GstPlaySinkAudioConvert *self =
637 +      GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad));
638 +  GstCaps *ret;
639 +  GstPad *otherpad, *peer;
640 +
641 +  GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
642 +  if (pad == self->srcpad)
643 +    otherpad = gst_object_ref (self->sinkpad);
644 +  else
645 +    otherpad = gst_object_ref (self->srcpad);
646 +  GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
647 +
648 +  peer = gst_pad_get_peer (otherpad);
649 +  if (peer) {
650 +    ret = gst_pad_get_caps_reffed (peer);
651 +    gst_object_unref (peer);
652 +  } else {
653 +    ret = gst_caps_new_any ();
654 +  }
655 +
656 +  gst_object_unref (otherpad);
657 +  gst_object_unref (self);
658 +
659 +  return ret;
660 +}
661 +
662 +static void
663 +gst_play_sink_audio_convert_finalize (GObject * object)
664 +{
665 +  GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (object);
666 +
667 +  if (self->volume)
668 +    gst_object_unref (self->volume);
669 +
670 +  gst_object_unref (self->sink_proxypad);
671 +  g_mutex_free (self->lock);
672 +
673 +  G_OBJECT_CLASS (parent_class)->finalize (object);
674 +}
675 +
676 +static GstStateChangeReturn
677 +gst_play_sink_audio_convert_change_state (GstElement * element,
678 +    GstStateChange transition)
679 +{
680 +  GstStateChangeReturn ret;
681 +  GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (element);
682 +
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);
691 +      break;
692 +    default:
693 +      break;
694 +  }
695 +
696 +  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
697 +  if (ret == GST_STATE_CHANGE_FAILURE)
698 +    return ret;
699 +
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);
704 +      if (self->conv) {
705 +        gst_element_set_state (self->conv, GST_STATE_NULL);
706 +        gst_bin_remove (GST_BIN_CAST (self), self->conv);
707 +        self->conv = NULL;
708 +      }
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;
713 +      }
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);
718 +        }
719 +      }
720 +      gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
721 +          self->sink_proxypad);
722 +      self->raw = FALSE;
723 +      GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
724 +      break;
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);
732 +    default:
733 +      break;
734 +  }
735 +
736 +  return ret;
737 +}
738 +
739 +static void
740 +gst_play_sink_audio_convert_class_init (GstPlaySinkAudioConvertClass * klass)
741 +{
742 +  GObjectClass *gobject_class;
743 +  GstElementClass *gstelement_class;
744 +
745 +  GST_DEBUG_CATEGORY_INIT (gst_play_sink_audio_convert_debug,
746 +      "playsinkaudioconvert", 0, "play bin");
747 +
748 +  gobject_class = (GObjectClass *) klass;
749 +  gstelement_class = (GstElementClass *) klass;
750 +
751 +  gobject_class->finalize = gst_play_sink_audio_convert_finalize;
752 +
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>");
761 +
762 +  gstelement_class->change_state =
763 +      GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_change_state);
764 +}
765 +
766 +static void
767 +gst_play_sink_audio_convert_init (GstPlaySinkAudioConvert * self)
768 +{
769 +  GstPadTemplate *templ;
770 +  GstIterator *it;
771 +
772 +  self->lock = g_mutex_new ();
773 +  gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
774 +
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));
785 +
786 +  it = gst_pad_iterate_internal_links (self->sinkpad);
787 +  g_assert (it);
788 +  gst_iterator_next (it, (gpointer *) & self->sink_proxypad);
789 +  g_assert (self->sink_proxypad);
790 +  gst_iterator_free (it);
791 +
792 +  gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad);
793 +  gst_object_unref (templ);
794 +
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);
801 +
802 +  gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
803 +      self->sink_proxypad);
804 +
805 +  /* FIXME: Only create this on demand but for now we need
806 +   * it to always exist because of playsink's volume proxying
807 +   * logic.
808 +   */
809 +  self->volume = gst_element_factory_make ("volume", "volume");
810 +  if (self->volume)
811 +    gst_object_ref_sink (self->volume);
812 +}
813 diff --git a/gst/playback/gstplaysinkaudioconvert.h b/gst/playback/gstplaysinkaudioconvert.h
814 new file mode 100644
815 index 0000000..b0016b9
816 --- a/dev/null
817 +++ b/gst/playback/gstplaysinkaudioconvert.h
818 @@ -0,0 +1,91 @@
819 +/* GStreamer
820 + * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
821 + *
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.
826 + *
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.
831 + *
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.
836 + */
837 +
838 +#include <gst/gst.h>
839 +
840 +#ifndef __GST_PLAY_SINK_AUDIO_CONVERT_H__
841 +#define __GST_PLAY_SINK_AUDIO_CONVERT_H__
842 +
843 +G_BEGIN_DECLS
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))
856 +
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 ());                                  \
865 +} G_STMT_END
866 +
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);              \
872 +} G_STMT_END
873 +
874 +typedef struct _GstPlaySinkAudioConvert GstPlaySinkAudioConvert;
875 +typedef struct _GstPlaySinkAudioConvertClass GstPlaySinkAudioConvertClass;
876 +
877 +struct _GstPlaySinkAudioConvert
878 +{
879 +  GstBin parent;
880 +
881 +  /* < private > */
882 +  GMutex *lock;
883 +
884 +  GstPad *sinkpad, *sink_proxypad;
885 +  GstPadEventFunction sink_event;
886 +  GstPadSetCapsFunction sink_setcaps;
887 +  gboolean sink_proxypad_blocked;
888 +  GstSegment segment;
889 +
890 +  GstPad *srcpad;
891 +
892 +  gboolean raw;
893 +  GstElement *conv, *resample;
894 +
895 +  /* < pseudo public > */
896 +  GstElement *volume;
897 +  gboolean use_volume;
898 +  gboolean use_converters;
899 +};
900 +
901 +struct _GstPlaySinkAudioConvertClass
902 +{
903 +  GstBinClass parent;
904 +};
905 +
906 +GType gst_play_sink_audio_convert_get_type (void);
907 +
908 +G_END_DECLS
909 +#endif /* __GST_PLAY_SINK_AUDIO_CONVERT_H__ */
910 diff --git a/gst/playback/gstplaysinkvideoconvert.c b/gst/playback/gstplaysinkvideoconvert.c
911 new file mode 100644
912 index 0000000..c544462
913 --- a/dev/null
914 +++ b/gst/playback/gstplaysinkvideoconvert.c
915 @@ -0,0 +1,485 @@
916 +/* GStreamer
917 + * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
918 + *
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.
923 + *
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.
928 + *
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.
933 + */
934 +
935 +#ifdef HAVE_CONFIG_H
936 +#include "config.h"
937 +#endif
938 +
939 +#include "gstplaysinkvideoconvert.h"
940 +
941 +#include <gst/pbutils/pbutils.h>
942 +#include <gst/gst-i18n-plugin.h>
943 +
944 +GST_DEBUG_CATEGORY_STATIC (gst_play_sink_video_convert_debug);
945 +#define GST_CAT_DEFAULT gst_play_sink_video_convert_debug
946 +
947 +#define parent_class gst_play_sink_video_convert_parent_class
948 +
949 +G_DEFINE_TYPE (GstPlaySinkVideoConvert, gst_play_sink_video_convert,
950 +    GST_TYPE_BIN);
951 +
952 +static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
953 +    GST_PAD_SRC,
954 +    GST_PAD_ALWAYS,
955 +    GST_STATIC_CAPS_ANY);
956 +
957 +static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
958 +    GST_PAD_SINK,
959 +    GST_PAD_ALWAYS,
960 +    GST_STATIC_CAPS_ANY);
961 +
962 +static gboolean
963 +is_raw_caps (GstCaps * caps)
964 +{
965 +  gint i, n;
966 +  GstStructure *s;
967 +  const gchar *name;
968 +
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"))
974 +      return FALSE;
975 +  }
976 +
977 +  return TRUE;
978 +}
979 +
980 +static void
981 +post_missing_element_message (GstPlaySinkVideoConvert * self,
982 +    const gchar * name)
983 +{
984 +  GstMessage *msg;
985 +
986 +  msg = gst_missing_element_message_new (GST_ELEMENT_CAST (self), name);
987 +  gst_element_post_message (GST_ELEMENT_CAST (self), msg);
988 +}
989 +
990 +static void
991 +distribute_running_time (GstElement * element, const GstSegment * segment)
992 +{
993 +  GstEvent *event;
994 +  GstPad *pad;
995 +
996 +  pad = gst_element_get_static_pad (element, "sink");
997 +
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);
1002 +  }
1003 +
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);
1008 +
1009 +  gst_object_unref (pad);
1010 +}
1011 +
1012 +static void
1013 +pad_blocked_cb (GstPad * pad, gboolean blocked, GstPlaySinkVideoConvert * self)
1014 +{
1015 +  GstPad *peer;
1016 +  GstCaps *caps;
1017 +  gboolean raw;
1018 +
1019 +  GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
1020 +  self->sink_proxypad_blocked = blocked;
1021 +  GST_DEBUG_OBJECT (self, "Pad blocked: %d", blocked);
1022 +  if (!blocked)
1023 +    goto done;
1024 +
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);
1028 +  if (!caps)
1029 +    caps = gst_pad_get_caps_reffed (peer);
1030 +  gst_object_unref (peer);
1031 +
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);
1035 +
1036 +  if (raw == self->raw)
1037 +    goto unblock;
1038 +  self->raw = raw;
1039 +
1040 +  if (raw) {
1041 +    GstBin *bin = GST_BIN_CAST (self);
1042 +    GstElement *head = NULL, *prev = NULL;
1043 +    GstPad *pad;
1044 +
1045 +    GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline");
1046 +
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);
1049 +
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"));
1056 +    } else {
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;
1061 +    }
1062 +
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?"));
1069 +    } else {
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);
1075 +      if (prev) {
1076 +        if (!gst_element_link_pads_full (prev, "src", self->scale, "sink",
1077 +                GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1078 +          goto link_failed;
1079 +      } else {
1080 +        head = self->scale;
1081 +      }
1082 +      prev = self->scale;
1083 +    }
1084 +
1085 +    if (head) {
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);
1089 +    }
1090 +
1091 +    if (prev) {
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);
1095 +    }
1096 +
1097 +    if (!head && !prev) {
1098 +      gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
1099 +          self->sink_proxypad);
1100 +    }
1101 +
1102 +    GST_DEBUG_OBJECT (self, "Raw conversion pipeline created");
1103 +  } else {
1104 +    GstBin *bin = GST_BIN_CAST (self);
1105 +
1106 +    GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline");
1107 +
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);
1110 +
1111 +    if (self->conv) {
1112 +      gst_element_set_state (self->conv, GST_STATE_NULL);
1113 +      gst_bin_remove (bin, self->conv);
1114 +      self->conv = NULL;
1115 +    }
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;
1120 +    }
1121 +
1122 +    gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
1123 +        self->sink_proxypad);
1124 +
1125 +    GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed");
1126 +  }
1127 +
1128 +unblock:
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);
1132 +
1133 +done:
1134 +  GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
1135 +  return;
1136 +
1137 +link_failed:
1138 +  {
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);
1146 +    return;
1147 +  }
1148 +}
1149 +
1150 +static gboolean
1151 +gst_play_sink_video_convert_sink_event (GstPad * pad, GstEvent * event)
1152 +{
1153 +  GstPlaySinkVideoConvert *self =
1154 +      GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
1155 +  gboolean ret;
1156 +
1157 +  ret = self->sink_event (pad, gst_event_ref (event));
1158 +
1159 +  if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
1160 +    gboolean update;
1161 +    gdouble rate, applied_rate;
1162 +    GstFormat format;
1163 +    gint64 start, stop, position;
1164 +
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);
1168 +
1169 +    GST_DEBUG_OBJECT (self, "Segment before %" GST_SEGMENT_FORMAT,
1170 +        &self->segment);
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,
1174 +        &self->segment);
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);
1181 +  }
1182 +
1183 +  gst_event_unref (event);
1184 +  gst_object_unref (self);
1185 +
1186 +  return ret;
1187 +}
1188 +
1189 +static gboolean
1190 +gst_play_sink_video_convert_sink_setcaps (GstPad * pad, GstCaps * caps)
1191 +{
1192 +  GstPlaySinkVideoConvert *self =
1193 +      GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
1194 +  gboolean ret;
1195 +  GstStructure *s;
1196 +  const gchar *name;
1197 +  gboolean reconfigure = FALSE;
1198 +
1199 +  GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
1200 +  s = gst_caps_get_structure (caps, 0);
1201 +  name = gst_structure_get_name (s);
1202 +
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);
1210 +    }
1211 +  } else {
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);
1218 +    }
1219 +  }
1220 +
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);
1225 +  }
1226 +  GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
1227 +
1228 +  ret = self->sink_setcaps (pad, caps);
1229 +
1230 +  GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT ": %d", caps,
1231 +      ret);
1232 +
1233 +  gst_object_unref (self);
1234 +
1235 +  return ret;
1236 +}
1237 +
1238 +static GstCaps *
1239 +gst_play_sink_video_convert_getcaps (GstPad * pad)
1240 +{
1241 +  GstPlaySinkVideoConvert *self =
1242 +      GST_PLAY_SINK_VIDEO_CONVERT (gst_pad_get_parent (pad));
1243 +  GstCaps *ret;
1244 +  GstPad *otherpad, *peer;
1245 +
1246 +  GST_PLAY_SINK_VIDEO_CONVERT_LOCK (self);
1247 +  if (pad == self->srcpad)
1248 +    otherpad = gst_object_ref (self->sinkpad);
1249 +  else
1250 +    otherpad = gst_object_ref (self->srcpad);
1251 +  GST_PLAY_SINK_VIDEO_CONVERT_UNLOCK (self);
1252 +
1253 +  peer = gst_pad_get_peer (otherpad);
1254 +  if (peer) {
1255 +    ret = gst_pad_get_caps_reffed (peer);
1256 +    gst_object_unref (peer);
1257 +  } else {
1258 +    ret = gst_caps_new_any ();
1259 +  }
1260 +
1261 +  gst_object_unref (otherpad);
1262 +  gst_object_unref (self);
1263 +
1264 +  return ret;
1265 +}
1266 +
1267 +static void
1268 +gst_play_sink_video_convert_finalize (GObject * object)
1269 +{
1270 +  GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (object);
1271 +
1272 +  gst_object_unref (self->sink_proxypad);
1273 +  g_mutex_free (self->lock);
1274 +
1275 +  G_OBJECT_CLASS (parent_class)->finalize (object);
1276 +}
1277 +
1278 +static GstStateChangeReturn
1279 +gst_play_sink_video_convert_change_state (GstElement * element,
1280 +    GstStateChange transition)
1281 +{
1282 +  GstStateChangeReturn ret;
1283 +  GstPlaySinkVideoConvert *self = GST_PLAY_SINK_VIDEO_CONVERT_CAST (element);
1284 +
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);
1293 +      break;
1294 +    default:
1295 +      break;
1296 +  }
1297 +
1298 +  ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
1299 +  if (ret == GST_STATE_CHANGE_FAILURE)
1300 +    return ret;
1301 +
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);
1306 +      if (self->conv) {
1307 +        gst_element_set_state (self->conv, GST_STATE_NULL);
1308 +        gst_bin_remove (GST_BIN_CAST (self), self->conv);
1309 +        self->conv = NULL;
1310 +      }
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;
1315 +      }
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);
1320 +      break;
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);
1328 +    default:
1329 +      break;
1330 +  }
1331 +
1332 +  return ret;
1333 +}
1334 +
1335 +static void
1336 +gst_play_sink_video_convert_class_init (GstPlaySinkVideoConvertClass * klass)
1337 +{
1338 +  GObjectClass *gobject_class;
1339 +  GstElementClass *gstelement_class;
1340 +
1341 +  GST_DEBUG_CATEGORY_INIT (gst_play_sink_video_convert_debug,
1342 +      "playsinkvideoconvert", 0, "play bin");
1343 +
1344 +  gobject_class = (GObjectClass *) klass;
1345 +  gstelement_class = (GstElementClass *) klass;
1346 +
1347 +  gobject_class->finalize = gst_play_sink_video_convert_finalize;
1348 +
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>");
1357 +
1358 +  gstelement_class->change_state =
1359 +      GST_DEBUG_FUNCPTR (gst_play_sink_video_convert_change_state);
1360 +}
1361 +
1362 +static void
1363 +gst_play_sink_video_convert_init (GstPlaySinkVideoConvert * self)
1364 +{
1365 +  GstPadTemplate *templ;
1366 +  GstIterator *it;
1367 +
1368 +  self->lock = g_mutex_new ();
1369 +  gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
1370 +
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));
1381 +
1382 +  it = gst_pad_iterate_internal_links (self->sinkpad);
1383 +  g_assert (it);
1384 +  gst_iterator_next (it, (gpointer *) & self->sink_proxypad);
1385 +  g_assert (self->sink_proxypad);
1386 +  gst_iterator_free (it);
1387 +
1388 +  gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad);
1389 +  gst_object_unref (templ);
1390 +
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);
1397 +
1398 +  gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
1399 +      self->sink_proxypad);
1400 +}
1401 diff --git a/gst/playback/gstplaysinkvideoconvert.h b/gst/playback/gstplaysinkvideoconvert.h
1402 new file mode 100644
1403 index 0000000..be15dd2
1404 --- a/dev/null
1405 +++ b/gst/playback/gstplaysinkvideoconvert.h
1406 @@ -0,0 +1,86 @@
1407 +/* GStreamer
1408 + * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
1409 + *
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.
1414 + *
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.
1419 + *
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.
1424 + */
1425 +
1426 +#include <gst/gst.h>
1427 +
1428 +#ifndef __GST_PLAY_SINK_VIDEO_CONVERT_H__
1429 +#define __GST_PLAY_SINK_VIDEO_CONVERT_H__
1430 +
1431 +G_BEGIN_DECLS
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))
1444 +
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 ());                                  \
1453 +} G_STMT_END
1454 +
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);              \
1460 +} G_STMT_END
1461 +
1462 +typedef struct _GstPlaySinkVideoConvert GstPlaySinkVideoConvert;
1463 +typedef struct _GstPlaySinkVideoConvertClass GstPlaySinkVideoConvertClass;
1464 +
1465 +struct _GstPlaySinkVideoConvert
1466 +{
1467 +  GstBin parent;
1468 +
1469 +  /* < private > */
1470 +  GMutex *lock;
1471 +
1472 +  GstPad *sinkpad, *sink_proxypad;
1473 +  GstPadEventFunction sink_event;
1474 +  GstPadSetCapsFunction sink_setcaps;
1475 +  gboolean sink_proxypad_blocked;
1476 +  GstSegment segment;
1477 +
1478 +  GstPad *srcpad;
1479 +
1480 +  gboolean raw;
1481 +  GstElement *conv, *scale;
1482 +};
1483 +
1484 +struct _GstPlaySinkVideoConvertClass
1485 +{
1486 +  GstBinClass parent;
1487 +};
1488 +
1489 +GType gst_play_sink_video_convert_get_type (void);
1490 +
1491 +G_END_DECLS
1492 +#endif /* __GST_PLAY_SINK_VIDEO_CONVERT_H__ */
1493 --
1494 cgit v0.9