/src/gstreamer/subprojects/gst-plugins-base/gst-libs/gst/pbutils/gstdiscoverer.c
Line | Count | Source |
1 | | /* GStreamer |
2 | | * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk> |
3 | | * 2009 Nokia Corporation |
4 | | * |
5 | | * This library is free software; you can redistribute it and/or |
6 | | * modify it under the terms of the GNU Library General Public |
7 | | * License as published by the Free Software Foundation; either |
8 | | * version 2 of the License, or (at your option) any later version. |
9 | | * |
10 | | * This library is distributed in the hope that it will be useful, |
11 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
13 | | * Library General Public License for more details. |
14 | | * |
15 | | * You should have received a copy of the GNU Library General Public |
16 | | * License along with this library; if not, write to the |
17 | | * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, |
18 | | * Boston, MA 02110-1301, USA. |
19 | | */ |
20 | | |
21 | | /** |
22 | | * SECTION:gstdiscoverer |
23 | | * @title: GstDiscoverer |
24 | | * @short_description: Utility for discovering information on URIs. |
25 | | * |
26 | | * The #GstDiscoverer is a utility object which allows to get as much |
27 | | * information as possible from one or many URIs. |
28 | | * |
29 | | * It provides two APIs, allowing usage in blocking or non-blocking mode. |
30 | | * |
31 | | * The blocking mode just requires calling gst_discoverer_discover_uri() |
32 | | * with the URI one wishes to discover. |
33 | | * |
34 | | * The non-blocking mode requires a running #GMainLoop iterating a |
35 | | * #GMainContext, where one connects to the various signals, appends the |
36 | | * URIs to be processed (through gst_discoverer_discover_uri_async()) and then |
37 | | * asks for the discovery to begin (through gst_discoverer_start()). |
38 | | * By default this will use the GLib default main context unless you have |
39 | | * set a custom context using g_main_context_push_thread_default(). |
40 | | * |
41 | | * All the information is returned in a #GstDiscovererInfo structure. |
42 | | */ |
43 | | |
44 | | #ifdef HAVE_CONFIG_H |
45 | | #include "config.h" |
46 | | #endif |
47 | | |
48 | | #include <gst/video/video.h> |
49 | | #include <gst/audio/audio.h> |
50 | | |
51 | | #include <string.h> |
52 | | |
53 | | #include "pbutils.h" |
54 | | #include "pbutils-private.h" |
55 | | |
56 | | /* For g_stat () */ |
57 | | #include <glib/gstdio.h> |
58 | | |
59 | | /* *INDENT-OFF* */ |
60 | | GST_DEBUG_CATEGORY_STATIC (discoverer_debug); |
61 | 1 | #define GST_CAT_DEFAULT discoverer_debug |
62 | | |
63 | 86 | GST_LOG_CONTEXT_STATIC_DEFINE (warning_message_log_ctx, GST_LOG_CONTEXT_FLAG_THROTTLE, |
64 | | GST_LOG_CONTEXT_BUILDER_SET_HASH_FLAGS(GST_LOG_CONTEXT_USE_STRING_ARGS); |
65 | | ); |
66 | | #define WARNING_MESSAGE_LOG_CTX GST_LOG_CONTEXT_LAZY_INIT (warning_message_log_ctx) |
67 | | /* *INDENT-ON* */ |
68 | | |
69 | 0 | #define CACHE_DIRNAME "discoverer" |
70 | | |
71 | | typedef struct |
72 | | { |
73 | | GstDiscoverer *dc; |
74 | | GstPad *pad; |
75 | | GstElement *queue; |
76 | | GstElement *sink; |
77 | | GstTagList *tags; |
78 | | GstToc *toc; |
79 | | gchar *stream_id; |
80 | | gulong probe_id; |
81 | | } PrivateStream; |
82 | | |
83 | | struct _GstDiscovererPrivate |
84 | | { |
85 | | gboolean async; |
86 | | |
87 | | /* allowed time to discover each uri in nanoseconds */ |
88 | | GstClockTime timeout; |
89 | | |
90 | | /* list of pending URI to process (current excluded) */ |
91 | | GList *pending_uris; |
92 | | |
93 | | GMutex lock; |
94 | | /* TRUE if cleaning up discoverer */ |
95 | | gboolean cleanup; |
96 | | |
97 | | /* TRUE if processing a URI */ |
98 | | gboolean processing; |
99 | | |
100 | | /* TRUE if discoverer has been started */ |
101 | | gboolean running; |
102 | | |
103 | | /* current items */ |
104 | | GstDiscovererInfo *current_info; |
105 | | gint current_info_stream_count; |
106 | | GError *current_error; |
107 | | GstStructure *current_topology; |
108 | | gchar *current_cachefile; |
109 | | gboolean current_info_from_cache; |
110 | | |
111 | | GstTagList *all_tags; |
112 | | GstTagList *global_tags; |
113 | | |
114 | | /* List of private streams */ |
115 | | GList *streams; |
116 | | |
117 | | /* List of these sinks and their handler IDs (to remove the probe) */ |
118 | | guint pending_subtitle_pads; |
119 | | |
120 | | /* Whether we received no_more_pads */ |
121 | | gboolean no_more_pads; |
122 | | |
123 | | GstState target_state; |
124 | | GstState current_state; |
125 | | |
126 | | /* Global elements */ |
127 | | GstBin *pipeline; |
128 | | GstElement *uridecodebin; |
129 | | GstBus *bus; |
130 | | |
131 | | /* Custom main context variables */ |
132 | | GMainContext *ctx; |
133 | | GSource *bus_source; |
134 | | GSource *timeout_source; |
135 | | |
136 | | /* reusable queries */ |
137 | | GstQuery *seeking_query; |
138 | | |
139 | | /* Handler ids for various callbacks */ |
140 | | gulong pad_added_id; |
141 | | gulong pad_remove_id; |
142 | | gulong no_more_pads_id; |
143 | | gulong source_chg_id; |
144 | | gulong bus_cb_id; |
145 | | |
146 | | gboolean use_cache; |
147 | | }; |
148 | | |
149 | 5.88k | #define DISCO_LOCK(dc) g_mutex_lock (&dc->priv->lock); |
150 | 345 | #define DISCO_UNLOCK(dc) g_mutex_unlock (&dc->priv->lock); |
151 | | |
152 | | static void |
153 | | _do_init (void) |
154 | 1 | { |
155 | 1 | GST_DEBUG_CATEGORY_INIT (discoverer_debug, "discoverer", 0, "Discoverer"); |
156 | 1 | }; |
157 | | |
158 | 1.03k | G_DEFINE_TYPE_EXTENDED (GstDiscoverer, gst_discoverer, G_TYPE_OBJECT, 0, |
159 | 1.03k | G_ADD_PRIVATE (GstDiscoverer) _do_init ()); |
160 | 1.03k | |
161 | 1.03k | enum |
162 | 1.03k | { |
163 | 1.03k | SIGNAL_FINISHED, |
164 | 1.03k | SIGNAL_STARTING, |
165 | 1.03k | SIGNAL_DISCOVERED, |
166 | 1.03k | SIGNAL_SOURCE_SETUP, |
167 | 1.03k | SIGNAL_LOAD_SERIALIZED_INFO, |
168 | 1.03k | LAST_SIGNAL |
169 | 1.03k | }; |
170 | 1.03k | |
171 | 1.03k | #define DEFAULT_PROP_TIMEOUT 15 * GST_SECOND |
172 | 346 | #define DEFAULT_PROP_USE_CACHE FALSE |
173 | | |
174 | | enum |
175 | | { |
176 | | PROP_0, |
177 | | PROP_TIMEOUT, |
178 | | PROP_USE_CACHE |
179 | | }; |
180 | | |
181 | | static guint gst_discoverer_signals[LAST_SIGNAL] = { 0 }; |
182 | | |
183 | | static void gst_discoverer_set_timeout (GstDiscoverer * dc, |
184 | | GstClockTime timeout); |
185 | | static gboolean async_timeout_cb (GstDiscoverer * dc); |
186 | | |
187 | | static void discoverer_bus_cb (GstBus * bus, GstMessage * msg, |
188 | | GstDiscoverer * dc); |
189 | | static void uridecodebin_pad_added_cb (GstElement * uridecodebin, GstPad * pad, |
190 | | GstDiscoverer * dc); |
191 | | static void uridecodebin_pad_removed_cb (GstElement * uridecodebin, |
192 | | GstPad * pad, GstDiscoverer * dc); |
193 | | static void uridecodebin_no_more_pads_cb (GstElement * uridecodebin, |
194 | | GstDiscoverer * dc); |
195 | | static void uridecodebin_source_changed_cb (GstElement * uridecodebin, |
196 | | GParamSpec * pspec, GstDiscoverer * dc); |
197 | | |
198 | | static void gst_discoverer_dispose (GObject * dc); |
199 | | static void gst_discoverer_finalize (GObject * dc); |
200 | | static void gst_discoverer_set_property (GObject * object, guint prop_id, |
201 | | const GValue * value, GParamSpec * pspec); |
202 | | static void gst_discoverer_get_property (GObject * object, guint prop_id, |
203 | | GValue * value, GParamSpec * pspec); |
204 | | static gboolean _setup_locked (GstDiscoverer * dc); |
205 | | static void handle_current_async (GstDiscoverer * dc); |
206 | | static gboolean emit_discovered_and_next (GstDiscoverer * dc); |
207 | | static GVariant *gst_discoverer_info_to_variant_recurse (GstDiscovererStreamInfo |
208 | | * sinfo, GstDiscovererSerializeFlags flags); |
209 | | static GstDiscovererStreamInfo *_parse_discovery (GVariant * variant, |
210 | | GstDiscovererInfo * info); |
211 | | static GstDiscovererInfo *load_serialized_info (GstDiscoverer * dc, |
212 | | gchar * uri); |
213 | | |
214 | | static gboolean |
215 | | _gst_discoverer_info_accumulator (GSignalInvocationHint * ihint, |
216 | | GValue * return_accu, const GValue * handler_return, gpointer dummy) |
217 | 345 | { |
218 | 345 | GstDiscovererInfo *info; |
219 | | |
220 | 345 | info = g_value_get_object (handler_return); |
221 | 345 | GST_DEBUG ("got discoverer info %" GST_PTR_FORMAT, info); |
222 | | |
223 | 345 | g_value_set_object (return_accu, info); |
224 | | |
225 | | /* stop emission if we have a discoverer info */ |
226 | 345 | return (info == NULL); |
227 | 345 | } |
228 | | |
229 | | static void |
230 | | gst_discoverer_class_init (GstDiscovererClass * klass) |
231 | 1 | { |
232 | 1 | GObjectClass *gobject_class = (GObjectClass *) klass; |
233 | | |
234 | 1 | gobject_class->dispose = gst_discoverer_dispose; |
235 | 1 | gobject_class->finalize = gst_discoverer_finalize; |
236 | | |
237 | 1 | gobject_class->set_property = gst_discoverer_set_property; |
238 | 1 | gobject_class->get_property = gst_discoverer_get_property; |
239 | | |
240 | 1 | klass->load_serialize_info = load_serialized_info; |
241 | | |
242 | | |
243 | | /* properties */ |
244 | | /** |
245 | | * GstDiscoverer:timeout: |
246 | | * |
247 | | * The duration (in nanoseconds) after which the discovery of an individual |
248 | | * URI will timeout. |
249 | | * |
250 | | * If the discovery of a URI times out, the %GST_DISCOVERER_TIMEOUT will be |
251 | | * set on the result flags. |
252 | | */ |
253 | 1 | g_object_class_install_property (gobject_class, PROP_TIMEOUT, |
254 | 1 | g_param_spec_uint64 ("timeout", "timeout", "Timeout", |
255 | 1 | GST_SECOND, 3600 * GST_SECOND, DEFAULT_PROP_TIMEOUT, |
256 | 1 | G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); |
257 | | |
258 | | /** |
259 | | * GstDiscoverer::use-cache: |
260 | | * |
261 | | * Whether to use a serialized version of the discoverer info from our |
262 | | * own cache if accessible. This allows the discovery to be much faster |
263 | | * as when using this option, we do not need to create a #GstPipeline |
264 | | * and run it, but instead, just reload the #GstDiscovererInfo in its |
265 | | * serialized form. |
266 | | * |
267 | | * The cache files are saved in `$XDG_CACHE_DIR/gstreamer-1.0/discoverer/`. |
268 | | * |
269 | | * Since: 1.16 |
270 | | */ |
271 | 1 | g_object_class_install_property (gobject_class, PROP_USE_CACHE, |
272 | 1 | g_param_spec_boolean ("use-cache", "use cache", "Use cache", |
273 | 1 | DEFAULT_PROP_USE_CACHE, |
274 | 1 | G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS)); |
275 | | |
276 | | /* signals */ |
277 | | /** |
278 | | * GstDiscoverer::finished: |
279 | | * @discoverer: the #GstDiscoverer |
280 | | * |
281 | | * Will be emitted in async mode when all pending URIs have been processed. |
282 | | */ |
283 | 1 | gst_discoverer_signals[SIGNAL_FINISHED] = |
284 | 1 | g_signal_new ("finished", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, |
285 | 1 | G_STRUCT_OFFSET (GstDiscovererClass, finished), NULL, NULL, NULL, |
286 | 1 | G_TYPE_NONE, 0, G_TYPE_NONE); |
287 | | |
288 | | /** |
289 | | * GstDiscoverer::starting: |
290 | | * @discoverer: the #GstDiscoverer |
291 | | * |
292 | | * Will be emitted when the discover starts analyzing the pending URIs |
293 | | */ |
294 | 1 | gst_discoverer_signals[SIGNAL_STARTING] = |
295 | 1 | g_signal_new ("starting", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, |
296 | 1 | G_STRUCT_OFFSET (GstDiscovererClass, starting), NULL, NULL, NULL, |
297 | 1 | G_TYPE_NONE, 0, G_TYPE_NONE); |
298 | | |
299 | | /** |
300 | | * GstDiscoverer::discovered: |
301 | | * @discoverer: the #GstDiscoverer |
302 | | * @info: the results #GstDiscovererInfo |
303 | | * @error: (allow-none) (type GLib.Error): #GError, which will be non-NULL |
304 | | * if an error occurred during |
305 | | * discovery. You must not free |
306 | | * this #GError, it will be freed by |
307 | | * the discoverer. |
308 | | * |
309 | | * Will be emitted in async mode when all information on a URI could be |
310 | | * discovered, or an error occurred. |
311 | | * |
312 | | * When an error occurs, @info might still contain some partial information, |
313 | | * depending on the circumstances of the error. |
314 | | */ |
315 | 1 | gst_discoverer_signals[SIGNAL_DISCOVERED] = |
316 | 1 | g_signal_new ("discovered", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, |
317 | 1 | G_STRUCT_OFFSET (GstDiscovererClass, discovered), NULL, NULL, NULL, |
318 | 1 | G_TYPE_NONE, 2, GST_TYPE_DISCOVERER_INFO, |
319 | 1 | G_TYPE_ERROR | G_SIGNAL_TYPE_STATIC_SCOPE); |
320 | | |
321 | | /** |
322 | | * GstDiscoverer::source-setup: |
323 | | * @discoverer: the #GstDiscoverer |
324 | | * @source: source element |
325 | | * |
326 | | * This signal is emitted after the source element has been created for, so |
327 | | * the URI being discovered, so it can be configured by setting additional |
328 | | * properties (e.g. set a proxy server for an http source, or set the device |
329 | | * and read speed for an audio cd source). |
330 | | * |
331 | | * This signal is usually emitted from the context of a GStreamer streaming |
332 | | * thread. |
333 | | */ |
334 | 1 | gst_discoverer_signals[SIGNAL_SOURCE_SETUP] = |
335 | 1 | g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass), |
336 | 1 | G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDiscovererClass, source_setup), |
337 | 1 | NULL, NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_ELEMENT); |
338 | | |
339 | | /** |
340 | | * GstDiscoverer::load-serialized-info: |
341 | | * @discoverer: the #GstDiscoverer |
342 | | * @uri: THe URI to load the serialized info for |
343 | | * |
344 | | * Retrieves information about a URI from and external source of information, |
345 | | * like a cache file. This is used by the discoverer to speed up the |
346 | | * discovery. |
347 | | * |
348 | | * Returns: (nullable) (transfer full): The #GstDiscovererInfo representing |
349 | | * @uri, or %NULL if no information |
350 | | * |
351 | | * Since: 1.24 |
352 | | */ |
353 | 1 | gst_discoverer_signals[SIGNAL_LOAD_SERIALIZED_INFO] = |
354 | 1 | g_signal_new ("load-serialized-info", G_TYPE_FROM_CLASS (klass), |
355 | 1 | G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstDiscovererClass, |
356 | 1 | load_serialize_info), _gst_discoverer_info_accumulator, NULL, NULL, |
357 | 1 | GST_TYPE_DISCOVERER_INFO, 1, G_TYPE_STRING); |
358 | 1 | } |
359 | | |
360 | | static void |
361 | | gst_discoverer_init (GstDiscoverer * dc) |
362 | 345 | { |
363 | 345 | GstFormat format = GST_FORMAT_TIME; |
364 | | |
365 | 345 | dc->priv = gst_discoverer_get_instance_private (dc); |
366 | | |
367 | 345 | dc->priv->timeout = DEFAULT_PROP_TIMEOUT; |
368 | 345 | dc->priv->use_cache = DEFAULT_PROP_USE_CACHE; |
369 | 345 | dc->priv->async = FALSE; |
370 | | |
371 | 345 | g_mutex_init (&dc->priv->lock); |
372 | | |
373 | 345 | dc->priv->pending_subtitle_pads = 0; |
374 | | |
375 | 345 | dc->priv->current_state = GST_STATE_NULL; |
376 | 345 | dc->priv->target_state = GST_STATE_NULL; |
377 | 345 | dc->priv->no_more_pads = FALSE; |
378 | | |
379 | 345 | dc->priv->all_tags = NULL; |
380 | 345 | dc->priv->global_tags = NULL; |
381 | | |
382 | 345 | GST_LOG ("Creating pipeline"); |
383 | 345 | dc->priv->pipeline = (GstBin *) gst_pipeline_new ("Discoverer"); |
384 | 345 | GST_LOG_OBJECT (dc, "Creating uridecodebin"); |
385 | 345 | dc->priv->uridecodebin = |
386 | 345 | gst_element_factory_make ("uridecodebin", "discoverer-uri"); |
387 | 345 | if (G_UNLIKELY (dc->priv->uridecodebin == NULL)) { |
388 | 0 | GST_ERROR_OBJECT (dc, "Can't create uridecodebin"); |
389 | 0 | return; |
390 | 0 | } |
391 | | |
392 | 345 | g_object_set (dc->priv->uridecodebin, "post-stream-topology", TRUE, NULL); |
393 | | |
394 | 345 | GST_LOG_OBJECT (dc, "Adding uridecodebin to pipeline"); |
395 | 345 | gst_bin_add (dc->priv->pipeline, dc->priv->uridecodebin); |
396 | | |
397 | 345 | dc->priv->pad_added_id = |
398 | 345 | g_signal_connect_object (dc->priv->uridecodebin, "pad-added", |
399 | 345 | G_CALLBACK (uridecodebin_pad_added_cb), dc, 0); |
400 | 345 | dc->priv->pad_remove_id = |
401 | 345 | g_signal_connect_object (dc->priv->uridecodebin, "pad-removed", |
402 | 345 | G_CALLBACK (uridecodebin_pad_removed_cb), dc, 0); |
403 | 345 | dc->priv->no_more_pads_id = |
404 | 345 | g_signal_connect_object (dc->priv->uridecodebin, "no-more-pads", |
405 | 345 | G_CALLBACK (uridecodebin_no_more_pads_cb), dc, 0); |
406 | 345 | dc->priv->source_chg_id = |
407 | 345 | g_signal_connect_object (dc->priv->uridecodebin, "notify::source", |
408 | 345 | G_CALLBACK (uridecodebin_source_changed_cb), dc, 0); |
409 | | |
410 | 345 | GST_LOG_OBJECT (dc, "Getting pipeline bus"); |
411 | 345 | dc->priv->bus = gst_pipeline_get_bus ((GstPipeline *) dc->priv->pipeline); |
412 | | |
413 | 345 | dc->priv->bus_cb_id = |
414 | 345 | g_signal_connect_object (dc->priv->bus, "message", |
415 | 345 | G_CALLBACK (discoverer_bus_cb), dc, 0); |
416 | | |
417 | 345 | GST_DEBUG_OBJECT (dc, "Done initializing Discoverer"); |
418 | | |
419 | | /* create queries */ |
420 | 345 | dc->priv->seeking_query = gst_query_new_seeking (format); |
421 | 345 | } |
422 | | |
423 | | static void |
424 | | discoverer_reset (GstDiscoverer * dc) |
425 | 345 | { |
426 | 345 | GST_DEBUG_OBJECT (dc, "Resetting"); |
427 | | |
428 | 345 | if (dc->priv->pending_uris) { |
429 | 0 | g_list_foreach (dc->priv->pending_uris, (GFunc) g_free, NULL); |
430 | 0 | g_list_free (dc->priv->pending_uris); |
431 | 0 | dc->priv->pending_uris = NULL; |
432 | 0 | } |
433 | | |
434 | 345 | if (dc->priv->pipeline) |
435 | 345 | gst_element_set_state ((GstElement *) dc->priv->pipeline, GST_STATE_NULL); |
436 | 345 | } |
437 | | |
438 | 1.72k | #define DISCONNECT_SIGNAL(o,i) G_STMT_START{ \ |
439 | 1.72k | if ((i) && g_signal_handler_is_connected ((o), (i))) \ |
440 | 1.72k | g_signal_handler_disconnect ((o), (i)); \ |
441 | 1.72k | (i) = 0; \ |
442 | 1.72k | }G_STMT_END |
443 | | |
444 | | static void |
445 | | gst_discoverer_dispose (GObject * obj) |
446 | 345 | { |
447 | 345 | GstDiscoverer *dc = (GstDiscoverer *) obj; |
448 | | |
449 | 345 | GST_DEBUG_OBJECT (dc, "Disposing"); |
450 | | |
451 | 345 | discoverer_reset (dc); |
452 | | |
453 | 345 | if (G_LIKELY (dc->priv->pipeline)) { |
454 | | /* Workaround for bug #118536 */ |
455 | 345 | DISCONNECT_SIGNAL (dc->priv->uridecodebin, dc->priv->pad_added_id); |
456 | 345 | DISCONNECT_SIGNAL (dc->priv->uridecodebin, dc->priv->pad_remove_id); |
457 | 345 | DISCONNECT_SIGNAL (dc->priv->uridecodebin, dc->priv->no_more_pads_id); |
458 | 345 | DISCONNECT_SIGNAL (dc->priv->uridecodebin, dc->priv->source_chg_id); |
459 | 345 | DISCONNECT_SIGNAL (dc->priv->bus, dc->priv->bus_cb_id); |
460 | | |
461 | | /* pipeline was set to NULL in _reset */ |
462 | 345 | gst_object_unref (dc->priv->pipeline); |
463 | 345 | if (dc->priv->bus) |
464 | 345 | gst_object_unref (dc->priv->bus); |
465 | | |
466 | 345 | dc->priv->pipeline = NULL; |
467 | 345 | dc->priv->uridecodebin = NULL; |
468 | 345 | dc->priv->bus = NULL; |
469 | 345 | } |
470 | | |
471 | 345 | gst_discoverer_stop (dc); |
472 | | |
473 | 345 | if (dc->priv->seeking_query) { |
474 | 345 | gst_query_unref (dc->priv->seeking_query); |
475 | 345 | dc->priv->seeking_query = NULL; |
476 | 345 | } |
477 | | |
478 | 345 | G_OBJECT_CLASS (gst_discoverer_parent_class)->dispose (obj); |
479 | 345 | } |
480 | | |
481 | | static void |
482 | | gst_discoverer_finalize (GObject * obj) |
483 | 345 | { |
484 | 345 | GstDiscoverer *dc = (GstDiscoverer *) obj; |
485 | | |
486 | 345 | g_mutex_clear (&dc->priv->lock); |
487 | | |
488 | 345 | G_OBJECT_CLASS (gst_discoverer_parent_class)->finalize (obj); |
489 | 345 | } |
490 | | |
491 | | static void |
492 | | gst_discoverer_set_property (GObject * object, guint prop_id, |
493 | | const GValue * value, GParamSpec * pspec) |
494 | 690 | { |
495 | 690 | GstDiscoverer *dc = (GstDiscoverer *) object; |
496 | | |
497 | 690 | switch (prop_id) { |
498 | 345 | case PROP_TIMEOUT: |
499 | 345 | gst_discoverer_set_timeout (dc, g_value_get_uint64 (value)); |
500 | 345 | break; |
501 | 345 | case PROP_USE_CACHE: |
502 | 345 | DISCO_LOCK (dc); |
503 | 345 | dc->priv->use_cache = g_value_get_boolean (value); |
504 | 345 | DISCO_UNLOCK (dc); |
505 | 345 | break; |
506 | 0 | default: |
507 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
508 | 0 | break; |
509 | 690 | } |
510 | 690 | } |
511 | | |
512 | | static void |
513 | | gst_discoverer_get_property (GObject * object, guint prop_id, |
514 | | GValue * value, GParamSpec * pspec) |
515 | 0 | { |
516 | 0 | GstDiscoverer *dc = (GstDiscoverer *) object; |
517 | |
|
518 | 0 | switch (prop_id) { |
519 | 0 | case PROP_TIMEOUT: |
520 | 0 | DISCO_LOCK (dc); |
521 | 0 | g_value_set_uint64 (value, dc->priv->timeout); |
522 | 0 | DISCO_UNLOCK (dc); |
523 | 0 | break; |
524 | 0 | case PROP_USE_CACHE: |
525 | 0 | DISCO_LOCK (dc); |
526 | 0 | g_value_set_boolean (value, dc->priv->use_cache); |
527 | 0 | DISCO_UNLOCK (dc); |
528 | 0 | break; |
529 | 0 | default: |
530 | 0 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); |
531 | 0 | break; |
532 | 0 | } |
533 | 0 | } |
534 | | |
535 | | static void |
536 | | gst_discoverer_set_timeout (GstDiscoverer * dc, GstClockTime timeout) |
537 | 345 | { |
538 | 345 | g_return_if_fail (GST_CLOCK_TIME_IS_VALID (timeout)); |
539 | | |
540 | 345 | GST_DEBUG_OBJECT (dc, "timeout : %" GST_TIME_FORMAT, GST_TIME_ARGS (timeout)); |
541 | | |
542 | | /* FIXME : update current pending timeout if we're running */ |
543 | 345 | DISCO_LOCK (dc); |
544 | 345 | dc->priv->timeout = timeout; |
545 | 345 | DISCO_UNLOCK (dc); |
546 | 345 | } |
547 | | |
548 | | static GstPadProbeReturn |
549 | | _event_probe (GstPad * pad, GstPadProbeInfo * info, PrivateStream * ps) |
550 | 2.76k | { |
551 | 2.76k | GstEvent *event = GST_PAD_PROBE_INFO_EVENT (info); |
552 | | |
553 | 2.76k | switch (GST_EVENT_TYPE (event)) { |
554 | 0 | case GST_EVENT_TOC:{ |
555 | 0 | GstToc *tmp; |
556 | |
|
557 | 0 | gst_event_parse_toc (event, &tmp, NULL); |
558 | 0 | GST_DEBUG_OBJECT (pad, "toc %" GST_PTR_FORMAT, tmp); |
559 | 0 | DISCO_LOCK (ps->dc); |
560 | 0 | ps->toc = tmp; |
561 | 0 | if (G_LIKELY (ps->dc->priv->processing)) { |
562 | 0 | GST_DEBUG_OBJECT (pad, "private stream %p toc %" GST_PTR_FORMAT, ps, |
563 | 0 | tmp); |
564 | 0 | } else |
565 | 0 | GST_DEBUG_OBJECT (pad, "Dropping toc since preroll is done"); |
566 | 0 | DISCO_UNLOCK (ps->dc); |
567 | 0 | break; |
568 | 0 | } |
569 | 542 | case GST_EVENT_STREAM_START:{ |
570 | 542 | const gchar *stream_id; |
571 | | |
572 | 542 | gst_event_parse_stream_start (event, &stream_id); |
573 | | |
574 | 542 | g_free (ps->stream_id); |
575 | 542 | ps->stream_id = stream_id ? g_strdup (stream_id) : NULL; |
576 | 542 | break; |
577 | 0 | } |
578 | 2.22k | default: |
579 | 2.22k | break; |
580 | 2.76k | } |
581 | | |
582 | 2.76k | return GST_PAD_PROBE_OK; |
583 | 2.76k | } |
584 | | |
585 | | static GstStaticCaps subtitle_caps = |
586 | | GST_STATIC_CAPS |
587 | | ("application/x-ssa; application/x-ass; application/x-kate"); |
588 | | |
589 | | static gboolean |
590 | | is_subtitle_caps (const GstCaps * caps) |
591 | 557 | { |
592 | 557 | GstCaps *subs_caps; |
593 | 557 | GstStructure *s; |
594 | 557 | const gchar *name; |
595 | 557 | gboolean ret; |
596 | | |
597 | 557 | s = gst_caps_get_structure (caps, 0); |
598 | 557 | if (!s) |
599 | 0 | return FALSE; |
600 | | |
601 | 557 | name = gst_structure_get_name (s); |
602 | 557 | if (g_str_has_prefix (name, "text/") || |
603 | 26 | g_str_has_prefix (name, "subpicture/") || |
604 | 26 | g_str_has_prefix (name, "subtitle/") || |
605 | 26 | g_str_has_prefix (name, "closedcaption/") || |
606 | 26 | g_str_has_prefix (name, "application/x-subtitle")) |
607 | 535 | return TRUE; |
608 | | |
609 | 22 | subs_caps = gst_static_caps_get (&subtitle_caps); |
610 | 22 | ret = gst_caps_can_intersect (caps, subs_caps); |
611 | 22 | gst_caps_unref (subs_caps); |
612 | | |
613 | 22 | return ret; |
614 | 557 | } |
615 | | |
616 | | static GstPadProbeReturn |
617 | | got_subtitle_data (GstPad * pad, GstPadProbeInfo * info, GstDiscoverer * dc) |
618 | 2.60k | { |
619 | 2.60k | GstMessage *msg; |
620 | | |
621 | 2.60k | if (!(GST_IS_BUFFER (info->data) || (GST_IS_EVENT (info->data) |
622 | 2.46k | && (GST_EVENT_TYPE ((GstEvent *) info->data) == GST_EVENT_GAP |
623 | 2.46k | || GST_EVENT_TYPE ((GstEvent *) info->data) == |
624 | 2.46k | GST_EVENT_EOS)))) |
625 | 2.46k | return GST_PAD_PROBE_OK; |
626 | | |
627 | | |
628 | 141 | DISCO_LOCK (dc); |
629 | | |
630 | 141 | dc->priv->pending_subtitle_pads--; |
631 | | |
632 | 141 | msg = gst_message_new_application (NULL, |
633 | 141 | gst_structure_new_empty ("DiscovererDone")); |
634 | 141 | gst_element_post_message ((GstElement *) dc->priv->pipeline, msg); |
635 | | |
636 | 141 | DISCO_UNLOCK (dc); |
637 | | |
638 | 141 | return GST_PAD_PROBE_REMOVE; |
639 | | |
640 | 2.60k | } |
641 | | |
642 | | static void |
643 | | uridecodebin_source_changed_cb (GstElement * uridecodebin, |
644 | | GParamSpec * pspec, GstDiscoverer * dc) |
645 | 345 | { |
646 | 345 | GstElement *src; |
647 | | /* get a handle to the source */ |
648 | 345 | g_object_get (uridecodebin, pspec->name, &src, NULL); |
649 | | |
650 | 345 | GST_DEBUG_OBJECT (dc, "got a new source %p", src); |
651 | | |
652 | 345 | g_signal_emit (dc, gst_discoverer_signals[SIGNAL_SOURCE_SETUP], 0, src); |
653 | 345 | gst_object_unref (src); |
654 | 345 | } |
655 | | |
656 | | static void |
657 | | uridecodebin_pad_added_cb (GstElement * uridecodebin, GstPad * pad, |
658 | | GstDiscoverer * dc) |
659 | 547 | { |
660 | 547 | PrivateStream *ps; |
661 | 547 | GstPad *sinkpad = NULL; |
662 | 547 | GstCaps *caps; |
663 | 547 | gchar *padname; |
664 | 547 | gchar *tmpname; |
665 | | |
666 | 547 | GST_DEBUG_OBJECT (dc, "pad %s:%s", GST_DEBUG_PAD_NAME (pad)); |
667 | | |
668 | 547 | DISCO_LOCK (dc); |
669 | 547 | if (dc->priv->cleanup) { |
670 | 5 | GST_WARNING_OBJECT (dc, "Cleanup, not adding pad"); |
671 | 5 | DISCO_UNLOCK (dc); |
672 | 5 | return; |
673 | 5 | } |
674 | 542 | if (dc->priv->current_error) { |
675 | 0 | GST_WARNING_OBJECT (dc, "Ongoing error, not adding more pads"); |
676 | 0 | DISCO_UNLOCK (dc); |
677 | 0 | return; |
678 | 0 | } |
679 | 542 | ps = g_new0 (PrivateStream, 1); |
680 | | |
681 | 542 | ps->dc = dc; |
682 | 542 | ps->pad = pad; |
683 | 542 | padname = gst_pad_get_name (pad); |
684 | 542 | tmpname = g_strdup_printf ("discoverer-queue-%s", padname); |
685 | 542 | ps->queue = gst_element_factory_make ("queue", tmpname); |
686 | 542 | g_free (tmpname); |
687 | 542 | tmpname = g_strdup_printf ("discoverer-sink-%s", padname); |
688 | 542 | ps->sink = gst_element_factory_make ("fakesink", tmpname); |
689 | 542 | g_free (tmpname); |
690 | 542 | g_free (padname); |
691 | | |
692 | 542 | if (G_UNLIKELY (ps->queue == NULL || ps->sink == NULL)) |
693 | 0 | goto error; |
694 | | |
695 | 542 | g_object_set (ps->sink, "silent", TRUE, NULL); |
696 | 542 | g_object_set (ps->queue, "max-size-buffers", 1, "silent", TRUE, NULL); |
697 | | |
698 | 542 | sinkpad = gst_element_get_static_pad (ps->queue, "sink"); |
699 | 542 | if (sinkpad == NULL) |
700 | 0 | goto error; |
701 | | |
702 | 542 | caps = gst_pad_get_current_caps (pad); |
703 | 542 | if (!caps) { |
704 | 0 | GST_WARNING ("Couldn't get negotiated caps from %s:%s", |
705 | 0 | GST_DEBUG_PAD_NAME (pad)); |
706 | 0 | caps = gst_pad_query_caps (pad, NULL); |
707 | 0 | } |
708 | | |
709 | 542 | if (caps && !gst_caps_is_empty (caps) && !gst_caps_is_any (caps) |
710 | 542 | && is_subtitle_caps (caps)) { |
711 | | /* Subtitle streams are sparse and may not provide any information - don't |
712 | | * wait for data to preroll */ |
713 | 520 | ps->probe_id = |
714 | 520 | gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM, |
715 | 520 | (GstPadProbeCallback) got_subtitle_data, dc, NULL); |
716 | 520 | g_object_set (ps->sink, "async", FALSE, NULL); |
717 | 520 | dc->priv->pending_subtitle_pads++; |
718 | 520 | } |
719 | | |
720 | 542 | if (caps) |
721 | 542 | gst_caps_unref (caps); |
722 | | |
723 | 542 | gst_bin_add_many (dc->priv->pipeline, ps->queue, ps->sink, NULL); |
724 | | |
725 | 542 | if (!gst_element_link_pads_full (ps->queue, "src", ps->sink, "sink", |
726 | 542 | GST_PAD_LINK_CHECK_NOTHING)) |
727 | 0 | goto error; |
728 | 542 | if (!gst_element_sync_state_with_parent (ps->sink)) |
729 | 0 | goto error; |
730 | 542 | if (!gst_element_sync_state_with_parent (ps->queue)) |
731 | 0 | goto error; |
732 | | |
733 | 542 | if (gst_pad_link_full (pad, sinkpad, |
734 | 542 | GST_PAD_LINK_CHECK_NOTHING) != GST_PAD_LINK_OK) |
735 | 0 | goto error; |
736 | 542 | gst_object_unref (sinkpad); |
737 | | |
738 | | /* Add an event probe */ |
739 | 542 | gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM, |
740 | 542 | (GstPadProbeCallback) _event_probe, ps, NULL); |
741 | | |
742 | 542 | dc->priv->streams = g_list_append (dc->priv->streams, ps); |
743 | 542 | DISCO_UNLOCK (dc); |
744 | | |
745 | 542 | GST_DEBUG_OBJECT (dc, "Done handling pad"); |
746 | | |
747 | 542 | return; |
748 | | |
749 | 0 | error: |
750 | 0 | GST_ERROR_OBJECT (dc, "Error while handling pad"); |
751 | 0 | if (sinkpad) |
752 | 0 | gst_object_unref (sinkpad); |
753 | 0 | if (ps->queue) |
754 | 0 | gst_object_unref (ps->queue); |
755 | 0 | if (ps->sink) |
756 | 0 | gst_object_unref (ps->sink); |
757 | 0 | g_free (ps); |
758 | 0 | DISCO_UNLOCK (dc); |
759 | 0 | return; |
760 | 542 | } |
761 | | |
762 | | static void |
763 | | uridecodebin_no_more_pads_cb (GstElement * uridecodebin, GstDiscoverer * dc) |
764 | 503 | { |
765 | 503 | GstMessage *msg = gst_message_new_application (NULL, |
766 | 503 | gst_structure_new_empty ("DiscovererDone")); |
767 | | |
768 | 503 | DISCO_LOCK (dc); |
769 | 503 | dc->priv->no_more_pads = TRUE; |
770 | 503 | gst_element_post_message ((GstElement *) dc->priv->pipeline, msg); |
771 | 503 | DISCO_UNLOCK (dc); |
772 | 503 | } |
773 | | |
774 | | static void |
775 | | uridecodebin_pad_removed_cb (GstElement * uridecodebin, GstPad * pad, |
776 | | GstDiscoverer * dc) |
777 | 547 | { |
778 | 547 | GList *tmp; |
779 | 547 | PrivateStream *ps; |
780 | 547 | GstPad *sinkpad; |
781 | | |
782 | 547 | GST_DEBUG_OBJECT (dc, "pad %s:%s", GST_DEBUG_PAD_NAME (pad)); |
783 | | |
784 | | /* Find the PrivateStream */ |
785 | 547 | DISCO_LOCK (dc); |
786 | 607 | for (tmp = dc->priv->streams; tmp; tmp = tmp->next) { |
787 | 602 | ps = (PrivateStream *) tmp->data; |
788 | 602 | if (ps->pad == pad) |
789 | 542 | break; |
790 | 602 | } |
791 | | |
792 | 547 | if (tmp == NULL) { |
793 | 5 | DISCO_UNLOCK (dc); |
794 | 5 | GST_DEBUG_OBJECT (dc, "The removed pad wasn't controlled by us !"); |
795 | 5 | return; |
796 | 5 | } |
797 | | |
798 | 542 | if (ps->probe_id) |
799 | 520 | gst_pad_remove_probe (pad, ps->probe_id); |
800 | | |
801 | 542 | dc->priv->streams = g_list_delete_link (dc->priv->streams, tmp); |
802 | | |
803 | 542 | gst_element_set_state (ps->sink, GST_STATE_NULL); |
804 | 542 | gst_element_set_state (ps->queue, GST_STATE_NULL); |
805 | 542 | gst_element_unlink (ps->queue, ps->sink); |
806 | | |
807 | 542 | sinkpad = gst_element_get_static_pad (ps->queue, "sink"); |
808 | 542 | gst_pad_unlink (pad, sinkpad); |
809 | 542 | gst_object_unref (sinkpad); |
810 | | |
811 | | /* references removed here */ |
812 | 542 | gst_bin_remove_many (dc->priv->pipeline, ps->sink, ps->queue, NULL); |
813 | | |
814 | 542 | DISCO_UNLOCK (dc); |
815 | 542 | if (ps->tags) { |
816 | 24 | gst_tag_list_unref (ps->tags); |
817 | 24 | } |
818 | 542 | if (ps->toc) { |
819 | 0 | gst_toc_unref (ps->toc); |
820 | 0 | } |
821 | 542 | g_free (ps->stream_id); |
822 | | |
823 | 542 | g_free (ps); |
824 | | |
825 | 542 | GST_DEBUG_OBJECT (dc, "Done handling pad"); |
826 | 542 | } |
827 | | |
828 | | static GstStructure * |
829 | | collect_stream_information (GstDiscoverer * dc, PrivateStream * ps, guint idx) |
830 | 9 | { |
831 | 9 | GstCaps *caps; |
832 | 9 | GstStructure *st; |
833 | 9 | gchar *stname; |
834 | | |
835 | 9 | stname = g_strdup_printf ("stream-%02d", idx); |
836 | 9 | st = gst_structure_new_empty (stname); |
837 | 9 | g_free (stname); |
838 | | |
839 | | /* Get caps */ |
840 | 9 | caps = gst_pad_get_current_caps (ps->pad); |
841 | 9 | if (!caps) { |
842 | 0 | GST_WARNING ("Couldn't get negotiated caps from %s:%s", |
843 | 0 | GST_DEBUG_PAD_NAME (ps->pad)); |
844 | 0 | caps = gst_pad_query_caps (ps->pad, NULL); |
845 | 0 | } |
846 | 9 | if (caps) { |
847 | 9 | GST_DEBUG_OBJECT (dc, "stream-%02d, got caps %" GST_PTR_FORMAT, idx, caps); |
848 | 9 | gst_structure_set_static_str (st, "caps", GST_TYPE_CAPS, caps, NULL); |
849 | 9 | gst_caps_unref (caps); |
850 | 9 | } |
851 | 9 | if (ps->tags) |
852 | 4 | gst_structure_set_static_str (st, "tags", GST_TYPE_TAG_LIST, ps->tags, |
853 | 4 | NULL); |
854 | 9 | if (ps->toc) |
855 | 0 | gst_structure_set_static_str (st, "toc", GST_TYPE_TOC, ps->toc, NULL); |
856 | 9 | if (ps->stream_id) |
857 | 9 | gst_structure_set_static_str (st, "stream-id", G_TYPE_STRING, ps->stream_id, |
858 | 9 | NULL); |
859 | | |
860 | 9 | return st; |
861 | 9 | } |
862 | | |
863 | | /* takes ownership of new_tags, may replace *taglist with a new one */ |
864 | | static void |
865 | | gst_discoverer_merge_and_replace_tags (GstTagList ** taglist, |
866 | | GstTagList * new_tags) |
867 | 4 | { |
868 | 4 | if (new_tags == NULL) |
869 | 0 | return; |
870 | | |
871 | 4 | if (*taglist == NULL) { |
872 | 4 | *taglist = new_tags; |
873 | 4 | return; |
874 | 4 | } |
875 | | |
876 | 0 | gst_tag_list_insert (*taglist, new_tags, GST_TAG_MERGE_REPLACE); |
877 | 0 | gst_tag_list_unref (new_tags); |
878 | 0 | } |
879 | | |
880 | | static void |
881 | | collect_common_information (GstDiscovererStreamInfo * info, |
882 | | const GstStructure * st) |
883 | 17 | { |
884 | 17 | if (gst_structure_has_field (st, "toc")) { |
885 | 0 | gst_structure_get (st, "toc", GST_TYPE_TOC, &info->toc, NULL); |
886 | 0 | } |
887 | | |
888 | 17 | if (gst_structure_has_field (st, "stream-id")) { |
889 | 9 | gst_structure_get (st, "stream-id", G_TYPE_STRING, &info->stream_id, NULL); |
890 | 9 | } |
891 | 17 | } |
892 | | |
893 | | static GstDiscovererStreamInfo * |
894 | | make_info (GstDiscovererStreamInfo * parent, GType type, GstCaps * caps) |
895 | 18 | { |
896 | 18 | GstDiscovererStreamInfo *info; |
897 | | |
898 | 18 | if (parent) |
899 | 8 | info = gst_discoverer_stream_info_ref (parent); |
900 | 10 | else { |
901 | 10 | info = g_object_new (type, NULL); |
902 | 10 | if (caps) |
903 | 9 | info->caps = gst_caps_ref (caps); |
904 | 10 | } |
905 | 18 | return info; |
906 | 18 | } |
907 | | |
908 | | /* Parses a set of caps and tags in st and populates a GstDiscovererStreamInfo |
909 | | * structure (parent, if !NULL, otherwise it allocates one) |
910 | | */ |
911 | | static GstDiscovererStreamInfo * |
912 | | collect_information (GstDiscoverer * dc, const GstStructure * st, |
913 | | GstDiscovererStreamInfo * parent) |
914 | 18 | { |
915 | 18 | GstPad *srcpad; |
916 | 18 | GstCaps *caps = NULL; |
917 | 18 | GstStructure *caps_st; |
918 | 18 | GstTagList *tags_st; |
919 | 18 | const gchar *name; |
920 | 18 | gint tmp, tmp2; |
921 | 18 | guint utmp; |
922 | | |
923 | 18 | if (!st || (!gst_structure_has_field (st, "caps") |
924 | 0 | && !gst_structure_has_field (st, "element-srcpad"))) { |
925 | 0 | GST_WARNING ("Couldn't find caps !"); |
926 | 0 | return make_info (parent, GST_TYPE_DISCOVERER_STREAM_INFO, NULL); |
927 | 0 | } |
928 | | |
929 | 18 | if (gst_structure_get (st, "element-srcpad", GST_TYPE_PAD, &srcpad, NULL)) { |
930 | 9 | caps = gst_pad_get_current_caps (srcpad); |
931 | 9 | gst_object_unref (srcpad); |
932 | 9 | } |
933 | 18 | if (!caps) { |
934 | 16 | gst_structure_get (st, "caps", GST_TYPE_CAPS, &caps, NULL); |
935 | 16 | } |
936 | | |
937 | 18 | if (!caps || gst_caps_is_empty (caps) || gst_caps_is_any (caps)) { |
938 | 1 | GST_WARNING ("Couldn't find caps !"); |
939 | 1 | if (caps) |
940 | 1 | gst_caps_unref (caps); |
941 | 1 | return make_info (parent, GST_TYPE_DISCOVERER_STREAM_INFO, NULL); |
942 | 1 | } |
943 | | |
944 | 17 | caps_st = gst_caps_get_structure (caps, 0); |
945 | 17 | name = gst_structure_get_name (caps_st); |
946 | | |
947 | 17 | if (g_str_has_prefix (name, "audio/")) { |
948 | 4 | GstDiscovererAudioInfo *info; |
949 | 4 | const gchar *format_str; |
950 | 4 | guint64 channel_mask; |
951 | | |
952 | 4 | info = (GstDiscovererAudioInfo *) make_info (parent, |
953 | 4 | GST_TYPE_DISCOVERER_AUDIO_INFO, caps); |
954 | | |
955 | 4 | if (gst_structure_get_int (caps_st, "rate", &tmp)) |
956 | 4 | info->sample_rate = (guint) tmp; |
957 | | |
958 | 4 | if (gst_structure_get_int (caps_st, "channels", &tmp)) |
959 | 4 | info->channels = (guint) tmp; |
960 | | |
961 | 4 | if (gst_structure_get (caps_st, "channel-mask", GST_TYPE_BITMASK, |
962 | 4 | &channel_mask, NULL)) { |
963 | 0 | info->channel_mask = channel_mask; |
964 | 4 | } else if (info->channels) { |
965 | 4 | info->channel_mask = gst_audio_channel_get_fallback_mask (info->channels); |
966 | 4 | } |
967 | | |
968 | | /* FIXME: we only want to extract depth if raw audio is what's in the |
969 | | * container (i.e. not if there is a decoder involved) */ |
970 | 4 | format_str = gst_structure_get_string (caps_st, "format"); |
971 | 4 | if (format_str != NULL) { |
972 | 2 | const GstAudioFormatInfo *finfo; |
973 | 2 | GstAudioFormat format; |
974 | | |
975 | 2 | format = gst_audio_format_from_string (format_str); |
976 | 2 | finfo = gst_audio_format_get_info (format); |
977 | 2 | if (finfo) |
978 | 2 | info->depth = GST_AUDIO_FORMAT_INFO_DEPTH (finfo); |
979 | 2 | } |
980 | | |
981 | 4 | if (gst_structure_has_field (st, "tags")) { |
982 | 2 | gst_structure_get (st, "tags", GST_TYPE_TAG_LIST, &tags_st, NULL); |
983 | 2 | if (gst_tag_list_get_uint (tags_st, GST_TAG_BITRATE, &utmp) || |
984 | 0 | gst_tag_list_get_uint (tags_st, GST_TAG_NOMINAL_BITRATE, &utmp)) |
985 | 2 | info->bitrate = utmp; |
986 | | |
987 | 2 | if (gst_tag_list_get_uint (tags_st, GST_TAG_MAXIMUM_BITRATE, &utmp)) |
988 | 0 | info->max_bitrate = utmp; |
989 | | |
990 | | /* FIXME: Is it worth it to remove the tags we've parsed? */ |
991 | 2 | gst_discoverer_merge_and_replace_tags (&info->parent.tags, tags_st); |
992 | 2 | } |
993 | | |
994 | 4 | collect_common_information (&info->parent, st); |
995 | | |
996 | 4 | if (!info->language && ((GstDiscovererStreamInfo *) info)->tags) { |
997 | 2 | gchar *language; |
998 | 2 | if (gst_tag_list_get_string (((GstDiscovererStreamInfo *) info)->tags, |
999 | 2 | GST_TAG_LANGUAGE_CODE, &language)) { |
1000 | 0 | info->language = language; |
1001 | 0 | } |
1002 | 2 | } |
1003 | | |
1004 | 4 | gst_caps_unref (caps); |
1005 | 4 | return (GstDiscovererStreamInfo *) info; |
1006 | | |
1007 | 13 | } else if (g_str_has_prefix (name, "video/") || |
1008 | 13 | g_str_has_prefix (name, "image/")) { |
1009 | 0 | GstDiscovererVideoInfo *info; |
1010 | 0 | const gchar *caps_str; |
1011 | |
|
1012 | 0 | info = (GstDiscovererVideoInfo *) make_info (parent, |
1013 | 0 | GST_TYPE_DISCOVERER_VIDEO_INFO, caps); |
1014 | |
|
1015 | 0 | if (gst_structure_get_int (caps_st, "width", &tmp)) |
1016 | 0 | info->width = (guint) tmp; |
1017 | 0 | if (gst_structure_get_int (caps_st, "height", &tmp)) |
1018 | 0 | info->height = (guint) tmp; |
1019 | |
|
1020 | 0 | if (gst_structure_get_fraction (caps_st, "framerate", &tmp, &tmp2)) { |
1021 | 0 | info->framerate_num = (guint) tmp; |
1022 | 0 | info->framerate_denom = (guint) tmp2; |
1023 | 0 | } else { |
1024 | 0 | info->framerate_num = 0; |
1025 | 0 | info->framerate_denom = 1; |
1026 | 0 | } |
1027 | |
|
1028 | 0 | if (gst_structure_get_fraction (caps_st, "pixel-aspect-ratio", &tmp, &tmp2)) { |
1029 | 0 | info->par_num = (guint) tmp; |
1030 | 0 | info->par_denom = (guint) tmp2; |
1031 | 0 | } else { |
1032 | 0 | info->par_num = 1; |
1033 | 0 | info->par_denom = 1; |
1034 | 0 | } |
1035 | | |
1036 | | /* FIXME: we only want to extract depth if raw video is what's in the |
1037 | | * container (i.e. not if there is a decoder involved) */ |
1038 | 0 | caps_str = gst_structure_get_string (caps_st, "format"); |
1039 | 0 | if (caps_str != NULL) { |
1040 | 0 | const GstVideoFormatInfo *finfo; |
1041 | 0 | GstVideoFormat format; |
1042 | |
|
1043 | 0 | format = gst_video_format_from_string (caps_str); |
1044 | 0 | finfo = gst_video_format_get_info (format); |
1045 | 0 | if (finfo) |
1046 | 0 | info->depth = finfo->bits * finfo->n_components; |
1047 | 0 | } |
1048 | |
|
1049 | 0 | caps_str = gst_structure_get_string (caps_st, "interlace-mode"); |
1050 | 0 | if (!caps_str || strcmp (caps_str, "progressive") == 0) |
1051 | 0 | info->interlaced = FALSE; |
1052 | 0 | else |
1053 | 0 | info->interlaced = TRUE; |
1054 | |
|
1055 | 0 | if (gst_structure_has_field (st, "tags")) { |
1056 | 0 | gst_structure_get (st, "tags", GST_TYPE_TAG_LIST, &tags_st, NULL); |
1057 | 0 | if (gst_tag_list_get_uint (tags_st, GST_TAG_BITRATE, &utmp) || |
1058 | 0 | gst_tag_list_get_uint (tags_st, GST_TAG_NOMINAL_BITRATE, &utmp)) |
1059 | 0 | info->bitrate = utmp; |
1060 | |
|
1061 | 0 | if (gst_tag_list_get_uint (tags_st, GST_TAG_MAXIMUM_BITRATE, &utmp)) |
1062 | 0 | info->max_bitrate = utmp; |
1063 | | |
1064 | | /* FIXME: Is it worth it to remove the tags we've parsed? */ |
1065 | 0 | gst_discoverer_merge_and_replace_tags (&info->parent.tags, tags_st); |
1066 | 0 | } |
1067 | |
|
1068 | 0 | collect_common_information (&info->parent, st); |
1069 | |
|
1070 | 0 | gst_caps_unref (caps); |
1071 | 0 | return (GstDiscovererStreamInfo *) info; |
1072 | |
|
1073 | 13 | } else if (is_subtitle_caps (caps)) { |
1074 | 13 | GstDiscovererSubtitleInfo *info; |
1075 | | |
1076 | 13 | info = (GstDiscovererSubtitleInfo *) make_info (parent, |
1077 | 13 | GST_TYPE_DISCOVERER_SUBTITLE_INFO, caps); |
1078 | | |
1079 | 13 | if (gst_structure_has_field (st, "tags")) { |
1080 | 2 | const gchar *language; |
1081 | | |
1082 | 2 | gst_structure_get (st, "tags", GST_TYPE_TAG_LIST, &tags_st, NULL); |
1083 | | |
1084 | 2 | language = gst_structure_get_string (caps_st, GST_TAG_LANGUAGE_CODE); |
1085 | 2 | if (language) |
1086 | 0 | info->language = g_strdup (language); |
1087 | | |
1088 | | /* FIXME: Is it worth it to remove the tags we've parsed? */ |
1089 | 2 | gst_discoverer_merge_and_replace_tags (&info->parent.tags, tags_st); |
1090 | 2 | } |
1091 | | |
1092 | 13 | collect_common_information (&info->parent, st); |
1093 | | |
1094 | 13 | if (!info->language && ((GstDiscovererStreamInfo *) info)->tags) { |
1095 | 2 | gchar *language; |
1096 | 2 | if (gst_tag_list_get_string (((GstDiscovererStreamInfo *) info)->tags, |
1097 | 2 | GST_TAG_LANGUAGE_CODE, &language)) { |
1098 | 0 | info->language = language; |
1099 | 0 | } |
1100 | 2 | } |
1101 | | |
1102 | 13 | gst_caps_unref (caps); |
1103 | 13 | return (GstDiscovererStreamInfo *) info; |
1104 | | |
1105 | 13 | } else { |
1106 | | /* None of the above - populate what information we can */ |
1107 | 0 | GstDiscovererStreamInfo *info; |
1108 | |
|
1109 | 0 | info = make_info (parent, GST_TYPE_DISCOVERER_STREAM_INFO, caps); |
1110 | |
|
1111 | 0 | if (gst_structure_get (st, "tags", GST_TYPE_TAG_LIST, &tags_st, NULL)) { |
1112 | 0 | gst_discoverer_merge_and_replace_tags (&info->tags, tags_st); |
1113 | 0 | } |
1114 | |
|
1115 | 0 | collect_common_information (info, st); |
1116 | |
|
1117 | 0 | gst_caps_unref (caps); |
1118 | 0 | return info; |
1119 | 0 | } |
1120 | | |
1121 | 17 | } |
1122 | | |
1123 | | static GstStructure * |
1124 | | find_stream_for_node (GstDiscoverer * dc, const GstStructure * topology) |
1125 | 18 | { |
1126 | 18 | GstPad *pad; |
1127 | 18 | GstPad *target_pad = NULL; |
1128 | 18 | GstStructure *st = NULL; |
1129 | 18 | PrivateStream *ps; |
1130 | 18 | guint i; |
1131 | 18 | GList *tmp; |
1132 | | |
1133 | 18 | if (!dc->priv->streams) { |
1134 | 0 | return NULL; |
1135 | 0 | } |
1136 | | |
1137 | 18 | if (!gst_structure_has_field (topology, "pad")) { |
1138 | 9 | GST_DEBUG_OBJECT (dc, "Could not find pad for node %" GST_PTR_FORMAT, |
1139 | 9 | topology); |
1140 | 9 | return NULL; |
1141 | 9 | } |
1142 | | |
1143 | 9 | gst_structure_get (topology, "pad", GST_TYPE_PAD, &pad, NULL); |
1144 | | |
1145 | 15 | for (i = 0, tmp = dc->priv->streams; tmp; tmp = tmp->next, i++) { |
1146 | 15 | ps = (PrivateStream *) tmp->data; |
1147 | | |
1148 | 15 | target_pad = gst_ghost_pad_get_target (GST_GHOST_PAD (ps->pad)); |
1149 | 15 | if (target_pad == NULL) |
1150 | 0 | continue; |
1151 | 15 | gst_object_unref (target_pad); |
1152 | | |
1153 | 15 | if (target_pad == pad) |
1154 | 9 | break; |
1155 | 15 | } |
1156 | | |
1157 | 9 | if (tmp) |
1158 | 9 | st = collect_stream_information (dc, ps, i); |
1159 | | |
1160 | 9 | gst_object_unref (pad); |
1161 | | |
1162 | 9 | return st; |
1163 | 18 | } |
1164 | | |
1165 | | /* this can fail due to {framed,parsed}={TRUE,FALSE} differences, thus we filter |
1166 | | * the parent */ |
1167 | | static gboolean |
1168 | | child_is_same_stream (const GstCaps * _parent, const GstCaps * child) |
1169 | 9 | { |
1170 | 9 | GstCaps *parent; |
1171 | 9 | gboolean res; |
1172 | | |
1173 | 9 | if (_parent == child) |
1174 | 4 | return TRUE; |
1175 | 5 | if (!_parent) |
1176 | 1 | return FALSE; |
1177 | 4 | if (!child) |
1178 | 0 | return FALSE; |
1179 | | |
1180 | 4 | parent = copy_and_clean_caps (_parent); |
1181 | 4 | res = gst_caps_can_intersect (parent, child); |
1182 | 4 | gst_caps_unref (parent); |
1183 | 4 | return res; |
1184 | 4 | } |
1185 | | |
1186 | | |
1187 | | static gboolean |
1188 | | child_is_raw_stream (const GstCaps * parent, const GstCaps * child) |
1189 | 5 | { |
1190 | 5 | const GstStructure *st1, *st2; |
1191 | 5 | const gchar *name1, *name2; |
1192 | | |
1193 | 5 | if (parent == child) |
1194 | 0 | return TRUE; |
1195 | 5 | if (!parent) |
1196 | 1 | return FALSE; |
1197 | 4 | if (!child) |
1198 | 0 | return FALSE; |
1199 | | |
1200 | 4 | st1 = gst_caps_get_structure (parent, 0); |
1201 | 4 | name1 = gst_structure_get_name (st1); |
1202 | 4 | st2 = gst_caps_get_structure (child, 0); |
1203 | 4 | name2 = gst_structure_get_name (st2); |
1204 | | |
1205 | 4 | if ((g_str_has_prefix (name1, "audio/") && |
1206 | 2 | g_str_has_prefix (name2, "audio/x-raw")) || |
1207 | 2 | ((g_str_has_prefix (name1, "video/") || |
1208 | 2 | g_str_has_prefix (name1, "image/")) && |
1209 | 2 | g_str_has_prefix (name2, "video/x-raw"))) { |
1210 | | /* child is the "raw" sub-stream corresponding to parent */ |
1211 | 2 | return TRUE; |
1212 | 2 | } |
1213 | | |
1214 | 2 | if (is_subtitle_caps (parent)) |
1215 | 2 | return TRUE; |
1216 | | |
1217 | 0 | return FALSE; |
1218 | 2 | } |
1219 | | |
1220 | | /* If a parent is non-NULL, collected stream information will be appended to it |
1221 | | * (and where the information exists, it will be overridden) |
1222 | | */ |
1223 | | static GstDiscovererStreamInfo * |
1224 | | parse_stream_topology (GstDiscoverer * dc, const GstStructure * topology, |
1225 | | GstDiscovererStreamInfo * parent) |
1226 | 21 | { |
1227 | 21 | GstDiscovererStreamInfo *res = NULL; |
1228 | 21 | GstCaps *caps = NULL; |
1229 | 21 | const GValue *nval = NULL; |
1230 | | |
1231 | 21 | GST_DEBUG_OBJECT (dc, "parsing: %" GST_PTR_FORMAT, topology); |
1232 | | |
1233 | 21 | nval = gst_structure_get_value (topology, "next"); |
1234 | | |
1235 | 21 | if (nval == NULL || GST_VALUE_HOLDS_STRUCTURE (nval)) { |
1236 | 18 | GstStructure *st = find_stream_for_node (dc, topology); |
1237 | 18 | gboolean add_to_list = TRUE; |
1238 | | |
1239 | 18 | if (st) { |
1240 | 9 | res = collect_information (dc, st, parent); |
1241 | 9 | gst_structure_free (st); |
1242 | 9 | } else { |
1243 | | /* Didn't find a stream structure, so let's just use the caps we have */ |
1244 | 9 | res = collect_information (dc, topology, parent); |
1245 | 9 | } |
1246 | | |
1247 | 18 | if (nval == NULL) { |
1248 | | /* FIXME : aggregate with information from main streams */ |
1249 | 9 | GST_DEBUG_OBJECT (dc, "Couldn't find 'next' ! might be the last entry"); |
1250 | 9 | } else { |
1251 | 9 | GstPad *srcpad; |
1252 | | |
1253 | 9 | st = (GstStructure *) gst_value_get_structure (nval); |
1254 | | |
1255 | 9 | GST_DEBUG_OBJECT (dc, "next is a structure %" GST_PTR_FORMAT, st); |
1256 | | |
1257 | 9 | if (!parent) |
1258 | 9 | parent = res; |
1259 | | |
1260 | 9 | if (gst_structure_get (st, "element-srcpad", GST_TYPE_PAD, &srcpad, NULL)) { |
1261 | 9 | caps = gst_pad_get_current_caps (srcpad); |
1262 | 9 | gst_object_unref (srcpad); |
1263 | 9 | } |
1264 | 9 | if (!caps) { |
1265 | 0 | gst_structure_get (st, "caps", GST_TYPE_CAPS, &caps, NULL); |
1266 | 0 | } |
1267 | | |
1268 | 9 | if (caps) { |
1269 | 9 | if (child_is_same_stream (parent->caps, caps)) { |
1270 | | /* We sometimes get an extra sub-stream from the parser. If this is |
1271 | | * the case, we just replace the parent caps with this stream's caps |
1272 | | * since they might contain more information */ |
1273 | 4 | gst_caps_replace (&parent->caps, caps); |
1274 | | |
1275 | 4 | parse_stream_topology (dc, st, parent); |
1276 | 4 | add_to_list = FALSE; |
1277 | 5 | } else if (child_is_raw_stream (parent->caps, caps)) { |
1278 | | /* This is the "raw" stream corresponding to the parent. This |
1279 | | * contains more information than the parent, tags etc. */ |
1280 | 4 | parse_stream_topology (dc, st, parent); |
1281 | 4 | add_to_list = FALSE; |
1282 | 4 | } else { |
1283 | 1 | GstDiscovererStreamInfo *next = parse_stream_topology (dc, st, NULL); |
1284 | 1 | res->next = next; |
1285 | 1 | next->previous = res; |
1286 | 1 | } |
1287 | 9 | gst_caps_unref (caps); |
1288 | 9 | } |
1289 | 9 | } |
1290 | | |
1291 | 18 | if (add_to_list) { |
1292 | 10 | res->stream_number = dc->priv->current_info_stream_count++; |
1293 | 10 | dc->priv->current_info->stream_list = |
1294 | 10 | g_list_append (dc->priv->current_info->stream_list, res); |
1295 | 10 | } else { |
1296 | 8 | gst_discoverer_stream_info_unref (res); |
1297 | 8 | } |
1298 | | |
1299 | 18 | } else if (GST_VALUE_HOLDS_LIST (nval)) { |
1300 | 3 | guint i, len; |
1301 | 3 | GstDiscovererContainerInfo *cont; |
1302 | 3 | GstPad *srcpad; |
1303 | | |
1304 | 3 | if (gst_structure_get (topology, "element-srcpad", GST_TYPE_PAD, |
1305 | 3 | &srcpad, NULL)) { |
1306 | 3 | caps = gst_pad_get_current_caps (srcpad); |
1307 | 3 | gst_object_unref (srcpad); |
1308 | 3 | } |
1309 | 3 | if (!caps) { |
1310 | 0 | gst_structure_get (topology, "caps", GST_TYPE_CAPS, &caps, NULL); |
1311 | 0 | } |
1312 | | |
1313 | 3 | if (!caps) |
1314 | 3 | GST_WARNING ("Couldn't find caps !"); |
1315 | | |
1316 | 3 | len = gst_value_list_get_size (nval); |
1317 | 3 | GST_DEBUG_OBJECT (dc, "next is a list of %d entries", len); |
1318 | | |
1319 | 3 | cont = (GstDiscovererContainerInfo *) |
1320 | 3 | g_object_new (GST_TYPE_DISCOVERER_CONTAINER_INFO, NULL); |
1321 | 3 | cont->parent.caps = caps; |
1322 | 3 | if (dc->priv->global_tags) |
1323 | 3 | cont->tags = gst_tag_list_ref (dc->priv->global_tags); |
1324 | 3 | res = (GstDiscovererStreamInfo *) cont; |
1325 | | |
1326 | 10 | for (i = 0; i < len; i++) { |
1327 | 7 | const GValue *subv = gst_value_list_get_value (nval, i); |
1328 | 7 | const GstStructure *subst = gst_value_get_structure (subv); |
1329 | 7 | GstDiscovererStreamInfo *substream; |
1330 | | |
1331 | 7 | GST_DEBUG_OBJECT (dc, "%d %" GST_PTR_FORMAT, i, subst); |
1332 | | |
1333 | 7 | substream = parse_stream_topology (dc, subst, NULL); |
1334 | | |
1335 | 7 | substream->previous = res; |
1336 | 7 | cont->streams = |
1337 | 7 | g_list_append (cont->streams, |
1338 | 7 | gst_discoverer_stream_info_ref (substream)); |
1339 | 7 | } |
1340 | 3 | } |
1341 | | |
1342 | 21 | return res; |
1343 | 21 | } |
1344 | | |
1345 | | /* Required DISCO_LOCK to be taken, and will release it */ |
1346 | | static void |
1347 | | setup_next_uri_locked (GstDiscoverer * dc) |
1348 | 0 | { |
1349 | 0 | if (dc->priv->pending_uris != NULL) { |
1350 | 0 | gboolean ready = _setup_locked (dc); |
1351 | 0 | DISCO_UNLOCK (dc); |
1352 | |
|
1353 | 0 | if (!ready) { |
1354 | | /* Start timeout */ |
1355 | 0 | if (dc->priv->processing) |
1356 | 0 | handle_current_async (dc); |
1357 | 0 | } else { |
1358 | 0 | g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, |
1359 | 0 | (GSourceFunc) emit_discovered_and_next, gst_object_ref (dc), |
1360 | 0 | gst_object_unref); |
1361 | 0 | } |
1362 | 0 | } else { |
1363 | | /* We're done ! */ |
1364 | 0 | DISCO_UNLOCK (dc); |
1365 | 0 | g_signal_emit (dc, gst_discoverer_signals[SIGNAL_FINISHED], 0); |
1366 | 0 | } |
1367 | 0 | } |
1368 | | |
1369 | | static void |
1370 | | _ensure_info_tags (GstDiscoverer * dc) |
1371 | 345 | { |
1372 | 345 | GstDiscovererInfo *info = dc->priv->current_info; |
1373 | | |
1374 | 345 | if (dc->priv->all_tags) |
1375 | 7 | info->tags = dc->priv->all_tags; |
1376 | 345 | dc->priv->all_tags = NULL; |
1377 | 345 | } |
1378 | | |
1379 | | static void |
1380 | | serialize_info_if_required (GstDiscoverer * dc, GstDiscovererInfo * info) |
1381 | 345 | { |
1382 | | |
1383 | 345 | if (dc->priv->use_cache && dc->priv->current_cachefile |
1384 | 0 | && info->result == GST_DISCOVERER_OK) { |
1385 | 0 | GVariant *variant = gst_discoverer_info_to_variant (info, |
1386 | 0 | GST_DISCOVERER_SERIALIZE_ALL); |
1387 | |
|
1388 | 0 | g_file_set_contents (dc->priv->current_cachefile, |
1389 | 0 | g_variant_get_data (variant), g_variant_get_size (variant), NULL); |
1390 | 0 | g_variant_unref (variant); |
1391 | 0 | } |
1392 | 345 | } |
1393 | | |
1394 | | static void |
1395 | | emit_discovered (GstDiscoverer * dc) |
1396 | 0 | { |
1397 | 0 | GstDiscovererInfo *info = dc->priv->current_info; |
1398 | 0 | GST_DEBUG_OBJECT (dc, "Emitting 'discovered' %s", info->uri); |
1399 | 0 | g_signal_emit (dc, gst_discoverer_signals[SIGNAL_DISCOVERED], 0, |
1400 | 0 | info, dc->priv->current_error); |
1401 | | |
1402 | | /* Clients get a copy of current_info since it is a boxed type */ |
1403 | 0 | gst_discoverer_info_unref (dc->priv->current_info); |
1404 | 0 | dc->priv->current_info = NULL; |
1405 | 0 | dc->priv->current_info_stream_count = 0; |
1406 | 0 | g_free (dc->priv->current_cachefile); |
1407 | 0 | dc->priv->current_cachefile = NULL; |
1408 | 0 | dc->priv->current_info_from_cache = FALSE; |
1409 | 0 | } |
1410 | | |
1411 | | static gboolean |
1412 | | emit_discovered_and_next (GstDiscoverer * dc) |
1413 | 0 | { |
1414 | 0 | emit_discovered (dc); |
1415 | |
|
1416 | 0 | DISCO_LOCK (dc); |
1417 | 0 | setup_next_uri_locked (dc); |
1418 | |
|
1419 | 0 | return G_SOURCE_REMOVE; |
1420 | 0 | } |
1421 | | |
1422 | | /* Called when pipeline is pre-rolled */ |
1423 | | static void |
1424 | | discoverer_collect (GstDiscoverer * dc) |
1425 | 345 | { |
1426 | 345 | GST_DEBUG_OBJECT (dc, "Collecting information"); |
1427 | | |
1428 | | /* Stop the timeout handler if present */ |
1429 | 345 | if (dc->priv->timeout_source) { |
1430 | 0 | g_source_destroy (dc->priv->timeout_source); |
1431 | 0 | g_source_unref (dc->priv->timeout_source); |
1432 | 0 | dc->priv->timeout_source = NULL; |
1433 | 0 | } |
1434 | | |
1435 | 345 | if (dc->priv->use_cache && dc->priv->current_info |
1436 | 0 | && dc->priv->current_info_from_cache) { |
1437 | 0 | GST_DEBUG_OBJECT (dc, |
1438 | 0 | "Nothing to collect as the info was built from the cache"); |
1439 | 0 | return; |
1440 | 0 | } |
1441 | | |
1442 | 345 | if (dc->priv->streams) { |
1443 | | /* FIXME : Make this querying optional */ |
1444 | 5 | if (TRUE) { |
1445 | 5 | GstElement *pipeline = (GstElement *) dc->priv->pipeline; |
1446 | 5 | gint64 dur; |
1447 | | |
1448 | 5 | GST_DEBUG_OBJECT (dc, "Attempting to query duration"); |
1449 | | |
1450 | 5 | if (gst_element_query_duration (pipeline, GST_FORMAT_TIME, &dur)) { |
1451 | 0 | GST_DEBUG_OBJECT (dc, "Got duration %" GST_TIME_FORMAT, |
1452 | 0 | GST_TIME_ARGS (dur)); |
1453 | 0 | dc->priv->current_info->duration = (guint64) dur; |
1454 | 5 | } else if (dc->priv->current_info->result != GST_DISCOVERER_ERROR) { |
1455 | 2 | GstStateChangeReturn sret; |
1456 | | /* Note: We don't switch to PLAYING if we previously saw an ERROR since |
1457 | | * the state of various element isn't guaranteed anymore */ |
1458 | | |
1459 | | /* Some parsers may not even return a rough estimate right away, e.g. |
1460 | | * because they've only processed a single frame so far, so if we |
1461 | | * didn't get a duration the first time, spin a bit and try again. |
1462 | | * Ugly, but still better than making parsers or other elements return |
1463 | | * completely bogus values. We need some API extensions to solve this |
1464 | | * better. */ |
1465 | 2 | GST_INFO ("No duration yet, try a bit harder.."); |
1466 | | /* Make sure we don't add/remove elements while switching to PLAYING itself */ |
1467 | 2 | DISCO_LOCK (dc); |
1468 | 2 | sret = gst_element_set_state (pipeline, GST_STATE_PLAYING); |
1469 | 2 | DISCO_UNLOCK (dc); |
1470 | 2 | if (sret != GST_STATE_CHANGE_FAILURE) { |
1471 | 2 | int i; |
1472 | | |
1473 | 6 | for (i = 0; i < 2; ++i) { |
1474 | 4 | g_usleep (G_USEC_PER_SEC / 20); |
1475 | 4 | if (gst_element_query_duration (pipeline, GST_FORMAT_TIME, &dur) |
1476 | 0 | && dur > 0) { |
1477 | 0 | GST_DEBUG_OBJECT (dc, "Got duration %" GST_TIME_FORMAT, |
1478 | 0 | GST_TIME_ARGS (dur)); |
1479 | 0 | dc->priv->current_info->duration = (guint64) dur; |
1480 | 0 | break; |
1481 | 0 | } |
1482 | 4 | } |
1483 | 2 | gst_element_set_state (pipeline, GST_STATE_PAUSED); |
1484 | 2 | } |
1485 | 2 | } |
1486 | | |
1487 | 5 | if (dc->priv->seeking_query) { |
1488 | 5 | if (gst_element_query (pipeline, dc->priv->seeking_query)) { |
1489 | 2 | GstFormat format; |
1490 | 2 | gboolean seekable; |
1491 | | |
1492 | 2 | gst_query_parse_seeking (dc->priv->seeking_query, &format, |
1493 | 2 | &seekable, NULL, NULL); |
1494 | 2 | if (format == GST_FORMAT_TIME) { |
1495 | 2 | GST_DEBUG_OBJECT (dc, "Got seekable %d", seekable); |
1496 | 2 | dc->priv->current_info->seekable = seekable; |
1497 | 2 | } |
1498 | 2 | } |
1499 | 5 | } |
1500 | 5 | } |
1501 | | |
1502 | 5 | if (dc->priv->target_state == GST_STATE_PAUSED) |
1503 | 5 | dc->priv->current_info->live = FALSE; |
1504 | 0 | else |
1505 | 0 | dc->priv->current_info->live = TRUE; |
1506 | | |
1507 | 5 | if (dc->priv->current_topology) { |
1508 | 5 | dc->priv->current_info_stream_count = 1; |
1509 | 5 | dc->priv->current_info->stream_info = parse_stream_topology (dc, |
1510 | 5 | dc->priv->current_topology, NULL); |
1511 | 5 | if (dc->priv->current_info->stream_info) |
1512 | 5 | dc->priv->current_info->stream_info->stream_number = 0; |
1513 | 5 | } |
1514 | | |
1515 | | /* |
1516 | | * Images need some special handling. They do not have a duration, have |
1517 | | * caps named image/<foo> (th exception being MJPEG video which is also |
1518 | | * type image/jpeg), and should consist of precisely one stream (actually |
1519 | | * initially there are 2, the image and raw stream, but we squash these |
1520 | | * while parsing the stream topology). At some point, if we find that these |
1521 | | * conditions are not sufficient, we can count the number of decoders and |
1522 | | * parsers in the chain, and if there's more than one decoder, or any |
1523 | | * parser at all, we should not mark this as an image. |
1524 | | */ |
1525 | 5 | if (dc->priv->current_info->duration == 0 && |
1526 | 5 | dc->priv->current_info->stream_info != NULL && |
1527 | 5 | dc->priv->current_info->stream_info->next == NULL) { |
1528 | 5 | GstDiscovererStreamInfo *stream_info; |
1529 | 5 | GstStructure *st; |
1530 | | |
1531 | 5 | stream_info = dc->priv->current_info->stream_info; |
1532 | 5 | st = gst_caps_get_structure (stream_info->caps, 0); |
1533 | | |
1534 | 5 | if (g_str_has_prefix (gst_structure_get_name (st), "image/")) |
1535 | 0 | ((GstDiscovererVideoInfo *) stream_info)->is_image = TRUE; |
1536 | 5 | } |
1537 | 5 | } |
1538 | | |
1539 | 345 | _ensure_info_tags (dc); |
1540 | | #if !GLIB_CHECK_VERSION(2,74,0) |
1541 | | /* Make sure the missing element details are NULL-terminated */ |
1542 | | g_ptr_array_add (dc->priv->current_info->missing_elements_details, NULL); |
1543 | | #endif |
1544 | 345 | serialize_info_if_required (dc, dc->priv->current_info); |
1545 | 345 | if (dc->priv->async) |
1546 | 0 | emit_discovered (dc); |
1547 | 345 | } |
1548 | | |
1549 | | static void |
1550 | | get_async_cb (gpointer cb_data, GSource * source, GSourceFunc * func, |
1551 | | gpointer * data) |
1552 | 0 | { |
1553 | 0 | *func = (GSourceFunc) async_timeout_cb; |
1554 | 0 | *data = cb_data; |
1555 | 0 | } |
1556 | | |
1557 | | /* Wrapper since GSourceCallbackFuncs don't expect a return value from ref() */ |
1558 | | static void |
1559 | | _void_g_object_ref (gpointer object) |
1560 | 0 | { |
1561 | 0 | g_object_ref (G_OBJECT (object)); |
1562 | 0 | } |
1563 | | |
1564 | | static void |
1565 | | handle_current_async (GstDiscoverer * dc) |
1566 | 0 | { |
1567 | 0 | GSource *source; |
1568 | 0 | static GSourceCallbackFuncs cb_funcs = { |
1569 | 0 | _void_g_object_ref, |
1570 | 0 | g_object_unref, |
1571 | 0 | get_async_cb, |
1572 | 0 | }; |
1573 | | |
1574 | | /* Attach a timeout to the main context */ |
1575 | 0 | source = g_timeout_source_new (dc->priv->timeout / GST_MSECOND); |
1576 | 0 | g_source_set_callback_indirect (source, g_object_ref (dc), &cb_funcs); |
1577 | 0 | g_source_attach (source, dc->priv->ctx); |
1578 | 0 | dc->priv->timeout_source = source; |
1579 | 0 | } |
1580 | | |
1581 | | |
1582 | | /* Returns TRUE if processing should stop */ |
1583 | | static gboolean |
1584 | | handle_message (GstDiscoverer * dc, GstMessage * msg) |
1585 | 14.1k | { |
1586 | 14.1k | gboolean done = FALSE; |
1587 | 14.1k | const gchar *dump_name = NULL; |
1588 | | |
1589 | 14.1k | GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "got a %s message", |
1590 | 14.1k | GST_MESSAGE_TYPE_NAME (msg)); |
1591 | | |
1592 | 14.1k | switch (GST_MESSAGE_TYPE (msg)) { |
1593 | 343 | case GST_MESSAGE_ERROR:{ |
1594 | 343 | GError *gerr; |
1595 | 343 | gchar *debug; |
1596 | | |
1597 | 343 | gst_message_parse_error (msg, &gerr, &debug); |
1598 | 343 | GST_WARNING_OBJECT (GST_MESSAGE_SRC (msg), |
1599 | 343 | "Got an error [debug:%s], [message:%s]", debug, gerr->message); |
1600 | 343 | dc->priv->current_error = gerr; |
1601 | 343 | g_free (debug); |
1602 | | |
1603 | | /* We need to stop */ |
1604 | 343 | done = TRUE; |
1605 | 343 | dump_name = "gst-discoverer-error"; |
1606 | | |
1607 | | /* Don't override missing plugin result code for missing plugin errors */ |
1608 | 343 | if (dc->priv->current_info->result != GST_DISCOVERER_MISSING_PLUGINS || |
1609 | 0 | (!g_error_matches (gerr, GST_CORE_ERROR, |
1610 | 0 | GST_CORE_ERROR_MISSING_PLUGIN) && |
1611 | 0 | !g_error_matches (gerr, GST_STREAM_ERROR, |
1612 | 343 | GST_STREAM_ERROR_CODEC_NOT_FOUND))) { |
1613 | 343 | GST_DEBUG_OBJECT (dc, "Setting result to ERROR"); |
1614 | 343 | dc->priv->current_info->result = GST_DISCOVERER_ERROR; |
1615 | 343 | } |
1616 | 343 | } |
1617 | 343 | break; |
1618 | | |
1619 | 86 | case GST_MESSAGE_WARNING:{ |
1620 | 86 | GError *err; |
1621 | 86 | gchar *debug = NULL; |
1622 | | |
1623 | 86 | gst_message_parse_warning (msg, &err, &debug); |
1624 | 86 | GST_CTX_WARNING_OBJECT (WARNING_MESSAGE_LOG_CTX, GST_MESSAGE_SRC (msg), |
1625 | 86 | "Got a warning [debug:%s], [message:%s]", debug, err->message); |
1626 | 86 | g_clear_error (&err); |
1627 | 86 | g_free (debug); |
1628 | 86 | dump_name = "gst-discoverer-warning"; |
1629 | 86 | break; |
1630 | 0 | } |
1631 | | |
1632 | 0 | case GST_MESSAGE_EOS: |
1633 | 0 | GST_DEBUG_OBJECT (dc, "Got EOS !"); |
1634 | 0 | done = TRUE; |
1635 | 0 | dump_name = "gst-discoverer-eos"; |
1636 | 0 | break; |
1637 | | |
1638 | 641 | case GST_MESSAGE_APPLICATION:{ |
1639 | 641 | const gchar *name = |
1640 | 641 | gst_structure_get_name (gst_message_get_structure (msg)); |
1641 | | |
1642 | 641 | if (g_strcmp0 (name, "DiscovererDone")) |
1643 | 0 | break; |
1644 | | |
1645 | | /* Maybe we already reached the target state, and all we're waiting for |
1646 | | * is either the subtitle tags or no_more_pads |
1647 | | */ |
1648 | 641 | DISCO_LOCK (dc); |
1649 | 641 | if (dc->priv->pending_subtitle_pads == 0) |
1650 | 2 | done = dc->priv->no_more_pads |
1651 | 2 | && dc->priv->target_state == dc->priv->current_state; |
1652 | 641 | DISCO_UNLOCK (dc); |
1653 | | |
1654 | 641 | if (done) |
1655 | 2 | dump_name = "gst-discoverer-application-message"; |
1656 | 641 | } |
1657 | 0 | break; |
1658 | | |
1659 | 6.94k | case GST_MESSAGE_STATE_CHANGED:{ |
1660 | 6.94k | GstState old, new, pending; |
1661 | | |
1662 | 6.94k | gst_message_parse_state_changed (msg, &old, &new, &pending); |
1663 | 6.94k | if (GST_MESSAGE_SRC (msg) == (GstObject *) dc->priv->pipeline) { |
1664 | 396 | DISCO_LOCK (dc); |
1665 | 396 | dc->priv->current_state = new; |
1666 | | |
1667 | 396 | if (dc->priv->pending_subtitle_pads == 0) |
1668 | 345 | done = dc->priv->no_more_pads |
1669 | 0 | && dc->priv->target_state == dc->priv->current_state; |
1670 | | /* Else we should get unblocked in GST_MESSAGE_APPLICATION */ |
1671 | | |
1672 | 396 | DISCO_UNLOCK (dc); |
1673 | 396 | } |
1674 | | |
1675 | 6.94k | if (done) |
1676 | 0 | dump_name = "gst-discoverer-target-state"; |
1677 | 6.94k | } |
1678 | 6.94k | break; |
1679 | | |
1680 | 500 | case GST_MESSAGE_ELEMENT: |
1681 | 500 | { |
1682 | 500 | const GstStructure *structure; |
1683 | | |
1684 | 500 | structure = gst_message_get_structure (msg); |
1685 | 500 | GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), |
1686 | 500 | "structure %" GST_PTR_FORMAT, structure); |
1687 | 500 | if (gst_structure_has_name (structure, "missing-plugin")) { |
1688 | 0 | GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), |
1689 | 0 | "Setting result to MISSING_PLUGINS"); |
1690 | 0 | dc->priv->current_info->result = GST_DISCOVERER_MISSING_PLUGINS; |
1691 | | /* FIXME 2.0 Remove completely the ->misc |
1692 | | * Keep the old behaviour for now. |
1693 | | */ |
1694 | 0 | if (dc->priv->current_info->misc) |
1695 | 0 | gst_structure_free (dc->priv->current_info->misc); |
1696 | 0 | dc->priv->current_info->misc = gst_structure_copy (structure); |
1697 | 0 | g_ptr_array_add (dc->priv->current_info->missing_elements_details, |
1698 | 0 | gst_missing_plugin_message_get_installer_detail (msg)); |
1699 | 500 | } else if (gst_structure_has_name (structure, "stream-topology")) { |
1700 | 500 | if (dc->priv->current_topology) |
1701 | 493 | gst_structure_free (dc->priv->current_topology); |
1702 | 500 | dc->priv->current_topology = gst_structure_copy (structure); |
1703 | 500 | } |
1704 | 500 | } |
1705 | 500 | break; |
1706 | | |
1707 | 581 | case GST_MESSAGE_TAG: |
1708 | 581 | { |
1709 | 581 | GstTagList *tl, *tmp = NULL; |
1710 | 581 | GstTagScope scope; |
1711 | | |
1712 | 581 | gst_message_parse_tag (msg, &tl); |
1713 | 581 | scope = gst_tag_list_get_scope (tl); |
1714 | 581 | GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "Got tags %" GST_PTR_FORMAT, tl); |
1715 | | |
1716 | 581 | tmp = gst_tag_list_merge (dc->priv->all_tags, tl, GST_TAG_MERGE_APPEND); |
1717 | 581 | if (dc->priv->all_tags) |
1718 | 574 | gst_tag_list_unref (dc->priv->all_tags); |
1719 | 581 | dc->priv->all_tags = tmp; |
1720 | | |
1721 | 581 | if (scope == GST_TAG_SCOPE_STREAM) { |
1722 | 46 | for (GList * curr = dc->priv->streams; curr; curr = curr->next) { |
1723 | 46 | PrivateStream *ps = (PrivateStream *) curr->data; |
1724 | 46 | if (GST_MESSAGE_SRC (msg) == GST_OBJECT_CAST (ps->sink)) { |
1725 | 46 | tmp = gst_tag_list_merge (ps->tags, tl, GST_TAG_MERGE_APPEND); |
1726 | 46 | if (ps->tags) |
1727 | 22 | gst_tag_list_unref (ps->tags); |
1728 | 46 | ps->tags = tmp; |
1729 | 46 | GST_DEBUG_OBJECT (ps->pad, "tags %" GST_PTR_FORMAT, ps->tags); |
1730 | 46 | break; |
1731 | 46 | } |
1732 | 46 | } |
1733 | 535 | } else { |
1734 | 535 | tmp = |
1735 | 535 | gst_tag_list_merge (dc->priv->global_tags, tl, |
1736 | 535 | GST_TAG_MERGE_APPEND); |
1737 | 535 | if (dc->priv->global_tags) |
1738 | 530 | gst_tag_list_unref (dc->priv->global_tags); |
1739 | 535 | dc->priv->global_tags = tmp; |
1740 | 535 | } |
1741 | 581 | gst_tag_list_unref (tl); |
1742 | 581 | } |
1743 | 581 | break; |
1744 | 0 | case GST_MESSAGE_TOC: |
1745 | 0 | { |
1746 | 0 | GstToc *tmp; |
1747 | |
|
1748 | 0 | gst_message_parse_toc (msg, &tmp, NULL); |
1749 | 0 | GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "Got toc %" GST_PTR_FORMAT, tmp); |
1750 | 0 | if (dc->priv->current_info->toc) |
1751 | 0 | gst_toc_unref (dc->priv->current_info->toc); |
1752 | 0 | dc->priv->current_info->toc = tmp; /* transfer ownership */ |
1753 | 0 | GST_DEBUG_OBJECT (GST_MESSAGE_SRC (msg), "Current info %p, toc %" |
1754 | 0 | GST_PTR_FORMAT, dc->priv->current_info, tmp); |
1755 | 0 | } |
1756 | 0 | break; |
1757 | | |
1758 | 5.03k | default: |
1759 | 5.03k | break; |
1760 | 14.1k | } |
1761 | | |
1762 | 14.1k | if (dump_name != NULL) { |
1763 | | /* dump graph when done or for warnings */ |
1764 | 431 | GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (dc->priv->pipeline), |
1765 | 431 | GST_DEBUG_GRAPH_SHOW_ALL, dump_name); |
1766 | 431 | } |
1767 | 14.1k | return done; |
1768 | 14.1k | } |
1769 | | |
1770 | | static void |
1771 | | handle_current_sync (GstDiscoverer * dc) |
1772 | 345 | { |
1773 | 345 | GTimer *timer; |
1774 | 345 | gdouble deadline = ((gdouble) dc->priv->timeout) / GST_SECOND; |
1775 | 345 | GstMessage *msg; |
1776 | 345 | gboolean done = FALSE; |
1777 | | |
1778 | 345 | timer = g_timer_new (); |
1779 | 345 | g_timer_start (timer); |
1780 | | |
1781 | 14.1k | do { |
1782 | | /* poll bus with timeout */ |
1783 | | /* FIXME : make the timeout more fine-tuned */ |
1784 | 14.1k | if ((msg = gst_bus_timed_pop (dc->priv->bus, GST_SECOND / 2))) { |
1785 | 14.1k | done = handle_message (dc, msg); |
1786 | 14.1k | gst_message_unref (msg); |
1787 | 14.1k | } |
1788 | 14.1k | } while (!done && (g_timer_elapsed (timer, NULL) < deadline)); |
1789 | | |
1790 | | /* return result */ |
1791 | 345 | if (!done) { |
1792 | 0 | GST_DEBUG_OBJECT (dc, "we timed out! Setting result to TIMEOUT"); |
1793 | 0 | dc->priv->current_info->result = GST_DISCOVERER_TIMEOUT; |
1794 | 0 | } |
1795 | | |
1796 | 345 | DISCO_LOCK (dc); |
1797 | 345 | dc->priv->processing = FALSE; |
1798 | 345 | DISCO_UNLOCK (dc); |
1799 | | |
1800 | | |
1801 | 345 | GST_DEBUG_OBJECT (dc, "Done"); |
1802 | | |
1803 | 345 | g_timer_stop (timer); |
1804 | 345 | g_timer_destroy (timer); |
1805 | 345 | } |
1806 | | |
1807 | | static gchar * |
1808 | | _serialized_info_get_path (GstDiscoverer * dc, gchar * uri) |
1809 | 0 | { |
1810 | 0 | GChecksum *cs = NULL; |
1811 | 0 | GStatBuf file_status; |
1812 | 0 | gchar *location = NULL, *res = NULL, *cache_dir = NULL, *tmp = NULL, |
1813 | 0 | *protocol = gst_uri_get_protocol (uri), hash_dirname[3] = "00"; |
1814 | 0 | const gchar *checksum; |
1815 | |
|
1816 | 0 | if (g_ascii_strcasecmp (protocol, "file") != 0) { |
1817 | 0 | GST_DEBUG_OBJECT (dc, "Can not work with serialized DiscovererInfo" |
1818 | 0 | " on non local files - protocol: %s", protocol); |
1819 | |
|
1820 | 0 | goto done; |
1821 | 0 | } |
1822 | | |
1823 | 0 | location = gst_uri_get_location (uri); |
1824 | 0 | if (g_stat (location, &file_status) < 0) { |
1825 | 0 | GST_DEBUG_OBJECT (dc, "Could not get stat for file: %s", location); |
1826 | |
|
1827 | 0 | goto done; |
1828 | 0 | } |
1829 | | |
1830 | 0 | tmp = g_strdup_printf ("%s-%" G_GSIZE_FORMAT "-%" G_GINT64_FORMAT, |
1831 | 0 | location, (gsize) file_status.st_size, (gint64) file_status.st_mtime); |
1832 | 0 | cs = g_checksum_new (G_CHECKSUM_SHA1); |
1833 | 0 | g_checksum_update (cs, (const guchar *) tmp, strlen (tmp)); |
1834 | 0 | checksum = g_checksum_get_string (cs); |
1835 | |
|
1836 | 0 | hash_dirname[0] = checksum[0]; |
1837 | 0 | hash_dirname[1] = checksum[1]; |
1838 | 0 | cache_dir = |
1839 | 0 | g_build_filename (g_get_user_cache_dir (), "gstreamer-" GST_API_VERSION, |
1840 | 0 | CACHE_DIRNAME, hash_dirname, NULL); |
1841 | 0 | g_mkdir_with_parents (cache_dir, 0777); |
1842 | |
|
1843 | 0 | res = g_build_filename (cache_dir, &checksum[2], NULL); |
1844 | |
|
1845 | 0 | done: |
1846 | 0 | g_checksum_free (cs); |
1847 | 0 | g_free (cache_dir); |
1848 | 0 | g_free (location); |
1849 | 0 | g_free (tmp); |
1850 | 0 | g_free (protocol); |
1851 | |
|
1852 | 0 | return res; |
1853 | 0 | } |
1854 | | |
1855 | | static GstDiscovererInfo * |
1856 | | _get_info_from_cachefile (GstDiscoverer * dc, gchar * cachefile) |
1857 | 0 | { |
1858 | 0 | gchar *data; |
1859 | 0 | gsize length; |
1860 | |
|
1861 | 0 | if (g_file_get_contents (cachefile, &data, &length, NULL)) { |
1862 | 0 | GstDiscovererInfo *info = NULL; |
1863 | 0 | GVariant *variant = |
1864 | 0 | g_variant_new_from_data (G_VARIANT_TYPE ("v"), data, length, |
1865 | 0 | TRUE, NULL, NULL); |
1866 | |
|
1867 | 0 | info = gst_discoverer_info_from_variant (variant); |
1868 | 0 | g_variant_unref (variant); |
1869 | |
|
1870 | 0 | if (info) { |
1871 | 0 | dc->priv->current_cachefile = cachefile; |
1872 | 0 | dc->priv->current_info_from_cache = TRUE; |
1873 | 0 | } else { |
1874 | 0 | g_free (cachefile); |
1875 | 0 | } |
1876 | |
|
1877 | 0 | GST_INFO_OBJECT (dc, "Got info from cache: %p %s", info, |
1878 | 0 | dc->priv->current_cachefile); |
1879 | 0 | g_free (data); |
1880 | |
|
1881 | 0 | return info; |
1882 | 0 | } else { |
1883 | 0 | g_free (cachefile); |
1884 | 0 | } |
1885 | | |
1886 | 0 | return NULL; |
1887 | 0 | } |
1888 | | |
1889 | | static GstDiscovererInfo * |
1890 | | load_serialized_info (GstDiscoverer * dc, gchar * uri) |
1891 | 345 | { |
1892 | 345 | GstDiscovererInfo *res = NULL; |
1893 | | |
1894 | 345 | if (dc->priv->use_cache) { |
1895 | 0 | gchar *cachefile = _serialized_info_get_path (dc, uri); |
1896 | |
|
1897 | 0 | if (cachefile) { |
1898 | 0 | res = _get_info_from_cachefile (dc, cachefile); |
1899 | 0 | } |
1900 | 0 | } |
1901 | | |
1902 | 345 | return res; |
1903 | 345 | } |
1904 | | |
1905 | | static gboolean |
1906 | | _setup_locked (GstDiscoverer * dc) |
1907 | 345 | { |
1908 | 345 | GstStateChangeReturn ret; |
1909 | 345 | gchar *uri = (gchar *) dc->priv->pending_uris->data; |
1910 | | |
1911 | 345 | dc->priv->pending_uris = |
1912 | 345 | g_list_delete_link (dc->priv->pending_uris, dc->priv->pending_uris); |
1913 | | |
1914 | | |
1915 | 345 | GST_DEBUG_OBJECT (dc, "Setting up"); |
1916 | | |
1917 | 345 | g_signal_emit (dc, gst_discoverer_signals[SIGNAL_LOAD_SERIALIZED_INFO], 0, |
1918 | 345 | uri, &dc->priv->current_info); |
1919 | 345 | if (dc->priv->current_info) { |
1920 | | /* Make sure the URI is exactly what the user passed in */ |
1921 | 0 | g_free (dc->priv->current_info->uri); |
1922 | 0 | dc->priv->current_info->uri = uri; |
1923 | |
|
1924 | 0 | dc->priv->processing = FALSE; |
1925 | 0 | dc->priv->target_state = GST_STATE_NULL; |
1926 | |
|
1927 | 0 | return TRUE; |
1928 | 0 | } |
1929 | | |
1930 | | /* Pop URI off the pending URI list */ |
1931 | 345 | dc->priv->current_info = |
1932 | 345 | (GstDiscovererInfo *) g_object_new (GST_TYPE_DISCOVERER_INFO, NULL); |
1933 | 345 | dc->priv->current_info_stream_count = 0; |
1934 | 345 | if (dc->priv->use_cache) |
1935 | 0 | dc->priv->current_cachefile = _serialized_info_get_path (dc, uri); |
1936 | 345 | dc->priv->current_info->uri = uri; |
1937 | | |
1938 | | /* set uri on uridecodebin */ |
1939 | 345 | g_object_set (dc->priv->uridecodebin, "uri", dc->priv->current_info->uri, |
1940 | 345 | NULL); |
1941 | | |
1942 | 345 | GST_DEBUG_OBJECT (dc, "Current is now %s", dc->priv->current_info->uri); |
1943 | | |
1944 | 345 | dc->priv->processing = TRUE; |
1945 | | |
1946 | 345 | dc->priv->target_state = GST_STATE_PAUSED; |
1947 | | |
1948 | | /* set pipeline to PAUSED */ |
1949 | 345 | DISCO_UNLOCK (dc); |
1950 | 345 | GST_DEBUG_OBJECT (dc, "Setting pipeline to PAUSED"); |
1951 | 345 | ret = |
1952 | 345 | gst_element_set_state ((GstElement *) dc->priv->pipeline, |
1953 | 345 | dc->priv->target_state); |
1954 | | |
1955 | 345 | if (ret == GST_STATE_CHANGE_NO_PREROLL) { |
1956 | 0 | GST_DEBUG_OBJECT (dc, "Source is live, switching to PLAYING"); |
1957 | 0 | dc->priv->target_state = GST_STATE_PLAYING; |
1958 | 0 | ret = |
1959 | 0 | gst_element_set_state ((GstElement *) dc->priv->pipeline, |
1960 | 0 | dc->priv->target_state); |
1961 | 0 | } |
1962 | 345 | DISCO_LOCK (dc); |
1963 | | |
1964 | | |
1965 | 345 | GST_DEBUG_OBJECT (dc, "Pipeline going to PAUSED : %s", |
1966 | 345 | gst_state_change_return_get_name (ret)); |
1967 | | |
1968 | 345 | return FALSE; |
1969 | 345 | } |
1970 | | |
1971 | | static void |
1972 | | discoverer_cleanup (GstDiscoverer * dc) |
1973 | 345 | { |
1974 | 345 | GST_DEBUG_OBJECT (dc, "Cleaning up"); |
1975 | | |
1976 | 345 | DISCO_LOCK (dc); |
1977 | 345 | dc->priv->cleanup = TRUE; |
1978 | 345 | DISCO_UNLOCK (dc); |
1979 | | |
1980 | 345 | gst_bus_set_flushing (dc->priv->bus, TRUE); |
1981 | | |
1982 | 345 | DISCO_LOCK (dc); |
1983 | 345 | if (dc->priv->current_error) { |
1984 | 343 | g_error_free (dc->priv->current_error); |
1985 | 343 | DISCO_UNLOCK (dc); |
1986 | 343 | gst_element_set_state ((GstElement *) dc->priv->pipeline, GST_STATE_NULL); |
1987 | 343 | } else { |
1988 | 2 | DISCO_UNLOCK (dc); |
1989 | 2 | } |
1990 | | |
1991 | 345 | gst_element_set_state ((GstElement *) dc->priv->pipeline, GST_STATE_READY); |
1992 | 345 | gst_bus_set_flushing (dc->priv->bus, FALSE); |
1993 | | |
1994 | 345 | DISCO_LOCK (dc); |
1995 | 345 | dc->priv->current_error = NULL; |
1996 | 345 | if (dc->priv->current_topology) { |
1997 | 7 | gst_structure_free (dc->priv->current_topology); |
1998 | 7 | dc->priv->current_topology = NULL; |
1999 | 7 | } |
2000 | | |
2001 | 345 | dc->priv->current_info = NULL; |
2002 | 345 | dc->priv->current_info_stream_count = 0; |
2003 | 345 | g_free (dc->priv->current_cachefile); |
2004 | 345 | dc->priv->current_cachefile = NULL; |
2005 | 345 | dc->priv->current_info_from_cache = FALSE; |
2006 | | |
2007 | 345 | if (dc->priv->all_tags) { |
2008 | 0 | gst_tag_list_unref (dc->priv->all_tags); |
2009 | 0 | dc->priv->all_tags = NULL; |
2010 | 0 | } |
2011 | | |
2012 | 345 | if (dc->priv->global_tags) { |
2013 | 5 | gst_tag_list_unref (dc->priv->global_tags); |
2014 | 5 | dc->priv->global_tags = NULL; |
2015 | 5 | } |
2016 | | |
2017 | 345 | dc->priv->pending_subtitle_pads = 0; |
2018 | | |
2019 | 345 | dc->priv->current_state = GST_STATE_NULL; |
2020 | 345 | dc->priv->target_state = GST_STATE_NULL; |
2021 | 345 | dc->priv->no_more_pads = FALSE; |
2022 | 345 | dc->priv->cleanup = FALSE; |
2023 | | |
2024 | | |
2025 | | /* Try popping the next uri */ |
2026 | 345 | if (dc->priv->async) { |
2027 | 0 | setup_next_uri_locked (dc); |
2028 | 0 | } else |
2029 | 345 | DISCO_UNLOCK (dc); |
2030 | | |
2031 | 345 | GST_DEBUG_OBJECT (dc, "out"); |
2032 | 345 | } |
2033 | | |
2034 | | static void |
2035 | | discoverer_bus_cb (GstBus * bus, GstMessage * msg, GstDiscoverer * dc) |
2036 | 0 | { |
2037 | 0 | if (dc->priv->processing) { |
2038 | 0 | if (handle_message (dc, msg)) { |
2039 | 0 | GST_DEBUG_OBJECT (dc, "Stopping asynchronously"); |
2040 | | /* Serialise with _event_probe() */ |
2041 | 0 | DISCO_LOCK (dc); |
2042 | 0 | dc->priv->processing = FALSE; |
2043 | 0 | DISCO_UNLOCK (dc); |
2044 | 0 | discoverer_collect (dc); |
2045 | 0 | discoverer_cleanup (dc); |
2046 | 0 | } |
2047 | 0 | } |
2048 | 0 | } |
2049 | | |
2050 | | static gboolean |
2051 | | async_timeout_cb (GstDiscoverer * dc) |
2052 | 0 | { |
2053 | 0 | if (!g_source_is_destroyed (g_main_current_source ())) { |
2054 | 0 | GST_DEBUG_OBJECT (dc, "Setting result to TIMEOUT"); |
2055 | 0 | dc->priv->current_info->result = GST_DISCOVERER_TIMEOUT; |
2056 | 0 | dc->priv->processing = FALSE; |
2057 | 0 | discoverer_collect (dc); |
2058 | 0 | discoverer_cleanup (dc); |
2059 | 0 | } |
2060 | 0 | return FALSE; |
2061 | 0 | } |
2062 | | |
2063 | | /* If there is a pending URI, it will pop it from the list of pending |
2064 | | * URIs and start the discovery on it. |
2065 | | * |
2066 | | * Returns GST_DISCOVERER_OK if the next URI was popped and is processing, |
2067 | | * else a error flag. |
2068 | | */ |
2069 | | static GstDiscovererResult |
2070 | | start_discovering (GstDiscoverer * dc) |
2071 | 345 | { |
2072 | 345 | gboolean ready; |
2073 | 345 | GstDiscovererResult res = GST_DISCOVERER_OK; |
2074 | | |
2075 | 345 | GST_DEBUG_OBJECT (dc, "Starting"); |
2076 | | |
2077 | 345 | DISCO_LOCK (dc); |
2078 | 345 | if (dc->priv->cleanup) { |
2079 | 0 | GST_DEBUG_OBJECT (dc, "The discoverer is busy cleaning up."); |
2080 | 0 | res = GST_DISCOVERER_BUSY; |
2081 | 0 | DISCO_UNLOCK (dc); |
2082 | 0 | goto beach; |
2083 | 0 | } |
2084 | | |
2085 | 345 | if (dc->priv->pending_uris == NULL) { |
2086 | 0 | GST_INFO_OBJECT (dc, "No URI to process"); |
2087 | 0 | res = GST_DISCOVERER_URI_INVALID; |
2088 | 0 | DISCO_UNLOCK (dc); |
2089 | 0 | goto beach; |
2090 | 0 | } |
2091 | | |
2092 | 345 | if (dc->priv->current_info != NULL) { |
2093 | 0 | GST_DEBUG_OBJECT (dc, "Already processing a file"); |
2094 | 0 | res = GST_DISCOVERER_BUSY; |
2095 | 0 | DISCO_UNLOCK (dc); |
2096 | 0 | goto beach; |
2097 | 0 | } |
2098 | | |
2099 | 345 | g_signal_emit (dc, gst_discoverer_signals[SIGNAL_STARTING], 0); |
2100 | | |
2101 | 345 | ready = _setup_locked (dc); |
2102 | | |
2103 | 345 | DISCO_UNLOCK (dc); |
2104 | | |
2105 | 345 | if (dc->priv->async) { |
2106 | 0 | if (ready) { |
2107 | 0 | GSource *source; |
2108 | |
|
2109 | 0 | source = g_idle_source_new (); |
2110 | 0 | g_source_set_callback (source, |
2111 | 0 | (GSourceFunc) emit_discovered_and_next, gst_object_ref (dc), |
2112 | 0 | gst_object_unref); |
2113 | 0 | g_source_attach (source, dc->priv->ctx); |
2114 | 0 | goto beach; |
2115 | 0 | } |
2116 | 0 | if (dc->priv->processing) |
2117 | 0 | handle_current_async (dc); |
2118 | 345 | } else { |
2119 | 345 | if (!ready) |
2120 | 345 | handle_current_sync (dc); |
2121 | 345 | } |
2122 | | |
2123 | 345 | beach: |
2124 | 345 | return res; |
2125 | 345 | } |
2126 | | |
2127 | | /* Serializing code */ |
2128 | | |
2129 | | static GVariant * |
2130 | | _serialize_common_stream_info (GstDiscovererStreamInfo * sinfo, |
2131 | | GstDiscovererSerializeFlags flags) |
2132 | 0 | { |
2133 | 0 | GVariant *common; |
2134 | 0 | GVariant *nextv = NULL; |
2135 | 0 | gchar *caps_str = NULL, *tags_str = NULL, *misc_str = NULL; |
2136 | |
|
2137 | 0 | if (sinfo->caps && (flags & GST_DISCOVERER_SERIALIZE_CAPS)) |
2138 | 0 | caps_str = gst_caps_to_string (sinfo->caps); |
2139 | |
|
2140 | 0 | if (sinfo->tags && (flags & GST_DISCOVERER_SERIALIZE_TAGS)) |
2141 | 0 | tags_str = gst_tag_list_to_string (sinfo->tags); |
2142 | |
|
2143 | 0 | if (sinfo->misc && (flags & GST_DISCOVERER_SERIALIZE_MISC)) |
2144 | 0 | misc_str = gst_structure_to_string (sinfo->misc); |
2145 | | |
2146 | |
|
2147 | 0 | if (sinfo->next) |
2148 | 0 | nextv = gst_discoverer_info_to_variant_recurse (sinfo->next, flags); |
2149 | 0 | else |
2150 | 0 | nextv = g_variant_new ("()"); |
2151 | |
|
2152 | 0 | common = |
2153 | 0 | g_variant_new ("(msmsmsmsv)", sinfo->stream_id, caps_str, tags_str, |
2154 | 0 | misc_str, nextv); |
2155 | |
|
2156 | 0 | g_free (caps_str); |
2157 | 0 | g_free (tags_str); |
2158 | 0 | g_free (misc_str); |
2159 | |
|
2160 | 0 | return common; |
2161 | 0 | } |
2162 | | |
2163 | | static GVariant * |
2164 | | _serialize_info (GstDiscovererInfo * info, GstDiscovererSerializeFlags flags) |
2165 | 0 | { |
2166 | 0 | gchar *tags_str = NULL; |
2167 | 0 | GVariant *ret; |
2168 | |
|
2169 | 0 | if (info->tags && (flags & GST_DISCOVERER_SERIALIZE_TAGS)) |
2170 | 0 | tags_str = gst_tag_list_to_string (info->tags); |
2171 | |
|
2172 | 0 | ret = |
2173 | 0 | g_variant_new ("(mstbmsb)", info->uri, info->duration, info->seekable, |
2174 | 0 | tags_str, info->live); |
2175 | |
|
2176 | 0 | g_free (tags_str); |
2177 | |
|
2178 | 0 | return ret; |
2179 | 0 | } |
2180 | | |
2181 | | static GVariant * |
2182 | | _serialize_audio_stream_info (GstDiscovererAudioInfo * ainfo) |
2183 | 0 | { |
2184 | 0 | return g_variant_new ("(uuuuumst)", |
2185 | 0 | ainfo->channels, |
2186 | 0 | ainfo->sample_rate, |
2187 | 0 | ainfo->bitrate, ainfo->max_bitrate, ainfo->depth, ainfo->language, |
2188 | 0 | ainfo->channel_mask); |
2189 | 0 | } |
2190 | | |
2191 | | static GVariant * |
2192 | | _serialize_video_stream_info (GstDiscovererVideoInfo * vinfo) |
2193 | 0 | { |
2194 | 0 | return g_variant_new ("(uuuuuuubuub)", |
2195 | 0 | vinfo->width, |
2196 | 0 | vinfo->height, |
2197 | 0 | vinfo->depth, |
2198 | 0 | vinfo->framerate_num, |
2199 | 0 | vinfo->framerate_denom, |
2200 | 0 | vinfo->par_num, |
2201 | 0 | vinfo->par_denom, |
2202 | 0 | vinfo->interlaced, vinfo->bitrate, vinfo->max_bitrate, vinfo->is_image); |
2203 | 0 | } |
2204 | | |
2205 | | static GVariant * |
2206 | | _serialize_subtitle_stream_info (GstDiscovererSubtitleInfo * sinfo) |
2207 | 0 | { |
2208 | 0 | return g_variant_new ("ms", sinfo->language); |
2209 | 0 | } |
2210 | | |
2211 | | static GVariant * |
2212 | | gst_discoverer_info_to_variant_recurse (GstDiscovererStreamInfo * sinfo, |
2213 | | GstDiscovererSerializeFlags flags) |
2214 | 0 | { |
2215 | 0 | GVariant *stream_variant = NULL; |
2216 | 0 | GVariant *common_stream_variant = |
2217 | 0 | _serialize_common_stream_info (sinfo, flags); |
2218 | |
|
2219 | 0 | if (GST_IS_DISCOVERER_CONTAINER_INFO (sinfo)) { |
2220 | 0 | GList *tmp; |
2221 | 0 | GList *streams = |
2222 | 0 | gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO |
2223 | 0 | (sinfo)); |
2224 | |
|
2225 | 0 | if (g_list_length (streams) > 0) { |
2226 | 0 | GVariantBuilder children; |
2227 | 0 | GVariant *child_variant; |
2228 | 0 | g_variant_builder_init (&children, G_VARIANT_TYPE_ARRAY); |
2229 | |
|
2230 | 0 | for (tmp = streams; tmp; tmp = tmp->next) { |
2231 | 0 | child_variant = |
2232 | 0 | gst_discoverer_info_to_variant_recurse (tmp->data, flags); |
2233 | 0 | g_variant_builder_add (&children, "v", child_variant); |
2234 | 0 | } |
2235 | 0 | stream_variant = |
2236 | 0 | g_variant_new ("(yvav)", 'c', common_stream_variant, &children); |
2237 | 0 | } else { |
2238 | 0 | stream_variant = |
2239 | 0 | g_variant_new ("(yvav)", 'c', common_stream_variant, NULL); |
2240 | 0 | } |
2241 | |
|
2242 | 0 | gst_discoverer_stream_info_list_free (streams); |
2243 | 0 | } else if (GST_IS_DISCOVERER_AUDIO_INFO (sinfo)) { |
2244 | 0 | GVariant *audio_stream_info = |
2245 | 0 | _serialize_audio_stream_info (GST_DISCOVERER_AUDIO_INFO (sinfo)); |
2246 | 0 | stream_variant = |
2247 | 0 | g_variant_new ("(yvv)", 'a', common_stream_variant, audio_stream_info); |
2248 | 0 | } else if (GST_IS_DISCOVERER_VIDEO_INFO (sinfo)) { |
2249 | 0 | GVariant *video_stream_info = |
2250 | 0 | _serialize_video_stream_info (GST_DISCOVERER_VIDEO_INFO (sinfo)); |
2251 | 0 | stream_variant = |
2252 | 0 | g_variant_new ("(yvv)", 'v', common_stream_variant, video_stream_info); |
2253 | 0 | } else if (GST_IS_DISCOVERER_SUBTITLE_INFO (sinfo)) { |
2254 | 0 | GVariant *subtitle_stream_info = |
2255 | 0 | _serialize_subtitle_stream_info (GST_DISCOVERER_SUBTITLE_INFO (sinfo)); |
2256 | 0 | stream_variant = |
2257 | 0 | g_variant_new ("(yvv)", 's', common_stream_variant, |
2258 | 0 | subtitle_stream_info); |
2259 | 0 | } else { |
2260 | 0 | GVariant *nextv = NULL; |
2261 | 0 | GstDiscovererStreamInfo *ninfo = |
2262 | 0 | gst_discoverer_stream_info_get_next (sinfo); |
2263 | |
|
2264 | 0 | if (ninfo) { |
2265 | 0 | nextv = gst_discoverer_info_to_variant_recurse (ninfo, flags); |
2266 | 0 | stream_variant = |
2267 | 0 | g_variant_new ("(yvv)", 'n', common_stream_variant, |
2268 | 0 | g_variant_new ("v", nextv)); |
2269 | 0 | } else { |
2270 | 0 | stream_variant = g_variant_new ("(yv)", 'n', common_stream_variant); |
2271 | 0 | } |
2272 | 0 | } |
2273 | |
|
2274 | 0 | return stream_variant; |
2275 | 0 | } |
2276 | | |
2277 | | /* Parsing code */ |
2278 | | |
2279 | 0 | #define GET_FROM_TUPLE(v, t, n, val) G_STMT_START{ \ |
2280 | 0 | GVariant *child = g_variant_get_child_value (v, n); \ |
2281 | 0 | *val = g_variant_get_##t(child); \ |
2282 | 0 | g_variant_unref (child); \ |
2283 | 0 | }G_STMT_END |
2284 | | |
2285 | | static const gchar * |
2286 | | _maybe_get_string_from_tuple (GVariant * tuple, guint index) |
2287 | 0 | { |
2288 | 0 | const gchar *ret = NULL; |
2289 | 0 | GVariant *maybe; |
2290 | 0 | GET_FROM_TUPLE (tuple, maybe, index, &maybe); |
2291 | 0 | if (maybe) { |
2292 | 0 | ret = g_variant_get_string (maybe, NULL); |
2293 | 0 | g_variant_unref (maybe); |
2294 | 0 | } |
2295 | |
|
2296 | 0 | return ret; |
2297 | 0 | } |
2298 | | |
2299 | | static void |
2300 | | _parse_info (GstDiscovererInfo * info, GVariant * info_variant) |
2301 | 0 | { |
2302 | 0 | const gchar *str; |
2303 | |
|
2304 | 0 | str = _maybe_get_string_from_tuple (info_variant, 0); |
2305 | 0 | if (str) |
2306 | 0 | info->uri = g_strdup (str); |
2307 | |
|
2308 | 0 | GET_FROM_TUPLE (info_variant, uint64, 1, &info->duration); |
2309 | 0 | GET_FROM_TUPLE (info_variant, boolean, 2, &info->seekable); |
2310 | |
|
2311 | 0 | str = _maybe_get_string_from_tuple (info_variant, 3); |
2312 | 0 | if (str) |
2313 | 0 | info->tags = gst_tag_list_new_from_string (str); |
2314 | |
|
2315 | 0 | GET_FROM_TUPLE (info_variant, boolean, 4, &info->live); |
2316 | 0 | } |
2317 | | |
2318 | | static void |
2319 | | _parse_common_stream_info (GstDiscovererStreamInfo * sinfo, GVariant * common, |
2320 | | GstDiscovererInfo * info) |
2321 | 0 | { |
2322 | 0 | const gchar *str; |
2323 | |
|
2324 | 0 | str = _maybe_get_string_from_tuple (common, 0); |
2325 | 0 | if (str) |
2326 | 0 | sinfo->stream_id = g_strdup (str); |
2327 | |
|
2328 | 0 | str = _maybe_get_string_from_tuple (common, 1); |
2329 | 0 | if (str) |
2330 | 0 | sinfo->caps = gst_caps_from_string (str); |
2331 | |
|
2332 | 0 | str = _maybe_get_string_from_tuple (common, 2); |
2333 | 0 | if (str) |
2334 | 0 | sinfo->tags = gst_tag_list_new_from_string (str); |
2335 | |
|
2336 | 0 | str = _maybe_get_string_from_tuple (common, 3); |
2337 | 0 | if (str) |
2338 | 0 | sinfo->misc = gst_structure_new_from_string (str); |
2339 | |
|
2340 | 0 | if (g_variant_n_children (common) > 4) { |
2341 | 0 | GVariant *nextv; |
2342 | |
|
2343 | 0 | GET_FROM_TUPLE (common, variant, 4, &nextv); |
2344 | 0 | if (g_variant_n_children (nextv) > 0) { |
2345 | 0 | sinfo->next = _parse_discovery (nextv, info); |
2346 | 0 | } |
2347 | 0 | g_variant_unref (nextv); |
2348 | 0 | } |
2349 | |
|
2350 | 0 | g_variant_unref (common); |
2351 | 0 | } |
2352 | | |
2353 | | static void |
2354 | | _parse_audio_stream_info (GstDiscovererAudioInfo * ainfo, GVariant * specific) |
2355 | 0 | { |
2356 | 0 | const gchar *str; |
2357 | |
|
2358 | 0 | GET_FROM_TUPLE (specific, uint32, 0, &ainfo->channels); |
2359 | 0 | GET_FROM_TUPLE (specific, uint32, 1, &ainfo->sample_rate); |
2360 | 0 | GET_FROM_TUPLE (specific, uint32, 2, &ainfo->bitrate); |
2361 | 0 | GET_FROM_TUPLE (specific, uint32, 3, &ainfo->max_bitrate); |
2362 | 0 | GET_FROM_TUPLE (specific, uint32, 4, &ainfo->depth); |
2363 | |
|
2364 | 0 | str = _maybe_get_string_from_tuple (specific, 5); |
2365 | |
|
2366 | 0 | if (str) |
2367 | 0 | ainfo->language = g_strdup (str); |
2368 | |
|
2369 | 0 | GET_FROM_TUPLE (specific, uint64, 6, &ainfo->channel_mask); |
2370 | |
|
2371 | 0 | g_variant_unref (specific); |
2372 | 0 | } |
2373 | | |
2374 | | static void |
2375 | | _parse_video_stream_info (GstDiscovererVideoInfo * vinfo, GVariant * specific) |
2376 | 0 | { |
2377 | 0 | GET_FROM_TUPLE (specific, uint32, 0, &vinfo->width); |
2378 | 0 | GET_FROM_TUPLE (specific, uint32, 1, &vinfo->height); |
2379 | 0 | GET_FROM_TUPLE (specific, uint32, 2, &vinfo->depth); |
2380 | 0 | GET_FROM_TUPLE (specific, uint32, 3, &vinfo->framerate_num); |
2381 | 0 | GET_FROM_TUPLE (specific, uint32, 4, &vinfo->framerate_denom); |
2382 | 0 | GET_FROM_TUPLE (specific, uint32, 5, &vinfo->par_num); |
2383 | 0 | GET_FROM_TUPLE (specific, uint32, 6, &vinfo->par_denom); |
2384 | 0 | GET_FROM_TUPLE (specific, boolean, 7, &vinfo->interlaced); |
2385 | 0 | GET_FROM_TUPLE (specific, uint32, 8, &vinfo->bitrate); |
2386 | 0 | GET_FROM_TUPLE (specific, uint32, 9, &vinfo->max_bitrate); |
2387 | 0 | GET_FROM_TUPLE (specific, boolean, 10, &vinfo->is_image); |
2388 | |
|
2389 | 0 | g_variant_unref (specific); |
2390 | 0 | } |
2391 | | |
2392 | | static void |
2393 | | _parse_subtitle_stream_info (GstDiscovererSubtitleInfo * sinfo, |
2394 | | GVariant * specific) |
2395 | 0 | { |
2396 | 0 | GVariant *maybe; |
2397 | |
|
2398 | 0 | maybe = g_variant_get_maybe (specific); |
2399 | 0 | if (maybe) { |
2400 | 0 | sinfo->language = g_strdup (g_variant_get_string (maybe, NULL)); |
2401 | 0 | g_variant_unref (maybe); |
2402 | 0 | } |
2403 | |
|
2404 | 0 | g_variant_unref (specific); |
2405 | 0 | } |
2406 | | |
2407 | | static GstDiscovererStreamInfo * |
2408 | | _parse_discovery (GVariant * variant, GstDiscovererInfo * info) |
2409 | 0 | { |
2410 | 0 | gchar type; |
2411 | 0 | GVariant *common = g_variant_get_child_value (variant, 1); |
2412 | 0 | GVariant *specific = NULL; |
2413 | 0 | GstDiscovererStreamInfo *sinfo = NULL; |
2414 | |
|
2415 | 0 | if (g_variant_n_children (variant) > 2) |
2416 | 0 | specific = g_variant_get_child_value (variant, 2); |
2417 | |
|
2418 | 0 | GET_FROM_TUPLE (variant, byte, 0, &type); |
2419 | 0 | switch (type) { |
2420 | 0 | case 'c': |
2421 | 0 | sinfo = g_object_new (GST_TYPE_DISCOVERER_CONTAINER_INFO, NULL); |
2422 | 0 | break; |
2423 | 0 | case 'a': |
2424 | 0 | sinfo = g_object_new (GST_TYPE_DISCOVERER_AUDIO_INFO, NULL); |
2425 | 0 | _parse_audio_stream_info (GST_DISCOVERER_AUDIO_INFO (sinfo), |
2426 | 0 | g_variant_get_child_value (specific, 0)); |
2427 | 0 | break; |
2428 | 0 | case 'v': |
2429 | 0 | sinfo = g_object_new (GST_TYPE_DISCOVERER_VIDEO_INFO, NULL); |
2430 | 0 | _parse_video_stream_info (GST_DISCOVERER_VIDEO_INFO (sinfo), |
2431 | 0 | g_variant_get_child_value (specific, 0)); |
2432 | 0 | break; |
2433 | 0 | case 's': |
2434 | 0 | sinfo = g_object_new (GST_TYPE_DISCOVERER_SUBTITLE_INFO, NULL); |
2435 | 0 | _parse_subtitle_stream_info (GST_DISCOVERER_SUBTITLE_INFO (sinfo), |
2436 | 0 | g_variant_get_child_value (specific, 0)); |
2437 | 0 | break; |
2438 | 0 | case 'n': |
2439 | 0 | sinfo = g_object_new (GST_TYPE_DISCOVERER_STREAM_INFO, NULL); |
2440 | 0 | break; |
2441 | 0 | default: |
2442 | 0 | GST_WARNING ("Unexpected discoverer info type %d", type); |
2443 | 0 | goto out; |
2444 | 0 | } |
2445 | | |
2446 | 0 | _parse_common_stream_info (sinfo, g_variant_get_child_value (common, 0), |
2447 | 0 | info); |
2448 | |
|
2449 | 0 | if (!GST_IS_DISCOVERER_CONTAINER_INFO (sinfo)) { |
2450 | 0 | info->stream_list = g_list_append (info->stream_list, sinfo); |
2451 | 0 | } |
2452 | |
|
2453 | 0 | if (!info->stream_info) { |
2454 | 0 | info->stream_info = sinfo; |
2455 | 0 | } |
2456 | |
|
2457 | 0 | if (GST_IS_DISCOVERER_CONTAINER_INFO (sinfo)) { |
2458 | 0 | GVariantIter iter; |
2459 | 0 | GVariant *child; |
2460 | |
|
2461 | 0 | GstDiscovererContainerInfo *cinfo = GST_DISCOVERER_CONTAINER_INFO (sinfo); |
2462 | 0 | g_variant_iter_init (&iter, specific); |
2463 | 0 | cinfo->tags = sinfo->tags; |
2464 | 0 | while ((child = g_variant_iter_next_value (&iter))) { |
2465 | 0 | GstDiscovererStreamInfo *child_info; |
2466 | 0 | child_info = _parse_discovery (g_variant_get_variant (child), info); |
2467 | 0 | if (child_info != NULL) { |
2468 | 0 | cinfo->streams = |
2469 | 0 | g_list_append (cinfo->streams, |
2470 | 0 | gst_discoverer_stream_info_ref (child_info)); |
2471 | 0 | } |
2472 | 0 | g_variant_unref (child); |
2473 | 0 | } |
2474 | 0 | } |
2475 | |
|
2476 | 0 | out: |
2477 | |
|
2478 | 0 | g_variant_unref (common); |
2479 | 0 | if (specific) |
2480 | 0 | g_variant_unref (specific); |
2481 | 0 | g_variant_unref (variant); |
2482 | 0 | return sinfo; |
2483 | 0 | } |
2484 | | |
2485 | | /** |
2486 | | * gst_discoverer_start: |
2487 | | * @discoverer: A #GstDiscoverer |
2488 | | * |
2489 | | * Allow asynchronous discovering of URIs to take place. |
2490 | | * A #GMainLoop must be available for #GstDiscoverer to properly work in |
2491 | | * asynchronous mode. |
2492 | | */ |
2493 | | void |
2494 | | gst_discoverer_start (GstDiscoverer * discoverer) |
2495 | 0 | { |
2496 | 0 | GSource *source; |
2497 | 0 | GMainContext *ctx = NULL; |
2498 | |
|
2499 | 0 | g_return_if_fail (GST_IS_DISCOVERER (discoverer)); |
2500 | | |
2501 | 0 | GST_DEBUG_OBJECT (discoverer, "Starting..."); |
2502 | |
|
2503 | 0 | if (discoverer->priv->async) { |
2504 | 0 | GST_DEBUG_OBJECT (discoverer, "We were already started"); |
2505 | 0 | return; |
2506 | 0 | } |
2507 | | |
2508 | 0 | discoverer->priv->async = TRUE; |
2509 | 0 | discoverer->priv->running = TRUE; |
2510 | |
|
2511 | 0 | ctx = g_main_context_get_thread_default (); |
2512 | | |
2513 | | /* Connect to bus signals */ |
2514 | 0 | if (ctx == NULL) |
2515 | 0 | ctx = g_main_context_default (); |
2516 | |
|
2517 | 0 | source = gst_bus_create_watch (discoverer->priv->bus); |
2518 | 0 | g_source_set_callback (source, (GSourceFunc) gst_bus_async_signal_func, |
2519 | 0 | NULL, NULL); |
2520 | 0 | g_source_attach (source, ctx); |
2521 | 0 | discoverer->priv->bus_source = source; |
2522 | 0 | discoverer->priv->ctx = g_main_context_ref (ctx); |
2523 | |
|
2524 | 0 | start_discovering (discoverer); |
2525 | 0 | GST_DEBUG_OBJECT (discoverer, "Started"); |
2526 | 0 | } |
2527 | | |
2528 | | /** |
2529 | | * gst_discoverer_stop: |
2530 | | * @discoverer: A #GstDiscoverer |
2531 | | * |
2532 | | * Stop the discovery of any pending URIs and clears the list of |
2533 | | * pending URIS (if any). |
2534 | | */ |
2535 | | void |
2536 | | gst_discoverer_stop (GstDiscoverer * discoverer) |
2537 | 345 | { |
2538 | 345 | g_return_if_fail (GST_IS_DISCOVERER (discoverer)); |
2539 | | |
2540 | 345 | GST_DEBUG_OBJECT (discoverer, "Stopping..."); |
2541 | | |
2542 | 345 | if (!discoverer->priv->async) { |
2543 | 345 | GST_DEBUG_OBJECT (discoverer, |
2544 | 345 | "We were already stopped, or running synchronously"); |
2545 | 345 | return; |
2546 | 345 | } |
2547 | | |
2548 | 0 | DISCO_LOCK (discoverer); |
2549 | 0 | if (discoverer->priv->processing) { |
2550 | | /* We prevent any further processing by setting the bus to |
2551 | | * flushing and setting the pipeline to READY. |
2552 | | * _reset() will take care of the rest of the cleanup */ |
2553 | 0 | if (discoverer->priv->bus) |
2554 | 0 | gst_bus_set_flushing (discoverer->priv->bus, TRUE); |
2555 | 0 | if (discoverer->priv->pipeline) |
2556 | 0 | gst_element_set_state ((GstElement *) discoverer->priv->pipeline, |
2557 | 0 | GST_STATE_READY); |
2558 | 0 | } |
2559 | 0 | discoverer->priv->running = FALSE; |
2560 | 0 | DISCO_UNLOCK (discoverer); |
2561 | | |
2562 | | /* Remove timeout handler */ |
2563 | 0 | if (discoverer->priv->timeout_source) { |
2564 | 0 | g_source_destroy (discoverer->priv->timeout_source); |
2565 | 0 | g_source_unref (discoverer->priv->timeout_source); |
2566 | 0 | discoverer->priv->timeout_source = NULL; |
2567 | 0 | } |
2568 | | /* Remove signal watch */ |
2569 | 0 | if (discoverer->priv->bus_source) { |
2570 | 0 | g_source_destroy (discoverer->priv->bus_source); |
2571 | 0 | g_source_unref (discoverer->priv->bus_source); |
2572 | 0 | discoverer->priv->bus_source = NULL; |
2573 | 0 | } |
2574 | | /* Unref main context */ |
2575 | 0 | if (discoverer->priv->ctx) { |
2576 | 0 | g_main_context_unref (discoverer->priv->ctx); |
2577 | 0 | discoverer->priv->ctx = NULL; |
2578 | 0 | } |
2579 | 0 | discoverer_reset (discoverer); |
2580 | |
|
2581 | 0 | discoverer->priv->async = FALSE; |
2582 | |
|
2583 | 0 | GST_DEBUG_OBJECT (discoverer, "Stopped"); |
2584 | 0 | } |
2585 | | |
2586 | | /** |
2587 | | * gst_discoverer_discover_uri_async: |
2588 | | * @discoverer: A #GstDiscoverer |
2589 | | * @uri: the URI to add. |
2590 | | * |
2591 | | * Appends the given @uri to the list of URIs to discoverer. The actual |
2592 | | * discovery of the @uri will only take place if gst_discoverer_start() has |
2593 | | * been called. |
2594 | | * |
2595 | | * A copy of @uri will be made internally, so the caller can safely g_free() |
2596 | | * afterwards. |
2597 | | * |
2598 | | * Returns: %TRUE if the @uri was successfully appended to the list of pending |
2599 | | * uris, else %FALSE |
2600 | | */ |
2601 | | gboolean |
2602 | | gst_discoverer_discover_uri_async (GstDiscoverer * discoverer, |
2603 | | const gchar * uri) |
2604 | 0 | { |
2605 | 0 | gboolean can_run; |
2606 | |
|
2607 | 0 | g_return_val_if_fail (GST_IS_DISCOVERER (discoverer), FALSE); |
2608 | | |
2609 | 0 | GST_DEBUG_OBJECT (discoverer, "uri : %s", uri); |
2610 | |
|
2611 | 0 | DISCO_LOCK (discoverer); |
2612 | 0 | can_run = (discoverer->priv->pending_uris == NULL); |
2613 | 0 | discoverer->priv->pending_uris = |
2614 | 0 | g_list_append (discoverer->priv->pending_uris, g_strdup (uri)); |
2615 | 0 | DISCO_UNLOCK (discoverer); |
2616 | |
|
2617 | 0 | if (can_run) |
2618 | 0 | start_discovering (discoverer); |
2619 | |
|
2620 | 0 | return TRUE; |
2621 | 0 | } |
2622 | | |
2623 | | |
2624 | | /* Synchronous mode */ |
2625 | | /** |
2626 | | * gst_discoverer_discover_uri: |
2627 | | * @discoverer: A #GstDiscoverer |
2628 | | * @uri: The URI to run on. |
2629 | | * @err: If an error occurred, this field will be filled in. |
2630 | | * |
2631 | | * Synchronously discovers the given @uri. |
2632 | | * |
2633 | | * A copy of @uri will be made internally, so the caller can safely g_free() |
2634 | | * afterwards. |
2635 | | * |
2636 | | * Returns: (transfer full): the result of the scanning. Can be %NULL if an |
2637 | | * error occurred. |
2638 | | */ |
2639 | | GstDiscovererInfo * |
2640 | | gst_discoverer_discover_uri (GstDiscoverer * discoverer, const gchar * uri, |
2641 | | GError ** err) |
2642 | 345 | { |
2643 | 345 | GstDiscovererResult res = 0; |
2644 | 345 | GstDiscovererInfo *info; |
2645 | | |
2646 | 345 | g_return_val_if_fail (GST_IS_DISCOVERER (discoverer), NULL); |
2647 | 345 | g_return_val_if_fail (uri, NULL); |
2648 | | |
2649 | 345 | GST_DEBUG_OBJECT (discoverer, "uri:%s", uri); |
2650 | | |
2651 | 345 | DISCO_LOCK (discoverer); |
2652 | 345 | if (G_UNLIKELY (discoverer->priv->current_info)) { |
2653 | 0 | DISCO_UNLOCK (discoverer); |
2654 | 0 | GST_WARNING_OBJECT (discoverer, "Already handling a uri"); |
2655 | 0 | if (err) |
2656 | 0 | *err = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_FAILED, |
2657 | 0 | "Already handling a uri"); |
2658 | 0 | return NULL; |
2659 | 0 | } |
2660 | | |
2661 | 345 | discoverer->priv->pending_uris = |
2662 | 345 | g_list_append (discoverer->priv->pending_uris, g_strdup (uri)); |
2663 | 345 | DISCO_UNLOCK (discoverer); |
2664 | | |
2665 | 345 | res = start_discovering (discoverer); |
2666 | 345 | discoverer_collect (discoverer); |
2667 | | |
2668 | | /* Get results */ |
2669 | 345 | if (err) { |
2670 | 345 | if (discoverer->priv->current_error) |
2671 | 343 | *err = g_error_copy (discoverer->priv->current_error); |
2672 | 2 | else |
2673 | 2 | *err = NULL; |
2674 | 345 | } |
2675 | 345 | if (res != GST_DISCOVERER_OK) { |
2676 | 0 | GST_DEBUG_OBJECT (discoverer, "Setting result to %d (was %d)", res, |
2677 | 0 | discoverer->priv->current_info->result); |
2678 | 0 | discoverer->priv->current_info->result = res; |
2679 | 0 | } |
2680 | | |
2681 | 345 | info = discoverer->priv->current_info; |
2682 | 345 | discoverer_cleanup (discoverer); |
2683 | | |
2684 | 345 | return info; |
2685 | 345 | } |
2686 | | |
2687 | | /** |
2688 | | * gst_discoverer_new: |
2689 | | * @timeout: timeout per file, in nanoseconds. Allowed are values between |
2690 | | * one second (#GST_SECOND) and one hour (3600 * #GST_SECOND) |
2691 | | * @err: a pointer to a #GError. can be %NULL |
2692 | | * |
2693 | | * Creates a new #GstDiscoverer with the provided timeout. |
2694 | | * |
2695 | | * Returns: (transfer full): The new #GstDiscoverer. |
2696 | | * If an error occurred when creating the discoverer, @err will be set |
2697 | | * accordingly and %NULL will be returned. If @err is set, the caller must |
2698 | | * free it when no longer needed using g_error_free(). |
2699 | | */ |
2700 | | GstDiscoverer * |
2701 | | gst_discoverer_new (GstClockTime timeout, GError ** err) |
2702 | 345 | { |
2703 | 345 | GstDiscoverer *res; |
2704 | | |
2705 | 345 | g_return_val_if_fail (GST_CLOCK_TIME_IS_VALID (timeout), NULL); |
2706 | | |
2707 | 345 | res = g_object_new (GST_TYPE_DISCOVERER, "timeout", timeout, NULL); |
2708 | 345 | if (res->priv->uridecodebin == NULL) { |
2709 | 0 | if (err) |
2710 | 0 | *err = g_error_new (GST_CORE_ERROR, GST_CORE_ERROR_MISSING_PLUGIN, |
2711 | 0 | "Couldn't create 'uridecodebin' element"); |
2712 | 0 | gst_object_unref (res); |
2713 | 0 | res = NULL; |
2714 | 0 | } |
2715 | 345 | return res; |
2716 | 345 | } |
2717 | | |
2718 | | /** |
2719 | | * gst_discoverer_info_to_variant: |
2720 | | * @info: A #GstDiscovererInfo |
2721 | | * @flags: A combination of #GstDiscovererSerializeFlags to specify |
2722 | | * what needs to be serialized. |
2723 | | * |
2724 | | * Serializes @info to a #GVariant that can be parsed again |
2725 | | * through gst_discoverer_info_from_variant(). |
2726 | | * |
2727 | | * Note that any #GstToc (s) that might have been discovered will not be serialized |
2728 | | * for now. |
2729 | | * |
2730 | | * Returns: (transfer full): A newly-allocated #GVariant representing @info. |
2731 | | * |
2732 | | * Since: 1.6 |
2733 | | */ |
2734 | | GVariant * |
2735 | | gst_discoverer_info_to_variant (GstDiscovererInfo * info, |
2736 | | GstDiscovererSerializeFlags flags) |
2737 | 0 | { |
2738 | | /* FIXME: implement TOC support */ |
2739 | 0 | GVariant *stream_variant; |
2740 | 0 | GVariant *variant, *info_variant; |
2741 | 0 | GstDiscovererStreamInfo *sinfo; |
2742 | 0 | GVariant *wrapper; |
2743 | |
|
2744 | 0 | g_return_val_if_fail (GST_IS_DISCOVERER_INFO (info), NULL); |
2745 | 0 | g_return_val_if_fail (gst_discoverer_info_get_result (info) == |
2746 | 0 | GST_DISCOVERER_OK |
2747 | 0 | || gst_discoverer_info_get_result (info) == |
2748 | 0 | GST_DISCOVERER_MISSING_PLUGINS, NULL); |
2749 | | |
2750 | 0 | sinfo = gst_discoverer_info_get_stream_info (info); |
2751 | 0 | stream_variant = gst_discoverer_info_to_variant_recurse (sinfo, flags); |
2752 | 0 | info_variant = _serialize_info (info, flags); |
2753 | |
|
2754 | 0 | variant = g_variant_new ("(vv)", info_variant, stream_variant); |
2755 | | |
2756 | | /* Returning a wrapper implies some small overhead, but simplifies |
2757 | | * deserializing from bytes */ |
2758 | 0 | wrapper = g_variant_new_variant (variant); |
2759 | |
|
2760 | 0 | gst_discoverer_stream_info_unref (sinfo); |
2761 | 0 | return wrapper; |
2762 | 0 | } |
2763 | | |
2764 | | /** |
2765 | | * gst_discoverer_info_from_variant: |
2766 | | * @variant: A #GVariant to deserialize into a #GstDiscovererInfo. |
2767 | | * |
2768 | | * Parses a #GVariant as produced by gst_discoverer_info_to_variant() |
2769 | | * back to a #GstDiscovererInfo. |
2770 | | * |
2771 | | * Returns: (transfer full) (nullable): A newly-allocated #GstDiscovererInfo. |
2772 | | * |
2773 | | * Since: 1.6 |
2774 | | */ |
2775 | | GstDiscovererInfo * |
2776 | | gst_discoverer_info_from_variant (GVariant * variant) |
2777 | 0 | { |
2778 | 0 | GVariant *info_specific_variant; |
2779 | 0 | GVariant *wrapped; |
2780 | |
|
2781 | 0 | g_return_val_if_fail (variant, NULL); |
2782 | | |
2783 | 0 | GVariant *info_variant = g_variant_get_variant (variant); |
2784 | |
|
2785 | 0 | if (!info_variant) { |
2786 | 0 | GST_WARNING ("Failed to get variant from serialized info"); |
2787 | |
|
2788 | 0 | return NULL; |
2789 | 0 | } |
2790 | | |
2791 | 0 | GET_FROM_TUPLE (info_variant, variant, 0, &info_specific_variant); |
2792 | 0 | GET_FROM_TUPLE (info_variant, variant, 1, &wrapped); |
2793 | |
|
2794 | 0 | GstDiscovererInfo *info = g_object_new (GST_TYPE_DISCOVERER_INFO, NULL); |
2795 | 0 | _parse_info (info, info_specific_variant); |
2796 | 0 | _parse_discovery (wrapped, info); |
2797 | 0 | g_variant_unref (info_specific_variant); |
2798 | 0 | g_variant_unref (info_variant); |
2799 | |
|
2800 | 0 | return info; |
2801 | 0 | } |