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