1 /* GStreamer
2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Library General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Library General Public License for more details.
14 *
15 * You should have received a copy of the GNU Library General Public
16 * License along with this library; if not, write to the
17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18 * Boston, MA 02110-1301, USA.
19 */
20
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #include <string.h>
26 #include <gst/gst.h>
27
28 #include <gst/gst-i18n-plugin.h>
29 #include <gst/pbutils/pbutils.h>
30 #include <gst/video/video.h>
31 #include <gst/audio/streamvolume.h>
32 #include <gst/video/colorbalance.h>
33 #include <gst/video/videooverlay.h>
34 #include <gst/video/navigation.h>
35
36 #include "gstplaybackelements.h"
37 #include "gstplaysink.h"
38 #include "gststreamsynchronizer.h"
39 #include "gstplaysinkvideoconvert.h"
40 #include "gstplaysinkaudioconvert.h"
41
42 GST_DEBUG_CATEGORY_STATIC (gst_play_sink_debug);
43 #define GST_CAT_DEFAULT gst_play_sink_debug
44
45 #define VOLUME_MAX_DOUBLE 10.0
46
47 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
48 GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_SOFT_COLORBALANCE
49
50 #define GST_PLAY_CHAIN(c) ((GstPlayChain *)(c))
51
52 /* enum types */
53 /**
54 * GstPlaySinkSendEventMode:
55 * @MODE_DEFAULT: default GstBin's send_event handling
56 * @MODE_FIRST: send event only to the first sink that return true
57 *
58 * Send event handling to use
59 */
60 typedef enum
61 {
62 MODE_DEFAULT = 0,
63 MODE_FIRST = 1
64 } GstPlaySinkSendEventMode;
65
66
67 #define GST_TYPE_PLAY_SINK_SEND_EVENT_MODE (gst_play_sink_send_event_mode_get_type ())
68 static GType
gst_play_sink_send_event_mode_get_type(void)69 gst_play_sink_send_event_mode_get_type (void)
70 {
71 static GType gtype = 0;
72
73 if (gtype == 0) {
74 static const GEnumValue values[] = {
75 {MODE_DEFAULT, "Default GstBin's send_event handling (default)",
76 "default"},
77 {MODE_FIRST, "Sends the event to sinks until the first one handles it",
78 "first"},
79 {0, NULL, NULL}
80 };
81
82 gtype = g_enum_register_static ("GstPlaySinkSendEventMode", values);
83 }
84 return gtype;
85 }
86
87 /* holds the common data fields for the audio and video pipelines. We keep them
88 * in a structure to more easily have all the info available. */
89 typedef struct
90 {
91 GstPlaySink *playsink;
92 GstElement *bin;
93 gboolean added;
94 gboolean activated;
95 gboolean raw;
96 } GstPlayChain;
97
98 typedef struct
99 {
100 GstPlayChain chain;
101 GstPad *sinkpad;
102 GstElement *queue;
103 GstElement *filter_conv;
104 GstElement *filter;
105 GstElement *conv;
106 GstElement *volume; /* element with the volume property */
107 gboolean sink_volume; /* if the volume was provided by the sink */
108 gulong notify_volume_id;
109 gulong notify_mute_id;
110 GstElement *sink;
111 GstElement *ts_offset;
112 } GstPlayAudioChain;
113
114 typedef struct
115 {
116 GstPlayChain chain;
117 GstPad *sinkpad, *srcpad;
118 GstElement *conv;
119 GstElement *deinterlace;
120 } GstPlayVideoDeinterlaceChain;
121
122 typedef struct
123 {
124 GstPlayChain chain;
125 GstPad *sinkpad;
126 GstElement *queue;
127 GstElement *filter_conv;
128 GstElement *filter;
129 GstElement *conv;
130 GstElement *sink;
131 gboolean async;
132 GstElement *ts_offset;
133 } GstPlayVideoChain;
134
135 typedef struct
136 {
137 GstPlayChain chain;
138 GstPad *sinkpad;
139 GstElement *queue;
140 GstElement *conv;
141 GstElement *resample;
142 GstPad *blockpad; /* srcpad of queue, used for blocking the vis */
143 GstPad *vispeerpad; /* srcpad of resample, used for unlinking the vis */
144 GstPad *vissinkpad; /* visualisation sinkpad, */
145 GstElement *vis;
146 GstPad *vissrcpad; /* visualisation srcpad, */
147 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
148 * chain */
149 } GstPlayVisChain;
150
151 typedef struct
152 {
153 GstPlayChain chain;
154 GstPad *sinkpad;
155 GstElement *queue;
156 GstElement *identity;
157 GstElement *overlay;
158 GstPad *videosinkpad;
159 GstPad *textsinkpad;
160 GstPad *srcpad; /* outgoing srcpad, used to connect to the next
161 * chain */
162 GstElement *sink; /* custom sink to receive subtitle buffers */
163 } GstPlayTextChain;
164
165 #define GST_PLAY_SINK_GET_LOCK(playsink) (&((GstPlaySink *)playsink)->lock)
166 #define GST_PLAY_SINK_LOCK(playsink) G_STMT_START { \
167 GST_LOG_OBJECT (playsink, "locking from thread %p", g_thread_self ()); \
168 g_rec_mutex_lock (GST_PLAY_SINK_GET_LOCK (playsink)); \
169 GST_LOG_OBJECT (playsink, "locked from thread %p", g_thread_self ()); \
170 } G_STMT_END
171 #define GST_PLAY_SINK_UNLOCK(playsink) G_STMT_START { \
172 GST_LOG_OBJECT (playsink, "unlocking from thread %p", g_thread_self ()); \
173 g_rec_mutex_unlock (GST_PLAY_SINK_GET_LOCK (playsink)); \
174 } G_STMT_END
175
176 #define PENDING_FLAG_SET(playsink, flagtype) \
177 ((playsink->pending_blocked_pads) |= (1 << flagtype))
178 #define PENDING_FLAG_UNSET(playsink, flagtype) \
179 ((playsink->pending_blocked_pads) &= ~(1 << flagtype))
180 #define PENDING_FLAG_IS_SET(playsink, flagtype) \
181 ((playsink->pending_blocked_pads) & (1 << flagtype))
182 #define PENDING_VIDEO_BLOCK(playsink) \
183 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_VIDEO))
184 #define PENDING_AUDIO_BLOCK(playsink) \
185 ((playsink->pending_blocked_pads) & (1 << GST_PLAY_SINK_TYPE_AUDIO))
186 #define PENDING_TEXT_BLOCK(playsink) \
187 PENDING_FLAG_IS_SET(playsink, GST_PLAY_SINK_TYPE_TEXT)
188
189 struct _GstPlaySink
190 {
191 GstBin bin;
192
193 GRecMutex lock;
194
195 gboolean async_pending;
196 gboolean need_async_start;
197 gboolean reconfigure_pending;
198
199 GstPlayFlags flags;
200
201 GstStreamSynchronizer *stream_synchronizer;
202
203 /* chains */
204 GstPlayAudioChain *audiochain;
205 GstPlayVideoDeinterlaceChain *videodeinterlacechain;
206 GstPlayVideoChain *videochain;
207 GstPlayVisChain *vischain;
208 GstPlayTextChain *textchain;
209
210 /* audio */
211 GstPad *audio_pad;
212 gboolean audio_pad_raw;
213 gboolean audio_pad_blocked;
214 GstPad *audio_srcpad_stream_synchronizer;
215 GstPad *audio_sinkpad_stream_synchronizer;
216 GstElement *audio_ssync_queue;
217 GstPad *audio_ssync_queue_sinkpad;
218 gulong audio_block_id;
219 gulong audio_notify_caps_id;
220 /* audio tee */
221 GstElement *audio_tee;
222 GstPad *audio_tee_sink;
223 GstPad *audio_tee_asrc;
224 GstPad *audio_tee_vissrc;
225 /* video */
226 GstPad *video_pad;
227 gboolean video_pad_raw;
228 gboolean video_pad_blocked;
229 GstPad *video_srcpad_stream_synchronizer;
230 GstPad *video_sinkpad_stream_synchronizer;
231 gulong video_block_id;
232 gulong video_notify_caps_id;
233 /* text */
234 GstPad *text_pad;
235 gboolean text_pad_blocked;
236 GstPad *text_srcpad_stream_synchronizer;
237 GstPad *text_sinkpad_stream_synchronizer;
238 gulong text_block_id;
239
240 gulong vis_pad_block_id;
241
242 guint32 pending_blocked_pads;
243
244 /* properties */
245 GstElement *audio_sink;
246 GstElement *video_sink;
247 GstElement *audio_filter;
248 GstElement *video_filter;
249 GstElement *visualisation;
250 GstElement *text_sink;
251 gdouble volume;
252 gboolean mute;
253 gchar *font_desc; /* font description */
254 gchar *subtitle_encoding; /* subtitle encoding */
255 guint connection_speed; /* connection speed in bits/sec (0 = unknown) */
256 guint count;
257 gboolean volume_changed; /* volume/mute changed while no audiochain */
258 gboolean mute_changed; /* ... has been created yet */
259 gint64 av_offset;
260 gint64 text_offset;
261 GstPlaySinkSendEventMode send_event_mode;
262 gboolean force_aspect_ratio;
263
264 /* videooverlay proxy interface */
265 GstVideoOverlay *overlay_element; /* protected with LOCK */
266 gboolean overlay_handle_set;
267 guintptr overlay_handle;
268 gboolean overlay_render_rectangle_set;
269 gint overlay_x, overlay_y, overlay_width, overlay_height;
270 gboolean overlay_handle_events_set;
271 gboolean overlay_handle_events;
272
273 /* colorbalance proxy interface */
274 GstColorBalance *colorbalance_element;
275 GList *colorbalance_channels; /* CONTRAST, BRIGHTNESS, HUE, SATURATION */
276 gint colorbalance_values[4];
277 gulong colorbalance_value_changed_id;
278
279 /* sending audio/video flushes break stream changes when the pipeline
280 * is paused and played again in 0.10 */
281 #if 0
282 gboolean video_custom_flush_finished;
283 gboolean video_ignore_wrong_state;
284 gboolean video_pending_flush;
285
286 gboolean audio_custom_flush_finished;
287 gboolean audio_ignore_wrong_state;
288 gboolean audio_pending_flush;
289 #endif
290
291 gboolean text_custom_flush_finished;
292 gboolean text_ignore_wrong_state;
293 gboolean text_pending_flush;
294 };
295
296 struct _GstPlaySinkClass
297 {
298 GstBinClass parent_class;
299
300 gboolean (*reconfigure) (GstPlaySink * playsink);
301
302 GstSample *(*convert_sample) (GstPlaySink * playsink, GstCaps * caps);
303 };
304
305
306 static GstStaticPadTemplate audiotemplate =
307 GST_STATIC_PAD_TEMPLATE ("audio_sink",
308 GST_PAD_SINK,
309 GST_PAD_REQUEST,
310 GST_STATIC_CAPS_ANY);
311 static GstStaticPadTemplate videotemplate =
312 GST_STATIC_PAD_TEMPLATE ("video_sink",
313 GST_PAD_SINK,
314 GST_PAD_REQUEST,
315 GST_STATIC_CAPS_ANY);
316 static GstStaticPadTemplate texttemplate = GST_STATIC_PAD_TEMPLATE ("text_sink",
317 GST_PAD_SINK,
318 GST_PAD_REQUEST,
319 GST_STATIC_CAPS_ANY);
320
321 /* FIXME 0.11: Remove */
322 static GstStaticPadTemplate audiorawtemplate =
323 GST_STATIC_PAD_TEMPLATE ("audio_raw_sink",
324 GST_PAD_SINK,
325 GST_PAD_REQUEST,
326 GST_STATIC_CAPS_ANY);
327 static GstStaticPadTemplate videorawtemplate =
328 GST_STATIC_PAD_TEMPLATE ("video_raw_sink",
329 GST_PAD_SINK,
330 GST_PAD_REQUEST,
331 GST_STATIC_CAPS_ANY);
332
333
334 /* props */
335 enum
336 {
337 PROP_0,
338 PROP_FLAGS,
339 PROP_MUTE,
340 PROP_VOLUME,
341 PROP_FONT_DESC,
342 PROP_SUBTITLE_ENCODING,
343 PROP_VIS_PLUGIN,
344 PROP_SAMPLE,
345 PROP_AV_OFFSET,
346 PROP_TEXT_OFFSET,
347 PROP_VIDEO_SINK,
348 PROP_AUDIO_SINK,
349 PROP_TEXT_SINK,
350 PROP_SEND_EVENT_MODE,
351 PROP_FORCE_ASPECT_RATIO,
352 PROP_VIDEO_FILTER,
353 PROP_AUDIO_FILTER
354 };
355
356 /* signals */
357 enum
358 {
359 LAST_SIGNAL
360 };
361
362 static void gst_play_sink_dispose (GObject * object);
363 static void gst_play_sink_finalize (GObject * object);
364 static void gst_play_sink_set_property (GObject * object, guint prop_id,
365 const GValue * value, GParamSpec * spec);
366 static void gst_play_sink_get_property (GObject * object, guint prop_id,
367 GValue * value, GParamSpec * spec);
368
369 static GstPad *gst_play_sink_request_new_pad (GstElement * element,
370 GstPadTemplate * templ, const gchar * name, const GstCaps * caps);
371 static void gst_play_sink_release_request_pad (GstElement * element,
372 GstPad * pad);
373 static gboolean gst_play_sink_send_event (GstElement * element,
374 GstEvent * event);
375 static GstStateChangeReturn gst_play_sink_change_state (GstElement * element,
376 GstStateChange transition);
377
378 static void gst_play_sink_handle_message (GstBin * bin, GstMessage * message);
379
380 /* sending audio/video flushes break stream changes when the pipeline
381 * is paused and played again in 0.10 */
382 #if 0
383 static gboolean gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event);
384 static GstFlowReturn gst_play_sink_video_sink_chain (GstPad * pad,
385 GstBuffer * buffer);
386 static gboolean gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event);
387 static GstFlowReturn gst_play_sink_audio_sink_chain (GstPad * pad,
388 GstBuffer * buffer);
389 #endif
390 static gboolean gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
391 GstEvent * event);
392 static GstFlowReturn gst_play_sink_text_sink_chain (GstPad * pad,
393 GstObject * parent, GstBuffer * buffer);
394
395 static void notify_volume_cb (GObject * object, GParamSpec * pspec,
396 GstPlaySink * playsink);
397 static void notify_mute_cb (GObject * object, GParamSpec * pspec,
398 GstPlaySink * playsink);
399
400 static void update_av_offset (GstPlaySink * playsink);
401 static void update_text_offset (GstPlaySink * playsink);
402
403 static gboolean gst_play_sink_do_reconfigure (GstPlaySink * playsink);
404
405 static GQuark _playsink_reset_segment_event_marker_id = 0;
406
407 /* static guint gst_play_sink_signals[LAST_SIGNAL] = { 0 }; */
408
409 static void gst_play_sink_overlay_init (gpointer g_iface,
410 gpointer g_iface_data);
411 static void gst_play_sink_navigation_init (gpointer g_iface,
412 gpointer g_iface_data);
413 static void gst_play_sink_colorbalance_init (gpointer g_iface,
414 gpointer g_iface_data);
415
416 static gboolean is_raw_pad (GstPad * pad);
417
418 static void
_do_init_type(GType type)419 _do_init_type (GType type)
420 {
421 static const GInterfaceInfo svol_info = {
422 NULL, NULL, NULL
423 };
424 static const GInterfaceInfo ov_info = {
425 gst_play_sink_overlay_init,
426 NULL, NULL
427 };
428 static const GInterfaceInfo nav_info = {
429 gst_play_sink_navigation_init,
430 NULL, NULL
431 };
432 static const GInterfaceInfo col_info = {
433 gst_play_sink_colorbalance_init,
434 NULL, NULL
435 };
436
437 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
438 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
439 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
440 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
441 }
442
443 G_DEFINE_TYPE_WITH_CODE (GstPlaySink, gst_play_sink, GST_TYPE_BIN,
444 _do_init_type (g_define_type_id));
445 #define _do_init \
446 GST_DEBUG_CATEGORY_INIT (gst_play_sink_debug, "playsink", 0, "play sink");\
447 playback_element_init (plugin);
448 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (playsink, "playsink", GST_RANK_NONE,
449 GST_TYPE_PLAY_SINK, _do_init);
450
451
452 static void
gst_play_sink_class_init(GstPlaySinkClass * klass)453 gst_play_sink_class_init (GstPlaySinkClass * klass)
454 {
455 GObjectClass *gobject_klass;
456 GstElementClass *gstelement_klass;
457 GstBinClass *gstbin_klass;
458
459 gobject_klass = (GObjectClass *) klass;
460 gstelement_klass = (GstElementClass *) klass;
461 gstbin_klass = (GstBinClass *) klass;
462
463 gobject_klass->dispose = gst_play_sink_dispose;
464 gobject_klass->finalize = gst_play_sink_finalize;
465 gobject_klass->set_property = gst_play_sink_set_property;
466 gobject_klass->get_property = gst_play_sink_get_property;
467
468
469 /**
470 * GstPlaySink:flags
471 *
472 * Control the behaviour of playsink.
473 */
474 g_object_class_install_property (gobject_klass, PROP_FLAGS,
475 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
476 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
477 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
478
479 /**
480 * GstPlaySink:volume:
481 *
482 * Get or set the current audio stream volume. 1.0 means 100%,
483 * 0.0 means mute. This uses a linear volume scale.
484 *
485 */
486 g_object_class_install_property (gobject_klass, PROP_VOLUME,
487 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
488 0.0, VOLUME_MAX_DOUBLE, 1.0,
489 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
490 g_object_class_install_property (gobject_klass, PROP_MUTE,
491 g_param_spec_boolean ("mute", "Mute",
492 "Mute the audio channel without changing the volume", FALSE,
493 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
494 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
495 g_param_spec_string ("subtitle-font-desc",
496 "Subtitle font description",
497 "Pango font description of font "
498 "to be used for subtitle rendering", NULL,
499 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
500 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
501 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
502 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
503 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
504 "be checked for an encoding to use. If that is not set either, "
505 "ISO-8859-15 will be assumed.", NULL,
506 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
507 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
508 g_param_spec_object ("vis-plugin", "Vis plugin",
509 "the visualization element to use (NULL = default)",
510 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
511 /**
512 * GstPlaySink:sample:
513 *
514 * Get the currently rendered or prerolled sample in the video sink.
515 * The #GstCaps in the sample will describe the format of the buffer.
516 */
517 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
518 g_param_spec_boxed ("sample", "Sample",
519 "The last sample (NULL = no video available)",
520 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
521 /**
522 * GstPlaySink:av-offset:
523 *
524 * Control the synchronisation offset between the audio and video streams.
525 * Positive values make the audio ahead of the video and negative values make
526 * the audio go behind the video.
527 */
528 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
529 g_param_spec_int64 ("av-offset", "AV Offset",
530 "The synchronisation offset between audio and video in nanoseconds",
531 G_MININT64, G_MAXINT64, 0,
532 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
533
534 /**
535 * GstPlaySink:text-offset:
536 *
537 * Control the synchronisation offset between the text and video streams.
538 * Positive values make the text ahead of the video and negative values make
539 * the text go behind the video.
540 */
541 g_object_class_install_property (gobject_klass, PROP_TEXT_OFFSET,
542 g_param_spec_int64 ("text-offset", "Text Offset",
543 "The synchronisation offset between text and video in nanoseconds",
544 G_MININT64, G_MAXINT64, 0,
545 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
546
547 /**
548 * GstPlaySink:video-filter:
549 *
550 * Set the video filter element/bin to use. Will apply on a best-effort basis
551 * unless GST_PLAY_FLAG_FORCE_FILTERS is set. playsink must be in
552 * %GST_STATE_NULL
553 */
554 g_object_class_install_property (gobject_klass, PROP_VIDEO_FILTER,
555 g_param_spec_object ("video-filter", "Video filter",
556 "the video filter(s) to apply, if possible",
557 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
558 /**
559 * GstPlaySink:audio-filter:
560 *
561 * Set the audio filter element/bin to use. Will apply on a best-effort basis
562 * unless GST_PLAY_FLAG_FORCE_FILTERS is set. playsink must be in
563 * %GST_STATE_NULL
564 */
565 g_object_class_install_property (gobject_klass, PROP_AUDIO_FILTER,
566 g_param_spec_object ("audio-filter", "Audio filter",
567 "the audio filter(s) to apply, if possible",
568 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
569
570 /**
571 * GstPlaySink:video-sink:
572 *
573 * Set the used video sink element. NULL will use the default sink. playsink
574 * must be in %GST_STATE_NULL
575 */
576 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
577 g_param_spec_object ("video-sink", "Video Sink",
578 "the video output element to use (NULL = default sink)",
579 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
580 /**
581 * GstPlaySink:audio-sink:
582 *
583 * Set the used audio sink element. NULL will use the default sink. playsink
584 * must be in %GST_STATE_NULL
585 */
586 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
587 g_param_spec_object ("audio-sink", "Audio Sink",
588 "the audio output element to use (NULL = default sink)",
589 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
590
591 /**
592 * GstPlaySink:text-sink:
593 *
594 * Set the used text sink element. NULL will use the default sink. playsink
595 * must be in %GST_STATE_NULL
596 */
597 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
598 g_param_spec_object ("text-sink", "Text sink",
599 "the text output element to use (NULL = default subtitleoverlay)",
600 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
601
602 /**
603 * GstPlaySink::send-event-mode:
604 *
605 * Sets the handling method used for events received from send_event
606 * function. The default is %MODE_DEFAULT, that uses %GstBin's default
607 * handling (push the event to all internal sinks).
608 */
609 g_object_class_install_property (gobject_klass, PROP_SEND_EVENT_MODE,
610 g_param_spec_enum ("send-event-mode", "Send event mode",
611 "How to send events received in send_event function",
612 GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, MODE_DEFAULT,
613 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
614
615 /**
616 * GstPlaySink::force-aspect-ratio:
617 *
618 * Requests the video sink to enforce the video display aspect ratio.
619 */
620 g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
621 g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
622 "When enabled, scaling will respect original aspect ratio", TRUE,
623 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
624
625 g_signal_new ("reconfigure", G_TYPE_FROM_CLASS (klass),
626 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GstPlaySinkClass,
627 reconfigure), NULL, NULL, NULL, G_TYPE_BOOLEAN, 0, G_TYPE_NONE);
628 /**
629 * GstPlaySink::convert-sample:
630 * @playsink: a #GstPlaySink
631 * @caps: the target format of the sample
632 *
633 * Action signal to retrieve the currently playing video sample in the format
634 * specified by @caps.
635 * If @caps is %NULL, no conversion will be performed and this function is
636 * equivalent to the #GstPlaySink:sample property.
637 *
638 * Returns: a #GstSample of the current video sample converted to #caps.
639 * The caps in the sample will describe the final layout of the buffer data.
640 * %NULL is returned when no current sample can be retrieved or when the
641 * conversion failed.
642 */
643 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
644 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
645 G_STRUCT_OFFSET (GstPlaySinkClass, convert_sample), NULL, NULL,
646 NULL, GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
647
648 gst_element_class_add_static_pad_template (gstelement_klass,
649 &audiorawtemplate);
650 gst_element_class_add_static_pad_template (gstelement_klass, &audiotemplate);
651 gst_element_class_add_static_pad_template (gstelement_klass,
652 &videorawtemplate);
653 gst_element_class_add_static_pad_template (gstelement_klass, &videotemplate);
654 gst_element_class_add_static_pad_template (gstelement_klass, &texttemplate);
655 gst_element_class_set_static_metadata (gstelement_klass, "Player Sink",
656 "Generic/Bin/Sink",
657 "Convenience sink for multiple streams",
658 "Wim Taymans <wim.taymans@gmail.com>");
659
660 gstelement_klass->change_state =
661 GST_DEBUG_FUNCPTR (gst_play_sink_change_state);
662 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_sink_send_event);
663 gstelement_klass->request_new_pad =
664 GST_DEBUG_FUNCPTR (gst_play_sink_request_new_pad);
665 gstelement_klass->release_pad =
666 GST_DEBUG_FUNCPTR (gst_play_sink_release_request_pad);
667
668 gstbin_klass->handle_message =
669 GST_DEBUG_FUNCPTR (gst_play_sink_handle_message);
670
671 klass->reconfigure = GST_DEBUG_FUNCPTR (gst_play_sink_reconfigure);
672 klass->convert_sample = GST_DEBUG_FUNCPTR (gst_play_sink_convert_sample);
673
674 _playsink_reset_segment_event_marker_id =
675 g_quark_from_static_string ("gst-playsink-reset-segment-event-marker");
676
677 g_type_class_ref (GST_TYPE_STREAM_SYNCHRONIZER);
678 g_type_class_ref (GST_TYPE_COLOR_BALANCE_CHANNEL);
679
680 gst_type_mark_as_plugin_api (GST_TYPE_PLAY_SINK_SEND_EVENT_MODE, 0);
681 }
682
683 static void
gst_play_sink_init(GstPlaySink * playsink)684 gst_play_sink_init (GstPlaySink * playsink)
685 {
686 GstColorBalanceChannel *channel;
687
688 /* init groups */
689 playsink->video_sink = NULL;
690 playsink->audio_sink = NULL;
691 playsink->visualisation = NULL;
692 playsink->text_sink = NULL;
693 playsink->volume = 1.0;
694 playsink->font_desc = NULL;
695 playsink->subtitle_encoding = NULL;
696 playsink->flags = DEFAULT_FLAGS;
697 playsink->send_event_mode = MODE_DEFAULT;
698 playsink->force_aspect_ratio = TRUE;
699
700 playsink->stream_synchronizer =
701 g_object_new (GST_TYPE_STREAM_SYNCHRONIZER, NULL);
702 gst_bin_add (GST_BIN_CAST (playsink),
703 GST_ELEMENT_CAST (playsink->stream_synchronizer));
704
705 g_rec_mutex_init (&playsink->lock);
706 GST_OBJECT_FLAG_SET (playsink, GST_ELEMENT_FLAG_SINK);
707 gst_bin_set_suppressed_flags (GST_BIN (playsink),
708 GST_ELEMENT_FLAG_SOURCE | GST_ELEMENT_FLAG_SINK);
709
710 channel =
711 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
712 NULL));
713 channel->label = g_strdup ("CONTRAST");
714 channel->min_value = -1000;
715 channel->max_value = 1000;
716 playsink->colorbalance_channels =
717 g_list_append (playsink->colorbalance_channels, channel);
718 playsink->colorbalance_values[0] = 0;
719
720 channel =
721 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
722 NULL));
723 channel->label = g_strdup ("BRIGHTNESS");
724 channel->min_value = -1000;
725 channel->max_value = 1000;
726 playsink->colorbalance_channels =
727 g_list_append (playsink->colorbalance_channels, channel);
728 playsink->colorbalance_values[1] = 0;
729
730 channel =
731 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
732 NULL));
733 channel->label = g_strdup ("HUE");
734 channel->min_value = -1000;
735 channel->max_value = 1000;
736 playsink->colorbalance_channels =
737 g_list_append (playsink->colorbalance_channels, channel);
738 playsink->colorbalance_values[2] = 0;
739
740 channel =
741 GST_COLOR_BALANCE_CHANNEL (g_object_new (GST_TYPE_COLOR_BALANCE_CHANNEL,
742 NULL));
743 channel->label = g_strdup ("SATURATION");
744 channel->min_value = -1000;
745 channel->max_value = 1000;
746 playsink->colorbalance_channels =
747 g_list_append (playsink->colorbalance_channels, channel);
748 playsink->colorbalance_values[3] = 0;
749 }
750
751 static void
disconnect_audio_chain(GstPlayAudioChain * chain,GstPlaySink * playsink)752 disconnect_audio_chain (GstPlayAudioChain * chain, GstPlaySink * playsink)
753 {
754 if (chain) {
755 if (chain->notify_volume_id)
756 g_signal_handler_disconnect (chain->volume, chain->notify_volume_id);
757 if (chain->notify_mute_id)
758 g_signal_handler_disconnect (chain->volume, chain->notify_mute_id);
759 chain->notify_volume_id = chain->notify_mute_id = 0;
760 }
761 }
762
763 static void
free_chain(GstPlayChain * chain)764 free_chain (GstPlayChain * chain)
765 {
766 if (chain) {
767 if (chain->bin)
768 gst_object_unref (chain->bin);
769 g_free (chain);
770 }
771 }
772
773 static void
gst_play_sink_remove_audio_ssync_queue(GstPlaySink * playsink)774 gst_play_sink_remove_audio_ssync_queue (GstPlaySink * playsink)
775 {
776 if (playsink->audio_ssync_queue) {
777 gst_element_set_state (playsink->audio_ssync_queue, GST_STATE_NULL);
778 gst_object_unref (playsink->audio_ssync_queue_sinkpad);
779 gst_bin_remove (GST_BIN_CAST (playsink), playsink->audio_ssync_queue);
780 playsink->audio_ssync_queue = NULL;
781 playsink->audio_ssync_queue_sinkpad = NULL;
782 }
783 }
784
785 static void
gst_play_sink_dispose(GObject * object)786 gst_play_sink_dispose (GObject * object)
787 {
788 GstPlaySink *playsink;
789
790 playsink = GST_PLAY_SINK (object);
791
792 if (playsink->audio_filter != NULL) {
793 gst_element_set_state (playsink->audio_filter, GST_STATE_NULL);
794 gst_object_unref (playsink->audio_filter);
795 playsink->audio_filter = NULL;
796 }
797 if (playsink->video_filter != NULL) {
798 gst_element_set_state (playsink->video_filter, GST_STATE_NULL);
799 gst_object_unref (playsink->video_filter);
800 playsink->video_filter = NULL;
801 }
802 if (playsink->audio_sink != NULL) {
803 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
804 gst_object_unref (playsink->audio_sink);
805 playsink->audio_sink = NULL;
806 }
807 if (playsink->video_sink != NULL) {
808 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
809 gst_object_unref (playsink->video_sink);
810 playsink->video_sink = NULL;
811 }
812 if (playsink->visualisation != NULL) {
813 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
814 gst_object_unref (playsink->visualisation);
815 playsink->visualisation = NULL;
816 }
817 if (playsink->text_sink != NULL) {
818 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
819 gst_object_unref (playsink->text_sink);
820 playsink->text_sink = NULL;
821 }
822
823 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
824 playsink->videodeinterlacechain = NULL;
825 free_chain ((GstPlayChain *) playsink->videochain);
826 playsink->videochain = NULL;
827 free_chain ((GstPlayChain *) playsink->audiochain);
828 playsink->audiochain = NULL;
829 free_chain ((GstPlayChain *) playsink->vischain);
830 playsink->vischain = NULL;
831 free_chain ((GstPlayChain *) playsink->textchain);
832 playsink->textchain = NULL;
833
834 if (playsink->audio_tee_sink) {
835 gst_object_unref (playsink->audio_tee_sink);
836 playsink->audio_tee_sink = NULL;
837 }
838
839 if (playsink->audio_tee_vissrc) {
840 gst_element_release_request_pad (playsink->audio_tee,
841 playsink->audio_tee_vissrc);
842 gst_object_unref (playsink->audio_tee_vissrc);
843 playsink->audio_tee_vissrc = NULL;
844 }
845
846 if (playsink->audio_tee_asrc) {
847 gst_element_release_request_pad (playsink->audio_tee,
848 playsink->audio_tee_asrc);
849 gst_object_unref (playsink->audio_tee_asrc);
850 playsink->audio_tee_asrc = NULL;
851 }
852
853 g_free (playsink->font_desc);
854 playsink->font_desc = NULL;
855
856 g_free (playsink->subtitle_encoding);
857 playsink->subtitle_encoding = NULL;
858
859 playsink->stream_synchronizer = NULL;
860
861 g_list_foreach (playsink->colorbalance_channels, (GFunc) gst_object_unref,
862 NULL);
863 g_list_free (playsink->colorbalance_channels);
864 playsink->colorbalance_channels = NULL;
865
866 G_OBJECT_CLASS (gst_play_sink_parent_class)->dispose (object);
867 }
868
869 static void
gst_play_sink_finalize(GObject * object)870 gst_play_sink_finalize (GObject * object)
871 {
872 GstPlaySink *playsink;
873
874 playsink = GST_PLAY_SINK (object);
875
876 g_rec_mutex_clear (&playsink->lock);
877
878 G_OBJECT_CLASS (gst_play_sink_parent_class)->finalize (object);
879 }
880
881 void
gst_play_sink_set_sink(GstPlaySink * playsink,GstPlaySinkType type,GstElement * sink)882 gst_play_sink_set_sink (GstPlaySink * playsink, GstPlaySinkType type,
883 GstElement * sink)
884 {
885 GstElement **elem = NULL, *old = NULL;
886 #ifndef GST_DISABLE_GST_DEBUG
887 GstPad *sink_pad;
888 const gchar *sink_type = NULL;
889 #endif
890
891 GST_LOG ("Setting sink %" GST_PTR_FORMAT " as sink type %d", sink, type);
892
893 GST_PLAY_SINK_LOCK (playsink);
894 switch (type) {
895 case GST_PLAY_SINK_TYPE_AUDIO:
896 elem = &playsink->audio_sink;
897 #ifndef GST_DISABLE_GST_DEBUG
898 sink_type = "audio";
899 #endif
900 break;
901 case GST_PLAY_SINK_TYPE_VIDEO:
902 elem = &playsink->video_sink;
903 #ifndef GST_DISABLE_GST_DEBUG
904 sink_type = "video";
905 #endif
906 break;
907 case GST_PLAY_SINK_TYPE_TEXT:
908 elem = &playsink->text_sink;
909 #ifndef GST_DISABLE_GST_DEBUG
910 sink_type = "text";
911 #endif
912 break;
913 default:
914 break;
915 }
916 if (elem) {
917 old = *elem;
918 if (sink)
919 gst_object_ref_sink (sink);
920 *elem = sink;
921 }
922 GST_PLAY_SINK_UNLOCK (playsink);
923
924 #ifndef GST_DISABLE_GST_DEBUG
925 /* Check and warn if an application sets a sink with no 'sink' pad */
926 if (sink && elem) {
927 if ((sink_pad = gst_element_get_static_pad (sink, "sink")) != NULL) {
928 gst_object_unref (sink_pad);
929 } else {
930 GST_ELEMENT_WARNING (playsink, CORE, FAILED,
931 ("Application error - playback can't work"),
932 ("custom %s sink has no pad named \"sink\"", sink_type));
933 }
934 }
935 #endif
936
937 if (old) {
938 /* Set the old sink to NULL if it is not used any longer */
939 if (old != sink && !GST_OBJECT_PARENT (old))
940 gst_element_set_state (old, GST_STATE_NULL);
941 gst_object_unref (old);
942 }
943 }
944
945 GstElement *
gst_play_sink_get_sink(GstPlaySink * playsink,GstPlaySinkType type)946 gst_play_sink_get_sink (GstPlaySink * playsink, GstPlaySinkType type)
947 {
948 GstElement *result = NULL;
949 GstElement *elem = NULL, *chainp = NULL;
950
951 GST_PLAY_SINK_LOCK (playsink);
952 switch (type) {
953 case GST_PLAY_SINK_TYPE_AUDIO:
954 {
955 GstPlayAudioChain *chain;
956 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
957 chainp = chain->sink;
958 elem = playsink->audio_sink;
959 break;
960 }
961 case GST_PLAY_SINK_TYPE_VIDEO:
962 {
963 GstPlayVideoChain *chain;
964 if ((chain = (GstPlayVideoChain *) playsink->videochain))
965 chainp = chain->sink;
966 elem = playsink->video_sink;
967 break;
968 }
969 case GST_PLAY_SINK_TYPE_TEXT:
970 {
971 GstPlayTextChain *chain;
972 if ((chain = (GstPlayTextChain *) playsink->textchain))
973 chainp = chain->sink;
974 elem = playsink->text_sink;
975 break;
976 }
977 default:
978 break;
979 }
980 if (chainp) {
981 /* we have an active chain with a sink, get the sink */
982 result = gst_object_ref (chainp);
983 }
984 /* nothing found, return last configured sink */
985 if (result == NULL && elem)
986 result = gst_object_ref (elem);
987 GST_PLAY_SINK_UNLOCK (playsink);
988
989 return result;
990 }
991
992 void
gst_play_sink_set_filter(GstPlaySink * playsink,GstPlaySinkType type,GstElement * filter)993 gst_play_sink_set_filter (GstPlaySink * playsink, GstPlaySinkType type,
994 GstElement * filter)
995 {
996 GstElement **elem = NULL, *old = NULL;
997
998 GST_LOG_OBJECT (playsink,
999 "Setting filter %" GST_PTR_FORMAT " as filter type %d", filter, type);
1000
1001 GST_PLAY_SINK_LOCK (playsink);
1002 switch (type) {
1003 case GST_PLAY_SINK_TYPE_AUDIO:
1004 elem = &playsink->audio_filter;
1005 break;
1006 case GST_PLAY_SINK_TYPE_VIDEO:
1007 elem = &playsink->video_filter;
1008 break;
1009 default:
1010 break;
1011 }
1012 if (elem) {
1013 old = *elem;
1014 if (filter)
1015 gst_object_ref_sink (filter);
1016 *elem = filter;
1017 }
1018 GST_PLAY_SINK_UNLOCK (playsink);
1019
1020 if (old) {
1021 /* Set the old filter to NULL if it is not used any longer */
1022 if (old != filter && !GST_OBJECT_PARENT (old))
1023 gst_element_set_state (old, GST_STATE_NULL);
1024 gst_object_unref (old);
1025 }
1026 }
1027
1028 GstElement *
gst_play_sink_get_filter(GstPlaySink * playsink,GstPlaySinkType type)1029 gst_play_sink_get_filter (GstPlaySink * playsink, GstPlaySinkType type)
1030 {
1031 GstElement *result = NULL;
1032 GstElement *elem = NULL, *chainp = NULL;
1033
1034 GST_PLAY_SINK_LOCK (playsink);
1035 switch (type) {
1036 case GST_PLAY_SINK_TYPE_AUDIO:
1037 {
1038 GstPlayAudioChain *chain;
1039 if ((chain = (GstPlayAudioChain *) playsink->audiochain))
1040 chainp = chain->filter;
1041 elem = playsink->audio_filter;
1042 break;
1043 }
1044 case GST_PLAY_SINK_TYPE_VIDEO:
1045 {
1046 GstPlayVideoChain *chain;
1047 if ((chain = (GstPlayVideoChain *) playsink->videochain))
1048 chainp = chain->filter;
1049 elem = playsink->video_filter;
1050 break;
1051 }
1052 default:
1053 break;
1054 }
1055 if (chainp) {
1056 /* we have an active chain with a filter, get the filter */
1057 result = gst_object_ref (chainp);
1058 }
1059 /* nothing found, return last configured filter */
1060 if (result == NULL && elem)
1061 result = gst_object_ref (elem);
1062 GST_PLAY_SINK_UNLOCK (playsink);
1063
1064 return result;
1065 }
1066
1067 static GstPadProbeReturn
gst_play_sink_vis_blocked(GstPad * tee_pad,GstPadProbeInfo * info,gpointer user_data)1068 gst_play_sink_vis_blocked (GstPad * tee_pad, GstPadProbeInfo * info,
1069 gpointer user_data)
1070 {
1071 GstPlaySink *playsink;
1072 GstPlayVisChain *chain;
1073
1074 playsink = GST_PLAY_SINK (user_data);
1075
1076 if (GST_IS_EVENT (info->data) && !GST_EVENT_IS_SERIALIZED (info->data)) {
1077 GST_DEBUG_OBJECT (playsink, "Letting non-serialized event %s pass",
1078 GST_EVENT_TYPE_NAME (info->data));
1079 return GST_PAD_PROBE_PASS;
1080 }
1081
1082 GST_PLAY_SINK_LOCK (playsink);
1083 GST_DEBUG_OBJECT (playsink, "vis pad blocked");
1084 /* now try to change the plugin in the running vis chain */
1085 if (!(chain = (GstPlayVisChain *) playsink->vischain))
1086 goto done;
1087
1088 /* unlink the old plugin and unghost the pad */
1089 gst_pad_unlink (chain->vispeerpad, chain->vissinkpad);
1090 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad), NULL);
1091
1092 /* set the old plugin to NULL and remove */
1093 gst_element_set_state (chain->vis, GST_STATE_NULL);
1094 gst_bin_remove (GST_BIN_CAST (chain->chain.bin), chain->vis);
1095
1096 /* add new plugin and set state to playing */
1097 chain->vis = playsink->visualisation;
1098 gst_bin_add (GST_BIN_CAST (chain->chain.bin), chain->vis);
1099 gst_element_set_state (chain->vis, GST_STATE_PLAYING);
1100
1101 /* get pads */
1102 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
1103 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
1104
1105 /* link pads */
1106 gst_pad_link_full (chain->vispeerpad, chain->vissinkpad,
1107 GST_PAD_LINK_CHECK_NOTHING);
1108 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (chain->srcpad),
1109 chain->vissrcpad);
1110
1111 done:
1112 playsink->vis_pad_block_id = 0;
1113
1114 GST_PLAY_SINK_UNLOCK (playsink);
1115
1116 /* remove the probe and unblock the pad */
1117 return GST_PAD_PROBE_REMOVE;
1118 }
1119
1120 void
gst_play_sink_set_vis_plugin(GstPlaySink * playsink,GstElement * vis)1121 gst_play_sink_set_vis_plugin (GstPlaySink * playsink, GstElement * vis)
1122 {
1123 GstPlayVisChain *chain;
1124
1125 /* setting NULL means creating the default vis plugin */
1126 if (vis == NULL)
1127 vis = gst_element_factory_make ("goom", "vis");
1128
1129 /* simply return if we don't have a vis plugin here */
1130 if (vis == NULL)
1131 return;
1132
1133 GST_PLAY_SINK_LOCK (playsink);
1134 /* first store the new vis */
1135 if (playsink->visualisation)
1136 gst_object_unref (playsink->visualisation);
1137 /* take ownership */
1138 gst_object_ref_sink (vis);
1139 playsink->visualisation = vis;
1140
1141 /* now try to change the plugin in the running vis chain, if we have no chain,
1142 * we don't bother, any future vis chain will be created with the new vis
1143 * plugin. */
1144 if (!(chain = (GstPlayVisChain *) playsink->vischain))
1145 goto done;
1146
1147 /* block the pad, the next time the callback is called we can change the
1148 * visualisation. It's possible that this never happens or that the pad was
1149 * already blocked. If the callback never happens, we don't have new data so
1150 * we don't need the new vis plugin. If the pad was already blocked, the
1151 * function returns FALSE but the previous pad block will do the right thing
1152 * anyway. */
1153 GST_DEBUG_OBJECT (playsink, "blocking vis pad");
1154 if (!playsink->vis_pad_block_id && !playsink->audio_block_id
1155 && !playsink->video_block_id && !playsink->text_block_id)
1156 playsink->vis_pad_block_id =
1157 gst_pad_add_probe (chain->blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
1158 gst_play_sink_vis_blocked, playsink, NULL);
1159 done:
1160 GST_PLAY_SINK_UNLOCK (playsink);
1161
1162 return;
1163 }
1164
1165 GstElement *
gst_play_sink_get_vis_plugin(GstPlaySink * playsink)1166 gst_play_sink_get_vis_plugin (GstPlaySink * playsink)
1167 {
1168 GstElement *result = NULL;
1169 GstPlayVisChain *chain;
1170
1171 GST_PLAY_SINK_LOCK (playsink);
1172 if ((chain = (GstPlayVisChain *) playsink->vischain)) {
1173 /* we have an active chain, get the sink */
1174 if (chain->vis)
1175 result = gst_object_ref (chain->vis);
1176 }
1177 /* nothing found, return last configured sink */
1178 if (result == NULL && playsink->visualisation)
1179 result = gst_object_ref (playsink->visualisation);
1180 GST_PLAY_SINK_UNLOCK (playsink);
1181
1182 return result;
1183 }
1184
1185 void
gst_play_sink_set_volume(GstPlaySink * playsink,gdouble volume)1186 gst_play_sink_set_volume (GstPlaySink * playsink, gdouble volume)
1187 {
1188 GstPlayAudioChain *chain;
1189
1190 GST_PLAY_SINK_LOCK (playsink);
1191 playsink->volume = volume;
1192 chain = (GstPlayAudioChain *) playsink->audiochain;
1193 if (chain && chain->volume) {
1194 GST_LOG_OBJECT (playsink,
1195 "elements: volume=%" GST_PTR_FORMAT "; new volume=%.03f, mute=%d",
1196 chain->volume, volume, playsink->mute);
1197 g_object_set (chain->volume, "volume", volume, NULL);
1198 } else {
1199 GST_LOG_OBJECT (playsink, "no volume element");
1200 playsink->volume_changed = TRUE;
1201 }
1202 GST_PLAY_SINK_UNLOCK (playsink);
1203 }
1204
1205 gdouble
gst_play_sink_get_volume(GstPlaySink * playsink)1206 gst_play_sink_get_volume (GstPlaySink * playsink)
1207 {
1208 gdouble result;
1209 GstPlayAudioChain *chain;
1210
1211 GST_PLAY_SINK_LOCK (playsink);
1212 chain = (GstPlayAudioChain *) playsink->audiochain;
1213 result = playsink->volume;
1214 if (chain && chain->volume) {
1215 g_object_get (chain->volume, "volume", &result, NULL);
1216 playsink->volume = result;
1217 }
1218 GST_PLAY_SINK_UNLOCK (playsink);
1219
1220 return result;
1221 }
1222
1223 void
gst_play_sink_set_mute(GstPlaySink * playsink,gboolean mute)1224 gst_play_sink_set_mute (GstPlaySink * playsink, gboolean mute)
1225 {
1226 GstPlayAudioChain *chain;
1227
1228 GST_PLAY_SINK_LOCK (playsink);
1229 playsink->mute = mute;
1230 chain = (GstPlayAudioChain *) playsink->audiochain;
1231 if (chain && chain->volume) {
1232 g_object_set (chain->volume, "mute", mute, NULL);
1233 } else {
1234 playsink->mute_changed = TRUE;
1235 }
1236 GST_PLAY_SINK_UNLOCK (playsink);
1237 }
1238
1239 gboolean
gst_play_sink_get_mute(GstPlaySink * playsink)1240 gst_play_sink_get_mute (GstPlaySink * playsink)
1241 {
1242 gboolean result;
1243 GstPlayAudioChain *chain;
1244
1245 GST_PLAY_SINK_LOCK (playsink);
1246 chain = (GstPlayAudioChain *) playsink->audiochain;
1247 if (chain && chain->volume) {
1248 g_object_get (chain->volume, "mute", &result, NULL);
1249 playsink->mute = result;
1250 } else {
1251 result = playsink->mute;
1252 }
1253 GST_PLAY_SINK_UNLOCK (playsink);
1254
1255 return result;
1256 }
1257
1258 static void
post_missing_element_message(GstPlaySink * playsink,const gchar * name)1259 post_missing_element_message (GstPlaySink * playsink, const gchar * name)
1260 {
1261 GstMessage *msg;
1262
1263 msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playsink), name);
1264 gst_element_post_message (GST_ELEMENT_CAST (playsink), msg);
1265 }
1266
1267 static gboolean
add_chain(GstPlayChain * chain,gboolean add)1268 add_chain (GstPlayChain * chain, gboolean add)
1269 {
1270 if (chain->added == add)
1271 return TRUE;
1272
1273 if (add)
1274 gst_bin_add (GST_BIN_CAST (chain->playsink), chain->bin);
1275 else {
1276 gst_bin_remove (GST_BIN_CAST (chain->playsink), chain->bin);
1277 }
1278
1279 chain->added = add;
1280
1281 return TRUE;
1282 }
1283
1284 static gboolean
activate_chain(GstPlayChain * chain,gboolean activate)1285 activate_chain (GstPlayChain * chain, gboolean activate)
1286 {
1287 GstState state;
1288
1289 if (chain->activated == activate)
1290 return TRUE;
1291
1292 GST_OBJECT_LOCK (chain->playsink);
1293 state = GST_STATE_TARGET (chain->playsink);
1294 GST_OBJECT_UNLOCK (chain->playsink);
1295
1296 if (activate)
1297 gst_element_set_state (chain->bin, state);
1298 else
1299 gst_element_set_state (chain->bin, GST_STATE_NULL);
1300
1301 chain->activated = activate;
1302
1303 return TRUE;
1304 }
1305
1306 static gboolean
element_is_sink(GstElement * element)1307 element_is_sink (GstElement * element)
1308 {
1309 gboolean is_sink;
1310
1311 GST_OBJECT_LOCK (element);
1312 is_sink = GST_OBJECT_FLAG_IS_SET (element, GST_ELEMENT_FLAG_SINK);
1313 GST_OBJECT_UNLOCK (element);
1314
1315 GST_DEBUG_OBJECT (element, "is a sink: %s", (is_sink) ? "yes" : "no");
1316 return is_sink;
1317 }
1318
1319 static gboolean
element_has_property(GstElement * element,const gchar * pname,GType type)1320 element_has_property (GstElement * element, const gchar * pname, GType type)
1321 {
1322 GParamSpec *pspec;
1323
1324 pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (element), pname);
1325
1326 if (pspec == NULL) {
1327 GST_DEBUG_OBJECT (element, "no %s property", pname);
1328 return FALSE;
1329 }
1330
1331 if (type == G_TYPE_INVALID || type == pspec->value_type ||
1332 g_type_is_a (pspec->value_type, type)) {
1333 GST_DEBUG_OBJECT (element, "has %s property of type %s", pname,
1334 (type == G_TYPE_INVALID) ? "any type" : g_type_name (type));
1335 return TRUE;
1336 }
1337
1338 GST_WARNING_OBJECT (element, "has %s property, but property is of type %s "
1339 "and we expected it to be of type %s", pname,
1340 g_type_name (pspec->value_type), g_type_name (type));
1341
1342 return FALSE;
1343 }
1344
1345 typedef struct
1346 {
1347 const gchar *prop_name;
1348 GType prop_type;
1349 gboolean need_sink;
1350 } FindPropertyHelper;
1351
1352 static gint
find_property(const GValue * item,FindPropertyHelper * helper)1353 find_property (const GValue * item, FindPropertyHelper * helper)
1354 {
1355 GstElement *element = g_value_get_object (item);
1356 if (helper->need_sink && !element_is_sink (element)) {
1357 return 1;
1358 }
1359
1360 if (!element_has_property (element, helper->prop_name, helper->prop_type)) {
1361 return 1;
1362 }
1363
1364 GST_INFO_OBJECT (element, "found %s with %s property", helper->prop_name,
1365 (helper->need_sink) ? "sink" : "element");
1366 return 0; /* keep it */
1367 }
1368
1369 /* FIXME: why not move these functions into core? */
1370 /* find a sink in the hierarchy with a property named @name. This function does
1371 * not increase the refcount of the returned object and thus remains valid as
1372 * long as the bin is valid. */
1373 static GstElement *
gst_play_sink_find_property_sinks(GstPlaySink * playsink,GstElement * obj,const gchar * name,GType expected_type)1374 gst_play_sink_find_property_sinks (GstPlaySink * playsink, GstElement * obj,
1375 const gchar * name, GType expected_type)
1376 {
1377 GstElement *result = NULL;
1378 GstIterator *it;
1379
1380 if (element_has_property (obj, name, expected_type)) {
1381 result = obj;
1382 } else if (GST_IS_BIN (obj)) {
1383 gboolean found;
1384 GValue item = { 0, };
1385 FindPropertyHelper helper = { name, expected_type, TRUE };
1386
1387 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1388 found = gst_iterator_find_custom (it,
1389 (GCompareFunc) find_property, &item, &helper);
1390 gst_iterator_free (it);
1391 if (found) {
1392 result = g_value_get_object (&item);
1393 /* we don't need the extra ref */
1394 g_value_unset (&item);
1395 }
1396 }
1397 return result;
1398 }
1399
1400 /* find an object in the hierarchy with a property named @name */
1401 static GstElement *
gst_play_sink_find_property(GstPlaySink * playsink,GstElement * obj,const gchar * name,GType expected_type)1402 gst_play_sink_find_property (GstPlaySink * playsink, GstElement * obj,
1403 const gchar * name, GType expected_type)
1404 {
1405 GstElement *result = NULL;
1406 GstIterator *it;
1407
1408 if (GST_IS_BIN (obj)) {
1409 gboolean found;
1410 GValue item = { 0, };
1411 FindPropertyHelper helper = { name, expected_type, FALSE };
1412
1413 it = gst_bin_iterate_recurse (GST_BIN_CAST (obj));
1414 found = gst_iterator_find_custom (it,
1415 (GCompareFunc) find_property, &item, &helper);
1416 gst_iterator_free (it);
1417 if (found) {
1418 result = g_value_dup_object (&item);
1419 g_value_unset (&item);
1420 }
1421 } else {
1422 if (element_has_property (obj, name, expected_type)) {
1423 result = obj;
1424 gst_object_ref (obj);
1425 }
1426 }
1427 return result;
1428 }
1429
1430 static void
do_async_start(GstPlaySink * playsink)1431 do_async_start (GstPlaySink * playsink)
1432 {
1433 GstMessage *message;
1434
1435 if (!playsink->need_async_start) {
1436 GST_INFO_OBJECT (playsink, "no async_start needed");
1437 return;
1438 }
1439
1440 playsink->async_pending = TRUE;
1441
1442 GST_INFO_OBJECT (playsink, "Sending async_start message");
1443 message = gst_message_new_async_start (GST_OBJECT_CAST (playsink));
1444 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1445 (playsink), message);
1446 }
1447
1448 static void
do_async_done(GstPlaySink * playsink)1449 do_async_done (GstPlaySink * playsink)
1450 {
1451 GstMessage *message;
1452
1453 if (playsink->async_pending) {
1454 GST_INFO_OBJECT (playsink, "Sending async_done message");
1455 message =
1456 gst_message_new_async_done (GST_OBJECT_CAST (playsink),
1457 GST_CLOCK_TIME_NONE);
1458 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (GST_BIN_CAST
1459 (playsink), message);
1460
1461 playsink->async_pending = FALSE;
1462 }
1463
1464 playsink->need_async_start = FALSE;
1465 }
1466
1467 /* try to change the state of an element. This function returns the element when
1468 * the state change could be performed. When this function returns NULL an error
1469 * occurred and the element is unreffed if @unref is TRUE. */
1470 static GstElement *
try_element(GstPlaySink * playsink,GstElement * element,gboolean unref)1471 try_element (GstPlaySink * playsink, GstElement * element, gboolean unref)
1472 {
1473 GstStateChangeReturn ret;
1474
1475 if (element) {
1476 ret = gst_element_set_state (element, GST_STATE_READY);
1477 if (ret == GST_STATE_CHANGE_FAILURE) {
1478 GST_DEBUG_OBJECT (playsink, "failed state change..");
1479 gst_element_set_state (element, GST_STATE_NULL);
1480 if (unref)
1481 gst_object_unref (element);
1482 element = NULL;
1483 }
1484 }
1485 return element;
1486 }
1487
1488 /* make the element (bin) that contains the elements needed to perform
1489 * video deinterlacing. Only used for *raw* video streams.
1490 *
1491 * +---------------------------------------+
1492 * | vbin |
1493 * | +----------+ +-----------+ |
1494 * | |colorspace| |deinterlace| |
1495 * | +-sink src-sink src-+ |
1496 * | | +----------+ +-----------+ | |
1497 * sink-+ +-src
1498 * +---------------------------------------+
1499 *
1500 */
1501 static GstPlayVideoDeinterlaceChain *
gen_video_deinterlace_chain(GstPlaySink * playsink)1502 gen_video_deinterlace_chain (GstPlaySink * playsink)
1503 {
1504 GstPlayVideoDeinterlaceChain *chain;
1505 GstBin *bin;
1506 GstPad *pad;
1507 GstElement *head = NULL, *prev = NULL;
1508
1509 chain = g_new0 (GstPlayVideoDeinterlaceChain, 1);
1510 chain->chain.playsink = playsink;
1511
1512 GST_DEBUG_OBJECT (playsink, "making video deinterlace chain %p", chain);
1513
1514 /* create a bin to hold objects, as we create them we add them to this bin so
1515 * that when something goes wrong we only need to unref the bin */
1516 chain->chain.bin = gst_bin_new ("vdbin");
1517 bin = GST_BIN_CAST (chain->chain.bin);
1518 gst_object_ref_sink (bin);
1519
1520 GST_DEBUG_OBJECT (playsink, "creating " COLORSPACE);
1521 chain->conv = gst_element_factory_make (COLORSPACE, "vdconv");
1522 if (chain->conv == NULL) {
1523 post_missing_element_message (playsink, COLORSPACE);
1524 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1525 (_("Missing element '%s' - check your GStreamer installation."),
1526 COLORSPACE), ("video rendering might fail"));
1527 } else {
1528 gst_bin_add (bin, chain->conv);
1529 head = chain->conv;
1530 prev = chain->conv;
1531 }
1532
1533 GST_DEBUG_OBJECT (playsink, "creating deinterlace");
1534 chain->deinterlace = gst_element_factory_make ("deinterlace", "deinterlace");
1535 if (chain->deinterlace == NULL) {
1536 post_missing_element_message (playsink, "deinterlace");
1537 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1538 (_("Missing element '%s' - check your GStreamer installation."),
1539 "deinterlace"), ("deinterlacing won't work"));
1540 } else {
1541 gst_bin_add (bin, chain->deinterlace);
1542 if (prev) {
1543 if (!gst_element_link_pads_full (prev, "src", chain->deinterlace, "sink",
1544 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1545 goto link_failed;
1546 } else {
1547 head = chain->deinterlace;
1548 }
1549 prev = chain->deinterlace;
1550 }
1551
1552 if (head) {
1553 pad = gst_element_get_static_pad (head, "sink");
1554 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1555 gst_object_unref (pad);
1556 } else {
1557 chain->sinkpad = gst_ghost_pad_new_no_target ("sink", GST_PAD_SINK);
1558 }
1559
1560 if (prev) {
1561 pad = gst_element_get_static_pad (prev, "src");
1562 chain->srcpad = gst_ghost_pad_new ("src", pad);
1563 gst_object_unref (pad);
1564 } else {
1565 chain->srcpad = gst_ghost_pad_new ("src", chain->sinkpad);
1566 }
1567
1568 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1569 gst_element_add_pad (chain->chain.bin, chain->srcpad);
1570
1571 return chain;
1572
1573 link_failed:
1574 {
1575 GST_ELEMENT_ERROR (playsink, CORE, PAD,
1576 (NULL), ("Failed to configure the video deinterlace chain."));
1577 free_chain ((GstPlayChain *) chain);
1578 return NULL;
1579 }
1580 }
1581
1582 static gboolean
is_valid_color_balance_element(GstColorBalance * bal)1583 is_valid_color_balance_element (GstColorBalance * bal)
1584 {
1585 gboolean have_brightness = FALSE;
1586 gboolean have_contrast = FALSE;
1587 gboolean have_hue = FALSE;
1588 gboolean have_saturation = FALSE;
1589 const GList *channels, *l;
1590
1591 channels = gst_color_balance_list_channels (bal);
1592 for (l = channels; l; l = l->next) {
1593 GstColorBalanceChannel *ch = l->data;
1594
1595 if (g_strrstr (ch->label, "BRIGHTNESS"))
1596 have_brightness = TRUE;
1597 else if (g_strrstr (ch->label, "CONTRAST"))
1598 have_contrast = TRUE;
1599 else if (g_strrstr (ch->label, "HUE"))
1600 have_hue = TRUE;
1601 else if (g_strrstr (ch->label, "SATURATION"))
1602 have_saturation = TRUE;
1603 }
1604
1605 return have_brightness && have_contrast && have_hue && have_saturation;
1606 }
1607
1608 static void
iterate_color_balance_elements(const GValue * item,gpointer user_data)1609 iterate_color_balance_elements (const GValue * item, gpointer user_data)
1610 {
1611 gboolean valid;
1612 GstColorBalance *cb, **cb_out = user_data;
1613
1614 cb = GST_COLOR_BALANCE (g_value_get_object (item));
1615 valid = is_valid_color_balance_element (cb);
1616 if (valid) {
1617 if (*cb_out
1618 && gst_color_balance_get_balance_type (*cb_out) ==
1619 GST_COLOR_BALANCE_SOFTWARE) {
1620 gst_object_unref (*cb_out);
1621 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1622 } else if (!*cb_out) {
1623 *cb_out = GST_COLOR_BALANCE (gst_object_ref (cb));
1624 }
1625 }
1626 }
1627
1628 static GstColorBalance *
find_color_balance_element(GstElement * element)1629 find_color_balance_element (GstElement * element)
1630 {
1631 GstIterator *it;
1632 GstColorBalance *cb = NULL;
1633
1634 if (GST_IS_COLOR_BALANCE (element)
1635 && is_valid_color_balance_element (GST_COLOR_BALANCE (element)))
1636 return GST_COLOR_BALANCE (gst_object_ref (element));
1637 else if (!GST_IS_BIN (element))
1638 return FALSE;
1639
1640 it = gst_bin_iterate_all_by_interface (GST_BIN (element),
1641 GST_TYPE_COLOR_BALANCE);
1642 while (gst_iterator_foreach (it, iterate_color_balance_elements,
1643 &cb) == GST_ITERATOR_RESYNC)
1644 gst_iterator_resync (it);
1645 gst_iterator_free (it);
1646
1647 return cb;
1648 }
1649
1650 static void
colorbalance_value_changed_cb(GstColorBalance * balance,GstColorBalanceChannel * channel,gint value,GstPlaySink * playsink)1651 colorbalance_value_changed_cb (GstColorBalance * balance,
1652 GstColorBalanceChannel * channel, gint value, GstPlaySink * playsink)
1653 {
1654 GList *l;
1655 gint i;
1656
1657 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1658 GstColorBalanceChannel *proxy = l->data;
1659
1660 if (g_strrstr (channel->label, proxy->label)) {
1661 gdouble new_val;
1662
1663 /* Convert to [0, 1] range */
1664 new_val =
1665 ((gdouble) value -
1666 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
1667 (gdouble) channel->min_value);
1668 /* Convert to proxy range */
1669 new_val =
1670 proxy->min_value + new_val * ((gdouble) proxy->max_value -
1671 (gdouble) proxy->min_value);
1672 playsink->colorbalance_values[i] = (gint) (0.5 + new_val);
1673
1674 gst_color_balance_value_changed (GST_COLOR_BALANCE (playsink), proxy,
1675 playsink->colorbalance_values[i]);
1676 break;
1677 }
1678 }
1679 }
1680
1681 static void
update_colorbalance(GstPlaySink * playsink)1682 update_colorbalance (GstPlaySink * playsink)
1683 {
1684 GstColorBalance *balance = NULL;
1685 GList *l;
1686 gint i;
1687
1688 GST_OBJECT_LOCK (playsink);
1689 if (playsink->colorbalance_element) {
1690 balance =
1691 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
1692 }
1693 GST_OBJECT_UNLOCK (playsink);
1694 if (!balance)
1695 return;
1696
1697 g_signal_handler_block (balance, playsink->colorbalance_value_changed_id);
1698
1699 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
1700 GstColorBalanceChannel *proxy = l->data;
1701 GstColorBalanceChannel *channel = NULL;
1702 const GList *channels, *k;
1703 gdouble new_val;
1704
1705 channels = gst_color_balance_list_channels (balance);
1706 for (k = channels; k; k = k->next) {
1707 GstColorBalanceChannel *tmp = k->data;
1708
1709 if (g_strrstr (tmp->label, proxy->label)) {
1710 channel = tmp;
1711 break;
1712 }
1713 }
1714
1715 g_assert (channel);
1716
1717 /* Convert to [0, 1] range */
1718 new_val =
1719 ((gdouble) playsink->colorbalance_values[i] -
1720 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
1721 (gdouble) proxy->min_value);
1722 /* Convert to channel range */
1723 new_val =
1724 channel->min_value + new_val * ((gdouble) channel->max_value -
1725 (gdouble) channel->min_value);
1726
1727 gst_color_balance_set_value (balance, channel, (gint) (new_val + 0.5));
1728 }
1729
1730 g_signal_handler_unblock (balance, playsink->colorbalance_value_changed_id);
1731
1732 gst_object_unref (balance);
1733 }
1734
1735 /* make the element (bin) that contains the elements needed to perform
1736 * video display.
1737 *
1738 * +------------------------------------------------------------------------+
1739 * | vbin |
1740 * | +--------+ +-------+ +----------+ +----------+ +---------+ |
1741 * | | filter | | queue | |colorspace| |videoscale| |videosink| |
1742 * | +-sink src-sink src-sink src-sink src-sink | |
1743 * | | +--------+ +-------+ +----------+ +----------+ +---------+ |
1744 * sink-+ |
1745 * +------------------------------------------------------------------------+
1746 *
1747 */
1748 static GstPlayVideoChain *
gen_video_chain(GstPlaySink * playsink,gboolean raw,gboolean async)1749 gen_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
1750 {
1751 GstPlayVideoChain *chain;
1752 GstBin *bin;
1753 GstPad *pad;
1754 GstElement *head = NULL, *prev = NULL, *elem = NULL;
1755
1756 chain = g_new0 (GstPlayVideoChain, 1);
1757 chain->chain.playsink = playsink;
1758 chain->chain.raw = raw;
1759
1760 GST_DEBUG_OBJECT (playsink, "making video chain %p", chain);
1761
1762 if (playsink->video_sink) {
1763 GST_DEBUG_OBJECT (playsink, "trying configured videosink");
1764 chain->sink = try_element (playsink, playsink->video_sink, FALSE);
1765 } else {
1766 /* only try fallback if no specific sink was chosen */
1767 if (chain->sink == NULL) {
1768 GST_DEBUG_OBJECT (playsink, "trying autovideosink");
1769 elem = gst_element_factory_make ("autovideosink", "videosink");
1770 chain->sink = try_element (playsink, elem, TRUE);
1771 }
1772 if (chain->sink == NULL) {
1773 /* if default sink from config.h is different then try it too */
1774 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
1775 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_VIDEOSINK);
1776 elem = gst_element_factory_make (DEFAULT_VIDEOSINK, "videosink");
1777 chain->sink = try_element (playsink, elem, TRUE);
1778 }
1779 }
1780 if (chain->sink)
1781 playsink->video_sink = gst_object_ref (chain->sink);
1782 }
1783 if (chain->sink == NULL)
1784 goto no_sinks;
1785 head = chain->sink;
1786
1787 /* if we can disable async behaviour of the sink, we can avoid adding a
1788 * queue for the audio chain. */
1789 elem =
1790 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
1791 G_TYPE_BOOLEAN);
1792 if (elem) {
1793 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
1794 async, GST_ELEMENT_NAME (elem));
1795 g_object_set (elem, "async", async, NULL);
1796 chain->async = async;
1797 } else {
1798 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
1799 chain->async = TRUE;
1800 }
1801
1802 /* Make sure the aspect ratio is kept */
1803 elem =
1804 gst_play_sink_find_property_sinks (playsink, chain->sink,
1805 "force-aspect-ratio", G_TYPE_BOOLEAN);
1806 if (elem)
1807 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
1808 NULL);
1809
1810 /* find ts-offset element */
1811 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
1812 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
1813 G_TYPE_INT64));
1814
1815 /* create a bin to hold objects, as we create them we add them to this bin so
1816 * that when something goes wrong we only need to unref the bin */
1817 chain->chain.bin = gst_bin_new ("vbin");
1818 bin = GST_BIN_CAST (chain->chain.bin);
1819 gst_object_ref_sink (bin);
1820 gst_bin_add (bin, chain->sink);
1821
1822 /* Get the VideoOverlay element */
1823 {
1824 GstVideoOverlay *overlay = NULL;
1825
1826 GST_OBJECT_LOCK (playsink);
1827 if (playsink->overlay_element)
1828 gst_object_unref (playsink->overlay_element);
1829 playsink->overlay_element =
1830 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
1831 GST_TYPE_VIDEO_OVERLAY));
1832 if (playsink->overlay_element)
1833 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
1834 GST_OBJECT_UNLOCK (playsink);
1835
1836 if (overlay) {
1837 if (playsink->overlay_handle_set)
1838 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
1839 if (playsink->overlay_handle_events_set)
1840 gst_video_overlay_handle_events (overlay,
1841 playsink->overlay_handle_events);
1842 if (playsink->overlay_render_rectangle_set)
1843 gst_video_overlay_set_render_rectangle (overlay,
1844 playsink->overlay_x, playsink->overlay_y,
1845 playsink->overlay_width, playsink->overlay_height);
1846 gst_object_unref (overlay);
1847 }
1848 }
1849
1850 head = chain->sink;
1851 prev = NULL;
1852
1853 /* add the video filter first, so everything is working with post-filter
1854 * samples */
1855 chain->filter = gst_play_sink_get_filter (playsink, GST_PLAY_SINK_TYPE_VIDEO);
1856 if (chain->filter) {
1857 if (!raw) {
1858 gst_object_unref (chain->filter);
1859 chain->filter = NULL;
1860
1861 if (playsink->flags & GST_PLAY_FLAG_FORCE_FILTERS) {
1862 goto filter_with_nonraw;
1863 } else {
1864 GST_DEBUG_OBJECT (playsink,
1865 "skipping video filter since we're not raw");
1866 }
1867 } else {
1868 GST_DEBUG_OBJECT (playsink, "adding video filter");
1869 chain->filter_conv =
1870 gst_element_factory_make ("videoconvert", "filter-convert");
1871 if (!chain->filter_conv) {
1872 post_missing_element_message (playsink, "videoconvert");
1873 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1874 (_("Missing element '%s' - check your GStreamer installation."),
1875 "videoconvert"),
1876 ("video playback and visualizations might not work"));
1877 } else {
1878 gst_bin_add (bin, chain->filter_conv);
1879 head = prev = chain->filter_conv;
1880 }
1881
1882 gst_bin_add (bin, chain->filter);
1883 /* Bin takes a new reference because we sinked any
1884 * floating reference ourselves already */
1885 gst_object_unref (chain->filter);
1886 if (prev) {
1887 if (!gst_element_link_pads_full (prev, "src", chain->filter, NULL,
1888 GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
1889 goto link_failed;
1890 }
1891 } else {
1892 head = chain->filter;
1893 }
1894 prev = chain->filter;
1895 }
1896 }
1897
1898 /* decouple decoder from sink, this improves playback quite a lot since the
1899 * decoder can continue while the sink blocks for synchronisation. We don't
1900 * need a lot of buffers as this consumes a lot of memory and we don't want
1901 * too little because else we would be context switching too quickly. */
1902 chain->queue = gst_element_factory_make ("queue", "vqueue");
1903 if (chain->queue == NULL) {
1904 post_missing_element_message (playsink, "queue");
1905 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
1906 (_("Missing element '%s' - check your GStreamer installation."),
1907 "queue"), ("video rendering might be suboptimal"));
1908 } else {
1909 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
1910 "max-size-bytes", 0, "max-size-time", (gint64) 0, "silent", TRUE, NULL);
1911 gst_bin_add (bin, chain->queue);
1912 if (prev) {
1913 if (!gst_element_link_pads_full (prev, "src", chain->queue, "sink",
1914 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1915 goto link_failed;
1916 } else {
1917 head = chain->queue;
1918 }
1919 prev = chain->queue;
1920 }
1921
1922 GST_OBJECT_LOCK (playsink);
1923 if (playsink->colorbalance_element) {
1924 g_signal_handler_disconnect (playsink->colorbalance_element,
1925 playsink->colorbalance_value_changed_id);
1926 gst_object_unref (playsink->colorbalance_element);
1927 playsink->colorbalance_value_changed_id = 0;
1928 }
1929 playsink->colorbalance_element = find_color_balance_element (chain->sink);
1930 if (playsink->colorbalance_element) {
1931 playsink->colorbalance_value_changed_id =
1932 g_signal_connect (playsink->colorbalance_element, "value-changed",
1933 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1934 }
1935 GST_OBJECT_UNLOCK (playsink);
1936
1937 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)
1938 || (!playsink->colorbalance_element
1939 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE))) {
1940 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO);
1941 gboolean use_balance = !playsink->colorbalance_element
1942 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
1943
1944 GST_DEBUG_OBJECT (playsink, "creating videoconverter");
1945 chain->conv =
1946 g_object_new (GST_TYPE_PLAY_SINK_VIDEO_CONVERT, "name", "vconv",
1947 "use-converters", use_converters, "use-balance", use_balance, NULL);
1948
1949 GST_OBJECT_LOCK (playsink);
1950 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance) {
1951 playsink->colorbalance_element =
1952 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
1953 (chain->conv)->balance));
1954 playsink->colorbalance_value_changed_id =
1955 g_signal_connect (playsink->colorbalance_element, "value-changed",
1956 G_CALLBACK (colorbalance_value_changed_cb), playsink);
1957 }
1958 GST_OBJECT_UNLOCK (playsink);
1959
1960 gst_bin_add (bin, chain->conv);
1961 if (prev) {
1962 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
1963 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1964 goto link_failed;
1965 } else {
1966 head = chain->conv;
1967 }
1968 prev = chain->conv;
1969 }
1970
1971 update_colorbalance (playsink);
1972
1973 if (prev) {
1974 GST_DEBUG_OBJECT (playsink, "linking to sink");
1975 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
1976 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
1977 goto link_failed;
1978 }
1979
1980 pad = gst_element_get_static_pad (head, "sink");
1981 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
1982
1983 /* sending audio/video flushes break stream changes when the pipeline
1984 * is paused and played again in 0.10 */
1985 #if 0
1986 gst_pad_set_event_function (chain->sinkpad,
1987 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_event));
1988 gst_pad_set_chain_function (chain->sinkpad,
1989 GST_DEBUG_FUNCPTR (gst_play_sink_video_sink_chain));
1990 #endif
1991
1992 gst_object_unref (pad);
1993 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
1994
1995 return chain;
1996
1997 /* ERRORS */
1998 no_sinks:
1999 {
2000 if (!elem && !playsink->video_sink) {
2001 post_missing_element_message (playsink, "autovideosink");
2002 if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
2003 post_missing_element_message (playsink, DEFAULT_VIDEOSINK);
2004 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2005 (_("Both autovideosink and %s elements are missing."),
2006 DEFAULT_VIDEOSINK), (NULL));
2007 } else {
2008 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2009 (_("The autovideosink element is missing.")), (NULL));
2010 }
2011 } else {
2012 if (playsink->video_sink) {
2013 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2014 (_("Configured videosink %s is not working."),
2015 GST_ELEMENT_NAME (playsink->video_sink)), (NULL));
2016 } else if (strcmp (DEFAULT_VIDEOSINK, "autovideosink")) {
2017 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2018 (_("Both autovideosink and %s elements are not working."),
2019 DEFAULT_VIDEOSINK), (NULL));
2020 } else {
2021 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2022 (_("The autovideosink element is not working.")), (NULL));
2023 }
2024 }
2025 free_chain ((GstPlayChain *) chain);
2026 return NULL;
2027 }
2028
2029 link_failed:
2030 {
2031 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2032 (NULL), ("Failed to configure the video sink."));
2033 goto cleanup;
2034 }
2035 filter_with_nonraw:
2036 {
2037 GST_ELEMENT_ERROR (playsink, CORE, NEGOTIATION,
2038 (NULL), ("Cannot apply video-filter on non-raw stream"));
2039 goto cleanup;
2040 }
2041 cleanup:
2042 /* checking sink made it READY */
2043 gst_element_set_state (chain->sink, GST_STATE_NULL);
2044 /* Remove chain from the bin to allow reuse later */
2045 gst_bin_remove (bin, chain->sink);
2046 free_chain ((GstPlayChain *) chain);
2047 return NULL;
2048 }
2049
2050 static gboolean
setup_video_chain(GstPlaySink * playsink,gboolean raw,gboolean async)2051 setup_video_chain (GstPlaySink * playsink, gboolean raw, gboolean async)
2052 {
2053 GstElement *elem;
2054 GstPlayVideoChain *chain;
2055 GstStateChangeReturn ret;
2056
2057 chain = playsink->videochain;
2058
2059 /* if we have a filter, and raw-ness changed, we have to force a rebuild */
2060 if (chain->filter && chain->chain.raw != raw)
2061 return FALSE;
2062
2063 chain->chain.raw = raw;
2064
2065 /* if the chain was active we don't do anything */
2066 if (GST_PLAY_CHAIN (chain)->activated)
2067 return TRUE;
2068
2069 /* try to set the sink element to READY again */
2070 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
2071 if (ret == GST_STATE_CHANGE_FAILURE)
2072 return FALSE;
2073
2074 /* Get the VideoOverlay element */
2075 {
2076 GstVideoOverlay *overlay = NULL;
2077
2078 GST_OBJECT_LOCK (playsink);
2079 if (playsink->overlay_element)
2080 gst_object_unref (playsink->overlay_element);
2081 playsink->overlay_element =
2082 GST_VIDEO_OVERLAY (gst_bin_get_by_interface (GST_BIN (chain->chain.bin),
2083 GST_TYPE_VIDEO_OVERLAY));
2084 if (playsink->overlay_element)
2085 overlay = GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
2086 GST_OBJECT_UNLOCK (playsink);
2087
2088 if (overlay) {
2089 if (playsink->overlay_handle_set)
2090 gst_video_overlay_set_window_handle (overlay, playsink->overlay_handle);
2091 if (playsink->overlay_handle_events_set)
2092 gst_video_overlay_handle_events (overlay,
2093 playsink->overlay_handle_events);
2094 if (playsink->overlay_render_rectangle_set)
2095 gst_video_overlay_set_render_rectangle (overlay,
2096 playsink->overlay_x, playsink->overlay_y,
2097 playsink->overlay_width, playsink->overlay_height);
2098 gst_object_unref (overlay);
2099 }
2100 }
2101
2102 /* find ts-offset element */
2103 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2104 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2105 G_TYPE_INT64));
2106
2107 /* if we can disable async behaviour of the sink, we can avoid adding a
2108 * queue for the audio chain. */
2109 elem =
2110 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2111 G_TYPE_BOOLEAN);
2112 if (elem) {
2113 GST_DEBUG_OBJECT (playsink, "setting async property to %d on element %s",
2114 async, GST_ELEMENT_NAME (elem));
2115 g_object_set (elem, "async", async, NULL);
2116 chain->async = async;
2117 } else {
2118 GST_DEBUG_OBJECT (playsink, "no async property on the sink");
2119 chain->async = TRUE;
2120 }
2121
2122 /* Make sure the aspect ratio is kept */
2123 elem =
2124 gst_play_sink_find_property_sinks (playsink, chain->sink,
2125 "force-aspect-ratio", G_TYPE_BOOLEAN);
2126 if (elem)
2127 g_object_set (elem, "force-aspect-ratio", playsink->force_aspect_ratio,
2128 NULL);
2129
2130 GST_OBJECT_LOCK (playsink);
2131 if (playsink->colorbalance_element) {
2132 g_signal_handler_disconnect (playsink->colorbalance_element,
2133 playsink->colorbalance_value_changed_id);
2134 playsink->colorbalance_value_changed_id = 0;
2135 gst_object_unref (playsink->colorbalance_element);
2136 }
2137 playsink->colorbalance_element = find_color_balance_element (chain->sink);
2138 if (playsink->colorbalance_element) {
2139 playsink->colorbalance_value_changed_id =
2140 g_signal_connect (playsink->colorbalance_element, "value-changed",
2141 G_CALLBACK (colorbalance_value_changed_cb), playsink);
2142 }
2143 GST_OBJECT_UNLOCK (playsink);
2144
2145 if (chain->conv) {
2146 gboolean use_balance = !playsink->colorbalance_element
2147 && (playsink->flags & GST_PLAY_FLAG_SOFT_COLORBALANCE);
2148
2149 g_object_set (chain->conv, "use-balance", use_balance, NULL);
2150
2151 GST_OBJECT_LOCK (playsink);
2152 if (use_balance && GST_PLAY_SINK_VIDEO_CONVERT (chain->conv)->balance) {
2153 playsink->colorbalance_element =
2154 GST_COLOR_BALANCE (gst_object_ref (GST_PLAY_SINK_VIDEO_CONVERT
2155 (chain->conv)->balance));
2156 playsink->colorbalance_value_changed_id =
2157 g_signal_connect (playsink->colorbalance_element, "value-changed",
2158 G_CALLBACK (colorbalance_value_changed_cb), playsink);
2159 }
2160 GST_OBJECT_UNLOCK (playsink);
2161 }
2162
2163 update_colorbalance (playsink);
2164
2165 return TRUE;
2166 }
2167
2168 static gboolean
gst_play_sink_sink_event(GstPad * pad,GstObject * parent,GstEvent * event,const gchar * sink_type,gboolean * sink_ignore_wrong_state,gboolean * sink_custom_flush_finished,gboolean * sink_pending_flush)2169 gst_play_sink_sink_event (GstPad * pad, GstObject * parent, GstEvent * event,
2170 const gchar * sink_type,
2171 gboolean * sink_ignore_wrong_state,
2172 gboolean * sink_custom_flush_finished, gboolean * sink_pending_flush)
2173 {
2174 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2175 gboolean ret;
2176 const GstStructure *structure = gst_event_get_structure (event);
2177
2178 if (GST_EVENT_TYPE (event) == GST_EVENT_CUSTOM_DOWNSTREAM_OOB && structure) {
2179 gchar *custom_flush;
2180 gchar *custom_flush_finish;
2181
2182 custom_flush = g_strdup_printf ("playsink-custom-%s-flush", sink_type);
2183 custom_flush_finish =
2184 g_strdup_printf ("playsink-custom-%s-flush-finish", sink_type);
2185 if (strcmp (gst_structure_get_name (structure), custom_flush) == 0) {
2186 GST_DEBUG_OBJECT (pad,
2187 "Custom %s flush event received, marking to flush %s", sink_type,
2188 sink_type);
2189 GST_PLAY_SINK_LOCK (playsink);
2190 *sink_ignore_wrong_state = TRUE;
2191 *sink_custom_flush_finished = FALSE;
2192 GST_PLAY_SINK_UNLOCK (playsink);
2193 } else if (strcmp (gst_structure_get_name (structure),
2194 custom_flush_finish) == 0) {
2195 GST_DEBUG_OBJECT (pad, "Custom %s flush finish event received",
2196 sink_type);
2197 GST_PLAY_SINK_LOCK (playsink);
2198 *sink_pending_flush = TRUE;
2199 *sink_custom_flush_finished = TRUE;
2200 GST_PLAY_SINK_UNLOCK (playsink);
2201 }
2202
2203 g_free (custom_flush);
2204 g_free (custom_flush_finish);
2205 }
2206
2207 GST_DEBUG_OBJECT (pad, "Forwarding event %" GST_PTR_FORMAT, event);
2208 ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
2209
2210 gst_event_unref (event);
2211 gst_object_unref (playsink);
2212 return ret;
2213 }
2214
2215 static GstFlowReturn
gst_play_sink_sink_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer,const gchar * sink_type,gboolean * sink_ignore_wrong_state,gboolean * sink_custom_flush_finished,gboolean * sink_pending_flush)2216 gst_play_sink_sink_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer,
2217 const gchar * sink_type,
2218 gboolean * sink_ignore_wrong_state,
2219 gboolean * sink_custom_flush_finished, gboolean * sink_pending_flush)
2220 {
2221 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2222 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2223 GstFlowReturn ret;
2224
2225 GST_PLAY_SINK_LOCK (playsink);
2226
2227 if (*sink_pending_flush) {
2228 GstEvent *segment_event;
2229 GstEvent *event;
2230 GstStructure *structure;
2231
2232 *sink_pending_flush = FALSE;
2233
2234 GST_PLAY_SINK_UNLOCK (playsink);
2235
2236 segment_event = gst_pad_get_sticky_event (pad, GST_EVENT_SEGMENT, 0);
2237
2238 /* make the bin drop all cached data.
2239 * This event will be dropped on the src pad, if any. */
2240 event = gst_event_new_flush_start ();
2241 if (segment_event)
2242 gst_event_set_seqnum (event, gst_event_get_seqnum (segment_event));
2243 structure = gst_event_writable_structure (event);
2244 gst_structure_id_set (structure,
2245 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2246
2247 GST_DEBUG_OBJECT (pad,
2248 "Pushing %s flush-start event with reset segment marker set: %"
2249 GST_PTR_FORMAT, sink_type, event);
2250 gst_pad_send_event (pad, event);
2251
2252 /* make queue drop all cached data.
2253 * This event will be dropped on the src pad. */
2254 event = gst_event_new_flush_stop (TRUE);
2255 if (segment_event)
2256 gst_event_set_seqnum (event, gst_event_get_seqnum (segment_event));
2257 structure = gst_event_writable_structure (event);
2258 gst_structure_id_set (structure,
2259 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2260
2261 GST_DEBUG_OBJECT (pad,
2262 "Pushing %s flush-stop event with reset segment marker set: %"
2263 GST_PTR_FORMAT, sink_type, event);
2264 gst_pad_send_event (pad, event);
2265
2266 /* Re-sync queue segment info after flush-stop.
2267 * This event will be dropped on the src pad. */
2268 if (segment_event) {
2269 event = gst_event_copy (segment_event);
2270 structure = gst_event_writable_structure (event);
2271 gst_structure_id_set (structure,
2272 _playsink_reset_segment_event_marker_id, G_TYPE_BOOLEAN, TRUE, NULL);
2273
2274 GST_DEBUG_OBJECT (playsink,
2275 "Pushing segment event with reset "
2276 "segment marker set: %" GST_PTR_FORMAT, event);
2277 gst_pad_send_event (pad, event);
2278 gst_event_unref (segment_event);
2279 }
2280 } else {
2281 GST_PLAY_SINK_UNLOCK (playsink);
2282 }
2283
2284 ret = gst_proxy_pad_chain_default (pad, parent, buffer);
2285
2286 GST_PLAY_SINK_LOCK (playsink);
2287 if (ret == GST_FLOW_FLUSHING && *sink_ignore_wrong_state) {
2288 GST_DEBUG_OBJECT (pad, "Ignoring wrong state for %s during flush",
2289 sink_type);
2290 if (*sink_custom_flush_finished) {
2291 GST_DEBUG_OBJECT (pad, "Custom flush finished, stop ignoring "
2292 "wrong state for %s", sink_type);
2293 *sink_ignore_wrong_state = FALSE;
2294 }
2295
2296 ret = GST_FLOW_OK;
2297 }
2298 GST_PLAY_SINK_UNLOCK (playsink);
2299
2300 gst_object_unref (playsink);
2301 gst_object_unref (tbin);
2302 return ret;
2303 }
2304
2305 /* sending audio/video flushes break stream changes when the pipeline
2306 * is paused and played again in 0.10 */
2307 #if 0
2308 static gboolean
2309 gst_play_sink_video_sink_event (GstPad * pad, GstEvent * event)
2310 {
2311 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2312 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2313 gboolean ret;
2314
2315 ret = gst_play_sink_sink_event (pad, event, "video",
2316 &playsink->video_ignore_wrong_state,
2317 &playsink->video_custom_flush_finished,
2318 &playsink->video_pending_flush, &playsink->video_segment);
2319
2320 gst_object_unref (playsink);
2321 gst_object_unref (tbin);
2322 return ret;
2323 }
2324
2325 static GstFlowReturn
2326 gst_play_sink_video_sink_chain (GstPad * pad, GstBuffer * buffer)
2327 {
2328 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2329 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2330 gboolean ret;
2331
2332 ret = gst_play_sink_sink_chain (pad, buffer, "video",
2333 &playsink->video_ignore_wrong_state,
2334 &playsink->video_custom_flush_finished,
2335 &playsink->video_pending_flush, &playsink->video_segment);
2336
2337 gst_object_unref (playsink);
2338 gst_object_unref (tbin);
2339 return ret;
2340 }
2341
2342 static gboolean
2343 gst_play_sink_audio_sink_event (GstPad * pad, GstEvent * event)
2344 {
2345 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2346 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2347 gboolean ret;
2348
2349 ret = gst_play_sink_sink_event (pad, event, "audio",
2350 &playsink->audio_ignore_wrong_state,
2351 &playsink->audio_custom_flush_finished,
2352 &playsink->audio_pending_flush, &playsink->audio_segment);
2353
2354 gst_object_unref (playsink);
2355 gst_object_unref (tbin);
2356 return ret;
2357 }
2358
2359 static GstFlowReturn
2360 gst_play_sink_audio_sink_chain (GstPad * pad, GstBuffer * buffer)
2361 {
2362 GstBin *tbin = GST_BIN_CAST (gst_pad_get_parent (pad));
2363 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_pad_get_parent (tbin));
2364 gboolean ret;
2365
2366 ret = gst_play_sink_sink_chain (pad, buffer, "audio",
2367 &playsink->audio_ignore_wrong_state,
2368 &playsink->audio_custom_flush_finished,
2369 &playsink->audio_pending_flush, &playsink->audio_segment);
2370
2371 gst_object_unref (playsink);
2372 gst_object_unref (tbin);
2373 return ret;
2374 }
2375 #endif
2376
2377 static gboolean
gst_play_sink_text_sink_event(GstPad * pad,GstObject * parent,GstEvent * event)2378 gst_play_sink_text_sink_event (GstPad * pad, GstObject * parent,
2379 GstEvent * event)
2380 {
2381 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2382 gboolean ret;
2383
2384 ret = gst_play_sink_sink_event (pad, parent, event, "subtitle",
2385 &playsink->text_ignore_wrong_state,
2386 &playsink->text_custom_flush_finished, &playsink->text_pending_flush);
2387
2388 gst_object_unref (playsink);
2389
2390 return ret;
2391 }
2392
2393 static GstFlowReturn
gst_play_sink_text_sink_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)2394 gst_play_sink_text_sink_chain (GstPad * pad, GstObject * parent,
2395 GstBuffer * buffer)
2396 {
2397 gboolean ret;
2398 GstPlaySink *playsink = GST_PLAY_SINK_CAST (gst_object_get_parent (parent));
2399
2400 ret = gst_play_sink_sink_chain (pad, parent, buffer, "subtitle",
2401 &playsink->text_ignore_wrong_state,
2402 &playsink->text_custom_flush_finished, &playsink->text_pending_flush);
2403
2404 gst_object_unref (playsink);
2405 return ret;
2406 }
2407
2408 static gboolean
gst_play_sink_text_src_event(GstPad * pad,GstObject * parent,GstEvent * event)2409 gst_play_sink_text_src_event (GstPad * pad, GstObject * parent,
2410 GstEvent * event)
2411 {
2412 gboolean ret;
2413 const GstStructure *structure;
2414
2415 GST_DEBUG_OBJECT (pad, "Got event %" GST_PTR_FORMAT, event);
2416
2417 structure = gst_event_get_structure (event);
2418
2419 if (structure &&
2420 gst_structure_id_has_field (structure,
2421 _playsink_reset_segment_event_marker_id)) {
2422 /* the events marked with a reset segment marker
2423 * are sent internally to reset the queue and
2424 * must be dropped here */
2425 GST_DEBUG_OBJECT (pad, "Dropping event with reset "
2426 "segment marker set: %" GST_PTR_FORMAT, event);
2427 ret = TRUE;
2428 goto out;
2429 }
2430
2431 ret = gst_pad_event_default (pad, parent, gst_event_ref (event));
2432
2433 out:
2434 gst_event_unref (event);
2435 return ret;
2436 }
2437
2438 /* make an element for playback of video with subtitles embedded.
2439 * Only used for *raw* video streams.
2440 *
2441 * +--------------------------------------------+
2442 * | tbin |
2443 * | +--------+ +-----------------+ |
2444 * | | queue | | subtitleoverlay | |
2445 * video--src sink---video_sink | |
2446 * | +--------+ | src--src
2447 * text------------------text_sink | |
2448 * | +-----------------+ |
2449 * +--------------------------------------------+
2450 *
2451 */
2452 static GstPlayTextChain *
gen_text_chain(GstPlaySink * playsink)2453 gen_text_chain (GstPlaySink * playsink)
2454 {
2455 GstPlayTextChain *chain;
2456 GstBin *bin;
2457 GstElement *elem;
2458 GstPad *videosinkpad, *textsinkpad, *srcpad;
2459
2460 chain = g_new0 (GstPlayTextChain, 1);
2461 chain->chain.playsink = playsink;
2462
2463 GST_DEBUG_OBJECT (playsink, "making text chain %p", chain);
2464
2465 chain->chain.bin = gst_bin_new ("tbin");
2466 bin = GST_BIN_CAST (chain->chain.bin);
2467 gst_object_ref_sink (bin);
2468
2469 videosinkpad = textsinkpad = srcpad = NULL;
2470
2471 /* first try to hook the text pad to the custom sink */
2472 if (playsink->text_sink) {
2473 GST_DEBUG_OBJECT (playsink, "trying configured textsink");
2474 chain->sink = try_element (playsink, playsink->text_sink, FALSE);
2475 if (chain->sink) {
2476 elem =
2477 gst_play_sink_find_property_sinks (playsink, chain->sink, "async",
2478 G_TYPE_BOOLEAN);
2479 if (elem) {
2480 /* make sure the sparse subtitles don't participate in the preroll */
2481 g_object_set (elem, "async", FALSE, NULL);
2482 GST_DEBUG_OBJECT (playsink, "adding custom text sink");
2483 gst_bin_add (bin, chain->sink);
2484 /* NOTE streamsynchronizer needs streams decoupled */
2485 /* make a little queue */
2486 chain->queue = gst_element_factory_make ("queue", "subqueue");
2487 if (chain->queue == NULL) {
2488 post_missing_element_message (playsink, "queue");
2489 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2490 (_("Missing element '%s' - check your GStreamer installation."),
2491 "queue"), ("rendering might be suboptimal"));
2492 } else {
2493 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2494 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2495 "silent", TRUE, NULL);
2496 gst_bin_add (bin, chain->queue);
2497 }
2498 /* we have a custom sink, this will be our textsinkpad */
2499 if (gst_element_link_pads_full (chain->queue, "src", chain->sink,
2500 "sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2501 /* we're all fine now and we can add the sink to the chain */
2502 GST_DEBUG_OBJECT (playsink, "using custom text sink");
2503 textsinkpad = gst_element_get_static_pad (chain->queue, "sink");
2504 } else {
2505 GST_WARNING_OBJECT (playsink,
2506 "can't find a sink pad on custom text sink");
2507 gst_bin_remove (bin, chain->sink);
2508 gst_bin_remove (bin, chain->queue);
2509 chain->sink = NULL;
2510 chain->queue = NULL;
2511 }
2512 /* try to set sync to true but it's no biggie when we can't */
2513 if (chain->sink && (elem =
2514 gst_play_sink_find_property_sinks (playsink, chain->sink,
2515 "sync", G_TYPE_BOOLEAN)))
2516 g_object_set (elem, "sync", TRUE, NULL);
2517
2518 if (!textsinkpad)
2519 gst_bin_remove (bin, chain->sink);
2520 } else {
2521 GST_WARNING_OBJECT (playsink,
2522 "can't find async property in custom text sink");
2523 }
2524 }
2525 if (textsinkpad == NULL) {
2526 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2527 (_("Custom text sink element is not usable.")),
2528 ("fallback to default subtitleoverlay"));
2529 }
2530 }
2531
2532 if (textsinkpad == NULL) {
2533 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_VIDEO)) {
2534 /* make a little queue */
2535 chain->queue = gst_element_factory_make ("queue", "vqueue");
2536 if (chain->queue == NULL) {
2537 post_missing_element_message (playsink, "queue");
2538 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2539 (_("Missing element '%s' - check your GStreamer installation."),
2540 "queue"), ("video rendering might be suboptimal"));
2541 } else {
2542 g_object_set (G_OBJECT (chain->queue), "max-size-buffers", 3,
2543 "max-size-bytes", 0, "max-size-time", (gint64) 0,
2544 "silent", TRUE, NULL);
2545 gst_bin_add (bin, chain->queue);
2546 videosinkpad = gst_element_get_static_pad (chain->queue, "sink");
2547 }
2548
2549 chain->overlay =
2550 gst_element_factory_make ("subtitleoverlay", "suboverlay");
2551 if (chain->overlay == NULL) {
2552 post_missing_element_message (playsink, "subtitleoverlay");
2553 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2554 (_("Missing element '%s' - check your GStreamer installation."),
2555 "subtitleoverlay"), ("subtitle rendering disabled"));
2556 } else {
2557 GstElement *element;
2558
2559 gst_bin_add (bin, chain->overlay);
2560
2561 g_object_set (G_OBJECT (chain->overlay), "silent", FALSE, NULL);
2562 if (playsink->font_desc) {
2563 g_object_set (G_OBJECT (chain->overlay), "font-desc",
2564 playsink->font_desc, NULL);
2565 }
2566 if (playsink->subtitle_encoding) {
2567 g_object_set (G_OBJECT (chain->overlay), "subtitle-encoding",
2568 playsink->subtitle_encoding, NULL);
2569 }
2570
2571 gst_element_link_pads_full (chain->queue, "src", chain->overlay,
2572 "video_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS);
2573
2574 /* make another little queue to decouple streams */
2575 element = gst_element_factory_make ("queue", "subqueue");
2576 if (element == NULL) {
2577 post_missing_element_message (playsink, "queue");
2578 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2579 (_("Missing element '%s' - check your GStreamer installation."),
2580 "queue"), ("rendering might be suboptimal"));
2581 } else {
2582 g_object_set (G_OBJECT (element), "max-size-buffers", 3,
2583 "max-size-bytes", 0, "max-size-time", (gint64) GST_SECOND,
2584 "silent", TRUE, NULL);
2585 gst_bin_add (bin, element);
2586 if (gst_element_link_pads_full (element, "src", chain->overlay,
2587 "subtitle_sink", GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2588 textsinkpad = gst_element_get_static_pad (element, "sink");
2589 srcpad = gst_element_get_static_pad (chain->overlay, "src");
2590 } else {
2591 gst_bin_remove (bin, chain->sink);
2592 gst_bin_remove (bin, chain->overlay);
2593 chain->sink = NULL;
2594 chain->overlay = NULL;
2595 gst_object_unref (videosinkpad);
2596 videosinkpad = NULL;
2597 }
2598 }
2599 }
2600 }
2601 }
2602
2603 if (videosinkpad == NULL) {
2604 /* if we still don't have a videosink, we don't have an overlay. the only
2605 * thing we can do is insert an identity and ghost the src
2606 * and sink pads. */
2607 chain->identity = gst_element_factory_make ("identity", "tidentity");
2608 if (chain->identity == NULL) {
2609 post_missing_element_message (playsink, "identity");
2610 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2611 (_("Missing element '%s' - check your GStreamer installation."),
2612 "identity"), (NULL));
2613 } else {
2614 g_object_set (chain->identity, "signal-handoffs", FALSE, NULL);
2615 g_object_set (chain->identity, "silent", TRUE, NULL);
2616 gst_bin_add (bin, chain->identity);
2617 srcpad = gst_element_get_static_pad (chain->identity, "src");
2618 videosinkpad = gst_element_get_static_pad (chain->identity, "sink");
2619 }
2620 }
2621
2622 /* expose the ghostpads */
2623 if (videosinkpad) {
2624 chain->videosinkpad = gst_ghost_pad_new ("sink", videosinkpad);
2625 gst_object_unref (videosinkpad);
2626 gst_element_add_pad (chain->chain.bin, chain->videosinkpad);
2627 }
2628 if (textsinkpad) {
2629 chain->textsinkpad = gst_ghost_pad_new ("text_sink", textsinkpad);
2630 gst_object_unref (textsinkpad);
2631
2632 gst_pad_set_event_function (chain->textsinkpad,
2633 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_event));
2634 gst_pad_set_chain_function (chain->textsinkpad,
2635 GST_DEBUG_FUNCPTR (gst_play_sink_text_sink_chain));
2636
2637 gst_element_add_pad (chain->chain.bin, chain->textsinkpad);
2638 }
2639 if (srcpad) {
2640 chain->srcpad = gst_ghost_pad_new ("src", srcpad);
2641 gst_object_unref (srcpad);
2642
2643 gst_pad_set_event_function (chain->srcpad,
2644 GST_DEBUG_FUNCPTR (gst_play_sink_text_src_event));
2645
2646 gst_element_add_pad (chain->chain.bin, chain->srcpad);
2647 }
2648
2649 return chain;
2650 }
2651
2652 static void
notify_volume_cb(GObject * object,GParamSpec * pspec,GstPlaySink * playsink)2653 notify_volume_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2654 {
2655 gdouble vol;
2656
2657 g_object_get (object, "volume", &vol, NULL);
2658 playsink->volume = vol;
2659
2660 g_object_notify (G_OBJECT (playsink), "volume");
2661 }
2662
2663 static void
notify_mute_cb(GObject * object,GParamSpec * pspec,GstPlaySink * playsink)2664 notify_mute_cb (GObject * object, GParamSpec * pspec, GstPlaySink * playsink)
2665 {
2666 gboolean mute;
2667
2668 g_object_get (object, "mute", &mute, NULL);
2669 playsink->mute = mute;
2670
2671 g_object_notify (G_OBJECT (playsink), "mute");
2672 }
2673
2674 /* make the chain that contains the elements needed to perform
2675 * audio playback.
2676 *
2677 * We add a tee as the first element so that we can link the visualisation chain
2678 * to it when requested.
2679 *
2680 * +--------------------------------------------------------------+
2681 * | abin |
2682 * | +----------+ +--------+ +---------+ +-----------+ |
2683 * | | filter | | queue | | convbin | | audiosink | |
2684 * | +-sink src-sink src-sink src-sink | |
2685 * | | +----------+ +--------+ +---------+ +-----------+ |
2686 * sink-+ |
2687 * +--------------------------------------------------------------+
2688 */
2689 static GstPlayAudioChain *
gen_audio_chain(GstPlaySink * playsink,gboolean raw)2690 gen_audio_chain (GstPlaySink * playsink, gboolean raw)
2691 {
2692 GstPlayAudioChain *chain;
2693 GstBin *bin;
2694 gboolean have_volume;
2695 GstPad *pad;
2696 GstElement *head, *prev, *elem = NULL;
2697
2698 chain = g_new0 (GstPlayAudioChain, 1);
2699 chain->chain.playsink = playsink;
2700 chain->chain.raw = raw;
2701
2702 GST_DEBUG_OBJECT (playsink, "making audio chain %p", chain);
2703
2704 if (playsink->audio_sink) {
2705 GST_DEBUG_OBJECT (playsink, "trying configured audiosink %" GST_PTR_FORMAT,
2706 playsink->audio_sink);
2707 chain->sink = try_element (playsink, playsink->audio_sink, FALSE);
2708 } else {
2709 /* only try fallback if no specific sink was chosen */
2710 if (chain->sink == NULL) {
2711 GST_DEBUG_OBJECT (playsink, "trying autoaudiosink");
2712 elem = gst_element_factory_make ("autoaudiosink", "audiosink");
2713 chain->sink = try_element (playsink, elem, TRUE);
2714 }
2715 if (chain->sink == NULL) {
2716 /* if default sink from config.h is different then try it too */
2717 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2718 GST_DEBUG_OBJECT (playsink, "trying " DEFAULT_AUDIOSINK);
2719 elem = gst_element_factory_make (DEFAULT_AUDIOSINK, "audiosink");
2720 chain->sink = try_element (playsink, elem, TRUE);
2721 }
2722 }
2723 if (chain->sink)
2724 playsink->audio_sink = gst_object_ref (chain->sink);
2725 }
2726 if (chain->sink == NULL)
2727 goto no_sinks;
2728
2729 chain->chain.bin = gst_bin_new ("abin");
2730 bin = GST_BIN_CAST (chain->chain.bin);
2731 gst_object_ref_sink (bin);
2732 gst_bin_add (bin, chain->sink);
2733
2734 head = chain->sink;
2735 prev = NULL;
2736
2737 /* add the audio filter first, so everything is working with post-filter
2738 * samples */
2739 chain->filter = gst_play_sink_get_filter (playsink, GST_PLAY_SINK_TYPE_AUDIO);
2740 if (chain->filter) {
2741 if (!raw) {
2742 gst_object_unref (chain->filter);
2743 chain->filter = NULL;
2744
2745 if (playsink->flags & GST_PLAY_FLAG_FORCE_FILTERS) {
2746 goto filter_with_nonraw;
2747 } else {
2748 GST_DEBUG_OBJECT (playsink,
2749 "skipping audio filter since we're not raw");
2750 }
2751 } else {
2752 GST_DEBUG_OBJECT (playsink, "adding audio filter");
2753 chain->filter_conv =
2754 gst_element_factory_make ("audioconvert", "filter-convert");
2755 if (!chain->filter_conv) {
2756 post_missing_element_message (playsink, "audioconvert");
2757 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2758 (_("Missing element '%s' - check your GStreamer installation."),
2759 "audioconvert"),
2760 ("audio playback and visualizations might not work"));
2761 } else {
2762 gst_bin_add (bin, chain->filter_conv);
2763 head = prev = chain->filter_conv;
2764 }
2765
2766 gst_bin_add (bin, chain->filter);
2767 /* Bin takes a new reference because we sinked any
2768 * floating reference ourselves already */
2769 gst_object_unref (chain->filter);
2770 if (prev) {
2771 if (!gst_element_link_pads_full (prev, "src", chain->filter, NULL,
2772 GST_PAD_LINK_CHECK_TEMPLATE_CAPS)) {
2773 goto link_failed;
2774 }
2775 } else {
2776 head = chain->filter;
2777 }
2778 prev = chain->filter;
2779 }
2780 }
2781
2782 /* we have to add a queue when we need to decouple for the video sink in
2783 * visualisations and for streamsynchronizer */
2784 GST_DEBUG_OBJECT (playsink, "adding audio queue");
2785 chain->queue = gst_element_factory_make ("queue", "aqueue");
2786 if (chain->queue == NULL) {
2787 post_missing_element_message (playsink, "queue");
2788 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
2789 (_("Missing element '%s' - check your GStreamer installation."),
2790 "queue"), ("audio playback and visualizations might not work"));
2791 } else {
2792 g_object_set (chain->queue, "silent", TRUE, NULL);
2793 gst_bin_add (bin, chain->queue);
2794 if (prev) {
2795 if (!gst_element_link_pads_full (prev, "src", chain->queue, "sink",
2796 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2797 goto link_failed;
2798 } else {
2799 head = chain->queue;
2800 }
2801 prev = chain->queue;
2802 }
2803
2804 /* find ts-offset element */
2805 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
2806 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
2807 G_TYPE_INT64));
2808
2809 /* check if the sink, or something within the sink, implements the
2810 * streamvolume interface. If it does we don't need to add a volume element. */
2811 if (GST_IS_BIN (chain->sink))
2812 elem =
2813 gst_bin_get_by_interface (GST_BIN_CAST (chain->sink),
2814 GST_TYPE_STREAM_VOLUME);
2815 else if (GST_IS_STREAM_VOLUME (chain->sink))
2816 elem = gst_object_ref (chain->sink);
2817 else
2818 elem = NULL;
2819 chain->notify_volume_id = chain->notify_mute_id = 0;
2820 if (elem) {
2821 chain->volume = elem;
2822
2823 chain->notify_volume_id = g_signal_connect (chain->volume, "notify::volume",
2824 G_CALLBACK (notify_volume_cb), playsink);
2825
2826 GST_DEBUG_OBJECT (playsink, "the sink has a volume property");
2827 have_volume = TRUE;
2828 chain->sink_volume = TRUE;
2829 chain->notify_mute_id = g_signal_connect (chain->volume, "notify::mute",
2830 G_CALLBACK (notify_mute_cb), playsink);
2831 /* use the sink to control the volume and mute */
2832 if (playsink->volume_changed) {
2833 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
2834 playsink->volume_changed = FALSE;
2835 }
2836 if (playsink->mute_changed) {
2837 g_object_set (chain->volume, "mute", playsink->mute, NULL);
2838 playsink->mute_changed = FALSE;
2839 }
2840 } else {
2841 /* no volume, we need to add a volume element when we can */
2842 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
2843 have_volume = FALSE;
2844 chain->sink_volume = FALSE;
2845 }
2846
2847 if (!(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO) || (!have_volume
2848 && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME))) {
2849 gboolean use_converters = !(playsink->flags & GST_PLAY_FLAG_NATIVE_AUDIO);
2850 gboolean use_volume =
2851 !have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME);
2852 GST_DEBUG_OBJECT (playsink,
2853 "creating audioconvert with use-converters %d, use-volume %d",
2854 use_converters, use_volume);
2855 chain->conv =
2856 g_object_new (GST_TYPE_PLAY_SINK_AUDIO_CONVERT, "name", "aconv",
2857 "use-converters", use_converters, "use-volume", use_volume, NULL);
2858 gst_bin_add (bin, chain->conv);
2859 if (prev) {
2860 if (!gst_element_link_pads_full (prev, "src", chain->conv, "sink",
2861 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2862 goto link_failed;
2863 } else {
2864 head = chain->conv;
2865 }
2866 prev = chain->conv;
2867
2868 if (!have_volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
2869 GstPlaySinkAudioConvert *conv =
2870 GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2871
2872 if (conv->volume) {
2873 chain->volume = conv->volume;
2874 have_volume = TRUE;
2875
2876 chain->notify_volume_id =
2877 g_signal_connect (chain->volume, "notify::volume",
2878 G_CALLBACK (notify_volume_cb), playsink);
2879
2880 /* volume also has the mute property */
2881 chain->notify_mute_id = g_signal_connect (chain->volume, "notify::mute",
2882 G_CALLBACK (notify_mute_cb), playsink);
2883
2884 /* configure with the latest volume and mute */
2885 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume,
2886 NULL);
2887 g_object_set (G_OBJECT (chain->volume), "mute", playsink->mute, NULL);
2888 }
2889 }
2890 }
2891
2892 if (prev) {
2893 /* we only have to link to the previous element if we have something in
2894 * front of the sink */
2895 GST_DEBUG_OBJECT (playsink, "linking to sink");
2896 if (!gst_element_link_pads_full (prev, "src", chain->sink, NULL,
2897 GST_PAD_LINK_CHECK_TEMPLATE_CAPS))
2898 goto link_failed;
2899 }
2900
2901 /* post a warning if we have no way to configure the volume */
2902 if (!have_volume) {
2903 GST_ELEMENT_WARNING (playsink, STREAM, NOT_IMPLEMENTED,
2904 (_("No volume control found")), ("Volume/mute is not available"));
2905 }
2906
2907 /* and ghost the sinkpad of the headmost element */
2908 GST_DEBUG_OBJECT (playsink, "ghosting sink pad");
2909 pad = gst_element_get_static_pad (head, "sink");
2910 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
2911
2912 /* sending audio/video flushes break stream changes when the pipeline
2913 * is paused and played again in 0.10 */
2914 #if 0
2915 gst_pad_set_event_function (chain->sinkpad,
2916 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_event));
2917 gst_pad_set_chain_function (chain->sinkpad,
2918 GST_DEBUG_FUNCPTR (gst_play_sink_audio_sink_chain));
2919 #endif
2920
2921 gst_object_unref (pad);
2922 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
2923
2924 return chain;
2925
2926 /* ERRORS */
2927 no_sinks:
2928 {
2929 if (!elem && !playsink->audio_sink) {
2930 post_missing_element_message (playsink, "autoaudiosink");
2931 if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2932 post_missing_element_message (playsink, DEFAULT_AUDIOSINK);
2933 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2934 (_("Both autoaudiosink and %s elements are missing."),
2935 DEFAULT_AUDIOSINK), (NULL));
2936 } else {
2937 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
2938 (_("The autoaudiosink element is missing.")), (NULL));
2939 }
2940 } else {
2941 if (playsink->audio_sink) {
2942 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2943 (_("Configured audiosink %s is not working."),
2944 GST_ELEMENT_NAME (playsink->audio_sink)), (NULL));
2945 } else if (strcmp (DEFAULT_AUDIOSINK, "autoaudiosink")) {
2946 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2947 (_("Both autoaudiosink and %s elements are not working."),
2948 DEFAULT_AUDIOSINK), (NULL));
2949 } else {
2950 GST_ELEMENT_ERROR (playsink, CORE, STATE_CHANGE,
2951 (_("The autoaudiosink element is not working.")), (NULL));
2952 }
2953 }
2954 free_chain ((GstPlayChain *) chain);
2955 return NULL;
2956 }
2957 link_failed:
2958 {
2959 GST_ELEMENT_ERROR (playsink, CORE, PAD,
2960 (NULL), ("Failed to configure the audio sink."));
2961 goto cleanup;
2962 }
2963 filter_with_nonraw:
2964 {
2965 GST_ELEMENT_ERROR (playsink, CORE, NEGOTIATION,
2966 (NULL), ("Cannot apply video-filter on non-raw stream"));
2967 goto cleanup;
2968 }
2969 cleanup:
2970 /* checking sink made it READY */
2971 gst_element_set_state (chain->sink, GST_STATE_NULL);
2972 /* Remove chain from the bin to allow reuse later */
2973 gst_bin_remove (bin, chain->sink);
2974 free_chain ((GstPlayChain *) chain);
2975 return NULL;
2976 }
2977
2978 static gboolean
setup_audio_chain(GstPlaySink * playsink,gboolean raw)2979 setup_audio_chain (GstPlaySink * playsink, gboolean raw)
2980 {
2981 GstElement *elem;
2982 GstPlayAudioChain *chain;
2983 GstStateChangeReturn ret;
2984 GstPlaySinkAudioConvert *conv;
2985
2986 chain = playsink->audiochain;
2987 conv = GST_PLAY_SINK_AUDIO_CONVERT_CAST (chain->conv);
2988
2989 /* if we have a filter, and raw-ness changed, we have to force a rebuild */
2990 if (chain->filter && chain->chain.raw != raw)
2991 return FALSE;
2992
2993 chain->chain.raw = raw;
2994
2995 /* if the chain was active we don't do anything */
2996 if (GST_PLAY_CHAIN (chain)->activated)
2997 return TRUE;
2998
2999 /* try to set the sink element to READY again */
3000 ret = gst_element_set_state (chain->sink, GST_STATE_READY);
3001 if (ret == GST_STATE_CHANGE_FAILURE)
3002 return FALSE;
3003
3004 /* find ts-offset element */
3005 gst_object_replace ((GstObject **) & chain->ts_offset, (GstObject *)
3006 gst_play_sink_find_property_sinks (playsink, chain->sink, "ts-offset",
3007 G_TYPE_INT64));
3008
3009 /* Disconnect signals */
3010 disconnect_audio_chain (chain, playsink);
3011
3012 /* check if the sink, or something within the sink, implements the
3013 * streamvolume interface. If it does we don't need to add a volume element. */
3014 if (GST_IS_BIN (chain->sink))
3015 elem =
3016 gst_bin_get_by_interface (GST_BIN_CAST (chain->sink),
3017 GST_TYPE_STREAM_VOLUME);
3018 else if (GST_IS_STREAM_VOLUME (chain->sink))
3019 elem = gst_object_ref (chain->sink);
3020 else
3021 elem = NULL;
3022 if (elem) {
3023 chain->volume = elem;
3024
3025 if (playsink->volume_changed) {
3026 GST_DEBUG_OBJECT (playsink, "the sink has a volume property, setting %f",
3027 playsink->volume);
3028 /* use the sink to control the volume */
3029 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
3030 playsink->volume_changed = FALSE;
3031 }
3032
3033 chain->notify_volume_id = g_signal_connect (chain->volume, "notify::volume",
3034 G_CALLBACK (notify_volume_cb), playsink);
3035 chain->notify_mute_id = g_signal_connect (chain->volume, "notify::mute",
3036 G_CALLBACK (notify_mute_cb), playsink);
3037 g_object_set (chain->volume, "mute", playsink->mute, NULL);
3038 playsink->mute_changed = FALSE;
3039
3040 g_object_set (chain->conv, "use-volume", FALSE, NULL);
3041 } else if (conv) {
3042 /* no volume, we need to add a volume element when we can */
3043 g_object_set (chain->conv, "use-volume",
3044 ! !(playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME), NULL);
3045 GST_DEBUG_OBJECT (playsink, "the sink has no volume property");
3046
3047 if (conv->volume && (playsink->flags & GST_PLAY_FLAG_SOFT_VOLUME)) {
3048 chain->volume = conv->volume;
3049
3050 chain->notify_volume_id =
3051 g_signal_connect (chain->volume, "notify::volume",
3052 G_CALLBACK (notify_volume_cb), playsink);
3053
3054 chain->notify_mute_id = g_signal_connect (chain->volume, "notify::mute",
3055 G_CALLBACK (notify_mute_cb), playsink);
3056
3057 /* configure with the latest volume and mute */
3058 g_object_set (G_OBJECT (chain->volume), "volume", playsink->volume, NULL);
3059 g_object_set (G_OBJECT (chain->volume), "mute", playsink->mute, NULL);
3060 }
3061
3062 GST_DEBUG_OBJECT (playsink, "reusing existing volume element");
3063 }
3064 return TRUE;
3065 }
3066
3067 /*
3068 * +-------------------------------------------------------------------+
3069 * | visbin |
3070 * | +----------+ +------------+ +----------+ +-------+ |
3071 * | | visqueue | | audioconv | | audiores | | vis | |
3072 * | +-sink src-sink + samp src-sink src-sink src-+ |
3073 * | | +----------+ +------------+ +----------+ +-------+ | |
3074 * sink-+ +-src
3075 * +-------------------------------------------------------------------+
3076 *
3077 */
3078 static GstPlayVisChain *
gen_vis_chain(GstPlaySink * playsink)3079 gen_vis_chain (GstPlaySink * playsink)
3080 {
3081 GstPlayVisChain *chain;
3082 GstBin *bin;
3083 gboolean res;
3084 GstPad *pad;
3085 GstElement *elem;
3086
3087 chain = g_new0 (GstPlayVisChain, 1);
3088 chain->chain.playsink = playsink;
3089
3090 GST_DEBUG_OBJECT (playsink, "making vis chain %p", chain);
3091
3092 chain->chain.bin = gst_bin_new ("visbin");
3093 bin = GST_BIN_CAST (chain->chain.bin);
3094 gst_object_ref_sink (bin);
3095
3096 /* we're queuing raw audio here, we can remove this queue when we can disable
3097 * async behaviour in the video sink. */
3098 chain->queue = gst_element_factory_make ("queue", "visqueue");
3099 if (chain->queue == NULL)
3100 goto no_queue;
3101 g_object_set (chain->queue, "silent", TRUE, NULL);
3102 gst_bin_add (bin, chain->queue);
3103
3104 chain->conv = gst_element_factory_make ("audioconvert", "aconv");
3105 if (chain->conv == NULL)
3106 goto no_audioconvert;
3107 gst_bin_add (bin, chain->conv);
3108
3109 chain->resample = gst_element_factory_make ("audioresample", "aresample");
3110 if (chain->resample == NULL)
3111 goto no_audioresample;
3112 gst_bin_add (bin, chain->resample);
3113
3114 /* this pad will be used for blocking the dataflow and switching the vis
3115 * plugin, we block right after the queue, this makes it possible for the
3116 * resample and convert to convert to a format supported by the new vis
3117 * plugin */
3118 chain->blockpad = gst_element_get_static_pad (chain->queue, "src");
3119 /* this is the pad where the vis is linked to */
3120 chain->vispeerpad = gst_element_get_static_pad (chain->resample, "src");
3121
3122 if (playsink->visualisation) {
3123 GST_DEBUG_OBJECT (playsink, "trying configure vis");
3124 chain->vis = try_element (playsink, playsink->visualisation, FALSE);
3125 }
3126 if (chain->vis == NULL) {
3127 GST_DEBUG_OBJECT (playsink, "trying goom");
3128 elem = gst_element_factory_make ("goom", "vis");
3129 chain->vis = try_element (playsink, elem, TRUE);
3130 gst_object_replace ((GstObject **) & playsink->visualisation,
3131 (GstObject *) elem);
3132 }
3133 if (chain->vis == NULL)
3134 goto no_goom;
3135
3136 gst_bin_add (bin, chain->vis);
3137
3138 res = gst_element_link_pads_full (chain->queue, "src", chain->conv, "sink",
3139 GST_PAD_LINK_CHECK_NOTHING);
3140 res &=
3141 gst_element_link_pads_full (chain->conv, "src", chain->resample, "sink",
3142 GST_PAD_LINK_CHECK_NOTHING);
3143 res &=
3144 gst_element_link_pads_full (chain->resample, "src", chain->vis, "sink",
3145 GST_PAD_LINK_CHECK_NOTHING);
3146 if (!res)
3147 goto link_failed;
3148
3149 chain->vissinkpad = gst_element_get_static_pad (chain->vis, "sink");
3150 chain->vissrcpad = gst_element_get_static_pad (chain->vis, "src");
3151
3152 pad = gst_element_get_static_pad (chain->queue, "sink");
3153 chain->sinkpad = gst_ghost_pad_new ("sink", pad);
3154 gst_object_unref (pad);
3155 gst_element_add_pad (chain->chain.bin, chain->sinkpad);
3156
3157 chain->srcpad = gst_ghost_pad_new ("src", chain->vissrcpad);
3158 gst_element_add_pad (chain->chain.bin, chain->srcpad);
3159
3160 return chain;
3161
3162 /* ERRORS */
3163 no_queue:
3164 {
3165 post_missing_element_message (playsink, "queue");
3166 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3167 (_("Missing element '%s' - check your GStreamer installation."),
3168 "queue"), (NULL));
3169 free_chain ((GstPlayChain *) chain);
3170 return NULL;
3171 }
3172 no_audioconvert:
3173 {
3174 post_missing_element_message (playsink, "audioconvert");
3175 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3176 (_("Missing element '%s' - check your GStreamer installation."),
3177 "audioconvert"), ("make sure audioconvert isn't blacklisted"));
3178 free_chain ((GstPlayChain *) chain);
3179 return NULL;
3180 }
3181 no_audioresample:
3182 {
3183 post_missing_element_message (playsink, "audioresample");
3184 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3185 (_("Missing element '%s' - check your GStreamer installation."),
3186 "audioresample"), (NULL));
3187 free_chain ((GstPlayChain *) chain);
3188 return NULL;
3189 }
3190 no_goom:
3191 {
3192 post_missing_element_message (playsink, "goom");
3193 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
3194 (_("Missing element '%s' - check your GStreamer installation."),
3195 "goom"), (NULL));
3196 free_chain ((GstPlayChain *) chain);
3197 return NULL;
3198 }
3199 link_failed:
3200 {
3201 GST_ELEMENT_ERROR (playsink, CORE, PAD,
3202 (NULL), ("Failed to configure the visualisation element."));
3203 /* element made it to READY */
3204 gst_element_set_state (chain->vis, GST_STATE_NULL);
3205 free_chain ((GstPlayChain *) chain);
3206 return NULL;
3207 }
3208 }
3209
3210 /* this function is called when all the request pads are requested and when we
3211 * have to construct the final pipeline. Based on the flags we construct the
3212 * final output pipelines.
3213 */
3214 static gboolean
gst_play_sink_do_reconfigure(GstPlaySink * playsink)3215 gst_play_sink_do_reconfigure (GstPlaySink * playsink)
3216 {
3217 GstPlayFlags flags;
3218 gboolean need_audio, need_video, need_deinterlace, need_vis, need_text;
3219
3220 GST_DEBUG_OBJECT (playsink, "reconfiguring");
3221
3222 /* assume we need nothing */
3223 need_audio = need_video = need_deinterlace = need_vis = need_text = FALSE;
3224
3225 GST_PLAY_SINK_LOCK (playsink);
3226 GST_OBJECT_LOCK (playsink);
3227 /* get flags, there are protected with the object lock */
3228 flags = playsink->flags;
3229 GST_OBJECT_UNLOCK (playsink);
3230
3231 /* figure out which components we need */
3232 if (flags & GST_PLAY_FLAG_TEXT && playsink->text_pad) {
3233 /* we have subtitles and we are requested to show it */
3234 need_text = TRUE;
3235 }
3236
3237 if (playsink->video_pad) {
3238 playsink->video_pad_raw = is_raw_pad (playsink->video_pad);
3239 GST_DEBUG_OBJECT (playsink, "Video pad is raw: %d",
3240 playsink->video_pad_raw);
3241 }
3242
3243 if (playsink->audio_pad) {
3244 playsink->audio_pad_raw = is_raw_pad (playsink->audio_pad);
3245 GST_DEBUG_OBJECT (playsink, "Audio pad is raw: %d",
3246 playsink->audio_pad_raw);
3247 }
3248
3249
3250 if (((flags & GST_PLAY_FLAG_VIDEO)
3251 || (flags & GST_PLAY_FLAG_NATIVE_VIDEO)) && playsink->video_pad) {
3252 /* we have video and we are requested to show it */
3253 need_video = TRUE;
3254
3255 /* we only deinterlace if native video is not requested and
3256 * we have raw video */
3257 if ((flags & GST_PLAY_FLAG_DEINTERLACE)
3258 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO) && playsink->video_pad_raw)
3259 need_deinterlace = TRUE;
3260 }
3261
3262 if (playsink->audio_pad) {
3263 if ((flags & GST_PLAY_FLAG_AUDIO) || (flags & GST_PLAY_FLAG_NATIVE_AUDIO)) {
3264 need_audio = TRUE;
3265 }
3266 if (playsink->audio_pad_raw) {
3267 /* only can do vis with raw uncompressed audio */
3268 if (flags & GST_PLAY_FLAG_VIS && !need_video) {
3269 /* also add video when we add visualisation */
3270 need_video = TRUE;
3271 need_vis = TRUE;
3272 }
3273 }
3274 }
3275
3276 /* we have a text_pad and we need text rendering, in this case we need a
3277 * video_pad to combine the video with the text or visualizations */
3278 if (need_text && !need_video && !playsink->text_sink) {
3279 if (playsink->video_pad) {
3280 need_video = TRUE;
3281 } else if (need_audio) {
3282 GST_ELEMENT_WARNING (playsink, STREAM, FORMAT,
3283 (_("Can't play a text file without video or visualizations.")),
3284 ("Have text pad but no video pad or visualizations"));
3285 need_text = FALSE;
3286 } else {
3287 GST_ELEMENT_ERROR (playsink, STREAM, FORMAT,
3288 (_("Can't play a text file without video or visualizations.")),
3289 ("Have text pad but no video pad or visualizations"));
3290 GST_PLAY_SINK_UNLOCK (playsink);
3291 return FALSE;
3292 }
3293 }
3294
3295 GST_DEBUG_OBJECT (playsink, "audio:%d, video:%d, vis:%d, text:%d", need_audio,
3296 need_video, need_vis, need_text);
3297
3298 /* set up video pipeline */
3299 if (need_video) {
3300 gboolean raw, async;
3301
3302 /* we need a raw sink when we do vis or when we have a raw pad */
3303 raw = need_vis ? TRUE : playsink->video_pad_raw;
3304 /* we try to set the sink async=FALSE when we need vis, this way we can
3305 * avoid a queue in the audio chain. */
3306 async = !need_vis;
3307
3308 GST_DEBUG_OBJECT (playsink, "adding video, raw %d",
3309 playsink->video_pad_raw);
3310
3311 if (playsink->videochain) {
3312 /* try to reactivate the chain */
3313 if ((playsink->video_sink
3314 && playsink->video_sink != playsink->videochain->sink)
3315 || (playsink->video_filter
3316 && playsink->video_filter != playsink->videochain->filter)
3317 || !setup_video_chain (playsink, raw, async)) {
3318 if (playsink->video_sinkpad_stream_synchronizer) {
3319 gst_element_release_request_pad (GST_ELEMENT_CAST
3320 (playsink->stream_synchronizer),
3321 playsink->video_sinkpad_stream_synchronizer);
3322 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3323 playsink->video_sinkpad_stream_synchronizer = NULL;
3324 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3325 playsink->video_srcpad_stream_synchronizer = NULL;
3326 }
3327
3328 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3329
3330 /* Remove the sink from the bin to keep its state
3331 * and unparent it to allow reuse */
3332 if (playsink->videochain->sink) {
3333 if (playsink->videochain->sink != playsink->video_sink)
3334 gst_element_set_state (playsink->videochain->sink, GST_STATE_NULL);
3335 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3336 playsink->videochain->sink);
3337 }
3338
3339 /* Remove the filter from the bin to keep its state
3340 * and unparent it to allow reuse */
3341 if (playsink->videochain->filter) {
3342 if (playsink->videochain->filter != playsink->video_filter)
3343 gst_element_set_state (playsink->videochain->filter,
3344 GST_STATE_NULL);
3345 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
3346 playsink->videochain->filter);
3347 }
3348
3349 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3350 free_chain ((GstPlayChain *) playsink->videochain);
3351 playsink->videochain = NULL;
3352
3353 GST_OBJECT_LOCK (playsink);
3354 if (playsink->overlay_element)
3355 gst_object_unref (playsink->overlay_element);
3356 playsink->overlay_element = NULL;
3357
3358 if (playsink->colorbalance_element) {
3359 g_signal_handler_disconnect (playsink->colorbalance_element,
3360 playsink->colorbalance_value_changed_id);
3361 playsink->colorbalance_value_changed_id = 0;
3362 gst_object_unref (playsink->colorbalance_element);
3363 }
3364 playsink->colorbalance_element = NULL;
3365 GST_OBJECT_UNLOCK (playsink);
3366 }
3367 }
3368
3369 if (!playsink->videochain)
3370 playsink->videochain = gen_video_chain (playsink, raw, async);
3371 if (!playsink->videochain)
3372 goto no_chain;
3373
3374 if (!playsink->video_sinkpad_stream_synchronizer) {
3375 GValue item = { 0, };
3376 GstIterator *it;
3377
3378 playsink->video_sinkpad_stream_synchronizer =
3379 gst_element_request_pad_simple (GST_ELEMENT_CAST
3380 (playsink->stream_synchronizer), "sink_%u");
3381 it = gst_pad_iterate_internal_links
3382 (playsink->video_sinkpad_stream_synchronizer);
3383 g_assert (it);
3384 gst_iterator_next (it, &item);
3385 playsink->video_srcpad_stream_synchronizer = g_value_dup_object (&item);
3386 g_value_unset (&item);
3387 g_assert (playsink->video_srcpad_stream_synchronizer);
3388 gst_iterator_free (it);
3389 }
3390
3391 if (playsink->video_pad)
3392 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3393 playsink->video_sinkpad_stream_synchronizer);
3394
3395 if (need_deinterlace) {
3396 if (!playsink->videodeinterlacechain)
3397 playsink->videodeinterlacechain =
3398 gen_video_deinterlace_chain (playsink);
3399 if (!playsink->videodeinterlacechain)
3400 goto no_chain;
3401
3402 GST_DEBUG_OBJECT (playsink, "adding video deinterlace chain");
3403
3404 GST_DEBUG_OBJECT (playsink, "setting up deinterlacing chain");
3405
3406 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3407 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), TRUE);
3408
3409 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3410 playsink->videochain->sinkpad);
3411 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3412 playsink->videodeinterlacechain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3413 } else {
3414 if (playsink->videodeinterlacechain) {
3415 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3416 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
3417 FALSE);
3418 }
3419 }
3420
3421 GST_DEBUG_OBJECT (playsink, "adding video chain");
3422 add_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3423 activate_chain (GST_PLAY_CHAIN (playsink->videochain), TRUE);
3424 /* if we are not part of vis or subtitles, set the ghostpad target */
3425 if (!need_vis && !need_text && (!playsink->textchain
3426 || !playsink->text_pad)) {
3427 GstPad *old_sink_peer = gst_pad_get_peer (playsink->videochain->sinkpad);
3428 GstPad *new_peer = NULL;
3429
3430 GST_DEBUG_OBJECT (playsink, "ghosting video sinkpad");
3431 if (need_deinterlace)
3432 new_peer = playsink->videodeinterlacechain->srcpad;
3433 else
3434 new_peer = playsink->video_srcpad_stream_synchronizer;
3435
3436 if (old_sink_peer != new_peer) {
3437 /* Make sure the srcpad we're linking to is unlinked. This may
3438 * leave a deinterlace or text overlay unlinked and lying around,
3439 * but that will be cleaned up below */
3440 GstPad *old_src_peer = gst_pad_get_peer (new_peer);
3441 if (old_src_peer != NULL) {
3442 gst_pad_unlink (new_peer, old_src_peer);
3443 gst_clear_object (&old_src_peer);
3444 }
3445
3446 /* And that the pad we're linking to is unlinked */
3447 if (old_sink_peer != NULL)
3448 gst_pad_unlink (old_sink_peer, playsink->videochain->sinkpad);
3449
3450 if (!GST_PAD_LINK_SUCCESSFUL (gst_pad_link_full (new_peer,
3451 playsink->videochain->sinkpad,
3452 GST_PAD_LINK_CHECK_NOTHING))) {
3453 if (need_deinterlace) {
3454 GST_WARNING_OBJECT (playsink,
3455 "Failed to link deinterlace srcpad to video sinkpad");
3456 } else {
3457 GST_WARNING_OBJECT (playsink,
3458 "Failed to link stream synchronizer srcpad to video sinkpad");
3459 }
3460 }
3461 }
3462
3463 gst_clear_object (&old_sink_peer);
3464 }
3465 } else {
3466 GST_DEBUG_OBJECT (playsink, "no video needed");
3467 if (playsink->videochain) {
3468 GST_DEBUG_OBJECT (playsink, "removing video chain");
3469 if (playsink->vischain) {
3470 GstPad *srcpad;
3471
3472 GST_DEBUG_OBJECT (playsink, "unlinking vis chain");
3473
3474 /* also had visualisation, release the tee srcpad before we then
3475 * unlink the video from it */
3476 if (playsink->audio_tee_vissrc) {
3477 gst_element_release_request_pad (playsink->audio_tee,
3478 playsink->audio_tee_vissrc);
3479 gst_object_unref (playsink->audio_tee_vissrc);
3480 playsink->audio_tee_vissrc = NULL;
3481 }
3482 srcpad =
3483 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3484 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3485 }
3486
3487 if (playsink->video_sinkpad_stream_synchronizer) {
3488 gst_element_release_request_pad (GST_ELEMENT_CAST
3489 (playsink->stream_synchronizer),
3490 playsink->video_sinkpad_stream_synchronizer);
3491 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3492 playsink->video_sinkpad_stream_synchronizer = NULL;
3493 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3494 playsink->video_srcpad_stream_synchronizer = NULL;
3495 }
3496
3497 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3498 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
3499 if (playsink->videochain->ts_offset)
3500 gst_object_unref (playsink->videochain->ts_offset);
3501 playsink->videochain->ts_offset = NULL;
3502 }
3503
3504 if (playsink->videodeinterlacechain) {
3505 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3506 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
3507 }
3508
3509 if (playsink->video_pad)
3510 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad), NULL);
3511
3512 GST_OBJECT_LOCK (playsink);
3513 if (playsink->overlay_element)
3514 gst_object_unref (playsink->overlay_element);
3515 playsink->overlay_element = NULL;
3516
3517 if (playsink->colorbalance_element) {
3518 g_signal_handler_disconnect (playsink->colorbalance_element,
3519 playsink->colorbalance_value_changed_id);
3520 playsink->colorbalance_value_changed_id = 0;
3521 gst_object_unref (playsink->colorbalance_element);
3522 }
3523 playsink->colorbalance_element = NULL;
3524 GST_OBJECT_UNLOCK (playsink);
3525
3526 #ifdef OHOS_OPT_COMPAT
3527 /* ohos.opt.compat.0023
3528 When playing pure audio, we will also connect the audio plug-in to video_Sink,
3529 so when the audio is changed to codec, it will turn here Set audio_sink to null */
3530 if (playsink->video_sink && (playsink->video_sink != playsink->audio_sink))
3531 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3532 #else
3533 if (playsink->video_sink)
3534 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
3535 #endif
3536
3537 if (playsink->video_filter)
3538 gst_element_set_state (playsink->video_filter, GST_STATE_NULL);
3539 }
3540
3541 if (need_audio) {
3542 gboolean raw;
3543
3544 GST_DEBUG_OBJECT (playsink, "adding audio");
3545
3546 /* get a raw sink if we are asked for a raw pad */
3547 raw = playsink->audio_pad_raw;
3548
3549 if (playsink->audiochain) {
3550 /* try to reactivate the chain */
3551 if ((playsink->audio_sink
3552 && playsink->audio_sink != playsink->audiochain->sink)
3553 || (playsink->audio_filter
3554 && playsink->audio_filter != playsink->audiochain->filter)
3555 || !setup_audio_chain (playsink, raw)) {
3556 GST_DEBUG_OBJECT (playsink, "removing current audio chain");
3557 if (playsink->audio_tee_asrc) {
3558 gst_element_release_request_pad (playsink->audio_tee,
3559 playsink->audio_tee_asrc);
3560 gst_object_unref (playsink->audio_tee_asrc);
3561 playsink->audio_tee_asrc = NULL;
3562 }
3563
3564 if (playsink->audio_sinkpad_stream_synchronizer) {
3565 gst_element_release_request_pad (GST_ELEMENT_CAST
3566 (playsink->stream_synchronizer),
3567 playsink->audio_sinkpad_stream_synchronizer);
3568 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3569 playsink->audio_sinkpad_stream_synchronizer = NULL;
3570 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3571 playsink->audio_srcpad_stream_synchronizer = NULL;
3572
3573 gst_play_sink_remove_audio_ssync_queue (playsink);
3574 }
3575
3576 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3577
3578 /* Remove the sink from the bin to keep its state
3579 * and unparent it to allow reuse */
3580 if (playsink->audiochain->sink) {
3581 if (playsink->audiochain->sink != playsink->audio_sink)
3582 gst_element_set_state (playsink->audiochain->sink, GST_STATE_NULL);
3583 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3584 playsink->audiochain->sink);
3585 }
3586
3587 /* Remove the filter from the bin to keep its state
3588 * and unparent it to allow reuse */
3589 if (playsink->audiochain->filter) {
3590 if (playsink->audiochain->filter != playsink->audio_filter)
3591 gst_element_set_state (playsink->audiochain->filter,
3592 GST_STATE_NULL);
3593 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
3594 playsink->audiochain->filter);
3595 }
3596
3597 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3598 disconnect_audio_chain (playsink->audiochain, playsink);
3599 if (playsink->audiochain->volume)
3600 gst_object_unref (playsink->audiochain->volume);
3601 playsink->audiochain->volume = NULL;
3602 if (playsink->audiochain->ts_offset)
3603 gst_object_unref (playsink->audiochain->ts_offset);
3604 playsink->audiochain->ts_offset = NULL;
3605 free_chain ((GstPlayChain *) playsink->audiochain);
3606 playsink->audiochain = NULL;
3607 playsink->volume_changed = playsink->mute_changed = FALSE;
3608 }
3609 }
3610
3611 if (!playsink->audiochain) {
3612 GST_DEBUG_OBJECT (playsink, "creating new audio chain");
3613 playsink->audiochain = gen_audio_chain (playsink, raw);
3614 }
3615
3616 if (!playsink->audiochain)
3617 goto no_chain;
3618
3619 if (!playsink->audio_sinkpad_stream_synchronizer) {
3620 GValue item = { 0, };
3621 GstIterator *it;
3622
3623 playsink->audio_sinkpad_stream_synchronizer =
3624 gst_element_request_pad_simple (GST_ELEMENT_CAST
3625 (playsink->stream_synchronizer), "sink_%u");
3626 it = gst_pad_iterate_internal_links
3627 (playsink->audio_sinkpad_stream_synchronizer);
3628 g_assert (it);
3629 gst_iterator_next (it, &item);
3630 playsink->audio_srcpad_stream_synchronizer = g_value_dup_object (&item);
3631 g_value_unset (&item);
3632 g_assert (playsink->audio_srcpad_stream_synchronizer);
3633 gst_iterator_free (it);
3634 }
3635
3636 if (need_vis) {
3637 GstPad *audio_queue_srcpad;
3638
3639 if (gst_pad_is_linked (playsink->audio_sinkpad_stream_synchronizer)) {
3640 GstPad *peer_pad =
3641 gst_pad_get_peer (playsink->audio_sinkpad_stream_synchronizer);
3642 gst_pad_unlink (peer_pad, playsink->audio_sinkpad_stream_synchronizer);
3643 gst_object_unref (peer_pad);
3644 }
3645
3646 if (!playsink->audio_ssync_queue) {
3647 GST_DEBUG_OBJECT (playsink, "adding audio stream synchronizer queue");
3648 playsink->audio_ssync_queue =
3649 gst_element_factory_make ("queue", "audiossyncqueue");
3650 if (playsink->audio_ssync_queue == NULL) {
3651 post_missing_element_message (playsink, "queue");
3652 GST_ELEMENT_WARNING (playsink, CORE, MISSING_PLUGIN,
3653 (_("Missing element '%s' - check your GStreamer installation."),
3654 "queue"),
3655 ("audio playback and visualizations might not work"));
3656 }
3657 g_object_set (playsink->audio_ssync_queue, "max-size-buffers",
3658 (guint) 1, NULL);
3659 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_ssync_queue);
3660 playsink->audio_ssync_queue_sinkpad =
3661 gst_element_get_static_pad (playsink->audio_ssync_queue, "sink");
3662 }
3663
3664 audio_queue_srcpad =
3665 gst_element_get_static_pad (playsink->audio_ssync_queue, "src");
3666 gst_pad_link_full (audio_queue_srcpad,
3667 playsink->audio_sinkpad_stream_synchronizer,
3668 GST_PAD_LINK_CHECK_NOTHING);
3669 gst_object_unref (audio_queue_srcpad);
3670 gst_element_sync_state_with_parent (playsink->audio_ssync_queue);
3671 }
3672
3673 if (playsink->audiochain) {
3674 GstPad *sinkpad;
3675
3676 GST_DEBUG_OBJECT (playsink, "adding audio chain");
3677 if (playsink->audio_tee_asrc == NULL) {
3678 playsink->audio_tee_asrc =
3679 gst_element_request_pad_simple (playsink->audio_tee, "src_%u");
3680 }
3681
3682 sinkpad = playsink->audio_ssync_queue_sinkpad;
3683 if (!sinkpad)
3684 sinkpad = playsink->audio_sinkpad_stream_synchronizer;
3685
3686 add_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3687 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), TRUE);
3688 gst_pad_link_full (playsink->audio_tee_asrc, sinkpad,
3689 GST_PAD_LINK_CHECK_NOTHING);
3690 gst_pad_link_full (playsink->audio_srcpad_stream_synchronizer,
3691 playsink->audiochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3692 }
3693 } else {
3694 GST_DEBUG_OBJECT (playsink, "no audio needed");
3695 /* we have no audio or we are requested to not play audio */
3696 if (playsink->audiochain) {
3697 GST_DEBUG_OBJECT (playsink, "removing audio chain");
3698 /* release the audio pad */
3699 if (playsink->audio_tee_asrc) {
3700 gst_element_release_request_pad (playsink->audio_tee,
3701 playsink->audio_tee_asrc);
3702 gst_object_unref (playsink->audio_tee_asrc);
3703 playsink->audio_tee_asrc = NULL;
3704 }
3705
3706 if (playsink->audio_sinkpad_stream_synchronizer) {
3707 gst_element_release_request_pad (GST_ELEMENT_CAST
3708 (playsink->stream_synchronizer),
3709 playsink->audio_sinkpad_stream_synchronizer);
3710 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
3711 playsink->audio_sinkpad_stream_synchronizer = NULL;
3712 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
3713 playsink->audio_srcpad_stream_synchronizer = NULL;
3714
3715 gst_play_sink_remove_audio_ssync_queue (playsink);
3716 }
3717
3718 if (playsink->audiochain->sink_volume) {
3719 disconnect_audio_chain (playsink->audiochain, playsink);
3720 if (playsink->audiochain->volume)
3721 gst_object_unref (playsink->audiochain->volume);
3722 playsink->audiochain->volume = NULL;
3723 if (playsink->audiochain->ts_offset)
3724 gst_object_unref (playsink->audiochain->ts_offset);
3725 playsink->audiochain->ts_offset = NULL;
3726 }
3727 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3728 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
3729 }
3730
3731 if (playsink->audio_sink)
3732 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
3733 if (playsink->audio_filter)
3734 gst_element_set_state (playsink->audio_filter, GST_STATE_NULL);
3735 }
3736
3737 if (need_vis) {
3738 GstPad *srcpad;
3739
3740 if (!playsink->vischain)
3741 playsink->vischain = gen_vis_chain (playsink);
3742
3743 GST_DEBUG_OBJECT (playsink, "adding visualisation");
3744
3745 if (playsink->vischain) {
3746 GST_DEBUG_OBJECT (playsink, "setting up vis chain");
3747
3748 /* Lazily add and activate chain */
3749 if (!playsink->vischain->chain.added) {
3750 srcpad =
3751 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3752 add_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3753 activate_chain (GST_PLAY_CHAIN (playsink->vischain), TRUE);
3754 if (playsink->audio_tee_vissrc == NULL) {
3755 playsink->audio_tee_vissrc =
3756 gst_element_request_pad_simple (playsink->audio_tee, "src_%u");
3757 }
3758 gst_pad_link_full (playsink->audio_tee_vissrc,
3759 playsink->vischain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3760 gst_pad_link_full (srcpad, playsink->video_sinkpad_stream_synchronizer,
3761 GST_PAD_LINK_CHECK_NOTHING);
3762 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3763 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3764 gst_object_unref (srcpad);
3765 }
3766
3767 /* Is a reconfiguration required? */
3768 if (playsink->vischain->vis != playsink->visualisation) {
3769 /* unlink the old plugin and unghost the pad */
3770 gst_pad_unlink (playsink->vischain->vispeerpad,
3771 playsink->vischain->vissinkpad);
3772 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->vischain->
3773 srcpad), NULL);
3774
3775 /* set the old plugin to NULL and remove */
3776 gst_element_set_state (playsink->vischain->vis, GST_STATE_NULL);
3777 gst_bin_remove (GST_BIN_CAST (playsink->vischain->chain.bin),
3778 playsink->vischain->vis);
3779
3780 /* add new plugin and set state to playing */
3781 playsink->vischain->vis = playsink->visualisation;
3782 gst_bin_add (GST_BIN_CAST (playsink->vischain->chain.bin),
3783 playsink->vischain->vis);
3784 gst_element_set_state (playsink->vischain->vis, GST_STATE_PLAYING);
3785
3786 /* get pads */
3787 playsink->vischain->vissinkpad =
3788 gst_element_get_static_pad (playsink->vischain->vis, "sink");
3789 playsink->vischain->vissrcpad =
3790 gst_element_get_static_pad (playsink->vischain->vis, "src");
3791
3792 /* link pads */
3793 gst_pad_link_full (playsink->vischain->vispeerpad,
3794 playsink->vischain->vissinkpad, GST_PAD_LINK_CHECK_NOTHING);
3795 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->vischain->
3796 srcpad), playsink->vischain->vissrcpad);
3797 }
3798 }
3799 } else {
3800 GST_DEBUG_OBJECT (playsink, "no vis needed");
3801 if (playsink->vischain) {
3802 if (playsink->audio_tee_vissrc) {
3803 gst_element_release_request_pad (playsink->audio_tee,
3804 playsink->audio_tee_vissrc);
3805 gst_object_unref (playsink->audio_tee_vissrc);
3806 playsink->audio_tee_vissrc = NULL;
3807 }
3808 GST_DEBUG_OBJECT (playsink, "removing vis chain");
3809 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3810 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
3811 }
3812 }
3813
3814 if (need_text) {
3815 GST_DEBUG_OBJECT (playsink, "adding text");
3816 if (!playsink->textchain) {
3817 GST_DEBUG_OBJECT (playsink, "creating text chain");
3818 playsink->textchain = gen_text_chain (playsink);
3819 }
3820 if (playsink->textchain) {
3821 GstIterator *it;
3822
3823 GST_DEBUG_OBJECT (playsink, "adding text chain");
3824 if (playsink->textchain->overlay)
3825 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", FALSE,
3826 NULL);
3827 add_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3828
3829 if (!playsink->text_sinkpad_stream_synchronizer) {
3830 GValue item = { 0, };
3831
3832 playsink->text_sinkpad_stream_synchronizer =
3833 gst_element_request_pad_simple (GST_ELEMENT_CAST
3834 (playsink->stream_synchronizer), "sink_%u");
3835 it = gst_pad_iterate_internal_links
3836 (playsink->text_sinkpad_stream_synchronizer);
3837 g_assert (it);
3838 gst_iterator_next (it, &item);
3839 playsink->text_srcpad_stream_synchronizer = g_value_dup_object (&item);
3840 g_value_unset (&item);
3841 g_assert (playsink->text_srcpad_stream_synchronizer);
3842 gst_iterator_free (it);
3843 }
3844
3845 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3846 playsink->text_sinkpad_stream_synchronizer);
3847 gst_pad_link_full (playsink->text_srcpad_stream_synchronizer,
3848 playsink->textchain->textsinkpad, GST_PAD_LINK_CHECK_NOTHING);
3849
3850 if (need_vis || need_video) {
3851 if (need_vis) {
3852 GstPad *srcpad;
3853
3854 srcpad =
3855 gst_element_get_static_pad (playsink->vischain->chain.bin, "src");
3856 gst_pad_unlink (srcpad, playsink->videochain->sinkpad);
3857 gst_pad_link_full (srcpad, playsink->textchain->videosinkpad,
3858 GST_PAD_LINK_CHECK_NOTHING);
3859 gst_object_unref (srcpad);
3860 } else {
3861 if (need_deinterlace) {
3862 gst_pad_unlink (playsink->videodeinterlacechain->srcpad,
3863 playsink->videochain->sinkpad);
3864 gst_pad_link_full (playsink->videodeinterlacechain->srcpad,
3865 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3866 } else {
3867 gst_pad_unlink (playsink->video_srcpad_stream_synchronizer,
3868 playsink->videochain->sinkpad);
3869 gst_pad_link_full (playsink->video_srcpad_stream_synchronizer,
3870 playsink->textchain->videosinkpad, GST_PAD_LINK_CHECK_NOTHING);
3871 }
3872 }
3873 gst_pad_link_full (playsink->textchain->srcpad,
3874 playsink->videochain->sinkpad, GST_PAD_LINK_CHECK_NOTHING);
3875 }
3876
3877 activate_chain (GST_PLAY_CHAIN (playsink->textchain), TRUE);
3878 }
3879 } else {
3880 GST_DEBUG_OBJECT (playsink, "no text needed");
3881 /* we have no subtitles/text or we are requested to not show them */
3882
3883 if (playsink->textchain) {
3884 if (playsink->text_pad == NULL) {
3885 /* no text pad, remove the chain entirely */
3886 GST_DEBUG_OBJECT (playsink, "removing text chain");
3887 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3888 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
3889
3890 if (playsink->text_sinkpad_stream_synchronizer) {
3891 gst_element_release_request_pad (GST_ELEMENT_CAST
3892 (playsink->stream_synchronizer),
3893 playsink->text_sinkpad_stream_synchronizer);
3894 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
3895 playsink->text_sinkpad_stream_synchronizer = NULL;
3896 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
3897 playsink->text_srcpad_stream_synchronizer = NULL;
3898 }
3899
3900 if (!need_video && playsink->video_pad) {
3901 if (playsink->video_sinkpad_stream_synchronizer) {
3902 gst_element_release_request_pad (GST_ELEMENT_CAST
3903 (playsink->stream_synchronizer),
3904 playsink->video_sinkpad_stream_synchronizer);
3905 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
3906 playsink->video_sinkpad_stream_synchronizer = NULL;
3907 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
3908 playsink->video_srcpad_stream_synchronizer = NULL;
3909 }
3910
3911 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->video_pad),
3912 NULL);
3913 }
3914
3915 if (playsink->text_pad && !playsink->textchain)
3916 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (playsink->text_pad),
3917 NULL);
3918
3919 if (playsink->text_sink)
3920 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
3921 } else {
3922 /* we have a chain and a textpad, turn the subtitles off */
3923 GST_DEBUG_OBJECT (playsink, "turning off the text");
3924 if (playsink->textchain->overlay)
3925 g_object_set (G_OBJECT (playsink->textchain->overlay), "silent", TRUE,
3926 NULL);
3927 }
3928 }
3929 }
3930 update_av_offset (playsink);
3931 update_text_offset (playsink);
3932 do_async_done (playsink);
3933
3934 playsink->reconfigure_pending = FALSE;
3935
3936 GST_PLAY_SINK_UNLOCK (playsink);
3937
3938 return TRUE;
3939
3940 /* ERRORS */
3941 no_chain:
3942 {
3943 /* gen_ chain already posted error */
3944 GST_DEBUG_OBJECT (playsink, "failed to setup chain");
3945 GST_PLAY_SINK_UNLOCK (playsink);
3946 return FALSE;
3947 }
3948 }
3949
3950 /**
3951 * gst_play_sink_set_flags:
3952 * @playsink: a #GstPlaySink
3953 * @flags: #GstPlayFlags
3954 *
3955 * Configure @flags on @playsink. The flags control the behaviour of @playsink
3956 * when constructing the sink pipelins.
3957 *
3958 * Returns: TRUE if the flags could be configured.
3959 */
3960 gboolean
gst_play_sink_set_flags(GstPlaySink * playsink,GstPlayFlags flags)3961 gst_play_sink_set_flags (GstPlaySink * playsink, GstPlayFlags flags)
3962 {
3963 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), FALSE);
3964
3965 GST_OBJECT_LOCK (playsink);
3966 playsink->flags = flags;
3967 GST_OBJECT_UNLOCK (playsink);
3968
3969 return TRUE;
3970 }
3971
3972 /**
3973 * gst_play_sink_get_flags:
3974 * @playsink: a #GstPlaySink
3975 *
3976 * Get the flags of @playsink. That flags control the behaviour of the sink when
3977 * it constructs the sink pipelines.
3978 *
3979 * Returns: the currently configured #GstPlayFlags.
3980 */
3981 GstPlayFlags
gst_play_sink_get_flags(GstPlaySink * playsink)3982 gst_play_sink_get_flags (GstPlaySink * playsink)
3983 {
3984 GstPlayFlags res;
3985
3986 g_return_val_if_fail (GST_IS_PLAY_SINK (playsink), 0);
3987
3988 GST_OBJECT_LOCK (playsink);
3989 res = playsink->flags;
3990 GST_OBJECT_UNLOCK (playsink);
3991
3992 return res;
3993 }
3994
3995 void
gst_play_sink_set_font_desc(GstPlaySink * playsink,const gchar * desc)3996 gst_play_sink_set_font_desc (GstPlaySink * playsink, const gchar * desc)
3997 {
3998 GstPlayTextChain *chain;
3999
4000 GST_PLAY_SINK_LOCK (playsink);
4001 chain = (GstPlayTextChain *) playsink->textchain;
4002 g_free (playsink->font_desc);
4003 playsink->font_desc = g_strdup (desc);
4004 if (chain && chain->overlay) {
4005 g_object_set (chain->overlay, "font-desc", desc, NULL);
4006 }
4007 GST_PLAY_SINK_UNLOCK (playsink);
4008 }
4009
4010 gchar *
gst_play_sink_get_font_desc(GstPlaySink * playsink)4011 gst_play_sink_get_font_desc (GstPlaySink * playsink)
4012 {
4013 gchar *result = NULL;
4014 GstPlayTextChain *chain;
4015
4016 GST_PLAY_SINK_LOCK (playsink);
4017 chain = (GstPlayTextChain *) playsink->textchain;
4018 if (chain && chain->overlay) {
4019 g_object_get (chain->overlay, "font-desc", &result, NULL);
4020 playsink->font_desc = g_strdup (result);
4021 } else {
4022 result = g_strdup (playsink->font_desc);
4023 }
4024 GST_PLAY_SINK_UNLOCK (playsink);
4025
4026 return result;
4027 }
4028
4029 void
gst_play_sink_set_subtitle_encoding(GstPlaySink * playsink,const gchar * encoding)4030 gst_play_sink_set_subtitle_encoding (GstPlaySink * playsink,
4031 const gchar * encoding)
4032 {
4033 GstPlayTextChain *chain;
4034
4035 GST_PLAY_SINK_LOCK (playsink);
4036 chain = (GstPlayTextChain *) playsink->textchain;
4037 g_free (playsink->subtitle_encoding);
4038 playsink->subtitle_encoding = g_strdup (encoding);
4039 if (chain && chain->overlay) {
4040 g_object_set (chain->overlay, "subtitle-encoding", encoding, NULL);
4041 }
4042 GST_PLAY_SINK_UNLOCK (playsink);
4043 }
4044
4045 gchar *
gst_play_sink_get_subtitle_encoding(GstPlaySink * playsink)4046 gst_play_sink_get_subtitle_encoding (GstPlaySink * playsink)
4047 {
4048 gchar *result = NULL;
4049 GstPlayTextChain *chain;
4050
4051 GST_PLAY_SINK_LOCK (playsink);
4052 chain = (GstPlayTextChain *) playsink->textchain;
4053 if (chain && chain->overlay) {
4054 g_object_get (chain->overlay, "subtitle-encoding", &result, NULL);
4055 playsink->subtitle_encoding = g_strdup (result);
4056 } else {
4057 result = g_strdup (playsink->subtitle_encoding);
4058 }
4059 GST_PLAY_SINK_UNLOCK (playsink);
4060
4061 return result;
4062 }
4063
4064 static void
update_av_offset(GstPlaySink * playsink)4065 update_av_offset (GstPlaySink * playsink)
4066 {
4067 gint64 av_offset;
4068 GstPlayAudioChain *achain;
4069 GstPlayVideoChain *vchain;
4070
4071 av_offset = playsink->av_offset;
4072 achain = (GstPlayAudioChain *) playsink->audiochain;
4073 vchain = (GstPlayVideoChain *) playsink->videochain;
4074
4075 if (achain && vchain && achain->ts_offset && vchain->ts_offset) {
4076 g_object_set (achain->ts_offset,
4077 "ts-offset", MAX (G_GINT64_CONSTANT (0), -av_offset), NULL);
4078 g_object_set (vchain->ts_offset,
4079 "ts-offset", MAX (G_GINT64_CONSTANT (0), av_offset), NULL);
4080 } else {
4081 GST_LOG_OBJECT (playsink, "no ts_offset elements");
4082 }
4083 }
4084
4085 void
gst_play_sink_set_av_offset(GstPlaySink * playsink,gint64 av_offset)4086 gst_play_sink_set_av_offset (GstPlaySink * playsink, gint64 av_offset)
4087 {
4088 GST_PLAY_SINK_LOCK (playsink);
4089 playsink->av_offset = av_offset;
4090 update_av_offset (playsink);
4091 GST_PLAY_SINK_UNLOCK (playsink);
4092 }
4093
4094 gint64
gst_play_sink_get_av_offset(GstPlaySink * playsink)4095 gst_play_sink_get_av_offset (GstPlaySink * playsink)
4096 {
4097 gint64 result;
4098
4099 GST_PLAY_SINK_LOCK (playsink);
4100 result = playsink->av_offset;
4101 GST_PLAY_SINK_UNLOCK (playsink);
4102
4103 return result;
4104 }
4105
4106 static void
update_text_offset(GstPlaySink * playsink)4107 update_text_offset (GstPlaySink * playsink)
4108 {
4109 gint64 text_offset;
4110 GstPlayTextChain *tchain;
4111 GstElement *elem;
4112
4113 text_offset = playsink->text_offset;
4114 tchain = (GstPlayTextChain *) playsink->textchain;
4115
4116 if (tchain) {
4117 if (tchain->sink) {
4118 elem =
4119 gst_play_sink_find_property_sinks (playsink, tchain->sink,
4120 "ts-offset", G_TYPE_INT64);
4121 if (elem)
4122 g_object_set (elem, "ts-offset", text_offset, NULL);
4123 } else if (tchain->overlay) {
4124 g_object_set (tchain->overlay, "subtitle-ts-offset", text_offset, NULL);
4125 }
4126 } else {
4127 GST_LOG_OBJECT (playsink, "no text chain");
4128 }
4129 }
4130
4131 void
gst_play_sink_set_text_offset(GstPlaySink * playsink,gint64 text_offset)4132 gst_play_sink_set_text_offset (GstPlaySink * playsink, gint64 text_offset)
4133 {
4134 GST_PLAY_SINK_LOCK (playsink);
4135 playsink->text_offset = text_offset;
4136 update_text_offset (playsink);
4137 GST_PLAY_SINK_UNLOCK (playsink);
4138 }
4139
4140 gint64
gst_play_sink_get_text_offset(GstPlaySink * playsink)4141 gst_play_sink_get_text_offset (GstPlaySink * playsink)
4142 {
4143 gint64 result;
4144
4145 GST_PLAY_SINK_LOCK (playsink);
4146 result = playsink->text_offset;
4147 GST_PLAY_SINK_UNLOCK (playsink);
4148
4149 return result;
4150 }
4151
4152 /**
4153 * gst_play_sink_get_last_sample:
4154 * @playsink: a #GstPlaySink
4155 *
4156 * Get the last displayed sample from @playsink. This sample is in the native
4157 * format of the sink element, the caps in the result sample contain the format
4158 * of the frame data.
4159 *
4160 * Returns: a #GstSample with the frame data or %NULL when no video frame is
4161 * available.
4162 */
4163 GstSample *
gst_play_sink_get_last_sample(GstPlaySink * playsink)4164 gst_play_sink_get_last_sample (GstPlaySink * playsink)
4165 {
4166 GstSample *result = NULL;
4167 GstPlayVideoChain *chain;
4168
4169 GST_PLAY_SINK_LOCK (playsink);
4170 GST_DEBUG_OBJECT (playsink, "taking last sample");
4171 /* get the video chain if we can */
4172 if ((chain = (GstPlayVideoChain *) playsink->videochain)) {
4173 GST_DEBUG_OBJECT (playsink, "found video chain");
4174 /* see if the chain is active */
4175 if (chain->chain.activated && chain->sink) {
4176 GstElement *elem;
4177
4178 GST_DEBUG_OBJECT (playsink, "video chain active and has a sink");
4179
4180 /* find and get the last-buffer property now */
4181 if ((elem =
4182 gst_play_sink_find_property (playsink, chain->sink,
4183 "last-sample", GST_TYPE_SAMPLE))) {
4184 GST_DEBUG_OBJECT (playsink, "getting last-sample property");
4185 g_object_get (elem, "last-sample", &result, NULL);
4186 gst_object_unref (elem);
4187 }
4188 }
4189 }
4190 GST_PLAY_SINK_UNLOCK (playsink);
4191
4192 return result;
4193 }
4194
4195 /**
4196 * gst_play_sink_convert_sample:
4197 * @playsink: a #GstPlaySink
4198 * @caps: a #GstCaps
4199 *
4200 * Get the last displayed frame from @playsink. If caps is %NULL, the video will
4201 * be in the native format of the sink element and the caps on the buffer
4202 * describe the format of the frame. If @caps is not %NULL, the video
4203 * frame will be converted to the format of the caps.
4204 *
4205 * Returns: a #GstSample of the current video sample converted to #caps.
4206 * The caps in the sample will describe the final layout of the buffer data.
4207 * %NULL is returned when no current sample can be retrieved or when the
4208 * conversion failed.
4209 */
4210 GstSample *
gst_play_sink_convert_sample(GstPlaySink * playsink,GstCaps * caps)4211 gst_play_sink_convert_sample (GstPlaySink * playsink, GstCaps * caps)
4212 {
4213 GstSample *result;
4214 GError *err = NULL;
4215
4216 result = gst_play_sink_get_last_sample (playsink);
4217 if (result != NULL && caps != NULL) {
4218 GstSample *temp;
4219
4220 temp = gst_video_convert_sample (result, caps, 25 * GST_SECOND, &err);
4221 if (temp == NULL && err)
4222 goto error;
4223
4224 gst_sample_unref (result);
4225 result = temp;
4226 }
4227 return result;
4228
4229 /* ERRORS */
4230 error:
4231 {
4232 /* I'm really uncertain whether we should make playsink post an error
4233 * on the bus or not. It's not like it's a critical issue regarding
4234 * playsink behaviour. */
4235 GST_ERROR ("Error converting frame: %s", err->message);
4236 gst_sample_unref (result);
4237 g_error_free (err);
4238 return NULL;
4239 }
4240 }
4241
4242 static gboolean
is_raw_structure(GstStructure * s)4243 is_raw_structure (GstStructure * s)
4244 {
4245 const gchar *name;
4246
4247 name = gst_structure_get_name (s);
4248
4249 if (g_str_equal (name, "video/x-raw") || g_str_equal (name, "audio/x-raw"))
4250 return TRUE;
4251 return FALSE;
4252 }
4253
4254 static gboolean
is_raw_pad(GstPad * pad)4255 is_raw_pad (GstPad * pad)
4256 {
4257 GstPad *peer = gst_pad_get_peer (pad);
4258 GstCaps *caps;
4259 gboolean raw = TRUE;
4260
4261 if (!peer)
4262 return raw;
4263
4264 caps = gst_pad_get_current_caps (peer);
4265 if (!caps) {
4266 guint i, n;
4267
4268 caps = gst_pad_query_caps (peer, NULL);
4269
4270 n = gst_caps_get_size (caps);
4271 for (i = 0; i < n; i++) {
4272 gboolean r = is_raw_structure (gst_caps_get_structure (caps, i));
4273
4274 if (i == 0) {
4275 raw = r;
4276 } else if (raw != r) {
4277 GST_ERROR_OBJECT (pad,
4278 "Caps contains raw and non-raw structures: %" GST_PTR_FORMAT, caps);
4279 raw = FALSE;
4280 break;
4281 }
4282 }
4283 } else {
4284 raw = is_raw_structure (gst_caps_get_structure (caps, 0));
4285 }
4286 gst_caps_unref (caps);
4287 gst_object_unref (peer);
4288
4289 return raw;
4290 }
4291
4292 static GstPadProbeReturn
4293 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
4294 gpointer user_data);
4295
4296 static void
video_set_blocked(GstPlaySink * playsink,gboolean blocked)4297 video_set_blocked (GstPlaySink * playsink, gboolean blocked)
4298 {
4299 if (playsink->video_pad) {
4300 GstPad *opad =
4301 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4302 (playsink->video_pad)));
4303 if (blocked && playsink->video_block_id == 0) {
4304 if (playsink->vis_pad_block_id)
4305 gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->
4306 blockpad, playsink->vis_pad_block_id);
4307 playsink->vis_pad_block_id = 0;
4308
4309 playsink->video_block_id =
4310 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4311 sinkpad_blocked_cb, playsink, NULL);
4312 } else if (!blocked && playsink->video_block_id) {
4313 gst_pad_remove_probe (opad, playsink->video_block_id);
4314 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_VIDEO);
4315 playsink->video_block_id = 0;
4316 playsink->video_pad_blocked = FALSE;
4317 }
4318 gst_object_unref (opad);
4319 }
4320 }
4321
4322 static void
audio_set_blocked(GstPlaySink * playsink,gboolean blocked)4323 audio_set_blocked (GstPlaySink * playsink, gboolean blocked)
4324 {
4325 if (playsink->audio_pad) {
4326 GstPad *opad =
4327 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4328 (playsink->audio_pad)));
4329 if (blocked && playsink->audio_block_id == 0) {
4330 if (playsink->vis_pad_block_id)
4331 gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->
4332 blockpad, playsink->vis_pad_block_id);
4333 playsink->vis_pad_block_id = 0;
4334
4335 playsink->audio_block_id =
4336 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4337 sinkpad_blocked_cb, playsink, NULL);
4338 } else if (!blocked && playsink->audio_block_id) {
4339 if (playsink->vis_pad_block_id)
4340 gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->
4341 blockpad, playsink->vis_pad_block_id);
4342 playsink->vis_pad_block_id = 0;
4343
4344 gst_pad_remove_probe (opad, playsink->audio_block_id);
4345 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_AUDIO);
4346 playsink->audio_block_id = 0;
4347 playsink->audio_pad_blocked = FALSE;
4348 }
4349 gst_object_unref (opad);
4350 }
4351 }
4352
4353 static void
text_set_blocked(GstPlaySink * playsink,gboolean blocked)4354 text_set_blocked (GstPlaySink * playsink, gboolean blocked)
4355 {
4356 if (playsink->text_pad) {
4357 GstPad *opad =
4358 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
4359 (playsink->text_pad)));
4360 if (blocked && playsink->text_block_id == 0) {
4361 if (playsink->vis_pad_block_id)
4362 gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->
4363 blockpad, playsink->vis_pad_block_id);
4364 playsink->vis_pad_block_id = 0;
4365
4366 playsink->text_block_id =
4367 gst_pad_add_probe (opad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4368 sinkpad_blocked_cb, playsink, NULL);
4369 } else if (!blocked && playsink->text_block_id) {
4370 gst_pad_remove_probe (opad, playsink->text_block_id);
4371 PENDING_FLAG_UNSET (playsink, GST_PLAY_SINK_TYPE_TEXT);
4372 playsink->text_block_id = 0;
4373 playsink->text_pad_blocked = FALSE;
4374 }
4375 gst_object_unref (opad);
4376 }
4377 }
4378
4379 gboolean
gst_play_sink_reconfigure(GstPlaySink * playsink)4380 gst_play_sink_reconfigure (GstPlaySink * playsink)
4381 {
4382 GST_LOG_OBJECT (playsink, "Triggering reconfiguration");
4383
4384 GST_PLAY_SINK_LOCK (playsink);
4385 video_set_blocked (playsink, TRUE);
4386 audio_set_blocked (playsink, TRUE);
4387 text_set_blocked (playsink, TRUE);
4388 playsink->reconfigure_pending = TRUE;
4389 GST_PLAY_SINK_UNLOCK (playsink);
4390
4391 return TRUE;
4392 }
4393
4394 /* Called with PLAY_SINK_LOCK */
4395 static gboolean
gst_play_sink_ready_to_reconfigure_locked(GstPlaySink * playsink)4396 gst_play_sink_ready_to_reconfigure_locked (GstPlaySink * playsink)
4397 {
4398 /* We reconfigure when for ALL streams:
4399 * * there isn't a pad
4400 * * OR the pad is blocked
4401 * * OR there are no pending blocks on that pad
4402 */
4403 if (playsink->reconfigure_pending == FALSE)
4404 return FALSE;
4405
4406 if (playsink->video_pad && !playsink->video_pad_blocked
4407 && PENDING_VIDEO_BLOCK (playsink))
4408 return FALSE;
4409
4410 if (playsink->audio_pad && !playsink->audio_pad_blocked
4411 && PENDING_AUDIO_BLOCK (playsink))
4412 return FALSE;
4413
4414 if (playsink->text_pad && !playsink->text_pad_blocked
4415 && PENDING_TEXT_BLOCK (playsink))
4416 return FALSE;
4417
4418 return TRUE;
4419 }
4420
4421 static GstPadProbeReturn
sinkpad_blocked_cb(GstPad * blockedpad,GstPadProbeInfo * info,gpointer user_data)4422 sinkpad_blocked_cb (GstPad * blockedpad, GstPadProbeInfo * info,
4423 gpointer user_data)
4424 {
4425 GstPlaySink *playsink = (GstPlaySink *) user_data;
4426 GstPad *pad;
4427
4428 if (GST_IS_EVENT (info->data) && !GST_EVENT_IS_SERIALIZED (info->data)) {
4429 GST_DEBUG_OBJECT (playsink, "Letting non-serialized event %s pass",
4430 GST_EVENT_TYPE_NAME (info->data));
4431 return GST_PAD_PROBE_PASS;
4432 }
4433
4434 GST_PLAY_SINK_LOCK (playsink);
4435
4436 pad = GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (blockedpad)));
4437 if (pad == playsink->video_pad) {
4438 playsink->video_pad_blocked = TRUE;
4439 GST_DEBUG_OBJECT (pad, "Video pad blocked");
4440 } else if (pad == playsink->audio_pad) {
4441 playsink->audio_pad_blocked = TRUE;
4442 GST_DEBUG_OBJECT (pad, "Audio pad blocked");
4443 } else if (pad == playsink->text_pad) {
4444 playsink->text_pad_blocked = TRUE;
4445 GST_DEBUG_OBJECT (pad, "Text pad blocked");
4446 }
4447
4448 if (gst_play_sink_ready_to_reconfigure_locked (playsink)) {
4449 GST_DEBUG_OBJECT (playsink, "All pads blocked -- reconfiguring");
4450
4451 gst_play_sink_do_reconfigure (playsink);
4452
4453 video_set_blocked (playsink, FALSE);
4454 audio_set_blocked (playsink, FALSE);
4455 text_set_blocked (playsink, FALSE);
4456 }
4457
4458 gst_object_unref (pad);
4459
4460 GST_PLAY_SINK_UNLOCK (playsink);
4461
4462 return GST_PAD_PROBE_OK;
4463 }
4464
4465 static void
caps_notify_cb(GstPad * pad,GParamSpec * unused,GstPlaySink * playsink)4466 caps_notify_cb (GstPad * pad, GParamSpec * unused, GstPlaySink * playsink)
4467 {
4468 gboolean reconfigure = FALSE;
4469 GstCaps *caps;
4470 gboolean raw;
4471
4472 g_object_get (pad, "caps", &caps, NULL);
4473 if (!caps)
4474 return;
4475
4476 if (pad == playsink->audio_pad) {
4477 raw = is_raw_pad (pad);
4478 reconfigure = (! !playsink->audio_pad_raw != ! !raw)
4479 && playsink->audiochain;
4480 GST_DEBUG_OBJECT (pad,
4481 "Audio caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
4482 reconfigure, caps);
4483 } else if (pad == playsink->video_pad) {
4484 raw = is_raw_pad (pad);
4485 reconfigure = (! !playsink->video_pad_raw != ! !raw)
4486 && playsink->videochain;
4487 GST_DEBUG_OBJECT (pad,
4488 "Video caps changed: raw %d reconfigure %d caps %" GST_PTR_FORMAT, raw,
4489 reconfigure, caps);
4490 }
4491
4492 gst_caps_unref (caps);
4493
4494 if (reconfigure)
4495 gst_play_sink_reconfigure (playsink);
4496 }
4497
4498 void
gst_play_sink_refresh_pad(GstPlaySink * playsink,GstPad * pad,GstPlaySinkType type)4499 gst_play_sink_refresh_pad (GstPlaySink * playsink, GstPad * pad,
4500 GstPlaySinkType type)
4501 {
4502 gulong *block_id = NULL;
4503
4504 GST_DEBUG_OBJECT (playsink, "refresh pad %" GST_PTR_FORMAT, pad);
4505
4506 GST_PLAY_SINK_LOCK (playsink);
4507 if (pad == playsink->video_pad) {
4508 if (type != GST_PLAY_SINK_TYPE_VIDEO)
4509 goto wrong_type;
4510 block_id = &playsink->video_block_id;
4511 } else if (pad == playsink->audio_pad) {
4512 if (type != GST_PLAY_SINK_TYPE_AUDIO)
4513 goto wrong_type;
4514 block_id = &playsink->audio_block_id;
4515 } else if (pad == playsink->text_pad) {
4516 if (type != GST_PLAY_SINK_TYPE_TEXT)
4517 goto wrong_type;
4518 block_id = &playsink->text_block_id;
4519 }
4520
4521 if (type != GST_PLAY_SINK_TYPE_FLUSHING && (block_id && *block_id == 0)) {
4522 GstPad *blockpad =
4523 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (pad)));
4524
4525 if (playsink->vis_pad_block_id)
4526 gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->blockpad,
4527 playsink->vis_pad_block_id);
4528 playsink->vis_pad_block_id = 0;
4529
4530 *block_id =
4531 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4532 sinkpad_blocked_cb, playsink, NULL);
4533 PENDING_FLAG_SET (playsink, type);
4534 gst_object_unref (blockpad);
4535 }
4536 GST_PLAY_SINK_UNLOCK (playsink);
4537
4538 return;
4539
4540 /* ERRORS */
4541 wrong_type:
4542 {
4543 GST_WARNING_OBJECT (playsink, "wrong type %u for pad %" GST_PTR_FORMAT,
4544 type, pad);
4545 GST_PLAY_SINK_UNLOCK (playsink);
4546 return;
4547 }
4548 }
4549
4550 /**
4551 * gst_play_sink_request_pad
4552 * @playsink: a #GstPlaySink
4553 * @type: a #GstPlaySinkType
4554 *
4555 * Create or return a pad of @type.
4556 *
4557 * Returns: a #GstPad of @type or %NULL when the pad could not be created.
4558 */
4559 GstPad *
gst_play_sink_request_pad(GstPlaySink * playsink,GstPlaySinkType type)4560 gst_play_sink_request_pad (GstPlaySink * playsink, GstPlaySinkType type)
4561 {
4562 GstPad *res = NULL;
4563 gboolean created = FALSE;
4564 gboolean activate = TRUE;
4565 const gchar *pad_name = NULL;
4566 gulong *block_id = NULL;
4567
4568 GST_DEBUG_OBJECT (playsink, "request pad type %d", type);
4569
4570 GST_PLAY_SINK_LOCK (playsink);
4571 switch (type) {
4572 case GST_PLAY_SINK_TYPE_AUDIO:
4573 pad_name = "audio_sink";
4574 if (!playsink->audio_tee) {
4575 GST_LOG_OBJECT (playsink, "creating tee");
4576 /* create tee when needed. This element will feed the audio sink chain
4577 * and the vis chain. */
4578 playsink->audio_tee = gst_element_factory_make ("tee", "audiotee");
4579 if (playsink->audio_tee == NULL) {
4580 post_missing_element_message (playsink, "tee");
4581 GST_ELEMENT_ERROR (playsink, CORE, MISSING_PLUGIN,
4582 (_("Missing element '%s' - check your GStreamer installation."),
4583 "tee"), (NULL));
4584 res = NULL;
4585 break;
4586 }
4587 playsink->audio_tee_sink =
4588 gst_element_get_static_pad (playsink->audio_tee, "sink");
4589 gst_bin_add (GST_BIN_CAST (playsink), playsink->audio_tee);
4590 }
4591 gst_element_set_state (playsink->audio_tee, GST_STATE_PAUSED);
4592 if (!playsink->audio_pad) {
4593 GST_LOG_OBJECT (playsink, "ghosting tee sinkpad");
4594 playsink->audio_pad =
4595 gst_ghost_pad_new (pad_name, playsink->audio_tee_sink);
4596 playsink->audio_notify_caps_id =
4597 g_signal_connect (G_OBJECT (playsink->audio_pad), "notify::caps",
4598 G_CALLBACK (caps_notify_cb), playsink);
4599 created = TRUE;
4600 }
4601 playsink->audio_pad_raw = FALSE;
4602 res = playsink->audio_pad;
4603 block_id = &playsink->audio_block_id;
4604 break;
4605 case GST_PLAY_SINK_TYPE_VIDEO:
4606 pad_name = "video_sink";
4607 if (!playsink->video_pad) {
4608 GST_LOG_OBJECT (playsink, "ghosting videosink");
4609 playsink->video_pad =
4610 gst_ghost_pad_new_no_target (pad_name, GST_PAD_SINK);
4611 playsink->video_notify_caps_id =
4612 g_signal_connect (G_OBJECT (playsink->video_pad), "notify::caps",
4613 G_CALLBACK (caps_notify_cb), playsink);
4614 created = TRUE;
4615 }
4616 playsink->video_pad_raw = FALSE;
4617 res = playsink->video_pad;
4618 block_id = &playsink->video_block_id;
4619 break;
4620 case GST_PLAY_SINK_TYPE_TEXT:
4621 GST_LOG_OBJECT (playsink, "ghosting text");
4622 if (!playsink->text_pad) {
4623 playsink->text_pad =
4624 gst_ghost_pad_new_no_target ("text_sink", GST_PAD_SINK);
4625 created = TRUE;
4626 }
4627 res = playsink->text_pad;
4628 block_id = &playsink->text_block_id;
4629 break;
4630 case GST_PLAY_SINK_TYPE_FLUSHING:
4631 {
4632 gchar *padname;
4633
4634 /* we need a unique padname for the flushing pad. */
4635 padname = g_strdup_printf ("flushing_%u", playsink->count);
4636 res = gst_ghost_pad_new_no_target (padname, GST_PAD_SINK);
4637 g_free (padname);
4638 playsink->count++;
4639 activate = FALSE;
4640 created = TRUE;
4641 break;
4642 }
4643 default:
4644 res = NULL;
4645 break;
4646 }
4647 GST_PLAY_SINK_UNLOCK (playsink);
4648
4649 if (created && res) {
4650 /* we have to add the pad when it's active or we get an error when the
4651 * element is 'running' */
4652 gst_pad_set_active (res, TRUE);
4653 gst_element_add_pad (GST_ELEMENT_CAST (playsink), res);
4654
4655 GST_PLAY_SINK_LOCK (playsink);
4656 if (block_id && *block_id == 0) {
4657 GstPad *blockpad =
4658 GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (res)));
4659
4660 if (playsink->vis_pad_block_id)
4661 gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->
4662 blockpad, playsink->vis_pad_block_id);
4663 playsink->vis_pad_block_id = 0;
4664
4665 *block_id =
4666 gst_pad_add_probe (blockpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
4667 sinkpad_blocked_cb, playsink, NULL);
4668 PENDING_FLAG_SET (playsink, type);
4669 gst_object_unref (blockpad);
4670 }
4671 GST_PLAY_SINK_UNLOCK (playsink);
4672 if (!activate)
4673 gst_pad_set_active (res, activate);
4674 }
4675
4676 return res;
4677 }
4678
4679
4680 static GstPad *
gst_play_sink_request_new_pad(GstElement * element,GstPadTemplate * templ,const gchar * name,const GstCaps * caps)4681 gst_play_sink_request_new_pad (GstElement * element, GstPadTemplate * templ,
4682 const gchar * name, const GstCaps * caps)
4683 {
4684 GstPlaySink *psink;
4685 GstPad *pad;
4686 GstPlaySinkType type;
4687 const gchar *tplname;
4688
4689 g_return_val_if_fail (templ != NULL, NULL);
4690
4691 GST_DEBUG_OBJECT (element, "name:%s", name);
4692
4693 psink = GST_PLAY_SINK (element);
4694 tplname = GST_PAD_TEMPLATE_NAME_TEMPLATE (templ);
4695
4696 /* Figure out the GstPlaySinkType based on the template */
4697 if (!strcmp (tplname, "audio_sink") || !strcmp (tplname, "audio_raw_sink"))
4698 type = GST_PLAY_SINK_TYPE_AUDIO;
4699 else if (!strcmp (tplname, "video_sink") ||
4700 !strcmp (tplname, "video_raw_sink"))
4701 type = GST_PLAY_SINK_TYPE_VIDEO;
4702 else if (!strcmp (tplname, "text_sink"))
4703 type = GST_PLAY_SINK_TYPE_TEXT;
4704 else
4705 goto unknown_template;
4706
4707 pad = gst_play_sink_request_pad (psink, type);
4708 return pad;
4709
4710 unknown_template:
4711 GST_WARNING_OBJECT (element, "Unknown pad template");
4712 return NULL;
4713 }
4714
4715 void
gst_play_sink_release_pad(GstPlaySink * playsink,GstPad * pad)4716 gst_play_sink_release_pad (GstPlaySink * playsink, GstPad * pad)
4717 {
4718 GstPad **res = NULL;
4719 gboolean untarget = TRUE;
4720
4721 GST_DEBUG_OBJECT (playsink, "release pad %" GST_PTR_FORMAT, pad);
4722
4723 GST_PLAY_SINK_LOCK (playsink);
4724 if (pad == playsink->video_pad) {
4725 res = &playsink->video_pad;
4726 g_signal_handler_disconnect (playsink->video_pad,
4727 playsink->video_notify_caps_id);
4728 video_set_blocked (playsink, FALSE);
4729 } else if (pad == playsink->audio_pad) {
4730 res = &playsink->audio_pad;
4731 g_signal_handler_disconnect (playsink->audio_pad,
4732 playsink->audio_notify_caps_id);
4733 audio_set_blocked (playsink, FALSE);
4734 } else if (pad == playsink->text_pad) {
4735 res = &playsink->text_pad;
4736 text_set_blocked (playsink, FALSE);
4737 } else {
4738 /* try to release the given pad anyway, these could be the FLUSHING pads. */
4739 res = &pad;
4740 untarget = FALSE;
4741 }
4742
4743 GST_PLAY_SINK_UNLOCK (playsink);
4744
4745 if (*res) {
4746 GST_DEBUG_OBJECT (playsink, "deactivate pad %" GST_PTR_FORMAT, *res);
4747 gst_pad_set_active (*res, FALSE);
4748 if (untarget) {
4749 GST_DEBUG_OBJECT (playsink, "untargeting pad %" GST_PTR_FORMAT, *res);
4750 gst_ghost_pad_set_target (GST_GHOST_PAD_CAST (*res), NULL);
4751 }
4752 GST_DEBUG_OBJECT (playsink, "remove pad %" GST_PTR_FORMAT, *res);
4753 gst_element_remove_pad (GST_ELEMENT_CAST (playsink), *res);
4754 *res = NULL;
4755 }
4756
4757 GST_PLAY_SINK_LOCK (playsink);
4758
4759 /* If we have a pending reconfigure, we might have met the conditions
4760 * to reconfigure now */
4761 if (gst_play_sink_ready_to_reconfigure_locked (playsink)) {
4762 GST_DEBUG_OBJECT (playsink,
4763 "All pads ready after release -- reconfiguring");
4764
4765 gst_play_sink_do_reconfigure (playsink);
4766
4767 video_set_blocked (playsink, FALSE);
4768 audio_set_blocked (playsink, FALSE);
4769 text_set_blocked (playsink, FALSE);
4770 }
4771
4772 GST_PLAY_SINK_UNLOCK (playsink);
4773 }
4774
4775 static void
gst_play_sink_release_request_pad(GstElement * element,GstPad * pad)4776 gst_play_sink_release_request_pad (GstElement * element, GstPad * pad)
4777 {
4778 GstPlaySink *psink = GST_PLAY_SINK (element);
4779
4780 gst_play_sink_release_pad (psink, pad);
4781 }
4782
4783 static void
gst_play_sink_handle_message(GstBin * bin,GstMessage * message)4784 gst_play_sink_handle_message (GstBin * bin, GstMessage * message)
4785 {
4786 GstPlaySink *playsink;
4787
4788 playsink = GST_PLAY_SINK_CAST (bin);
4789
4790 switch (GST_MESSAGE_TYPE (message)) {
4791 case GST_MESSAGE_STEP_DONE:
4792 {
4793 GstFormat format;
4794 guint64 amount;
4795 gdouble rate;
4796 gboolean flush, intermediate, eos;
4797 guint64 duration;
4798
4799 GST_INFO_OBJECT (playsink, "Handling step-done message");
4800 gst_message_parse_step_done (message, &format, &amount, &rate, &flush,
4801 &intermediate, &duration, &eos);
4802
4803 if (format == GST_FORMAT_BUFFERS) {
4804 /* for the buffer format, we align the other streams */
4805 if (playsink->audiochain
4806 && !gst_object_has_as_ancestor (GST_MESSAGE_SRC (message),
4807 GST_OBJECT (playsink->audiochain->chain.bin))) {
4808 GstEvent *event;
4809
4810 event =
4811 gst_event_new_step (GST_FORMAT_TIME, duration, rate, flush,
4812 intermediate);
4813
4814 if (!gst_element_send_event (playsink->audiochain->chain.bin, event)) {
4815 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4816 }
4817 }
4818 }
4819 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4820 break;
4821 }
4822 case GST_MESSAGE_ELEMENT:{
4823 if (gst_is_video_overlay_prepare_window_handle_message (message)) {
4824 GstVideoOverlay *overlay;
4825
4826 GST_OBJECT_LOCK (playsink);
4827 if (playsink->overlay_element
4828 && GST_OBJECT_CAST (playsink->overlay_element) !=
4829 GST_MESSAGE_SRC (message)) {
4830 gst_object_unref (playsink->overlay_element);
4831 playsink->overlay_element = NULL;
4832 }
4833
4834 if (!playsink->overlay_element)
4835 playsink->overlay_element =
4836 GST_VIDEO_OVERLAY (gst_object_ref (GST_MESSAGE_SRC (message)));
4837 overlay =
4838 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
4839 GST_OBJECT_UNLOCK (playsink);
4840
4841 GST_DEBUG_OBJECT (playsink, "Got prepare-xwindow-id message");
4842
4843 if (playsink->overlay_handle_set)
4844 gst_video_overlay_set_window_handle (playsink->overlay_element,
4845 playsink->overlay_handle);
4846 if (playsink->overlay_handle_events_set)
4847 gst_video_overlay_handle_events (playsink->overlay_element,
4848 playsink->overlay_handle_events);
4849 if (playsink->overlay_render_rectangle_set)
4850 gst_video_overlay_set_render_rectangle (playsink->overlay_element,
4851 playsink->overlay_x, playsink->overlay_y,
4852 playsink->overlay_width, playsink->overlay_height);
4853
4854 gst_object_unref (overlay);
4855 gst_message_unref (message);
4856 gst_video_overlay_prepare_window_handle (GST_VIDEO_OVERLAY (playsink));
4857 } else {
4858 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin,
4859 message);
4860 }
4861 break;
4862 }
4863 default:
4864 GST_BIN_CLASS (gst_play_sink_parent_class)->handle_message (bin, message);
4865 break;
4866 }
4867 }
4868
4869 /* Send an event to our sinks until one of them works; don't then send to the
4870 * remaining sinks (unlike GstBin)
4871 * Special case: If a text sink is set we need to send the event
4872 * to them in case it's source is different from the a/v stream's source.
4873 */
4874 static gboolean
gst_play_sink_send_event_to_sink(GstPlaySink * playsink,GstEvent * event,gboolean force_video)4875 gst_play_sink_send_event_to_sink (GstPlaySink * playsink, GstEvent * event,
4876 gboolean force_video)
4877 {
4878 gboolean res = TRUE;
4879 if (playsink->send_event_mode == MODE_FIRST || force_video) {
4880 if (playsink->textchain && playsink->textchain->sink) {
4881 gst_event_ref (event);
4882 if ((res =
4883 gst_element_send_event (playsink->textchain->chain.bin, event))) {
4884 GST_DEBUG_OBJECT (playsink, "Sent event successfully to text sink");
4885 } else {
4886 GST_DEBUG_OBJECT (playsink, "Event failed when sent to text sink");
4887 }
4888 }
4889
4890 if (playsink->videochain) {
4891 gst_event_ref (event);
4892 if ((res =
4893 gst_element_send_event (playsink->videochain->chain.bin,
4894 event))) {
4895 GST_DEBUG_OBJECT (playsink, "Sent event successfully to video sink");
4896 goto done;
4897 }
4898 GST_DEBUG_OBJECT (playsink, "Event failed when sent to video sink");
4899 }
4900 if (!force_video && playsink->audiochain) {
4901 gst_event_ref (event);
4902 if ((res =
4903 gst_element_send_event (playsink->audiochain->chain.bin,
4904 event))) {
4905 GST_DEBUG_OBJECT (playsink, "Sent event successfully to audio sink");
4906 goto done;
4907 }
4908 GST_DEBUG_OBJECT (playsink, "Event failed when sent to audio sink");
4909 } else {
4910 res = FALSE;
4911 }
4912 } else {
4913 return
4914 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event
4915 (GST_ELEMENT_CAST (playsink), event);
4916 }
4917
4918 done:
4919 gst_event_unref (event);
4920 return res;
4921 }
4922
4923 /* We only want to send the event to a single sink (overriding GstBin's
4924 * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
4925 * events appropriately. So, this is a messy duplication of code. */
4926 static gboolean
gst_play_sink_send_event(GstElement * element,GstEvent * event)4927 gst_play_sink_send_event (GstElement * element, GstEvent * event)
4928 {
4929 gboolean res = FALSE;
4930 GstEventType event_type = GST_EVENT_TYPE (event);
4931 GstPlaySink *playsink;
4932 playsink = GST_PLAY_SINK_CAST (element);
4933 switch (event_type) {
4934 case GST_EVENT_SEEK:
4935 GST_DEBUG_OBJECT (element, "Sending event to a sink");
4936 res = gst_play_sink_send_event_to_sink (playsink, event, FALSE);
4937 break;
4938 case GST_EVENT_STEP:
4939 {
4940 GstFormat format;
4941 guint64 amount;
4942 gdouble rate;
4943 gboolean flush, intermediate;
4944 gst_event_parse_step (event, &format, &amount, &rate, &flush,
4945 &intermediate);
4946 if (format == GST_FORMAT_BUFFERS) {
4947 /* for buffers, we will try to step video frames, for other formats we
4948 * send the step to all sinks */
4949 res = gst_play_sink_send_event_to_sink (playsink, event, TRUE);
4950 } else {
4951 res =
4952 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4953 event);
4954 }
4955 break;
4956 }
4957 default:
4958 res =
4959 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->send_event (element,
4960 event);
4961 break;
4962 }
4963 return res;
4964 }
4965
4966 static GstStateChangeReturn
gst_play_sink_change_state(GstElement * element,GstStateChange transition)4967 gst_play_sink_change_state (GstElement * element, GstStateChange transition)
4968 {
4969 GstStateChangeReturn ret;
4970 GstStateChangeReturn bret;
4971 GstPlaySink *playsink;
4972 playsink = GST_PLAY_SINK (element);
4973 switch (transition) {
4974 case GST_STATE_CHANGE_READY_TO_PAUSED:
4975 playsink->need_async_start = TRUE;
4976 /* we want to go async to PAUSED until we managed to configure and add the
4977 * sinks */
4978 do_async_start (playsink);
4979 ret = GST_STATE_CHANGE_ASYNC;
4980
4981 /* block all pads here */
4982 if (!gst_play_sink_reconfigure (playsink)) {
4983 ret = GST_STATE_CHANGE_FAILURE;
4984 goto activate_failed;
4985 }
4986 break;
4987 case GST_STATE_CHANGE_PAUSED_TO_READY:
4988 /* unblock all pads here */
4989 GST_PLAY_SINK_LOCK (playsink);
4990 video_set_blocked (playsink, FALSE);
4991 audio_set_blocked (playsink, FALSE);
4992 text_set_blocked (playsink, FALSE);
4993 if (playsink->vis_pad_block_id)
4994 gst_pad_remove_probe (((GstPlayVisChain *) playsink->vischain)->
4995 blockpad, playsink->vis_pad_block_id);
4996 playsink->vis_pad_block_id = 0;
4997
4998 GST_PLAY_SINK_UNLOCK (playsink);
4999 /* fall through */
5000 case GST_STATE_CHANGE_READY_TO_NULL:
5001 if (playsink->audiochain && playsink->audiochain->sink_volume) {
5002 /* remove our links to the volume elements when they were
5003 * provided by a sink */
5004 disconnect_audio_chain (playsink->audiochain, playsink);
5005 if (playsink->audiochain->volume)
5006 gst_object_unref (playsink->audiochain->volume);
5007 playsink->audiochain->volume = NULL;
5008 }
5009
5010 if (playsink->audiochain && playsink->audiochain->ts_offset) {
5011 gst_object_unref (playsink->audiochain->ts_offset);
5012 playsink->audiochain->ts_offset = NULL;
5013 }
5014
5015 if (playsink->videochain && playsink->videochain->ts_offset) {
5016 gst_object_unref (playsink->videochain->ts_offset);
5017 playsink->videochain->ts_offset = NULL;
5018 }
5019
5020 GST_OBJECT_LOCK (playsink);
5021 if (playsink->overlay_element)
5022 gst_object_unref (playsink->overlay_element);
5023 playsink->overlay_element = NULL;
5024
5025 if (playsink->colorbalance_element) {
5026 g_signal_handler_disconnect (playsink->colorbalance_element,
5027 playsink->colorbalance_value_changed_id);
5028 playsink->colorbalance_value_changed_id = 0;
5029 gst_object_unref (playsink->colorbalance_element);
5030 }
5031 playsink->colorbalance_element = NULL;
5032 GST_OBJECT_UNLOCK (playsink);
5033
5034 ret = GST_STATE_CHANGE_SUCCESS;
5035 break;
5036 default:
5037 /* all other state changes return SUCCESS by default, this value can be
5038 * overridden by the result of the children */
5039 ret = GST_STATE_CHANGE_SUCCESS;
5040 break;
5041 }
5042
5043 /* do the state change of the children */
5044 bret =
5045 GST_ELEMENT_CLASS (gst_play_sink_parent_class)->change_state (element,
5046 transition);
5047 /* now look at the result of our children and adjust the return value */
5048 switch (bret) {
5049 case GST_STATE_CHANGE_FAILURE:
5050 /* failure, we stop */
5051 goto activate_failed;
5052 case GST_STATE_CHANGE_NO_PREROLL:
5053 /* some child returned NO_PREROLL. This is strange but we never know. We
5054 * commit our async state change (if any) and return the NO_PREROLL */
5055 do_async_done (playsink);
5056 ret = bret;
5057 break;
5058 case GST_STATE_CHANGE_ASYNC:
5059 /* some child was async, return this */
5060 ret = bret;
5061 break;
5062 default:
5063 /* return our previously configured return value */
5064 break;
5065 }
5066
5067 switch (transition) {
5068 case GST_STATE_CHANGE_READY_TO_PAUSED:
5069 break;
5070 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
5071 /* FIXME Release audio device when we implement that */
5072 playsink->need_async_start = TRUE;
5073 break;
5074 case GST_STATE_CHANGE_PAUSED_TO_READY:{
5075 if (playsink->video_sinkpad_stream_synchronizer) {
5076 gst_element_release_request_pad (GST_ELEMENT_CAST
5077 (playsink->stream_synchronizer),
5078 playsink->video_sinkpad_stream_synchronizer);
5079 gst_object_unref (playsink->video_sinkpad_stream_synchronizer);
5080 playsink->video_sinkpad_stream_synchronizer = NULL;
5081 gst_object_unref (playsink->video_srcpad_stream_synchronizer);
5082 playsink->video_srcpad_stream_synchronizer = NULL;
5083 }
5084 if (playsink->audio_sinkpad_stream_synchronizer) {
5085 gst_element_release_request_pad (GST_ELEMENT_CAST
5086 (playsink->stream_synchronizer),
5087 playsink->audio_sinkpad_stream_synchronizer);
5088 gst_object_unref (playsink->audio_sinkpad_stream_synchronizer);
5089 playsink->audio_sinkpad_stream_synchronizer = NULL;
5090 gst_object_unref (playsink->audio_srcpad_stream_synchronizer);
5091 playsink->audio_srcpad_stream_synchronizer = NULL;
5092
5093 gst_play_sink_remove_audio_ssync_queue (playsink);
5094 }
5095 if (playsink->text_sinkpad_stream_synchronizer) {
5096 gst_element_release_request_pad (GST_ELEMENT_CAST
5097 (playsink->stream_synchronizer),
5098 playsink->text_sinkpad_stream_synchronizer);
5099 gst_object_unref (playsink->text_sinkpad_stream_synchronizer);
5100 playsink->text_sinkpad_stream_synchronizer = NULL;
5101 gst_object_unref (playsink->text_srcpad_stream_synchronizer);
5102 playsink->text_srcpad_stream_synchronizer = NULL;
5103 }
5104 }
5105 /* fall through */
5106 case GST_STATE_CHANGE_READY_TO_NULL:
5107 /* remove sinks we added */
5108 if (playsink->videodeinterlacechain) {
5109 activate_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain),
5110 FALSE);
5111 add_chain (GST_PLAY_CHAIN (playsink->videodeinterlacechain), FALSE);
5112 }
5113 if (playsink->videochain) {
5114 activate_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
5115 add_chain (GST_PLAY_CHAIN (playsink->videochain), FALSE);
5116 }
5117 if (playsink->audiochain) {
5118 activate_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
5119 add_chain (GST_PLAY_CHAIN (playsink->audiochain), FALSE);
5120 }
5121 if (playsink->vischain) {
5122 activate_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
5123 add_chain (GST_PLAY_CHAIN (playsink->vischain), FALSE);
5124 }
5125 if (playsink->textchain) {
5126 activate_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
5127 add_chain (GST_PLAY_CHAIN (playsink->textchain), FALSE);
5128 }
5129 do_async_done (playsink);
5130 /* when going to READY, keep elements around as long as possible,
5131 * so they may be re-used faster next time/url around.
5132 * when really going to NULL, clean up everything completely. */
5133 if (transition == GST_STATE_CHANGE_READY_TO_NULL) {
5134
5135 /* Unparent the sinks to allow reuse */
5136 if (playsink->videochain && playsink->videochain->sink)
5137 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
5138 playsink->videochain->sink);
5139 if (playsink->audiochain && playsink->audiochain->sink)
5140 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
5141 playsink->audiochain->sink);
5142 if (playsink->textchain && playsink->textchain->sink)
5143 gst_bin_remove (GST_BIN_CAST (playsink->textchain->chain.bin),
5144 playsink->textchain->sink);
5145 if (playsink->audio_sink != NULL)
5146 gst_element_set_state (playsink->audio_sink, GST_STATE_NULL);
5147 if (playsink->video_sink != NULL)
5148 gst_element_set_state (playsink->video_sink, GST_STATE_NULL);
5149 if (playsink->visualisation != NULL)
5150 gst_element_set_state (playsink->visualisation, GST_STATE_NULL);
5151 if (playsink->text_sink != NULL)
5152 gst_element_set_state (playsink->text_sink, GST_STATE_NULL);
5153
5154 /* Unparent the filters to allow reuse */
5155 if (playsink->videochain && playsink->videochain->filter)
5156 gst_bin_remove (GST_BIN_CAST (playsink->videochain->chain.bin),
5157 playsink->videochain->filter);
5158 if (playsink->audiochain && playsink->audiochain->filter)
5159 gst_bin_remove (GST_BIN_CAST (playsink->audiochain->chain.bin),
5160 playsink->audiochain->filter);
5161 if (playsink->audio_filter != NULL)
5162 gst_element_set_state (playsink->audio_filter, GST_STATE_NULL);
5163 if (playsink->video_filter != NULL)
5164 gst_element_set_state (playsink->video_filter, GST_STATE_NULL);
5165
5166 free_chain ((GstPlayChain *) playsink->videodeinterlacechain);
5167 playsink->videodeinterlacechain = NULL;
5168 free_chain ((GstPlayChain *) playsink->videochain);
5169 playsink->videochain = NULL;
5170 free_chain ((GstPlayChain *) playsink->audiochain);
5171 playsink->audiochain = NULL;
5172 free_chain ((GstPlayChain *) playsink->vischain);
5173 playsink->vischain = NULL;
5174 free_chain ((GstPlayChain *) playsink->textchain);
5175 playsink->textchain = NULL;
5176 }
5177 break;
5178 default:
5179 break;
5180 }
5181 return ret;
5182 /* ERRORS */
5183 activate_failed:
5184 {
5185 GST_DEBUG_OBJECT (element,
5186 "element failed to change states -- activation problem?");
5187 do_async_done (playsink);
5188 return GST_STATE_CHANGE_FAILURE;
5189 }
5190 }
5191
5192 static void
gst_play_sink_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * spec)5193 gst_play_sink_set_property (GObject * object, guint prop_id,
5194 const GValue * value, GParamSpec * spec)
5195 {
5196 GstPlaySink *playsink = GST_PLAY_SINK (object);
5197 switch (prop_id) {
5198 case PROP_FLAGS:
5199 gst_play_sink_set_flags (playsink, g_value_get_flags (value));
5200 break;
5201 case PROP_VOLUME:
5202 gst_play_sink_set_volume (playsink, g_value_get_double (value));
5203 break;
5204 case PROP_MUTE:
5205 gst_play_sink_set_mute (playsink, g_value_get_boolean (value));
5206 break;
5207 case PROP_FONT_DESC:
5208 gst_play_sink_set_font_desc (playsink, g_value_get_string (value));
5209 break;
5210 case PROP_SUBTITLE_ENCODING:
5211 gst_play_sink_set_subtitle_encoding (playsink,
5212 g_value_get_string (value));
5213 break;
5214 case PROP_VIS_PLUGIN:
5215 gst_play_sink_set_vis_plugin (playsink, g_value_get_object (value));
5216 break;
5217 case PROP_AV_OFFSET:
5218 gst_play_sink_set_av_offset (playsink, g_value_get_int64 (value));
5219 break;
5220 case PROP_TEXT_OFFSET:
5221 gst_play_sink_set_text_offset (playsink, g_value_get_int64 (value));
5222 break;
5223 case PROP_VIDEO_FILTER:
5224 gst_play_sink_set_filter (playsink, GST_PLAY_SINK_TYPE_VIDEO,
5225 g_value_get_object (value));
5226 break;
5227 case PROP_AUDIO_FILTER:
5228 gst_play_sink_set_filter (playsink, GST_PLAY_SINK_TYPE_AUDIO,
5229 g_value_get_object (value));
5230 break;
5231 case PROP_VIDEO_SINK:
5232 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_VIDEO,
5233 g_value_get_object (value));
5234 break;
5235 case PROP_AUDIO_SINK:
5236 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_AUDIO,
5237 g_value_get_object (value));
5238 break;
5239 case PROP_TEXT_SINK:
5240 gst_play_sink_set_sink (playsink, GST_PLAY_SINK_TYPE_TEXT,
5241 g_value_get_object (value));
5242 break;
5243 case PROP_SEND_EVENT_MODE:
5244 playsink->send_event_mode = g_value_get_enum (value);
5245 break;
5246 case PROP_FORCE_ASPECT_RATIO:{
5247 GstPlayVideoChain *chain;
5248 GstElement *elem;
5249
5250 playsink->force_aspect_ratio = g_value_get_boolean (value);
5251
5252 GST_PLAY_SINK_LOCK (playsink);
5253 if (playsink->videochain) {
5254 chain = (GstPlayVideoChain *) playsink->videochain;
5255
5256 if (chain->sink) {
5257 elem =
5258 gst_play_sink_find_property_sinks (playsink, chain->sink,
5259 "force-aspect-ratio", G_TYPE_BOOLEAN);
5260
5261 if (elem)
5262 g_object_set (elem, "force-aspect-ratio",
5263 playsink->force_aspect_ratio, NULL);
5264 }
5265 }
5266 GST_PLAY_SINK_UNLOCK (playsink);
5267 break;
5268 }
5269 default:
5270 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
5271 break;
5272 }
5273 }
5274
5275 static void
gst_play_sink_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * spec)5276 gst_play_sink_get_property (GObject * object, guint prop_id,
5277 GValue * value, GParamSpec * spec)
5278 {
5279 GstPlaySink *playsink = GST_PLAY_SINK (object);
5280 switch (prop_id) {
5281 case PROP_FLAGS:
5282 g_value_set_flags (value, gst_play_sink_get_flags (playsink));
5283 break;
5284 case PROP_VOLUME:
5285 g_value_set_double (value, gst_play_sink_get_volume (playsink));
5286 break;
5287 case PROP_MUTE:
5288 g_value_set_boolean (value, gst_play_sink_get_mute (playsink));
5289 break;
5290 case PROP_FONT_DESC:
5291 g_value_take_string (value, gst_play_sink_get_font_desc (playsink));
5292 break;
5293 case PROP_SUBTITLE_ENCODING:
5294 g_value_take_string (value,
5295 gst_play_sink_get_subtitle_encoding (playsink));
5296 break;
5297 case PROP_VIS_PLUGIN:
5298 g_value_take_object (value, gst_play_sink_get_vis_plugin (playsink));
5299 break;
5300 case PROP_SAMPLE:
5301 gst_value_take_sample (value, gst_play_sink_get_last_sample (playsink));
5302 break;
5303 case PROP_AV_OFFSET:
5304 g_value_set_int64 (value, gst_play_sink_get_av_offset (playsink));
5305 break;
5306 case PROP_TEXT_OFFSET:
5307 g_value_set_int64 (value, gst_play_sink_get_text_offset (playsink));
5308 break;
5309 case PROP_VIDEO_FILTER:
5310 g_value_take_object (value, gst_play_sink_get_filter (playsink,
5311 GST_PLAY_SINK_TYPE_VIDEO));
5312 break;
5313 case PROP_AUDIO_FILTER:
5314 g_value_take_object (value, gst_play_sink_get_filter (playsink,
5315 GST_PLAY_SINK_TYPE_AUDIO));
5316 break;
5317 case PROP_VIDEO_SINK:
5318 g_value_take_object (value, gst_play_sink_get_sink (playsink,
5319 GST_PLAY_SINK_TYPE_VIDEO));
5320 break;
5321 case PROP_AUDIO_SINK:
5322 g_value_take_object (value, gst_play_sink_get_sink (playsink,
5323 GST_PLAY_SINK_TYPE_AUDIO));
5324 break;
5325 case PROP_TEXT_SINK:
5326 g_value_take_object (value, gst_play_sink_get_sink (playsink,
5327 GST_PLAY_SINK_TYPE_TEXT));
5328 break;
5329 case PROP_SEND_EVENT_MODE:
5330 g_value_set_enum (value, playsink->send_event_mode);
5331 break;
5332 case PROP_FORCE_ASPECT_RATIO:
5333 g_value_set_boolean (value, playsink->force_aspect_ratio);
5334 break;
5335 default:
5336 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, spec);
5337 break;
5338 }
5339 }
5340
5341 static void
gst_play_sink_overlay_expose(GstVideoOverlay * overlay)5342 gst_play_sink_overlay_expose (GstVideoOverlay * overlay)
5343 {
5344 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
5345 GstVideoOverlay *overlay_element;
5346
5347 GST_OBJECT_LOCK (playsink);
5348 if (playsink->overlay_element)
5349 overlay_element =
5350 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
5351 else
5352 overlay_element = NULL;
5353 GST_OBJECT_UNLOCK (playsink);
5354
5355 if (overlay_element) {
5356 gst_video_overlay_expose (overlay_element);
5357 gst_object_unref (overlay_element);
5358 }
5359 }
5360
5361 static void
gst_play_sink_overlay_handle_events(GstVideoOverlay * overlay,gboolean handle_events)5362 gst_play_sink_overlay_handle_events (GstVideoOverlay * overlay,
5363 gboolean handle_events)
5364 {
5365 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
5366 GstVideoOverlay *overlay_element;
5367
5368 GST_OBJECT_LOCK (playsink);
5369 if (playsink->overlay_element)
5370 overlay_element =
5371 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
5372 else
5373 overlay_element = NULL;
5374 GST_OBJECT_UNLOCK (playsink);
5375
5376 playsink->overlay_handle_events_set = TRUE;
5377 playsink->overlay_handle_events = handle_events;
5378
5379 if (overlay_element) {
5380 gst_video_overlay_handle_events (overlay_element, handle_events);
5381 gst_object_unref (overlay_element);
5382 }
5383 }
5384
5385 static void
gst_play_sink_overlay_set_render_rectangle(GstVideoOverlay * overlay,gint x,gint y,gint width,gint height)5386 gst_play_sink_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
5387 gint y, gint width, gint height)
5388 {
5389 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
5390 GstVideoOverlay *overlay_element;
5391
5392 GST_OBJECT_LOCK (playsink);
5393 if (playsink->overlay_element)
5394 overlay_element =
5395 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
5396 else
5397 overlay_element = NULL;
5398 GST_OBJECT_UNLOCK (playsink);
5399
5400 playsink->overlay_render_rectangle_set = TRUE;
5401 playsink->overlay_x = x;
5402 playsink->overlay_y = y;
5403 playsink->overlay_width = width;
5404 playsink->overlay_height = height;
5405
5406 if (overlay_element) {
5407 gst_video_overlay_set_render_rectangle (overlay_element, x, y, width,
5408 height);
5409 gst_object_unref (overlay_element);
5410 }
5411 }
5412
5413 static void
gst_play_sink_overlay_set_window_handle(GstVideoOverlay * overlay,guintptr handle)5414 gst_play_sink_overlay_set_window_handle (GstVideoOverlay * overlay,
5415 guintptr handle)
5416 {
5417 GstPlaySink *playsink = GST_PLAY_SINK (overlay);
5418 GstVideoOverlay *overlay_element;
5419
5420 GST_OBJECT_LOCK (playsink);
5421 if (playsink->overlay_element)
5422 overlay_element =
5423 GST_VIDEO_OVERLAY (gst_object_ref (playsink->overlay_element));
5424 else
5425 overlay_element = NULL;
5426 GST_OBJECT_UNLOCK (playsink);
5427
5428 playsink->overlay_handle_set = TRUE;
5429 playsink->overlay_handle = handle;
5430
5431 if (overlay_element) {
5432 gst_video_overlay_set_window_handle (overlay_element, handle);
5433 gst_object_unref (overlay_element);
5434 }
5435 }
5436
5437 static void
gst_play_sink_overlay_init(gpointer g_iface,gpointer g_iface_data)5438 gst_play_sink_overlay_init (gpointer g_iface, gpointer g_iface_data)
5439 {
5440 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
5441 iface->expose = gst_play_sink_overlay_expose;
5442 iface->handle_events = gst_play_sink_overlay_handle_events;
5443 iface->set_render_rectangle = gst_play_sink_overlay_set_render_rectangle;
5444 iface->set_window_handle = gst_play_sink_overlay_set_window_handle;
5445 }
5446
5447 static void
gst_play_sink_navigation_send_event(GstNavigation * navigation,GstStructure * structure)5448 gst_play_sink_navigation_send_event (GstNavigation * navigation,
5449 GstStructure * structure)
5450 {
5451 GstPlaySink *playsink = GST_PLAY_SINK (navigation);
5452 GstBin *bin = NULL;
5453
5454 GST_PLAY_SINK_LOCK (playsink);
5455 if (playsink->videochain && playsink->videochain->chain.bin)
5456 bin = GST_BIN (gst_object_ref (playsink->videochain->chain.bin));
5457 GST_PLAY_SINK_UNLOCK (playsink);
5458
5459 if (bin) {
5460 GstElement *nav = gst_bin_get_by_interface (bin, GST_TYPE_NAVIGATION);
5461
5462 if (nav) {
5463 gst_navigation_send_event (GST_NAVIGATION (nav), structure);
5464 structure = NULL;
5465 gst_object_unref (nav);
5466 } else {
5467 GstEvent *event = gst_event_new_navigation (structure);
5468 structure = NULL;
5469 gst_element_send_event (GST_ELEMENT (bin), event);
5470 }
5471
5472 gst_object_unref (bin);
5473 }
5474
5475 if (structure)
5476 gst_structure_free (structure);
5477 }
5478
5479 static void
gst_play_sink_navigation_init(gpointer g_iface,gpointer g_iface_data)5480 gst_play_sink_navigation_init (gpointer g_iface, gpointer g_iface_data)
5481 {
5482 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
5483
5484 iface->send_event = gst_play_sink_navigation_send_event;
5485 }
5486
5487 static const GList *
gst_play_sink_colorbalance_list_channels(GstColorBalance * balance)5488 gst_play_sink_colorbalance_list_channels (GstColorBalance * balance)
5489 {
5490 GstPlaySink *playsink = GST_PLAY_SINK (balance);
5491
5492 return playsink->colorbalance_channels;
5493 }
5494
5495 static void
gst_play_sink_colorbalance_set_value(GstColorBalance * balance,GstColorBalanceChannel * proxy,gint value)5496 gst_play_sink_colorbalance_set_value (GstColorBalance * balance,
5497 GstColorBalanceChannel * proxy, gint value)
5498 {
5499 GstPlaySink *playsink = GST_PLAY_SINK (balance);
5500 GList *l;
5501 gint i;
5502 GstColorBalance *balance_element = NULL;
5503
5504 GST_OBJECT_LOCK (playsink);
5505 if (playsink->colorbalance_element)
5506 balance_element =
5507 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
5508 GST_OBJECT_UNLOCK (playsink);
5509
5510 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
5511 GstColorBalanceChannel *proxy_tmp = l->data;
5512 gdouble new_val;
5513
5514 if (proxy_tmp != proxy)
5515 continue;
5516
5517 playsink->colorbalance_values[i] = value;
5518
5519 if (balance_element) {
5520 GstColorBalanceChannel *channel = NULL;
5521 const GList *channels, *k;
5522
5523 channels = gst_color_balance_list_channels (balance_element);
5524 for (k = channels; k; k = k->next) {
5525 GstColorBalanceChannel *tmp = k->data;
5526
5527 if (g_strrstr (tmp->label, proxy->label)) {
5528 channel = tmp;
5529 break;
5530 }
5531 }
5532
5533 g_assert (channel);
5534
5535 /* Convert to [0, 1] range */
5536 new_val =
5537 ((gdouble) value -
5538 (gdouble) proxy->min_value) / ((gdouble) proxy->max_value -
5539 (gdouble) proxy->min_value);
5540 /* Convert to channel range */
5541 new_val =
5542 channel->min_value + new_val * ((gdouble) channel->max_value -
5543 (gdouble) channel->min_value);
5544
5545 gst_color_balance_set_value (balance_element, channel,
5546 (gint) (new_val + 0.5));
5547
5548 gst_object_unref (balance_element);
5549 }
5550
5551 gst_color_balance_value_changed (balance, proxy, value);
5552 break;
5553 }
5554 }
5555
5556 static gint
gst_play_sink_colorbalance_get_value(GstColorBalance * balance,GstColorBalanceChannel * proxy)5557 gst_play_sink_colorbalance_get_value (GstColorBalance * balance,
5558 GstColorBalanceChannel * proxy)
5559 {
5560 GstPlaySink *playsink = GST_PLAY_SINK (balance);
5561 GList *l;
5562 gint i;
5563
5564 for (i = 0, l = playsink->colorbalance_channels; l; l = l->next, i++) {
5565 GstColorBalanceChannel *proxy_tmp = l->data;
5566
5567 if (proxy_tmp != proxy)
5568 continue;
5569
5570 return playsink->colorbalance_values[i];
5571 }
5572
5573 g_return_val_if_reached (0);
5574 }
5575
5576 static GstColorBalanceType
gst_play_sink_colorbalance_get_balance_type(GstColorBalance * balance)5577 gst_play_sink_colorbalance_get_balance_type (GstColorBalance * balance)
5578 {
5579 GstPlaySink *playsink = GST_PLAY_SINK (balance);
5580 GstColorBalance *balance_element = NULL;
5581 GstColorBalanceType t = GST_COLOR_BALANCE_SOFTWARE;
5582
5583 GST_OBJECT_LOCK (playsink);
5584 if (playsink->colorbalance_element)
5585 balance_element =
5586 GST_COLOR_BALANCE (gst_object_ref (playsink->colorbalance_element));
5587 GST_OBJECT_UNLOCK (playsink);
5588
5589 if (balance_element) {
5590 t = gst_color_balance_get_balance_type (balance_element);
5591 gst_object_unref (balance_element);
5592 }
5593
5594 return t;
5595 }
5596
5597 static void
gst_play_sink_colorbalance_init(gpointer g_iface,gpointer g_iface_data)5598 gst_play_sink_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
5599 {
5600 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
5601
5602 iface->list_channels = gst_play_sink_colorbalance_list_channels;
5603 iface->set_value = gst_play_sink_colorbalance_set_value;
5604 iface->get_value = gst_play_sink_colorbalance_get_value;
5605 iface->get_balance_type = gst_play_sink_colorbalance_get_balance_type;
5606 }
5607