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