1 # Basic tutorial 9: Media information gathering
5 Sometimes you might want to quickly find out what kind of media a file
6 (or URI) contains, or if you will be able to play the media at all. You
7 can build a pipeline, set it to run, and watch the bus messages, but
8 GStreamer has a utility that does just that for you. This tutorial
11 - How to recover information regarding a URI
13 - How to find out if a URI is playable
17 `GstDiscoverer` is a utility object found in the `pbutils` library
18 (Plug-in Base utilities) that accepts a URI or list of URIs, and returns
19 information about them. It can work in synchronous or asynchronous
22 In synchronous mode, there is only a single function to call,
23 `gst_discoverer_discover_uri()`, which blocks until the information is
24 ready. Due to this blocking, it is usually less interesting for
25 GUI-based applications and the asynchronous mode is used, as described
28 The recovered information includes codec descriptions, stream topology
29 (number of streams and sub-streams) and available metadata (like the
32 As an example, this is the result
33 of discovering https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel\_trailer-480p.webm
35 Duration: 0:00:52.250000000
39 container format: Matroska
40 application name: ffmpeg2theora-0.24
41 encoder: Xiph.Org libVorbis I 20090709
44 nominal bitrate: 80000
52 container format: Matroska
54 application name: ffmpeg2theora-0.24
55 encoder: Xiph.Org libVorbis I 20090709
57 nominal bitrate: 80000
61 video codec: VP8 video
62 container format: Matroska
64 The following code tries to discover the URI provided through the
65 command line, and outputs the retrieved information (If no URI is
66 provided it uses a default one).
68 This is a simplified version of what the `gst-discoverer-1.0` tool does
69 ([](tutorials/basic/gstreamer-tools.md)), which is
70 an application that only displays data, but does not perform any
73 ## The GStreamer Discoverer
75 Copy this code into a text file named `basic-tutorial-9.c` (or find it
76 in the SDK installation).
78 **basic-tutorial-9.c**
83 #include <gst/pbutils/pbutils.h>
85 /* Structure to contain all our information, so we can pass it around */
86 typedef struct _CustomData {
87 GstDiscoverer *discoverer;
91 /* Print a tag in a human-readable format (name: value) */
92 static void print_tag_foreach (const GstTagList *tags, const gchar *tag, gpointer user_data) {
95 gint depth = GPOINTER_TO_INT (user_data);
97 gst_tag_list_copy_value (&val, tags, tag);
99 if (G_VALUE_HOLDS_STRING (&val))
100 str = g_value_dup_string (&val);
102 str = gst_value_serialize (&val);
104 g_print ("%*s%s: %s\n", 2 * depth, " ", gst_tag_get_nick (tag), str);
107 g_value_unset (&val);
110 /* Print information regarding a stream */
111 static void print_stream_info (GstDiscovererStreamInfo *info, gint depth) {
114 const GstTagList *tags;
116 caps = gst_discoverer_stream_info_get_caps (info);
119 if (gst_caps_is_fixed (caps))
120 desc = gst_pb_utils_get_codec_description (caps);
122 desc = gst_caps_to_string (caps);
123 gst_caps_unref (caps);
126 g_print ("%*s%s: %s\n", 2 * depth, " ", gst_discoverer_stream_info_get_stream_type_nick (info), (desc ? desc : ""));
133 tags = gst_discoverer_stream_info_get_tags (info);
135 g_print ("%*sTags:\n", 2 * (depth + 1), " ");
136 gst_tag_list_foreach (tags, print_tag_foreach, GINT_TO_POINTER (depth + 2));
140 /* Print information regarding a stream and its substreams, if any */
141 static void print_topology (GstDiscovererStreamInfo *info, gint depth) {
142 GstDiscovererStreamInfo *next;
147 print_stream_info (info, depth);
149 next = gst_discoverer_stream_info_get_next (info);
151 print_topology (next, depth + 1);
152 gst_discoverer_stream_info_unref (next);
153 } else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) {
154 GList *tmp, *streams;
156 streams = gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO (info));
157 for (tmp = streams; tmp; tmp = tmp->next) {
158 GstDiscovererStreamInfo *tmpinf = (GstDiscovererStreamInfo *) tmp->data;
159 print_topology (tmpinf, depth + 1);
161 gst_discoverer_stream_info_list_free (streams);
165 /* This function is called every time the discoverer has information regarding
166 * one of the URIs we provided.*/
167 static void on_discovered_cb (GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, CustomData *data) {
168 GstDiscovererResult result;
170 const GstTagList *tags;
171 GstDiscovererStreamInfo *sinfo;
173 uri = gst_discoverer_info_get_uri (info);
174 result = gst_discoverer_info_get_result (info);
176 case GST_DISCOVERER_URI_INVALID:
177 g_print ("Invalid URI '%s'\n", uri);
179 case GST_DISCOVERER_ERROR:
180 g_print ("Discoverer error: %s\n", err->message);
182 case GST_DISCOVERER_TIMEOUT:
183 g_print ("Timeout\n");
185 case GST_DISCOVERER_BUSY:
188 case GST_DISCOVERER_MISSING_PLUGINS:{
189 const GstStructure *s;
192 s = gst_discoverer_info_get_misc (info);
193 str = gst_structure_to_string (s);
195 g_print ("Missing plugins: %s\n", str);
199 case GST_DISCOVERER_OK:
200 g_print ("Discovered '%s'\n", uri);
204 if (result != GST_DISCOVERER_OK) {
205 g_printerr ("This URI cannot be played\n");
209 /* If we got no error, show the retrieved information */
211 g_print ("\nDuration: %" GST_TIME_FORMAT "\n", GST_TIME_ARGS (gst_discoverer_info_get_duration (info)));
213 tags = gst_discoverer_info_get_tags (info);
216 gst_tag_list_foreach (tags, print_tag_foreach, GINT_TO_POINTER (1));
219 g_print ("Seekable: %s\n", (gst_discoverer_info_get_seekable (info) ? "yes" : "no"));
223 sinfo = gst_discoverer_info_get_stream_info (info);
227 g_print ("Stream information:\n");
229 print_topology (sinfo, 1);
231 gst_discoverer_stream_info_unref (sinfo);
236 /* This function is called when the discoverer has finished examining
237 * all the URIs we provided.*/
238 static void on_finished_cb (GstDiscoverer *discoverer, CustomData *data) {
239 g_print ("Finished discovering\n");
241 g_main_loop_quit (data->loop);
244 int main (int argc, char **argv) {
247 gchar *uri = "https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm";
249 /* if a URI was provided, use it instead of the default one */
254 /* Initialize cumstom data structure */
255 memset (&data, 0, sizeof (data));
257 /* Initialize GStreamer */
258 gst_init (&argc, &argv);
260 g_print ("Discovering '%s'\n", uri);
262 /* Instantiate the Discoverer */
263 data.discoverer = gst_discoverer_new (5 * GST_SECOND, &err);
264 if (!data.discoverer) {
265 g_print ("Error creating discoverer instance: %s\n", err->message);
266 g_clear_error (&err);
270 /* Connect to the interesting signals */
271 g_signal_connect (data.discoverer, "discovered", G_CALLBACK (on_discovered_cb), &data);
272 g_signal_connect (data.discoverer, "finished", G_CALLBACK (on_finished_cb), &data);
274 /* Start the discoverer process (nothing to do yet) */
275 gst_discoverer_start (data.discoverer);
277 /* Add a request to process asynchronously the URI passed through the command line */
278 if (!gst_discoverer_discover_uri_async (data.discoverer, uri)) {
279 g_print ("Failed to start discovering URI '%s'\n", uri);
280 g_object_unref (data.discoverer);
284 /* Create a GLib Main Loop and set it to run, so we can wait for the signals */
285 data.loop = g_main_loop_new (NULL, FALSE);
286 g_main_loop_run (data.loop);
288 /* Stop the discoverer process */
289 gst_discoverer_stop (data.discoverer);
292 g_object_unref (data.discoverer);
293 g_main_loop_unref (data.loop);
300 > ![Information](images/icons/emoticons/information.png)
303 > If you need help to compile this code, refer to the **Building the tutorials** section for your platform: [Linux](installing/on-linux.md#InstallingonLinux-Build), [Mac OS X](installing/on-mac-osx.md#InstallingonMacOSX-Build) or [Windows](installing/on-windows.md#InstallingonWindows-Build), or use this specific command on Linux:
305 > ``gcc basic-tutorial-9.c -o basic-tutorial-9 `pkg-config --cflags --libs gstreamer-1.0 gstreamer-pbutils-1.0` ``
307 >If you need help to run this code, refer to the **Running the tutorials** section for your platform: [Linux](installing/on-linux.md#InstallingonLinux-Run), [Mac OS X](installing/on-mac-osx.md#InstallingonMacOSX-Run) or [Windows](installing/on-windows.md#InstallingonWindows-Run).
309 > This tutorial opens the URI passed as the first parameter in the command line (or a default URI if none is provided) and outputs information about it on the screen. If the media is located on the Internet, the application might take a bit to react depending on your connection speed.
311 > Required libraries: `gstreamer-pbutils-1.0` `gstreamer-1.0`
316 These are the main steps to use the `GstDiscoverer`:
319 /* Instantiate the Discoverer */
320 data.discoverer = gst_discoverer_new (5 * GST_SECOND, &err);
321 if (!data.discoverer) {
322 g_print ("Error creating discoverer instance: %s\n", err->message);
323 g_clear_error (&err);
328 `gst_discoverer_new()` creates a new Discoverer object. The first
329 parameter is the timeout per file, in nanoseconds (use the
330 `GST_SECOND` macro for simplicity).
333 /* Connect to the interesting signals */
334 g_signal_connect (data.discoverer, "discovered", G_CALLBACK (on_discovered_cb), &data);
335 g_signal_connect (data.discoverer, "finished", G_CALLBACK (on_finished_cb), &data);
338 Connect to the interesting signals, as usual. We discuss them in the
339 snippet for their callbacks.
342 /* Start the discoverer process (nothing to do yet) */
343 gst_discoverer_start (data.discoverer);
346 `gst_discoverer_start()` launches the discovering process, but we have
347 not provided any URI to discover yet. This is done
351 /* Add a request to process asynchronously the URI passed through the command line */
352 if (!gst_discoverer_discover_uri_async (data.discoverer, uri)) {
353 g_print ("Failed to start discovering URI '%s'\n", uri);
354 g_object_unref (data.discoverer);
359 `gst_discoverer_discover_uri_async()` enqueues the provided URI for
360 discovery. Multiple URIs can be enqueued with this function. As the
361 discovery process for each of them finishes, the registered callback
362 functions will be fired
366 /* Create a GLib Main Loop and set it to run, so we can wait for the signals */
367 data.loop = g_main_loop_new (NULL, FALSE);
368 g_main_loop_run (data.loop);
371 The usual GLib main loop is instantiated and executed. We will get out
372 of it when `g_main_loop_quit()` is called from the
373 `on_finished_cb` callback.
376 /* Stop the discoverer process */
377 gst_discoverer_stop (data.discoverer);
380 Once we are done with the discoverer, we stop it with
381 `gst_discoverer_stop()` and unref it with `g_object_unref()`.
383 Let's review now the callbacks we have
387 /* This function is called every time the discoverer has information regarding
388 * one of the URIs we provided.*/
389 static void on_discovered_cb (GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, CustomData *data) {
390 GstDiscovererResult result;
392 const GstTagList *tags;
393 GstDiscovererStreamInfo *sinfo;
395 uri = gst_discoverer_info_get_uri (info);
396 result = gst_discoverer_info_get_result (info);
399 We got here because the Discoverer has finished working on one URI, and
400 provides us a `GstDiscovererInfo` structure with all the information.
402 The first step is to retrieve the particular URI this call refers to (in
403 case we had multiple discover process running, which is not the case in
404 this example) with `gst_discoverer_info_get_uri()` and the discovery
405 result with `gst_discoverer_info_get_result()`.
409 case GST_DISCOVERER_URI_INVALID:
410 g_print ("Invalid URI '%s'\n", uri);
412 case GST_DISCOVERER_ERROR:
413 g_print ("Discoverer error: %s\n", err->message);
415 case GST_DISCOVERER_TIMEOUT:
416 g_print ("Timeout\n");
418 case GST_DISCOVERER_BUSY:
421 case GST_DISCOVERER_MISSING_PLUGINS:{
422 const GstStructure *s;
425 s = gst_discoverer_info_get_misc (info);
426 str = gst_structure_to_string (s);
428 g_print ("Missing plugins: %s\n", str);
432 case GST_DISCOVERER_OK:
433 g_print ("Discovered '%s'\n", uri);
437 if (result != GST_DISCOVERER_OK) {
438 g_printerr ("This URI cannot be played\n");
443 As the code shows, any result other than `GST_DISCOVERER_OK` means that
444 there has been some kind of problem, and this URI cannot be played. The
445 reasons can vary, but the enum values are quite explicit
446 (`GST_DISCOVERER_BUSY` can only happen when in synchronous mode, which
447 is not used in this example).
449 If no error happened, information can be retrieved from the
450 `GstDiscovererInfo` structure with the different
451 `gst_discoverer_info_get_*` methods (like,
452 `gst_discoverer_info_get_duration()`, for example).
454 Bits of information which are made of lists, like tags and stream info,
455 needs some extra parsing:
458 tags = gst_discoverer_info_get_tags (info);
461 gst_tag_list_foreach (tags, print_tag_foreach, GINT_TO_POINTER (1));
465 Tags are metadata (labels) attached to the media. They can be examined
466 with `gst_tag_list_foreach()`, which will call `print_tag_foreach` for
467 each tag found (the list could also be traversed manually, for example,
468 or a specific tag could be searched for with
469 `gst_tag_list_get_string()`). The code for `print_tag_foreach` is pretty
470 much self-explicative.
473 sinfo = gst_discoverer_info_get_stream_info (info);
477 g_print ("Stream information:\n");
479 print_topology (sinfo, 1);
481 gst_discoverer_stream_info_unref (sinfo);
484 `gst_discoverer_info_get_stream_info()` returns
485 a `GstDiscovererStreamInfo` structure that is parsed in
486 the `print_topology` function, and then discarded
487 with `gst_discoverer_stream_info_unref()`.
490 /* Print information regarding a stream and its substreams, if any */
491 static void print_topology (GstDiscovererStreamInfo *info, gint depth) {
492 GstDiscovererStreamInfo *next;
497 print_stream_info (info, depth);
499 next = gst_discoverer_stream_info_get_next (info);
501 print_topology (next, depth + 1);
502 gst_discoverer_stream_info_unref (next);
503 } else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) {
504 GList *tmp, *streams;
506 streams = gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO (info));
507 for (tmp = streams; tmp; tmp = tmp->next) {
508 GstDiscovererStreamInfo *tmpinf = (GstDiscovererStreamInfo *) tmp->data;
509 print_topology (tmpinf, depth + 1);
511 gst_discoverer_stream_info_list_free (streams);
516 The `print_stream_info` function's code is also pretty much
517 self-explicative: it prints the stream's capabilities and then the
518 associated caps, using `print_tag_foreach` too.
520 Then, `print_topology` looks for the next element to display. If
521 `gst_discoverer_stream_info_get_next()` returns a non-NULL stream info,
522 it refers to our descendant and that should be displayed. Otherwise, if
523 we are a container, recursively call `print_topology` on each of our
524 children obatined with `gst_discoverer_container_info_get_streams()`.
525 Otherwise, we are a final stream, and do not need to recurse (This part
526 of the Discoverer API is admittedly a bit obscure).
530 This tutorial has shown:
532 - How to recover information regarding a URI using the `GstDiscoverer`
534 - How to find out if a URI is playable by looking at the return code
535 obtained with `gst_discoverer_info_get_result()`.
537 It has been a pleasure having you here, and see you soon!