|
1 /* GStreamer |
|
2 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com> |
|
3 * |
|
4 * This library is free software; you can redistribute it and/or |
|
5 * modify it under the terms of the GNU Library General Public |
|
6 * License as published by the Free Software Foundation; either |
|
7 * version 2 of the License, or (at your option) any later version. |
|
8 * |
|
9 * This library is distributed in the hope that it will be useful, |
|
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|
12 * Library General Public License for more details. |
|
13 * |
|
14 * You should have received a copy of the GNU Library General Public |
|
15 * License along with this library; if not, write to the |
|
16 * Free Software Foundation, Inc., 59 Temple Place - Suite 330, |
|
17 * Boston, MA 02111-1307, USA. |
|
18 */ |
|
19 |
|
20 #ifdef HAVE_CONFIG_H |
|
21 #include "config.h" |
|
22 #endif |
|
23 |
|
24 #include <gst/gst-i18n-plugin.h> |
|
25 #include <string.h> |
|
26 #include "gstplaybasebin.h" |
|
27 #include "gststreamselector.h" |
|
28 #include "gstplay-marshal.h" |
|
29 |
|
30 #include <gst/pbutils/pbutils.h> |
|
31 |
|
32 #ifdef __SYMBIAN32__ |
|
33 #include <glib_global.h> |
|
34 #endif |
|
35 |
|
36 GST_DEBUG_CATEGORY_STATIC (gst_play_base_bin_debug); |
|
37 #define GST_CAT_DEFAULT gst_play_base_bin_debug |
|
38 |
|
39 #define DEFAULT_QUEUE_SIZE (3 * GST_SECOND) |
|
40 #define DEFAULT_QUEUE_MIN_THRESHOLD ((DEFAULT_QUEUE_SIZE * 30) / 100) |
|
41 #define DEFAULT_QUEUE_THRESHOLD ((DEFAULT_QUEUE_SIZE * 95) / 100) |
|
42 #define DEFAULT_CONNECTION_SPEED 0 |
|
43 |
|
44 #define GROUP_LOCK(pbb) g_mutex_lock (pbb->group_lock) |
|
45 #define GROUP_UNLOCK(pbb) g_mutex_unlock (pbb->group_lock) |
|
46 #define GROUP_WAIT(pbb) g_cond_wait (pbb->group_cond, pbb->group_lock) |
|
47 #define GROUP_SIGNAL(pbb) g_cond_signal (pbb->group_cond) |
|
48 |
|
49 /* props */ |
|
50 enum |
|
51 { |
|
52 ARG_0, |
|
53 ARG_URI, |
|
54 ARG_SUBURI, |
|
55 ARG_QUEUE_SIZE, |
|
56 ARG_QUEUE_THRESHOLD, |
|
57 ARG_QUEUE_MIN_THRESHOLD, |
|
58 ARG_NSTREAMS, |
|
59 ARG_STREAMINFO, |
|
60 ARG_STREAMINFO_VALUES, |
|
61 ARG_SOURCE, |
|
62 ARG_VIDEO, |
|
63 ARG_AUDIO, |
|
64 ARG_TEXT, |
|
65 ARG_SUBTITLE_ENCODING, |
|
66 ARG_CONNECTION_SPEED |
|
67 }; |
|
68 |
|
69 static void gst_play_base_bin_class_init (GstPlayBaseBinClass * klass); |
|
70 static void gst_play_base_bin_init (GstPlayBaseBin * play_base_bin); |
|
71 static void gst_play_base_bin_dispose (GObject * object); |
|
72 static void gst_play_base_bin_finalize (GObject * object); |
|
73 |
|
74 static void gst_play_base_bin_set_property (GObject * object, guint prop_id, |
|
75 const GValue * value, GParamSpec * spec); |
|
76 static void gst_play_base_bin_get_property (GObject * object, guint prop_id, |
|
77 GValue * value, GParamSpec * spec); |
|
78 static void gst_play_base_bin_handle_message_func (GstBin * bin, |
|
79 GstMessage * msg); |
|
80 |
|
81 static GstStateChangeReturn gst_play_base_bin_change_state (GstElement * |
|
82 element, GstStateChange transition); |
|
83 |
|
84 static const GList *gst_play_base_bin_get_streaminfo (GstPlayBaseBin * bin); |
|
85 static GValueArray *gst_play_base_bin_get_streaminfo_value_array (GstPlayBaseBin |
|
86 * play_base_bin); |
|
87 static void preroll_remove_overrun (GstElement * element, |
|
88 GstPlayBaseBin * play_base_bin); |
|
89 static void queue_remove_probe (GstElement * queue, GstPlayBaseBin |
|
90 * play_base_bin); |
|
91 |
|
92 static GstElement *make_decoder (GstPlayBaseBin * play_base_bin); |
|
93 static gboolean has_all_raw_caps (GstPad * pad, gboolean * all_raw); |
|
94 |
|
95 static gboolean prepare_output (GstPlayBaseBin * play_base_bin); |
|
96 static void set_active_source (GstPlayBaseBin * play_base_bin, |
|
97 GstStreamType type, gint source_num); |
|
98 static gboolean probe_triggered (GstPad * pad, GstEvent * event, |
|
99 gpointer user_data); |
|
100 static void setup_substreams (GstPlayBaseBin * play_base_bin); |
|
101 |
|
102 static GstPipelineClass *parent_class; |
|
103 |
|
104 /* |
|
105 * GObject playbasebin wrappers. |
|
106 */ |
|
107 #ifdef __SYMBIAN32__ |
|
108 EXPORT_C |
|
109 #endif |
|
110 |
|
111 |
|
112 GType |
|
113 gst_play_base_bin_get_type (void) |
|
114 { |
|
115 static GType gst_play_base_bin_type = 0; |
|
116 |
|
117 if (!gst_play_base_bin_type) { |
|
118 static const GTypeInfo gst_play_base_bin_info = { |
|
119 sizeof (GstPlayBaseBinClass), |
|
120 NULL, |
|
121 NULL, |
|
122 (GClassInitFunc) gst_play_base_bin_class_init, |
|
123 NULL, |
|
124 NULL, |
|
125 sizeof (GstPlayBaseBin), |
|
126 0, |
|
127 (GInstanceInitFunc) gst_play_base_bin_init, |
|
128 NULL |
|
129 }; |
|
130 |
|
131 gst_play_base_bin_type = g_type_register_static (GST_TYPE_PIPELINE, |
|
132 "GstPlayBaseBin", &gst_play_base_bin_info, 0); |
|
133 } |
|
134 |
|
135 return gst_play_base_bin_type; |
|
136 } |
|
137 |
|
138 static void |
|
139 gst_play_base_bin_class_init (GstPlayBaseBinClass * klass) |
|
140 { |
|
141 GObjectClass *gobject_klass; |
|
142 GstElementClass *gstelement_klass; |
|
143 GstBinClass *gstbin_klass; |
|
144 |
|
145 gobject_klass = (GObjectClass *) klass; |
|
146 gstelement_klass = (GstElementClass *) klass; |
|
147 gstbin_klass = (GstBinClass *) klass; |
|
148 |
|
149 parent_class = g_type_class_peek_parent (klass); |
|
150 |
|
151 gobject_klass->set_property = gst_play_base_bin_set_property; |
|
152 gobject_klass->get_property = gst_play_base_bin_get_property; |
|
153 |
|
154 g_object_class_install_property (gobject_klass, ARG_URI, |
|
155 g_param_spec_string ("uri", "URI", "URI of the media to play", |
|
156 NULL, G_PARAM_READWRITE)); |
|
157 g_object_class_install_property (gobject_klass, ARG_SUBURI, |
|
158 g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle", |
|
159 NULL, G_PARAM_READWRITE)); |
|
160 |
|
161 g_object_class_install_property (gobject_klass, ARG_QUEUE_SIZE, |
|
162 g_param_spec_uint64 ("queue-size", "Queue size", |
|
163 "Size of internal queues in nanoseconds", 0, G_MAXINT64, |
|
164 DEFAULT_QUEUE_SIZE, G_PARAM_READWRITE)); |
|
165 g_object_class_install_property (gobject_klass, ARG_QUEUE_THRESHOLD, |
|
166 g_param_spec_uint64 ("queue-threshold", "Queue threshold", |
|
167 "Buffering threshold of internal queues in nanoseconds", 0, |
|
168 G_MAXINT64, DEFAULT_QUEUE_THRESHOLD, G_PARAM_READWRITE)); |
|
169 g_object_class_install_property (gobject_klass, ARG_QUEUE_MIN_THRESHOLD, |
|
170 g_param_spec_uint64 ("queue-min-threshold", "Queue min threshold", |
|
171 "Buffering low threshold of internal queues in nanoseconds", 0, |
|
172 G_MAXINT64, DEFAULT_QUEUE_MIN_THRESHOLD, G_PARAM_READWRITE)); |
|
173 |
|
174 g_object_class_install_property (gobject_klass, ARG_NSTREAMS, |
|
175 g_param_spec_int ("nstreams", "NStreams", "number of streams", |
|
176 0, G_MAXINT, 0, G_PARAM_READABLE)); |
|
177 g_object_class_install_property (gobject_klass, ARG_STREAMINFO, |
|
178 g_param_spec_pointer ("stream-info", "Stream info", "List of streaminfo", |
|
179 G_PARAM_READABLE)); |
|
180 g_object_class_install_property (gobject_klass, ARG_STREAMINFO_VALUES, |
|
181 g_param_spec_value_array ("stream-info-value-array", |
|
182 "StreamInfo GValueArray", "value array of streaminfo", |
|
183 g_param_spec_object ("streaminfo", "StreamInfo", "Streaminfo object", |
|
184 GST_TYPE_STREAM_INFO, G_PARAM_READABLE), G_PARAM_READABLE)); |
|
185 g_object_class_install_property (gobject_klass, ARG_SOURCE, |
|
186 g_param_spec_object ("source", "Source", "Source element", |
|
187 GST_TYPE_ELEMENT, G_PARAM_READABLE)); |
|
188 |
|
189 g_object_class_install_property (gobject_klass, ARG_VIDEO, |
|
190 g_param_spec_int ("current-video", "Current video", |
|
191 "Currently playing video stream (-1 = none)", |
|
192 -1, G_MAXINT, -1, G_PARAM_READWRITE)); |
|
193 g_object_class_install_property (gobject_klass, ARG_AUDIO, |
|
194 g_param_spec_int ("current-audio", "Current audio", |
|
195 "Currently playing audio stream (-1 = none)", |
|
196 -1, G_MAXINT, -1, G_PARAM_READWRITE)); |
|
197 g_object_class_install_property (gobject_klass, ARG_TEXT, |
|
198 g_param_spec_int ("current-text", "Current text", |
|
199 "Currently playing text stream (-1 = none)", |
|
200 -1, G_MAXINT, -1, G_PARAM_READWRITE)); |
|
201 g_object_class_install_property (gobject_klass, ARG_SUBTITLE_ENCODING, |
|
202 g_param_spec_string ("subtitle-encoding", "subtitle encoding", |
|
203 "Encoding to assume if input subtitles are not in UTF-8 encoding. " |
|
204 "If not set, the GST_SUBTITLE_ENCODING environment variable will " |
|
205 "be checked for an encoding to use. If that is not set either, " |
|
206 "ISO-8859-15 will be assumed.", NULL, G_PARAM_READWRITE)); |
|
207 /** |
|
208 * GstPlayBin::connection-speed |
|
209 * |
|
210 * Network connection speed in kbps (0 = unknown) |
|
211 * |
|
212 * Since: 0.10.10 at gstplaybin.c, 0.10.15 moved to gstplaybasebin |
|
213 **/ |
|
214 g_object_class_install_property (gobject_klass, ARG_CONNECTION_SPEED, |
|
215 g_param_spec_uint ("connection-speed", "Connection Speed", |
|
216 "Network connection speed in kbps (0 = unknown)", |
|
217 0, G_MAXUINT, DEFAULT_CONNECTION_SPEED, G_PARAM_READWRITE)); |
|
218 |
|
219 GST_DEBUG_CATEGORY_INIT (gst_play_base_bin_debug, "playbasebin", 0, |
|
220 "playbasebin"); |
|
221 |
|
222 gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_base_bin_dispose); |
|
223 gobject_klass->finalize = GST_DEBUG_FUNCPTR (gst_play_base_bin_finalize); |
|
224 |
|
225 gstbin_klass->handle_message = |
|
226 GST_DEBUG_FUNCPTR (gst_play_base_bin_handle_message_func); |
|
227 |
|
228 gstelement_klass->change_state = |
|
229 GST_DEBUG_FUNCPTR (gst_play_base_bin_change_state); |
|
230 } |
|
231 |
|
232 static void |
|
233 gst_play_base_bin_init (GstPlayBaseBin * play_base_bin) |
|
234 { |
|
235 play_base_bin->uri = NULL; |
|
236 play_base_bin->suburi = NULL; |
|
237 play_base_bin->need_rebuild = TRUE; |
|
238 play_base_bin->is_stream = FALSE; |
|
239 play_base_bin->source = NULL; |
|
240 play_base_bin->decoders = NULL; |
|
241 play_base_bin->subtitle = NULL; |
|
242 play_base_bin->subencoding = NULL; |
|
243 play_base_bin->subtitle_elements = NULL; |
|
244 play_base_bin->sub_lock = g_mutex_new (); |
|
245 |
|
246 play_base_bin->group_lock = g_mutex_new (); |
|
247 play_base_bin->group_cond = g_cond_new (); |
|
248 |
|
249 play_base_bin->building_group = NULL; |
|
250 play_base_bin->queued_groups = NULL; |
|
251 |
|
252 play_base_bin->queue_size = DEFAULT_QUEUE_SIZE; |
|
253 play_base_bin->queue_threshold = DEFAULT_QUEUE_THRESHOLD; |
|
254 play_base_bin->queue_min_threshold = DEFAULT_QUEUE_MIN_THRESHOLD; |
|
255 play_base_bin->connection_speed = DEFAULT_CONNECTION_SPEED; |
|
256 } |
|
257 |
|
258 static void |
|
259 gst_play_base_bin_dispose (GObject * object) |
|
260 { |
|
261 GstPlayBaseBin *play_base_bin; |
|
262 |
|
263 play_base_bin = GST_PLAY_BASE_BIN (object); |
|
264 g_free (play_base_bin->uri); |
|
265 play_base_bin->uri = NULL; |
|
266 g_free (play_base_bin->suburi); |
|
267 play_base_bin->suburi = NULL; |
|
268 g_free (play_base_bin->subencoding); |
|
269 play_base_bin->subencoding = NULL; |
|
270 if (play_base_bin->subtitle_elements) { |
|
271 g_slist_free (play_base_bin->subtitle_elements); |
|
272 play_base_bin->subtitle_elements = NULL; |
|
273 } |
|
274 G_OBJECT_CLASS (parent_class)->dispose (object); |
|
275 } |
|
276 |
|
277 static void |
|
278 gst_play_base_bin_finalize (GObject * object) |
|
279 { |
|
280 GstPlayBaseBin *play_base_bin = GST_PLAY_BASE_BIN (object); |
|
281 |
|
282 g_mutex_free (play_base_bin->group_lock); |
|
283 g_cond_free (play_base_bin->group_cond); |
|
284 |
|
285 g_mutex_free (play_base_bin->sub_lock); |
|
286 |
|
287 G_OBJECT_CLASS (parent_class)->finalize (object); |
|
288 } |
|
289 |
|
290 /* |
|
291 * playbasebingroup stuff. |
|
292 */ |
|
293 |
|
294 static GstPlayBaseGroup * |
|
295 group_create (GstPlayBaseBin * play_base_bin) |
|
296 { |
|
297 GstPlayBaseGroup *group; |
|
298 |
|
299 group = g_new0 (GstPlayBaseGroup, 1); |
|
300 group->bin = play_base_bin; |
|
301 group->streaminfo_value_array = g_value_array_new (0); |
|
302 |
|
303 GST_DEBUG_OBJECT (play_base_bin, "created new group %p", group); |
|
304 |
|
305 return group; |
|
306 } |
|
307 |
|
308 /* |
|
309 * Gets the currently playing group. |
|
310 * |
|
311 * Callers must have group-lock held when calling this. |
|
312 */ |
|
313 |
|
314 static GstPlayBaseGroup * |
|
315 get_active_group (GstPlayBaseBin * play_base_bin) |
|
316 { |
|
317 GstPlayBaseGroup *group = NULL; |
|
318 |
|
319 if (play_base_bin->queued_groups) |
|
320 group = play_base_bin->queued_groups->data; |
|
321 |
|
322 return group; |
|
323 } |
|
324 |
|
325 /* |
|
326 * get the group used for discovering the different streams. |
|
327 * This function creates a group is there is none. |
|
328 * |
|
329 * Callers must have group-lock held when calling this. |
|
330 */ |
|
331 static GstPlayBaseGroup * |
|
332 get_building_group (GstPlayBaseBin * play_base_bin) |
|
333 { |
|
334 GstPlayBaseGroup *group; |
|
335 |
|
336 group = play_base_bin->building_group; |
|
337 if (group == NULL) { |
|
338 group = group_create (play_base_bin); |
|
339 play_base_bin->building_group = group; |
|
340 } |
|
341 |
|
342 return group; |
|
343 } |
|
344 |
|
345 /* |
|
346 * Callers must have lock held when calling this! |
|
347 */ |
|
348 |
|
349 static void |
|
350 group_destroy (GstPlayBaseGroup * group) |
|
351 { |
|
352 GstPlayBaseBin *play_base_bin = group->bin; |
|
353 gint n; |
|
354 |
|
355 GST_LOG ("removing group %p", group); |
|
356 |
|
357 /* remove the preroll queues */ |
|
358 for (n = 0; n < NUM_TYPES; n++) { |
|
359 GstElement *element = group->type[n].preroll; |
|
360 GstElement *fakesrc; |
|
361 GstElement *sel; |
|
362 const GList *item; |
|
363 |
|
364 if (!element) |
|
365 continue; |
|
366 |
|
367 sel = group->type[n].selector; |
|
368 |
|
369 /* remove any fakesrc elements for this preroll element */ |
|
370 for (item = sel->pads; item != NULL; item = item->next) { |
|
371 GstPad *pad = GST_PAD (item->data); |
|
372 guint sig_id; |
|
373 |
|
374 if (GST_PAD_DIRECTION (pad) != GST_PAD_SINK) |
|
375 continue; |
|
376 |
|
377 sig_id = |
|
378 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pad), "unlinked_id")); |
|
379 |
|
380 if (sig_id != 0) { |
|
381 GST_LOG ("removing unlink signal %s:%s", GST_DEBUG_PAD_NAME (pad)); |
|
382 g_signal_handler_disconnect (G_OBJECT (pad), sig_id); |
|
383 g_object_set_data (G_OBJECT (pad), "unlinked_id", GINT_TO_POINTER (0)); |
|
384 } |
|
385 |
|
386 fakesrc = (GstElement *) g_object_get_data (G_OBJECT (pad), "fakesrc"); |
|
387 if (fakesrc != NULL) { |
|
388 GST_LOG ("removing fakesrc from %s:%s", |
|
389 GST_PAD_NAME (pad), GST_ELEMENT_NAME (GST_PAD_PARENT (pad))); |
|
390 gst_element_set_state (fakesrc, GST_STATE_NULL); |
|
391 gst_bin_remove (GST_BIN_CAST (play_base_bin), fakesrc); |
|
392 } |
|
393 } |
|
394 |
|
395 /* if the group is currently being played, we have to remove the element |
|
396 * from the thread */ |
|
397 gst_element_set_state (element, GST_STATE_NULL); |
|
398 gst_element_set_state (group->type[n].selector, GST_STATE_NULL); |
|
399 |
|
400 GST_LOG ("removing preroll element %s", GST_ELEMENT_NAME (element)); |
|
401 |
|
402 gst_bin_remove (group->type[n].bin, element); |
|
403 gst_bin_remove (group->type[n].bin, group->type[n].selector); |
|
404 |
|
405 group->type[n].preroll = NULL; |
|
406 group->type[n].selector = NULL; |
|
407 group->type[n].bin = NULL; |
|
408 } |
|
409 |
|
410 /* free the streaminfo too */ |
|
411 g_list_foreach (group->streaminfo, (GFunc) g_object_unref, NULL); |
|
412 g_list_free (group->streaminfo); |
|
413 g_value_array_free (group->streaminfo_value_array); |
|
414 g_free (group); |
|
415 } |
|
416 |
|
417 /* |
|
418 * is called when the current building group is completely finished |
|
419 * and ready for playback |
|
420 * |
|
421 * This function grabs lock, so take care when calling. |
|
422 */ |
|
423 static void |
|
424 group_commit (GstPlayBaseBin * play_base_bin, gboolean fatal, gboolean subtitle) |
|
425 { |
|
426 GstPlayBaseGroup *group; |
|
427 gboolean had_active_group; |
|
428 gboolean res; |
|
429 |
|
430 GROUP_LOCK (play_base_bin); |
|
431 group = play_base_bin->building_group; |
|
432 had_active_group = (get_active_group (play_base_bin) != NULL); |
|
433 |
|
434 GST_DEBUG_OBJECT (play_base_bin, "commit group %p, had active %d", |
|
435 group, had_active_group); |
|
436 |
|
437 /* if an element signalled a no-more-pads after we stopped due |
|
438 * to preroll, the group is NULL. This is not an error */ |
|
439 if (group == NULL) { |
|
440 if (!fatal) { |
|
441 GROUP_UNLOCK (play_base_bin); |
|
442 return; |
|
443 } else { |
|
444 GST_DEBUG_OBJECT (play_base_bin, "Group loading failed, bailing out"); |
|
445 } |
|
446 } else { |
|
447 if (!subtitle) { |
|
448 gint n; |
|
449 |
|
450 GST_DEBUG_OBJECT (play_base_bin, "group %p done", group); |
|
451 |
|
452 play_base_bin->queued_groups = |
|
453 g_list_append (play_base_bin->queued_groups, group); |
|
454 |
|
455 play_base_bin->building_group = NULL; |
|
456 |
|
457 /* remove signals. We don't want anymore signals from the preroll |
|
458 * elements at this stage. */ |
|
459 for (n = 0; n < NUM_TYPES; n++) { |
|
460 GstElement *element = group->type[n].preroll; |
|
461 |
|
462 if (!element) |
|
463 continue; |
|
464 |
|
465 preroll_remove_overrun (element, play_base_bin); |
|
466 /* if overrun is removed, probe alse has to be removed */ |
|
467 queue_remove_probe (element, play_base_bin); |
|
468 } |
|
469 } else { |
|
470 /* this is a special subtitle bin, we don't commit the group but |
|
471 * mark the subtitles as detected before we signal. */ |
|
472 GST_DEBUG_OBJECT (play_base_bin, "marking subtitle bin as complete"); |
|
473 play_base_bin->subtitle_done = TRUE; |
|
474 } |
|
475 } |
|
476 |
|
477 GST_DEBUG_OBJECT (play_base_bin, "signal group done"); |
|
478 GROUP_SIGNAL (play_base_bin); |
|
479 GST_DEBUG_OBJECT (play_base_bin, "signaled group done"); |
|
480 |
|
481 if (!subtitle && !had_active_group) { |
|
482 if (!prepare_output (play_base_bin)) { |
|
483 GROUP_UNLOCK (play_base_bin); |
|
484 return; |
|
485 } |
|
486 |
|
487 setup_substreams (play_base_bin); |
|
488 GST_DEBUG_OBJECT (play_base_bin, "Emitting signal"); |
|
489 res = GST_PLAY_BASE_BIN_GET_CLASS (play_base_bin)-> |
|
490 setup_output_pads (play_base_bin, group); |
|
491 GST_DEBUG_OBJECT (play_base_bin, "done"); |
|
492 |
|
493 GROUP_UNLOCK (play_base_bin); |
|
494 |
|
495 g_object_notify (G_OBJECT (play_base_bin), "stream-info"); |
|
496 } else { |
|
497 GROUP_UNLOCK (play_base_bin); |
|
498 } |
|
499 } |
|
500 |
|
501 /* |
|
502 * check if there are streams in the group that are not muted |
|
503 * |
|
504 * Callers must have group-lock held when calling this. |
|
505 */ |
|
506 static gboolean |
|
507 group_is_muted (GstPlayBaseGroup * group) |
|
508 { |
|
509 gint n; |
|
510 |
|
511 for (n = 0; n < NUM_TYPES; n++) { |
|
512 if (group->type[n].preroll && !group->type[n].done) |
|
513 return FALSE; |
|
514 } |
|
515 |
|
516 return TRUE; |
|
517 } |
|
518 |
|
519 /* |
|
520 * Buffer/cache checking. |
|
521 */ |
|
522 |
|
523 static inline void |
|
524 fill_buffer (GstPlayBaseBin * play_base_bin, gint percent) |
|
525 { |
|
526 GST_DEBUG_OBJECT (play_base_bin, "buffering %d", percent); |
|
527 gst_element_post_message (GST_ELEMENT_CAST (play_base_bin), |
|
528 gst_message_new_buffering (GST_OBJECT_CAST (play_base_bin), percent)); |
|
529 } |
|
530 |
|
531 static gboolean |
|
532 check_queue_event (GstPad * pad, GstEvent * event, gpointer user_data) |
|
533 { |
|
534 GstElement *queue = GST_ELEMENT_CAST (user_data); |
|
535 |
|
536 switch (GST_EVENT_TYPE (event)) { |
|
537 case GST_EVENT_EOS: |
|
538 GST_DEBUG ("EOS event, mark EOS"); |
|
539 g_object_set_data (G_OBJECT (queue), "eos", "1"); |
|
540 break; |
|
541 case GST_EVENT_FLUSH_STOP: |
|
542 GST_DEBUG ("FLUSH_STOP event, remove EOS"); |
|
543 g_object_set_data (G_OBJECT (queue), "eos", NULL); |
|
544 break; |
|
545 default: |
|
546 GST_DEBUG ("uninteresting event %s", GST_EVENT_TYPE_NAME (event)); |
|
547 break; |
|
548 } |
|
549 return TRUE; |
|
550 } |
|
551 |
|
552 static gboolean |
|
553 check_queue (GstPad * pad, GstBuffer * data, gpointer user_data) |
|
554 { |
|
555 GstElement *queue = GST_ELEMENT_CAST (user_data); |
|
556 GstPlayBaseBin *play_base_bin = g_object_get_data (G_OBJECT (queue), "pbb"); |
|
557 guint64 level = 0; |
|
558 |
|
559 GST_DEBUG_OBJECT (queue, "check queue triggered"); |
|
560 |
|
561 g_object_get (G_OBJECT (queue), "current-level-time", &level, NULL); |
|
562 GST_DEBUG_OBJECT (play_base_bin, "Queue size: %" GST_TIME_FORMAT, |
|
563 GST_TIME_ARGS (level)); |
|
564 |
|
565 if (play_base_bin->queue_threshold > 0) { |
|
566 level = level * 99 / play_base_bin->queue_threshold; |
|
567 if (level > 99) |
|
568 level = 99; |
|
569 } else |
|
570 level = 99; |
|
571 |
|
572 fill_buffer (play_base_bin, level); |
|
573 |
|
574 /* continue! */ |
|
575 return TRUE; |
|
576 } |
|
577 |
|
578 /* If a queue overruns and we are buffer in streaming mode (we have a min-time) |
|
579 * we can potentially create a deadlock when: |
|
580 * |
|
581 * 1) the max-bytes is hit and |
|
582 * 2) the min-time is not hit. |
|
583 * |
|
584 * We recover from this situation in this callback by |
|
585 * setting the max-bytes to unlimited if we see that there is |
|
586 * a current-time-level (which means some sort of timestamping is |
|
587 * done). |
|
588 */ |
|
589 static void |
|
590 queue_deadlock_check (GstElement * queue, GstPlayBaseBin * play_base_bin) |
|
591 { |
|
592 guint64 time, min_time; |
|
593 guint bytes; |
|
594 |
|
595 GST_DEBUG_OBJECT (play_base_bin, "overrun signal received from queue %s", |
|
596 GST_ELEMENT_NAME (queue)); |
|
597 |
|
598 /* figure out where we are */ |
|
599 g_object_get (G_OBJECT (queue), "current-level-time", &time, |
|
600 "current-level-bytes", &bytes, "min-threshold-time", &min_time, NULL); |
|
601 |
|
602 GST_DEBUG_OBJECT (play_base_bin, "streaming mode, queue %s current %" |
|
603 GST_TIME_FORMAT ", min %" GST_TIME_FORMAT |
|
604 ", bytes %d", GST_ELEMENT_NAME (queue), |
|
605 GST_TIME_ARGS (time), GST_TIME_ARGS (min_time), bytes); |
|
606 |
|
607 /* if the bytes in the queue represent time, we disable bytes |
|
608 * overrun checking to not cause deadlocks. |
|
609 */ |
|
610 if (bytes && time != 0 && time < min_time) { |
|
611 GST_DEBUG_OBJECT (play_base_bin, |
|
612 "possible deadlock found, removing byte limit"); |
|
613 |
|
614 /* queue knows about time but is filled with bytes that do |
|
615 * not represent min-threshold time, disable bytes checking so |
|
616 * the queue can grow some more. */ |
|
617 g_object_set (G_OBJECT (queue), "max-size-bytes", 0, NULL); |
|
618 |
|
619 /* bytes limit is removed, we cannot deadlock anymore */ |
|
620 g_signal_handlers_disconnect_by_func (queue, |
|
621 (gpointer) queue_deadlock_check, play_base_bin); |
|
622 } else { |
|
623 GST_DEBUG_OBJECT (play_base_bin, "no deadlock"); |
|
624 } |
|
625 } |
|
626 |
|
627 static void |
|
628 queue_remove_probe (GstElement * queue, GstPlayBaseBin * play_base_bin) |
|
629 { |
|
630 gpointer data; |
|
631 GstPad *sinkpad; |
|
632 |
|
633 data = g_object_get_data (G_OBJECT (queue), "probe"); |
|
634 sinkpad = gst_element_get_pad (queue, "sink"); |
|
635 |
|
636 if (data) { |
|
637 GST_DEBUG_OBJECT (play_base_bin, |
|
638 "Removing buffer probe from pad %s:%s (%p)", |
|
639 GST_DEBUG_PAD_NAME (sinkpad), sinkpad); |
|
640 |
|
641 g_object_set_data (G_OBJECT (queue), "probe", NULL); |
|
642 gst_pad_remove_buffer_probe (sinkpad, GPOINTER_TO_INT (data)); |
|
643 } else { |
|
644 GST_DEBUG_OBJECT (play_base_bin, |
|
645 "No buffer probe to remove from %s:%s (%p)", |
|
646 GST_DEBUG_PAD_NAME (sinkpad), sinkpad); |
|
647 } |
|
648 gst_object_unref (sinkpad); |
|
649 } |
|
650 |
|
651 /* Used for time-based buffering in streaming mode and is called when a queue |
|
652 * emits the running signal. This means that the high watermark threshold is |
|
653 * reached and the buffering is completed. */ |
|
654 static void |
|
655 queue_threshold_reached (GstElement * queue, GstPlayBaseBin * play_base_bin) |
|
656 { |
|
657 GstPlayBaseGroup *group; |
|
658 gpointer data; |
|
659 gint n; |
|
660 |
|
661 GST_DEBUG_OBJECT (play_base_bin, "running signal received from queue %s", |
|
662 GST_ELEMENT_NAME (queue)); |
|
663 |
|
664 /* we disconnect the signal so that we don't get called for every buffer. */ |
|
665 g_signal_handlers_disconnect_by_func (queue, |
|
666 (gpointer) queue_threshold_reached, play_base_bin); |
|
667 |
|
668 data = g_object_get_data (G_OBJECT (queue), "eos"); |
|
669 if (data) { |
|
670 GST_DEBUG_OBJECT (play_base_bin, "disable min threshold time, we are EOS"); |
|
671 g_object_set (queue, "min-threshold-time", (guint64) 0, NULL); |
|
672 } else { |
|
673 /* now place the limits at the low threshold. When we hit this limit, the |
|
674 * underrun signal will be called. The underrun signal is always connected. */ |
|
675 GST_DEBUG_OBJECT (play_base_bin, |
|
676 "setting min threshold time to %" G_GUINT64_FORMAT, |
|
677 play_base_bin->queue_min_threshold); |
|
678 g_object_set (queue, "min-threshold-time", |
|
679 play_base_bin->queue_min_threshold, NULL); |
|
680 } |
|
681 |
|
682 GROUP_LOCK (play_base_bin); |
|
683 group = get_active_group (play_base_bin); |
|
684 if (!group) { |
|
685 GROUP_UNLOCK (play_base_bin); |
|
686 return; |
|
687 } |
|
688 |
|
689 /* we remove the probe now because we don't need it anymore to give progress |
|
690 * about the buffering. */ |
|
691 for (n = 0; n < NUM_TYPES; n++) { |
|
692 GstElement *element = group->type[n].preroll; |
|
693 |
|
694 if (!element) |
|
695 continue; |
|
696 |
|
697 queue_remove_probe (element, play_base_bin); |
|
698 } |
|
699 |
|
700 GROUP_UNLOCK (play_base_bin); |
|
701 |
|
702 /* we post a 100% buffering message to notify the app that buffering is |
|
703 * completed and playback can start/continue */ |
|
704 if (play_base_bin->is_stream) |
|
705 fill_buffer (play_base_bin, 100); |
|
706 } |
|
707 |
|
708 /* this signal will be fired when one of the queues with raw |
|
709 * data is filled. This means that the group building stage is over |
|
710 * and playback of the new queued group should start. This is a rather unusual |
|
711 * situation because normally the group is commited when the "no_more_pads" |
|
712 * signal is fired. |
|
713 */ |
|
714 static void |
|
715 queue_overrun (GstElement * queue, GstPlayBaseBin * play_base_bin) |
|
716 { |
|
717 GST_DEBUG_OBJECT (play_base_bin, "queue %s overrun", |
|
718 GST_ELEMENT_NAME (queue)); |
|
719 |
|
720 preroll_remove_overrun (queue, play_base_bin); |
|
721 |
|
722 group_commit (play_base_bin, FALSE, |
|
723 GST_OBJECT_PARENT (GST_OBJECT_CAST (queue)) == |
|
724 GST_OBJECT_CAST (play_base_bin->subtitle)); |
|
725 |
|
726 /* notify end of buffering */ |
|
727 queue_threshold_reached (queue, play_base_bin); |
|
728 } |
|
729 |
|
730 /* this signal is only added when in streaming mode to catch underruns |
|
731 */ |
|
732 static void |
|
733 queue_out_of_data (GstElement * queue, GstPlayBaseBin * play_base_bin) |
|
734 { |
|
735 GST_DEBUG_OBJECT (play_base_bin, "underrun signal received from queue %s", |
|
736 GST_ELEMENT_NAME (queue)); |
|
737 |
|
738 /* On underrun, we want to temoprarily pause playback, set a "min-size" |
|
739 * threshold and wait for the running signal and then play again. |
|
740 * |
|
741 * This signal could never be called because the queue max-size limits are set |
|
742 * too low. We take care of this possible deadlock in the the overrun signal |
|
743 * handler. */ |
|
744 g_signal_connect (G_OBJECT (queue), "pushing", |
|
745 G_CALLBACK (queue_threshold_reached), play_base_bin); |
|
746 GST_DEBUG_OBJECT (play_base_bin, |
|
747 "setting min threshold time to %" G_GUINT64_FORMAT, |
|
748 (guint64) play_base_bin->queue_threshold); |
|
749 g_object_set (queue, "min-threshold-time", |
|
750 (guint64) play_base_bin->queue_threshold, NULL); |
|
751 |
|
752 /* re-connect probe, this will fire feedback about the percentage that we |
|
753 * buffered and is posted in the BUFFERING message. */ |
|
754 if (!g_object_get_data (G_OBJECT (queue), "probe")) { |
|
755 GstPad *sinkpad; |
|
756 guint id; |
|
757 |
|
758 sinkpad = gst_element_get_pad (queue, "sink"); |
|
759 id = gst_pad_add_buffer_probe (sinkpad, G_CALLBACK (check_queue), queue); |
|
760 g_object_set_data (G_OBJECT (queue), "probe", GINT_TO_POINTER (id)); |
|
761 GST_DEBUG_OBJECT (play_base_bin, |
|
762 "Re-attaching buffering probe to pad %s:%s %p", |
|
763 GST_DEBUG_PAD_NAME (sinkpad), sinkpad); |
|
764 gst_object_unref (sinkpad); |
|
765 |
|
766 fill_buffer (play_base_bin, 0); |
|
767 } |
|
768 } |
|
769 |
|
770 /* |
|
771 * generate a preroll element which is simply a queue. While there |
|
772 * are still dynamic elements in the pipeline, we wait for one |
|
773 * of the queues to fill. The assumption is that all the dynamic |
|
774 * streams will be detected by that time. |
|
775 * |
|
776 * Callers must have the group-lock held when calling this. |
|
777 */ |
|
778 static void |
|
779 gen_preroll_element (GstPlayBaseBin * play_base_bin, |
|
780 GstPlayBaseGroup * group, GstStreamType type, GstPad * pad, |
|
781 GstStreamInfo * info) |
|
782 { |
|
783 GstElement *selector, *preroll; |
|
784 gchar *name, *padname; |
|
785 const gchar *prename; |
|
786 guint overrun_sig; |
|
787 GstPad *preroll_pad; |
|
788 GstBin *target; |
|
789 GstState state; |
|
790 |
|
791 if (type == GST_STREAM_TYPE_VIDEO) |
|
792 prename = "video"; |
|
793 else if (type == GST_STREAM_TYPE_TEXT) |
|
794 prename = "text"; |
|
795 else if (type == GST_STREAM_TYPE_AUDIO) |
|
796 prename = "audio"; |
|
797 else |
|
798 g_return_if_reached (); |
|
799 |
|
800 /* create stream selector */ |
|
801 selector = g_object_new (GST_TYPE_STREAM_SELECTOR, NULL); |
|
802 padname = gst_pad_get_name (pad); |
|
803 name = g_strdup_printf ("selector_%s_%s", prename, padname); |
|
804 gst_object_set_name (GST_OBJECT_CAST (selector), name); |
|
805 g_free (name); |
|
806 |
|
807 /* create preroll queue */ |
|
808 name = g_strdup_printf ("preroll_%s_%s", prename, padname); |
|
809 preroll = gst_element_factory_make ("queue", name); |
|
810 g_free (name); |
|
811 g_free (padname); |
|
812 |
|
813 /* for buffering of raw data we ideally want to buffer a |
|
814 * very small amount of buffers since the memory used by |
|
815 * this raw data can be enormously huge. |
|
816 * |
|
817 * We use an upper limit of typically a few seconds here but |
|
818 * cap in case no timestamps are set on the raw data (bad!). |
|
819 * |
|
820 * FIXME: we abuse this buffer to do network buffering since |
|
821 * we can then easily do time-based buffering. The better |
|
822 * solution would be to add a specific network queue right |
|
823 * after the source that measures the datarate and scales this |
|
824 * queue of encoded data instead. |
|
825 */ |
|
826 g_object_set (G_OBJECT (preroll), |
|
827 "max-size-buffers", 0, "max-size-bytes", |
|
828 ((type == GST_STREAM_TYPE_VIDEO) ? 25 : 2) * 1024 * 1024, |
|
829 "max-size-time", play_base_bin->queue_size, NULL); |
|
830 |
|
831 /* the overrun signal is always attached and serves two purposes: |
|
832 * |
|
833 * 1) when we are building a group and the overrun is called, we commit the |
|
834 * group. The reason being that if we fill the entire queue without a |
|
835 * normal group commit (with _no_more_pads()) we can assume the |
|
836 * audio/video is completely wacked or the element just does not know when |
|
837 * it is ready with all the pads (mpeg). |
|
838 * 2) When we are doing network buffering, we keep track of low/high |
|
839 * watermarks in the queue. It is possible that we set the high watermark |
|
840 * higher than the max-size limits to trigger an overrun. In this case we |
|
841 * will never get a running signal but we can use the overrun signal to |
|
842 * detect this deadlock and correct it. |
|
843 */ |
|
844 overrun_sig = g_signal_connect (G_OBJECT (preroll), "overrun", |
|
845 G_CALLBACK (queue_overrun), play_base_bin); |
|
846 |
|
847 /* keep a ref to the signal id so that we can disconnect the signal callback |
|
848 * when we are done with the preroll */ |
|
849 g_object_set_data (G_OBJECT (preroll), "overrun_signal_id", |
|
850 GINT_TO_POINTER (overrun_sig)); |
|
851 |
|
852 if (play_base_bin->is_stream && |
|
853 ((type == GST_STREAM_TYPE_VIDEO && |
|
854 group->type[GST_STREAM_TYPE_AUDIO - 1].npads == 0) || |
|
855 (type == GST_STREAM_TYPE_AUDIO && |
|
856 group->type[GST_STREAM_TYPE_VIDEO - 1].npads == 0))) { |
|
857 GstPad *sinkpad; |
|
858 guint id; |
|
859 |
|
860 /* catch deadlocks when we are network buffering in time but the max-limit |
|
861 * in bytes is hit. */ |
|
862 g_signal_connect (G_OBJECT (preroll), "overrun", |
|
863 G_CALLBACK (queue_deadlock_check), play_base_bin); |
|
864 |
|
865 /* attach pointer to playbasebin */ |
|
866 g_object_set_data (G_OBJECT (preroll), "pbb", play_base_bin); |
|
867 |
|
868 /* give updates on queue size */ |
|
869 sinkpad = gst_element_get_pad (preroll, "sink"); |
|
870 id = gst_pad_add_buffer_probe (sinkpad, G_CALLBACK (check_queue), preroll); |
|
871 GST_DEBUG_OBJECT (play_base_bin, "Attaching probe to pad %s:%s (%p)", |
|
872 GST_DEBUG_PAD_NAME (sinkpad), sinkpad); |
|
873 gst_object_unref (sinkpad); |
|
874 g_object_set_data (G_OBJECT (preroll), "probe", GINT_TO_POINTER (id)); |
|
875 |
|
876 /* catch eos and flush events so that we can ignore underruns */ |
|
877 id = gst_pad_add_event_probe (sinkpad, G_CALLBACK (check_queue_event), |
|
878 preroll); |
|
879 g_object_set_data (G_OBJECT (preroll), "eos_probe", GINT_TO_POINTER (id)); |
|
880 |
|
881 /* When we connect this queue, it will start running and immediatly |
|
882 * fire an underrun. */ |
|
883 g_signal_connect (G_OBJECT (preroll), "underrun", |
|
884 G_CALLBACK (queue_out_of_data), play_base_bin); |
|
885 /* configure threshold and callbacks */ |
|
886 queue_out_of_data (preroll, play_base_bin); |
|
887 } |
|
888 |
|
889 /* listen for EOS so we can switch groups when one ended. */ |
|
890 preroll_pad = gst_element_get_pad (preroll, "src"); |
|
891 gst_pad_add_event_probe (preroll_pad, G_CALLBACK (probe_triggered), info); |
|
892 gst_object_unref (preroll_pad); |
|
893 |
|
894 /* add to group list */ |
|
895 group->type[type - 1].selector = selector; |
|
896 group->type[type - 1].preroll = preroll; |
|
897 |
|
898 /* figure out where the preroll element should go */ |
|
899 if (type == GST_STREAM_TYPE_TEXT && play_base_bin->subtitle) |
|
900 target = GST_BIN_CAST (play_base_bin->subtitle); |
|
901 else |
|
902 target = GST_BIN_CAST (play_base_bin); |
|
903 |
|
904 group->type[type - 1].bin = target; |
|
905 gst_bin_add (target, selector); |
|
906 gst_bin_add (target, preroll); |
|
907 |
|
908 gst_element_link (selector, preroll); |
|
909 |
|
910 /* figure out target state and set */ |
|
911 state = (GST_STATE (play_base_bin) == GST_STATE_PLAYING ? |
|
912 GST_STATE_PLAYING : GST_STATE_PAUSED); |
|
913 |
|
914 gst_element_set_state (selector, state); |
|
915 gst_element_set_state (preroll, state); |
|
916 } |
|
917 |
|
918 static void |
|
919 preroll_remove_overrun (GstElement * element, GstPlayBaseBin * play_base_bin) |
|
920 { |
|
921 guint overrun_sig; |
|
922 GObject *obj = G_OBJECT (element); |
|
923 |
|
924 overrun_sig = GPOINTER_TO_INT (g_object_get_data (obj, "overrun_signal_id")); |
|
925 if (overrun_sig) { |
|
926 GST_LOG_OBJECT (play_base_bin, "removing preroll signal %s", |
|
927 GST_ELEMENT_NAME (element)); |
|
928 g_signal_handler_disconnect (obj, overrun_sig); |
|
929 /* We have disconnected this signal, remove the signal_id from the object |
|
930 * data */ |
|
931 g_object_set_data (obj, "overrun_signal_id", NULL); |
|
932 } |
|
933 } |
|
934 |
|
935 static void |
|
936 remove_groups (GstPlayBaseBin * play_base_bin) |
|
937 { |
|
938 GROUP_LOCK (play_base_bin); |
|
939 |
|
940 /* first destroy the group we were building if any */ |
|
941 if (play_base_bin->building_group) { |
|
942 group_destroy (play_base_bin->building_group); |
|
943 play_base_bin->building_group = NULL; |
|
944 } |
|
945 |
|
946 /* remove the queued groups */ |
|
947 g_list_foreach (play_base_bin->queued_groups, (GFunc) group_destroy, NULL); |
|
948 g_list_free (play_base_bin->queued_groups); |
|
949 play_base_bin->queued_groups = NULL; |
|
950 |
|
951 /* clear subs */ |
|
952 if (play_base_bin->subtitle) { |
|
953 gst_element_set_state (play_base_bin->subtitle, GST_STATE_NULL); |
|
954 gst_bin_remove (GST_BIN_CAST (play_base_bin), play_base_bin->subtitle); |
|
955 play_base_bin->subtitle = NULL; |
|
956 } |
|
957 |
|
958 GROUP_UNLOCK (play_base_bin); |
|
959 } |
|
960 |
|
961 /* |
|
962 * Add/remove a single stream to current building group. |
|
963 * |
|
964 * Must be called with group-lock held. |
|
965 */ |
|
966 static void |
|
967 add_stream (GstPlayBaseGroup * group, GstStreamInfo * info) |
|
968 { |
|
969 GValue v = { 0, }; |
|
970 GST_DEBUG ("add stream to group %p", group); |
|
971 |
|
972 /* keep ref to the group */ |
|
973 g_object_set_data (G_OBJECT (info), "group", group); |
|
974 |
|
975 g_value_init (&v, G_TYPE_OBJECT); |
|
976 g_value_set_object (&v, info); |
|
977 g_value_array_append (group->streaminfo_value_array, &v); |
|
978 g_value_unset (&v); |
|
979 group->streaminfo = g_list_append (group->streaminfo, info); |
|
980 if (info->type > 0 && info->type <= NUM_TYPES) { |
|
981 group->type[info->type - 1].npads++; |
|
982 } |
|
983 } |
|
984 |
|
985 static gboolean |
|
986 string_arr_has_str (const gchar * values[], const gchar * value) |
|
987 { |
|
988 if (values && value) { |
|
989 while (*values != NULL) { |
|
990 if (strcmp (value, *values) == 0) |
|
991 return TRUE; |
|
992 ++values; |
|
993 } |
|
994 } |
|
995 return FALSE; |
|
996 } |
|
997 |
|
998 /* mime types we are not handling on purpose right now, don't post a |
|
999 * missing-plugin message for these */ |
|
1000 static const gchar *blacklisted_mimes[] = { |
|
1001 "video/x-dvd-subpicture", NULL |
|
1002 }; |
|
1003 |
|
1004 #define IS_BLACKLISTED_MIME(type) (string_arr_has_str(blacklisted_mimes,type)) |
|
1005 |
|
1006 static void |
|
1007 gst_play_base_bin_handle_message_func (GstBin * bin, GstMessage * msg) |
|
1008 { |
|
1009 if (gst_is_missing_plugin_message (msg)) { |
|
1010 gchar *detail; |
|
1011 guint i; |
|
1012 |
|
1013 detail = gst_missing_plugin_message_get_installer_detail (msg); |
|
1014 for (i = 0; detail != NULL && blacklisted_mimes[i] != NULL; ++i) { |
|
1015 if (strstr (detail, "|decoder-") && strstr (detail, blacklisted_mimes[i])) { |
|
1016 GST_LOG_OBJECT (bin, "suppressing message %" GST_PTR_FORMAT, msg); |
|
1017 gst_message_unref (msg); |
|
1018 g_free (detail); |
|
1019 return; |
|
1020 } |
|
1021 } |
|
1022 g_free (detail); |
|
1023 } |
|
1024 GST_BIN_CLASS (parent_class)->handle_message (bin, msg); |
|
1025 } |
|
1026 |
|
1027 /* |
|
1028 * signal fired when an unknown stream is found. We create a new |
|
1029 * UNKNOWN streaminfo object. |
|
1030 */ |
|
1031 static void |
|
1032 unknown_type (GstElement * element, GstPad * pad, GstCaps * caps, |
|
1033 GstPlayBaseBin * play_base_bin) |
|
1034 { |
|
1035 const gchar *type_name; |
|
1036 GstStreamInfo *info; |
|
1037 GstPlayBaseGroup *group; |
|
1038 |
|
1039 type_name = gst_structure_get_name (gst_caps_get_structure (caps, 0)); |
|
1040 if (type_name && !IS_BLACKLISTED_MIME (type_name)) { |
|
1041 gchar *capsstr; |
|
1042 |
|
1043 capsstr = gst_caps_to_string (caps); |
|
1044 GST_DEBUG_OBJECT (play_base_bin, "don't know how to handle %s", capsstr); |
|
1045 /* FIXME, g_message() ? */ |
|
1046 g_message ("don't know how to handle %s", capsstr); |
|
1047 g_free (capsstr); |
|
1048 } else { |
|
1049 /* don't spew stuff to the terminal or send message if it's blacklisted */ |
|
1050 GST_DEBUG_OBJECT (play_base_bin, "media type %s not handled on purpose, " |
|
1051 "not posting a missing-plugin message on the bus", type_name); |
|
1052 } |
|
1053 |
|
1054 GROUP_LOCK (play_base_bin); |
|
1055 |
|
1056 group = get_building_group (play_base_bin); |
|
1057 |
|
1058 /* add the stream to the list */ |
|
1059 info = gst_stream_info_new (GST_OBJECT_CAST (pad), GST_STREAM_TYPE_UNKNOWN, |
|
1060 NULL, caps); |
|
1061 info->origin = GST_OBJECT_CAST (pad); |
|
1062 add_stream (group, info); |
|
1063 |
|
1064 GROUP_UNLOCK (play_base_bin); |
|
1065 } |
|
1066 |
|
1067 /* add a streaminfo that indicates that the stream is handled by the |
|
1068 * given element. This usually means that a stream without actual data is |
|
1069 * produced but one that is sunken by an element. Examples of this are: |
|
1070 * cdaudio, a hardware decoder/sink, dvd meta bins etc... |
|
1071 */ |
|
1072 static void |
|
1073 add_element_stream (GstElement * element, GstPlayBaseBin * play_base_bin) |
|
1074 { |
|
1075 GstStreamInfo *info; |
|
1076 GstPlayBaseGroup *group; |
|
1077 |
|
1078 GROUP_LOCK (play_base_bin); |
|
1079 |
|
1080 group = get_building_group (play_base_bin); |
|
1081 |
|
1082 /* add the stream to the list */ |
|
1083 info = |
|
1084 gst_stream_info_new (GST_OBJECT_CAST (element), GST_STREAM_TYPE_ELEMENT, |
|
1085 NULL, NULL); |
|
1086 info->origin = GST_OBJECT_CAST (element); |
|
1087 add_stream (group, info); |
|
1088 |
|
1089 GROUP_UNLOCK (play_base_bin); |
|
1090 } |
|
1091 |
|
1092 /* when the decoder element signals that no more pads will be generated, we |
|
1093 * can commit the current group. |
|
1094 */ |
|
1095 static void |
|
1096 no_more_pads_full (GstElement * element, gboolean subs, |
|
1097 GstPlayBaseBin * play_base_bin) |
|
1098 { |
|
1099 /* setup phase */ |
|
1100 GST_DEBUG_OBJECT (element, "no more pads, %d pending", |
|
1101 play_base_bin->pending); |
|
1102 |
|
1103 /* nothing pending, we can exit */ |
|
1104 if (play_base_bin->pending == 0) |
|
1105 return; |
|
1106 |
|
1107 /* the object has no pending no_more_pads */ |
|
1108 if (!g_object_get_data (G_OBJECT (element), "pending")) |
|
1109 return; |
|
1110 |
|
1111 g_object_set_data (G_OBJECT (element), "pending", NULL); |
|
1112 |
|
1113 play_base_bin->pending--; |
|
1114 |
|
1115 GST_DEBUG_OBJECT (element, "remove pending, now %d pending", |
|
1116 play_base_bin->pending); |
|
1117 |
|
1118 if (play_base_bin->pending == 0) { |
|
1119 /* we can commit this group for playback now */ |
|
1120 group_commit (play_base_bin, play_base_bin->is_stream, subs); |
|
1121 } |
|
1122 } |
|
1123 |
|
1124 static void |
|
1125 no_more_pads (GstElement * element, GstPlayBaseBin * play_base_bin) |
|
1126 { |
|
1127 no_more_pads_full (element, FALSE, play_base_bin); |
|
1128 } |
|
1129 |
|
1130 static void |
|
1131 sub_no_more_pads (GstElement * element, GstPlayBaseBin * play_base_bin) |
|
1132 { |
|
1133 no_more_pads_full (element, TRUE, play_base_bin); |
|
1134 } |
|
1135 |
|
1136 static void |
|
1137 source_no_more_pads (GstElement * element, GstPlayBaseBin * bin) |
|
1138 { |
|
1139 GST_DEBUG_OBJECT (bin, "No more pads in source element %s.", |
|
1140 GST_ELEMENT_NAME (element)); |
|
1141 |
|
1142 g_signal_handler_disconnect (G_OBJECT (element), bin->src_np_sig_id); |
|
1143 bin->src_np_sig_id = 0; |
|
1144 g_signal_handler_disconnect (G_OBJECT (element), bin->src_nmp_sig_id); |
|
1145 bin->src_nmp_sig_id = 0; |
|
1146 |
|
1147 no_more_pads_full (element, FALSE, bin); |
|
1148 } |
|
1149 |
|
1150 static gboolean |
|
1151 probe_triggered (GstPad * pad, GstEvent * event, gpointer user_data) |
|
1152 { |
|
1153 GstPlayBaseGroup *group; |
|
1154 GstPlayBaseBin *play_base_bin; |
|
1155 GstStreamInfo *info; |
|
1156 gboolean res; |
|
1157 GstEventType type; |
|
1158 |
|
1159 type = GST_EVENT_TYPE (event); |
|
1160 |
|
1161 GST_LOG ("probe triggered, (%d) %s", type, gst_event_type_get_name (type)); |
|
1162 |
|
1163 /* we only care about EOS */ |
|
1164 if (type != GST_EVENT_EOS) |
|
1165 return TRUE; |
|
1166 |
|
1167 info = GST_STREAM_INFO (user_data); |
|
1168 group = (GstPlayBaseGroup *) g_object_get_data (G_OBJECT (info), "group"); |
|
1169 play_base_bin = group->bin; |
|
1170 |
|
1171 if (type == GST_EVENT_EOS) { |
|
1172 gint num_groups = 0; |
|
1173 gboolean have_left; |
|
1174 |
|
1175 GST_DEBUG_OBJECT (play_base_bin, "probe got EOS in group %p", group); |
|
1176 |
|
1177 GROUP_LOCK (play_base_bin); |
|
1178 |
|
1179 /* mute this stream */ |
|
1180 g_object_set (G_OBJECT (info), "mute", TRUE, NULL); |
|
1181 if (info->type > 0 && info->type <= NUM_TYPES) |
|
1182 group->type[info->type - 1].done = TRUE; |
|
1183 |
|
1184 /* see if we have some more groups left to play */ |
|
1185 num_groups = g_list_length (play_base_bin->queued_groups); |
|
1186 if (play_base_bin->building_group) |
|
1187 num_groups++; |
|
1188 have_left = (num_groups > 1); |
|
1189 |
|
1190 /* see if the complete group is muted */ |
|
1191 if (!group_is_muted (group)) { |
|
1192 /* group is not completely muted, we remove the EOS event |
|
1193 * and continue, eventually the other streams will be EOSed and |
|
1194 * we can switch out this group. */ |
|
1195 GST_DEBUG ("group %p not completely muted", group); |
|
1196 |
|
1197 GROUP_UNLOCK (play_base_bin); |
|
1198 |
|
1199 /* remove the EOS if we have something left */ |
|
1200 return !have_left; |
|
1201 } |
|
1202 |
|
1203 if (have_left) { |
|
1204 /* ok, get rid of the current group then */ |
|
1205 //group_destroy (group); |
|
1206 /* removing the current group brings the next group |
|
1207 * active */ |
|
1208 play_base_bin->queued_groups = |
|
1209 g_list_remove (play_base_bin->queued_groups, group); |
|
1210 /* and wait for the next one to be ready */ |
|
1211 while (!play_base_bin->queued_groups) { |
|
1212 GROUP_WAIT (play_base_bin); |
|
1213 } |
|
1214 group = play_base_bin->queued_groups->data; |
|
1215 |
|
1216 /* now activate the next one */ |
|
1217 setup_substreams (play_base_bin); |
|
1218 GST_DEBUG ("switching to next group %p - emitting signal", group); |
|
1219 /* and signal the new group */ |
|
1220 res = GST_PLAY_BASE_BIN_GET_CLASS (play_base_bin)-> |
|
1221 setup_output_pads (play_base_bin, group); |
|
1222 |
|
1223 GROUP_UNLOCK (play_base_bin); |
|
1224 |
|
1225 g_object_notify (G_OBJECT (play_base_bin), "stream-info"); |
|
1226 |
|
1227 /* get rid of the EOS event */ |
|
1228 return FALSE; |
|
1229 } else { |
|
1230 GROUP_UNLOCK (play_base_bin); |
|
1231 GST_LOG ("Last group done, EOS"); |
|
1232 } |
|
1233 } |
|
1234 |
|
1235 return TRUE; |
|
1236 } |
|
1237 |
|
1238 /* This function will be called when the sinkpad of the preroll element |
|
1239 * is unlinked, we have to connect something to the sinkpad or else the |
|
1240 * state change will fail.. |
|
1241 */ |
|
1242 static void |
|
1243 preroll_unlinked (GstPad * pad, GstPad * peerpad, |
|
1244 GstPlayBaseBin * play_base_bin) |
|
1245 { |
|
1246 GstElement *fakesrc; |
|
1247 guint sig_id; |
|
1248 GstPad *srcpad; |
|
1249 |
|
1250 /* make a fakesrc that will just emit one EOS */ |
|
1251 fakesrc = gst_element_factory_make ("fakesrc", NULL); |
|
1252 g_object_set (G_OBJECT (fakesrc), "num_buffers", 0, NULL); |
|
1253 |
|
1254 GST_DEBUG ("patching unlinked pad %s:%s", GST_DEBUG_PAD_NAME (pad)); |
|
1255 |
|
1256 srcpad = gst_element_get_pad (fakesrc, "src"); |
|
1257 gst_bin_add (GST_BIN_CAST (play_base_bin), fakesrc); |
|
1258 gst_pad_link (srcpad, pad); |
|
1259 gst_object_unref (srcpad); |
|
1260 |
|
1261 /* keep track of these patch elements */ |
|
1262 g_object_set_data (G_OBJECT (pad), "fakesrc", fakesrc); |
|
1263 |
|
1264 /* now unlink the unlinked signal so that it is not called again when |
|
1265 * we destroy the queue */ |
|
1266 sig_id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (pad), "unlinked_id")); |
|
1267 if (sig_id != 0) { |
|
1268 g_signal_handler_disconnect (G_OBJECT (pad), sig_id); |
|
1269 g_object_set_data (G_OBJECT (pad), "unlinked_id", GINT_TO_POINTER (0)); |
|
1270 } |
|
1271 } |
|
1272 |
|
1273 /* Mute stream on first data - for header-is-in-stream-stuff |
|
1274 * (vorbis, ogmtext). */ |
|
1275 static gboolean |
|
1276 mute_stream (GstPad * pad, GstBuffer * buf, gpointer data) |
|
1277 { |
|
1278 GstStreamInfo *info = GST_STREAM_INFO (data); |
|
1279 guint id; |
|
1280 |
|
1281 GST_DEBUG ("mute stream triggered"); |
|
1282 |
|
1283 g_object_set (G_OBJECT (info), "mute", TRUE, NULL); |
|
1284 id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info), "mute_probe")); |
|
1285 g_object_set_data (G_OBJECT (info), "mute_probe", NULL); |
|
1286 if (id > 0) |
|
1287 gst_pad_remove_buffer_probe (GST_PAD_CAST (info->object), id); |
|
1288 |
|
1289 /* no data */ |
|
1290 return FALSE; |
|
1291 } |
|
1292 |
|
1293 /* Eat data. */ |
|
1294 static gboolean |
|
1295 silence_stream (GstPad * pad, GstMiniObject * data, gpointer user_data) |
|
1296 { |
|
1297 GST_DEBUG ("silence stream triggered"); |
|
1298 |
|
1299 /* no data */ |
|
1300 return FALSE; |
|
1301 } |
|
1302 |
|
1303 /* Called by the signal handlers when a decodebin (main or subtitle) has |
|
1304 * found a new raw pad. We create a preroll element if needed and the |
|
1305 * appropriate streaminfo. Commits the group if there will be no more pads |
|
1306 * from decodebin */ |
|
1307 static void |
|
1308 new_decoded_pad_full (GstElement * element, GstPad * pad, gboolean last, |
|
1309 GstPlayBaseBin * play_base_bin, gboolean is_subs) |
|
1310 { |
|
1311 GstStructure *structure; |
|
1312 const gchar *mimetype; |
|
1313 GstCaps *caps; |
|
1314 GstStreamInfo *info; |
|
1315 GstStreamType type = GST_STREAM_TYPE_UNKNOWN; |
|
1316 GstPad *sinkpad; |
|
1317 GstPlayBaseGroup *group; |
|
1318 guint sig; |
|
1319 GstObject *parent; |
|
1320 gboolean first_pad; |
|
1321 |
|
1322 GST_DEBUG ("play base: new decoded pad. Last: %d", last); |
|
1323 |
|
1324 /* first see if this pad has interesting caps */ |
|
1325 caps = gst_pad_get_caps (pad); |
|
1326 if (caps == NULL || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) |
|
1327 goto no_type; |
|
1328 |
|
1329 /* get the mime type */ |
|
1330 structure = gst_caps_get_structure (caps, 0); |
|
1331 mimetype = gst_structure_get_name (structure); |
|
1332 |
|
1333 GROUP_LOCK (play_base_bin); |
|
1334 |
|
1335 group = get_building_group (play_base_bin); |
|
1336 |
|
1337 group->nstreams++; |
|
1338 |
|
1339 parent = gst_object_get_parent (GST_OBJECT_CAST (element)); |
|
1340 if (g_str_has_prefix (mimetype, "audio/") && |
|
1341 parent != GST_OBJECT_CAST (play_base_bin->subtitle)) { |
|
1342 type = GST_STREAM_TYPE_AUDIO; |
|
1343 } else if (g_str_has_prefix (mimetype, "video/") && |
|
1344 parent != GST_OBJECT_CAST (play_base_bin->subtitle)) { |
|
1345 type = GST_STREAM_TYPE_VIDEO; |
|
1346 } else if (g_str_has_prefix (mimetype, "text/")) { |
|
1347 type = GST_STREAM_TYPE_TEXT; |
|
1348 } |
|
1349 gst_object_unref (parent); |
|
1350 |
|
1351 info = gst_stream_info_new (GST_OBJECT_CAST (pad), type, NULL, caps); |
|
1352 gst_caps_unref (caps); |
|
1353 |
|
1354 if (type == GST_STREAM_TYPE_UNKNOWN) { |
|
1355 /* Unknown streams get added to the group, but the data |
|
1356 * just gets ignored */ |
|
1357 add_stream (group, info); |
|
1358 |
|
1359 GROUP_UNLOCK (play_base_bin); |
|
1360 |
|
1361 /* signal the no more pads after adding the stream */ |
|
1362 if (last) |
|
1363 no_more_pads_full (element, is_subs, play_base_bin); |
|
1364 |
|
1365 return; |
|
1366 } |
|
1367 |
|
1368 /* first pad of each type gets a selector + preroll queue */ |
|
1369 first_pad = (group->type[type - 1].npads == 0); |
|
1370 |
|
1371 if (first_pad) { |
|
1372 GST_DEBUG ("play base: pad needs new preroll"); |
|
1373 gen_preroll_element (play_base_bin, group, type, pad, info); |
|
1374 } |
|
1375 |
|
1376 /* add to stream selector */ |
|
1377 sinkpad = |
|
1378 gst_element_get_request_pad (group->type[type - 1].selector, "sink%d"); |
|
1379 |
|
1380 /* make sure we catch unlink signals */ |
|
1381 sig = g_signal_connect (G_OBJECT (sinkpad), "unlinked", |
|
1382 G_CALLBACK (preroll_unlinked), play_base_bin); |
|
1383 /* keep a ref to the signal id so that we can disconnect the signal callback */ |
|
1384 g_object_set_data (G_OBJECT (sinkpad), "unlinked_id", GINT_TO_POINTER (sig)); |
|
1385 /* Store a pointer to the stream selector pad for this stream */ |
|
1386 g_object_set_data (G_OBJECT (pad), "pb_sel_pad", sinkpad); |
|
1387 |
|
1388 gst_pad_link (pad, sinkpad); |
|
1389 gst_object_unref (sinkpad); |
|
1390 |
|
1391 /* select 1st for now - we'll select a preferred one after preroll */ |
|
1392 if (!first_pad) { |
|
1393 guint id; |
|
1394 |
|
1395 GST_DEBUG ("Adding silence_stream data probe on type %d (npads %d)", type, |
|
1396 group->type[type - 1].npads); |
|
1397 |
|
1398 id = gst_pad_add_data_probe (GST_PAD_CAST (pad), |
|
1399 G_CALLBACK (silence_stream), info); |
|
1400 g_object_set_data (G_OBJECT (pad), "eat_probe", GINT_TO_POINTER (id)); |
|
1401 } |
|
1402 |
|
1403 /* add the stream to the list */ |
|
1404 add_stream (group, info); |
|
1405 |
|
1406 GROUP_UNLOCK (play_base_bin); |
|
1407 |
|
1408 /* signal the no more pads after adding the stream */ |
|
1409 if (last) |
|
1410 no_more_pads_full (element, is_subs, play_base_bin); |
|
1411 |
|
1412 return; |
|
1413 |
|
1414 /* ERRORS */ |
|
1415 no_type: |
|
1416 { |
|
1417 g_warning ("no type on pad %s:%s", GST_DEBUG_PAD_NAME (pad)); |
|
1418 if (caps) |
|
1419 gst_caps_unref (caps); |
|
1420 return; |
|
1421 } |
|
1422 } |
|
1423 |
|
1424 static void |
|
1425 new_decoded_pad (GstElement * element, GstPad * pad, gboolean last, |
|
1426 GstPlayBaseBin * play_base_bin) |
|
1427 { |
|
1428 new_decoded_pad_full (element, pad, last, play_base_bin, FALSE); |
|
1429 } |
|
1430 |
|
1431 static void |
|
1432 subs_new_decoded_pad (GstElement * element, GstPad * pad, gboolean last, |
|
1433 GstPlayBaseBin * play_base_bin) |
|
1434 { |
|
1435 new_decoded_pad_full (element, pad, last, play_base_bin, TRUE); |
|
1436 } |
|
1437 |
|
1438 static void |
|
1439 set_encoding_element (GstElement * element, gchar * encoding) |
|
1440 { |
|
1441 GST_DEBUG_OBJECT (element, "setting encoding to %s", GST_STR_NULL (encoding)); |
|
1442 g_object_set (G_OBJECT (element), "subtitle-encoding", encoding, NULL); |
|
1443 } |
|
1444 |
|
1445 |
|
1446 static void |
|
1447 decodebin_element_added_cb (GstBin * decodebin, GstElement * element, |
|
1448 gpointer data) |
|
1449 { |
|
1450 GstPlayBaseBin *play_base_bin = GST_PLAY_BASE_BIN (data); |
|
1451 gchar *encoding; |
|
1452 |
|
1453 if (!g_object_class_find_property (G_OBJECT_GET_CLASS (element), |
|
1454 "subtitle-encoding")) { |
|
1455 return; |
|
1456 } |
|
1457 |
|
1458 g_mutex_lock (play_base_bin->sub_lock); |
|
1459 play_base_bin->subtitle_elements = |
|
1460 g_slist_append (play_base_bin->subtitle_elements, element); |
|
1461 encoding = g_strdup (play_base_bin->subencoding); |
|
1462 g_mutex_unlock (play_base_bin->sub_lock); |
|
1463 |
|
1464 set_encoding_element (element, encoding); |
|
1465 g_free (encoding); |
|
1466 } |
|
1467 |
|
1468 static void |
|
1469 decodebin_element_removed_cb (GstBin * decodebin, GstElement * element, |
|
1470 gpointer data) |
|
1471 { |
|
1472 GstPlayBaseBin *play_base_bin = GST_PLAY_BASE_BIN (data); |
|
1473 |
|
1474 g_mutex_lock (play_base_bin->sub_lock); |
|
1475 play_base_bin->subtitle_elements = |
|
1476 g_slist_remove (play_base_bin->subtitle_elements, element); |
|
1477 g_mutex_unlock (play_base_bin->sub_lock); |
|
1478 } |
|
1479 |
|
1480 |
|
1481 /* |
|
1482 * Generate source ! subparse bins. |
|
1483 */ |
|
1484 |
|
1485 static GstElement * |
|
1486 setup_subtitle (GstPlayBaseBin * play_base_bin, gchar * sub_uri) |
|
1487 { |
|
1488 GstElement *source, *subdecodebin, *subbin; |
|
1489 |
|
1490 if (!gst_uri_is_valid (sub_uri)) |
|
1491 goto invalid_uri; |
|
1492 |
|
1493 source = gst_element_make_from_uri (GST_URI_SRC, sub_uri, NULL); |
|
1494 if (!source) |
|
1495 goto unknown_uri; |
|
1496 |
|
1497 if (g_getenv ("USE_DECODEBIN2")) |
|
1498 subdecodebin = gst_element_factory_make ("decodebin2", "subtitle-decoder"); |
|
1499 else |
|
1500 subdecodebin = gst_element_factory_make ("decodebin", "subtitle-decoder"); |
|
1501 g_signal_connect (subdecodebin, "element-added", |
|
1502 G_CALLBACK (decodebin_element_added_cb), play_base_bin); |
|
1503 g_signal_connect (subdecodebin, "element-removed", |
|
1504 G_CALLBACK (decodebin_element_removed_cb), play_base_bin); |
|
1505 subbin = gst_bin_new ("subtitle-bin"); |
|
1506 gst_bin_add_many (GST_BIN_CAST (subbin), source, subdecodebin, NULL); |
|
1507 |
|
1508 gst_element_link (source, subdecodebin); |
|
1509 |
|
1510 /* return the subtitle GstElement object */ |
|
1511 return subbin; |
|
1512 |
|
1513 /* WARNINGS */ |
|
1514 invalid_uri: |
|
1515 { |
|
1516 GST_ELEMENT_WARNING (play_base_bin, RESOURCE, NOT_FOUND, |
|
1517 (_("Invalid subtitle URI \"%s\", subtitles disabled."), sub_uri), |
|
1518 (NULL)); |
|
1519 return NULL; |
|
1520 } |
|
1521 unknown_uri: |
|
1522 { |
|
1523 gchar *prot = gst_uri_get_protocol (sub_uri); |
|
1524 |
|
1525 if (prot) { |
|
1526 gchar *desc; |
|
1527 |
|
1528 gst_element_post_message (GST_ELEMENT (play_base_bin), |
|
1529 gst_missing_uri_source_message_new (GST_ELEMENT (play_base_bin), |
|
1530 prot)); |
|
1531 |
|
1532 desc = gst_pb_utils_get_source_description (prot); |
|
1533 GST_ELEMENT_ERROR (play_base_bin, CORE, MISSING_PLUGIN, |
|
1534 (_("A %s plugin is required to play this stream, but not installed."), |
|
1535 desc), ("No URI handler to handle sub_uri: %s", sub_uri)); |
|
1536 g_free (desc); |
|
1537 g_free (prot); |
|
1538 } else |
|
1539 goto invalid_uri; |
|
1540 |
|
1541 return NULL; |
|
1542 } |
|
1543 } |
|
1544 |
|
1545 /* helper function to lookup stuff in lists */ |
|
1546 static gboolean |
|
1547 array_has_value (const gchar * values[], const gchar * value) |
|
1548 { |
|
1549 gint i; |
|
1550 |
|
1551 for (i = 0; values[i]; i++) { |
|
1552 if (g_str_has_prefix (value, values[i])) |
|
1553 return TRUE; |
|
1554 } |
|
1555 return FALSE; |
|
1556 } |
|
1557 |
|
1558 /* list of URIs that we consider to be streams and that need buffering. |
|
1559 * We have no mechanism yet to figure this out with a query. */ |
|
1560 static const gchar *stream_uris[] = { "http://", "mms://", "mmsh://", |
|
1561 "mmsu://", "mmst://", "myth://", NULL |
|
1562 }; |
|
1563 |
|
1564 /* blacklisted URIs, we know they will always fail. */ |
|
1565 static const gchar *blacklisted_uris[] = { NULL }; |
|
1566 |
|
1567 /* mime types that we don't consider to be media types */ |
|
1568 static const gchar *no_media_mimes[] = { |
|
1569 "application/x-executable", "application/x-bzip", "application/x-gzip", |
|
1570 "application/zip", "application/x-compress", NULL |
|
1571 }; |
|
1572 |
|
1573 /* mime types we consider raw media */ |
|
1574 static const gchar *raw_mimes[] = { |
|
1575 "audio/x-raw", "video/x-raw", NULL |
|
1576 }; |
|
1577 |
|
1578 #define IS_STREAM_URI(uri) (array_has_value (stream_uris, uri)) |
|
1579 #define IS_BLACKLISTED_URI(uri) (array_has_value (blacklisted_uris, uri)) |
|
1580 #define IS_NO_MEDIA_MIME(mime) (array_has_value (no_media_mimes, mime)) |
|
1581 #define IS_RAW_MIME(mime) (array_has_value (raw_mimes, mime)) |
|
1582 |
|
1583 /* |
|
1584 * Generate and configure a source element. |
|
1585 */ |
|
1586 static GstElement * |
|
1587 gen_source_element (GstPlayBaseBin * play_base_bin, GstElement ** subbin) |
|
1588 { |
|
1589 GstElement *source; |
|
1590 |
|
1591 if (!play_base_bin->uri) |
|
1592 goto no_uri; |
|
1593 |
|
1594 if (!gst_uri_is_valid (play_base_bin->uri)) |
|
1595 goto invalid_uri; |
|
1596 |
|
1597 if (IS_BLACKLISTED_URI (play_base_bin->uri)) |
|
1598 goto uri_blacklisted; |
|
1599 |
|
1600 if (play_base_bin->suburi) { |
|
1601 GST_LOG_OBJECT (play_base_bin, "Creating decoder for subtitles URI %s", |
|
1602 play_base_bin->suburi); |
|
1603 /* subtitle specified */ |
|
1604 *subbin = setup_subtitle (play_base_bin, play_base_bin->suburi); |
|
1605 } else { |
|
1606 /* no subtitle specified */ |
|
1607 *subbin = NULL; |
|
1608 } |
|
1609 |
|
1610 source = gst_element_make_from_uri (GST_URI_SRC, play_base_bin->uri, |
|
1611 "source"); |
|
1612 if (!source) |
|
1613 goto no_source; |
|
1614 |
|
1615 play_base_bin->is_stream = IS_STREAM_URI (play_base_bin->uri); |
|
1616 |
|
1617 /* make HTTP sources send extra headers so we get icecast |
|
1618 * metadata in case the stream is an icecast stream */ |
|
1619 if (!strncmp (play_base_bin->uri, "http://", 7) && |
|
1620 g_object_class_find_property (G_OBJECT_GET_CLASS (source), |
|
1621 "iradio-mode")) { |
|
1622 g_object_set (source, "iradio-mode", TRUE, NULL); |
|
1623 } |
|
1624 |
|
1625 if (g_object_class_find_property (G_OBJECT_GET_CLASS (source), |
|
1626 "connection-speed")) { |
|
1627 GST_DEBUG_OBJECT (play_base_bin, |
|
1628 "setting connection-speed=%d to source element", |
|
1629 play_base_bin->connection_speed / 1000); |
|
1630 g_object_set (source, "connection-speed", |
|
1631 play_base_bin->connection_speed / 1000, NULL); |
|
1632 } |
|
1633 |
|
1634 return source; |
|
1635 |
|
1636 /* ERRORS */ |
|
1637 no_uri: |
|
1638 { |
|
1639 GST_ELEMENT_ERROR (play_base_bin, RESOURCE, NOT_FOUND, |
|
1640 (_("No URI specified to play from.")), (NULL)); |
|
1641 return NULL; |
|
1642 } |
|
1643 invalid_uri: |
|
1644 { |
|
1645 GST_ELEMENT_ERROR (play_base_bin, RESOURCE, NOT_FOUND, |
|
1646 (_("Invalid URI \"%s\"."), play_base_bin->uri), (NULL)); |
|
1647 return NULL; |
|
1648 } |
|
1649 uri_blacklisted: |
|
1650 { |
|
1651 GST_ELEMENT_ERROR (play_base_bin, RESOURCE, FAILED, |
|
1652 (_("RTSP streams cannot be played yet.")), (NULL)); |
|
1653 return NULL; |
|
1654 } |
|
1655 no_source: |
|
1656 { |
|
1657 gchar *prot = gst_uri_get_protocol (play_base_bin->uri); |
|
1658 |
|
1659 /* whoops, could not create the source element, dig a little deeper to |
|
1660 * figure out what might be wrong. */ |
|
1661 if (prot) { |
|
1662 gchar *desc; |
|
1663 |
|
1664 gst_element_post_message (GST_ELEMENT (play_base_bin), |
|
1665 gst_missing_uri_source_message_new (GST_ELEMENT (play_base_bin), |
|
1666 prot)); |
|
1667 |
|
1668 desc = gst_pb_utils_get_source_description (prot); |
|
1669 GST_ELEMENT_ERROR (play_base_bin, CORE, MISSING_PLUGIN, |
|
1670 (_("A %s plugin is required to play this stream, but not installed."), |
|
1671 desc), ("No URI handler for %s", prot)); |
|
1672 g_free (desc); |
|
1673 g_free (prot); |
|
1674 } else |
|
1675 goto invalid_uri; |
|
1676 |
|
1677 return NULL; |
|
1678 } |
|
1679 } |
|
1680 |
|
1681 /* is called when a dynamic source element created a new pad. */ |
|
1682 static void |
|
1683 source_new_pad (GstElement * element, GstPad * pad, GstPlayBaseBin * bin) |
|
1684 { |
|
1685 GstElement *decoder; |
|
1686 gboolean is_raw; |
|
1687 |
|
1688 GST_DEBUG_OBJECT (bin, "Found new pad %s.%s in source element %s", |
|
1689 GST_DEBUG_PAD_NAME (pad), GST_ELEMENT_NAME (element)); |
|
1690 |
|
1691 /* if this is a pad with all raw caps, we can expose it */ |
|
1692 if (has_all_raw_caps (pad, &is_raw) && is_raw) { |
|
1693 /* it's all raw, create output pads. */ |
|
1694 new_decoded_pad_full (element, pad, FALSE, bin, FALSE); |
|
1695 return; |
|
1696 } |
|
1697 |
|
1698 /* not raw, create decoder */ |
|
1699 decoder = make_decoder (bin); |
|
1700 if (!decoder) |
|
1701 goto no_decodebin; |
|
1702 |
|
1703 /* and link to decoder */ |
|
1704 if (!gst_element_link (bin->source, decoder)) |
|
1705 goto could_not_link; |
|
1706 |
|
1707 gst_element_set_state (decoder, GST_STATE_PAUSED); |
|
1708 |
|
1709 return; |
|
1710 |
|
1711 /* ERRORS */ |
|
1712 no_decodebin: |
|
1713 { |
|
1714 /* error was posted */ |
|
1715 return; |
|
1716 } |
|
1717 could_not_link: |
|
1718 { |
|
1719 GST_ELEMENT_ERROR (bin, CORE, NEGOTIATION, |
|
1720 (NULL), ("Can't link source to decoder element")); |
|
1721 return; |
|
1722 } |
|
1723 } |
|
1724 |
|
1725 /* |
|
1726 * Setup the substreams (is called right after group_commit () when |
|
1727 * loading a new group, or after switching groups). |
|
1728 * |
|
1729 * Should be called with group-lock held. |
|
1730 */ |
|
1731 static void |
|
1732 setup_substreams (GstPlayBaseBin * play_base_bin) |
|
1733 { |
|
1734 GstPlayBaseGroup *group; |
|
1735 gint n; |
|
1736 const GList *item; |
|
1737 |
|
1738 GST_DEBUG_OBJECT (play_base_bin, "setting up substreams"); |
|
1739 |
|
1740 /* Remove the eat probes */ |
|
1741 group = get_active_group (play_base_bin); |
|
1742 for (item = group->streaminfo; item; item = item->next) { |
|
1743 GstStreamInfo *info = item->data; |
|
1744 gpointer data; |
|
1745 |
|
1746 data = g_object_get_data (G_OBJECT (info->object), "eat_probe"); |
|
1747 if (data) { |
|
1748 gst_pad_remove_data_probe (GST_PAD_CAST (info->object), |
|
1749 GPOINTER_TO_INT (data)); |
|
1750 g_object_set_data (G_OBJECT (info->object), "eat_probe", NULL); |
|
1751 } |
|
1752 |
|
1753 /* now remove unknown pads */ |
|
1754 if (info->type == GST_STREAM_TYPE_UNKNOWN) { |
|
1755 guint id; |
|
1756 |
|
1757 id = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (info), "mute_probe")); |
|
1758 if (id == 0) { |
|
1759 id = gst_pad_add_buffer_probe (GST_PAD_CAST (info->object), |
|
1760 G_CALLBACK (mute_stream), info); |
|
1761 g_object_set_data (G_OBJECT (info), "mute_probe", GINT_TO_POINTER (id)); |
|
1762 } |
|
1763 } |
|
1764 } |
|
1765 |
|
1766 /* now check if the requested current streams exist. If |
|
1767 * current >= num_streams, decrease current so at least |
|
1768 * we have output. Always keep it enabled. */ |
|
1769 for (n = 0; n < NUM_TYPES; n++) { |
|
1770 if (play_base_bin->current[n] >= group->type[n].npads) { |
|
1771 GST_DEBUG_OBJECT (play_base_bin, "reset type %d to current 0", n); |
|
1772 play_base_bin->current[n] = 0; |
|
1773 } |
|
1774 } |
|
1775 |
|
1776 /* now activate the right sources. Don't forget that during preroll, |
|
1777 * we set the first source to forwarding and ignored the rest. */ |
|
1778 for (n = 0; n < NUM_TYPES; n++) { |
|
1779 GST_DEBUG_OBJECT (play_base_bin, "setting type %d to current %d", n, |
|
1780 play_base_bin->current[n]); |
|
1781 set_active_source (play_base_bin, n + 1, play_base_bin->current[n]); |
|
1782 } |
|
1783 } |
|
1784 |
|
1785 /** |
|
1786 * has_all_raw_caps: |
|
1787 * @pad: a #GstPad |
|
1788 * @all_raw: pointer to hold the result |
|
1789 * |
|
1790 * check if the caps of the pad are all raw. The caps are all raw if |
|
1791 * all of its structures contain audio/x-raw or video/x-raw. |
|
1792 * |
|
1793 * Returns: %FALSE @pad has no caps. Else TRUE and @all_raw set t the result. |
|
1794 */ |
|
1795 static gboolean |
|
1796 has_all_raw_caps (GstPad * pad, gboolean * all_raw) |
|
1797 { |
|
1798 GstCaps *caps; |
|
1799 gint capssize; |
|
1800 guint i, num_raw = 0; |
|
1801 gboolean res = FALSE; |
|
1802 |
|
1803 caps = gst_pad_get_caps (pad); |
|
1804 if (caps == NULL) |
|
1805 return FALSE; |
|
1806 |
|
1807 capssize = gst_caps_get_size (caps); |
|
1808 /* no caps, skip and move to the next pad */ |
|
1809 if (capssize == 0 || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) |
|
1810 goto done; |
|
1811 |
|
1812 /* count the number of raw formats in the caps */ |
|
1813 for (i = 0; i < capssize; ++i) { |
|
1814 GstStructure *s; |
|
1815 const gchar *mime_type; |
|
1816 |
|
1817 s = gst_caps_get_structure (caps, i); |
|
1818 mime_type = gst_structure_get_name (s); |
|
1819 |
|
1820 if (IS_RAW_MIME (mime_type)) |
|
1821 ++num_raw; |
|
1822 } |
|
1823 |
|
1824 *all_raw = (num_raw == capssize); |
|
1825 res = TRUE; |
|
1826 |
|
1827 done: |
|
1828 gst_caps_unref (caps); |
|
1829 return res; |
|
1830 } |
|
1831 |
|
1832 /** |
|
1833 * analyse_source: |
|
1834 * @play_base_bin: a #GstPlayBaseBin |
|
1835 * @is_raw: are all pads raw data |
|
1836 * @have_out: does the source have output |
|
1837 * @is_dynamic: is this a dynamic source |
|
1838 * |
|
1839 * Check the source of @play_base_bin and collect information about it. |
|
1840 * |
|
1841 * @is_raw will be set to TRUE if the source only produces raw pads. When this |
|
1842 * function returns, all of the raw pad of the source will be added |
|
1843 * to @play_base_bin. |
|
1844 * |
|
1845 * @have_out: will be set to TRUE if the source has output pads. |
|
1846 * |
|
1847 * @is_dynamic: TRUE if the element will create (more) pads dynamically later |
|
1848 * on. |
|
1849 * |
|
1850 * Returns: FALSE if a fatal error occured while scanning. |
|
1851 */ |
|
1852 static gboolean |
|
1853 analyse_source (GstPlayBaseBin * play_base_bin, gboolean * is_raw, |
|
1854 gboolean * have_out, gboolean * is_dynamic) |
|
1855 { |
|
1856 GstIterator *pads_iter; |
|
1857 gboolean done = FALSE; |
|
1858 gboolean res = TRUE; |
|
1859 |
|
1860 *have_out = FALSE; |
|
1861 *is_raw = FALSE; |
|
1862 *is_dynamic = FALSE; |
|
1863 |
|
1864 pads_iter = gst_element_iterate_src_pads (play_base_bin->source); |
|
1865 while (!done) { |
|
1866 GstPad *pad = NULL; |
|
1867 |
|
1868 switch (gst_iterator_next (pads_iter, (gpointer) & pad)) { |
|
1869 case GST_ITERATOR_ERROR: |
|
1870 res = FALSE; |
|
1871 /* FALLTROUGH */ |
|
1872 case GST_ITERATOR_DONE: |
|
1873 done = TRUE; |
|
1874 break; |
|
1875 case GST_ITERATOR_RESYNC: |
|
1876 /* reset results and resync */ |
|
1877 *have_out = FALSE; |
|
1878 *is_raw = FALSE; |
|
1879 *is_dynamic = FALSE; |
|
1880 gst_iterator_resync (pads_iter); |
|
1881 break; |
|
1882 case GST_ITERATOR_OK: |
|
1883 /* we now officially have an ouput pad */ |
|
1884 *have_out = TRUE; |
|
1885 |
|
1886 /* if FALSE, this pad has no caps and we continue with the next pad. */ |
|
1887 if (!has_all_raw_caps (pad, is_raw)) { |
|
1888 gst_object_unref (pad); |
|
1889 break; |
|
1890 } |
|
1891 |
|
1892 /* caps on source pad are all raw, we can add the pad */ |
|
1893 if (*is_raw) { |
|
1894 new_decoded_pad_full (play_base_bin->source, pad, FALSE, |
|
1895 play_base_bin, FALSE); |
|
1896 } |
|
1897 |
|
1898 gst_object_unref (pad); |
|
1899 break; |
|
1900 } |
|
1901 } |
|
1902 gst_iterator_free (pads_iter); |
|
1903 |
|
1904 if (!*have_out) { |
|
1905 GstElementClass *elemclass; |
|
1906 GList *walk; |
|
1907 |
|
1908 /* element has no output pads, check for padtemplates that list SOMETIMES |
|
1909 * pads. */ |
|
1910 elemclass = GST_ELEMENT_GET_CLASS (play_base_bin->source); |
|
1911 |
|
1912 walk = gst_element_class_get_pad_template_list (elemclass); |
|
1913 while (walk != NULL) { |
|
1914 GstPadTemplate *templ; |
|
1915 |
|
1916 templ = (GstPadTemplate *) walk->data; |
|
1917 if (GST_PAD_TEMPLATE_DIRECTION (templ) == GST_PAD_SRC) { |
|
1918 if (GST_PAD_TEMPLATE_PRESENCE (templ) == GST_PAD_SOMETIMES) { |
|
1919 *is_dynamic = TRUE; |
|
1920 break; /* only break out if we found a sometimes src pad |
|
1921 continue walking through if say a request src pad is found |
|
1922 elements such as mpegtsparse and dvbbasebin have request |
|
1923 and sometimes src pads */ |
|
1924 } |
|
1925 } |
|
1926 walk = g_list_next (walk); |
|
1927 } |
|
1928 } |
|
1929 |
|
1930 return res; |
|
1931 } |
|
1932 |
|
1933 static void |
|
1934 remove_decoders (GstPlayBaseBin * bin) |
|
1935 { |
|
1936 GSList *walk; |
|
1937 |
|
1938 for (walk = bin->decoders; walk; walk = g_slist_next (walk)) { |
|
1939 GstElement *decoder = GST_ELEMENT_CAST (walk->data); |
|
1940 |
|
1941 GST_DEBUG_OBJECT (bin, "removing old decoder element"); |
|
1942 gst_element_set_state (decoder, GST_STATE_NULL); |
|
1943 gst_bin_remove (GST_BIN_CAST (bin), decoder); |
|
1944 } |
|
1945 g_slist_free (bin->decoders); |
|
1946 bin->decoders = NULL; |
|
1947 } |
|
1948 |
|
1949 static GstElement * |
|
1950 make_decoder (GstPlayBaseBin * play_base_bin) |
|
1951 { |
|
1952 GstElement *decoder; |
|
1953 |
|
1954 /* now create the decoder element */ |
|
1955 if (g_getenv ("USE_DECODEBIN2")) |
|
1956 decoder = gst_element_factory_make ("decodebin2", NULL); |
|
1957 else |
|
1958 decoder = gst_element_factory_make ("decodebin", NULL); |
|
1959 if (!decoder) |
|
1960 goto no_decodebin; |
|
1961 |
|
1962 g_signal_connect (decoder, "element-added", |
|
1963 G_CALLBACK (decodebin_element_added_cb), play_base_bin); |
|
1964 g_signal_connect (decoder, "element-removed", |
|
1965 G_CALLBACK (decodebin_element_removed_cb), play_base_bin); |
|
1966 |
|
1967 gst_bin_add (GST_BIN_CAST (play_base_bin), decoder); |
|
1968 |
|
1969 /* set up callbacks to create the links between decoded data |
|
1970 * and video/audio/subtitle rendering/output. */ |
|
1971 g_signal_connect (G_OBJECT (decoder), |
|
1972 "new-decoded-pad", G_CALLBACK (new_decoded_pad), play_base_bin); |
|
1973 g_signal_connect (G_OBJECT (decoder), "no-more-pads", |
|
1974 G_CALLBACK (no_more_pads), play_base_bin); |
|
1975 g_signal_connect (G_OBJECT (decoder), |
|
1976 "unknown-type", G_CALLBACK (unknown_type), play_base_bin); |
|
1977 g_object_set_data (G_OBJECT (decoder), "pending", "1"); |
|
1978 play_base_bin->pending++; |
|
1979 |
|
1980 GST_DEBUG_OBJECT (play_base_bin, "created decodebin, %d pending", |
|
1981 play_base_bin->pending); |
|
1982 |
|
1983 play_base_bin->decoders = g_slist_prepend (play_base_bin->decoders, decoder); |
|
1984 |
|
1985 return decoder; |
|
1986 |
|
1987 /* ERRORS */ |
|
1988 no_decodebin: |
|
1989 { |
|
1990 GST_ELEMENT_ERROR (play_base_bin, CORE, MISSING_PLUGIN, |
|
1991 (_("Could not create \"decodebin\" element.")), (NULL)); |
|
1992 return NULL; |
|
1993 } |
|
1994 } |
|
1995 |
|
1996 static void |
|
1997 remove_source (GstPlayBaseBin * bin) |
|
1998 { |
|
1999 GstElement *source = bin->source; |
|
2000 |
|
2001 if (source) { |
|
2002 GST_DEBUG_OBJECT (bin, "removing old src element"); |
|
2003 gst_element_set_state (source, GST_STATE_NULL); |
|
2004 |
|
2005 if (bin->src_np_sig_id) { |
|
2006 g_signal_handler_disconnect (G_OBJECT (source), bin->src_np_sig_id); |
|
2007 bin->src_np_sig_id = 0; |
|
2008 } |
|
2009 if (bin->src_nmp_sig_id) { |
|
2010 g_signal_handler_disconnect (G_OBJECT (source), bin->src_nmp_sig_id); |
|
2011 bin->src_nmp_sig_id = 0; |
|
2012 } |
|
2013 gst_bin_remove (GST_BIN_CAST (bin), source); |
|
2014 bin->source = NULL; |
|
2015 } |
|
2016 } |
|
2017 |
|
2018 static GstBusSyncReply |
|
2019 subbin_startup_sync_msg (GstBus * bus, GstMessage * msg, gpointer user_data) |
|
2020 { |
|
2021 if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) { |
|
2022 GstPlayBaseBin *play_base_bin; |
|
2023 |
|
2024 play_base_bin = GST_PLAY_BASE_BIN (user_data); |
|
2025 if (!play_base_bin->subtitle_done) { |
|
2026 GST_WARNING_OBJECT (play_base_bin, "error starting up subtitle bin: %" |
|
2027 GST_PTR_FORMAT, msg); |
|
2028 play_base_bin->subtitle_done = TRUE; |
|
2029 GST_DEBUG_OBJECT (play_base_bin, "signal group done"); |
|
2030 GROUP_SIGNAL (play_base_bin); |
|
2031 GST_DEBUG_OBJECT (play_base_bin, "signaled group done"); |
|
2032 } |
|
2033 } |
|
2034 return GST_BUS_PASS; |
|
2035 } |
|
2036 |
|
2037 /* construct and run the source and decoder elements until we found |
|
2038 * all the streams or until a preroll queue has been filled. |
|
2039 */ |
|
2040 static gboolean |
|
2041 setup_source (GstPlayBaseBin * play_base_bin) |
|
2042 { |
|
2043 GstElement *subbin = NULL; |
|
2044 gboolean is_raw, have_out, is_dynamic; |
|
2045 |
|
2046 if (!play_base_bin->need_rebuild) |
|
2047 return TRUE; |
|
2048 |
|
2049 GST_DEBUG_OBJECT (play_base_bin, "setup source"); |
|
2050 |
|
2051 /* delete old src */ |
|
2052 remove_source (play_base_bin); |
|
2053 |
|
2054 /* create and configure an element that can handle the uri */ |
|
2055 if (!(play_base_bin->source = gen_source_element (play_base_bin, &subbin))) |
|
2056 goto no_source; |
|
2057 |
|
2058 /* state will be merged later - if file is not found, error will be |
|
2059 * handled by the application right after. */ |
|
2060 gst_bin_add (GST_BIN_CAST (play_base_bin), play_base_bin->source); |
|
2061 g_object_notify (G_OBJECT (play_base_bin), "source"); |
|
2062 |
|
2063 /* remove the old decoders now, if any */ |
|
2064 remove_decoders (play_base_bin); |
|
2065 |
|
2066 /* remove our previous preroll queues */ |
|
2067 remove_groups (play_base_bin); |
|
2068 |
|
2069 /* clear pending dynamic elements */ |
|
2070 play_base_bin->pending = 0; |
|
2071 |
|
2072 /* do subs */ |
|
2073 if (subbin) { |
|
2074 GstElement *db; |
|
2075 GstBus *bus; |
|
2076 |
|
2077 play_base_bin->subtitle = subbin; |
|
2078 db = gst_bin_get_by_name (GST_BIN_CAST (subbin), "subtitle-decoder"); |
|
2079 |
|
2080 /* do type detection, without adding (so no preroll) */ |
|
2081 g_signal_connect (G_OBJECT (db), "new-decoded-pad", |
|
2082 G_CALLBACK (subs_new_decoded_pad), play_base_bin); |
|
2083 g_signal_connect (G_OBJECT (db), "no-more-pads", |
|
2084 G_CALLBACK (sub_no_more_pads), play_base_bin); |
|
2085 g_signal_connect (G_OBJECT (db), "unknown-type", |
|
2086 G_CALLBACK (unknown_type), play_base_bin); |
|
2087 g_object_set_data (G_OBJECT (db), "pending", "1"); |
|
2088 play_base_bin->pending++; |
|
2089 |
|
2090 GST_DEBUG_OBJECT (play_base_bin, "we have subtitles, %d pending", |
|
2091 play_base_bin->pending); |
|
2092 |
|
2093 if (!play_base_bin->is_stream) { |
|
2094 GstStateChangeReturn sret; |
|
2095 |
|
2096 /* either when the queues are filled or when the decoder element |
|
2097 * has no more dynamic streams, the cond is unlocked. We can remove |
|
2098 * the signal handlers then |
|
2099 */ |
|
2100 GST_DEBUG_OBJECT (play_base_bin, "starting subtitle bin"); |
|
2101 |
|
2102 /* for subtitles in a separate bin we will not commit the |
|
2103 * current building group since we need to add the other |
|
2104 * audio/video streams to the group. We check if we managed |
|
2105 * to commit the subtitle group using an extra flag. */ |
|
2106 play_base_bin->subtitle_done = FALSE; |
|
2107 |
|
2108 /* since subbin is still a stand-alone bin, we need to add a custom bus |
|
2109 * to intercept error messages, so we can stop waiting and continue */ |
|
2110 bus = gst_bus_new (); |
|
2111 gst_element_set_bus (subbin, bus); |
|
2112 gst_bus_set_sync_handler (bus, subbin_startup_sync_msg, play_base_bin); |
|
2113 |
|
2114 sret = gst_element_set_state (subbin, GST_STATE_PAUSED); |
|
2115 if (sret != GST_STATE_CHANGE_FAILURE) { |
|
2116 GROUP_LOCK (play_base_bin); |
|
2117 GST_DEBUG ("waiting for subtitle to complete..."); |
|
2118 while (!play_base_bin->subtitle_done) |
|
2119 GROUP_WAIT (play_base_bin); |
|
2120 GST_DEBUG ("group done !"); |
|
2121 GROUP_UNLOCK (play_base_bin); |
|
2122 |
|
2123 if (!play_base_bin->building_group || |
|
2124 play_base_bin->building_group->type[GST_STREAM_TYPE_TEXT - |
|
2125 1].npads == 0) { |
|
2126 |
|
2127 GST_DEBUG ("No subtitle found - ignoring"); |
|
2128 gst_element_set_state (subbin, GST_STATE_NULL); |
|
2129 gst_object_unref (play_base_bin->subtitle); |
|
2130 play_base_bin->subtitle = NULL; |
|
2131 } else { |
|
2132 GST_DEBUG_OBJECT (play_base_bin, "Subtitle set-up successful"); |
|
2133 } |
|
2134 } else { |
|
2135 GST_WARNING_OBJECT (play_base_bin, "Failed to start subtitle bin"); |
|
2136 gst_element_set_state (subbin, GST_STATE_NULL); |
|
2137 gst_object_unref (play_base_bin->subtitle); |
|
2138 play_base_bin->subtitle = NULL; |
|
2139 } |
|
2140 |
|
2141 gst_bus_set_sync_handler (bus, NULL, NULL); |
|
2142 gst_element_set_bus (subbin, NULL); |
|
2143 gst_object_unref (bus); |
|
2144 } |
|
2145 gst_object_unref (db); |
|
2146 } |
|
2147 /* see if the source element emits raw audio/video all by itself, |
|
2148 * if so, we can create streams for the pads and be done with it. |
|
2149 * Also check that is has source pads, if not, we assume it will |
|
2150 * do everything itself. */ |
|
2151 if (!analyse_source (play_base_bin, &is_raw, &have_out, &is_dynamic)) |
|
2152 goto invalid_source; |
|
2153 |
|
2154 if (is_raw) { |
|
2155 GST_DEBUG_OBJECT (play_base_bin, "Source provides all raw data"); |
|
2156 /* source provides raw data, we added the pads and we can now signal a |
|
2157 * no_more pads because we are done. */ |
|
2158 group_commit (play_base_bin, play_base_bin->is_stream, FALSE); |
|
2159 return TRUE; |
|
2160 } |
|
2161 if (!have_out && !is_dynamic) { |
|
2162 GST_DEBUG_OBJECT (play_base_bin, "Source has no output pads"); |
|
2163 /* create a stream to indicate that this uri is handled by a self |
|
2164 * contained element. We are now done. */ |
|
2165 add_element_stream (play_base_bin->source, play_base_bin); |
|
2166 group_commit (play_base_bin, play_base_bin->is_stream, FALSE); |
|
2167 return TRUE; |
|
2168 } |
|
2169 if (is_dynamic) { |
|
2170 /* connect a handler for the new-pad signal */ |
|
2171 play_base_bin->src_np_sig_id = |
|
2172 g_signal_connect (G_OBJECT (play_base_bin->source), "pad-added", |
|
2173 G_CALLBACK (source_new_pad), play_base_bin); |
|
2174 play_base_bin->src_nmp_sig_id = |
|
2175 g_signal_connect (G_OBJECT (play_base_bin->source), "no-more-pads", |
|
2176 G_CALLBACK (source_no_more_pads), play_base_bin); |
|
2177 g_object_set_data (G_OBJECT (play_base_bin->source), "pending", "1"); |
|
2178 play_base_bin->pending++; |
|
2179 GST_DEBUG_OBJECT (play_base_bin, |
|
2180 "Source has dynamic output pads, %d pending", play_base_bin->pending); |
|
2181 } else { |
|
2182 GstElement *decoder; |
|
2183 |
|
2184 /* no dynamic source, we can link now */ |
|
2185 decoder = make_decoder (play_base_bin); |
|
2186 if (!decoder) |
|
2187 goto no_decodebin; |
|
2188 |
|
2189 if (!gst_element_link (play_base_bin->source, decoder)) |
|
2190 goto could_not_link; |
|
2191 } |
|
2192 |
|
2193 if (play_base_bin->subtitle) |
|
2194 gst_bin_add (GST_BIN_CAST (play_base_bin), play_base_bin->subtitle); |
|
2195 |
|
2196 play_base_bin->need_rebuild = FALSE; |
|
2197 |
|
2198 return TRUE; |
|
2199 |
|
2200 /* ERRORS */ |
|
2201 no_source: |
|
2202 { |
|
2203 /* error message was already posted */ |
|
2204 return FALSE; |
|
2205 } |
|
2206 invalid_source: |
|
2207 { |
|
2208 GST_ELEMENT_ERROR (play_base_bin, CORE, FAILED, |
|
2209 (_("Source element is invalid.")), (NULL)); |
|
2210 return FALSE; |
|
2211 } |
|
2212 no_decodebin: |
|
2213 { |
|
2214 /* message was posted */ |
|
2215 return FALSE; |
|
2216 } |
|
2217 could_not_link: |
|
2218 { |
|
2219 GST_ELEMENT_ERROR (play_base_bin, CORE, NEGOTIATION, |
|
2220 (NULL), ("Can't link source to decoder element")); |
|
2221 return FALSE; |
|
2222 } |
|
2223 } |
|
2224 |
|
2225 static void |
|
2226 finish_source (GstPlayBaseBin * play_base_bin) |
|
2227 { |
|
2228 /* FIXME: no need to grab the group lock here? (tpm) */ |
|
2229 if (get_active_group (play_base_bin) != NULL) { |
|
2230 if (play_base_bin->subtitle) { |
|
2231 /* make subs iterate from now on */ |
|
2232 gst_bin_add (GST_BIN_CAST (play_base_bin), play_base_bin->subtitle); |
|
2233 } |
|
2234 } |
|
2235 } |
|
2236 |
|
2237 /* |
|
2238 * Caller must have group-lock held. |
|
2239 * |
|
2240 * We iterate over all detected streams in the streaminfo and try to find |
|
2241 * impossible cases, like subtitles without video. |
|
2242 */ |
|
2243 static gboolean |
|
2244 prepare_output (GstPlayBaseBin * play_base_bin) |
|
2245 { |
|
2246 const GList *item; |
|
2247 gboolean stream_found = FALSE, no_media = FALSE; |
|
2248 gboolean got_video = FALSE, got_subtitle = FALSE; |
|
2249 GstPlayBaseGroup *group; |
|
2250 |
|
2251 group = get_active_group (play_base_bin); |
|
2252 |
|
2253 /* check if we found any supported stream... if not, then |
|
2254 * we detected stream type (or the above would've failed), |
|
2255 * but linking/decoding failed - plugin probably missing. */ |
|
2256 for (item = group ? group->streaminfo : NULL; item != NULL; item = item->next) { |
|
2257 GstStreamInfo *info = GST_STREAM_INFO (item->data); |
|
2258 |
|
2259 if (info->type == GST_STREAM_TYPE_VIDEO) { |
|
2260 stream_found = TRUE; |
|
2261 got_video = TRUE; |
|
2262 break; |
|
2263 } else if (info->type == GST_STREAM_TYPE_ELEMENT) { |
|
2264 stream_found = TRUE; |
|
2265 } else if (info->type == GST_STREAM_TYPE_AUDIO) { |
|
2266 stream_found = TRUE; |
|
2267 } else if (info->type == GST_STREAM_TYPE_TEXT || |
|
2268 info->type == GST_STREAM_TYPE_SUBPICTURE) { |
|
2269 got_subtitle = TRUE; |
|
2270 } else if (!item->prev && !item->next) { |
|
2271 /* We're no audio/video and the only stream... We could |
|
2272 * be something not-media that's detected because then our |
|
2273 * typefind doesn't mess up with mp3 (bz2, gz, elf, ...) */ |
|
2274 if (info->caps && !gst_caps_is_empty (info->caps)) { |
|
2275 const gchar *mime = |
|
2276 gst_structure_get_name (gst_caps_get_structure (info->caps, 0)); |
|
2277 |
|
2278 no_media = IS_NO_MEDIA_MIME (mime); |
|
2279 } |
|
2280 } |
|
2281 } |
|
2282 |
|
2283 if (!stream_found) { |
|
2284 if (got_subtitle) { |
|
2285 GST_ELEMENT_ERROR (play_base_bin, STREAM, WRONG_TYPE, |
|
2286 (_("Only a subtitle stream was detected. " |
|
2287 "Either you are loading a subtitle file or some other type of " |
|
2288 "text file, or the media file was not recognized.")), (NULL)); |
|
2289 } else if (!no_media) { |
|
2290 GST_ELEMENT_ERROR (play_base_bin, STREAM, CODEC_NOT_FOUND, |
|
2291 (_("You do not have a decoder installed to handle this file. " |
|
2292 "You might need to install the necessary plugins.")), (NULL)); |
|
2293 } else { |
|
2294 GST_ELEMENT_ERROR (play_base_bin, STREAM, WRONG_TYPE, |
|
2295 (_("This is not a media file")), (NULL)); |
|
2296 } |
|
2297 return FALSE; |
|
2298 } else if (got_subtitle && !got_video) { |
|
2299 GST_ELEMENT_ERROR (play_base_bin, STREAM, WRONG_TYPE, |
|
2300 (_("A subtitle stream was detected, but no video stream.")), (NULL)); |
|
2301 return FALSE; |
|
2302 } |
|
2303 |
|
2304 return TRUE; |
|
2305 } |
|
2306 |
|
2307 /* |
|
2308 * Multi-stream management. -1 = none. |
|
2309 * |
|
2310 * Caller has group-lock held. |
|
2311 */ |
|
2312 static gint |
|
2313 get_active_source (GstPlayBaseBin * play_base_bin, GstStreamType type) |
|
2314 { |
|
2315 GstPlayBaseGroup *group; |
|
2316 GList *s; |
|
2317 gint num = 0; |
|
2318 |
|
2319 group = get_active_group (play_base_bin); |
|
2320 if (!group) |
|
2321 return -1; |
|
2322 |
|
2323 for (s = group->streaminfo; s; s = s->next) { |
|
2324 GstStreamInfo *info = s->data; |
|
2325 |
|
2326 if (info->type == type) { |
|
2327 if (!info->mute && !g_object_get_data (G_OBJECT (info), "mute_probe")) { |
|
2328 return num; |
|
2329 } else { |
|
2330 num++; |
|
2331 } |
|
2332 } |
|
2333 } |
|
2334 |
|
2335 return -1; |
|
2336 } |
|
2337 |
|
2338 /* Kill pad reactivation on state change. */ |
|
2339 |
|
2340 #if 0 |
|
2341 static void muted_group_change_state (GstElement * element, |
|
2342 gint old_state, gint new_state, gpointer data); |
|
2343 #endif |
|
2344 |
|
2345 static void |
|
2346 mute_group_type (GstPlayBaseGroup * group, GstStreamType type, gboolean mute) |
|
2347 { |
|
2348 gboolean active = !mute; |
|
2349 GstPad *pad; |
|
2350 |
|
2351 pad = gst_element_get_pad (group->type[type - 1].preroll, "src"); |
|
2352 gst_pad_set_active (pad, active); |
|
2353 gst_object_unref (pad); |
|
2354 pad = gst_element_get_pad (group->type[type - 1].preroll, "sink"); |
|
2355 gst_pad_set_active (pad, active); |
|
2356 gst_object_unref (pad); |
|
2357 pad = gst_element_get_pad (group->type[type - 1].selector, "src"); |
|
2358 gst_pad_set_active (pad, active); |
|
2359 gst_object_unref (pad); |
|
2360 |
|
2361 #if 0 |
|
2362 if (mute) { |
|
2363 g_signal_connect (group->type[type - 1].preroll, "state-changed", |
|
2364 G_CALLBACK (muted_group_change_state), group); |
|
2365 } else { |
|
2366 g_signal_handlers_disconnect_by_func (group->type[type - 1].preroll, |
|
2367 G_CALLBACK (muted_group_change_state), group); |
|
2368 } |
|
2369 #endif |
|
2370 } |
|
2371 |
|
2372 #if 0 |
|
2373 static void |
|
2374 muted_group_change_state (GstElement * element, |
|
2375 gint old_state, gint new_state, gpointer data) |
|
2376 { |
|
2377 GstPlayBaseGroup *group = data; |
|
2378 |
|
2379 GROUP_LOCK (group->bin); |
|
2380 |
|
2381 if (new_state == GST_STATE_PLAYING) { |
|
2382 gint n; |
|
2383 |
|
2384 for (n = 0; n < NUM_TYPES; n++) { |
|
2385 if (group->type[n].selector == element) { |
|
2386 mute_group_type (group, n + 1, TRUE); |
|
2387 } |
|
2388 } |
|
2389 } |
|
2390 |
|
2391 GROUP_UNLOCK (group->bin); |
|
2392 } |
|
2393 #endif |
|
2394 |
|
2395 static void |
|
2396 set_subtitles_visible (GstPlayBaseBin * play_base_bin, gboolean visible) |
|
2397 { |
|
2398 GstPlayBaseBinClass *klass = GST_PLAY_BASE_BIN_GET_CLASS (play_base_bin); |
|
2399 |
|
2400 /* we use a vfunc for this since we don't have a reference to the |
|
2401 * textoverlay element, but playbin does */ |
|
2402 if (klass != NULL && klass->set_subtitles_visible != NULL) |
|
2403 klass->set_subtitles_visible (play_base_bin, visible); |
|
2404 } |
|
2405 |
|
2406 /* |
|
2407 * Caller has group-lock held. |
|
2408 */ |
|
2409 |
|
2410 static void |
|
2411 set_active_source (GstPlayBaseBin * play_base_bin, |
|
2412 GstStreamType type, gint source_num) |
|
2413 { |
|
2414 GstPlayBaseGroup *group; |
|
2415 GList *s; |
|
2416 gint num = 0; |
|
2417 gboolean have_active = FALSE; |
|
2418 GstElement *sel; |
|
2419 |
|
2420 GST_LOG ("Changing active source of type %d to %d", type, source_num); |
|
2421 play_base_bin->current[type - 1] = source_num; |
|
2422 |
|
2423 group = get_active_group (play_base_bin); |
|
2424 if (!group || !group->type[type - 1].preroll) { |
|
2425 GST_LOG ("No active group, or group for type %d has no preroll", type); |
|
2426 return; |
|
2427 } |
|
2428 |
|
2429 /* HACK: instead of unlinking the subtitle input (= lots of hassle, |
|
2430 * especially if subtitles come from an external source), just tell |
|
2431 * textoverlay not to render them */ |
|
2432 if (type == GST_STREAM_TYPE_TEXT) { |
|
2433 gboolean visible = (source_num != -1); |
|
2434 |
|
2435 set_subtitles_visible (play_base_bin, visible); |
|
2436 if (!visible) |
|
2437 return; |
|
2438 } |
|
2439 |
|
2440 sel = group->type[type - 1].selector; |
|
2441 |
|
2442 for (s = group->streaminfo; s; s = s->next) { |
|
2443 GstStreamInfo *info = s->data; |
|
2444 |
|
2445 if (info->type == type) { |
|
2446 if (num == source_num) { |
|
2447 GstPad *sel_pad; |
|
2448 |
|
2449 GST_LOG ("Unmuting (if already muted) source %d of type %d", source_num, |
|
2450 type); |
|
2451 g_object_set (info, "mute", FALSE, NULL); |
|
2452 |
|
2453 /* Tell the stream selector which pad to accept */ |
|
2454 sel_pad = GST_PAD_CAST (g_object_get_data (G_OBJECT (info->object), |
|
2455 "pb_sel_pad")); |
|
2456 |
|
2457 if (sel && sel_pad != NULL) { |
|
2458 g_object_set (G_OBJECT (sel), "active-pad", sel_pad, NULL); |
|
2459 } |
|
2460 |
|
2461 have_active = TRUE; |
|
2462 } else { |
|
2463 guint id; |
|
2464 |
|
2465 GST_LOG_OBJECT (info->object, "Muting source %d of type %d", num, type); |
|
2466 |
|
2467 id = gst_pad_add_buffer_probe (GST_PAD_CAST (info->object), |
|
2468 G_CALLBACK (mute_stream), info); |
|
2469 g_object_set_data (G_OBJECT (info), "mute_probe", GINT_TO_POINTER (id)); |
|
2470 } |
|
2471 num++; |
|
2472 } |
|
2473 } |
|
2474 |
|
2475 if (!have_active) { |
|
2476 GST_LOG ("Muting group type: %d", type); |
|
2477 g_object_set (sel, "active-pad", NULL, NULL); |
|
2478 } else { |
|
2479 GST_LOG ("Unmuting group type: %d", type); |
|
2480 } |
|
2481 mute_group_type (group, type, !have_active); |
|
2482 } |
|
2483 |
|
2484 static void |
|
2485 gst_play_base_bin_set_property (GObject * object, guint prop_id, |
|
2486 const GValue * value, GParamSpec * pspec) |
|
2487 { |
|
2488 GstPlayBaseBin *play_base_bin; |
|
2489 |
|
2490 g_return_if_fail (GST_IS_PLAY_BASE_BIN (object)); |
|
2491 |
|
2492 play_base_bin = GST_PLAY_BASE_BIN (object); |
|
2493 |
|
2494 switch (prop_id) { |
|
2495 case ARG_URI: |
|
2496 { |
|
2497 const gchar *uri = g_value_get_string (value); |
|
2498 |
|
2499 if (uri == NULL) { |
|
2500 g_warning ("cannot set NULL uri"); |
|
2501 return; |
|
2502 } |
|
2503 /* if we have no previous uri, or the new uri is different from the |
|
2504 * old one, replug */ |
|
2505 if (play_base_bin->uri == NULL || strcmp (play_base_bin->uri, uri) != 0) { |
|
2506 g_free (play_base_bin->uri); |
|
2507 play_base_bin->uri = g_strdup (uri); |
|
2508 |
|
2509 GST_DEBUG ("setting new uri to %s", uri); |
|
2510 |
|
2511 play_base_bin->need_rebuild = TRUE; |
|
2512 } |
|
2513 break; |
|
2514 } |
|
2515 case ARG_SUBURI:{ |
|
2516 const gchar *suburi = g_value_get_string (value); |
|
2517 |
|
2518 if ((!suburi && !play_base_bin->suburi) || |
|
2519 (suburi && play_base_bin->suburi && |
|
2520 !strcmp (play_base_bin->suburi, suburi))) |
|
2521 return; |
|
2522 g_free (play_base_bin->suburi); |
|
2523 play_base_bin->suburi = g_strdup (suburi); |
|
2524 GST_DEBUG ("setting new .sub uri to %s", suburi); |
|
2525 play_base_bin->need_rebuild = TRUE; |
|
2526 break; |
|
2527 } |
|
2528 case ARG_QUEUE_SIZE: |
|
2529 play_base_bin->queue_size = g_value_get_uint64 (value); |
|
2530 break; |
|
2531 case ARG_QUEUE_THRESHOLD: |
|
2532 play_base_bin->queue_threshold = g_value_get_uint64 (value); |
|
2533 break; |
|
2534 case ARG_QUEUE_MIN_THRESHOLD: |
|
2535 play_base_bin->queue_min_threshold = g_value_get_uint64 (value); |
|
2536 break; |
|
2537 case ARG_CONNECTION_SPEED: |
|
2538 play_base_bin->connection_speed = g_value_get_uint (value) * 1000; |
|
2539 break; |
|
2540 case ARG_VIDEO: |
|
2541 GROUP_LOCK (play_base_bin); |
|
2542 set_active_source (play_base_bin, |
|
2543 GST_STREAM_TYPE_VIDEO, g_value_get_int (value)); |
|
2544 GROUP_UNLOCK (play_base_bin); |
|
2545 break; |
|
2546 case ARG_AUDIO: |
|
2547 GROUP_LOCK (play_base_bin); |
|
2548 set_active_source (play_base_bin, |
|
2549 GST_STREAM_TYPE_AUDIO, g_value_get_int (value)); |
|
2550 GROUP_UNLOCK (play_base_bin); |
|
2551 break; |
|
2552 case ARG_TEXT: |
|
2553 GROUP_LOCK (play_base_bin); |
|
2554 set_active_source (play_base_bin, |
|
2555 GST_STREAM_TYPE_TEXT, g_value_get_int (value)); |
|
2556 GROUP_UNLOCK (play_base_bin); |
|
2557 break; |
|
2558 case ARG_SUBTITLE_ENCODING: |
|
2559 { |
|
2560 const gchar *encoding; |
|
2561 GSList *list; |
|
2562 |
|
2563 encoding = g_value_get_string (value); |
|
2564 if (encoding && play_base_bin->subencoding && |
|
2565 !strcmp (play_base_bin->subencoding, encoding)) { |
|
2566 return; |
|
2567 } |
|
2568 if (encoding == NULL && play_base_bin->subencoding == NULL) |
|
2569 return; |
|
2570 |
|
2571 g_mutex_lock (play_base_bin->sub_lock); |
|
2572 g_free (play_base_bin->subencoding); |
|
2573 play_base_bin->subencoding = g_strdup (encoding); |
|
2574 list = g_slist_copy (play_base_bin->subtitle_elements); |
|
2575 g_slist_foreach (list, (GFunc) gst_object_ref, NULL); |
|
2576 g_mutex_unlock (play_base_bin->sub_lock); |
|
2577 |
|
2578 /* we can't hold a lock when calling g_object_set() on a child, since |
|
2579 * the notify event will trigger GstObject to send a deep-notify event |
|
2580 * which will try to take the lock ... */ |
|
2581 g_slist_foreach (list, (GFunc) set_encoding_element, (gpointer) encoding); |
|
2582 g_slist_foreach (list, (GFunc) gst_object_unref, NULL); |
|
2583 g_slist_free (list); |
|
2584 break; |
|
2585 } |
|
2586 default: |
|
2587 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
2588 break; |
|
2589 } |
|
2590 } |
|
2591 |
|
2592 static void |
|
2593 gst_play_base_bin_get_property (GObject * object, guint prop_id, GValue * value, |
|
2594 GParamSpec * pspec) |
|
2595 { |
|
2596 GstPlayBaseBin *play_base_bin; |
|
2597 |
|
2598 g_return_if_fail (GST_IS_PLAY_BASE_BIN (object)); |
|
2599 |
|
2600 play_base_bin = GST_PLAY_BASE_BIN (object); |
|
2601 |
|
2602 switch (prop_id) { |
|
2603 case ARG_URI: |
|
2604 g_value_set_string (value, play_base_bin->uri); |
|
2605 break; |
|
2606 case ARG_SUBURI: |
|
2607 g_value_set_string (value, play_base_bin->suburi); |
|
2608 break; |
|
2609 case ARG_NSTREAMS: |
|
2610 { |
|
2611 GstPlayBaseGroup *group; |
|
2612 |
|
2613 GROUP_LOCK (play_base_bin); |
|
2614 group = get_active_group (play_base_bin); |
|
2615 if (group) { |
|
2616 g_value_set_int (value, group->nstreams); |
|
2617 } else { |
|
2618 g_value_set_int (value, 0); |
|
2619 } |
|
2620 GROUP_UNLOCK (play_base_bin); |
|
2621 break; |
|
2622 } |
|
2623 case ARG_QUEUE_SIZE: |
|
2624 g_value_set_uint64 (value, play_base_bin->queue_size); |
|
2625 break; |
|
2626 case ARG_QUEUE_THRESHOLD: |
|
2627 g_value_set_uint64 (value, play_base_bin->queue_threshold); |
|
2628 break; |
|
2629 case ARG_QUEUE_MIN_THRESHOLD: |
|
2630 g_value_set_uint64 (value, play_base_bin->queue_min_threshold); |
|
2631 break; |
|
2632 case ARG_CONNECTION_SPEED: |
|
2633 g_value_set_uint (value, play_base_bin->connection_speed / 1000); |
|
2634 break; |
|
2635 case ARG_STREAMINFO: |
|
2636 /* FIXME: hold some kind of lock here, use iterator */ |
|
2637 g_value_set_pointer (value, |
|
2638 (gpointer) gst_play_base_bin_get_streaminfo (play_base_bin)); |
|
2639 break; |
|
2640 case ARG_STREAMINFO_VALUES:{ |
|
2641 GValueArray *copy; |
|
2642 |
|
2643 copy = gst_play_base_bin_get_streaminfo_value_array (play_base_bin); |
|
2644 g_value_take_boxed (value, copy); |
|
2645 break; |
|
2646 } |
|
2647 case ARG_SOURCE: |
|
2648 g_value_set_object (value, play_base_bin->source); |
|
2649 break; |
|
2650 case ARG_VIDEO: |
|
2651 GROUP_LOCK (play_base_bin); |
|
2652 g_value_set_int (value, get_active_source (play_base_bin, |
|
2653 GST_STREAM_TYPE_VIDEO)); |
|
2654 GROUP_UNLOCK (play_base_bin); |
|
2655 break; |
|
2656 case ARG_AUDIO: |
|
2657 GROUP_LOCK (play_base_bin); |
|
2658 g_value_set_int (value, get_active_source (play_base_bin, |
|
2659 GST_STREAM_TYPE_AUDIO)); |
|
2660 GROUP_UNLOCK (play_base_bin); |
|
2661 break; |
|
2662 case ARG_TEXT: |
|
2663 GROUP_LOCK (play_base_bin); |
|
2664 g_value_set_int (value, get_active_source (play_base_bin, |
|
2665 GST_STREAM_TYPE_TEXT)); |
|
2666 GROUP_UNLOCK (play_base_bin); |
|
2667 break; |
|
2668 case ARG_SUBTITLE_ENCODING: |
|
2669 GST_OBJECT_LOCK (play_base_bin); |
|
2670 g_value_set_string (value, play_base_bin->subencoding); |
|
2671 GST_OBJECT_UNLOCK (play_base_bin); |
|
2672 break; |
|
2673 default: |
|
2674 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
|
2675 break; |
|
2676 } |
|
2677 } |
|
2678 |
|
2679 static GstStateChangeReturn |
|
2680 gst_play_base_bin_change_state (GstElement * element, GstStateChange transition) |
|
2681 { |
|
2682 GstStateChangeReturn ret; |
|
2683 GstPlayBaseBin *play_base_bin; |
|
2684 |
|
2685 play_base_bin = GST_PLAY_BASE_BIN (element); |
|
2686 |
|
2687 switch (transition) { |
|
2688 case GST_STATE_CHANGE_READY_TO_PAUSED: |
|
2689 if (!setup_source (play_base_bin)) |
|
2690 goto source_failed; |
|
2691 break; |
|
2692 default: |
|
2693 break; |
|
2694 } |
|
2695 |
|
2696 ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); |
|
2697 |
|
2698 switch (transition) { |
|
2699 case GST_STATE_CHANGE_READY_TO_PAUSED: |
|
2700 if (ret == GST_STATE_CHANGE_FAILURE) |
|
2701 goto cleanup_groups; |
|
2702 |
|
2703 finish_source (play_base_bin); |
|
2704 break; |
|
2705 /* clean-up in both cases, READY=>NULL clean-up is if there was an error */ |
|
2706 case GST_STATE_CHANGE_PAUSED_TO_READY: |
|
2707 case GST_STATE_CHANGE_READY_TO_NULL: |
|
2708 play_base_bin->need_rebuild = TRUE; |
|
2709 remove_decoders (play_base_bin); |
|
2710 remove_groups (play_base_bin); |
|
2711 remove_source (play_base_bin); |
|
2712 break; |
|
2713 default: |
|
2714 break; |
|
2715 } |
|
2716 return ret; |
|
2717 |
|
2718 /* ERRORS */ |
|
2719 source_failed: |
|
2720 { |
|
2721 play_base_bin->need_rebuild = TRUE; |
|
2722 |
|
2723 return GST_STATE_CHANGE_FAILURE; |
|
2724 } |
|
2725 cleanup_groups: |
|
2726 { |
|
2727 /* clean up leftover groups */ |
|
2728 remove_groups (play_base_bin); |
|
2729 play_base_bin->need_rebuild = TRUE; |
|
2730 |
|
2731 return GST_STATE_CHANGE_FAILURE; |
|
2732 } |
|
2733 } |
|
2734 |
|
2735 static const GList * |
|
2736 gst_play_base_bin_get_streaminfo (GstPlayBaseBin * play_base_bin) |
|
2737 { |
|
2738 GstPlayBaseGroup *group = get_active_group (play_base_bin); |
|
2739 GList *info = NULL; |
|
2740 |
|
2741 if (group) { |
|
2742 info = group->streaminfo; |
|
2743 } |
|
2744 return info; |
|
2745 } |
|
2746 |
|
2747 static GValueArray * |
|
2748 gst_play_base_bin_get_streaminfo_value_array (GstPlayBaseBin * play_base_bin) |
|
2749 { |
|
2750 GstPlayBaseGroup *group; |
|
2751 GValueArray *array = NULL; |
|
2752 |
|
2753 GROUP_LOCK (play_base_bin); |
|
2754 group = get_active_group (play_base_bin); |
|
2755 if (group) { |
|
2756 array = g_value_array_copy (group->streaminfo_value_array); |
|
2757 } else { |
|
2758 array = g_value_array_new (0); |
|
2759 } |
|
2760 GROUP_UNLOCK (play_base_bin); |
|
2761 |
|
2762 return array; |
|
2763 } |