c409d025c10300e949c06a4012456efa3fdc30cf
[platform/upstream/gstreamer.git] / gst / playback / gstplaysinkaudioconvert.c
1 /* GStreamer
2  * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include "gstplaysinkaudioconvert.h"
25
26 #include <gst/pbutils/pbutils.h>
27 #include <gst/gst-i18n-plugin.h>
28
29 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_audio_convert_debug);
30 #define GST_CAT_DEFAULT gst_play_sink_audio_convert_debug
31
32 #define parent_class gst_play_sink_audio_convert_parent_class
33
34 G_DEFINE_TYPE (GstPlaySinkAudioConvert, gst_play_sink_audio_convert,
35     GST_TYPE_BIN);
36
37 static GstStaticPadTemplate srctemplate = GST_STATIC_PAD_TEMPLATE ("src",
38     GST_PAD_SRC,
39     GST_PAD_ALWAYS,
40     GST_STATIC_CAPS_ANY);
41
42 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
43     GST_PAD_SINK,
44     GST_PAD_ALWAYS,
45     GST_STATIC_CAPS_ANY);
46
47 static gboolean
48 is_raw_caps (GstCaps * caps)
49 {
50   gint i, n;
51   GstStructure *s;
52   const gchar *name;
53
54   n = gst_caps_get_size (caps);
55   for (i = 0; i < n; i++) {
56     s = gst_caps_get_structure (caps, i);
57     name = gst_structure_get_name (s);
58     if (!g_str_has_prefix (name, "audio/x-raw"))
59       return FALSE;
60   }
61
62   return TRUE;
63 }
64
65 static void
66 post_missing_element_message (GstPlaySinkAudioConvert * self,
67     const gchar * name)
68 {
69   GstMessage *msg;
70
71   msg = gst_missing_element_message_new (GST_ELEMENT_CAST (self), name);
72   gst_element_post_message (GST_ELEMENT_CAST (self), msg);
73 }
74
75 static void
76 distribute_running_time (GstElement * element, const GstSegment * segment)
77 {
78   GstEvent *event;
79   GstPad *pad;
80
81   pad = gst_element_get_static_pad (element, "sink");
82
83   if (segment->accum) {
84     event = gst_event_new_new_segment_full (FALSE, segment->rate,
85         segment->applied_rate, segment->format, 0, segment->accum, 0);
86     gst_pad_send_event (pad, event);
87   }
88
89   event = gst_event_new_new_segment_full (FALSE, segment->rate,
90       segment->applied_rate, segment->format,
91       segment->start, segment->stop, segment->time);
92   gst_pad_send_event (pad, event);
93
94   gst_object_unref (pad);
95 }
96
97 static GstProbeReturn
98 pad_blocked_cb (GstPad * pad, GstProbeType type, gpointer type_data,
99     gpointer user_data)
100 {
101   GstPlaySinkAudioConvert *self = user_data;
102   GstPad *peer;
103   GstCaps *caps;
104   gboolean raw;
105
106   GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
107   GST_DEBUG_OBJECT (self, "Pad blocked");
108
109   /* There must be a peer at this point */
110   peer = gst_pad_get_peer (self->sinkpad);
111   caps = gst_pad_get_current_caps (peer);
112   if (!caps)
113     caps = gst_pad_get_caps (peer, NULL);
114   gst_object_unref (peer);
115
116   raw = is_raw_caps (caps);
117   GST_DEBUG_OBJECT (self, "Caps %" GST_PTR_FORMAT " are raw: %d", caps, raw);
118   gst_caps_unref (caps);
119
120   if (raw == self->raw)
121     goto unblock;
122   self->raw = raw;
123
124   if (raw) {
125     GstBin *bin = GST_BIN_CAST (self);
126     GstElement *head = NULL, *prev = NULL;
127     GstPad *pad;
128
129     GST_DEBUG_OBJECT (self, "Creating raw conversion pipeline");
130
131     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
132     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
133
134     if (self->use_converters) {
135       self->conv = gst_element_factory_make ("audioconvert", "conv");
136       if (self->conv == NULL) {
137         post_missing_element_message (self, "audioconvert");
138         GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
139             (_("Missing element '%s' - check your GStreamer installation."),
140                 "audioconvert"), ("audio rendering might fail"));
141       } else {
142         gst_bin_add (bin, self->conv);
143         gst_element_sync_state_with_parent (self->conv);
144         prev = head = self->conv;
145       }
146
147       self->resample = gst_element_factory_make ("audioresample", "resample");
148       if (self->resample == NULL) {
149         post_missing_element_message (self, "audioresample");
150         GST_ELEMENT_WARNING (self, CORE, MISSING_PLUGIN,
151             (_("Missing element '%s' - check your GStreamer installation."),
152                 "audioresample"), ("possibly a liboil version mismatch?"));
153       } else {
154         gst_bin_add (bin, self->resample);
155         gst_element_sync_state_with_parent (self->resample);
156         if (prev) {
157           if (!gst_element_link_pads_full (prev, "src", self->resample, "sink",
158                   GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
159             goto link_failed;
160         } else {
161           head = self->resample;
162         }
163         prev = self->resample;
164       }
165     }
166
167     if (self->use_volume && self->volume) {
168       gst_bin_add (bin, gst_object_ref (self->volume));
169       gst_element_sync_state_with_parent (self->volume);
170       if (prev) {
171         if (!gst_element_link_pads_full (prev, "src", self->volume, "sink",
172                 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
173           goto link_failed;
174       } else {
175         head = self->volume;
176       }
177       prev = self->volume;
178     }
179
180     if (head) {
181       pad = gst_element_get_static_pad (head, "sink");
182       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), pad);
183       gst_object_unref (pad);
184     }
185
186     if (prev) {
187       pad = gst_element_get_static_pad (prev, "src");
188       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), pad);
189       gst_object_unref (pad);
190     }
191
192     if (!head && !prev) {
193       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
194           self->sink_proxypad);
195     }
196
197     GST_DEBUG_OBJECT (self, "Raw conversion pipeline created");
198   } else {
199     GstBin *bin = GST_BIN_CAST (self);
200
201     GST_DEBUG_OBJECT (self, "Removing raw conversion pipeline");
202
203     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
204     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
205
206     if (self->conv) {
207       gst_element_set_state (self->conv, GST_STATE_NULL);
208       gst_bin_remove (bin, self->conv);
209       self->conv = NULL;
210     }
211     if (self->resample) {
212       gst_element_set_state (self->resample, GST_STATE_NULL);
213       gst_bin_remove (bin, self->resample);
214       self->resample = NULL;
215     }
216     if (self->volume) {
217       gst_element_set_state (self->volume, GST_STATE_NULL);
218       if (GST_OBJECT_PARENT (self->volume) == GST_OBJECT_CAST (self)) {
219         gst_bin_remove (GST_BIN_CAST (self), self->volume);
220       }
221     }
222
223     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
224         self->sink_proxypad);
225
226     GST_DEBUG_OBJECT (self, "Raw conversion pipeline removed");
227   }
228
229 unblock:
230   self->sink_proxypad_block_id = 0;
231   GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
232
233   return GST_PROBE_REMOVE;
234
235 link_failed:
236   {
237     GST_ELEMENT_ERROR (self, CORE, PAD,
238         (NULL), ("Failed to configure the audio converter."));
239     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
240         self->sink_proxypad);
241     self->sink_proxypad_block_id = 0;
242     GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
243
244     return GST_PROBE_REMOVE;
245   }
246 }
247
248 static void
249 block_proxypad (GstPlaySinkAudioConvert * self)
250 {
251   if (self->sink_proxypad_block_id == 0) {
252     self->sink_proxypad_block_id =
253         gst_pad_add_probe (self->sink_proxypad, GST_PROBE_TYPE_BLOCK,
254         pad_blocked_cb, gst_object_ref (self),
255         (GDestroyNotify) gst_object_unref);
256   }
257 }
258
259 static void
260 unblock_proxypad (GstPlaySinkAudioConvert * self)
261 {
262   if (self->sink_proxypad_block_id != 0) {
263     gst_pad_remove_probe (self->sink_proxypad, self->sink_proxypad_block_id);
264     self->sink_proxypad_block_id = 0;
265   }
266 }
267
268 static gboolean
269 gst_play_sink_audio_convert_sink_setcaps (GstPlaySinkAudioConvert * self,
270     GstCaps * caps)
271 {
272   GstStructure *s;
273   const gchar *name;
274   gboolean reconfigure = FALSE;
275
276   GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
277   s = gst_caps_get_structure (caps, 0);
278   name = gst_structure_get_name (s);
279
280   if (g_str_has_prefix (name, "audio/x-raw")) {
281     if (!self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
282       GST_DEBUG_OBJECT (self, "Changing caps from non-raw to raw");
283       reconfigure = TRUE;
284       block_proxypad (self);
285     }
286   } else {
287     if (self->raw && !gst_pad_is_blocked (self->sink_proxypad)) {
288       GST_DEBUG_OBJECT (self, "Changing caps from raw to non-raw");
289       reconfigure = TRUE;
290       block_proxypad (self);
291     }
292   }
293
294   /* Otherwise the setcaps below fails */
295   if (reconfigure) {
296     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->sinkpad), NULL);
297     gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad), NULL);
298   }
299
300   GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
301
302   GST_DEBUG_OBJECT (self, "Setting sink caps %" GST_PTR_FORMAT, caps);
303
304   return TRUE;
305 }
306
307 static gboolean
308 gst_play_sink_audio_convert_sink_event (GstPad * pad, GstEvent * event)
309 {
310   GstPlaySinkAudioConvert *self =
311       GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad));
312   gboolean ret;
313
314   switch (GST_EVENT_TYPE (event)) {
315     case GST_EVENT_CAPS:
316     {
317       GstCaps *caps;
318
319       gst_event_parse_caps (event, &caps);
320       ret = gst_play_sink_audio_convert_sink_setcaps (self, caps);
321       break;
322     }
323     default:
324       break;
325   }
326
327   ret = gst_proxy_pad_event_default (pad, gst_event_ref (event));
328
329   switch (GST_EVENT_TYPE (event)) {
330     case GST_EVENT_SEGMENT:
331       GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
332       GST_DEBUG_OBJECT (self, "Segment before %" GST_SEGMENT_FORMAT,
333           &self->segment);
334       gst_event_copy_segment (event, &self->segment);
335       GST_DEBUG_OBJECT (self, "Segment after %" GST_SEGMENT_FORMAT,
336           &self->segment);
337       GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
338       break;
339     case GST_EVENT_FLUSH_STOP:
340       GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
341       GST_DEBUG_OBJECT (self, "Resetting segment");
342       gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
343       GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
344       break;
345     default:
346       break;
347   }
348
349   gst_event_unref (event);
350   gst_object_unref (self);
351
352   return ret;
353 }
354
355 static GstCaps *
356 gst_play_sink_audio_convert_getcaps (GstPad * pad, GstCaps * filter)
357 {
358   GstPlaySinkAudioConvert *self =
359       GST_PLAY_SINK_AUDIO_CONVERT (gst_pad_get_parent (pad));
360   GstCaps *ret;
361   GstPad *otherpad, *peer;
362
363   GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
364   if (pad == self->srcpad)
365     otherpad = gst_object_ref (self->sinkpad);
366   else
367     otherpad = gst_object_ref (self->srcpad);
368   GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
369
370   peer = gst_pad_get_peer (otherpad);
371   if (peer) {
372     ret = gst_pad_get_caps (peer, filter);
373     gst_object_unref (peer);
374   } else {
375     ret = (filter ? gst_caps_ref (filter) : gst_caps_new_any ());
376   }
377
378   gst_object_unref (otherpad);
379   gst_object_unref (self);
380
381   return ret;
382 }
383
384 static void
385 gst_play_sink_audio_convert_finalize (GObject * object)
386 {
387   GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (object);
388
389   if (self->volume)
390     gst_object_unref (self->volume);
391
392   gst_object_unref (self->sink_proxypad);
393   g_mutex_free (self->lock);
394
395   G_OBJECT_CLASS (parent_class)->finalize (object);
396 }
397
398 static GstStateChangeReturn
399 gst_play_sink_audio_convert_change_state (GstElement * element,
400     GstStateChange transition)
401 {
402   GstStateChangeReturn ret;
403   GstPlaySinkAudioConvert *self = GST_PLAY_SINK_AUDIO_CONVERT_CAST (element);
404
405   switch (transition) {
406     case GST_STATE_CHANGE_PAUSED_TO_READY:
407       GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
408       unblock_proxypad (self);
409       GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
410       break;
411     default:
412       break;
413   }
414
415   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
416   if (ret == GST_STATE_CHANGE_FAILURE)
417     return ret;
418
419   switch (transition) {
420     case GST_STATE_CHANGE_PAUSED_TO_READY:
421       GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
422       gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
423       if (self->conv) {
424         gst_element_set_state (self->conv, GST_STATE_NULL);
425         gst_bin_remove (GST_BIN_CAST (self), self->conv);
426         self->conv = NULL;
427       }
428       if (self->resample) {
429         gst_element_set_state (self->resample, GST_STATE_NULL);
430         gst_bin_remove (GST_BIN_CAST (self), self->resample);
431         self->resample = NULL;
432       }
433       if (self->volume) {
434         gst_element_set_state (self->volume, GST_STATE_NULL);
435         if (GST_OBJECT_PARENT (self->volume) == GST_OBJECT_CAST (self)) {
436           gst_bin_remove (GST_BIN_CAST (self), self->volume);
437         }
438       }
439       gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
440           self->sink_proxypad);
441       self->raw = FALSE;
442       GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
443       break;
444     case GST_STATE_CHANGE_READY_TO_PAUSED:
445       GST_PLAY_SINK_AUDIO_CONVERT_LOCK (self);
446       unblock_proxypad (self);
447       GST_PLAY_SINK_AUDIO_CONVERT_UNLOCK (self);
448     default:
449       break;
450   }
451
452   return ret;
453 }
454
455 static void
456 gst_play_sink_audio_convert_class_init (GstPlaySinkAudioConvertClass * klass)
457 {
458   GObjectClass *gobject_class;
459   GstElementClass *gstelement_class;
460
461   GST_DEBUG_CATEGORY_INIT (gst_play_sink_audio_convert_debug,
462       "playsinkaudioconvert", 0, "play bin");
463
464   gobject_class = (GObjectClass *) klass;
465   gstelement_class = (GstElementClass *) klass;
466
467   gobject_class->finalize = gst_play_sink_audio_convert_finalize;
468
469   gst_element_class_add_pad_template (gstelement_class,
470       gst_static_pad_template_get (&srctemplate));
471   gst_element_class_add_pad_template (gstelement_class,
472       gst_static_pad_template_get (&sinktemplate));
473   gst_element_class_set_details_simple (gstelement_class,
474       "Player Sink Audio Converter", "Audio/Bin/Converter",
475       "Convenience bin for audio conversion",
476       "Sebastian Dröge <sebastian.droege@collabora.co.uk>");
477
478   gstelement_class->change_state =
479       GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_change_state);
480 }
481
482 static void
483 gst_play_sink_audio_convert_init (GstPlaySinkAudioConvert * self)
484 {
485   GstPadTemplate *templ;
486
487   self->lock = g_mutex_new ();
488   gst_segment_init (&self->segment, GST_FORMAT_UNDEFINED);
489
490   templ = gst_static_pad_template_get (&sinktemplate);
491   self->sinkpad = gst_ghost_pad_new_no_target_from_template ("sink", templ);
492   gst_pad_set_event_function (self->sinkpad,
493       GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_sink_event));
494   gst_pad_set_getcaps_function (self->sinkpad,
495       GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_getcaps));
496
497   self->sink_proxypad =
498       GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (self->sinkpad)));
499
500   gst_element_add_pad (GST_ELEMENT_CAST (self), self->sinkpad);
501   gst_object_unref (templ);
502
503   templ = gst_static_pad_template_get (&srctemplate);
504   self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ);
505   gst_pad_set_getcaps_function (self->srcpad,
506       GST_DEBUG_FUNCPTR (gst_play_sink_audio_convert_getcaps));
507   gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
508   gst_object_unref (templ);
509
510   gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (self->srcpad),
511       self->sink_proxypad);
512
513   /* FIXME: Only create this on demand but for now we need
514    * it to always exist because of playsink's volume proxying
515    * logic.
516    */
517   self->volume = gst_element_factory_make ("volume", "volume");
518   if (self->volume)
519     gst_object_ref_sink (self->volume);
520 }