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 * Copyright (C) <2013> Collabora Ltd.
5 * Author: Sebastian Dröge <sebastian.droege@collabora.co.uk>
6 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
16 *
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
21 */
22
23 /**
24 * SECTION:element-playbin
25 * @title: playbin
26 *
27 * Playbin provides a stand-alone everything-in-one abstraction for an
28 * audio and/or video player.
29 *
30 * Playbin can handle both audio and video files and features
31 *
32 * * automatic file type recognition and based on that automatic
33 * selection and usage of the right audio/video/subtitle demuxers/decoders
34 * * visualisations for audio files
35 * * subtitle support for video files. Subtitles can be store in external
36 * files.
37 * * stream selection between different video/audio/subtitles streams
38 * * meta info (tag) extraction
39 * * easy access to the last video sample
40 * * buffering when playing streams over a network
41 * * volume control with mute option
42 *
43 * ## Usage
44 *
45 * A playbin element can be created just like any other element using
46 * gst_element_factory_make(). The file/URI to play should be set via the #GstPlayBin:uri
47 * property. This must be an absolute URI, relative file paths are not allowed.
48 * Example URIs are file:///home/joe/movie.avi or http://www.joedoe.com/foo.ogg
49 *
50 * Playbin is a #GstPipeline. It will notify the application of everything
51 * that's happening (errors, end of stream, tags found, state changes, etc.)
52 * by posting messages on its #GstBus. The application needs to watch the
53 * bus.
54 *
55 * Playback can be initiated by setting the element to PLAYING state using
56 * gst_element_set_state(). Note that the state change will take place in
57 * the background in a separate thread, when the function returns playback
58 * is probably not happening yet and any errors might not have occurred yet.
59 * Applications using playbin should ideally be written to deal with things
60 * completely asynchroneous.
61 *
62 * When playback has finished (an EOS message has been received on the bus)
63 * or an error has occurred (an ERROR message has been received on the bus) or
64 * the user wants to play a different track, playbin should be set back to
65 * READY or NULL state, then the #GstPlayBin:uri property should be set to the
66 * new location and then playbin be set to PLAYING state again.
67 *
68 * Seeking can be done using gst_element_seek_simple() or gst_element_seek()
69 * on the playbin element. Again, the seek will not be executed
70 * instantaneously, but will be done in a background thread. When the seek
71 * call returns the seek will most likely still be in process. An application
72 * may wait for the seek to finish (or fail) using gst_element_get_state() with
73 * -1 as the timeout, but this will block the user interface and is not
74 * recommended at all.
75 *
76 * Applications may query the current position and duration of the stream
77 * via gst_element_query_position() and gst_element_query_duration() and
78 * setting the format passed to GST_FORMAT_TIME. If the query was successful,
79 * the duration or position will have been returned in units of nanoseconds.
80 *
81 * ## Advanced Usage: specifying the audio and video sink
82 *
83 * By default, if no audio sink or video sink has been specified via the
84 * #GstPlayBin:audio-sink or #GstPlayBin:video-sink property, playbin will use the autoaudiosink
85 * and autovideosink elements to find the first-best available output method.
86 * This should work in most cases, but is not always desirable. Often either
87 * the user or application might want to specify more explicitly what to use
88 * for audio and video output.
89 *
90 * If the application wants more control over how audio or video should be
91 * output, it may create the audio/video sink elements itself (for example
92 * using gst_element_factory_make()) and provide them to playbin using the
93 * #GstPlayBin:audio-sink or #GstPlayBin:video-sink property.
94 *
95 * GNOME-based applications, for example, will usually want to create
96 * gconfaudiosink and gconfvideosink elements and make playbin use those,
97 * so that output happens to whatever the user has configured in the GNOME
98 * Multimedia System Selector configuration dialog.
99 *
100 * The sink elements do not necessarily need to be ready-made sinks. It is
101 * possible to create container elements that look like a sink to playbin,
102 * but in reality contain a number of custom elements linked together. This
103 * can be achieved by creating a #GstBin and putting elements in there and
104 * linking them, and then creating a sink #GstGhostPad for the bin and pointing
105 * it to the sink pad of the first element within the bin. This can be used
106 * for a number of purposes, for example to force output to a particular
107 * format or to modify or observe the data before it is output.
108 *
109 * It is also possible to 'suppress' audio and/or video output by using
110 * 'fakesink' elements (or capture it from there using the fakesink element's
111 * "handoff" signal, which, nota bene, is fired from the streaming thread!).
112 *
113 * ## Retrieving Tags and Other Meta Data
114 *
115 * Most of the common meta data (artist, title, etc.) can be retrieved by
116 * watching for TAG messages on the pipeline's bus (see above).
117 *
118 * Other more specific meta information like width/height/framerate of video
119 * streams or samplerate/number of channels of audio streams can be obtained
120 * from the negotiated caps on the sink pads of the sinks.
121 *
122 * ## Buffering
123 * Playbin handles buffering automatically for the most part, but applications
124 * need to handle parts of the buffering process as well. Whenever playbin is
125 * buffering, it will post BUFFERING messages on the bus with a percentage
126 * value that shows the progress of the buffering process. Applications need
127 * to set playbin to PLAYING or PAUSED state in response to these messages.
128 * They may also want to convey the buffering progress to the user in some
129 * way. Here is how to extract the percentage information from the message:
130 * |[
131 * switch (GST_MESSAGE_TYPE (msg)) {
132 * case GST_MESSAGE_BUFFERING: {
133 * gint percent = 0;
134 * gst_message_parse_buffering (msg, &percent);
135 * g_print ("Buffering (%u percent done)", percent);
136 * break;
137 * }
138 * ...
139 * }
140 * ]|
141 *
142 * Note that applications should keep/set the pipeline in the PAUSED state when
143 * a BUFFERING message is received with a buffer percent value < 100 and set
144 * the pipeline back to PLAYING state when a BUFFERING message with a value
145 * of 100 percent is received (if PLAYING is the desired state, that is).
146 *
147 * ## Embedding the video window in your application
148 * By default, playbin (or rather the video sinks used) will create their own
149 * window. Applications will usually want to force output to a window of their
150 * own, however. This can be done using the #GstVideoOverlay interface, which most
151 * video sinks implement. See the documentation there for more details.
152 *
153 * ## Specifying which CD/DVD device to use
154 * The device to use for CDs/DVDs needs to be set on the source element
155 * playbin creates before it is opened. The most generic way of doing this
156 * is to connect to playbin's "source-setup" (or "notify::source") signal,
157 * which will be emitted by playbin when it has created the source element
158 * for a particular URI. In the signal callback you can check if the source
159 * element has a "device" property and set it appropriately. In some cases
160 * the device can also be set as part of the URI, but it depends on the
161 * elements involved if this will work or not. For example, for DVD menu
162 * playback, the following syntax might work (if the resindvd plugin is used):
163 * dvd://[/path/to/device]
164 *
165 * ## Handling redirects
166 *
167 * Some elements may post 'redirect' messages on the bus to tell the
168 * application to open another location. These are element messages containing
169 * a structure named 'redirect' along with a 'new-location' field of string
170 * type. The new location may be a relative or an absolute URI. Examples
171 * for such redirects can be found in many quicktime movie trailers.
172 *
173 * NOTE: playbin will internally handle the redirect messages in the case
174 * that the redirecting stream doesn't contain any tracks and thus
175 * needs to report an error message on the bus.
176 *
177 * ## Examples
178 * |[
179 * gst-launch-1.0 -v playbin uri=file:///path/to/somefile.mp4
180 * ]|
181 * This will play back the given AVI video file, given that the video and
182 * audio decoders required to decode the content are installed. Since no
183 * special audio sink or video sink is supplied (via playbin's audio-sink or
184 * video-sink properties) playbin will try to find a suitable audio and
185 * video sink automatically using the autoaudiosink and autovideosink elements.
186 * |[
187 * gst-launch-1.0 -v playbin uri=cdda://4
188 * ]|
189 * This will play back track 4 on an audio CD in your disc drive (assuming
190 * the drive is detected automatically by the plugin).
191 * |[
192 * gst-launch-1.0 -v playbin uri=dvd://
193 * ]|
194 * This will play back the DVD in your disc drive (assuming
195 * the drive is detected automatically by the plugin).
196 *
197 */
198
199 /* FIXME 0.11: suppress warnings for deprecated API such as GValueArray
200 * with newer GLib versions (>= 2.31.0) */
201 #define GLIB_DISABLE_DEPRECATION_WARNINGS
202
203 #ifdef HAVE_CONFIG_H
204 #include "config.h"
205 #endif
206
207 #include <string.h>
208 #include <gst/gst.h>
209
210 #include <gst/gst-i18n-plugin.h>
211 #include <gst/pbutils/pbutils.h>
212 #include <gst/audio/streamvolume.h>
213 #include <gst/video/video-info.h>
214 #include <gst/video/video-multiview.h>
215 #include <gst/video/videooverlay.h>
216 #include <gst/video/navigation.h>
217 #include <gst/video/colorbalance.h>
218 #include "gstplay-enum.h"
219 #include "gstplaybackelements.h"
220 #include "gstplaysink.h"
221 #include "gstsubtitleoverlay.h"
222 #include "gstplaybackutils.h"
223
224 GST_DEBUG_CATEGORY_STATIC (gst_play_bin_debug);
225 #define GST_CAT_DEFAULT gst_play_bin_debug
226
227 #define GST_TYPE_PLAY_BIN (gst_play_bin_get_type())
228 #define GST_PLAY_BIN(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLAY_BIN,GstPlayBin))
229 #define GST_PLAY_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PLAY_BIN,GstPlayBinClass))
230 #define GST_IS_PLAY_BIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLAY_BIN))
231 #define GST_IS_PLAY_BIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLAY_BIN))
232
233 #define ULONG_TO_POINTER(number) ((gpointer) (guintptr) (number))
234 #define POINTER_TO_ULONG(number) ((guintptr) (number))
235
236 #define VOLUME_MAX_DOUBLE 10.0
237
238 typedef struct _GstPlayBin GstPlayBin;
239 typedef struct _GstPlayBinClass GstPlayBinClass;
240 typedef struct _GstSourceGroup GstSourceGroup;
241 typedef struct _GstSourceCombine GstSourceCombine;
242
243 typedef GstCaps *(*SourceCombineGetMediaCapsFunc) (void);
244
245 /* has the info for a combiner and provides the link to the sink */
246 struct _GstSourceCombine
247 {
248 const gchar *media_list[8]; /* the media types for the combiner */
249 SourceCombineGetMediaCapsFunc get_media_caps; /* more complex caps for the combiner */
250 GstPlaySinkType type; /* the sink pad type of the combiner */
251
252 GstElement *combiner; /* the combiner */
253 GPtrArray *channels;
254 GstPad *srcpad; /* the source pad of the combiner */
255 GstPad *sinkpad; /* the sinkpad of the sink when the combiner
256 * is linked
257 */
258 gulong block_id;
259
260 gboolean has_active_pad; /* stream combiner has the "active-pad" property */
261
262 gboolean has_always_ok; /* stream combiner's sink pads have the "always-ok" property */
263 gboolean has_tags; /* stream combiner's sink pads have the "tags" property */
264 };
265
266 #define GST_SOURCE_GROUP_GET_LOCK(group) (&((GstSourceGroup*)(group))->lock)
267 #define GST_SOURCE_GROUP_LOCK(group) (g_mutex_lock (GST_SOURCE_GROUP_GET_LOCK(group)))
268 #define GST_SOURCE_GROUP_UNLOCK(group) (g_mutex_unlock (GST_SOURCE_GROUP_GET_LOCK(group)))
269
270 enum
271 {
272 PLAYBIN_STREAM_AUDIO = 0,
273 PLAYBIN_STREAM_VIDEO,
274 PLAYBIN_STREAM_TEXT,
275 PLAYBIN_STREAM_LAST
276 };
277
278 static void avelements_free (gpointer data);
279 static GSequence *avelements_create (GstPlayBin * playbin,
280 gboolean isaudioelement);
281
282 /* The GstAudioVideoElement structure holding the audio/video decoder
283 * and the audio/video sink factories together with field indicating
284 * the number of common caps features */
285 typedef struct
286 {
287 GstElementFactory *dec; /* audio:video decoder */
288 GstElementFactory *sink; /* audio:video sink */
289 guint n_comm_cf; /* number of common caps features */
290 } GstAVElement;
291
292 /* a structure to hold the objects for decoding a uri and the subtitle uri
293 */
294 struct _GstSourceGroup
295 {
296 GstPlayBin *playbin;
297
298 GMutex lock;
299
300 gboolean valid; /* the group has valid info to start playback */
301 gboolean active; /* the group is active */
302
303 /* properties */
304 gchar *uri;
305 gchar *suburi;
306 GValueArray *streaminfo;
307 GstElement *source;
308
309 GPtrArray *video_channels; /* links to combiner pads */
310 GPtrArray *audio_channels; /* links to combiner pads */
311 GPtrArray *text_channels; /* links to combiner pads */
312
313 /* Sinks for this group. These are initialized with
314 * the configure or currently used sink, otherwise
315 * left as NULL and playbin tries to automatically
316 * select a good sink
317 */
318 GstElement *audio_sink;
319 GstElement *video_sink;
320 GstElement *text_sink;
321
322 /* uridecodebins for uri and subtitle uri */
323 GstElement *uridecodebin;
324 GstElement *suburidecodebin;
325 gint pending;
326 gboolean sub_pending;
327
328 gboolean have_group_id;
329 guint group_id;
330
331 gulong pad_added_id;
332 gulong pad_removed_id;
333 gulong no_more_pads_id;
334 gulong notify_source_id;
335 gulong source_setup_id;
336 gulong drained_id;
337 gulong autoplug_factories_id;
338 gulong autoplug_select_id;
339 gulong autoplug_continue_id;
340 gulong autoplug_query_id;
341
342 gulong sub_pad_added_id;
343 gulong sub_pad_removed_id;
344 gulong sub_no_more_pads_id;
345 gulong sub_autoplug_continue_id;
346 gulong sub_autoplug_query_id;
347
348 gulong block_id;
349
350 GMutex stream_changed_pending_lock;
351 gboolean stream_changed_pending;
352
353 /* to prevent that suburidecodebin seek flushes disrupt playback */
354 GMutex suburi_flushes_to_drop_lock;
355 GSList *suburi_flushes_to_drop;
356
357 /* buffering message stored for after switching */
358 GstMessage *pending_buffering_msg;
359
360 /* combiners for different streams */
361 GstSourceCombine combiner[PLAYBIN_STREAM_LAST];
362
363 #ifdef OHOS_EXT_FUNC
364 // ohos.ext.func.0028
365 gulong bitrate_parse_complete_id;
366 #endif
367 };
368
369 #define GST_PLAY_BIN_GET_LOCK(bin) (&((GstPlayBin*)(bin))->lock)
370 #define GST_PLAY_BIN_LOCK(bin) (g_rec_mutex_lock (GST_PLAY_BIN_GET_LOCK(bin)))
371 #define GST_PLAY_BIN_UNLOCK(bin) (g_rec_mutex_unlock (GST_PLAY_BIN_GET_LOCK(bin)))
372
373 /* lock to protect dynamic callbacks, like no-more-pads */
374 #define GST_PLAY_BIN_DYN_LOCK(bin) g_mutex_lock (&(bin)->dyn_lock)
375 #define GST_PLAY_BIN_DYN_UNLOCK(bin) g_mutex_unlock (&(bin)->dyn_lock)
376
377 /* lock for shutdown */
378 #define GST_PLAY_BIN_SHUTDOWN_LOCK(bin,label) \
379 G_STMT_START { \
380 if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) \
381 goto label; \
382 GST_PLAY_BIN_DYN_LOCK (bin); \
383 if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) { \
384 GST_PLAY_BIN_DYN_UNLOCK (bin); \
385 goto label; \
386 } \
387 } G_STMT_END
388
389 /* unlock for shutdown */
390 #define GST_PLAY_BIN_SHUTDOWN_UNLOCK(bin) \
391 GST_PLAY_BIN_DYN_UNLOCK (bin); \
392
393 /**
394 * GstPlayBin:
395 *
396 * playbin element structure
397 */
398 struct _GstPlayBin
399 {
400 GstPipeline parent;
401
402 GRecMutex lock; /* to protect group switching */
403
404 /* the groups, we use a double buffer to switch between current and next */
405 GstSourceGroup groups[2]; /* array with group info */
406 GstSourceGroup *curr_group; /* pointer to the currently playing group */
407 GstSourceGroup *next_group; /* pointer to the next group */
408
409 /* properties */
410 guint64 connection_speed; /* connection speed in bits/sec (0 = unknown) */
411 gint current_video; /* the currently selected stream */
412 gint current_audio; /* the currently selected stream */
413 gint current_text; /* the currently selected stream */
414
415 guint64 buffer_duration; /* When buffering, the max buffer duration (ns) */
416 guint buffer_size; /* When buffering, the max buffer size (bytes) */
417 gboolean force_aspect_ratio;
418
419 /* Multiview/stereoscopic overrides */
420 GstVideoMultiviewFramePacking multiview_mode;
421 GstVideoMultiviewFlags multiview_flags;
422
423 /* our play sink */
424 GstPlaySink *playsink;
425
426 /* the last activated source */
427 GstElement *source;
428
429 /* lock protecting dynamic adding/removing */
430 GMutex dyn_lock;
431 /* if we are shutting down or not */
432 gint shutdown;
433 gboolean async_pending; /* async-start has been emitted */
434
435 GMutex elements_lock;
436 guint32 elements_cookie;
437 GList *elements; /* factories we can use for selecting elements */
438
439 gboolean have_selector; /* set to FALSE when we fail to create an
440 * input-selector, so that we only post a
441 * warning once */
442
443 gboolean video_pending_flush_finish; /* whether we are pending to send a custom
444 * custom-video-flush-finish event
445 * on pad activation */
446 gboolean audio_pending_flush_finish; /* whether we are pending to send a custom
447 * custom-audio-flush-finish event
448 * on pad activation */
449 gboolean text_pending_flush_finish; /* whether we are pending to send a custom
450 * custom-subtitle-flush-finish event
451 * on pad activation */
452
453 GstElement *audio_sink; /* configured audio sink, or NULL */
454 GstElement *video_sink; /* configured video sink, or NULL */
455 GstElement *text_sink; /* configured text sink, or NULL */
456
457 GstElement *audio_stream_combiner; /* configured audio stream combiner, or NULL */
458 GstElement *video_stream_combiner; /* configured video stream combiner, or NULL */
459 GstElement *text_stream_combiner; /* configured text stream combiner, or NULL */
460
461 GSequence *aelements; /* a list of GstAVElements for audio stream */
462 GSequence *velements; /* a list of GstAVElements for video stream */
463
464 struct
465 {
466 gboolean valid;
467 GstFormat format;
468 gint64 duration;
469 } duration[5]; /* cached durations */
470
471 guint64 ring_buffer_max_size; /* 0 means disabled */
472
473 #ifdef OHOS_EXT_FUNC
474 // ohos.ext.func.0012
475 gint low_percent;
476 gint high_percent;
477 #endif
478
479 GList *contexts;
480
481 gboolean is_live;
482 };
483
484 struct _GstPlayBinClass
485 {
486 GstPipelineClass parent_class;
487
488 /* notify app that the current uri finished decoding and it is possible to
489 * queue a new one for gapless playback */
490 void (*about_to_finish) (GstPlayBin * playbin);
491
492 /* notify app that number of audio/video/text streams changed */
493 void (*video_changed) (GstPlayBin * playbin);
494 void (*audio_changed) (GstPlayBin * playbin);
495 void (*text_changed) (GstPlayBin * playbin);
496
497 /* notify app that the tags of audio/video/text streams changed */
498 void (*video_tags_changed) (GstPlayBin * playbin, gint stream);
499 void (*audio_tags_changed) (GstPlayBin * playbin, gint stream);
500 void (*text_tags_changed) (GstPlayBin * playbin, gint stream);
501
502 /* get audio/video/text tags for a stream */
503 GstTagList *(*get_video_tags) (GstPlayBin * playbin, gint stream);
504 GstTagList *(*get_audio_tags) (GstPlayBin * playbin, gint stream);
505 GstTagList *(*get_text_tags) (GstPlayBin * playbin, gint stream);
506
507 /* get the last video sample and convert it to the given caps */
508 GstSample *(*convert_sample) (GstPlayBin * playbin, GstCaps * caps);
509
510 /* get audio/video/text pad for a stream */
511 GstPad *(*get_video_pad) (GstPlayBin * playbin, gint stream);
512 GstPad *(*get_audio_pad) (GstPlayBin * playbin, gint stream);
513 GstPad *(*get_text_pad) (GstPlayBin * playbin, gint stream);
514 };
515
516 /* props */
517 #define DEFAULT_URI NULL
518 #define DEFAULT_SUBURI NULL
519 #define DEFAULT_SOURCE NULL
520 #define DEFAULT_FLAGS GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
521 GST_PLAY_FLAG_SOFT_VOLUME | GST_PLAY_FLAG_DEINTERLACE | \
522 GST_PLAY_FLAG_SOFT_COLORBALANCE
523 #define DEFAULT_N_VIDEO 0
524 #define DEFAULT_CURRENT_VIDEO -1
525 #define DEFAULT_N_AUDIO 0
526 #define DEFAULT_CURRENT_AUDIO -1
527 #define DEFAULT_N_TEXT 0
528 #define DEFAULT_CURRENT_TEXT -1
529 #define DEFAULT_SUBTITLE_ENCODING NULL
530 #define DEFAULT_AUDIO_SINK NULL
531 #define DEFAULT_VIDEO_SINK NULL
532 #define DEFAULT_VIS_PLUGIN NULL
533 #define DEFAULT_TEXT_SINK NULL
534 #define DEFAULT_VOLUME 1.0
535 #define DEFAULT_MUTE FALSE
536 #define DEFAULT_FRAME NULL
537 #define DEFAULT_FONT_DESC NULL
538 #define DEFAULT_CONNECTION_SPEED 0
539 #ifdef OHOS_EXT_FUNC
540 // ohos.ext.func.0012
541 #define DEFAULT_BUFFER_DURATION 0
542 #else
543 #define DEFAULT_BUFFER_DURATION -1
544 #endif
545 #define DEFAULT_BUFFER_SIZE -1
546 #define DEFAULT_RING_BUFFER_MAX_SIZE 0
547
548 #ifdef OHOS_EXT_FUNC
549 // ohos.ext.func.0012
550 #define DEFAULT_LOW_PERCENT 10
551 #define DEFAULT_HIGH_PERCENT 99
552 #define DEFAULT_TIMEOUT 15
553 #endif
554
555 enum
556 {
557 PROP_0,
558 PROP_URI,
559 PROP_CURRENT_URI,
560 PROP_SUBURI,
561 PROP_CURRENT_SUBURI,
562 PROP_SOURCE,
563 PROP_FLAGS,
564 PROP_N_VIDEO,
565 PROP_CURRENT_VIDEO,
566 PROP_N_AUDIO,
567 PROP_CURRENT_AUDIO,
568 PROP_N_TEXT,
569 PROP_CURRENT_TEXT,
570 PROP_SUBTITLE_ENCODING,
571 PROP_AUDIO_SINK,
572 PROP_VIDEO_SINK,
573 PROP_VIS_PLUGIN,
574 PROP_TEXT_SINK,
575 PROP_VIDEO_STREAM_COMBINER,
576 PROP_AUDIO_STREAM_COMBINER,
577 PROP_TEXT_STREAM_COMBINER,
578 PROP_VOLUME,
579 PROP_MUTE,
580 PROP_SAMPLE,
581 PROP_FONT_DESC,
582 PROP_CONNECTION_SPEED,
583 PROP_BUFFER_SIZE,
584 PROP_BUFFER_DURATION,
585 PROP_AV_OFFSET,
586 PROP_TEXT_OFFSET,
587 PROP_RING_BUFFER_MAX_SIZE,
588 PROP_FORCE_ASPECT_RATIO,
589 PROP_AUDIO_FILTER,
590 PROP_VIDEO_FILTER,
591 PROP_MULTIVIEW_MODE,
592 #ifdef OHOS_EXT_FUNC
593 // ohos.ext.func.0012
594 PROP_BUFFERING_FLAGS,
595 PROP_LOW_PERCENT,
596 PROP_HIGH_PERCENT,
597 PROP_STATE_CHANGE,
598 PROP_EXIT_BLOCK,
599 PROP_TIMEOUT,
600 #endif
601 PROP_MULTIVIEW_FLAGS
602 };
603
604 /* signals */
605 enum
606 {
607 SIGNAL_ABOUT_TO_FINISH,
608 SIGNAL_CONVERT_SAMPLE,
609 SIGNAL_VIDEO_CHANGED,
610 SIGNAL_AUDIO_CHANGED,
611 SIGNAL_TEXT_CHANGED,
612 SIGNAL_VIDEO_TAGS_CHANGED,
613 SIGNAL_AUDIO_TAGS_CHANGED,
614 SIGNAL_TEXT_TAGS_CHANGED,
615 SIGNAL_GET_VIDEO_TAGS,
616 SIGNAL_GET_AUDIO_TAGS,
617 SIGNAL_GET_TEXT_TAGS,
618 SIGNAL_GET_VIDEO_PAD,
619 SIGNAL_GET_AUDIO_PAD,
620 SIGNAL_GET_TEXT_PAD,
621 SIGNAL_SOURCE_SETUP,
622 SIGNAL_ELEMENT_SETUP,
623 #ifdef OHOS_EXT_FUNC
624 // ohos.ext.func.0028
625 SIGNAL_BITRATE_PARSE_COMPLETE,
626 #endif
627 LAST_SIGNAL
628 };
629
630 static GstStaticCaps raw_audio_caps = GST_STATIC_CAPS ("audio/x-raw(ANY)");
631 static GstStaticCaps raw_video_caps = GST_STATIC_CAPS ("video/x-raw(ANY)");
632
633 static void gst_play_bin_finalize (GObject * object);
634
635 static void gst_play_bin_set_property (GObject * object, guint prop_id,
636 const GValue * value, GParamSpec * spec);
637 static void gst_play_bin_get_property (GObject * object, guint prop_id,
638 GValue * value, GParamSpec * spec);
639
640 static GstStateChangeReturn gst_play_bin_change_state (GstElement * element,
641 GstStateChange transition);
642
643 static void gst_play_bin_handle_message (GstBin * bin, GstMessage * message);
644 static void gst_play_bin_deep_element_added (GstBin * playbin, GstBin * sub_bin,
645 GstElement * child);
646 static gboolean gst_play_bin_query (GstElement * element, GstQuery * query);
647 static void gst_play_bin_set_context (GstElement * element,
648 GstContext * context);
649 static gboolean gst_play_bin_send_event (GstElement * element,
650 GstEvent * event);
651
652 static GstTagList *gst_play_bin_get_video_tags (GstPlayBin * playbin,
653 gint stream);
654 static GstTagList *gst_play_bin_get_audio_tags (GstPlayBin * playbin,
655 gint stream);
656 static GstTagList *gst_play_bin_get_text_tags (GstPlayBin * playbin,
657 gint stream);
658
659 static GstSample *gst_play_bin_convert_sample (GstPlayBin * playbin,
660 GstCaps * caps);
661
662 static GstPad *gst_play_bin_get_video_pad (GstPlayBin * playbin, gint stream);
663 static GstPad *gst_play_bin_get_audio_pad (GstPlayBin * playbin, gint stream);
664 static GstPad *gst_play_bin_get_text_pad (GstPlayBin * playbin, gint stream);
665
666 static GstStateChangeReturn setup_next_source (GstPlayBin * playbin,
667 GstState target);
668
669 static void no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group);
670 static void pad_removed_cb (GstElement * decodebin, GstPad * pad,
671 GstSourceGroup * group);
672
673 static void gst_play_bin_suburidecodebin_block (GstSourceGroup * group,
674 GstElement * suburidecodebin, gboolean block);
675 static void gst_play_bin_suburidecodebin_seek_to_start (GstSourceGroup * group);
676 static void
677 gst_play_bin_update_context (GstPlayBin * playbin, GstContext * context);
678
679 static GstElementClass *parent_class;
680
681 static guint gst_play_bin_signals[LAST_SIGNAL] = { 0 };
682
683 #define REMOVE_SIGNAL(obj,id) \
684 if (id) { \
685 g_signal_handler_disconnect (obj, id); \
686 id = 0; \
687 }
688
689 static void gst_play_bin_overlay_init (gpointer g_iface, gpointer g_iface_data);
690 static void gst_play_bin_navigation_init (gpointer g_iface,
691 gpointer g_iface_data);
692 static void gst_play_bin_colorbalance_init (gpointer g_iface,
693 gpointer g_iface_data);
694
695 static GType gst_play_bin_get_type (void);
696
697 static void
_do_init_type(GType type)698 _do_init_type (GType type)
699 {
700
701 static const GInterfaceInfo svol_info = {
702 NULL, NULL, NULL
703 };
704 static const GInterfaceInfo ov_info = {
705 gst_play_bin_overlay_init,
706 NULL, NULL
707 };
708 static const GInterfaceInfo nav_info = {
709 gst_play_bin_navigation_init,
710 NULL, NULL
711 };
712 static const GInterfaceInfo col_info = {
713 gst_play_bin_colorbalance_init,
714 NULL, NULL
715 };
716
717 g_type_add_interface_static (type, GST_TYPE_STREAM_VOLUME, &svol_info);
718 g_type_add_interface_static (type, GST_TYPE_VIDEO_OVERLAY, &ov_info);
719 g_type_add_interface_static (type, GST_TYPE_NAVIGATION, &nav_info);
720 g_type_add_interface_static (type, GST_TYPE_COLOR_BALANCE, &col_info);
721 }
722
723 G_DEFINE_TYPE_WITH_CODE (GstPlayBin, gst_play_bin, GST_TYPE_PIPELINE,
724 _do_init_type (g_define_type_id));
725 #define _do_init \
726 GST_DEBUG_CATEGORY_INIT (gst_play_bin_debug, "playbin", 0, "play bin");\
727 playback_element_init (plugin);
728 GST_ELEMENT_REGISTER_DEFINE_WITH_CODE (playbin, "playbin", GST_RANK_NONE,
729 GST_TYPE_PLAY_BIN, _do_init);
730
731 void
gst_play_bin_class_init(GstPlayBinClass * klass)732 gst_play_bin_class_init (GstPlayBinClass * klass)
733 {
734 GObjectClass *gobject_klass;
735 GstElementClass *gstelement_klass;
736 GstBinClass *gstbin_klass;
737
738 gobject_klass = (GObjectClass *) klass;
739 gstelement_klass = (GstElementClass *) klass;
740 gstbin_klass = (GstBinClass *) klass;
741
742 parent_class = g_type_class_peek_parent (klass);
743
744 gobject_klass->set_property = gst_play_bin_set_property;
745 gobject_klass->get_property = gst_play_bin_get_property;
746
747 gobject_klass->finalize = gst_play_bin_finalize;
748
749 /**
750 * GstPlayBin:uri
751 *
752 * Set the next URI that playbin will play. This property can be set from the
753 * about-to-finish signal to queue the next media file.
754 */
755 g_object_class_install_property (gobject_klass, PROP_URI,
756 g_param_spec_string ("uri", "URI", "URI of the media to play",
757 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
758
759 /**
760 * GstPlayBin:current-uri
761 *
762 * The currently playing uri.
763 */
764 g_object_class_install_property (gobject_klass, PROP_CURRENT_URI,
765 g_param_spec_string ("current-uri", "Current URI",
766 "The currently playing URI", NULL,
767 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
768
769 /**
770 * GstPlayBin:suburi
771 *
772 * Set the next subtitle URI that playbin will play. This property can be
773 * set from the about-to-finish signal to queue the next subtitle media file.
774 */
775 g_object_class_install_property (gobject_klass, PROP_SUBURI,
776 g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
777 NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
778
779 /**
780 * GstPlayBin:current-suburi
781 *
782 * The currently playing subtitle uri.
783 */
784 g_object_class_install_property (gobject_klass, PROP_CURRENT_SUBURI,
785 g_param_spec_string ("current-suburi", "Current .sub-URI",
786 "The currently playing URI of a subtitle",
787 NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
788
789 g_object_class_install_property (gobject_klass, PROP_SOURCE,
790 g_param_spec_object ("source", "Source", "Source element",
791 GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
792
793 /**
794 * GstPlayBin:flags
795 *
796 * Control the behaviour of playbin.
797 */
798 g_object_class_install_property (gobject_klass, PROP_FLAGS,
799 g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
800 GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
801 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
802
803 /**
804 * GstPlayBin:n-video
805 *
806 * Get the total number of available video streams.
807 */
808 g_object_class_install_property (gobject_klass, PROP_N_VIDEO,
809 g_param_spec_int ("n-video", "Number Video",
810 "Total number of video streams", 0, G_MAXINT, 0,
811 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
812 /**
813 * GstPlayBin:current-video
814 *
815 * Get or set the currently playing video stream. By default the first video
816 * stream with data is played.
817 */
818 g_object_class_install_property (gobject_klass, PROP_CURRENT_VIDEO,
819 g_param_spec_int ("current-video", "Current Video",
820 "Currently playing video stream (-1 = auto)",
821 -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
822 /**
823 * GstPlayBin:n-audio
824 *
825 * Get the total number of available audio streams.
826 */
827 g_object_class_install_property (gobject_klass, PROP_N_AUDIO,
828 g_param_spec_int ("n-audio", "Number Audio",
829 "Total number of audio streams", 0, G_MAXINT, 0,
830 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
831 /**
832 * GstPlayBin:current-audio
833 *
834 * Get or set the currently playing audio stream. By default the first audio
835 * stream with data is played.
836 */
837 g_object_class_install_property (gobject_klass, PROP_CURRENT_AUDIO,
838 g_param_spec_int ("current-audio", "Current audio",
839 "Currently playing audio stream (-1 = auto)",
840 -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
841 /**
842 * GstPlayBin:n-text
843 *
844 * Get the total number of available subtitle streams.
845 */
846 g_object_class_install_property (gobject_klass, PROP_N_TEXT,
847 g_param_spec_int ("n-text", "Number Text",
848 "Total number of text streams", 0, G_MAXINT, 0,
849 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
850 /**
851 * GstPlayBin:current-text:
852 *
853 * Get or set the currently playing subtitle stream. By default the first
854 * subtitle stream with data is played.
855 */
856 g_object_class_install_property (gobject_klass, PROP_CURRENT_TEXT,
857 g_param_spec_int ("current-text", "Current Text",
858 "Currently playing text stream (-1 = auto)",
859 -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
860
861 g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
862 g_param_spec_string ("subtitle-encoding", "subtitle encoding",
863 "Encoding to assume if input subtitles are not in UTF-8 encoding. "
864 "If not set, the GST_SUBTITLE_ENCODING environment variable will "
865 "be checked for an encoding to use. If that is not set either, "
866 "ISO-8859-15 will be assumed.", NULL,
867 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
868
869 g_object_class_install_property (gobject_klass, PROP_VIDEO_FILTER,
870 g_param_spec_object ("video-filter", "Video filter",
871 "the video filter(s) to apply, if possible",
872 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
873 g_object_class_install_property (gobject_klass, PROP_AUDIO_FILTER,
874 g_param_spec_object ("audio-filter", "Audio filter",
875 "the audio filter(s) to apply, if possible",
876 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
877 g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
878 g_param_spec_object ("video-sink", "Video Sink",
879 "the video output element to use (NULL = default sink)",
880 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
881 g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
882 g_param_spec_object ("audio-sink", "Audio Sink",
883 "the audio output element to use (NULL = default sink)",
884 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
885 g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
886 g_param_spec_object ("vis-plugin", "Vis plugin",
887 "the visualization element to use (NULL = default)",
888 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
889 g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
890 g_param_spec_object ("text-sink", "Text plugin",
891 "the text output element to use (NULL = default subtitleoverlay)",
892 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
893 /**
894 * GstPlayBin:video-stream-combiner
895 *
896 * Get or set the current video stream combiner. By default, an input-selector
897 * is created and deleted as-needed.
898 */
899 g_object_class_install_property (gobject_klass, PROP_VIDEO_STREAM_COMBINER,
900 g_param_spec_object ("video-stream-combiner", "Video stream combiner",
901 "Current video stream combiner (NULL = input-selector)",
902 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
903 /**
904 * GstPlayBin:audio-stream-combiner
905 *
906 * Get or set the current audio stream combiner. By default, an input-selector
907 * is created and deleted as-needed.
908 */
909 g_object_class_install_property (gobject_klass, PROP_AUDIO_STREAM_COMBINER,
910 g_param_spec_object ("audio-stream-combiner", "Audio stream combiner",
911 "Current audio stream combiner (NULL = input-selector)",
912 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
913 /**
914 * GstPlayBin:text-stream-combiner
915 *
916 * Get or set the current text stream combiner. By default, an input-selector
917 * is created and deleted as-needed.
918 */
919 g_object_class_install_property (gobject_klass, PROP_TEXT_STREAM_COMBINER,
920 g_param_spec_object ("text-stream-combiner", "Text stream combiner",
921 "Current text stream combiner (NULL = input-selector)",
922 GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
923
924 /**
925 * GstPlayBin:volume:
926 *
927 * Get or set the current audio stream volume. 1.0 means 100%,
928 * 0.0 means mute. This uses a linear volume scale.
929 *
930 */
931 g_object_class_install_property (gobject_klass, PROP_VOLUME,
932 g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
933 0.0, VOLUME_MAX_DOUBLE, 1.0,
934 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
935 g_object_class_install_property (gobject_klass, PROP_MUTE,
936 g_param_spec_boolean ("mute", "Mute",
937 "Mute the audio channel without changing the volume", FALSE,
938 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
939
940 /**
941 * GstPlayBin:sample:
942 * @playbin: a #GstPlayBin
943 *
944 * Get the currently rendered or prerolled sample in the video sink.
945 * The #GstCaps in the sample will describe the format of the buffer.
946 */
947 g_object_class_install_property (gobject_klass, PROP_SAMPLE,
948 g_param_spec_boxed ("sample", "Sample",
949 "The last sample (NULL = no video available)",
950 GST_TYPE_SAMPLE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
951
952 g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
953 g_param_spec_string ("subtitle-font-desc",
954 "Subtitle font description",
955 "Pango font description of font "
956 "to be used for subtitle rendering", NULL,
957 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
958
959 g_object_class_install_property (gobject_klass, PROP_CONNECTION_SPEED,
960 g_param_spec_uint64 ("connection-speed", "Connection Speed",
961 "Network connection speed in kbps (0 = unknown)",
962 0, G_MAXUINT64 / 1000, DEFAULT_CONNECTION_SPEED,
963 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
964
965 g_object_class_install_property (gobject_klass, PROP_BUFFER_SIZE,
966 g_param_spec_int ("buffer-size", "Buffer size (bytes)",
967 "Buffer size when buffering network streams",
968 -1, G_MAXINT, DEFAULT_BUFFER_SIZE,
969 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
970
971 #ifdef OHOS_EXT_FUNC
972 // ohos.ext.func.0012
973 g_object_class_install_property (gobject_klass, PROP_BUFFER_DURATION,
974 g_param_spec_uint64 ("buffer-duration", "Buffer duration (ns)",
975 "Buffer duration when buffering network streams",
976 0, G_MAXUINT64 / 1000, DEFAULT_BUFFER_DURATION,
977 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
978 #else
979 g_object_class_install_property (gobject_klass, PROP_BUFFER_DURATION,
980 g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
981 "Buffer duration when buffering network streams",
982 -1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
983 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
984 #endif
985 /**
986 * GstPlayBin:av-offset:
987 *
988 * Control the synchronisation offset between the audio and video streams.
989 * Positive values make the audio ahead of the video and negative values make
990 * the audio go behind the video.
991 */
992 g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
993 g_param_spec_int64 ("av-offset", "AV Offset",
994 "The synchronisation offset between audio and video in nanoseconds",
995 G_MININT64, G_MAXINT64, 0,
996 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
997
998 /**
999 * GstPlayBin:text-offset:
1000 *
1001 * Control the synchronisation offset between the text and video streams.
1002 * Positive values make the text ahead of the video and negative values make
1003 * the text go behind the video.
1004 */
1005 g_object_class_install_property (gobject_klass, PROP_TEXT_OFFSET,
1006 g_param_spec_int64 ("text-offset", "Text Offset",
1007 "The synchronisation offset between text and video in nanoseconds",
1008 G_MININT64, G_MAXINT64, 0,
1009 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1010
1011 /**
1012 * GstPlayBin:ring-buffer-max-size:
1013 *
1014 * The maximum size of the ring buffer in bytes. If set to 0, the ring
1015 * buffer is disabled. Default 0.
1016 */
1017 g_object_class_install_property (gobject_klass, PROP_RING_BUFFER_MAX_SIZE,
1018 g_param_spec_uint64 ("ring-buffer-max-size",
1019 "Max. ring buffer size (bytes)",
1020 "Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)",
1021 0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE,
1022 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1023
1024 /**
1025 * GstPlayBin::force-aspect-ratio:
1026 *
1027 * Requests the video sink to enforce the video display aspect ratio.
1028 */
1029 g_object_class_install_property (gobject_klass, PROP_FORCE_ASPECT_RATIO,
1030 g_param_spec_boolean ("force-aspect-ratio", "Force Aspect Ratio",
1031 "When enabled, scaling will respect original aspect ratio", TRUE,
1032 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1033
1034 /**
1035 * GstPlayBin::video-multiview-mode:
1036 *
1037 * Set the stereoscopic mode for video streams that don't contain
1038 * any information in the stream, so they can be correctly played
1039 * as 3D streams. If a video already has multiview information
1040 * encoded, this property can override other modes in the set,
1041 * but cannot be used to re-interpret MVC or mixed-mono streams.
1042 *
1043 * See Also: The #GstPlayBin::video-multiview-flags property
1044 *
1045 */
1046 g_object_class_install_property (gobject_klass, PROP_MULTIVIEW_MODE,
1047 g_param_spec_enum ("video-multiview-mode",
1048 "Multiview Mode Override",
1049 "Re-interpret a video stream as one of several frame-packed stereoscopic modes.",
1050 GST_TYPE_VIDEO_MULTIVIEW_FRAME_PACKING,
1051 GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE,
1052 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1053
1054 /**
1055 * GstPlayBin::video-multiview-flags:
1056 *
1057 * When overriding the multiview mode of an input stream,
1058 * these flags modify details of the view layout.
1059 *
1060 * See Also: The #GstPlayBin::video-multiview-mode property
1061 */
1062 g_object_class_install_property (gobject_klass, PROP_MULTIVIEW_FLAGS,
1063 g_param_spec_flags ("video-multiview-flags",
1064 "Multiview Flags Override",
1065 "Override details of the multiview frame layout",
1066 GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
1067 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1068
1069 #ifdef OHOS_EXT_FUNC
1070 // ohos.ext.func.0012
1071 /**
1072 * GstPlayBin:state-change
1073 *
1074 * state change to modify wait time for httpsoupsrc.
1075 */
1076 g_object_class_install_property (gobject_klass, PROP_STATE_CHANGE,
1077 g_param_spec_int ("state-change", "state-change from adaptive-demux",
1078 "state-change from adaptive-demux", 0, (gint) (G_MAXINT32), 0,
1079 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1080
1081 /**
1082 * GstPlayBin:exit-block
1083 *
1084 * exit block to connect for httpsoupsrc.
1085 */
1086 g_object_class_install_property (gobject_klass, PROP_EXIT_BLOCK,
1087 g_param_spec_int ("exit-block", "EXIT BLOCK",
1088 "souphttpsrc exit block", 0, (gint) (G_MAXINT32), 0,
1089 G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
1090
1091 /**
1092 * GstPlayBin:low-percent
1093 *
1094 * Low threshold percent for buffering to start.
1095 */
1096 g_object_class_install_property (gobject_klass, PROP_LOW_PERCENT,
1097 g_param_spec_int ("low-percent", "Low percent",
1098 "Low threshold for buffering to start", 0, 100,
1099 DEFAULT_LOW_PERCENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1100 /**
1101 * GstPlayBin:high-percent
1102 *
1103 * High threshold percent for buffering to finish.
1104 */
1105 g_object_class_install_property (gobject_klass, PROP_HIGH_PERCENT,
1106 g_param_spec_int ("high-percent", "High percent",
1107 "High threshold for buffering to finish", 0, 100,
1108 DEFAULT_HIGH_PERCENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1109
1110 g_object_class_install_property (gobject_klass, PROP_TIMEOUT,
1111 g_param_spec_uint ("timeout", "timeout",
1112 "Value in seconds to timeout a blocking I/O (0 = No timeout).", 0,
1113 3600, DEFAULT_TIMEOUT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1114
1115 g_object_class_install_property (gobject_klass, PROP_BUFFERING_FLAGS,
1116 g_param_spec_boolean ("buffering-flags", "Buffering Flags", "Flags to control behaviour",
1117 FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
1118 #endif
1119
1120 /**
1121 * GstPlayBin::about-to-finish
1122 * @playbin: a #GstPlayBin
1123 *
1124 * This signal is emitted when the current uri is about to finish. You can
1125 * set the uri and suburi to make sure that playback continues.
1126 *
1127 * This signal is emitted from the context of a GStreamer streaming thread.
1128 */
1129 gst_play_bin_signals[SIGNAL_ABOUT_TO_FINISH] =
1130 g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
1131 G_SIGNAL_RUN_LAST,
1132 G_STRUCT_OFFSET (GstPlayBinClass, about_to_finish), NULL, NULL, NULL,
1133 G_TYPE_NONE, 0, G_TYPE_NONE);
1134
1135 /**
1136 * GstPlayBin::video-changed:
1137 * @playbin: a #GstPlayBin
1138 *
1139 * This signal is emitted whenever the number or order of the video
1140 * streams has changed. The application will most likely want to select
1141 * a new video stream.
1142 *
1143 * This signal is usually emitted from the context of a GStreamer streaming
1144 * thread. You can use gst_message_new_application() and
1145 * gst_element_post_message() to notify your application's main thread.
1146 */
1147 /* FIXME 0.11: turn video-changed signal into message? */
1148 gst_play_bin_signals[SIGNAL_VIDEO_CHANGED] =
1149 g_signal_new ("video-changed", G_TYPE_FROM_CLASS (klass),
1150 G_SIGNAL_RUN_LAST,
1151 G_STRUCT_OFFSET (GstPlayBinClass, video_changed), NULL, NULL, NULL,
1152 G_TYPE_NONE, 0, G_TYPE_NONE);
1153 /**
1154 * GstPlayBin::audio-changed
1155 * @playbin: a #GstPlayBin
1156 *
1157 * This signal is emitted whenever the number or order of the audio
1158 * streams has changed. The application will most likely want to select
1159 * a new audio stream.
1160 *
1161 * This signal may be emitted from the context of a GStreamer streaming thread.
1162 * You can use gst_message_new_application() and gst_element_post_message()
1163 * to notify your application's main thread.
1164 */
1165 /* FIXME 0.11: turn audio-changed signal into message? */
1166 gst_play_bin_signals[SIGNAL_AUDIO_CHANGED] =
1167 g_signal_new ("audio-changed", G_TYPE_FROM_CLASS (klass),
1168 G_SIGNAL_RUN_LAST,
1169 G_STRUCT_OFFSET (GstPlayBinClass, audio_changed), NULL, NULL, NULL,
1170 G_TYPE_NONE, 0, G_TYPE_NONE);
1171 /**
1172 * GstPlayBin::text-changed:
1173 * @playbin: a #GstPlayBin
1174 *
1175 * This signal is emitted whenever the number or order of the text
1176 * streams has changed. The application will most likely want to select
1177 * a new text stream.
1178 *
1179 * This signal may be emitted from the context of a GStreamer streaming thread.
1180 * You can use gst_message_new_application() and gst_element_post_message()
1181 * to notify your application's main thread.
1182 */
1183 /* FIXME 0.11: turn text-changed signal into message? */
1184 gst_play_bin_signals[SIGNAL_TEXT_CHANGED] =
1185 g_signal_new ("text-changed", G_TYPE_FROM_CLASS (klass),
1186 G_SIGNAL_RUN_LAST,
1187 G_STRUCT_OFFSET (GstPlayBinClass, text_changed), NULL, NULL, NULL,
1188 G_TYPE_NONE, 0, G_TYPE_NONE);
1189
1190 /**
1191 * GstPlayBin::video-tags-changed:
1192 * @playbin: a #GstPlayBin
1193 * @stream: stream index with changed tags
1194 *
1195 * This signal is emitted whenever the tags of a video stream have changed.
1196 * The application will most likely want to get the new tags.
1197 *
1198 * This signal may be emitted from the context of a GStreamer streaming thread.
1199 * You can use gst_message_new_application() and gst_element_post_message()
1200 * to notify your application's main thread.
1201 */
1202 gst_play_bin_signals[SIGNAL_VIDEO_TAGS_CHANGED] =
1203 g_signal_new ("video-tags-changed", G_TYPE_FROM_CLASS (klass),
1204 G_SIGNAL_RUN_LAST,
1205 G_STRUCT_OFFSET (GstPlayBinClass, video_tags_changed), NULL, NULL, NULL,
1206 G_TYPE_NONE, 1, G_TYPE_INT);
1207
1208 /**
1209 * GstPlayBin::audio-tags-changed:
1210 * @playbin: a #GstPlayBin
1211 * @stream: stream index with changed tags
1212 *
1213 * This signal is emitted whenever the tags of an audio stream have changed.
1214 * The application will most likely want to get the new tags.
1215 *
1216 * This signal may be emitted from the context of a GStreamer streaming thread.
1217 * You can use gst_message_new_application() and gst_element_post_message()
1218 * to notify your application's main thread.
1219 */
1220 gst_play_bin_signals[SIGNAL_AUDIO_TAGS_CHANGED] =
1221 g_signal_new ("audio-tags-changed", G_TYPE_FROM_CLASS (klass),
1222 G_SIGNAL_RUN_LAST,
1223 G_STRUCT_OFFSET (GstPlayBinClass, audio_tags_changed), NULL, NULL, NULL,
1224 G_TYPE_NONE, 1, G_TYPE_INT);
1225
1226 /**
1227 * GstPlayBin::text-tags-changed:
1228 * @playbin: a #GstPlayBin
1229 * @stream: stream index with changed tags
1230 *
1231 * This signal is emitted whenever the tags of a text stream have changed.
1232 * The application will most likely want to get the new tags.
1233 *
1234 * This signal may be emitted from the context of a GStreamer streaming thread.
1235 * You can use gst_message_new_application() and gst_element_post_message()
1236 * to notify your application's main thread.
1237 */
1238 gst_play_bin_signals[SIGNAL_TEXT_TAGS_CHANGED] =
1239 g_signal_new ("text-tags-changed", G_TYPE_FROM_CLASS (klass),
1240 G_SIGNAL_RUN_LAST,
1241 G_STRUCT_OFFSET (GstPlayBinClass, text_tags_changed), NULL, NULL, NULL,
1242 G_TYPE_NONE, 1, G_TYPE_INT);
1243
1244 /**
1245 * GstPlayBin::source-setup:
1246 * @playbin: a #GstPlayBin
1247 * @source: source element
1248 *
1249 * This signal is emitted after the source element has been created, so
1250 * it can be configured by setting additional properties (e.g. set a
1251 * proxy server for an http source, or set the device and read speed for
1252 * an audio cd source). This is functionally equivalent to connecting to
1253 * the notify::source signal, but more convenient.
1254 *
1255 * This signal is usually emitted from the context of a GStreamer streaming
1256 * thread.
1257 */
1258 gst_play_bin_signals[SIGNAL_SOURCE_SETUP] =
1259 g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
1260 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
1261
1262 /**
1263 * GstPlayBin::element-setup:
1264 * @playbin: a #GstPlayBin
1265 * @element: an element that was added to the playbin hierarchy
1266 *
1267 * This signal is emitted when a new element is added to playbin or any of
1268 * its sub-bins. This signal can be used to configure elements, e.g. to set
1269 * properties on decoders. This is functionally equivalent to connecting to
1270 * the deep-element-added signal, but more convenient.
1271 *
1272 * This signal is usually emitted from the context of a GStreamer streaming
1273 * thread, so might be called at the same time as code running in the main
1274 * application thread.
1275 *
1276 * Since: 1.10
1277 */
1278 gst_play_bin_signals[SIGNAL_ELEMENT_SETUP] =
1279 g_signal_new ("element-setup", G_TYPE_FROM_CLASS (klass),
1280 G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);
1281
1282 /**
1283 * GstPlayBin::get-video-tags:
1284 * @playbin: a #GstPlayBin
1285 * @stream: a video stream number
1286 *
1287 * Action signal to retrieve the tags of a specific video stream number.
1288 * This information can be used to select a stream.
1289 *
1290 * Returns: a GstTagList with tags or NULL when the stream number does not
1291 * exist.
1292 */
1293 gst_play_bin_signals[SIGNAL_GET_VIDEO_TAGS] =
1294 g_signal_new ("get-video-tags", G_TYPE_FROM_CLASS (klass),
1295 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1296 G_STRUCT_OFFSET (GstPlayBinClass, get_video_tags), NULL, NULL, NULL,
1297 GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
1298 /**
1299 * GstPlayBin::get-audio-tags:
1300 * @playbin: a #GstPlayBin
1301 * @stream: an audio stream number
1302 *
1303 * Action signal to retrieve the tags of a specific audio stream number.
1304 * This information can be used to select a stream.
1305 *
1306 * Returns: a GstTagList with tags or NULL when the stream number does not
1307 * exist.
1308 */
1309 gst_play_bin_signals[SIGNAL_GET_AUDIO_TAGS] =
1310 g_signal_new ("get-audio-tags", G_TYPE_FROM_CLASS (klass),
1311 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1312 G_STRUCT_OFFSET (GstPlayBinClass, get_audio_tags), NULL, NULL, NULL,
1313 GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
1314 /**
1315 * GstPlayBin::get-text-tags:
1316 * @playbin: a #GstPlayBin
1317 * @stream: a text stream number
1318 *
1319 * Action signal to retrieve the tags of a specific text stream number.
1320 * This information can be used to select a stream.
1321 *
1322 * Returns: a GstTagList with tags or NULL when the stream number does not
1323 * exist.
1324 */
1325 gst_play_bin_signals[SIGNAL_GET_TEXT_TAGS] =
1326 g_signal_new ("get-text-tags", G_TYPE_FROM_CLASS (klass),
1327 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1328 G_STRUCT_OFFSET (GstPlayBinClass, get_text_tags), NULL, NULL, NULL,
1329 GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
1330 /**
1331 * GstPlayBin::convert-sample:
1332 * @playbin: a #GstPlayBin
1333 * @caps: the target format of the frame
1334 *
1335 * Action signal to retrieve the currently playing video frame in the format
1336 * specified by @caps.
1337 * If @caps is %NULL, no conversion will be performed and this function is
1338 * equivalent to the #GstPlayBin:sample property.
1339 *
1340 * Returns: a #GstSample of the current video frame converted to #caps.
1341 * The caps on the sample will describe the final layout of the buffer data.
1342 * %NULL is returned when no current buffer can be retrieved or when the
1343 * conversion failed.
1344 */
1345 gst_play_bin_signals[SIGNAL_CONVERT_SAMPLE] =
1346 g_signal_new ("convert-sample", G_TYPE_FROM_CLASS (klass),
1347 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1348 G_STRUCT_OFFSET (GstPlayBinClass, convert_sample), NULL, NULL, NULL,
1349 GST_TYPE_SAMPLE, 1, GST_TYPE_CAPS);
1350
1351 /**
1352 * GstPlayBin::get-video-pad:
1353 * @playbin: a #GstPlayBin
1354 * @stream: a video stream number
1355 *
1356 * Action signal to retrieve the stream-combiner sinkpad for a specific
1357 * video stream.
1358 * This pad can be used for notifications of caps changes, stream-specific
1359 * queries, etc.
1360 *
1361 * Returns: a #GstPad, or NULL when the stream number does not exist.
1362 */
1363 gst_play_bin_signals[SIGNAL_GET_VIDEO_PAD] =
1364 g_signal_new ("get-video-pad", G_TYPE_FROM_CLASS (klass),
1365 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1366 G_STRUCT_OFFSET (GstPlayBinClass, get_video_pad), NULL, NULL, NULL,
1367 GST_TYPE_PAD, 1, G_TYPE_INT);
1368 /**
1369 * GstPlayBin::get-audio-pad:
1370 * @playbin: a #GstPlayBin
1371 * @stream: an audio stream number
1372 *
1373 * Action signal to retrieve the stream-combiner sinkpad for a specific
1374 * audio stream.
1375 * This pad can be used for notifications of caps changes, stream-specific
1376 * queries, etc.
1377 *
1378 * Returns: a #GstPad, or NULL when the stream number does not exist.
1379 */
1380 gst_play_bin_signals[SIGNAL_GET_AUDIO_PAD] =
1381 g_signal_new ("get-audio-pad", G_TYPE_FROM_CLASS (klass),
1382 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1383 G_STRUCT_OFFSET (GstPlayBinClass, get_audio_pad), NULL, NULL, NULL,
1384 GST_TYPE_PAD, 1, G_TYPE_INT);
1385 /**
1386 * GstPlayBin::get-text-pad:
1387 * @playbin: a #GstPlayBin
1388 * @stream: a text stream number
1389 *
1390 * Action signal to retrieve the stream-combiner sinkpad for a specific
1391 * text stream.
1392 * This pad can be used for notifications of caps changes, stream-specific
1393 * queries, etc.
1394 *
1395 * Returns: a #GstPad, or NULL when the stream number does not exist.
1396 */
1397 gst_play_bin_signals[SIGNAL_GET_TEXT_PAD] =
1398 g_signal_new ("get-text-pad", G_TYPE_FROM_CLASS (klass),
1399 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
1400 G_STRUCT_OFFSET (GstPlayBinClass, get_text_pad), NULL, NULL, NULL,
1401 GST_TYPE_PAD, 1, G_TYPE_INT);
1402
1403 #ifdef OHOS_EXT_FUNC
1404 // ohos.ext.func.0028
1405 gst_play_bin_signals[SIGNAL_BITRATE_PARSE_COMPLETE] =
1406 g_signal_new("bitrate-parse-complete",
1407 G_TYPE_FROM_CLASS(klass), G_SIGNAL_RUN_LAST,
1408 0, NULL, NULL,
1409 g_cclosure_marshal_generic, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT);
1410 #endif
1411
1412 klass->get_video_tags = gst_play_bin_get_video_tags;
1413 klass->get_audio_tags = gst_play_bin_get_audio_tags;
1414 klass->get_text_tags = gst_play_bin_get_text_tags;
1415
1416 klass->convert_sample = gst_play_bin_convert_sample;
1417
1418 klass->get_video_pad = gst_play_bin_get_video_pad;
1419 klass->get_audio_pad = gst_play_bin_get_audio_pad;
1420 klass->get_text_pad = gst_play_bin_get_text_pad;
1421
1422 gst_element_class_set_static_metadata (gstelement_klass,
1423 "Player Bin 2", "Generic/Bin/Player",
1424 "Autoplug and play media from an uri",
1425 "Wim Taymans <wim.taymans@gmail.com>");
1426
1427 gstelement_klass->change_state =
1428 GST_DEBUG_FUNCPTR (gst_play_bin_change_state);
1429 gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_play_bin_query);
1430 gstelement_klass->set_context = GST_DEBUG_FUNCPTR (gst_play_bin_set_context);
1431 gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_bin_send_event);
1432
1433 gstbin_klass->handle_message =
1434 GST_DEBUG_FUNCPTR (gst_play_bin_handle_message);
1435 gstbin_klass->deep_element_added =
1436 GST_DEBUG_FUNCPTR (gst_play_bin_deep_element_added);
1437
1438 gst_type_mark_as_plugin_api (GST_TYPE_PLAY_FLAGS, 0);
1439 }
1440
1441 static void
do_async_start(GstPlayBin * playbin)1442 do_async_start (GstPlayBin * playbin)
1443 {
1444 GstMessage *message;
1445
1446 playbin->async_pending = TRUE;
1447
1448 message = gst_message_new_async_start (GST_OBJECT_CAST (playbin));
1449 GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (playbin),
1450 message);
1451 }
1452
1453 static void
do_async_done(GstPlayBin * playbin)1454 do_async_done (GstPlayBin * playbin)
1455 {
1456 GstMessage *message;
1457
1458 if (playbin->async_pending) {
1459 GST_DEBUG_OBJECT (playbin, "posting ASYNC_DONE");
1460 message =
1461 gst_message_new_async_done (GST_OBJECT_CAST (playbin),
1462 GST_CLOCK_TIME_NONE);
1463 GST_BIN_CLASS (parent_class)->handle_message (GST_BIN_CAST (playbin),
1464 message);
1465
1466 playbin->async_pending = FALSE;
1467 }
1468 }
1469
1470 static void
init_group(GstPlayBin * playbin,GstSourceGroup * group)1471 init_group (GstPlayBin * playbin, GstSourceGroup * group)
1472 {
1473 /* store the array for the different channels */
1474 group->video_channels = g_ptr_array_new ();
1475 group->audio_channels = g_ptr_array_new ();
1476 group->text_channels = g_ptr_array_new ();
1477 g_mutex_init (&group->lock);
1478
1479 group->stream_changed_pending = FALSE;
1480 g_mutex_init (&group->stream_changed_pending_lock);
1481
1482 /* init combiners. The combiner is found by finding the first prefix that
1483 * matches the media. */
1484 group->playbin = playbin;
1485 /* If you add any items to these lists, check that media_list[] is defined
1486 * above to be large enough to hold MAX(items)+1, so as to accommodate a
1487 * NULL terminator (set when the memory is zeroed on allocation) */
1488 group->combiner[PLAYBIN_STREAM_AUDIO].media_list[0] = "audio/";
1489 group->combiner[PLAYBIN_STREAM_AUDIO].type = GST_PLAY_SINK_TYPE_AUDIO;
1490 group->combiner[PLAYBIN_STREAM_AUDIO].channels = group->audio_channels;
1491 group->combiner[PLAYBIN_STREAM_VIDEO].media_list[0] = "video/";
1492 group->combiner[PLAYBIN_STREAM_VIDEO].media_list[1] = "image/";
1493 group->combiner[PLAYBIN_STREAM_VIDEO].type = GST_PLAY_SINK_TYPE_VIDEO;
1494 group->combiner[PLAYBIN_STREAM_VIDEO].channels = group->video_channels;
1495 group->combiner[PLAYBIN_STREAM_TEXT].media_list[0] = "text/";
1496 group->combiner[PLAYBIN_STREAM_TEXT].media_list[1] = "application/x-subtitle";
1497 group->combiner[PLAYBIN_STREAM_TEXT].media_list[2] = "application/x-ssa";
1498 group->combiner[PLAYBIN_STREAM_TEXT].media_list[3] = "application/x-ass";
1499 group->combiner[PLAYBIN_STREAM_TEXT].media_list[4] = "subpicture/x-dvd";
1500 group->combiner[PLAYBIN_STREAM_TEXT].media_list[5] = "subpicture/";
1501 group->combiner[PLAYBIN_STREAM_TEXT].media_list[6] = "subtitle/";
1502 group->combiner[PLAYBIN_STREAM_TEXT].get_media_caps =
1503 gst_subtitle_overlay_create_factory_caps;
1504 group->combiner[PLAYBIN_STREAM_TEXT].type = GST_PLAY_SINK_TYPE_TEXT;
1505 group->combiner[PLAYBIN_STREAM_TEXT].channels = group->text_channels;
1506 }
1507
1508 static void
free_group(GstPlayBin * playbin,GstSourceGroup * group)1509 free_group (GstPlayBin * playbin, GstSourceGroup * group)
1510 {
1511 g_free (group->uri);
1512 g_free (group->suburi);
1513 g_ptr_array_free (group->video_channels, TRUE);
1514 g_ptr_array_free (group->audio_channels, TRUE);
1515 g_ptr_array_free (group->text_channels, TRUE);
1516
1517 g_mutex_clear (&group->lock);
1518 if (group->audio_sink)
1519 gst_object_unref (group->audio_sink);
1520 group->audio_sink = NULL;
1521 if (group->video_sink)
1522 gst_object_unref (group->video_sink);
1523 group->video_sink = NULL;
1524 if (group->text_sink)
1525 gst_object_unref (group->text_sink);
1526 group->text_sink = NULL;
1527
1528 group->stream_changed_pending = FALSE;
1529 g_mutex_clear (&group->stream_changed_pending_lock);
1530
1531 g_slist_free (group->suburi_flushes_to_drop);
1532 group->suburi_flushes_to_drop = NULL;
1533
1534 if (group->suburi_flushes_to_drop_lock.p)
1535 g_mutex_clear (&group->suburi_flushes_to_drop_lock);
1536 group->suburi_flushes_to_drop_lock.p = NULL;
1537
1538 if (group->pending_buffering_msg)
1539 gst_message_unref (group->pending_buffering_msg);
1540 group->pending_buffering_msg = NULL;
1541 }
1542
1543 static void
notify_volume_cb(GObject * combiner,GParamSpec * pspec,GstPlayBin * playbin)1544 notify_volume_cb (GObject * combiner, GParamSpec * pspec, GstPlayBin * playbin)
1545 {
1546 g_object_notify (G_OBJECT (playbin), "volume");
1547 }
1548
1549 static void
notify_mute_cb(GObject * combiner,GParamSpec * pspec,GstPlayBin * playbin)1550 notify_mute_cb (GObject * combiner, GParamSpec * pspec, GstPlayBin * playbin)
1551 {
1552 g_object_notify (G_OBJECT (playbin), "mute");
1553 }
1554
1555 static void
colorbalance_value_changed_cb(GstColorBalance * balance,GstColorBalanceChannel * channel,gint value,GstPlayBin * playbin)1556 colorbalance_value_changed_cb (GstColorBalance * balance,
1557 GstColorBalanceChannel * channel, gint value, GstPlayBin * playbin)
1558 {
1559 gst_color_balance_value_changed (GST_COLOR_BALANCE (playbin), channel, value);
1560 }
1561
1562 static gint
compare_factories_func(gconstpointer p1,gconstpointer p2)1563 compare_factories_func (gconstpointer p1, gconstpointer p2)
1564 {
1565 GstPluginFeature *f1, *f2;
1566 gboolean is_sink1, is_sink2;
1567 gboolean is_parser1, is_parser2;
1568
1569 f1 = (GstPluginFeature *) p1;
1570 f2 = (GstPluginFeature *) p2;
1571
1572 is_sink1 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f1),
1573 GST_ELEMENT_FACTORY_TYPE_SINK);
1574 is_sink2 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f2),
1575 GST_ELEMENT_FACTORY_TYPE_SINK);
1576 is_parser1 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f1),
1577 GST_ELEMENT_FACTORY_TYPE_PARSER);
1578 is_parser2 = gst_element_factory_list_is_type (GST_ELEMENT_FACTORY_CAST (f2),
1579 GST_ELEMENT_FACTORY_TYPE_PARSER);
1580
1581 /* First we want all sinks as we prefer a sink if it directly
1582 * supports the current caps */
1583 if (is_sink1 && !is_sink2)
1584 return -1;
1585 else if (!is_sink1 && is_sink2)
1586 return 1;
1587
1588 /* Then we want all parsers as we always want to plug parsers
1589 * before decoders */
1590 if (is_parser1 && !is_parser2)
1591 return -1;
1592 else if (!is_parser1 && is_parser2)
1593 return 1;
1594
1595 /* And if it's a both a parser or sink we first sort by rank
1596 * and then by factory name */
1597 return gst_plugin_feature_rank_compare_func (p1, p2);
1598 }
1599
1600 /* Must be called with elements lock! */
1601 static void
gst_play_bin_update_elements_list(GstPlayBin * playbin)1602 gst_play_bin_update_elements_list (GstPlayBin * playbin)
1603 {
1604 GList *res, *tmp;
1605 guint cookie;
1606
1607 cookie = gst_registry_get_feature_list_cookie (gst_registry_get ());
1608
1609 if (!playbin->elements || playbin->elements_cookie != cookie) {
1610 if (playbin->elements)
1611 gst_plugin_feature_list_free (playbin->elements);
1612 res =
1613 gst_element_factory_list_get_elements
1614 (GST_ELEMENT_FACTORY_TYPE_DECODABLE, GST_RANK_MARGINAL);
1615 tmp =
1616 gst_element_factory_list_get_elements
1617 (GST_ELEMENT_FACTORY_TYPE_AUDIOVIDEO_SINKS, GST_RANK_MARGINAL);
1618 playbin->elements = g_list_concat (res, tmp);
1619 playbin->elements = g_list_sort (playbin->elements, compare_factories_func);
1620 }
1621
1622 if (!playbin->aelements || playbin->elements_cookie != cookie) {
1623 if (playbin->aelements)
1624 g_sequence_free (playbin->aelements);
1625 playbin->aelements = avelements_create (playbin, TRUE);
1626 }
1627
1628 if (!playbin->velements || playbin->elements_cookie != cookie) {
1629 if (playbin->velements)
1630 g_sequence_free (playbin->velements);
1631 playbin->velements = avelements_create (playbin, FALSE);
1632 }
1633
1634 playbin->elements_cookie = cookie;
1635 }
1636
1637 static void
gst_play_bin_init(GstPlayBin * playbin)1638 gst_play_bin_init (GstPlayBin * playbin)
1639 {
1640 g_rec_mutex_init (&playbin->lock);
1641 g_mutex_init (&playbin->dyn_lock);
1642
1643 /* assume we can create an input-selector */
1644 playbin->have_selector = TRUE;
1645
1646 /* init groups */
1647 playbin->curr_group = &playbin->groups[0];
1648 playbin->next_group = &playbin->groups[1];
1649 init_group (playbin, &playbin->groups[0]);
1650 init_group (playbin, &playbin->groups[1]);
1651
1652 /* first filter out the interesting element factories */
1653 g_mutex_init (&playbin->elements_lock);
1654
1655 /* add sink */
1656 playbin->playsink =
1657 g_object_new (GST_TYPE_PLAY_SINK, "name", "playsink", "send-event-mode",
1658 1, NULL);
1659 gst_bin_add (GST_BIN_CAST (playbin), GST_ELEMENT_CAST (playbin->playsink));
1660 gst_play_sink_set_flags (playbin->playsink, DEFAULT_FLAGS);
1661 /* Connect to notify::volume and notify::mute signals for proxying */
1662 g_signal_connect (playbin->playsink, "notify::volume",
1663 G_CALLBACK (notify_volume_cb), playbin);
1664 g_signal_connect (playbin->playsink, "notify::mute",
1665 G_CALLBACK (notify_mute_cb), playbin);
1666 g_signal_connect (playbin->playsink, "value-changed",
1667 G_CALLBACK (colorbalance_value_changed_cb), playbin);
1668
1669 playbin->current_video = DEFAULT_CURRENT_VIDEO;
1670 playbin->current_audio = DEFAULT_CURRENT_AUDIO;
1671 playbin->current_text = DEFAULT_CURRENT_TEXT;
1672
1673 playbin->buffer_duration = DEFAULT_BUFFER_DURATION;
1674 playbin->buffer_size = DEFAULT_BUFFER_SIZE;
1675 playbin->ring_buffer_max_size = DEFAULT_RING_BUFFER_MAX_SIZE;
1676
1677 playbin->force_aspect_ratio = TRUE;
1678
1679 playbin->multiview_mode = GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE;
1680 playbin->multiview_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
1681
1682 #ifdef OHOS_EXT_FUNC
1683 // ohos.ext.func.0012
1684 playbin->low_percent = DEFAULT_LOW_PERCENT;
1685 playbin->high_percent = DEFAULT_HIGH_PERCENT;
1686 #endif
1687
1688 }
1689
1690 static void
gst_play_bin_finalize(GObject * object)1691 gst_play_bin_finalize (GObject * object)
1692 {
1693 GstPlayBin *playbin;
1694
1695 playbin = GST_PLAY_BIN (object);
1696
1697 free_group (playbin, &playbin->groups[0]);
1698 free_group (playbin, &playbin->groups[1]);
1699
1700 if (playbin->source)
1701 gst_object_unref (playbin->source);
1702
1703 /* Setting states to NULL is safe here because playsink
1704 * will already be gone and none of these sinks will be
1705 * a child of playsink
1706 */
1707 if (playbin->video_sink) {
1708 gst_element_set_state (playbin->video_sink, GST_STATE_NULL);
1709 gst_object_unref (playbin->video_sink);
1710 }
1711 if (playbin->audio_sink) {
1712 gst_element_set_state (playbin->audio_sink, GST_STATE_NULL);
1713 gst_object_unref (playbin->audio_sink);
1714 }
1715 if (playbin->text_sink) {
1716 gst_element_set_state (playbin->text_sink, GST_STATE_NULL);
1717 gst_object_unref (playbin->text_sink);
1718 }
1719
1720 if (playbin->video_stream_combiner) {
1721 gst_element_set_state (playbin->video_stream_combiner, GST_STATE_NULL);
1722 gst_object_unref (playbin->video_stream_combiner);
1723 }
1724 if (playbin->audio_stream_combiner) {
1725 gst_element_set_state (playbin->audio_stream_combiner, GST_STATE_NULL);
1726 gst_object_unref (playbin->audio_stream_combiner);
1727 }
1728 if (playbin->text_stream_combiner) {
1729 gst_element_set_state (playbin->text_stream_combiner, GST_STATE_NULL);
1730 gst_object_unref (playbin->text_stream_combiner);
1731 }
1732
1733 if (playbin->elements)
1734 gst_plugin_feature_list_free (playbin->elements);
1735
1736 if (playbin->aelements)
1737 g_sequence_free (playbin->aelements);
1738
1739 if (playbin->velements)
1740 g_sequence_free (playbin->velements);
1741
1742 g_list_free_full (playbin->contexts, (GDestroyNotify) gst_context_unref);
1743
1744 g_rec_mutex_clear (&playbin->lock);
1745 g_mutex_clear (&playbin->dyn_lock);
1746 g_mutex_clear (&playbin->elements_lock);
1747
1748 playbin->is_live = FALSE;
1749
1750 G_OBJECT_CLASS (parent_class)->finalize (object);
1751 }
1752
1753 static gboolean
gst_playbin_uri_is_valid(GstPlayBin * playbin,const gchar * uri)1754 gst_playbin_uri_is_valid (GstPlayBin * playbin, const gchar * uri)
1755 {
1756 const gchar *c;
1757
1758 GST_LOG_OBJECT (playbin, "checking uri '%s'", uri);
1759
1760 /* this just checks the protocol */
1761 if (!gst_uri_is_valid (uri))
1762 return FALSE;
1763
1764 for (c = uri; *c != '\0'; ++c) {
1765 if (!g_ascii_isprint (*c))
1766 goto invalid;
1767 if (*c == ' ')
1768 goto invalid;
1769 }
1770
1771 return TRUE;
1772
1773 invalid:
1774 {
1775 GST_WARNING_OBJECT (playbin, "uri '%s' not valid, character #%u",
1776 uri, (guint) ((guintptr) c - (guintptr) uri));
1777 return FALSE;
1778 }
1779 }
1780
1781 static void
gst_play_bin_set_uri(GstPlayBin * playbin,const gchar * uri)1782 gst_play_bin_set_uri (GstPlayBin * playbin, const gchar * uri)
1783 {
1784 GstSourceGroup *group;
1785
1786 if (uri && !gst_playbin_uri_is_valid (playbin, uri)) {
1787 if (g_str_has_prefix (uri, "file:")) {
1788 GST_WARNING_OBJECT (playbin, "not entirely correct file URI '%s' - make "
1789 "sure to escape spaces and non-ASCII characters properly and specify "
1790 "an absolute path. Use gst_filename_to_uri() to convert filenames "
1791 "to URIs", uri);
1792 } else {
1793 /* GST_ERROR_OBJECT (playbin, "malformed URI '%s'", uri); */
1794 }
1795 }
1796
1797 GST_PLAY_BIN_LOCK (playbin);
1798 group = playbin->next_group;
1799
1800 GST_SOURCE_GROUP_LOCK (group);
1801 /* store the uri in the next group we will play */
1802 g_free (group->uri);
1803 if (uri) {
1804 group->uri = g_strdup (uri);
1805 group->valid = TRUE;
1806 } else {
1807 group->uri = NULL;
1808 group->valid = FALSE;
1809 }
1810 GST_SOURCE_GROUP_UNLOCK (group);
1811
1812 GST_DEBUG ("set new uri to %s", GST_STR_NULL (uri));
1813 GST_PLAY_BIN_UNLOCK (playbin);
1814 }
1815
1816 static void
gst_play_bin_set_suburi(GstPlayBin * playbin,const gchar * suburi)1817 gst_play_bin_set_suburi (GstPlayBin * playbin, const gchar * suburi)
1818 {
1819 GstSourceGroup *group;
1820
1821 GST_PLAY_BIN_LOCK (playbin);
1822 group = playbin->next_group;
1823
1824 GST_SOURCE_GROUP_LOCK (group);
1825 g_free (group->suburi);
1826 group->suburi = g_strdup (suburi);
1827 GST_SOURCE_GROUP_UNLOCK (group);
1828
1829 GST_DEBUG ("setting new .sub uri to %s", suburi);
1830
1831 GST_PLAY_BIN_UNLOCK (playbin);
1832 }
1833
1834 static void
gst_play_bin_set_flags(GstPlayBin * playbin,GstPlayFlags flags)1835 gst_play_bin_set_flags (GstPlayBin * playbin, GstPlayFlags flags)
1836 {
1837 GstPlayFlags old_flags;
1838 old_flags = gst_play_sink_get_flags (playbin->playsink);
1839
1840 if (flags != old_flags) {
1841 gst_play_sink_set_flags (playbin->playsink, flags);
1842 gst_play_sink_reconfigure (playbin->playsink);
1843 }
1844 }
1845
1846 static GstPlayFlags
gst_play_bin_get_flags(GstPlayBin * playbin)1847 gst_play_bin_get_flags (GstPlayBin * playbin)
1848 {
1849 GstPlayFlags flags;
1850
1851 flags = gst_play_sink_get_flags (playbin->playsink);
1852
1853 return flags;
1854 }
1855
1856 /* get the currently playing group or if nothing is playing, the next
1857 * group. Must be called with the PLAY_BIN_LOCK. */
1858 static GstSourceGroup *
get_group(GstPlayBin * playbin)1859 get_group (GstPlayBin * playbin)
1860 {
1861 GstSourceGroup *result;
1862
1863 if (!(result = playbin->curr_group))
1864 result = playbin->next_group;
1865
1866 return result;
1867 }
1868
1869 static GstPad *
gst_play_bin_get_video_pad(GstPlayBin * playbin,gint stream)1870 gst_play_bin_get_video_pad (GstPlayBin * playbin, gint stream)
1871 {
1872 GstPad *sinkpad = NULL;
1873 GstSourceGroup *group;
1874
1875 GST_PLAY_BIN_LOCK (playbin);
1876 group = get_group (playbin);
1877 if (stream < group->video_channels->len) {
1878 sinkpad = g_ptr_array_index (group->video_channels, stream);
1879 gst_object_ref (sinkpad);
1880 }
1881 GST_PLAY_BIN_UNLOCK (playbin);
1882
1883 return sinkpad;
1884 }
1885
1886 static GstPad *
gst_play_bin_get_audio_pad(GstPlayBin * playbin,gint stream)1887 gst_play_bin_get_audio_pad (GstPlayBin * playbin, gint stream)
1888 {
1889 GstPad *sinkpad = NULL;
1890 GstSourceGroup *group;
1891
1892 GST_PLAY_BIN_LOCK (playbin);
1893 group = get_group (playbin);
1894 if (stream < group->audio_channels->len) {
1895 sinkpad = g_ptr_array_index (group->audio_channels, stream);
1896 gst_object_ref (sinkpad);
1897 }
1898 GST_PLAY_BIN_UNLOCK (playbin);
1899
1900 return sinkpad;
1901 }
1902
1903 static GstPad *
gst_play_bin_get_text_pad(GstPlayBin * playbin,gint stream)1904 gst_play_bin_get_text_pad (GstPlayBin * playbin, gint stream)
1905 {
1906 GstPad *sinkpad = NULL;
1907 GstSourceGroup *group;
1908
1909 GST_PLAY_BIN_LOCK (playbin);
1910 group = get_group (playbin);
1911 if (stream < group->text_channels->len) {
1912 sinkpad = g_ptr_array_index (group->text_channels, stream);
1913 gst_object_ref (sinkpad);
1914 }
1915 GST_PLAY_BIN_UNLOCK (playbin);
1916
1917 return sinkpad;
1918 }
1919
1920
1921 static GstTagList *
get_tags(GstPlayBin * playbin,GstSourceGroup * group,gint type,gint stream)1922 get_tags (GstPlayBin * playbin, GstSourceGroup * group, gint type, gint stream)
1923 {
1924 GstTagList *result;
1925 GPtrArray *channels;
1926 GstPad *sinkpad;
1927
1928 switch (type) {
1929 case PLAYBIN_STREAM_AUDIO:
1930 channels = group->audio_channels;
1931 break;
1932 case PLAYBIN_STREAM_VIDEO:
1933 channels = group->video_channels;
1934 break;
1935 case PLAYBIN_STREAM_TEXT:
1936 channels = group->text_channels;
1937 break;
1938 default:
1939 channels = NULL;
1940 break;
1941 }
1942
1943 if (!channels || stream >= channels->len || !group->combiner[type].has_tags)
1944 return NULL;
1945
1946 sinkpad = g_ptr_array_index (channels, stream);
1947 g_object_get (sinkpad, "tags", &result, NULL);
1948
1949 return result;
1950 }
1951
1952 static GstTagList *
gst_play_bin_get_video_tags(GstPlayBin * playbin,gint stream)1953 gst_play_bin_get_video_tags (GstPlayBin * playbin, gint stream)
1954 {
1955 GstTagList *result;
1956 GstSourceGroup *group;
1957
1958 GST_PLAY_BIN_LOCK (playbin);
1959 group = get_group (playbin);
1960 result = get_tags (playbin, group, PLAYBIN_STREAM_VIDEO, stream);
1961 GST_PLAY_BIN_UNLOCK (playbin);
1962
1963 return result;
1964 }
1965
1966 static GstTagList *
gst_play_bin_get_audio_tags(GstPlayBin * playbin,gint stream)1967 gst_play_bin_get_audio_tags (GstPlayBin * playbin, gint stream)
1968 {
1969 GstTagList *result;
1970 GstSourceGroup *group;
1971
1972 GST_PLAY_BIN_LOCK (playbin);
1973 group = get_group (playbin);
1974 result = get_tags (playbin, group, PLAYBIN_STREAM_AUDIO, stream);
1975 GST_PLAY_BIN_UNLOCK (playbin);
1976
1977 return result;
1978 }
1979
1980 static GstTagList *
gst_play_bin_get_text_tags(GstPlayBin * playbin,gint stream)1981 gst_play_bin_get_text_tags (GstPlayBin * playbin, gint stream)
1982 {
1983 GstTagList *result;
1984 GstSourceGroup *group;
1985
1986 GST_PLAY_BIN_LOCK (playbin);
1987 group = get_group (playbin);
1988 result = get_tags (playbin, group, PLAYBIN_STREAM_TEXT, stream);
1989 GST_PLAY_BIN_UNLOCK (playbin);
1990
1991 return result;
1992 }
1993
1994 static GstSample *
gst_play_bin_convert_sample(GstPlayBin * playbin,GstCaps * caps)1995 gst_play_bin_convert_sample (GstPlayBin * playbin, GstCaps * caps)
1996 {
1997 return gst_play_sink_convert_sample (playbin->playsink, caps);
1998 }
1999
2000 /* Returns current stream number, or -1 if none has been selected yet */
2001 static int
get_current_stream_number(GstPlayBin * playbin,GstSourceCombine * combine,GPtrArray * channels)2002 get_current_stream_number (GstPlayBin * playbin, GstSourceCombine * combine,
2003 GPtrArray * channels)
2004 {
2005 /* Internal API cleanup would make this easier... */
2006 int i;
2007 GstPad *pad, *current;
2008 GstObject *combiner = NULL;
2009 int ret = -1;
2010
2011 if (!combine->has_active_pad) {
2012 GST_WARNING_OBJECT (playbin,
2013 "combiner doesn't have the \"active-pad\" property");
2014 return ret;
2015 }
2016
2017 for (i = 0; i < channels->len; i++) {
2018 pad = g_ptr_array_index (channels, i);
2019 if ((combiner = gst_pad_get_parent (pad))) {
2020 g_object_get (combiner, "active-pad", ¤t, NULL);
2021 gst_object_unref (combiner);
2022
2023 if (pad == current) {
2024 gst_object_unref (current);
2025 ret = i;
2026 break;
2027 }
2028
2029 if (current)
2030 gst_object_unref (current);
2031 }
2032 }
2033
2034 return ret;
2035 }
2036
2037 static gboolean
gst_play_bin_send_custom_event(GstObject * combiner,const gchar * event_name)2038 gst_play_bin_send_custom_event (GstObject * combiner, const gchar * event_name)
2039 {
2040 GstPad *src;
2041 GstPad *peer;
2042 GstStructure *s;
2043 GstEvent *event;
2044 gboolean ret = FALSE;
2045
2046 src = gst_element_get_static_pad (GST_ELEMENT_CAST (combiner), "src");
2047 peer = gst_pad_get_peer (src);
2048 if (peer) {
2049 s = gst_structure_new_empty (event_name);
2050 event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM_OOB, s);
2051 gst_pad_send_event (peer, event);
2052 gst_object_unref (peer);
2053 ret = TRUE;
2054 }
2055 gst_object_unref (src);
2056 return ret;
2057 }
2058
2059 static gboolean
gst_play_bin_set_current_video_stream(GstPlayBin * playbin,gint stream)2060 gst_play_bin_set_current_video_stream (GstPlayBin * playbin, gint stream)
2061 {
2062 GstSourceGroup *group;
2063 GPtrArray *channels;
2064 GstPad *sinkpad;
2065
2066 GST_PLAY_BIN_LOCK (playbin);
2067
2068 GST_DEBUG_OBJECT (playbin, "Changing current video stream %d -> %d",
2069 playbin->current_video, stream);
2070
2071 group = get_group (playbin);
2072 if (!group->combiner[PLAYBIN_STREAM_VIDEO].has_active_pad)
2073 goto no_active_pad;
2074 if (!(channels = group->video_channels))
2075 goto no_channels;
2076
2077 if (stream == -1 || channels->len <= stream) {
2078 sinkpad = NULL;
2079 } else {
2080 /* take channel from selected stream */
2081 sinkpad = g_ptr_array_index (channels, stream);
2082 }
2083
2084 if (sinkpad)
2085 gst_object_ref (sinkpad);
2086 GST_PLAY_BIN_UNLOCK (playbin);
2087
2088 if (sinkpad) {
2089 GstObject *combiner;
2090
2091 if ((combiner = gst_pad_get_parent (sinkpad))) {
2092 GstPad *old_sinkpad;
2093
2094 g_object_get (combiner, "active-pad", &old_sinkpad, NULL);
2095
2096 if (old_sinkpad != sinkpad) {
2097 if (gst_play_bin_send_custom_event (combiner,
2098 "playsink-custom-video-flush"))
2099 playbin->video_pending_flush_finish = TRUE;
2100
2101 /* activate the selected pad */
2102 g_object_set (combiner, "active-pad", sinkpad, NULL);
2103 }
2104
2105 if (old_sinkpad)
2106 gst_object_unref (old_sinkpad);
2107
2108 gst_object_unref (combiner);
2109 }
2110 gst_object_unref (sinkpad);
2111 }
2112 return TRUE;
2113
2114 no_active_pad:
2115 {
2116 GST_PLAY_BIN_UNLOCK (playbin);
2117 GST_WARNING_OBJECT (playbin,
2118 "can't switch video, the stream combiner's sink pads don't have the \"active-pad\" property");
2119 return FALSE;
2120 }
2121 no_channels:
2122 {
2123 GST_PLAY_BIN_UNLOCK (playbin);
2124 GST_DEBUG_OBJECT (playbin, "can't switch video, we have no channels");
2125 return FALSE;
2126 }
2127 }
2128
2129 static gboolean
gst_play_bin_set_current_audio_stream(GstPlayBin * playbin,gint stream)2130 gst_play_bin_set_current_audio_stream (GstPlayBin * playbin, gint stream)
2131 {
2132 GstSourceGroup *group;
2133 GPtrArray *channels;
2134 GstPad *sinkpad;
2135
2136 GST_PLAY_BIN_LOCK (playbin);
2137
2138 GST_DEBUG_OBJECT (playbin, "Changing current audio stream %d -> %d",
2139 playbin->current_audio, stream);
2140
2141 group = get_group (playbin);
2142 if (!group->combiner[PLAYBIN_STREAM_AUDIO].has_active_pad)
2143 goto no_active_pad;
2144 if (!(channels = group->audio_channels))
2145 goto no_channels;
2146
2147 if (stream == -1 || channels->len <= stream) {
2148 sinkpad = NULL;
2149 } else {
2150 /* take channel from selected stream */
2151 sinkpad = g_ptr_array_index (channels, stream);
2152 }
2153
2154 if (sinkpad)
2155 gst_object_ref (sinkpad);
2156 GST_PLAY_BIN_UNLOCK (playbin);
2157
2158 if (sinkpad) {
2159 GstObject *combiner;
2160
2161 if ((combiner = gst_pad_get_parent (sinkpad))) {
2162 GstPad *old_sinkpad;
2163
2164 g_object_get (combiner, "active-pad", &old_sinkpad, NULL);
2165
2166 if (old_sinkpad != sinkpad) {
2167 if (gst_play_bin_send_custom_event (combiner,
2168 "playsink-custom-audio-flush"))
2169 playbin->audio_pending_flush_finish = TRUE;
2170
2171 /* activate the selected pad */
2172 g_object_set (combiner, "active-pad", sinkpad, NULL);
2173 }
2174
2175 if (old_sinkpad)
2176 gst_object_unref (old_sinkpad);
2177
2178 gst_object_unref (combiner);
2179 }
2180 gst_object_unref (sinkpad);
2181 }
2182 return TRUE;
2183
2184 no_active_pad:
2185 {
2186 GST_PLAY_BIN_UNLOCK (playbin);
2187 GST_WARNING_OBJECT (playbin,
2188 "can't switch audio, the stream combiner's sink pads don't have the \"active-pad\" property");
2189 return FALSE;
2190 }
2191 no_channels:
2192 {
2193 GST_PLAY_BIN_UNLOCK (playbin);
2194 GST_DEBUG_OBJECT (playbin, "can't switch audio, we have no channels");
2195 return FALSE;
2196 }
2197 }
2198
2199 static void
gst_play_bin_suburidecodebin_seek_to_start(GstSourceGroup * group)2200 gst_play_bin_suburidecodebin_seek_to_start (GstSourceGroup * group)
2201 {
2202 GstElement *suburidecodebin = group->suburidecodebin;
2203 GstIterator *it = gst_element_iterate_src_pads (suburidecodebin);
2204 GstPad *sinkpad;
2205 GValue item = { 0, };
2206
2207 if (it && gst_iterator_next (it, &item) == GST_ITERATOR_OK
2208 && ((sinkpad = g_value_get_object (&item)) != NULL)) {
2209 GstEvent *event;
2210 guint32 seqnum;
2211
2212 event =
2213 gst_event_new_seek (1.0, GST_FORMAT_BYTES, GST_SEEK_FLAG_FLUSH,
2214 GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, -1);
2215 seqnum = gst_event_get_seqnum (event);
2216
2217 /* store the seqnum to drop flushes from this seek later */
2218 g_mutex_lock (&group->suburi_flushes_to_drop_lock);
2219 group->suburi_flushes_to_drop =
2220 g_slist_append (group->suburi_flushes_to_drop,
2221 GUINT_TO_POINTER (seqnum));
2222 g_mutex_unlock (&group->suburi_flushes_to_drop_lock);
2223
2224 if (!gst_pad_send_event (sinkpad, event)) {
2225 event =
2226 gst_event_new_seek (1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH,
2227 GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_NONE, -1);
2228 gst_event_set_seqnum (event, seqnum);
2229 if (!gst_pad_send_event (sinkpad, event)) {
2230 GST_DEBUG_OBJECT (suburidecodebin, "Seeking to the beginning failed!");
2231
2232 g_mutex_lock (&group->suburi_flushes_to_drop_lock);
2233 group->suburi_flushes_to_drop =
2234 g_slist_remove (group->suburi_flushes_to_drop,
2235 GUINT_TO_POINTER (seqnum));
2236 g_mutex_unlock (&group->suburi_flushes_to_drop_lock);
2237 }
2238 }
2239
2240 g_value_unset (&item);
2241 }
2242
2243 if (it)
2244 gst_iterator_free (it);
2245 }
2246
2247 static void
source_combine_remove_pads(GstPlayBin * playbin,GstSourceCombine * combine)2248 source_combine_remove_pads (GstPlayBin * playbin, GstSourceCombine * combine)
2249 {
2250 if (combine->sinkpad) {
2251 GST_LOG_OBJECT (playbin, "unlinking from sink");
2252 gst_pad_unlink (combine->srcpad, combine->sinkpad);
2253
2254 /* release back */
2255 GST_LOG_OBJECT (playbin, "release sink pad");
2256 gst_play_sink_release_pad (playbin->playsink, combine->sinkpad);
2257 gst_object_unref (combine->sinkpad);
2258 combine->sinkpad = NULL;
2259 }
2260 gst_object_unref (combine->srcpad);
2261 combine->srcpad = NULL;
2262 }
2263
2264 static GstPadProbeReturn
block_serialized_data_cb(GstPad * pad,GstPadProbeInfo * info,gpointer user_data)2265 block_serialized_data_cb (GstPad * pad, GstPadProbeInfo * info,
2266 gpointer user_data)
2267 {
2268 if (GST_IS_EVENT (info->data) && !GST_EVENT_IS_SERIALIZED (info->data)) {
2269 GST_DEBUG_OBJECT (pad, "Letting non-serialized event %s pass",
2270 GST_EVENT_TYPE_NAME (info->data));
2271 return GST_PAD_PROBE_PASS;
2272 }
2273
2274 return GST_PAD_PROBE_OK;
2275 }
2276
2277 static void
gst_play_bin_suburidecodebin_block(GstSourceGroup * group,GstElement * suburidecodebin,gboolean block)2278 gst_play_bin_suburidecodebin_block (GstSourceGroup * group,
2279 GstElement * suburidecodebin, gboolean block)
2280 {
2281 GstIterator *it = gst_element_iterate_src_pads (suburidecodebin);
2282 gboolean done = FALSE;
2283 GValue item = { 0, };
2284
2285 GST_DEBUG_OBJECT (suburidecodebin, "Blocking suburidecodebin: %d", block);
2286
2287 if (!it)
2288 return;
2289 while (!done) {
2290 GstPad *sinkpad;
2291
2292 switch (gst_iterator_next (it, &item)) {
2293 case GST_ITERATOR_OK:
2294 sinkpad = g_value_get_object (&item);
2295 if (block) {
2296 group->block_id =
2297 gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
2298 block_serialized_data_cb, NULL, NULL);
2299 } else if (group->block_id) {
2300 gst_pad_remove_probe (sinkpad, group->block_id);
2301 group->block_id = 0;
2302 }
2303 g_value_reset (&item);
2304 break;
2305 case GST_ITERATOR_DONE:
2306 done = TRUE;
2307 break;
2308 case GST_ITERATOR_RESYNC:
2309 gst_iterator_resync (it);
2310 break;
2311 case GST_ITERATOR_ERROR:
2312 done = TRUE;
2313 break;
2314 }
2315 }
2316 g_value_unset (&item);
2317 gst_iterator_free (it);
2318 }
2319
2320 static gboolean
gst_play_bin_set_current_text_stream(GstPlayBin * playbin,gint stream)2321 gst_play_bin_set_current_text_stream (GstPlayBin * playbin, gint stream)
2322 {
2323 GstSourceGroup *group;
2324 GPtrArray *channels;
2325 GstPad *sinkpad;
2326
2327 GST_PLAY_BIN_LOCK (playbin);
2328
2329 GST_DEBUG_OBJECT (playbin, "Changing current text stream %d -> %d",
2330 playbin->current_text, stream);
2331
2332 group = get_group (playbin);
2333 if (!group->combiner[PLAYBIN_STREAM_TEXT].has_active_pad)
2334 goto no_active_pad;
2335 if (!(channels = group->text_channels))
2336 goto no_channels;
2337
2338 if (stream == -1 || channels->len <= stream) {
2339 sinkpad = NULL;
2340 } else {
2341 /* take channel from selected stream */
2342 sinkpad = g_ptr_array_index (channels, stream);
2343 }
2344
2345 if (sinkpad)
2346 gst_object_ref (sinkpad);
2347 GST_PLAY_BIN_UNLOCK (playbin);
2348
2349 if (sinkpad) {
2350 GstObject *combiner;
2351
2352 if ((combiner = gst_pad_get_parent (sinkpad))) {
2353 GstPad *old_sinkpad;
2354
2355 g_object_get (combiner, "active-pad", &old_sinkpad, NULL);
2356
2357 if (old_sinkpad != sinkpad) {
2358 gboolean need_unblock, need_block, need_seek;
2359 GstPad *peer = NULL, *oldpeer = NULL;
2360 GstElement *parent_element = NULL, *old_parent_element = NULL;
2361
2362 /* Now check if we need to seek the suburidecodebin to the beginning
2363 * or if we need to block all suburidecodebin sinkpads or if we need
2364 * to unblock all suburidecodebin sinkpads
2365 */
2366 if (sinkpad)
2367 peer = gst_pad_get_peer (sinkpad);
2368 if (old_sinkpad)
2369 oldpeer = gst_pad_get_peer (old_sinkpad);
2370
2371 if (peer)
2372 parent_element = gst_pad_get_parent_element (peer);
2373 if (oldpeer)
2374 old_parent_element = gst_pad_get_parent_element (oldpeer);
2375
2376 need_block = (old_parent_element == group->suburidecodebin
2377 && parent_element != old_parent_element);
2378 need_unblock = (parent_element == group->suburidecodebin
2379 && parent_element != old_parent_element);
2380 need_seek = (parent_element == group->suburidecodebin);
2381
2382 if (peer)
2383 gst_object_unref (peer);
2384 if (oldpeer)
2385 gst_object_unref (oldpeer);
2386 if (parent_element)
2387 gst_object_unref (parent_element);
2388 if (old_parent_element)
2389 gst_object_unref (old_parent_element);
2390
2391 /* Block all suburidecodebin sinkpads */
2392 if (need_block)
2393 gst_play_bin_suburidecodebin_block (group, group->suburidecodebin,
2394 TRUE);
2395
2396 if (gst_play_bin_send_custom_event (combiner,
2397 "playsink-custom-subtitle-flush"))
2398 playbin->text_pending_flush_finish = TRUE;
2399
2400 /* activate the selected pad */
2401 g_object_set (combiner, "active-pad", sinkpad, NULL);
2402
2403 /* Unblock pads if necessary */
2404 if (need_unblock)
2405 gst_play_bin_suburidecodebin_block (group, group->suburidecodebin,
2406 FALSE);
2407
2408 /* seek to the beginning */
2409 if (need_seek)
2410 gst_play_bin_suburidecodebin_seek_to_start (group);
2411 }
2412 gst_object_unref (combiner);
2413
2414 if (old_sinkpad)
2415 gst_object_unref (old_sinkpad);
2416 }
2417 gst_object_unref (sinkpad);
2418 }
2419 return TRUE;
2420
2421 no_active_pad:
2422 {
2423 GST_PLAY_BIN_UNLOCK (playbin);
2424 GST_WARNING_OBJECT (playbin,
2425 "can't switch text, the stream combiner's sink pads don't have the \"active-pad\" property");
2426 return FALSE;
2427 }
2428 no_channels:
2429 {
2430 GST_PLAY_BIN_UNLOCK (playbin);
2431 return FALSE;
2432 }
2433 }
2434
2435 static void
gst_play_bin_set_sink(GstPlayBin * playbin,GstPlaySinkType type,const gchar * dbg,GstElement ** elem,GstElement * sink)2436 gst_play_bin_set_sink (GstPlayBin * playbin, GstPlaySinkType type,
2437 const gchar * dbg, GstElement ** elem, GstElement * sink)
2438 {
2439 GST_INFO_OBJECT (playbin, "Setting %s sink to %" GST_PTR_FORMAT, dbg, sink);
2440
2441 gst_play_sink_set_sink (playbin->playsink, type, sink);
2442
2443 if (*elem)
2444 gst_object_unref (*elem);
2445 *elem = sink ? gst_object_ref (sink) : NULL;
2446 }
2447
2448 static void
gst_play_bin_set_stream_combiner(GstPlayBin * playbin,GstElement ** elem,const gchar * dbg,GstElement * combiner)2449 gst_play_bin_set_stream_combiner (GstPlayBin * playbin, GstElement ** elem,
2450 const gchar * dbg, GstElement * combiner)
2451 {
2452 GST_INFO_OBJECT (playbin, "Setting %s stream combiner to %" GST_PTR_FORMAT,
2453 dbg, combiner);
2454
2455 GST_PLAY_BIN_LOCK (playbin);
2456 if (*elem != combiner) {
2457 GstElement *old;
2458
2459 old = *elem;
2460 if (combiner)
2461 gst_object_ref_sink (combiner);
2462
2463 *elem = combiner;
2464 if (old)
2465 gst_object_unref (old);
2466 }
2467 GST_LOG_OBJECT (playbin, "%s stream combiner now %" GST_PTR_FORMAT, dbg,
2468 *elem);
2469 GST_PLAY_BIN_UNLOCK (playbin);
2470 }
2471
2472 static void
gst_play_bin_set_encoding(GstPlayBin * playbin,const gchar * encoding)2473 gst_play_bin_set_encoding (GstPlayBin * playbin, const gchar * encoding)
2474 {
2475 GstElement *elem;
2476
2477 GST_PLAY_BIN_LOCK (playbin);
2478
2479 /* set subtitles on all current and next decodebins. */
2480 if ((elem = playbin->groups[0].uridecodebin))
2481 g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
2482 if ((elem = playbin->groups[0].suburidecodebin))
2483 g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
2484 if ((elem = playbin->groups[1].uridecodebin))
2485 g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
2486 if ((elem = playbin->groups[1].suburidecodebin))
2487 g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
2488
2489 gst_play_sink_set_subtitle_encoding (playbin->playsink, encoding);
2490 GST_PLAY_BIN_UNLOCK (playbin);
2491 }
2492
2493 static void
gst_play_bin_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)2494 gst_play_bin_set_property (GObject * object, guint prop_id,
2495 const GValue * value, GParamSpec * pspec)
2496 {
2497 GstPlayBin *playbin = GST_PLAY_BIN (object);
2498
2499 switch (prop_id) {
2500 case PROP_URI:
2501 gst_play_bin_set_uri (playbin, g_value_get_string (value));
2502 break;
2503 case PROP_SUBURI:
2504 gst_play_bin_set_suburi (playbin, g_value_get_string (value));
2505 break;
2506 case PROP_FLAGS:
2507 gst_play_bin_set_flags (playbin, g_value_get_flags (value));
2508 if (playbin->curr_group) {
2509 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
2510 if (playbin->curr_group->uridecodebin) {
2511 guint flags = g_value_get_flags (value);
2512 g_object_set (playbin->curr_group->uridecodebin,
2513 "download", (flags & GST_PLAY_FLAG_DOWNLOAD) != 0,
2514 "force-sw-decoders",
2515 (flags & GST_PLAY_FLAG_FORCE_SW_DECODERS) != 0, NULL);
2516 }
2517 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
2518 }
2519 break;
2520 case PROP_CURRENT_VIDEO:
2521 gst_play_bin_set_current_video_stream (playbin, g_value_get_int (value));
2522 break;
2523 case PROP_CURRENT_AUDIO:
2524 gst_play_bin_set_current_audio_stream (playbin, g_value_get_int (value));
2525 break;
2526 case PROP_CURRENT_TEXT:
2527 gst_play_bin_set_current_text_stream (playbin, g_value_get_int (value));
2528 break;
2529 case PROP_SUBTITLE_ENCODING:
2530 gst_play_bin_set_encoding (playbin, g_value_get_string (value));
2531 break;
2532 case PROP_VIDEO_FILTER:
2533 gst_play_sink_set_filter (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO,
2534 GST_ELEMENT (g_value_get_object (value)));
2535 break;
2536 case PROP_AUDIO_FILTER:
2537 gst_play_sink_set_filter (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO,
2538 GST_ELEMENT (g_value_get_object (value)));
2539 break;
2540 case PROP_VIDEO_SINK:
2541 gst_play_bin_set_sink (playbin, GST_PLAY_SINK_TYPE_VIDEO, "video",
2542 &playbin->video_sink, g_value_get_object (value));
2543 break;
2544 case PROP_AUDIO_SINK:
2545 gst_play_bin_set_sink (playbin, GST_PLAY_SINK_TYPE_AUDIO, "audio",
2546 &playbin->audio_sink, g_value_get_object (value));
2547 break;
2548 case PROP_VIS_PLUGIN:
2549 gst_play_sink_set_vis_plugin (playbin->playsink,
2550 g_value_get_object (value));
2551 break;
2552 case PROP_TEXT_SINK:
2553 gst_play_bin_set_sink (playbin, GST_PLAY_SINK_TYPE_TEXT, "text",
2554 &playbin->text_sink, g_value_get_object (value));
2555 break;
2556 case PROP_VIDEO_STREAM_COMBINER:
2557 gst_play_bin_set_stream_combiner (playbin,
2558 &playbin->video_stream_combiner, "video", g_value_get_object (value));
2559 break;
2560 case PROP_AUDIO_STREAM_COMBINER:
2561 gst_play_bin_set_stream_combiner (playbin,
2562 &playbin->audio_stream_combiner, "audio", g_value_get_object (value));
2563 break;
2564 case PROP_TEXT_STREAM_COMBINER:
2565 gst_play_bin_set_stream_combiner (playbin,
2566 &playbin->text_stream_combiner, "text", g_value_get_object (value));
2567 break;
2568 case PROP_VOLUME:
2569 gst_play_sink_set_volume (playbin->playsink, g_value_get_double (value));
2570 break;
2571 case PROP_MUTE:
2572 gst_play_sink_set_mute (playbin->playsink, g_value_get_boolean (value));
2573 break;
2574 case PROP_FONT_DESC:
2575 gst_play_sink_set_font_desc (playbin->playsink,
2576 g_value_get_string (value));
2577 break;
2578 case PROP_CONNECTION_SPEED:
2579 GST_PLAY_BIN_LOCK (playbin);
2580 playbin->connection_speed = g_value_get_uint64 (value) * 1000;
2581 GST_PLAY_BIN_UNLOCK (playbin);
2582 #ifdef OHOS_EXT_FUNC
2583 // ohos.ext.func.0028
2584 if (playbin->curr_group && playbin->curr_group->uridecodebin) {
2585 g_object_set (playbin->curr_group->uridecodebin, "connection-speed", g_value_get_uint64 (value), NULL);
2586 }
2587 #endif
2588 break;
2589 case PROP_BUFFER_SIZE:
2590 playbin->buffer_size = g_value_get_int (value);
2591 break;
2592 case PROP_BUFFER_DURATION:
2593 #ifdef OHOS_EXT_FUNC
2594 // ohos.ext.func.0012
2595 playbin->buffer_duration = g_value_get_uint64 (value);
2596 #else
2597 playbin->buffer_duration = g_value_get_int64 (value);
2598 #endif
2599 break;
2600 case PROP_AV_OFFSET:
2601 gst_play_sink_set_av_offset (playbin->playsink,
2602 g_value_get_int64 (value));
2603 break;
2604 case PROP_TEXT_OFFSET:
2605 gst_play_sink_set_text_offset (playbin->playsink,
2606 g_value_get_int64 (value));
2607 break;
2608 case PROP_RING_BUFFER_MAX_SIZE:
2609 playbin->ring_buffer_max_size = g_value_get_uint64 (value);
2610 if (playbin->curr_group) {
2611 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
2612 if (playbin->curr_group->uridecodebin) {
2613 g_object_set (playbin->curr_group->uridecodebin,
2614 "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL);
2615 }
2616 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
2617 }
2618 break;
2619 case PROP_FORCE_ASPECT_RATIO:
2620 g_object_set (playbin->playsink, "force-aspect-ratio",
2621 g_value_get_boolean (value), NULL);
2622 break;
2623 case PROP_MULTIVIEW_MODE:
2624 GST_PLAY_BIN_LOCK (playbin);
2625 playbin->multiview_mode = g_value_get_enum (value);
2626 GST_PLAY_BIN_UNLOCK (playbin);
2627 break;
2628 case PROP_MULTIVIEW_FLAGS:
2629 GST_PLAY_BIN_LOCK (playbin);
2630 playbin->multiview_flags = g_value_get_flags (value);
2631 GST_PLAY_BIN_UNLOCK (playbin);
2632 break;
2633 #ifdef OHOS_EXT_FUNC
2634 // ohos.ext.func.0012
2635 case PROP_STATE_CHANGE:
2636 if (playbin->curr_group) {
2637 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
2638 if (playbin->curr_group->uridecodebin) {
2639 g_object_set (playbin->curr_group->uridecodebin, "state-change", g_value_get_int (value), NULL);
2640 }
2641 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
2642 }
2643 break;
2644 case PROP_EXIT_BLOCK:
2645 if (playbin->curr_group != NULL) {
2646 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
2647 if (playbin->curr_group->uridecodebin) {
2648 g_object_set (playbin->curr_group->uridecodebin, "exit-block", g_value_get_int (value), NULL);
2649 }
2650 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
2651 }
2652 break;
2653 case PROP_TIMEOUT:
2654 if (playbin->curr_group != NULL) {
2655 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
2656 if (playbin->curr_group->uridecodebin) {
2657 g_object_set (playbin->curr_group->uridecodebin, "timeout", g_value_get_uint (value), NULL);
2658 }
2659 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
2660 }
2661 break;
2662 case PROP_LOW_PERCENT:
2663 playbin->low_percent = g_value_get_int (value);
2664 break;
2665 case PROP_HIGH_PERCENT:
2666 playbin->high_percent = g_value_get_int (value);
2667 break;
2668 case PROP_BUFFERING_FLAGS:
2669 if (g_value_get_boolean (value)) {
2670 GstPlayFlags flags = gst_play_bin_get_flags (playbin);
2671 flags |= GST_PLAY_FLAG_BUFFERING;
2672 gst_play_bin_set_flags (playbin, flags);
2673 }
2674 break;
2675 #endif
2676 default:
2677 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2678 break;
2679 }
2680 }
2681
2682 static GstElement *
gst_play_bin_get_current_sink(GstPlayBin * playbin,GstElement ** elem,const gchar * dbg,GstPlaySinkType type)2683 gst_play_bin_get_current_sink (GstPlayBin * playbin, GstElement ** elem,
2684 const gchar * dbg, GstPlaySinkType type)
2685 {
2686 GstElement *sink = gst_play_sink_get_sink (playbin->playsink, type);
2687
2688 GST_LOG_OBJECT (playbin, "play_sink_get_sink() returned %s sink %"
2689 GST_PTR_FORMAT ", the originally set %s sink is %" GST_PTR_FORMAT,
2690 dbg, sink, dbg, *elem);
2691
2692 if (sink == NULL) {
2693 GST_PLAY_BIN_LOCK (playbin);
2694 if ((sink = *elem))
2695 gst_object_ref (sink);
2696 GST_PLAY_BIN_UNLOCK (playbin);
2697 }
2698
2699 return sink;
2700 }
2701
2702 static GstElement *
gst_play_bin_get_current_stream_combiner(GstPlayBin * playbin,GstElement ** elem,const gchar * dbg,int stream_type)2703 gst_play_bin_get_current_stream_combiner (GstPlayBin * playbin,
2704 GstElement ** elem, const gchar * dbg, int stream_type)
2705 {
2706 GstElement *combiner;
2707
2708 GST_PLAY_BIN_LOCK (playbin);
2709 if ((combiner = playbin->curr_group->combiner[stream_type].combiner))
2710 gst_object_ref (combiner);
2711 else if ((combiner = *elem))
2712 gst_object_ref (combiner);
2713 GST_PLAY_BIN_UNLOCK (playbin);
2714
2715 return combiner;
2716 }
2717
2718 static void
gst_play_bin_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)2719 gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
2720 GParamSpec * pspec)
2721 {
2722 GstPlayBin *playbin = GST_PLAY_BIN (object);
2723
2724 switch (prop_id) {
2725 case PROP_URI:
2726 {
2727 GstSourceGroup *group;
2728
2729 GST_PLAY_BIN_LOCK (playbin);
2730 group = playbin->next_group;
2731 g_value_set_string (value, group->uri);
2732 GST_PLAY_BIN_UNLOCK (playbin);
2733 break;
2734 }
2735 case PROP_CURRENT_URI:
2736 {
2737 GstSourceGroup *group;
2738
2739 GST_PLAY_BIN_LOCK (playbin);
2740 group = get_group (playbin);
2741 g_value_set_string (value, group->uri);
2742 GST_PLAY_BIN_UNLOCK (playbin);
2743 break;
2744 }
2745 case PROP_SUBURI:
2746 {
2747 GstSourceGroup *group;
2748
2749 GST_PLAY_BIN_LOCK (playbin);
2750 group = playbin->next_group;
2751 g_value_set_string (value, group->suburi);
2752 GST_PLAY_BIN_UNLOCK (playbin);
2753 break;
2754 }
2755 case PROP_CURRENT_SUBURI:
2756 {
2757 GstSourceGroup *group;
2758
2759 GST_PLAY_BIN_LOCK (playbin);
2760 group = get_group (playbin);
2761 g_value_set_string (value, group->suburi);
2762 GST_PLAY_BIN_UNLOCK (playbin);
2763 break;
2764 }
2765 case PROP_SOURCE:
2766 {
2767 GST_OBJECT_LOCK (playbin);
2768 g_value_set_object (value, playbin->source);
2769 GST_OBJECT_UNLOCK (playbin);
2770 break;
2771 }
2772 case PROP_FLAGS:
2773 g_value_set_flags (value, gst_play_bin_get_flags (playbin));
2774 break;
2775 case PROP_N_VIDEO:
2776 {
2777 GstSourceGroup *group;
2778 gint n_video;
2779
2780 GST_PLAY_BIN_LOCK (playbin);
2781 group = get_group (playbin);
2782 n_video = (group->video_channels ? group->video_channels->len : 0);
2783 g_value_set_int (value, n_video);
2784 GST_PLAY_BIN_UNLOCK (playbin);
2785 break;
2786 }
2787 case PROP_CURRENT_VIDEO:
2788 GST_PLAY_BIN_LOCK (playbin);
2789 g_value_set_int (value, playbin->current_video);
2790 GST_PLAY_BIN_UNLOCK (playbin);
2791 break;
2792 case PROP_N_AUDIO:
2793 {
2794 GstSourceGroup *group;
2795 gint n_audio;
2796
2797 GST_PLAY_BIN_LOCK (playbin);
2798 group = get_group (playbin);
2799 n_audio = (group->audio_channels ? group->audio_channels->len : 0);
2800 g_value_set_int (value, n_audio);
2801 GST_PLAY_BIN_UNLOCK (playbin);
2802 break;
2803 }
2804 case PROP_CURRENT_AUDIO:
2805 GST_PLAY_BIN_LOCK (playbin);
2806 g_value_set_int (value, playbin->current_audio);
2807 GST_PLAY_BIN_UNLOCK (playbin);
2808 break;
2809 case PROP_N_TEXT:
2810 {
2811 GstSourceGroup *group;
2812 gint n_text;
2813
2814 GST_PLAY_BIN_LOCK (playbin);
2815 group = get_group (playbin);
2816 n_text = (group->text_channels ? group->text_channels->len : 0);
2817 g_value_set_int (value, n_text);
2818 GST_PLAY_BIN_UNLOCK (playbin);
2819 break;
2820 }
2821 case PROP_CURRENT_TEXT:
2822 GST_PLAY_BIN_LOCK (playbin);
2823 g_value_set_int (value, playbin->current_text);
2824 GST_PLAY_BIN_UNLOCK (playbin);
2825 break;
2826 case PROP_SUBTITLE_ENCODING:
2827 GST_PLAY_BIN_LOCK (playbin);
2828 g_value_take_string (value,
2829 gst_play_sink_get_subtitle_encoding (playbin->playsink));
2830 GST_PLAY_BIN_UNLOCK (playbin);
2831 break;
2832 case PROP_VIDEO_FILTER:
2833 g_value_take_object (value,
2834 gst_play_sink_get_filter (playbin->playsink,
2835 GST_PLAY_SINK_TYPE_VIDEO));
2836 break;
2837 case PROP_AUDIO_FILTER:
2838 g_value_take_object (value,
2839 gst_play_sink_get_filter (playbin->playsink,
2840 GST_PLAY_SINK_TYPE_AUDIO));
2841 break;
2842 case PROP_VIDEO_SINK:
2843 g_value_take_object (value,
2844 gst_play_bin_get_current_sink (playbin, &playbin->video_sink,
2845 "video", GST_PLAY_SINK_TYPE_VIDEO));
2846 break;
2847 case PROP_AUDIO_SINK:
2848 g_value_take_object (value,
2849 gst_play_bin_get_current_sink (playbin, &playbin->audio_sink,
2850 "audio", GST_PLAY_SINK_TYPE_AUDIO));
2851 break;
2852 case PROP_VIS_PLUGIN:
2853 g_value_take_object (value,
2854 gst_play_sink_get_vis_plugin (playbin->playsink));
2855 break;
2856 case PROP_TEXT_SINK:
2857 g_value_take_object (value,
2858 gst_play_bin_get_current_sink (playbin, &playbin->text_sink,
2859 "text", GST_PLAY_SINK_TYPE_TEXT));
2860 break;
2861 case PROP_VIDEO_STREAM_COMBINER:
2862 g_value_take_object (value,
2863 gst_play_bin_get_current_stream_combiner (playbin,
2864 &playbin->video_stream_combiner, "video", PLAYBIN_STREAM_VIDEO));
2865 break;
2866 case PROP_AUDIO_STREAM_COMBINER:
2867 g_value_take_object (value,
2868 gst_play_bin_get_current_stream_combiner (playbin,
2869 &playbin->audio_stream_combiner, "audio", PLAYBIN_STREAM_AUDIO));
2870 break;
2871 case PROP_TEXT_STREAM_COMBINER:
2872 g_value_take_object (value,
2873 gst_play_bin_get_current_stream_combiner (playbin,
2874 &playbin->text_stream_combiner, "text", PLAYBIN_STREAM_TEXT));
2875 break;
2876 case PROP_VOLUME:
2877 g_value_set_double (value, gst_play_sink_get_volume (playbin->playsink));
2878 break;
2879 case PROP_MUTE:
2880 g_value_set_boolean (value, gst_play_sink_get_mute (playbin->playsink));
2881 break;
2882 case PROP_SAMPLE:
2883 gst_value_take_sample (value,
2884 gst_play_sink_get_last_sample (playbin->playsink));
2885 break;
2886 case PROP_FONT_DESC:
2887 g_value_take_string (value,
2888 gst_play_sink_get_font_desc (playbin->playsink));
2889 break;
2890 case PROP_CONNECTION_SPEED:
2891 GST_PLAY_BIN_LOCK (playbin);
2892 g_value_set_uint64 (value, playbin->connection_speed / 1000);
2893 GST_PLAY_BIN_UNLOCK (playbin);
2894 break;
2895 case PROP_BUFFER_SIZE:
2896 GST_OBJECT_LOCK (playbin);
2897 g_value_set_int (value, playbin->buffer_size);
2898 GST_OBJECT_UNLOCK (playbin);
2899 break;
2900 case PROP_BUFFER_DURATION:
2901 GST_OBJECT_LOCK (playbin);
2902 #ifdef OHOS_EXT_FUNC
2903 // ohos.ext.func.0012
2904 g_value_set_uint64 (value, playbin->buffer_duration);
2905 #else
2906 g_value_set_int64 (value, playbin->buffer_duration);
2907 #endif
2908 GST_OBJECT_UNLOCK (playbin);
2909 break;
2910 case PROP_AV_OFFSET:
2911 g_value_set_int64 (value,
2912 gst_play_sink_get_av_offset (playbin->playsink));
2913 break;
2914 case PROP_TEXT_OFFSET:
2915 g_value_set_int64 (value,
2916 gst_play_sink_get_text_offset (playbin->playsink));
2917 break;
2918 case PROP_RING_BUFFER_MAX_SIZE:
2919 g_value_set_uint64 (value, playbin->ring_buffer_max_size);
2920 break;
2921 case PROP_FORCE_ASPECT_RATIO:{
2922 gboolean v;
2923
2924 g_object_get (playbin->playsink, "force-aspect-ratio", &v, NULL);
2925 g_value_set_boolean (value, v);
2926 break;
2927 }
2928 case PROP_MULTIVIEW_MODE:
2929 GST_OBJECT_LOCK (playbin);
2930 g_value_set_enum (value, playbin->multiview_mode);
2931 GST_OBJECT_UNLOCK (playbin);
2932 break;
2933 case PROP_MULTIVIEW_FLAGS:
2934 GST_OBJECT_LOCK (playbin);
2935 g_value_set_flags (value, playbin->multiview_flags);
2936 GST_OBJECT_UNLOCK (playbin);
2937 break;
2938 #ifdef OHOS_EXT_FUNC
2939 // ohos.ext.func.0012
2940 case PROP_LOW_PERCENT:
2941 g_value_set_int (value, playbin->low_percent);
2942 break;
2943 case PROP_HIGH_PERCENT:
2944 g_value_set_int (value, playbin->high_percent);
2945 break;
2946 #endif
2947 default:
2948 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
2949 break;
2950 }
2951 }
2952
2953 static void
gst_play_bin_update_cached_duration_from_query(GstPlayBin * playbin,gboolean valid,GstQuery * query)2954 gst_play_bin_update_cached_duration_from_query (GstPlayBin * playbin,
2955 gboolean valid, GstQuery * query)
2956 {
2957 GstFormat fmt;
2958 gint64 duration;
2959 gint i;
2960
2961 GST_DEBUG_OBJECT (playbin, "Updating cached duration from query");
2962 gst_query_parse_duration (query, &fmt, &duration);
2963
2964 for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
2965 if (playbin->duration[i].format == 0 || fmt == playbin->duration[i].format) {
2966 playbin->duration[i].valid = valid;
2967 playbin->duration[i].format = fmt;
2968 playbin->duration[i].duration = valid ? duration : -1;
2969 break;
2970 }
2971 }
2972 }
2973
2974 static void
gst_play_bin_update_cached_duration(GstPlayBin * playbin)2975 gst_play_bin_update_cached_duration (GstPlayBin * playbin)
2976 {
2977 const GstFormat formats[] =
2978 { GST_FORMAT_TIME, GST_FORMAT_BYTES, GST_FORMAT_DEFAULT };
2979 gboolean ret;
2980 GstQuery *query;
2981 gint i;
2982
2983 GST_DEBUG_OBJECT (playbin, "Updating cached durations before group switch");
2984 for (i = 0; i < G_N_ELEMENTS (formats); i++) {
2985 query = gst_query_new_duration (formats[i]);
2986 ret =
2987 GST_ELEMENT_CLASS (parent_class)->query (GST_ELEMENT_CAST (playbin),
2988 query);
2989 gst_play_bin_update_cached_duration_from_query (playbin, ret, query);
2990 gst_query_unref (query);
2991 }
2992 }
2993
2994 static gboolean
gst_play_bin_query(GstElement * element,GstQuery * query)2995 gst_play_bin_query (GstElement * element, GstQuery * query)
2996 {
2997 GstPlayBin *playbin = GST_PLAY_BIN (element);
2998 gboolean ret;
2999
3000 /* During a group switch we shouldn't allow duration queries
3001 * because it's not clear if the old or new group's duration
3002 * is returned and if the sinks are already playing new data
3003 * or old data. See bug #585969
3004 *
3005 * While we're at it, also don't do any other queries during
3006 * a group switch or any other event that causes topology changes
3007 * by taking the playbin lock in any case.
3008 */
3009 GST_PLAY_BIN_LOCK (playbin);
3010
3011 if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION) {
3012 GstSourceGroup *group = playbin->curr_group;
3013 gboolean pending;
3014
3015 GST_SOURCE_GROUP_LOCK (group);
3016
3017 pending = group->pending || group->stream_changed_pending;
3018
3019 if (pending) {
3020 GstFormat fmt;
3021 gint i;
3022
3023 ret = FALSE;
3024 gst_query_parse_duration (query, &fmt, NULL);
3025 for (i = 0; i < G_N_ELEMENTS (playbin->duration); i++) {
3026 if (fmt == playbin->duration[i].format) {
3027 ret = playbin->duration[i].valid;
3028 gst_query_set_duration (query, fmt,
3029 (ret ? playbin->duration[i].duration : -1));
3030 break;
3031 }
3032 }
3033 /* if nothing cached yet, we might as well request duration,
3034 * such as during initial startup */
3035 if (ret) {
3036 GST_DEBUG_OBJECT (playbin,
3037 "Taking cached duration because of pending group switch: %d", ret);
3038 GST_SOURCE_GROUP_UNLOCK (group);
3039 GST_PLAY_BIN_UNLOCK (playbin);
3040 return ret;
3041 }
3042 }
3043 GST_SOURCE_GROUP_UNLOCK (group);
3044 }
3045
3046 ret = GST_ELEMENT_CLASS (parent_class)->query (element, query);
3047
3048 if (GST_QUERY_TYPE (query) == GST_QUERY_DURATION)
3049 gst_play_bin_update_cached_duration_from_query (playbin, ret, query);
3050 GST_PLAY_BIN_UNLOCK (playbin);
3051
3052 return ret;
3053 }
3054
3055 /* mime types we are not handling on purpose right now, don't post a
3056 * missing-plugin message for these */
3057 static const gchar *blacklisted_mimes[] = {
3058 NULL
3059 };
3060
3061 static void
gst_play_bin_handle_message(GstBin * bin,GstMessage * msg)3062 gst_play_bin_handle_message (GstBin * bin, GstMessage * msg)
3063 {
3064 GstPlayBin *playbin = GST_PLAY_BIN (bin);
3065 GstSourceGroup *group;
3066 gboolean do_reset_time = FALSE;
3067
3068 if (gst_is_missing_plugin_message (msg)) {
3069 gchar *detail;
3070 guint i;
3071
3072 detail = gst_missing_plugin_message_get_installer_detail (msg);
3073 for (i = 0; detail != NULL && blacklisted_mimes[i] != NULL; ++i) {
3074 if (strstr (detail, "|decoder-") && strstr (detail, blacklisted_mimes[i])) {
3075 GST_LOG_OBJECT (bin, "suppressing message %" GST_PTR_FORMAT, msg);
3076 gst_message_unref (msg);
3077 g_free (detail);
3078 return;
3079 }
3080 }
3081 g_free (detail);
3082 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ASYNC_START ||
3083 GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ASYNC_DONE) {
3084 GstObject *src = GST_OBJECT_CAST (msg->src);
3085
3086 /* Ignore async state changes from the uridecodebin children,
3087 * see bug #602000. */
3088 group = playbin->curr_group;
3089 if (src && group &&
3090 ((group->uridecodebin && src == GST_OBJECT_CAST (group->uridecodebin))
3091 || (group->suburidecodebin
3092 && src == GST_OBJECT_CAST (group->suburidecodebin)))) {
3093 GST_DEBUG_OBJECT (playbin,
3094 "Ignoring async state change of uridecodebin: %s",
3095 GST_OBJECT_NAME (src));
3096 gst_message_unref (msg);
3097 msg = NULL;
3098 }
3099 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_STREAM_START) {
3100 GstSourceGroup *new_group = playbin->curr_group;
3101 GstMessage *buffering_msg = NULL;
3102
3103 GST_SOURCE_GROUP_LOCK (new_group);
3104 new_group->stream_changed_pending = FALSE;
3105 if (new_group->pending_buffering_msg) {
3106 buffering_msg = new_group->pending_buffering_msg;
3107 new_group->pending_buffering_msg = NULL;
3108 }
3109 GST_SOURCE_GROUP_UNLOCK (new_group);
3110
3111 GST_DEBUG_OBJECT (playbin, "Stream start from new group %p", new_group);
3112
3113 if (buffering_msg) {
3114 GST_DEBUG_OBJECT (playbin, "Posting pending buffering message: %"
3115 GST_PTR_FORMAT, buffering_msg);
3116 GST_BIN_CLASS (parent_class)->handle_message (bin, buffering_msg);
3117 }
3118
3119 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_BUFFERING) {
3120 GstSourceGroup *group = playbin->curr_group;
3121 gboolean pending;
3122
3123 /* drop buffering messages from child queues while we are switching
3124 * groups (because the application set a new uri in about-to-finish)
3125 * if the playsink queue still has buffers to play */
3126
3127 GST_SOURCE_GROUP_LOCK (group);
3128 pending = group->stream_changed_pending;
3129
3130 if (pending) {
3131 GST_DEBUG_OBJECT (playbin, "Storing buffering message from pending group "
3132 "%p %" GST_PTR_FORMAT, group, msg);
3133 gst_message_replace (&group->pending_buffering_msg, msg);
3134 gst_message_unref (msg);
3135 msg = NULL;
3136 }
3137 GST_SOURCE_GROUP_UNLOCK (group);
3138 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
3139 /* If we get an error of the subtitle uridecodebin transform
3140 * them into warnings and disable the subtitles */
3141 group = playbin->curr_group;
3142 if (group && group->suburidecodebin) {
3143 if (G_UNLIKELY (gst_object_has_as_ancestor (msg->src, GST_OBJECT_CAST
3144 (group->suburidecodebin)))) {
3145 GError *err;
3146 gchar *debug = NULL;
3147 GstMessage *new_msg;
3148 GstIterator *it;
3149 gboolean done = FALSE;
3150 GValue item = { 0, };
3151
3152 gst_message_parse_error (msg, &err, &debug);
3153 new_msg = gst_message_new_warning (msg->src, err, debug);
3154
3155 gst_message_unref (msg);
3156 g_error_free (err);
3157 g_free (debug);
3158 msg = new_msg;
3159
3160 REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_added_id);
3161 REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_removed_id);
3162 REMOVE_SIGNAL (group->suburidecodebin, group->sub_no_more_pads_id);
3163 REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_continue_id);
3164 REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_query_id);
3165
3166 it = gst_element_iterate_src_pads (group->suburidecodebin);
3167 while (it && !done) {
3168 GstPad *p = NULL;
3169 GstIteratorResult res;
3170
3171 res = gst_iterator_next (it, &item);
3172
3173 switch (res) {
3174 case GST_ITERATOR_DONE:
3175 done = TRUE;
3176 break;
3177 case GST_ITERATOR_OK:
3178 p = g_value_get_object (&item);
3179 pad_removed_cb (NULL, p, group);
3180 g_value_reset (&item);
3181 break;
3182
3183 case GST_ITERATOR_RESYNC:
3184 gst_iterator_resync (it);
3185 break;
3186 case GST_ITERATOR_ERROR:
3187 done = TRUE;
3188 break;
3189 }
3190 }
3191 g_value_unset (&item);
3192 if (it)
3193 gst_iterator_free (it);
3194
3195 gst_object_ref (group->suburidecodebin);
3196 gst_bin_remove (bin, group->suburidecodebin);
3197 gst_element_set_locked_state (group->suburidecodebin, FALSE);
3198 gst_object_unref (group->suburidecodebin);
3199
3200 GST_SOURCE_GROUP_LOCK (group);
3201 g_free (group->suburi);
3202 group->suburi = NULL;
3203 GST_SOURCE_GROUP_UNLOCK (group);
3204
3205 if (group->sub_pending) {
3206 group->sub_pending = FALSE;
3207 no_more_pads_cb (NULL, group);
3208 }
3209 }
3210 } else {
3211 const GstStructure *details = NULL;
3212
3213 gst_message_parse_error_details (msg, &details);
3214 if (details && gst_structure_has_field (details, "redirect-location")) {
3215 gchar *uri = NULL;
3216 const gchar *location =
3217 gst_structure_get_string ((GstStructure *) details,
3218 "redirect-location");
3219
3220 if (gst_uri_is_valid (location)) {
3221 uri = g_strdup (location);
3222 } else {
3223 uri = gst_uri_join_strings (group->uri, location);
3224 }
3225
3226 if (g_strcmp0 (uri, group->uri)) {
3227 GST_PLAY_BIN_LOCK (playbin);
3228 if (playbin->next_group && playbin->next_group->valid) {
3229 GST_DEBUG_OBJECT (playbin,
3230 "User already setup next uri %s, using it",
3231 playbin->next_group->uri);
3232 } else {
3233 GST_DEBUG_OBJECT (playbin,
3234 "Using newly configured redirect URI: %s", uri);
3235 gst_play_bin_set_uri (playbin, uri);
3236 }
3237 GST_PLAY_BIN_UNLOCK (playbin);
3238
3239 setup_next_source (playbin, GST_STATE_PAUSED);
3240 gst_message_unref (msg);
3241 msg = NULL;
3242 }
3243
3244 g_free (uri);
3245 }
3246 }
3247 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_NEED_CONTEXT) {
3248 const gchar *context_type;
3249 GList *iter;
3250
3251 gst_message_parse_context_type (msg, &context_type);
3252 GST_OBJECT_LOCK (playbin);
3253 for (iter = playbin->contexts; iter; iter = g_list_next (iter)) {
3254 GstContext *tmp = iter->data;
3255 const gchar *tmp_type = gst_context_get_context_type (tmp);
3256
3257 if (strcmp (context_type, tmp_type) == 0) {
3258 gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), tmp);
3259 break;
3260 }
3261 }
3262 GST_OBJECT_UNLOCK (playbin);
3263
3264 /* don't need to post upward this if it's answered by us */
3265 if (iter) {
3266 gst_message_unref (msg);
3267 msg = NULL;
3268 }
3269 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_HAVE_CONTEXT) {
3270 GstContext *context;
3271
3272 gst_message_parse_have_context (msg, &context);
3273 gst_play_bin_update_context (playbin, context);
3274 gst_context_unref (context);
3275 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_RESET_TIME) {
3276 if (playbin->is_live && GST_STATE_TARGET (playbin) == GST_STATE_PLAYING) {
3277 do_reset_time = TRUE;
3278 }
3279 }
3280
3281 if (msg)
3282 GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
3283
3284 if (do_reset_time) {
3285 /* If we are live, sample a new base_time immediately */
3286 gst_element_change_state (GST_ELEMENT (playbin),
3287 GST_STATE_CHANGE_PAUSED_TO_PLAYING);
3288 }
3289 }
3290
3291 static void
gst_play_bin_deep_element_added(GstBin * playbin,GstBin * sub_bin,GstElement * child)3292 gst_play_bin_deep_element_added (GstBin * playbin, GstBin * sub_bin,
3293 GstElement * child)
3294 {
3295 GST_LOG_OBJECT (playbin, "element %" GST_PTR_FORMAT " was added to "
3296 "%" GST_PTR_FORMAT, child, sub_bin);
3297
3298 g_signal_emit (playbin, gst_play_bin_signals[SIGNAL_ELEMENT_SETUP], 0, child);
3299
3300 GST_BIN_CLASS (parent_class)->deep_element_added (playbin, sub_bin, child);
3301 }
3302
3303 static void
combiner_active_pad_changed(GObject * combiner,GParamSpec * pspec,GstPlayBin * playbin)3304 combiner_active_pad_changed (GObject * combiner, GParamSpec * pspec,
3305 GstPlayBin * playbin)
3306 {
3307 const gchar *property;
3308 GstSourceGroup *group;
3309 GstSourceCombine *combine = NULL;
3310 int i;
3311
3312 GST_PLAY_BIN_LOCK (playbin);
3313 group = get_group (playbin);
3314
3315 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3316 if (combiner == G_OBJECT (group->combiner[i].combiner)) {
3317 combine = &group->combiner[i];
3318 }
3319 }
3320
3321 /* We got a pad-change after our group got switched out; no need to notify */
3322 if (!combine) {
3323 GST_PLAY_BIN_UNLOCK (playbin);
3324 return;
3325 }
3326
3327 switch (combine->type) {
3328 case GST_PLAY_SINK_TYPE_VIDEO:
3329 property = "current-video";
3330 playbin->current_video = get_current_stream_number (playbin,
3331 combine, group->video_channels);
3332
3333 if (playbin->video_pending_flush_finish) {
3334 playbin->video_pending_flush_finish = FALSE;
3335 GST_PLAY_BIN_UNLOCK (playbin);
3336 gst_play_bin_send_custom_event (GST_OBJECT (combiner),
3337 "playsink-custom-video-flush-finish");
3338 goto notify;
3339 }
3340 break;
3341 case GST_PLAY_SINK_TYPE_AUDIO:
3342 property = "current-audio";
3343 playbin->current_audio = get_current_stream_number (playbin,
3344 combine, group->audio_channels);
3345
3346 if (playbin->audio_pending_flush_finish) {
3347 playbin->audio_pending_flush_finish = FALSE;
3348 GST_PLAY_BIN_UNLOCK (playbin);
3349 gst_play_bin_send_custom_event (GST_OBJECT (combiner),
3350 "playsink-custom-audio-flush-finish");
3351 goto notify;
3352 }
3353 break;
3354 case GST_PLAY_SINK_TYPE_TEXT:
3355 property = "current-text";
3356 playbin->current_text = get_current_stream_number (playbin,
3357 combine, group->text_channels);
3358
3359 if (playbin->text_pending_flush_finish) {
3360 playbin->text_pending_flush_finish = FALSE;
3361 GST_PLAY_BIN_UNLOCK (playbin);
3362 gst_play_bin_send_custom_event (GST_OBJECT (combiner),
3363 "playsink-custom-subtitle-flush-finish");
3364 goto notify;
3365 }
3366 break;
3367 default:
3368 property = NULL;
3369 }
3370 GST_PLAY_BIN_UNLOCK (playbin);
3371
3372 notify:
3373 if (property)
3374 g_object_notify (G_OBJECT (playbin), property);
3375 }
3376
3377 static GstCaps *
update_video_multiview_caps(GstPlayBin * playbin,GstCaps * caps)3378 update_video_multiview_caps (GstPlayBin * playbin, GstCaps * caps)
3379 {
3380 GstVideoMultiviewMode mv_mode;
3381 GstVideoMultiviewMode cur_mv_mode;
3382 guint mv_flags, cur_mv_flags;
3383 GstStructure *s;
3384 const gchar *mview_mode_str;
3385 GstCaps *out_caps;
3386
3387 GST_OBJECT_LOCK (playbin);
3388 mv_mode = (GstVideoMultiviewMode) playbin->multiview_mode;
3389 mv_flags = playbin->multiview_flags;
3390 GST_OBJECT_UNLOCK (playbin);
3391
3392 if (mv_mode == GST_VIDEO_MULTIVIEW_MODE_NONE)
3393 return NULL;
3394
3395 cur_mv_mode = GST_VIDEO_MULTIVIEW_MODE_NONE;
3396 cur_mv_flags = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
3397
3398 s = gst_caps_get_structure (caps, 0);
3399
3400 gst_structure_get_flagset (s, "multiview-flags", &cur_mv_flags, NULL);
3401 if ((mview_mode_str = gst_structure_get_string (s, "multiview-mode")))
3402 cur_mv_mode = gst_video_multiview_mode_from_caps_string (mview_mode_str);
3403
3404 /* We can't override an existing annotated multiview mode, except
3405 * maybe (in the future) we could change some flags. */
3406 if ((gint) cur_mv_mode > GST_VIDEO_MULTIVIEW_MAX_FRAME_PACKING) {
3407 GST_INFO_OBJECT (playbin, "Cannot override existing multiview mode");
3408 return NULL;
3409 }
3410
3411 mview_mode_str = gst_video_multiview_mode_to_caps_string (mv_mode);
3412 g_assert (mview_mode_str != NULL);
3413 out_caps = gst_caps_copy (caps);
3414 s = gst_caps_get_structure (out_caps, 0);
3415
3416 gst_structure_set (s, "multiview-mode", G_TYPE_STRING, mview_mode_str,
3417 "multiview-flags", GST_TYPE_VIDEO_MULTIVIEW_FLAGSET, mv_flags,
3418 GST_FLAG_SET_MASK_EXACT, NULL);
3419
3420 return out_caps;
3421 }
3422
3423 static GstPadProbeReturn
_uridecodebin_event_probe(GstPad * pad,GstPadProbeInfo * info,gpointer udata)3424 _uridecodebin_event_probe (GstPad * pad, GstPadProbeInfo * info, gpointer udata)
3425 {
3426 GstPadProbeReturn ret = GST_PAD_PROBE_OK;
3427 GstSourceGroup *group = udata;
3428 GstEvent *event = GST_PAD_PROBE_INFO_DATA (info);
3429 gboolean suburidecodebin = (GST_PAD_PARENT (pad) == group->suburidecodebin);
3430
3431 if (suburidecodebin) {
3432 /* Drop flushes that we caused from the suburidecodebin */
3433 switch (GST_EVENT_TYPE (event)) {
3434 case GST_EVENT_FLUSH_START:
3435 case GST_EVENT_FLUSH_STOP:
3436 {
3437 guint32 seqnum = gst_event_get_seqnum (event);
3438 GSList *item = g_slist_find (group->suburi_flushes_to_drop,
3439 GUINT_TO_POINTER (seqnum));
3440 if (item) {
3441 if (GST_EVENT_TYPE (event) == GST_EVENT_FLUSH_STOP) {
3442 group->suburi_flushes_to_drop =
3443 g_slist_delete_link (group->suburi_flushes_to_drop, item);
3444 }
3445 }
3446 }
3447 default:
3448 break;
3449 }
3450 }
3451
3452 switch (GST_EVENT_TYPE (event)) {
3453 case GST_EVENT_STREAM_START:{
3454 guint group_id;
3455
3456 GST_SOURCE_GROUP_LOCK (group);
3457 if (gst_event_parse_group_id (event, &group_id)) {
3458 if (group->have_group_id) {
3459 if (group->group_id != group_id) {
3460 event = gst_event_copy (event);
3461 gst_event_set_group_id (event, group->group_id);
3462 gst_event_replace ((GstEvent **) & info->data, event);
3463 gst_event_unref (event);
3464 }
3465 } else {
3466 group->group_id = group_id;
3467 group->have_group_id = TRUE;
3468 }
3469 } else {
3470 GST_FIXME_OBJECT (pad,
3471 "Consider implementing group-id handling on stream-start event");
3472
3473 if (!group->have_group_id) {
3474 group->group_id = gst_util_group_id_next ();
3475 group->have_group_id = TRUE;
3476 }
3477
3478 event = gst_event_copy (event);
3479 gst_event_set_group_id (event, group->group_id);
3480 gst_event_replace ((GstEvent **) & info->data, event);
3481 gst_event_unref (event);
3482 }
3483 GST_SOURCE_GROUP_UNLOCK (group);
3484 break;
3485 }
3486 case GST_EVENT_CAPS:{
3487 GstCaps *caps = NULL;
3488 const GstStructure *s;
3489 const gchar *name;
3490
3491 gst_event_parse_caps (event, &caps);
3492 /* If video caps, check if we should override multiview flags */
3493 s = gst_caps_get_structure (caps, 0);
3494 name = gst_structure_get_name (s);
3495 if (g_str_has_prefix (name, "video/")) {
3496 caps = update_video_multiview_caps (group->playbin, caps);
3497 if (caps) {
3498 gst_event_unref (event);
3499 event = gst_event_new_caps (caps);
3500 GST_PAD_PROBE_INFO_DATA (info) = event;
3501 gst_caps_unref (caps);
3502 }
3503 }
3504 break;
3505 }
3506 default:
3507 break;
3508 }
3509
3510 return ret;
3511 }
3512
3513 /* helper function to lookup stuff in lists */
3514 static gboolean
array_has_value(const gchar * values[],const gchar * value,gboolean exact)3515 array_has_value (const gchar * values[], const gchar * value, gboolean exact)
3516 {
3517 gint i;
3518
3519 for (i = 0; values[i]; i++) {
3520 if (exact && !strcmp (value, values[i]))
3521 return TRUE;
3522 if (!exact && g_str_has_prefix (value, values[i]))
3523 return TRUE;
3524 }
3525 return FALSE;
3526 }
3527
3528 typedef struct
3529 {
3530 GstPlayBin *playbin;
3531 gint stream_id;
3532 GstPlaySinkType type;
3533 } NotifyTagsData;
3534
3535 static void
notify_tags_cb(GObject * object,GParamSpec * pspec,gpointer user_data)3536 notify_tags_cb (GObject * object, GParamSpec * pspec, gpointer user_data)
3537 {
3538 NotifyTagsData *ntdata = (NotifyTagsData *) user_data;
3539 gint signal;
3540
3541 GST_DEBUG_OBJECT (ntdata->playbin, "Tags on pad %" GST_PTR_FORMAT
3542 " with stream id %d and type %d have changed",
3543 object, ntdata->stream_id, ntdata->type);
3544
3545 switch (ntdata->type) {
3546 case GST_PLAY_SINK_TYPE_VIDEO:
3547 signal = SIGNAL_VIDEO_TAGS_CHANGED;
3548 break;
3549 case GST_PLAY_SINK_TYPE_AUDIO:
3550 signal = SIGNAL_AUDIO_TAGS_CHANGED;
3551 break;
3552 case GST_PLAY_SINK_TYPE_TEXT:
3553 signal = SIGNAL_TEXT_TAGS_CHANGED;
3554 break;
3555 default:
3556 signal = -1;
3557 break;
3558 }
3559
3560 if (signal >= 0)
3561 g_signal_emit (G_OBJECT (ntdata->playbin), gst_play_bin_signals[signal], 0,
3562 ntdata->stream_id);
3563 }
3564
3565 /* this function is called when a new pad is added to decodebin. We check the
3566 * type of the pad and add it to the combiner element of the group.
3567 */
3568 static void
pad_added_cb(GstElement * decodebin,GstPad * pad,GstSourceGroup * group)3569 pad_added_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
3570 {
3571 GstPlayBin *playbin;
3572 GstCaps *caps;
3573 const GstStructure *s;
3574 const gchar *name;
3575 GstPad *sinkpad;
3576 GstPadLinkReturn res;
3577 GstSourceCombine *combine = NULL;
3578 gint i, pass;
3579 gboolean changed = FALSE;
3580 GstElement *custom_combiner = NULL;
3581 gulong group_id_probe_handler;
3582
3583 playbin = group->playbin;
3584
3585 GST_PLAY_BIN_SHUTDOWN_LOCK (playbin, shutdown);
3586
3587 caps = gst_pad_get_current_caps (pad);
3588 if (!caps)
3589 caps = gst_pad_query_caps (pad, NULL);
3590 s = gst_caps_get_structure (caps, 0);
3591 name = gst_structure_get_name (s);
3592
3593 GST_DEBUG_OBJECT (playbin,
3594 "pad %s:%s with caps %" GST_PTR_FORMAT " added in group %p",
3595 GST_DEBUG_PAD_NAME (pad), caps, group);
3596
3597 /* major type of the pad, this determines the combiner to use,
3598 try exact match first */
3599 for (pass = 0; !combine && pass < 2; pass++) {
3600 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3601 if (array_has_value (group->combiner[i].media_list, name, pass == 0)) {
3602 combine = &group->combiner[i];
3603 break;
3604 } else if (group->combiner[i].get_media_caps) {
3605 GstCaps *media_caps = group->combiner[i].get_media_caps ();
3606
3607 if (media_caps && gst_caps_can_intersect (media_caps, caps)) {
3608 combine = &group->combiner[i];
3609 gst_caps_unref (media_caps);
3610 break;
3611 }
3612 gst_caps_unref (media_caps);
3613 }
3614 }
3615 /* get custom stream combiner if there is one */
3616 if (combine) {
3617 if (i == PLAYBIN_STREAM_AUDIO) {
3618 custom_combiner = playbin->audio_stream_combiner;
3619 } else if (i == PLAYBIN_STREAM_TEXT) {
3620 custom_combiner = playbin->text_stream_combiner;
3621 } else if (i == PLAYBIN_STREAM_VIDEO) {
3622 custom_combiner = playbin->video_stream_combiner;
3623 }
3624 }
3625 }
3626 /* no combiner found for the media type, don't bother linking it to a
3627 * combiner. This will leave the pad unlinked and thus ignored. */
3628 if (combine == NULL) {
3629 GST_PLAY_BIN_SHUTDOWN_UNLOCK (playbin);
3630 goto unknown_type;
3631 }
3632
3633 GST_SOURCE_GROUP_LOCK (group);
3634 if (combine->combiner == NULL && playbin->have_selector) {
3635 /* no combiner, create one */
3636 GST_DEBUG_OBJECT (playbin, "creating new input selector");
3637 if (custom_combiner)
3638 combine->combiner = custom_combiner;
3639 else
3640 combine->combiner = gst_element_factory_make ("input-selector", NULL);
3641
3642 if (combine->combiner == NULL) {
3643 /* post the missing input-selector message only once */
3644 playbin->have_selector = FALSE;
3645 gst_element_post_message (GST_ELEMENT_CAST (playbin),
3646 gst_missing_element_message_new (GST_ELEMENT_CAST (playbin),
3647 "input-selector"));
3648 GST_ELEMENT_WARNING (playbin, CORE, MISSING_PLUGIN,
3649 (_("Missing element '%s' - check your GStreamer installation."),
3650 "input-selector"), (NULL));
3651 } else {
3652 /* find out which properties the stream combiner supports */
3653 combine->has_active_pad =
3654 g_object_class_find_property (G_OBJECT_GET_CLASS (combine->combiner),
3655 "active-pad") != NULL;
3656
3657 if (!custom_combiner) {
3658 /* sync-mode=1, use clock */
3659 if (combine->type == GST_PLAY_SINK_TYPE_TEXT)
3660 g_object_set (combine->combiner, "sync-streams", TRUE,
3661 "sync-mode", 1, "cache-buffers", TRUE, NULL);
3662 else
3663 g_object_set (combine->combiner, "sync-streams", TRUE, NULL);
3664 }
3665
3666 if (combine->has_active_pad)
3667 g_signal_connect (combine->combiner, "notify::active-pad",
3668 G_CALLBACK (combiner_active_pad_changed), playbin);
3669
3670 GST_DEBUG_OBJECT (playbin, "adding new stream combiner %p",
3671 combine->combiner);
3672 gst_element_set_state (combine->combiner, GST_STATE_PAUSED);
3673 gst_bin_add (GST_BIN_CAST (playbin), combine->combiner);
3674 }
3675 }
3676
3677 GST_PLAY_BIN_SHUTDOWN_UNLOCK (playbin);
3678
3679 if (combine->srcpad == NULL) {
3680 if (combine->combiner) {
3681 /* save source pad of the combiner */
3682 combine->srcpad = gst_element_get_static_pad (combine->combiner, "src");
3683 } else {
3684 /* no combiner, use the pad as the source pad then */
3685 combine->srcpad = gst_object_ref (pad);
3686 }
3687
3688 /* block the combiner srcpad. It's possible that multiple decodebins start
3689 * pushing data into the combiners before we have a chance to collect all
3690 * streams and connect the sinks, resulting in not-linked errors. After we
3691 * configured the sinks we will unblock them all. */
3692 GST_DEBUG_OBJECT (playbin, "blocking %" GST_PTR_FORMAT, combine->srcpad);
3693 combine->block_id =
3694 gst_pad_add_probe (combine->srcpad, GST_PAD_PROBE_TYPE_BLOCK_DOWNSTREAM,
3695 block_serialized_data_cb, NULL, NULL);
3696 }
3697
3698 /* get sinkpad for the new stream */
3699 if (combine->combiner) {
3700 if ((sinkpad =
3701 gst_element_request_pad_simple (combine->combiner, "sink_%u"))) {
3702
3703 GST_DEBUG_OBJECT (playbin, "got pad %s:%s from combiner",
3704 GST_DEBUG_PAD_NAME (sinkpad));
3705
3706 /* find out which properties the sink pad supports */
3707 combine->has_always_ok =
3708 g_object_class_find_property (G_OBJECT_GET_CLASS (sinkpad),
3709 "always-ok") != NULL;
3710 combine->has_tags =
3711 g_object_class_find_property (G_OBJECT_GET_CLASS (sinkpad),
3712 "tags") != NULL;
3713
3714 /* store the combiner for the pad */
3715 g_object_set_data (G_OBJECT (sinkpad), "playbin.combine", combine);
3716
3717 if (combine->has_tags) {
3718 gulong notify_tags_handler = 0;
3719 NotifyTagsData *ntdata;
3720
3721 /* connect to the notify::tags signal for our
3722 * own *-tags-changed signals
3723 */
3724 ntdata = g_new0 (NotifyTagsData, 1);
3725 ntdata->playbin = playbin;
3726 ntdata->stream_id = combine->channels->len;
3727 ntdata->type = combine->type;
3728
3729 notify_tags_handler =
3730 g_signal_connect_data (G_OBJECT (sinkpad), "notify::tags",
3731 G_CALLBACK (notify_tags_cb), ntdata, (GClosureNotify) g_free,
3732 (GConnectFlags) 0);
3733 g_object_set_data (G_OBJECT (sinkpad), "playbin.notify_tags_handler",
3734 ULONG_TO_POINTER (notify_tags_handler));
3735 }
3736
3737 /* store the pad in the array */
3738 GST_DEBUG_OBJECT (playbin, "pad %p added to array", sinkpad);
3739 g_ptr_array_add (combine->channels, sinkpad);
3740
3741 res = gst_pad_link (pad, sinkpad);
3742 if (GST_PAD_LINK_FAILED (res))
3743 goto link_failed;
3744
3745 /* store combiner pad so we can release it */
3746 g_object_set_data (G_OBJECT (pad), "playbin.sinkpad", sinkpad);
3747
3748 changed = TRUE;
3749 GST_DEBUG_OBJECT (playbin, "linked pad %s:%s to combiner %p",
3750 GST_DEBUG_PAD_NAME (pad), combine->combiner);
3751 } else {
3752 goto request_pad_failed;
3753 }
3754 } else {
3755 /* no combiner, don't configure anything, we'll link the new pad directly to
3756 * the sink. */
3757 changed = FALSE;
3758 sinkpad = NULL;
3759
3760 /* store the combiner for the pad */
3761 g_object_set_data (G_OBJECT (pad), "playbin.combine", combine);
3762 }
3763 GST_SOURCE_GROUP_UNLOCK (group);
3764
3765 group_id_probe_handler =
3766 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
3767 _uridecodebin_event_probe, group, NULL);
3768 g_object_set_data (G_OBJECT (pad), "playbin.event_probe_id",
3769 ULONG_TO_POINTER (group_id_probe_handler));
3770
3771 if (changed) {
3772 int signal;
3773
3774 switch (combine->type) {
3775 case GST_PLAY_SINK_TYPE_VIDEO:
3776 signal = SIGNAL_VIDEO_CHANGED;
3777 break;
3778 case GST_PLAY_SINK_TYPE_AUDIO:
3779 signal = SIGNAL_AUDIO_CHANGED;
3780 break;
3781 case GST_PLAY_SINK_TYPE_TEXT:
3782 signal = SIGNAL_TEXT_CHANGED;
3783 break;
3784 default:
3785 signal = -1;
3786 }
3787
3788 if (signal >= 0) {
3789 /* we want to return NOT_LINKED for unselected pads but only for pads
3790 * from the normal uridecodebin. This makes sure that subtitle streams
3791 * are not raced past audio/video from decodebin's multiqueue.
3792 * For pads from suburidecodebin OK should always be returned, otherwise
3793 * it will most likely stop. */
3794 if (combine->has_always_ok) {
3795 gboolean always_ok = (decodebin == group->suburidecodebin);
3796 g_object_set (sinkpad, "always-ok", always_ok, NULL);
3797 }
3798 g_signal_emit (G_OBJECT (playbin), gst_play_bin_signals[signal], 0, NULL);
3799 }
3800 }
3801
3802 done:
3803 gst_caps_unref (caps);
3804 return;
3805
3806 /* ERRORS */
3807 unknown_type:
3808 {
3809 GST_ERROR_OBJECT (playbin, "unknown type %s for pad %s:%s",
3810 name, GST_DEBUG_PAD_NAME (pad));
3811 goto done;
3812 }
3813 link_failed:
3814 {
3815 GST_ERROR_OBJECT (playbin,
3816 "failed to link pad %s:%s to combiner, reason %s (%d)",
3817 GST_DEBUG_PAD_NAME (pad), gst_pad_link_get_name (res), res);
3818 GST_SOURCE_GROUP_UNLOCK (group);
3819 goto done;
3820 }
3821 request_pad_failed:
3822 GST_ELEMENT_ERROR (playbin, CORE, PAD,
3823 ("Internal playbin error."),
3824 ("Failed to get request pad from combiner %p.", combine->combiner));
3825 GST_SOURCE_GROUP_UNLOCK (group);
3826 goto done;
3827 shutdown:
3828 {
3829 GST_DEBUG ("ignoring, we are shutting down. Pad will be left unlinked");
3830 /* not going to done as we didn't request the caps */
3831 return;
3832 }
3833 }
3834
3835 /* called when a pad is removed from the uridecodebin. We unlink the pad from
3836 * the combiner. This will make the combiner select a new pad. */
3837 static void
pad_removed_cb(GstElement * decodebin,GstPad * pad,GstSourceGroup * group)3838 pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
3839 {
3840 GstPlayBin *playbin;
3841 GstPad *peer;
3842 GstElement *combiner;
3843 GstSourceCombine *combine;
3844 int signal = -1;
3845 gulong group_id_probe_handler;
3846
3847 playbin = group->playbin;
3848
3849 GST_DEBUG_OBJECT (playbin,
3850 "pad %s:%s removed from group %p", GST_DEBUG_PAD_NAME (pad), group);
3851
3852 GST_SOURCE_GROUP_LOCK (group);
3853
3854 if ((group_id_probe_handler =
3855 POINTER_TO_ULONG (g_object_get_data (G_OBJECT (pad),
3856 "playbin.event_probe_id")))) {
3857 gst_pad_remove_probe (pad, group_id_probe_handler);
3858 g_object_set_data (G_OBJECT (pad), "playbin.event_probe_id", NULL);
3859 }
3860
3861 if ((combine = g_object_get_data (G_OBJECT (pad), "playbin.combine"))) {
3862 g_assert (combine->combiner == NULL);
3863 g_assert (combine->srcpad == pad);
3864 source_combine_remove_pads (playbin, combine);
3865 goto exit;
3866 }
3867
3868 /* get the combiner sinkpad */
3869 if (!(peer = g_object_get_data (G_OBJECT (pad), "playbin.sinkpad")))
3870 goto not_linked;
3871
3872 /* unlink the pad now (can fail, the pad is unlinked before it's removed) */
3873 gst_pad_unlink (pad, peer);
3874
3875 /* get combiner */
3876 combiner = GST_ELEMENT_CAST (gst_pad_get_parent (peer));
3877 g_assert (combiner != NULL);
3878
3879 if ((combine = g_object_get_data (G_OBJECT (peer), "playbin.combine"))) {
3880 if (combine->has_tags) {
3881 gulong notify_tags_handler;
3882
3883 notify_tags_handler =
3884 POINTER_TO_ULONG (g_object_get_data (G_OBJECT (peer),
3885 "playbin.notify_tags_handler"));
3886 if (notify_tags_handler != 0)
3887 g_signal_handler_disconnect (G_OBJECT (peer), notify_tags_handler);
3888 g_object_set_data (G_OBJECT (peer), "playbin.notify_tags_handler", NULL);
3889 }
3890
3891 /* remove the pad from the array */
3892 g_ptr_array_remove (combine->channels, peer);
3893 GST_DEBUG_OBJECT (playbin, "pad %p removed from array", peer);
3894
3895 /* get the correct type-changed signal */
3896 switch (combine->type) {
3897 case GST_PLAY_SINK_TYPE_VIDEO:
3898 signal = SIGNAL_VIDEO_CHANGED;
3899 break;
3900 case GST_PLAY_SINK_TYPE_AUDIO:
3901 signal = SIGNAL_AUDIO_CHANGED;
3902 break;
3903 case GST_PLAY_SINK_TYPE_TEXT:
3904 signal = SIGNAL_TEXT_CHANGED;
3905 break;
3906 default:
3907 signal = -1;
3908 }
3909
3910 if (!combine->channels->len && combine->combiner) {
3911 GST_DEBUG_OBJECT (playbin, "all combiner sinkpads removed");
3912 GST_DEBUG_OBJECT (playbin, "removing combiner %p", combine->combiner);
3913 source_combine_remove_pads (playbin, combine);
3914 gst_element_set_state (combine->combiner, GST_STATE_NULL);
3915 gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner);
3916 combine->combiner = NULL;
3917 }
3918 }
3919
3920 /* release the pad to the combiner, this will make the combiner choose a new
3921 * pad. */
3922 gst_element_release_request_pad (combiner, peer);
3923 gst_object_unref (peer);
3924
3925 gst_object_unref (combiner);
3926 exit:
3927 GST_SOURCE_GROUP_UNLOCK (group);
3928
3929 if (signal >= 0)
3930 g_signal_emit (G_OBJECT (playbin), gst_play_bin_signals[signal], 0, NULL);
3931
3932 return;
3933
3934 /* ERRORS */
3935 not_linked:
3936 {
3937 GST_DEBUG_OBJECT (playbin, "pad not linked");
3938 goto exit;
3939 }
3940 }
3941
3942 /* we get called when all pads are available and we must connect the sinks to
3943 * them.
3944 * The main purpose of the code is to see if we have video/audio and subtitles
3945 * and pick the right pipelines to display them.
3946 *
3947 * The combiners installed on the group tell us about the presence of
3948 * audio/video and subtitle streams. This allows us to see if we need
3949 * visualisation, video or/and audio.
3950 */
3951 static void
no_more_pads_cb(GstElement * decodebin,GstSourceGroup * group)3952 no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group)
3953 {
3954 GstPlayBin *playbin;
3955 GstPadLinkReturn res;
3956 gint i;
3957 gboolean configure;
3958
3959 playbin = group->playbin;
3960
3961 GST_DEBUG_OBJECT (playbin, "no more pads in group %p", group);
3962
3963 GST_PLAY_BIN_SHUTDOWN_LOCK (playbin, shutdown);
3964
3965 GST_SOURCE_GROUP_LOCK (group);
3966 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
3967 GstSourceCombine *combine = &group->combiner[i];
3968
3969 /* check if the specific media type was detected and thus has a combiner
3970 * created for it. If there is the media type, get a sinkpad from the sink
3971 * and link it. We only do this if we have not yet requested the sinkpad
3972 * before. */
3973 if (combine->srcpad && combine->sinkpad == NULL) {
3974 GST_DEBUG_OBJECT (playbin, "requesting new sink pad %d", combine->type);
3975 combine->sinkpad =
3976 gst_play_sink_request_pad (playbin->playsink, combine->type);
3977 gst_object_ref (combine->sinkpad);
3978 } else if (combine->srcpad && combine->sinkpad) {
3979 GST_DEBUG_OBJECT (playbin, "refreshing new sink pad %d", combine->type);
3980 gst_play_sink_refresh_pad (playbin->playsink, combine->sinkpad,
3981 combine->type);
3982 } else if (combine->sinkpad && combine->srcpad == NULL) {
3983 GST_DEBUG_OBJECT (playbin, "releasing sink pad %d", combine->type);
3984 gst_play_sink_release_pad (playbin->playsink, combine->sinkpad);
3985 gst_object_unref (combine->sinkpad);
3986 combine->sinkpad = NULL;
3987 }
3988 if (combine->sinkpad && combine->srcpad &&
3989 !gst_pad_is_linked (combine->srcpad)) {
3990 res = gst_pad_link (combine->srcpad, combine->sinkpad);
3991 GST_DEBUG_OBJECT (playbin, "linked type %s, result: %d",
3992 combine->media_list[0], res);
3993 if (res != GST_PAD_LINK_OK) {
3994 GST_ELEMENT_ERROR (playbin, CORE, PAD,
3995 ("Internal playbin error."),
3996 ("Failed to link combiner to sink. Error %d", res));
3997 }
3998 }
3999 }
4000 GST_DEBUG_OBJECT (playbin, "pending %d > %d", group->pending,
4001 group->pending - 1);
4002
4003 if (group->pending > 0)
4004 group->pending--;
4005
4006 if (group->suburidecodebin == decodebin)
4007 group->sub_pending = FALSE;
4008
4009 if (group->pending == 0) {
4010 /* we are the last group to complete, we will configure the output and then
4011 * signal the other waiters. */
4012 GST_LOG_OBJECT (playbin, "last group complete");
4013 configure = TRUE;
4014 } else {
4015 GST_LOG_OBJECT (playbin, "have more pending groups");
4016 configure = FALSE;
4017 }
4018 GST_SOURCE_GROUP_UNLOCK (group);
4019
4020 if (configure) {
4021 /* if we have custom sinks, configure them now */
4022 GST_SOURCE_GROUP_LOCK (group);
4023
4024 if (group->audio_sink) {
4025 GST_INFO_OBJECT (playbin, "setting custom audio sink %" GST_PTR_FORMAT,
4026 group->audio_sink);
4027 gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO,
4028 group->audio_sink);
4029 }
4030
4031 if (group->video_sink) {
4032 GST_INFO_OBJECT (playbin, "setting custom video sink %" GST_PTR_FORMAT,
4033 group->video_sink);
4034 gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO,
4035 group->video_sink);
4036 }
4037
4038 if (group->text_sink) {
4039 GST_INFO_OBJECT (playbin, "setting custom text sink %" GST_PTR_FORMAT,
4040 group->text_sink);
4041 gst_play_sink_set_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT,
4042 group->text_sink);
4043 }
4044
4045 GST_SOURCE_GROUP_UNLOCK (group);
4046
4047 /* signal the other decodebins that they can continue now. */
4048 GST_SOURCE_GROUP_LOCK (group);
4049 /* unblock all combiners */
4050 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
4051 GstSourceCombine *combine = &group->combiner[i];
4052
4053 if (combine->srcpad) {
4054 GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT,
4055 combine->srcpad);
4056 if (combine->block_id) {
4057 gst_pad_remove_probe (combine->srcpad, combine->block_id);
4058 combine->block_id = 0;
4059 }
4060 }
4061 }
4062 GST_SOURCE_GROUP_UNLOCK (group);
4063 gst_play_sink_reconfigure (playbin->playsink);
4064 }
4065
4066 GST_PLAY_BIN_SHUTDOWN_UNLOCK (playbin);
4067
4068 if (configure) {
4069 do_async_done (playbin);
4070 }
4071
4072 return;
4073
4074 shutdown:
4075 {
4076 GST_DEBUG ("ignoring, we are shutting down");
4077 /* Request a flushing pad from playsink that we then link to the combiner.
4078 * Then we unblock the combiners so that they stop with a WRONG_STATE
4079 * instead of a NOT_LINKED error.
4080 */
4081 GST_SOURCE_GROUP_LOCK (group);
4082 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
4083 GstSourceCombine *combine = &group->combiner[i];
4084
4085 if (combine->srcpad) {
4086 if (combine->sinkpad == NULL) {
4087 GST_DEBUG_OBJECT (playbin, "requesting new flushing sink pad");
4088 combine->sinkpad =
4089 gst_play_sink_request_pad (playbin->playsink,
4090 GST_PLAY_SINK_TYPE_FLUSHING);
4091 gst_object_ref (combine->sinkpad);
4092 res = gst_pad_link (combine->srcpad, combine->sinkpad);
4093 GST_DEBUG_OBJECT (playbin, "linked flushing, result: %d", res);
4094 }
4095 GST_DEBUG_OBJECT (playbin, "unblocking %" GST_PTR_FORMAT,
4096 combine->srcpad);
4097 if (combine->block_id) {
4098 gst_pad_remove_probe (combine->srcpad, combine->block_id);
4099 combine->block_id = 0;
4100 }
4101 }
4102 }
4103 GST_SOURCE_GROUP_UNLOCK (group);
4104 return;
4105 }
4106 }
4107
4108 static void
drained_cb(GstElement * decodebin,GstSourceGroup * group)4109 drained_cb (GstElement * decodebin, GstSourceGroup * group)
4110 {
4111 GstPlayBin *playbin;
4112
4113 playbin = group->playbin;
4114
4115 GST_DEBUG_OBJECT (playbin, "about to finish in group %p", group);
4116
4117 /* after this call, we should have a next group to activate or we EOS */
4118 g_signal_emit (G_OBJECT (playbin),
4119 gst_play_bin_signals[SIGNAL_ABOUT_TO_FINISH], 0, NULL);
4120
4121 /* now activate the next group. If the app did not set a uri, this will
4122 * fail and we can do EOS */
4123 setup_next_source (playbin, GST_STATE_PAUSED);
4124 }
4125
4126 /* Like gst_element_factory_can_sink_any_caps() but doesn't
4127 * allow ANY caps on the sinkpad template */
4128 static gboolean
_factory_can_sink_caps(GstElementFactory * factory,GstCaps * caps)4129 _factory_can_sink_caps (GstElementFactory * factory, GstCaps * caps)
4130 {
4131 const GList *templs;
4132
4133 templs = gst_element_factory_get_static_pad_templates (factory);
4134
4135 while (templs) {
4136 GstStaticPadTemplate *templ = (GstStaticPadTemplate *) templs->data;
4137
4138 if (templ->direction == GST_PAD_SINK) {
4139 GstCaps *templcaps = gst_static_caps_get (&templ->static_caps);
4140
4141 if (!gst_caps_is_any (templcaps)
4142 && gst_caps_is_subset (caps, templcaps)) {
4143 gst_caps_unref (templcaps);
4144 return TRUE;
4145 }
4146 gst_caps_unref (templcaps);
4147 }
4148 templs = g_list_next (templs);
4149 }
4150
4151 return FALSE;
4152 }
4153
4154 static void
avelements_free(gpointer avelement)4155 avelements_free (gpointer avelement)
4156 {
4157 GstAVElement *elm = (GstAVElement *) avelement;
4158
4159 if (elm->dec)
4160 gst_object_unref (elm->dec);
4161 if (elm->sink)
4162 gst_object_unref (elm->sink);
4163 g_slice_free (GstAVElement, elm);
4164 }
4165
4166 static gint
avelement_compare_decoder(gconstpointer p1,gconstpointer p2,gpointer user_data)4167 avelement_compare_decoder (gconstpointer p1, gconstpointer p2,
4168 gpointer user_data)
4169 {
4170 GstAVElement *v1, *v2;
4171
4172 v1 = (GstAVElement *) p1;
4173 v2 = (GstAVElement *) p2;
4174
4175 return strcmp (GST_OBJECT_NAME (v1->dec), GST_OBJECT_NAME (v2->dec));
4176 }
4177
4178 static gint
avelement_lookup_decoder(gconstpointer p1,gconstpointer p2,gpointer user_data)4179 avelement_lookup_decoder (gconstpointer p1, gconstpointer p2,
4180 gpointer user_data)
4181 {
4182 GstAVElement *v1;
4183 GstElementFactory *f2;
4184
4185 v1 = (GstAVElement *) p1;
4186 f2 = (GstElementFactory *) p2;
4187
4188 return strcmp (GST_OBJECT_NAME (v1->dec), GST_OBJECT_NAME (f2));
4189 }
4190
4191 static gint
avelement_compare(gconstpointer p1,gconstpointer p2)4192 avelement_compare (gconstpointer p1, gconstpointer p2)
4193 {
4194 GstAVElement *v1, *v2;
4195 GstPluginFeature *fd1, *fd2, *fs1, *fs2;
4196 gint64 diff, v1_rank, v2_rank;
4197
4198 v1 = (GstAVElement *) p1;
4199 v2 = (GstAVElement *) p2;
4200
4201 fd1 = (GstPluginFeature *) v1->dec;
4202 fd2 = (GstPluginFeature *) v2->dec;
4203
4204 /* If both have a sink, we also compare their ranks */
4205 if (v1->sink && v2->sink) {
4206 fs1 = (GstPluginFeature *) v1->sink;
4207 fs2 = (GstPluginFeature *) v2->sink;
4208 v1_rank = (gint64) gst_plugin_feature_get_rank (fd1) *
4209 gst_plugin_feature_get_rank (fs1);
4210 v2_rank = (gint64) gst_plugin_feature_get_rank (fd2) *
4211 gst_plugin_feature_get_rank (fs2);
4212 } else {
4213 v1_rank = gst_plugin_feature_get_rank (fd1);
4214 v2_rank = gst_plugin_feature_get_rank (fd2);
4215 fs1 = fs2 = NULL;
4216 }
4217
4218 /* comparison based on the rank */
4219 diff = v2_rank - v1_rank;
4220 if (diff < 0)
4221 return -1;
4222 else if (diff > 0)
4223 return 1;
4224
4225 /* comparison based on number of common caps features */
4226 diff = v2->n_comm_cf - v1->n_comm_cf;
4227 if (diff != 0)
4228 return diff;
4229
4230 if (fs1 && fs2) {
4231 /* comparison based on the name of sink elements */
4232 diff = strcmp (GST_OBJECT_NAME (fs1), GST_OBJECT_NAME (fs2));
4233 if (diff != 0)
4234 return diff;
4235 }
4236
4237 /* comparison based on the name of decoder elements */
4238 return strcmp (GST_OBJECT_NAME (fd1), GST_OBJECT_NAME (fd2));
4239 }
4240
4241 static GSequence *
avelements_create(GstPlayBin * playbin,gboolean isaudioelement)4242 avelements_create (GstPlayBin * playbin, gboolean isaudioelement)
4243 {
4244 GstElementFactory *d_factory, *s_factory;
4245 GList *dec_list, *sink_list, *dl, *sl;
4246 GSequence *ave_seq = NULL;
4247 GstAVElement *ave;
4248 guint n_common_cf = 0;
4249
4250 if (isaudioelement) {
4251 sink_list = gst_element_factory_list_get_elements
4252 (GST_ELEMENT_FACTORY_TYPE_SINK |
4253 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL);
4254 dec_list =
4255 gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_DECODER
4256 | GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO, GST_RANK_MARGINAL);
4257 } else {
4258 sink_list = gst_element_factory_list_get_elements
4259 (GST_ELEMENT_FACTORY_TYPE_SINK |
4260 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4261 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE, GST_RANK_MARGINAL);
4262
4263 dec_list =
4264 gst_element_factory_list_get_elements (GST_ELEMENT_FACTORY_TYPE_DECODER
4265 | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4266 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE, GST_RANK_MARGINAL);
4267 }
4268
4269 /* create a list of audio/video elements. Each element in the list
4270 * is holding an audio/video decoder and an audio/video sink in which
4271 * the decoders srcpad template caps and sink element's sinkpad template
4272 * caps are compatible */
4273 dl = dec_list;
4274 sl = sink_list;
4275
4276 ave_seq = g_sequence_new ((GDestroyNotify) avelements_free);
4277
4278 for (; dl; dl = dl->next) {
4279 d_factory = (GstElementFactory *) dl->data;
4280 for (; sl; sl = sl->next) {
4281 s_factory = (GstElementFactory *) sl->data;
4282
4283 n_common_cf =
4284 gst_playback_utils_get_n_common_capsfeatures (d_factory, s_factory,
4285 gst_play_bin_get_flags (playbin), isaudioelement);
4286 if (n_common_cf < 1)
4287 continue;
4288
4289 ave = g_slice_new (GstAVElement);
4290 ave->dec = gst_object_ref (d_factory);
4291 ave->sink = gst_object_ref (s_factory);
4292 ave->n_comm_cf = n_common_cf;
4293 g_sequence_append (ave_seq, ave);
4294 }
4295 sl = sink_list;
4296 }
4297 g_sequence_sort (ave_seq, (GCompareDataFunc) avelement_compare_decoder, NULL);
4298
4299 gst_plugin_feature_list_free (dec_list);
4300 gst_plugin_feature_list_free (sink_list);
4301
4302 return ave_seq;
4303 }
4304
4305 static gboolean
avelement_iter_is_equal(GSequenceIter * iter,GstElementFactory * factory)4306 avelement_iter_is_equal (GSequenceIter * iter, GstElementFactory * factory)
4307 {
4308 GstAVElement *ave;
4309
4310 if (!iter)
4311 return FALSE;
4312
4313 ave = g_sequence_get (iter);
4314 if (!ave)
4315 return FALSE;
4316
4317 return strcmp (GST_OBJECT_NAME (ave->dec), GST_OBJECT_NAME (factory)) == 0;
4318 }
4319
4320 static GList *
create_decoders_list(GList * factory_list,GSequence * avelements,GstPlayFlags flags)4321 create_decoders_list (GList * factory_list, GSequence * avelements,
4322 GstPlayFlags flags)
4323 {
4324 GList *dec_list = NULL, *tmp;
4325 GList *ave_list = NULL;
4326 GList *ave_free_list = NULL;
4327 GstAVElement *ave, *best_ave;
4328
4329 g_return_val_if_fail (factory_list != NULL, NULL);
4330 g_return_val_if_fail (avelements != NULL, NULL);
4331
4332 for (tmp = factory_list; tmp; tmp = tmp->next) {
4333 GstElementFactory *factory = (GstElementFactory *) tmp->data;
4334
4335 /* if there are parsers or sink elements, add them first */
4336 if (gst_element_factory_list_is_type (factory,
4337 GST_ELEMENT_FACTORY_TYPE_PARSER) ||
4338 gst_element_factory_list_is_type (factory,
4339 GST_ELEMENT_FACTORY_TYPE_SINK)) {
4340 dec_list = g_list_prepend (dec_list, gst_object_ref (factory));
4341 } else if (!(((flags & GST_PLAY_FLAG_FORCE_SW_DECODERS) != 0)
4342 && gst_element_factory_list_is_type (factory,
4343 GST_ELEMENT_FACTORY_TYPE_HARDWARE))) {
4344 GSequenceIter *seq_iter;
4345
4346 seq_iter =
4347 g_sequence_lookup (avelements, factory,
4348 (GCompareDataFunc) avelement_lookup_decoder, NULL);
4349 if (!seq_iter) {
4350 GstAVElement *ave = g_slice_new0 (GstAVElement);
4351
4352 ave->dec = factory;
4353 ave->sink = NULL;
4354 /* There's at least raw */
4355 ave->n_comm_cf = 1;
4356
4357 ave_list = g_list_prepend (ave_list, ave);
4358
4359 /* We need to free these later */
4360 ave_free_list = g_list_prepend (ave_free_list, ave);
4361 continue;
4362 }
4363
4364 /* Go to first iter with that decoder */
4365 do {
4366 GSequenceIter *tmp_seq_iter;
4367
4368 tmp_seq_iter = g_sequence_iter_prev (seq_iter);
4369 if (!avelement_iter_is_equal (tmp_seq_iter, factory))
4370 break;
4371 seq_iter = tmp_seq_iter;
4372 } while (!g_sequence_iter_is_begin (seq_iter));
4373
4374 /* Get the best ranked GstAVElement for that factory */
4375 best_ave = NULL;
4376 while (!g_sequence_iter_is_end (seq_iter)
4377 && avelement_iter_is_equal (seq_iter, factory)) {
4378 ave = g_sequence_get (seq_iter);
4379
4380 if (!best_ave || avelement_compare (ave, best_ave) < 0)
4381 best_ave = ave;
4382
4383 seq_iter = g_sequence_iter_next (seq_iter);
4384 }
4385 ave_list = g_list_prepend (ave_list, best_ave);
4386 }
4387 }
4388
4389 /* Sort all GstAVElements by their relative ranks and insert
4390 * into the decoders list */
4391 ave_list = g_list_sort (ave_list, (GCompareFunc) avelement_compare);
4392 for (tmp = ave_list; tmp; tmp = tmp->next) {
4393 ave = (GstAVElement *) tmp->data;
4394 dec_list = g_list_prepend (dec_list, gst_object_ref (ave->dec));
4395 }
4396 g_list_free (ave_list);
4397 gst_plugin_feature_list_free (factory_list);
4398
4399 for (tmp = ave_free_list; tmp; tmp = tmp->next)
4400 g_slice_free (GstAVElement, tmp->data);
4401 g_list_free (ave_free_list);
4402
4403 dec_list = g_list_reverse (dec_list);
4404
4405 return dec_list;
4406 }
4407
4408 /* Called when we must provide a list of factories to plug to @pad with @caps.
4409 * We first check if we have a sink that can handle the format and if we do, we
4410 * return NULL, to expose the pad. If we have no sink (or the sink does not
4411 * work), we return the list of elements that can connect. */
4412 static GValueArray *
autoplug_factories_cb(GstElement * decodebin,GstPad * pad,GstCaps * caps,GstSourceGroup * group)4413 autoplug_factories_cb (GstElement * decodebin, GstPad * pad,
4414 GstCaps * caps, GstSourceGroup * group)
4415 {
4416 GstPlayBin *playbin;
4417 GList *factory_list, *tmp;
4418 GValueArray *result;
4419 gboolean unref_caps = FALSE;
4420 gboolean isaudiodeclist = FALSE;
4421 gboolean isvideodeclist = FALSE;
4422
4423 if (!caps) {
4424 caps = gst_caps_new_any ();
4425 unref_caps = TRUE;
4426 }
4427
4428 playbin = group->playbin;
4429
4430 GST_DEBUG_OBJECT (playbin, "factories group %p for %s:%s, %" GST_PTR_FORMAT,
4431 group, GST_DEBUG_PAD_NAME (pad), caps);
4432
4433 /* filter out the elements based on the caps. */
4434 g_mutex_lock (&playbin->elements_lock);
4435 gst_play_bin_update_elements_list (playbin);
4436 factory_list =
4437 gst_element_factory_list_filter (playbin->elements, caps, GST_PAD_SINK,
4438 gst_caps_is_fixed (caps));
4439 g_mutex_unlock (&playbin->elements_lock);
4440
4441 GST_DEBUG_OBJECT (playbin, "found factories %p", factory_list);
4442 GST_PLUGIN_FEATURE_LIST_DEBUG (factory_list);
4443
4444 /* check whether the caps are asking for a list of audio/video decoders */
4445 tmp = factory_list;
4446 if (!gst_caps_is_any (caps)) {
4447 for (; tmp; tmp = tmp->next) {
4448 GstElementFactory *factory = (GstElementFactory *) tmp->data;
4449
4450 isvideodeclist = gst_element_factory_list_is_type (factory,
4451 GST_ELEMENT_FACTORY_TYPE_DECODER |
4452 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4453 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE);
4454 isaudiodeclist = gst_element_factory_list_is_type (factory,
4455 GST_ELEMENT_FACTORY_TYPE_DECODER |
4456 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO);
4457
4458 if (isaudiodeclist || isvideodeclist)
4459 break;
4460 }
4461 }
4462
4463 if (isaudiodeclist || isvideodeclist) {
4464 GSequence **ave_list;
4465 GstPlayFlags flags;
4466
4467 if (isaudiodeclist)
4468 ave_list = &playbin->aelements;
4469 else
4470 ave_list = &playbin->velements;
4471
4472 flags = gst_play_bin_get_flags (playbin);
4473
4474 g_mutex_lock (&playbin->elements_lock);
4475 /* sort factory_list based on the GstAVElement list priority */
4476 factory_list = create_decoders_list (factory_list, *ave_list, flags);
4477 g_mutex_unlock (&playbin->elements_lock);
4478 }
4479
4480 /* 2 additional elements for the already set audio/video sinks */
4481 result = g_value_array_new (g_list_length (factory_list) + 2);
4482
4483 /* Check if we already have an audio/video sink and if this is the case
4484 * put it as the first element of the array */
4485 if (group->audio_sink) {
4486 GstElementFactory *factory = gst_element_get_factory (group->audio_sink);
4487
4488 if (factory && _factory_can_sink_caps (factory, caps)) {
4489 GValue val = { 0, };
4490
4491 g_value_init (&val, G_TYPE_OBJECT);
4492 g_value_set_object (&val, factory);
4493 result = g_value_array_append (result, &val);
4494 g_value_unset (&val);
4495 }
4496 }
4497
4498 if (group->video_sink) {
4499 GstElementFactory *factory = gst_element_get_factory (group->video_sink);
4500
4501 if (factory && _factory_can_sink_caps (factory, caps)) {
4502 GValue val = { 0, };
4503
4504 g_value_init (&val, G_TYPE_OBJECT);
4505 g_value_set_object (&val, factory);
4506 result = g_value_array_append (result, &val);
4507 g_value_unset (&val);
4508 }
4509 }
4510
4511 for (tmp = factory_list; tmp; tmp = tmp->next) {
4512 GstElementFactory *factory = GST_ELEMENT_FACTORY_CAST (tmp->data);
4513 GValue val = { 0, };
4514
4515 if (group->audio_sink && gst_element_factory_list_is_type (factory,
4516 GST_ELEMENT_FACTORY_TYPE_SINK |
4517 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
4518 continue;
4519 }
4520 if (group->video_sink && gst_element_factory_list_is_type (factory,
4521 GST_ELEMENT_FACTORY_TYPE_SINK | GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO
4522 | GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
4523 continue;
4524 }
4525
4526 g_value_init (&val, G_TYPE_OBJECT);
4527 g_value_set_object (&val, factory);
4528 g_value_array_append (result, &val);
4529 g_value_unset (&val);
4530 }
4531 gst_plugin_feature_list_free (factory_list);
4532
4533 if (unref_caps)
4534 gst_caps_unref (caps);
4535
4536 return result;
4537 }
4538
4539 static gboolean
gst_play_bin_send_event(GstElement * element,GstEvent * event)4540 gst_play_bin_send_event (GstElement * element, GstEvent * event)
4541 {
4542 GstPlayBin *playbin = GST_PLAY_BIN (element);
4543
4544 /* Send event directly to playsink instead of letting GstBin iterate
4545 * over all sink elements. The latter might send the event multiple times
4546 * in case the SEEK causes a reconfiguration of the pipeline, as can easily
4547 * happen with adaptive streaming demuxers.
4548 *
4549 * What would then happen is that the iterator would be reset, we send the
4550 * event again, and on the second time it will fail in the majority of cases
4551 * because the pipeline is still being reconfigured
4552 */
4553 if (GST_EVENT_IS_UPSTREAM (event)) {
4554 return gst_element_send_event (GST_ELEMENT_CAST (playbin->playsink), event);
4555 }
4556
4557 return GST_ELEMENT_CLASS (parent_class)->send_event (element, event);
4558 }
4559
4560 static void
gst_play_bin_set_context(GstElement * element,GstContext * context)4561 gst_play_bin_set_context (GstElement * element, GstContext * context)
4562 {
4563 GstPlayBin *playbin = GST_PLAY_BIN (element);
4564
4565 /* Proxy contexts to the sinks, they might not be in playsink yet */
4566 GST_PLAY_BIN_LOCK (playbin);
4567 if (playbin->audio_sink)
4568 gst_element_set_context (playbin->audio_sink, context);
4569 if (playbin->video_sink)
4570 gst_element_set_context (playbin->video_sink, context);
4571 if (playbin->text_sink)
4572 gst_element_set_context (playbin->text_sink, context);
4573
4574 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
4575
4576 if (playbin->curr_group->audio_sink)
4577 gst_element_set_context (playbin->curr_group->audio_sink, context);
4578 if (playbin->curr_group->video_sink)
4579 gst_element_set_context (playbin->curr_group->video_sink, context);
4580 if (playbin->curr_group->text_sink)
4581 gst_element_set_context (playbin->curr_group->text_sink, context);
4582
4583 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
4584 GST_PLAY_BIN_UNLOCK (playbin);
4585
4586 GST_ELEMENT_CLASS (parent_class)->set_context (element, context);
4587 }
4588
4589 /* Pass sink messages to the application, e.g. NEED_CONTEXT messages */
4590 static void
gst_play_bin_update_context(GstPlayBin * playbin,GstContext * context)4591 gst_play_bin_update_context (GstPlayBin * playbin, GstContext * context)
4592 {
4593 GList *l;
4594 const gchar *context_type;
4595
4596 GST_OBJECT_LOCK (playbin);
4597 context_type = gst_context_get_context_type (context);
4598 for (l = playbin->contexts; l; l = l->next) {
4599 GstContext *tmp = l->data;
4600 const gchar *tmp_type = gst_context_get_context_type (tmp);
4601
4602 /* Always store newest context but never replace
4603 * a persistent one by a non-persistent one */
4604 if (strcmp (context_type, tmp_type) == 0 &&
4605 (gst_context_is_persistent (context) ||
4606 !gst_context_is_persistent (tmp))) {
4607 gst_context_replace ((GstContext **) & l->data, context);
4608 break;
4609 }
4610 }
4611 /* Not found? Add */
4612 if (l == NULL)
4613 playbin->contexts =
4614 g_list_prepend (playbin->contexts, gst_context_ref (context));
4615 GST_OBJECT_UNLOCK (playbin);
4616 }
4617
4618 static GstBusSyncReply
activate_sink_bus_handler(GstBus * bus,GstMessage * msg,GstPlayBin * playbin)4619 activate_sink_bus_handler (GstBus * bus, GstMessage * msg, GstPlayBin * playbin)
4620 {
4621 if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
4622 /* Only proxy errors from a fixed sink. If that fails we can just error out
4623 * early as stuff will fail later anyway */
4624 if (playbin->audio_sink
4625 && gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
4626 GST_OBJECT_CAST (playbin->audio_sink)))
4627 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4628 else if (playbin->video_sink
4629 && gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
4630 GST_OBJECT_CAST (playbin->video_sink)))
4631 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4632 else if (playbin->text_sink
4633 && gst_object_has_as_ancestor (GST_MESSAGE_SRC (msg),
4634 GST_OBJECT_CAST (playbin->text_sink)))
4635 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4636 else
4637 gst_message_unref (msg);
4638 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_NEED_CONTEXT) {
4639 const gchar *context_type;
4640 GList *l;
4641
4642 gst_message_parse_context_type (msg, &context_type);
4643 GST_OBJECT_LOCK (playbin);
4644 for (l = playbin->contexts; l; l = l->next) {
4645 GstContext *tmp = l->data;
4646 const gchar *tmp_type = gst_context_get_context_type (tmp);
4647
4648 if (strcmp (context_type, tmp_type) == 0) {
4649 gst_element_set_context (GST_ELEMENT (GST_MESSAGE_SRC (msg)), l->data);
4650 break;
4651 }
4652 }
4653 GST_OBJECT_UNLOCK (playbin);
4654
4655 /* Forward if we couldn't answer the message */
4656 if (l == NULL) {
4657 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4658 } else {
4659 gst_message_unref (msg);
4660 }
4661 } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_HAVE_CONTEXT) {
4662 GstContext *context;
4663
4664 gst_message_parse_have_context (msg, &context);
4665 gst_play_bin_update_context (playbin, context);
4666 gst_context_unref (context);
4667
4668 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4669 } else {
4670 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
4671 }
4672
4673 /* Doesn't really matter, nothing is using this bus */
4674 return GST_BUS_DROP;
4675 }
4676
4677 static gboolean
activate_sink(GstPlayBin * playbin,GstElement * sink,gboolean * activated)4678 activate_sink (GstPlayBin * playbin, GstElement * sink, gboolean * activated)
4679 {
4680 GstState state;
4681 GstBus *bus = NULL;
4682 GstStateChangeReturn sret;
4683 gboolean ret = FALSE;
4684
4685 if (activated)
4686 *activated = FALSE;
4687
4688 GST_OBJECT_LOCK (sink);
4689 state = GST_STATE (sink);
4690 GST_OBJECT_UNLOCK (sink);
4691 if (state >= GST_STATE_READY) {
4692 ret = TRUE;
4693 goto done;
4694 }
4695
4696 if (!GST_OBJECT_PARENT (sink)) {
4697 bus = gst_bus_new ();
4698 gst_bus_set_sync_handler (bus,
4699 (GstBusSyncHandler) activate_sink_bus_handler, playbin, NULL);
4700 gst_element_set_bus (sink, bus);
4701 }
4702
4703 sret = gst_element_set_state (sink, GST_STATE_READY);
4704 if (sret == GST_STATE_CHANGE_FAILURE)
4705 goto done;
4706
4707 if (activated)
4708 *activated = TRUE;
4709 ret = TRUE;
4710
4711 done:
4712 if (bus) {
4713 gst_element_set_bus (sink, NULL);
4714 gst_object_unref (bus);
4715 }
4716
4717 return ret;
4718 }
4719
4720 /* autoplug-continue decides, if a pad has raw caps that can be exposed
4721 * directly or if further decoding is necessary. We use this to expose
4722 * supported subtitles directly */
4723
4724 /* FIXME 0.11: Remove the checks for ANY caps, a sink should specify
4725 * explicitly the caps it supports and if it claims to support ANY
4726 * caps it really should support everything */
4727 static gboolean
autoplug_continue_cb(GstElement * element,GstPad * pad,GstCaps * caps,GstSourceGroup * group)4728 autoplug_continue_cb (GstElement * element, GstPad * pad, GstCaps * caps,
4729 GstSourceGroup * group)
4730 {
4731 gboolean ret = TRUE;
4732 GstPad *sinkpad = NULL;
4733 gboolean activated_sink;
4734
4735 GST_SOURCE_GROUP_LOCK (group);
4736
4737 if (group->text_sink &&
4738 activate_sink (group->playbin, group->text_sink, &activated_sink)) {
4739 sinkpad = gst_element_get_static_pad (group->text_sink, "sink");
4740 if (sinkpad) {
4741 GstCaps *sinkcaps;
4742
4743 sinkcaps = gst_pad_query_caps (sinkpad, NULL);
4744 if (!gst_caps_is_any (sinkcaps))
4745 ret = !gst_caps_is_subset (caps, sinkcaps);
4746 gst_caps_unref (sinkcaps);
4747 gst_object_unref (sinkpad);
4748 }
4749 if (activated_sink)
4750 gst_element_set_state (group->text_sink, GST_STATE_NULL);
4751 } else {
4752 GstCaps *subcaps = gst_subtitle_overlay_create_factory_caps ();
4753 ret = !gst_caps_is_subset (caps, subcaps);
4754 gst_caps_unref (subcaps);
4755 }
4756 /* If autoplugging can stop don't do additional checks */
4757 if (!ret)
4758 goto done;
4759
4760 /* If this is from the subtitle uridecodebin we don't need to
4761 * check the audio and video sink */
4762 if (group->suburidecodebin
4763 && gst_object_has_as_ancestor (GST_OBJECT_CAST (element),
4764 GST_OBJECT_CAST (group->suburidecodebin)))
4765 goto done;
4766
4767 if (group->audio_sink &&
4768 activate_sink (group->playbin, group->audio_sink, &activated_sink)) {
4769
4770 sinkpad = gst_element_get_static_pad (group->audio_sink, "sink");
4771 if (sinkpad) {
4772 GstCaps *sinkcaps;
4773
4774 sinkcaps = gst_pad_query_caps (sinkpad, NULL);
4775 if (!gst_caps_is_any (sinkcaps))
4776 ret = !gst_caps_is_subset (caps, sinkcaps);
4777 gst_caps_unref (sinkcaps);
4778 gst_object_unref (sinkpad);
4779 }
4780 if (activated_sink)
4781 gst_element_set_state (group->audio_sink, GST_STATE_NULL);
4782 }
4783 if (!ret)
4784 goto done;
4785
4786 if (group->video_sink
4787 && activate_sink (group->playbin, group->video_sink, &activated_sink)) {
4788 sinkpad = gst_element_get_static_pad (group->video_sink, "sink");
4789 if (sinkpad) {
4790 GstCaps *sinkcaps;
4791
4792 sinkcaps = gst_pad_query_caps (sinkpad, NULL);
4793 if (!gst_caps_is_any (sinkcaps))
4794 ret = !gst_caps_is_subset (caps, sinkcaps);
4795 gst_caps_unref (sinkcaps);
4796 gst_object_unref (sinkpad);
4797 }
4798 if (activated_sink)
4799 gst_element_set_state (group->video_sink, GST_STATE_NULL);
4800 }
4801
4802 done:
4803 GST_SOURCE_GROUP_UNLOCK (group);
4804
4805 GST_DEBUG_OBJECT (group->playbin,
4806 "continue autoplugging group %p for %s:%s, %" GST_PTR_FORMAT ": %d",
4807 group, GST_DEBUG_PAD_NAME (pad), caps, ret);
4808
4809 return ret;
4810 }
4811
4812 static gboolean
sink_accepts_caps(GstPlayBin * playbin,GstElement * sink,GstCaps * caps)4813 sink_accepts_caps (GstPlayBin * playbin, GstElement * sink, GstCaps * caps)
4814 {
4815 GstPad *sinkpad;
4816 gboolean ret = TRUE;
4817
4818 if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) {
4819 GstCaps *sinkcaps;
4820
4821 sinkcaps = gst_pad_query_caps (sinkpad, NULL);
4822 /* Got the sink pad, now let's see if the element actually does accept the
4823 * caps that we have */
4824 ret = gst_caps_is_subset (caps, sinkcaps);
4825 gst_caps_unref (sinkcaps);
4826 gst_object_unref (sinkpad);
4827 }
4828
4829 return ret;
4830 }
4831
4832 /* We are asked to select an element. See if the next element to check
4833 * is a sink. If this is the case, we see if the sink works by setting it to
4834 * READY. If the sink works, we return SELECT_EXPOSE to make decodebin
4835 * expose the raw pad so that we can setup the mixers. */
4836 static GstAutoplugSelectResult
autoplug_select_cb(GstElement * decodebin,GstPad * pad,GstCaps * caps,GstElementFactory * factory,GstSourceGroup * group)4837 autoplug_select_cb (GstElement * decodebin, GstPad * pad,
4838 GstCaps * caps, GstElementFactory * factory, GstSourceGroup * group)
4839 {
4840 GstPlayBin *playbin;
4841 GstElement *element;
4842 const gchar *klass;
4843 GstPlaySinkType type;
4844 GstElement **sinkp;
4845 GList *ave_list = NULL, *l;
4846 GstAVElement *ave = NULL;
4847 GSequence *ave_seq = NULL;
4848 GSequenceIter *seq_iter;
4849 gboolean created_sink = FALSE;
4850
4851 playbin = group->playbin;
4852
4853 GST_DEBUG_OBJECT (playbin, "select group %p for %s:%s, %" GST_PTR_FORMAT,
4854 group, GST_DEBUG_PAD_NAME (pad), caps);
4855
4856 GST_DEBUG_OBJECT (playbin, "checking factory %s", GST_OBJECT_NAME (factory));
4857
4858 /* if it's not a sink, we make sure the element is compatible with
4859 * the fixed sink */
4860 if (!gst_element_factory_list_is_type (factory,
4861 GST_ELEMENT_FACTORY_TYPE_SINK)) {
4862 gboolean isvideodec = gst_element_factory_list_is_type (factory,
4863 GST_ELEMENT_FACTORY_TYPE_DECODER |
4864 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
4865 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE);
4866 gboolean isaudiodec = gst_element_factory_list_is_type (factory,
4867 GST_ELEMENT_FACTORY_TYPE_DECODER |
4868 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO);
4869
4870 if (!isvideodec && !isaudiodec)
4871 return GST_AUTOPLUG_SELECT_TRY;
4872
4873 GST_SOURCE_GROUP_LOCK (group);
4874 g_mutex_lock (&playbin->elements_lock);
4875
4876 if (isaudiodec) {
4877 ave_seq = playbin->aelements;
4878 sinkp = &group->audio_sink;
4879 } else {
4880 ave_seq = playbin->velements;
4881 sinkp = &group->video_sink;
4882 }
4883
4884 seq_iter =
4885 g_sequence_lookup (ave_seq, factory,
4886 (GCompareDataFunc) avelement_lookup_decoder, NULL);
4887 if (seq_iter) {
4888 /* Go to first iter with that decoder */
4889 do {
4890 GSequenceIter *tmp_seq_iter;
4891
4892 tmp_seq_iter = g_sequence_iter_prev (seq_iter);
4893 if (!avelement_iter_is_equal (tmp_seq_iter, factory))
4894 break;
4895 seq_iter = tmp_seq_iter;
4896 } while (!g_sequence_iter_is_begin (seq_iter));
4897
4898 while (!g_sequence_iter_is_end (seq_iter)
4899 && avelement_iter_is_equal (seq_iter, factory)) {
4900 ave = g_sequence_get (seq_iter);
4901 ave_list = g_list_prepend (ave_list, ave);
4902 seq_iter = g_sequence_iter_next (seq_iter);
4903 }
4904
4905 /* Sort all GstAVElements by their relative ranks and insert
4906 * into the decoders list */
4907 ave_list = g_list_sort (ave_list, (GCompareFunc) avelement_compare);
4908 } else {
4909 ave_list = g_list_prepend (ave_list, NULL);
4910 }
4911
4912 /* if it is a decoder and we don't have a fixed sink, then find out
4913 * the matching audio/video sink from GstAVElements list */
4914 for (l = ave_list; l; l = l->next) {
4915 ave = (GstAVElement *) l->data;
4916
4917 if (((isaudiodec && !group->audio_sink) ||
4918 (isvideodec && !group->video_sink))) {
4919 if (ave && ave->sink) {
4920 GST_DEBUG_OBJECT (playbin,
4921 "Trying to create sink '%s' for decoder '%s'",
4922 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)),
4923 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
4924 if ((*sinkp = gst_element_factory_create (ave->sink, NULL)) == NULL) {
4925 GST_WARNING_OBJECT (playbin,
4926 "Could not create an element from %s",
4927 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)));
4928 continue;
4929 } else {
4930 /* The sink is ours now, don't leak the floating reference in the
4931 * state-changed messages */
4932 gst_object_ref_sink (*sinkp);
4933
4934 if (!activate_sink (playbin, *sinkp, NULL)) {
4935 gst_object_unref (*sinkp);
4936 *sinkp = NULL;
4937 GST_WARNING_OBJECT (playbin,
4938 "Could not activate sink %s",
4939 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (ave->sink)));
4940 continue;
4941 }
4942 created_sink = TRUE;
4943 }
4944 }
4945 }
4946
4947 /* If it is a decoder and we have a fixed sink for the media
4948 * type it outputs, check that the decoder is compatible with this sink */
4949 if ((isaudiodec && group->audio_sink) || (isvideodec
4950 && group->video_sink)) {
4951 gboolean compatible = FALSE;
4952 GstPad *sinkpad;
4953 GstCaps *caps;
4954 GstElement *sink;
4955
4956 sink = *sinkp;
4957
4958 if ((sinkpad = gst_element_get_static_pad (sink, "sink"))) {
4959 GstPlayFlags flags = gst_play_bin_get_flags (playbin);
4960 GstCaps *raw_caps =
4961 (isaudiodec) ? gst_static_caps_get (&raw_audio_caps) :
4962 gst_static_caps_get (&raw_video_caps);
4963
4964 caps = gst_pad_query_caps (sinkpad, NULL);
4965
4966 /* If the sink supports raw audio/video, we first check
4967 * if the decoder could output any raw audio/video format
4968 * and assume it is compatible with the sink then. We don't
4969 * do a complete compatibility check here if converters
4970 * are plugged between the decoder and the sink because
4971 * the converters will convert between raw formats and
4972 * even if the decoder format is not supported by the decoder
4973 * a converter will convert it.
4974 *
4975 * We assume here that the converters can convert between
4976 * any raw format.
4977 */
4978 if ((isaudiodec && !(flags & GST_PLAY_FLAG_NATIVE_AUDIO)
4979 && gst_caps_can_intersect (caps, raw_caps)) || (!isaudiodec
4980 #ifdef OHOS_OPT_COMPAT
4981 /*
4982 * ohos.opt.compat.0021
4983 * when open GST_PLAY_FLAG_NATIVE_VIDEO will not change decoder caps
4984 * Otherwise, the decoding plug-in is identified as the H264 soft plug-in
4985 */
4986 && (!(flags & GST_PLAY_FLAG_NATIVE_VIDEO) || (flags & GST_PLAY_FLAG_HARDWARE_VIDEO))
4987 #else
4988 && !(flags & GST_PLAY_FLAG_NATIVE_VIDEO)
4989 #endif
4990 && gst_caps_can_intersect (caps, raw_caps))) {
4991 compatible =
4992 gst_element_factory_can_src_any_caps (factory, raw_caps)
4993 || gst_element_factory_can_src_any_caps (factory, caps);
4994 } else {
4995 compatible = gst_element_factory_can_src_any_caps (factory, caps);
4996 }
4997
4998 gst_object_unref (sinkpad);
4999 gst_caps_unref (raw_caps);
5000 gst_caps_unref (caps);
5001 }
5002
5003 if (compatible)
5004 break;
5005
5006 GST_DEBUG_OBJECT (playbin, "%s not compatible with the fixed sink",
5007 GST_OBJECT_NAME (factory));
5008
5009 /* If it is not compatible, either continue with the next possible
5010 * sink or if we have a fixed sink, skip the decoder */
5011 if (created_sink) {
5012 gst_element_set_state (*sinkp, GST_STATE_NULL);
5013 gst_object_unref (*sinkp);
5014 *sinkp = NULL;
5015 created_sink = FALSE;
5016 } else {
5017 g_mutex_unlock (&playbin->elements_lock);
5018 GST_SOURCE_GROUP_UNLOCK (group);
5019 return GST_AUTOPLUG_SELECT_SKIP;
5020 }
5021 }
5022 }
5023 g_list_free (ave_list);
5024 g_mutex_unlock (&playbin->elements_lock);
5025 GST_SOURCE_GROUP_UNLOCK (group);
5026 return GST_AUTOPLUG_SELECT_TRY;
5027 }
5028
5029 /* it's a sink, see if an instance of it actually works */
5030 GST_DEBUG_OBJECT (playbin, "we found a sink '%s'", GST_OBJECT_NAME (factory));
5031
5032 klass =
5033 gst_element_factory_get_metadata (factory, GST_ELEMENT_METADATA_KLASS);
5034
5035 /* figure out the klass */
5036 if (strstr (klass, "Audio")) {
5037 GST_DEBUG_OBJECT (playbin, "we found an audio sink");
5038 type = GST_PLAY_SINK_TYPE_AUDIO;
5039 sinkp = &group->audio_sink;
5040 } else if (strstr (klass, "Video")) {
5041 GST_DEBUG_OBJECT (playbin, "we found a video sink");
5042 type = GST_PLAY_SINK_TYPE_VIDEO;
5043 sinkp = &group->video_sink;
5044 } else {
5045 /* unknown klass, skip this element */
5046 GST_WARNING_OBJECT (playbin, "unknown sink klass %s found", klass);
5047 return GST_AUTOPLUG_SELECT_SKIP;
5048 }
5049
5050 /* if we are asked to do visualisations and it's an audio sink, skip the
5051 * element. We can only do visualisations with raw sinks */
5052 if (gst_play_sink_get_flags (playbin->playsink) & GST_PLAY_FLAG_VIS) {
5053 if (type == GST_PLAY_SINK_TYPE_AUDIO) {
5054 GST_DEBUG_OBJECT (playbin, "skip audio sink because of vis");
5055 return GST_AUTOPLUG_SELECT_SKIP;
5056 }
5057 }
5058
5059 /* now see if we already have a sink element */
5060 GST_SOURCE_GROUP_LOCK (group);
5061 if (*sinkp && GST_STATE (*sinkp) >= GST_STATE_READY) {
5062 GstElement *sink = gst_object_ref (*sinkp);
5063
5064 if (sink_accepts_caps (playbin, sink, caps)) {
5065 GST_DEBUG_OBJECT (playbin,
5066 "Existing sink '%s' accepts caps: %" GST_PTR_FORMAT,
5067 GST_ELEMENT_NAME (sink), caps);
5068 gst_object_unref (sink);
5069 GST_SOURCE_GROUP_UNLOCK (group);
5070 return GST_AUTOPLUG_SELECT_EXPOSE;
5071 } else {
5072 GST_DEBUG_OBJECT (playbin,
5073 "Existing sink '%s' does not accept caps: %" GST_PTR_FORMAT,
5074 GST_ELEMENT_NAME (sink), caps);
5075 gst_object_unref (sink);
5076 GST_SOURCE_GROUP_UNLOCK (group);
5077 return GST_AUTOPLUG_SELECT_SKIP;
5078 }
5079 }
5080 GST_DEBUG_OBJECT (playbin, "we have no pending sink, try to create '%s'",
5081 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
5082
5083 if ((*sinkp = gst_element_factory_create (factory, NULL)) == NULL) {
5084 GST_WARNING_OBJECT (playbin, "Could not create an element from %s",
5085 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
5086 GST_SOURCE_GROUP_UNLOCK (group);
5087 return GST_AUTOPLUG_SELECT_SKIP;
5088 }
5089
5090 /* The sink is ours now, don't leak floating references in the state-changed
5091 * messages, but only do that if we didn't just create the sink above and
5092 * already ref_sink'd it there */
5093 if (!created_sink)
5094 gst_object_ref_sink (*sinkp);
5095
5096 element = *sinkp;
5097
5098 if (!activate_sink (playbin, element, NULL)) {
5099 GST_WARNING_OBJECT (playbin, "Could not activate sink %s",
5100 gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
5101 *sinkp = NULL;
5102 gst_object_unref (element);
5103 GST_SOURCE_GROUP_UNLOCK (group);
5104 return GST_AUTOPLUG_SELECT_SKIP;
5105 }
5106
5107 /* Check if the selected sink actually supports the
5108 * caps and can be set to READY*/
5109 if (!sink_accepts_caps (playbin, element, caps)) {
5110 *sinkp = NULL;
5111 gst_element_set_state (element, GST_STATE_NULL);
5112 gst_object_unref (element);
5113 GST_SOURCE_GROUP_UNLOCK (group);
5114 return GST_AUTOPLUG_SELECT_SKIP;
5115 }
5116
5117 /* remember the sink in the group now
5118 *
5119 * store the sink in the group, we will configure it later when we
5120 * reconfigure the sink */
5121 GST_DEBUG_OBJECT (playbin, "remember sink");
5122 GST_SOURCE_GROUP_UNLOCK (group);
5123
5124 /* tell decodebin to expose the pad because we are going to use this
5125 * sink */
5126 GST_DEBUG_OBJECT (playbin, "we found a working sink, expose pad");
5127
5128 return GST_AUTOPLUG_SELECT_EXPOSE;
5129 }
5130
5131 #define GST_PLAY_BIN_FILTER_CAPS(filter,caps) G_STMT_START { \
5132 if ((filter)) { \
5133 GstCaps *intersection = \
5134 gst_caps_intersect_full ((filter), (caps), GST_CAPS_INTERSECT_FIRST); \
5135 gst_caps_unref ((caps)); \
5136 (caps) = intersection; \
5137 } \
5138 } G_STMT_END
5139
5140 static gboolean
autoplug_query_caps(GstElement * uridecodebin,GstPad * pad,GstElement * element,GstQuery * query,GstSourceGroup * group)5141 autoplug_query_caps (GstElement * uridecodebin, GstPad * pad,
5142 GstElement * element, GstQuery * query, GstSourceGroup * group)
5143 {
5144 GstCaps *filter, *result = NULL;
5145 GstElement *sink;
5146 GstPad *sinkpad = NULL;
5147 GstElementFactory *factory;
5148 GstElementFactoryListType factory_type;
5149 gboolean have_sink = FALSE;
5150
5151 GST_SOURCE_GROUP_LOCK (group);
5152 gst_query_parse_caps (query, &filter);
5153
5154 factory = gst_element_get_factory (element);
5155 if (!factory)
5156 goto done;
5157
5158 if (gst_element_factory_list_is_type (factory,
5159 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
5160 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
5161 factory_type =
5162 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
5163 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE;
5164
5165 /* If this is from the subtitle uridecodebin we don't need to
5166 * check the audio and video sink */
5167 if (group->suburidecodebin
5168 && gst_object_has_as_ancestor (GST_OBJECT_CAST (pad),
5169 GST_OBJECT_CAST (group->suburidecodebin))) {
5170 goto done;
5171 }
5172
5173 if ((sink = group->video_sink)) {
5174 sinkpad = gst_element_get_static_pad (sink, "sink");
5175 if (sinkpad) {
5176 GstCaps *sinkcaps;
5177
5178 sinkcaps = gst_pad_query_caps (sinkpad, filter);
5179 if (!gst_caps_is_any (sinkcaps)) {
5180 if (!result)
5181 result = sinkcaps;
5182 else
5183 result = gst_caps_merge (result, sinkcaps);
5184 } else {
5185 gst_caps_unref (sinkcaps);
5186 }
5187 gst_object_unref (sinkpad);
5188 }
5189 have_sink = TRUE;
5190 }
5191 } else if (gst_element_factory_list_is_type (factory,
5192 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
5193 factory_type = GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO;
5194
5195 /* If this is from the subtitle uridecodebin we don't need to
5196 * check the audio and video sink */
5197 if (group->suburidecodebin
5198 && gst_object_has_as_ancestor (GST_OBJECT_CAST (pad),
5199 GST_OBJECT_CAST (group->suburidecodebin))) {
5200 goto done;
5201 }
5202
5203 if ((sink = group->audio_sink)) {
5204 sinkpad = gst_element_get_static_pad (sink, "sink");
5205 if (sinkpad) {
5206 GstCaps *sinkcaps;
5207
5208 sinkcaps = gst_pad_query_caps (sinkpad, filter);
5209 if (!gst_caps_is_any (sinkcaps)) {
5210 if (!result)
5211 result = sinkcaps;
5212 else
5213 result = gst_caps_merge (result, sinkcaps);
5214 } else {
5215 gst_caps_unref (sinkcaps);
5216 }
5217 gst_object_unref (sinkpad);
5218 }
5219 have_sink = TRUE;
5220 }
5221 } else if (gst_element_factory_list_is_type (factory,
5222 GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE)) {
5223 factory_type = GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE;
5224
5225 if ((sink = group->playbin->text_sink)) {
5226 sinkpad = gst_element_get_static_pad (sink, "sink");
5227 if (sinkpad) {
5228 GstCaps *sinkcaps;
5229
5230 sinkcaps = gst_pad_query_caps (sinkpad, filter);
5231 if (!gst_caps_is_any (sinkcaps)) {
5232 if (!result)
5233 result = sinkcaps;
5234 else
5235 result = gst_caps_merge (result, sinkcaps);
5236 } else {
5237 gst_caps_unref (sinkcaps);
5238 }
5239 gst_object_unref (sinkpad);
5240 }
5241 have_sink = TRUE;
5242 } else {
5243 GstCaps *subcaps = gst_subtitle_overlay_create_factory_caps ();
5244 GST_PLAY_BIN_FILTER_CAPS (filter, subcaps);
5245 if (!result)
5246 result = subcaps;
5247 else
5248 result = gst_caps_merge (result, subcaps);
5249 }
5250 } else {
5251 goto done;
5252 }
5253
5254 if (!have_sink) {
5255 GValueArray *factories;
5256 gint i, n;
5257
5258 factories = autoplug_factories_cb (uridecodebin, pad, NULL, group);
5259 n = factories->n_values;
5260 for (i = 0; i < n; i++) {
5261 GValue *v = g_value_array_get_nth (factories, i);
5262 GstElementFactory *f = g_value_get_object (v);
5263 const GList *templates;
5264 const GList *l;
5265 GstCaps *templ_caps;
5266
5267 if (!gst_element_factory_list_is_type (f, factory_type))
5268 continue;
5269
5270 templates = gst_element_factory_get_static_pad_templates (f);
5271
5272 for (l = templates; l; l = l->next) {
5273 templ_caps = gst_static_pad_template_get_caps (l->data);
5274
5275 if (!gst_caps_is_any (templ_caps)) {
5276 GST_PLAY_BIN_FILTER_CAPS (filter, templ_caps);
5277 if (!result)
5278 result = templ_caps;
5279 else
5280 result = gst_caps_merge (result, templ_caps);
5281 } else {
5282 gst_caps_unref (templ_caps);
5283 }
5284 }
5285 }
5286 g_value_array_free (factories);
5287 }
5288
5289 done:
5290 GST_SOURCE_GROUP_UNLOCK (group);
5291
5292 if (!result)
5293 return FALSE;
5294
5295 /* Add the actual decoder/parser/etc caps at the very end to
5296 * make sure we don't cause empty caps to be returned, e.g.
5297 * if a parser asks us but a decoder is required after it
5298 * because no sink can handle the format directly.
5299 */
5300 {
5301 GstPad *target = gst_ghost_pad_get_target (GST_GHOST_PAD (pad));
5302
5303 if (target) {
5304 GstCaps *target_caps = gst_pad_get_pad_template_caps (target);
5305
5306 GST_PLAY_BIN_FILTER_CAPS (filter, target_caps);
5307 if (!gst_caps_is_any (target_caps)) {
5308 GstCaps *tmp;
5309 GstCapsFeatures *features;
5310 GstStructure *s;
5311 guint i, n;
5312
5313 n = gst_caps_get_size (target_caps);
5314 tmp = gst_caps_new_empty ();
5315 for (i = 0; i < n; i++) {
5316 features = gst_caps_get_features (target_caps, i);
5317 s = gst_caps_get_structure (target_caps, i);
5318
5319 if (!gst_structure_has_name (s, "video/x-raw") &&
5320 !gst_structure_has_name (s, "audio/x-raw")) {
5321 gst_caps_append_structure_full (tmp,
5322 gst_structure_copy (s), gst_caps_features_copy (features));
5323 } else if (gst_caps_features_is_any (features)
5324 || gst_caps_features_is_equal (features,
5325 GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY)) {
5326 gst_caps_append_structure (tmp, gst_structure_copy (s));
5327 }
5328 }
5329 gst_caps_unref (target_caps);
5330 result = gst_caps_merge (result, tmp);
5331 }
5332 gst_object_unref (target);
5333 }
5334 }
5335
5336
5337 gst_query_set_caps_result (query, result);
5338 gst_caps_unref (result);
5339
5340 return TRUE;
5341 }
5342
5343 static gboolean
autoplug_query_context(GstElement * uridecodebin,GstPad * pad,GstElement * element,GstQuery * query,GstSourceGroup * group)5344 autoplug_query_context (GstElement * uridecodebin, GstPad * pad,
5345 GstElement * element, GstQuery * query, GstSourceGroup * group)
5346 {
5347 GstElement *sink;
5348 GstPad *sinkpad = NULL;
5349 GstElementFactory *factory;
5350 gboolean res = FALSE;
5351
5352 GST_SOURCE_GROUP_LOCK (group);
5353
5354 factory = gst_element_get_factory (element);
5355 if (!factory)
5356 goto done;
5357
5358 if (gst_element_factory_list_is_type (factory,
5359 GST_ELEMENT_FACTORY_TYPE_MEDIA_VIDEO |
5360 GST_ELEMENT_FACTORY_TYPE_MEDIA_IMAGE)) {
5361 /* If this is from the subtitle uridecodebin we don't need to
5362 * check the audio and video sink */
5363 if (group->suburidecodebin
5364 && gst_object_has_as_ancestor (GST_OBJECT_CAST (pad),
5365 GST_OBJECT_CAST (group->suburidecodebin))) {
5366 goto done;
5367 }
5368
5369 if ((sink = group->video_sink)) {
5370 sinkpad = gst_element_get_static_pad (sink, "sink");
5371 if (sinkpad) {
5372 res = gst_pad_query (sinkpad, query);
5373 gst_object_unref (sinkpad);
5374 }
5375 }
5376 } else if (gst_element_factory_list_is_type (factory,
5377 GST_ELEMENT_FACTORY_TYPE_MEDIA_AUDIO)) {
5378 /* If this is from the subtitle uridecodebin we don't need to
5379 * check the audio and video sink */
5380 if (group->suburidecodebin
5381 && gst_object_has_as_ancestor (GST_OBJECT_CAST (pad),
5382 GST_OBJECT_CAST (group->suburidecodebin))) {
5383 goto done;
5384 }
5385
5386 if ((sink = group->audio_sink)) {
5387 sinkpad = gst_element_get_static_pad (sink, "sink");
5388 if (sinkpad) {
5389 res = gst_pad_query (sinkpad, query);
5390 gst_object_unref (sinkpad);
5391 }
5392 }
5393 } else if (gst_element_factory_list_is_type (factory,
5394 GST_ELEMENT_FACTORY_TYPE_MEDIA_SUBTITLE)) {
5395 if ((sink = group->playbin->text_sink)) {
5396 sinkpad = gst_element_get_static_pad (sink, "sink");
5397 if (sinkpad) {
5398 res = gst_pad_query (sinkpad, query);
5399 gst_object_unref (sinkpad);
5400 }
5401 }
5402 } else {
5403 goto done;
5404 }
5405
5406 done:
5407 GST_SOURCE_GROUP_UNLOCK (group);
5408
5409 return res;
5410 }
5411
5412 static gboolean
autoplug_query_cb(GstElement * uridecodebin,GstPad * pad,GstElement * element,GstQuery * query,GstSourceGroup * group)5413 autoplug_query_cb (GstElement * uridecodebin, GstPad * pad,
5414 GstElement * element, GstQuery * query, GstSourceGroup * group)
5415 {
5416
5417 switch (GST_QUERY_TYPE (query)) {
5418 case GST_QUERY_CAPS:
5419 return autoplug_query_caps (uridecodebin, pad, element, query, group);
5420 case GST_QUERY_CONTEXT:
5421 return autoplug_query_context (uridecodebin, pad, element, query, group);
5422 default:
5423 return FALSE;
5424 }
5425 }
5426
5427 static void
notify_source_cb(GstElement * uridecodebin,GParamSpec * pspec,GstSourceGroup * group)5428 notify_source_cb (GstElement * uridecodebin, GParamSpec * pspec,
5429 GstSourceGroup * group)
5430 {
5431 GstPlayBin *playbin;
5432 GstElement *source;
5433
5434 playbin = group->playbin;
5435
5436 g_object_get (group->uridecodebin, "source", &source, NULL);
5437
5438 GST_OBJECT_LOCK (playbin);
5439 if (playbin->source)
5440 gst_object_unref (playbin->source);
5441 playbin->source = source;
5442 GST_OBJECT_UNLOCK (playbin);
5443
5444 g_object_notify (G_OBJECT (playbin), "source");
5445 }
5446
5447 static void
source_setup_cb(GstElement * uridecodebin,GstElement * source,GstSourceGroup * group)5448 source_setup_cb (GstElement * uridecodebin, GstElement * source,
5449 GstSourceGroup * group)
5450 {
5451 GstPlayBin *playbin;
5452
5453 playbin = group->playbin;
5454
5455 g_signal_emit (playbin, gst_play_bin_signals[SIGNAL_SOURCE_SETUP], 0, source);
5456 }
5457
5458 /* must be called with the group lock */
5459 static gboolean
group_set_locked_state_unlocked(GstPlayBin * playbin,GstSourceGroup * group,gboolean locked)5460 group_set_locked_state_unlocked (GstPlayBin * playbin, GstSourceGroup * group,
5461 gboolean locked)
5462 {
5463 GST_DEBUG_OBJECT (playbin, "locked_state %d on group %p", locked, group);
5464
5465 if (group->uridecodebin)
5466 gst_element_set_locked_state (group->uridecodebin, locked);
5467 if (group->suburidecodebin)
5468 gst_element_set_locked_state (group->suburidecodebin, locked);
5469
5470 return TRUE;
5471 }
5472
5473 #ifdef OHOS_EXT_FUNC
5474 // ohos.ext.func.0028
5475 static void
bitrate_parse_complete_cb(GstElement * uridecodebin,gpointer input,guint num,GstSourceGroup * group)5476 bitrate_parse_complete_cb (GstElement * uridecodebin, gpointer input, guint num, GstSourceGroup * group)
5477 {
5478 if ((group == NULL) || (group->playbin == NULL)) {
5479 return;
5480 }
5481 GstPlayBin *playbin = group->playbin;
5482 GST_INFO_OBJECT (playbin, "in manifest parse complete");
5483 g_signal_emit (playbin, gst_play_bin_signals[SIGNAL_BITRATE_PARSE_COMPLETE], 0, input, num);
5484 GST_INFO_OBJECT (playbin, "out manifest parse complete");
5485 }
5486 #endif
5487
5488 /* must be called with PLAY_BIN_LOCK */
5489 static GstStateChangeReturn
activate_group(GstPlayBin * playbin,GstSourceGroup * group,GstState target)5490 activate_group (GstPlayBin * playbin, GstSourceGroup * group, GstState target)
5491 {
5492 GstElement *uridecodebin = NULL;
5493 GstElement *suburidecodebin = NULL;
5494 GstPlayFlags flags;
5495 gboolean audio_sink_activated = FALSE;
5496 gboolean video_sink_activated = FALSE;
5497 gboolean text_sink_activated = FALSE;
5498 GstStateChangeReturn state_ret;
5499
5500 g_return_val_if_fail (group->valid, GST_STATE_CHANGE_FAILURE);
5501 g_return_val_if_fail (!group->active, GST_STATE_CHANGE_FAILURE);
5502
5503 GST_DEBUG_OBJECT (playbin, "activating group %p", group);
5504
5505 GST_SOURCE_GROUP_LOCK (group);
5506
5507 /* First set up the custom sources */
5508 if (playbin->audio_sink)
5509 group->audio_sink = gst_object_ref (playbin->audio_sink);
5510 else
5511 group->audio_sink =
5512 gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_AUDIO);
5513
5514 if (group->audio_sink) {
5515 if (!activate_sink (playbin, group->audio_sink, &audio_sink_activated)) {
5516 if (group->audio_sink == playbin->audio_sink) {
5517 goto sink_failure;
5518 } else {
5519 gst_object_unref (group->audio_sink);
5520 group->audio_sink = NULL;
5521 }
5522 }
5523 }
5524
5525 if (playbin->video_sink)
5526 group->video_sink = gst_object_ref (playbin->video_sink);
5527 else
5528 group->video_sink =
5529 gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_VIDEO);
5530
5531 if (group->video_sink) {
5532 if (!activate_sink (playbin, group->video_sink, &video_sink_activated)) {
5533 if (group->video_sink == playbin->video_sink) {
5534 goto sink_failure;
5535 } else {
5536 gst_object_unref (group->video_sink);
5537 group->video_sink = NULL;
5538 }
5539 }
5540 }
5541
5542 if (playbin->text_sink)
5543 group->text_sink = gst_object_ref (playbin->text_sink);
5544 else
5545 group->text_sink =
5546 gst_play_sink_get_sink (playbin->playsink, GST_PLAY_SINK_TYPE_TEXT);
5547
5548 if (group->text_sink) {
5549 if (!activate_sink (playbin, group->text_sink, &text_sink_activated)) {
5550 if (group->text_sink == playbin->text_sink) {
5551 goto sink_failure;
5552 } else {
5553 gst_object_unref (group->text_sink);
5554 group->text_sink = NULL;
5555 }
5556 }
5557 }
5558
5559 g_slist_free (group->suburi_flushes_to_drop);
5560 group->suburi_flushes_to_drop = NULL;
5561 if (!group->suburi_flushes_to_drop_lock.p)
5562 g_mutex_init (&group->suburi_flushes_to_drop_lock);
5563
5564 if (group->uridecodebin) {
5565 GST_DEBUG_OBJECT (playbin, "reusing existing uridecodebin");
5566 uridecodebin = group->uridecodebin;
5567 gst_element_set_state (uridecodebin, GST_STATE_READY);
5568 /* no need to take extra ref, we already have one
5569 * and the bin will add one since it is no longer floating,
5570 * as it was at least added once before (below) */
5571 gst_bin_add (GST_BIN_CAST (playbin), uridecodebin);
5572 } else {
5573 GST_DEBUG_OBJECT (playbin, "making new uridecodebin");
5574 uridecodebin = gst_element_factory_make ("uridecodebin", NULL);
5575 if (!uridecodebin)
5576 goto no_decodebin;
5577 gst_bin_add (GST_BIN_CAST (playbin), uridecodebin);
5578 group->uridecodebin = gst_object_ref (uridecodebin);
5579 }
5580
5581 #ifdef OHOS_EXT_FUNC
5582 // ohos.ext.func.0012
5583 g_object_set (uridecodebin,
5584 "low-percent", playbin->low_percent,
5585 "high-percent", playbin->high_percent, NULL);
5586
5587 // ohos.ext.func.0028
5588 group->bitrate_parse_complete_id =
5589 g_signal_connect (uridecodebin, "bitrate-parse-complete", G_CALLBACK (bitrate_parse_complete_cb), group);
5590 #endif
5591 flags = gst_play_sink_get_flags (playbin->playsink);
5592
5593 g_object_set (uridecodebin,
5594 /* configure connection speed */
5595 "connection-speed", playbin->connection_speed / 1000,
5596 /* configure uri */
5597 "uri", group->uri,
5598 /* configure download buffering */
5599 "download", ((flags & GST_PLAY_FLAG_DOWNLOAD) != 0),
5600 /* configure buffering of demuxed/parsed data */
5601 "use-buffering", ((flags & GST_PLAY_FLAG_BUFFERING) != 0),
5602 /* enable hardware-based elements */
5603 "force-sw-decoders", ((flags & GST_PLAY_FLAG_FORCE_SW_DECODERS) != 0),
5604 /* configure buffering parameters */
5605 "buffer-duration", playbin->buffer_duration,
5606 "buffer-size", playbin->buffer_size,
5607 "ring-buffer-max-size", playbin->ring_buffer_max_size, NULL);
5608
5609 /* connect pads and other things */
5610 group->pad_added_id = g_signal_connect (uridecodebin, "pad-added",
5611 G_CALLBACK (pad_added_cb), group);
5612 group->pad_removed_id = g_signal_connect (uridecodebin, "pad-removed",
5613 G_CALLBACK (pad_removed_cb), group);
5614 group->no_more_pads_id = g_signal_connect (uridecodebin, "no-more-pads",
5615 G_CALLBACK (no_more_pads_cb), group);
5616 group->notify_source_id = g_signal_connect (uridecodebin, "notify::source",
5617 G_CALLBACK (notify_source_cb), group);
5618 group->source_setup_id = g_signal_connect (uridecodebin, "source-setup",
5619 G_CALLBACK (source_setup_cb), group);
5620
5621 /* we have 1 pending no-more-pads */
5622 group->pending = 1;
5623
5624 /* is called when the uridecodebin is out of data and we can switch to the
5625 * next uri */
5626 group->drained_id =
5627 g_signal_connect (uridecodebin, "drained", G_CALLBACK (drained_cb),
5628 group);
5629
5630 /* will be called when a new media type is found. We return a list of decoders
5631 * including sinks for decodebin to try */
5632 group->autoplug_factories_id =
5633 g_signal_connect (uridecodebin, "autoplug-factories",
5634 G_CALLBACK (autoplug_factories_cb), group);
5635 group->autoplug_select_id =
5636 g_signal_connect (uridecodebin, "autoplug-select",
5637 G_CALLBACK (autoplug_select_cb), group);
5638 group->autoplug_continue_id =
5639 g_signal_connect (uridecodebin, "autoplug-continue",
5640 G_CALLBACK (autoplug_continue_cb), group);
5641 group->autoplug_query_id =
5642 g_signal_connect (uridecodebin, "autoplug-query",
5643 G_CALLBACK (autoplug_query_cb), group);
5644
5645 if (group->suburi) {
5646 /* subtitles */
5647 if (group->suburidecodebin) {
5648 GST_DEBUG_OBJECT (playbin, "reusing existing suburidecodebin");
5649 suburidecodebin = group->suburidecodebin;
5650 gst_element_set_state (suburidecodebin, GST_STATE_READY);
5651 /* no need to take extra ref, we already have one
5652 * and the bin will add one since it is no longer floating,
5653 * as it was at least added once before (below) */
5654 gst_bin_add (GST_BIN_CAST (playbin), suburidecodebin);
5655 } else {
5656 GST_DEBUG_OBJECT (playbin, "making new suburidecodebin");
5657 suburidecodebin = gst_element_factory_make ("uridecodebin", NULL);
5658 if (!suburidecodebin)
5659 goto no_decodebin;
5660
5661 gst_bin_add (GST_BIN_CAST (playbin), suburidecodebin);
5662 group->suburidecodebin = gst_object_ref (suburidecodebin);
5663 }
5664
5665 g_object_set (suburidecodebin,
5666 /* configure connection speed */
5667 "connection-speed", playbin->connection_speed,
5668 /* configure uri */
5669 "uri", group->suburi, NULL);
5670
5671 /* connect pads and other things */
5672 group->sub_pad_added_id = g_signal_connect (suburidecodebin, "pad-added",
5673 G_CALLBACK (pad_added_cb), group);
5674 group->sub_pad_removed_id = g_signal_connect (suburidecodebin,
5675 "pad-removed", G_CALLBACK (pad_removed_cb), group);
5676 group->sub_no_more_pads_id = g_signal_connect (suburidecodebin,
5677 "no-more-pads", G_CALLBACK (no_more_pads_cb), group);
5678
5679 group->sub_autoplug_continue_id =
5680 g_signal_connect (suburidecodebin, "autoplug-continue",
5681 G_CALLBACK (autoplug_continue_cb), group);
5682
5683 group->sub_autoplug_query_id =
5684 g_signal_connect (suburidecodebin, "autoplug-query",
5685 G_CALLBACK (autoplug_query_cb), group);
5686
5687 /* we have 2 pending no-more-pads */
5688 group->pending = 2;
5689 group->sub_pending = TRUE;
5690 } else {
5691 group->sub_pending = FALSE;
5692 }
5693
5694 /* release the group lock before setting the state of the decodebins, they
5695 * might fire signals in this thread that we need to handle with the
5696 * group_lock taken. */
5697 GST_SOURCE_GROUP_UNLOCK (group);
5698
5699 if (suburidecodebin) {
5700 if (gst_element_set_state (suburidecodebin,
5701 target) == GST_STATE_CHANGE_FAILURE) {
5702 GST_DEBUG_OBJECT (playbin,
5703 "failed state change of subtitle uridecodebin");
5704 GST_SOURCE_GROUP_LOCK (group);
5705
5706 REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_added_id);
5707 REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_removed_id);
5708 REMOVE_SIGNAL (group->suburidecodebin, group->sub_no_more_pads_id);
5709 REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_continue_id);
5710 REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_query_id);
5711
5712 /* Might already be removed because of an error message */
5713 if (GST_OBJECT_PARENT (suburidecodebin) == GST_OBJECT_CAST (playbin))
5714 gst_bin_remove (GST_BIN_CAST (playbin), suburidecodebin);
5715 if (group->sub_pending) {
5716 group->pending--;
5717 group->sub_pending = FALSE;
5718 }
5719 gst_element_set_state (suburidecodebin, GST_STATE_READY);
5720 g_free (group->suburi);
5721 group->suburi = NULL;
5722 GST_SOURCE_GROUP_UNLOCK (group);
5723 }
5724 }
5725 if ((state_ret =
5726 gst_element_set_state (uridecodebin,
5727 target)) == GST_STATE_CHANGE_FAILURE)
5728 goto uridecodebin_failure;
5729
5730 GST_SOURCE_GROUP_LOCK (group);
5731 /* allow state changes of the playbin affect the group elements now */
5732 group_set_locked_state_unlocked (playbin, group, FALSE);
5733 group->active = TRUE;
5734 GST_SOURCE_GROUP_UNLOCK (group);
5735
5736 return state_ret;
5737
5738 /* ERRORS */
5739 no_decodebin:
5740 {
5741 GstMessage *msg;
5742
5743 GST_SOURCE_GROUP_UNLOCK (group);
5744 msg =
5745 gst_missing_element_message_new (GST_ELEMENT_CAST (playbin),
5746 "uridecodebin");
5747 gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
5748
5749 GST_ELEMENT_ERROR (playbin, CORE, MISSING_PLUGIN,
5750 (_("Could not create \"uridecodebin\" element.")), (NULL));
5751
5752 GST_SOURCE_GROUP_LOCK (group);
5753
5754 goto error_cleanup;
5755 }
5756 uridecodebin_failure:
5757 {
5758 GST_DEBUG_OBJECT (playbin, "failed state change of uridecodebin");
5759 GST_SOURCE_GROUP_LOCK (group);
5760 goto error_cleanup;
5761 }
5762 sink_failure:
5763 {
5764 GST_ERROR_OBJECT (playbin, "failed to activate sinks");
5765 goto error_cleanup;
5766 }
5767
5768 error_cleanup:
5769 {
5770 /* delete any custom sinks we might have */
5771 if (group->audio_sink) {
5772 /* If this is a automatically created sink set it to NULL */
5773 if (audio_sink_activated)
5774 gst_element_set_state (group->audio_sink, GST_STATE_NULL);
5775 gst_object_unref (group->audio_sink);
5776 }
5777 group->audio_sink = NULL;
5778
5779 if (group->video_sink) {
5780 /* If this is a automatically created sink set it to NULL */
5781 if (video_sink_activated)
5782 gst_element_set_state (group->video_sink, GST_STATE_NULL);
5783 gst_object_unref (group->video_sink);
5784 }
5785 group->video_sink = NULL;
5786
5787 if (group->text_sink) {
5788 /* If this is a automatically created sink set it to NULL */
5789 if (text_sink_activated)
5790 gst_element_set_state (group->text_sink, GST_STATE_NULL);
5791 gst_object_unref (group->text_sink);
5792 }
5793 group->text_sink = NULL;
5794
5795 if (uridecodebin) {
5796 REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id);
5797 REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id);
5798 REMOVE_SIGNAL (group->uridecodebin, group->no_more_pads_id);
5799 REMOVE_SIGNAL (group->uridecodebin, group->notify_source_id);
5800 REMOVE_SIGNAL (group->uridecodebin, group->source_setup_id);
5801 REMOVE_SIGNAL (group->uridecodebin, group->drained_id);
5802 REMOVE_SIGNAL (group->uridecodebin, group->autoplug_factories_id);
5803 REMOVE_SIGNAL (group->uridecodebin, group->autoplug_select_id);
5804 REMOVE_SIGNAL (group->uridecodebin, group->autoplug_continue_id);
5805 REMOVE_SIGNAL (group->uridecodebin, group->autoplug_query_id);
5806 #ifdef OHOS_EXT_FUNC
5807 // ohos.ext.func.0028
5808 REMOVE_SIGNAL (group->uridecodebin, group->bitrate_parse_complete_id);
5809 #endif
5810
5811 gst_element_set_state (uridecodebin, GST_STATE_NULL);
5812 gst_bin_remove (GST_BIN_CAST (playbin), uridecodebin);
5813 }
5814
5815 GST_SOURCE_GROUP_UNLOCK (group);
5816
5817 return GST_STATE_CHANGE_FAILURE;
5818 }
5819 }
5820
5821 /* unlink a group of uridecodebins from the sink.
5822 * must be called with PLAY_BIN_LOCK */
5823 static gboolean
deactivate_group(GstPlayBin * playbin,GstSourceGroup * group)5824 deactivate_group (GstPlayBin * playbin, GstSourceGroup * group)
5825 {
5826 gint i;
5827
5828 g_return_val_if_fail (group->active, FALSE);
5829 g_return_val_if_fail (group->valid, FALSE);
5830
5831 GST_DEBUG_OBJECT (playbin, "unlinking group %p", group);
5832
5833 GST_SOURCE_GROUP_LOCK (group);
5834 group->active = FALSE;
5835 for (i = 0; i < PLAYBIN_STREAM_LAST; i++) {
5836 GstSourceCombine *combine = &group->combiner[i];
5837
5838 GST_DEBUG_OBJECT (playbin, "unlinking combiner %s", combine->media_list[0]);
5839
5840 if (combine->srcpad) {
5841 source_combine_remove_pads (playbin, combine);
5842 }
5843
5844 if (combine->combiner) {
5845 gint n;
5846
5847 /* release and unref requests pad from the combiner */
5848 for (n = 0; n < combine->channels->len; n++) {
5849 GstPad *sinkpad = g_ptr_array_index (combine->channels, n);
5850
5851 gst_element_release_request_pad (combine->combiner, sinkpad);
5852 gst_object_unref (sinkpad);
5853 }
5854 g_ptr_array_set_size (combine->channels, 0);
5855
5856 gst_element_set_state (combine->combiner, GST_STATE_NULL);
5857 gst_bin_remove (GST_BIN_CAST (playbin), combine->combiner);
5858 combine->combiner = NULL;
5859 }
5860 }
5861 /* delete any custom sinks we might have.
5862 * conditionally set them to null if they aren't inside playsink yet */
5863 if (group->audio_sink) {
5864 if (!gst_object_has_as_ancestor (GST_OBJECT_CAST (group->audio_sink),
5865 GST_OBJECT_CAST (playbin->playsink))) {
5866 gst_element_set_state (group->audio_sink, GST_STATE_NULL);
5867 }
5868 gst_object_unref (group->audio_sink);
5869 }
5870 group->audio_sink = NULL;
5871 if (group->video_sink) {
5872 if (!gst_object_has_as_ancestor (GST_OBJECT_CAST (group->video_sink),
5873 GST_OBJECT_CAST (playbin->playsink))) {
5874 gst_element_set_state (group->video_sink, GST_STATE_NULL);
5875 }
5876 gst_object_unref (group->video_sink);
5877 }
5878 group->video_sink = NULL;
5879 if (group->text_sink) {
5880 if (!gst_object_has_as_ancestor (GST_OBJECT_CAST (group->text_sink),
5881 GST_OBJECT_CAST (playbin->playsink))) {
5882 gst_element_set_state (group->text_sink, GST_STATE_NULL);
5883 }
5884 gst_object_unref (group->text_sink);
5885 }
5886 group->text_sink = NULL;
5887
5888 if (group->uridecodebin) {
5889 REMOVE_SIGNAL (group->uridecodebin, group->pad_added_id);
5890 REMOVE_SIGNAL (group->uridecodebin, group->pad_removed_id);
5891 REMOVE_SIGNAL (group->uridecodebin, group->no_more_pads_id);
5892 REMOVE_SIGNAL (group->uridecodebin, group->notify_source_id);
5893 REMOVE_SIGNAL (group->uridecodebin, group->source_setup_id);
5894 REMOVE_SIGNAL (group->uridecodebin, group->drained_id);
5895 REMOVE_SIGNAL (group->uridecodebin, group->autoplug_factories_id);
5896 REMOVE_SIGNAL (group->uridecodebin, group->autoplug_select_id);
5897 REMOVE_SIGNAL (group->uridecodebin, group->autoplug_continue_id);
5898 REMOVE_SIGNAL (group->uridecodebin, group->autoplug_query_id);
5899 #ifdef OHOS_EXT_FUNC
5900 // ohos.ext.func.0028
5901 REMOVE_SIGNAL (group->uridecodebin, group->bitrate_parse_complete_id);
5902 #endif
5903 gst_bin_remove (GST_BIN_CAST (playbin), group->uridecodebin);
5904 }
5905
5906 if (group->suburidecodebin) {
5907 REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_added_id);
5908 REMOVE_SIGNAL (group->suburidecodebin, group->sub_pad_removed_id);
5909 REMOVE_SIGNAL (group->suburidecodebin, group->sub_no_more_pads_id);
5910 REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_continue_id);
5911 REMOVE_SIGNAL (group->suburidecodebin, group->sub_autoplug_query_id);
5912
5913 /* Might already be removed because of errors */
5914 if (GST_OBJECT_PARENT (group->suburidecodebin) == GST_OBJECT_CAST (playbin))
5915 gst_bin_remove (GST_BIN_CAST (playbin), group->suburidecodebin);
5916 }
5917
5918 group->have_group_id = FALSE;
5919
5920 GST_SOURCE_GROUP_UNLOCK (group);
5921
5922 return TRUE;
5923 }
5924
5925 /* setup the next group to play, this assumes the next_group is valid and
5926 * configured. It swaps out the current_group and activates the valid
5927 * next_group. */
5928 static GstStateChangeReturn
setup_next_source(GstPlayBin * playbin,GstState target)5929 setup_next_source (GstPlayBin * playbin, GstState target)
5930 {
5931 GstSourceGroup *new_group, *old_group;
5932 GstStateChangeReturn state_ret;
5933
5934 GST_DEBUG_OBJECT (playbin, "setup sources");
5935
5936 /* see if there is a next group */
5937 GST_PLAY_BIN_LOCK (playbin);
5938 new_group = playbin->next_group;
5939 if (!new_group || !new_group->valid)
5940 goto no_next_group;
5941
5942 /* first unlink the current source, if any */
5943 old_group = playbin->curr_group;
5944 if (old_group && old_group->valid && old_group->active) {
5945 new_group->stream_changed_pending = TRUE;
5946
5947 gst_play_bin_update_cached_duration (playbin);
5948 /* unlink our pads with the sink */
5949 deactivate_group (playbin, old_group);
5950 old_group->valid = FALSE;
5951 }
5952
5953 /* swap old and new */
5954 playbin->curr_group = new_group;
5955 playbin->next_group = old_group;
5956
5957 /* activate the new group */
5958 if ((state_ret =
5959 activate_group (playbin, new_group,
5960 target)) == GST_STATE_CHANGE_FAILURE)
5961 goto activate_failed;
5962
5963 GST_PLAY_BIN_UNLOCK (playbin);
5964
5965 return state_ret;
5966
5967 /* ERRORS */
5968 no_next_group:
5969 {
5970 GST_DEBUG_OBJECT (playbin, "no next group");
5971 if (target == GST_STATE_READY && new_group && new_group->uri == NULL)
5972 GST_ELEMENT_ERROR (playbin, RESOURCE, NOT_FOUND, ("No URI set"), (NULL));
5973 GST_PLAY_BIN_UNLOCK (playbin);
5974 return GST_STATE_CHANGE_FAILURE;
5975 }
5976 activate_failed:
5977 {
5978 new_group->stream_changed_pending = FALSE;
5979 GST_DEBUG_OBJECT (playbin, "activate failed");
5980 new_group->valid = FALSE;
5981 GST_PLAY_BIN_UNLOCK (playbin);
5982 return GST_STATE_CHANGE_FAILURE;
5983 }
5984 }
5985
5986 /* The group that is currently playing is copied again to the
5987 * next_group so that it will start playing the next time.
5988 */
5989 static gboolean
save_current_group(GstPlayBin * playbin)5990 save_current_group (GstPlayBin * playbin)
5991 {
5992 GstSourceGroup *curr_group;
5993
5994 GST_DEBUG_OBJECT (playbin, "save current group");
5995
5996 /* see if there is a current group */
5997 GST_PLAY_BIN_LOCK (playbin);
5998 curr_group = playbin->curr_group;
5999 if (curr_group && curr_group->valid && curr_group->active) {
6000 /* unlink our pads with the sink */
6001 deactivate_group (playbin, curr_group);
6002 }
6003 /* swap old and new */
6004 playbin->curr_group = playbin->next_group;
6005 playbin->next_group = curr_group;
6006 GST_PLAY_BIN_UNLOCK (playbin);
6007
6008 return TRUE;
6009 }
6010
6011 /* clear the locked state from all groups. This function is called before a
6012 * state change to NULL is performed on them. */
6013 static gboolean
groups_set_locked_state(GstPlayBin * playbin,gboolean locked)6014 groups_set_locked_state (GstPlayBin * playbin, gboolean locked)
6015 {
6016 GST_DEBUG_OBJECT (playbin, "setting locked state to %d on all groups",
6017 locked);
6018
6019 GST_PLAY_BIN_LOCK (playbin);
6020 GST_SOURCE_GROUP_LOCK (playbin->curr_group);
6021 group_set_locked_state_unlocked (playbin, playbin->curr_group, locked);
6022 GST_SOURCE_GROUP_UNLOCK (playbin->curr_group);
6023 GST_SOURCE_GROUP_LOCK (playbin->next_group);
6024 group_set_locked_state_unlocked (playbin, playbin->next_group, locked);
6025 GST_SOURCE_GROUP_UNLOCK (playbin->next_group);
6026 GST_PLAY_BIN_UNLOCK (playbin);
6027
6028 return TRUE;
6029 }
6030
6031 static GstStateChangeReturn
gst_play_bin_change_state(GstElement * element,GstStateChange transition)6032 gst_play_bin_change_state (GstElement * element, GstStateChange transition)
6033 {
6034 GstStateChangeReturn ret;
6035 GstPlayBin *playbin;
6036 gboolean do_save = FALSE;
6037
6038 playbin = GST_PLAY_BIN (element);
6039
6040 switch (transition) {
6041 case GST_STATE_CHANGE_NULL_TO_READY:
6042 memset (&playbin->duration, 0, sizeof (playbin->duration));
6043 break;
6044 case GST_STATE_CHANGE_READY_TO_PAUSED:
6045 GST_LOG_OBJECT (playbin, "clearing shutdown flag");
6046 memset (&playbin->duration, 0, sizeof (playbin->duration));
6047 g_atomic_int_set (&playbin->shutdown, 0);
6048 do_async_start (playbin);
6049 break;
6050 case GST_STATE_CHANGE_PAUSED_TO_READY:
6051 async_down:
6052 /* FIXME unlock our waiting groups */
6053 GST_LOG_OBJECT (playbin, "setting shutdown flag");
6054 g_atomic_int_set (&playbin->shutdown, 1);
6055 memset (&playbin->duration, 0, sizeof (playbin->duration));
6056
6057 /* wait for all callbacks to end by taking the lock.
6058 * No dynamic (critical) new callbacks will
6059 * be able to happen as we set the shutdown flag. */
6060 GST_PLAY_BIN_DYN_LOCK (playbin);
6061 GST_LOG_OBJECT (playbin, "dynamic lock taken, we can continue shutdown");
6062 GST_PLAY_BIN_DYN_UNLOCK (playbin);
6063 if (!do_save)
6064 break;
6065 case GST_STATE_CHANGE_READY_TO_NULL:
6066 /* we go async to PAUSED, so if that fails, we never make it to PAUSED
6067 * and no state change PAUSED to READY passes here,
6068 * though it is a nice-to-have ... */
6069 if (!g_atomic_int_get (&playbin->shutdown)) {
6070 do_save = TRUE;
6071 goto async_down;
6072 }
6073 memset (&playbin->duration, 0, sizeof (playbin->duration));
6074
6075 /* unlock so that all groups go to NULL */
6076 groups_set_locked_state (playbin, FALSE);
6077 break;
6078 default:
6079 break;
6080 }
6081
6082 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
6083 if (ret == GST_STATE_CHANGE_FAILURE)
6084 goto failure;
6085
6086 switch (transition) {
6087 case GST_STATE_CHANGE_READY_TO_PAUSED:
6088 if ((ret =
6089 setup_next_source (playbin,
6090 GST_STATE_PAUSED)) == GST_STATE_CHANGE_FAILURE)
6091 goto failure;
6092 if (ret == GST_STATE_CHANGE_SUCCESS)
6093 ret = GST_STATE_CHANGE_ASYNC;
6094
6095 break;
6096 case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
6097 do_async_done (playbin);
6098 /* FIXME Release audio device when we implement that */
6099 break;
6100 case GST_STATE_CHANGE_PAUSED_TO_READY:
6101 playbin->is_live = FALSE;
6102 save_current_group (playbin);
6103 break;
6104 case GST_STATE_CHANGE_READY_TO_NULL:
6105 {
6106 guint i;
6107 GList *l;
6108
6109 /* also do missed state change down to READY */
6110 if (do_save)
6111 save_current_group (playbin);
6112 /* Deactivate the groups, set the uridecodebins to NULL
6113 * and unref them.
6114 */
6115 for (i = 0; i < 2; i++) {
6116 if (playbin->groups[i].active && playbin->groups[i].valid) {
6117 deactivate_group (playbin, &playbin->groups[i]);
6118 playbin->groups[i].valid = FALSE;
6119 }
6120
6121 if (playbin->groups[i].uridecodebin) {
6122 gst_element_set_state (playbin->groups[i].uridecodebin,
6123 GST_STATE_NULL);
6124 gst_object_unref (playbin->groups[i].uridecodebin);
6125 playbin->groups[i].uridecodebin = NULL;
6126 }
6127
6128 if (playbin->groups[i].suburidecodebin) {
6129 gst_element_set_state (playbin->groups[i].suburidecodebin,
6130 GST_STATE_NULL);
6131 gst_object_unref (playbin->groups[i].suburidecodebin);
6132 playbin->groups[i].suburidecodebin = NULL;
6133 }
6134 }
6135
6136 /* Set our sinks back to NULL, they might not be child of playbin */
6137 if (playbin->audio_sink)
6138 gst_element_set_state (playbin->audio_sink, GST_STATE_NULL);
6139 if (playbin->video_sink)
6140 gst_element_set_state (playbin->video_sink, GST_STATE_NULL);
6141 if (playbin->text_sink)
6142 gst_element_set_state (playbin->text_sink, GST_STATE_NULL);
6143
6144 if (playbin->video_stream_combiner)
6145 gst_element_set_state (playbin->video_stream_combiner, GST_STATE_NULL);
6146 if (playbin->audio_stream_combiner)
6147 gst_element_set_state (playbin->audio_stream_combiner, GST_STATE_NULL);
6148 if (playbin->text_stream_combiner)
6149 gst_element_set_state (playbin->text_stream_combiner, GST_STATE_NULL);
6150
6151 /* make sure the groups don't perform a state change anymore until we
6152 * enable them again */
6153 groups_set_locked_state (playbin, TRUE);
6154
6155 /* Remove all non-persistent contexts */
6156 GST_OBJECT_LOCK (playbin);
6157 for (l = playbin->contexts; l;) {
6158 GstContext *context = l->data;
6159
6160 if (!gst_context_is_persistent (context)) {
6161 GList *next;
6162
6163 gst_context_unref (context);
6164
6165 next = l->next;
6166 playbin->contexts = g_list_delete_link (playbin->contexts, l);
6167 l = next;
6168 } else {
6169 l = l->next;
6170 }
6171 }
6172
6173 if (playbin->source) {
6174 gst_object_unref (playbin->source);
6175 playbin->source = NULL;
6176 }
6177
6178 GST_OBJECT_UNLOCK (playbin);
6179 break;
6180 }
6181 default:
6182 break;
6183 }
6184
6185 if (GST_STATE_TRANSITION_NEXT (transition) == GST_STATE_PAUSED)
6186 playbin->is_live = ret == GST_STATE_CHANGE_NO_PREROLL;
6187
6188 if (ret == GST_STATE_CHANGE_NO_PREROLL)
6189 do_async_done (playbin);
6190
6191 return ret;
6192
6193 /* ERRORS */
6194 failure:
6195 {
6196 do_async_done (playbin);
6197
6198 if (transition == GST_STATE_CHANGE_READY_TO_PAUSED) {
6199 GstSourceGroup *curr_group;
6200
6201 curr_group = playbin->curr_group;
6202 if (curr_group) {
6203 if (curr_group->active && curr_group->valid) {
6204 /* unlink our pads with the sink */
6205 deactivate_group (playbin, curr_group);
6206 }
6207 curr_group->valid = FALSE;
6208 }
6209
6210 /* Swap current and next group back */
6211 playbin->curr_group = playbin->next_group;
6212 playbin->next_group = curr_group;
6213 }
6214 return ret;
6215 }
6216 }
6217
6218 static void
gst_play_bin_overlay_expose(GstVideoOverlay * overlay)6219 gst_play_bin_overlay_expose (GstVideoOverlay * overlay)
6220 {
6221 GstPlayBin *playbin = GST_PLAY_BIN (overlay);
6222
6223 gst_video_overlay_expose (GST_VIDEO_OVERLAY (playbin->playsink));
6224 }
6225
6226 static void
gst_play_bin_overlay_handle_events(GstVideoOverlay * overlay,gboolean handle_events)6227 gst_play_bin_overlay_handle_events (GstVideoOverlay * overlay,
6228 gboolean handle_events)
6229 {
6230 GstPlayBin *playbin = GST_PLAY_BIN (overlay);
6231
6232 gst_video_overlay_handle_events (GST_VIDEO_OVERLAY (playbin->playsink),
6233 handle_events);
6234 }
6235
6236 static void
gst_play_bin_overlay_set_render_rectangle(GstVideoOverlay * overlay,gint x,gint y,gint width,gint height)6237 gst_play_bin_overlay_set_render_rectangle (GstVideoOverlay * overlay, gint x,
6238 gint y, gint width, gint height)
6239 {
6240 GstPlayBin *playbin = GST_PLAY_BIN (overlay);
6241
6242 gst_video_overlay_set_render_rectangle (GST_VIDEO_OVERLAY (playbin->playsink),
6243 x, y, width, height);
6244 }
6245
6246 static void
gst_play_bin_overlay_set_window_handle(GstVideoOverlay * overlay,guintptr handle)6247 gst_play_bin_overlay_set_window_handle (GstVideoOverlay * overlay,
6248 guintptr handle)
6249 {
6250 GstPlayBin *playbin = GST_PLAY_BIN (overlay);
6251
6252 gst_video_overlay_set_window_handle (GST_VIDEO_OVERLAY (playbin->playsink),
6253 handle);
6254 }
6255
6256 static void
gst_play_bin_overlay_init(gpointer g_iface,gpointer g_iface_data)6257 gst_play_bin_overlay_init (gpointer g_iface, gpointer g_iface_data)
6258 {
6259 GstVideoOverlayInterface *iface = (GstVideoOverlayInterface *) g_iface;
6260 iface->expose = gst_play_bin_overlay_expose;
6261 iface->handle_events = gst_play_bin_overlay_handle_events;
6262 iface->set_render_rectangle = gst_play_bin_overlay_set_render_rectangle;
6263 iface->set_window_handle = gst_play_bin_overlay_set_window_handle;
6264 }
6265
6266 static void
gst_play_bin_navigation_send_event(GstNavigation * navigation,GstStructure * structure)6267 gst_play_bin_navigation_send_event (GstNavigation * navigation,
6268 GstStructure * structure)
6269 {
6270 GstPlayBin *playbin = GST_PLAY_BIN (navigation);
6271
6272 gst_navigation_send_event (GST_NAVIGATION (playbin->playsink), structure);
6273 }
6274
6275 static void
gst_play_bin_navigation_init(gpointer g_iface,gpointer g_iface_data)6276 gst_play_bin_navigation_init (gpointer g_iface, gpointer g_iface_data)
6277 {
6278 GstNavigationInterface *iface = (GstNavigationInterface *) g_iface;
6279
6280 iface->send_event = gst_play_bin_navigation_send_event;
6281 }
6282
6283 static const GList *
gst_play_bin_colorbalance_list_channels(GstColorBalance * balance)6284 gst_play_bin_colorbalance_list_channels (GstColorBalance * balance)
6285 {
6286 GstPlayBin *playbin = GST_PLAY_BIN (balance);
6287
6288 return
6289 gst_color_balance_list_channels (GST_COLOR_BALANCE (playbin->playsink));
6290 }
6291
6292 static void
gst_play_bin_colorbalance_set_value(GstColorBalance * balance,GstColorBalanceChannel * channel,gint value)6293 gst_play_bin_colorbalance_set_value (GstColorBalance * balance,
6294 GstColorBalanceChannel * channel, gint value)
6295 {
6296 GstPlayBin *playbin = GST_PLAY_BIN (balance);
6297
6298 gst_color_balance_set_value (GST_COLOR_BALANCE (playbin->playsink), channel,
6299 value);
6300 }
6301
6302 static gint
gst_play_bin_colorbalance_get_value(GstColorBalance * balance,GstColorBalanceChannel * channel)6303 gst_play_bin_colorbalance_get_value (GstColorBalance * balance,
6304 GstColorBalanceChannel * channel)
6305 {
6306 GstPlayBin *playbin = GST_PLAY_BIN (balance);
6307
6308 return gst_color_balance_get_value (GST_COLOR_BALANCE (playbin->playsink),
6309 channel);
6310 }
6311
6312 static GstColorBalanceType
gst_play_bin_colorbalance_get_balance_type(GstColorBalance * balance)6313 gst_play_bin_colorbalance_get_balance_type (GstColorBalance * balance)
6314 {
6315 GstPlayBin *playbin = GST_PLAY_BIN (balance);
6316
6317 return
6318 gst_color_balance_get_balance_type (GST_COLOR_BALANCE
6319 (playbin->playsink));
6320 }
6321
6322 static void
gst_play_bin_colorbalance_init(gpointer g_iface,gpointer g_iface_data)6323 gst_play_bin_colorbalance_init (gpointer g_iface, gpointer g_iface_data)
6324 {
6325 GstColorBalanceInterface *iface = (GstColorBalanceInterface *) g_iface;
6326
6327 iface->list_channels = gst_play_bin_colorbalance_list_channels;
6328 iface->set_value = gst_play_bin_colorbalance_set_value;
6329 iface->get_value = gst_play_bin_colorbalance_get_value;
6330 iface->get_balance_type = gst_play_bin_colorbalance_get_balance_type;
6331 }
6332