• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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", &current, 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