summaryrefslogtreecommitdiff
path: root/sdk-basic-tutorial-media-information-gathering.md
diff options
context:
space:
mode:
authorMathieu Duponchelle <mathieu.duponchelle@opencreed.com>2016-06-05 23:32:50 +0200
committerMathieu Duponchelle <mathieu.duponchelle@opencreed.com>2016-06-05 23:36:01 +0200
commitc26379435d2baa57505f928c7b0cd263a37fa36f (patch)
tree96d7932cdfde65c5a8b8415a2ddf069732c819c1 /sdk-basic-tutorial-media-information-gathering.md
parent6cdcdcd2d9fc6e12803d1a3151084fbf9e631819 (diff)
Make naming consistent
Diffstat (limited to 'sdk-basic-tutorial-media-information-gathering.md')
-rw-r--r--sdk-basic-tutorial-media-information-gathering.md548
1 files changed, 548 insertions, 0 deletions
diff --git a/sdk-basic-tutorial-media-information-gathering.md b/sdk-basic-tutorial-media-information-gathering.md
new file mode 100644
index 0000000..04fa816
--- /dev/null
+++ b/sdk-basic-tutorial-media-information-gathering.md
@@ -0,0 +1,548 @@
+# Basic tutorial 9: Media information gathering
+
+# Goal
+
+Sometimes you might want to quickly find out what kind of media a file
+(or URI) contains, or if you will be able to play the media at all. You
+can build a pipeline, set it to run, and watch the bus messages, but
+GStreamer has a utility that does just that for you. This tutorial
+shows:
+
+ - How to recover information regarding a URI
+
+ - How to find out if a URI is playable
+
+# Introduction
+
+`GstDiscoverer` is a utility object found in the `pbutils` library
+(Plug-in Base utilities) that accepts a URI or list of URIs, and returns
+information about them. It can work in synchronous or asynchronous
+modes.
+
+In synchronous mode, there is only a single function to call,
+`gst_discoverer_discover_uri()`, which blocks until the information is
+ready. Due to this blocking, it is usually less interesting for
+GUI-based applications and the asynchronous mode is used, as described
+in this tutorial.
+
+The recovered information includes codec descriptions, stream topology
+(number of streams and sub-streams) and available metadata (like the
+audio language).
+
+![](images/icons/grey_arrow_down.gif)As an example, this is the result
+of discovering http://docs.gstreamer.com/media/sintel\_trailer-480p.webm
+(Click to expand)
+
+ Duration: 0:00:52.250000000
+ Tags:
+ video codec: On2 VP8
+ language code: en
+ container format: Matroska
+ application name: ffmpeg2theora-0.24
+ encoder: Xiph.Org libVorbis I 20090709
+ encoder version: 0
+ audio codec: Vorbis
+ nominal bitrate: 80000
+ bitrate: 80000
+ Seekable: yes
+ Stream information:
+ container: WebM
+ audio: Vorbis
+ Tags:
+ language code: en
+ container format: Matroska
+ audio codec: Vorbis
+ application name: ffmpeg2theora-0.24
+ encoder: Xiph.Org libVorbis I 20090709
+ encoder version: 0
+ nominal bitrate: 80000
+ bitrate: 80000
+ video: VP8
+ Tags:
+ video codec: VP8 video
+ container format: Matroska
+
+The following code tries to discover the URI provided through the
+command line, and outputs the retrieved information (If no URI is
+provided it uses a default one).
+
+This is a simplified version of what the `gst-discoverer-1.0` tool does
+([Basic tutorial 10: GStreamer
+tools](Basic%2Btutorial%2B10%253A%2BGStreamer%2Btools.html)), which is
+an application that only displays data, but does not perform any
+playback.
+
+# The GStreamer Discoverer
+
+Copy this code into a text file named `basic-tutorial-9.c` (or find it
+in the SDK installation).
+
+**basic-tutorial-9.c**
+
+``` lang=c
+#include <string.h>
+#include <gst/gst.h>
+#include <gst/pbutils/pbutils.h>
+
+/* Structure to contain all our information, so we can pass it around */
+typedef struct _CustomData {
+ GstDiscoverer *discoverer;
+ GMainLoop *loop;
+} CustomData;
+
+/* Print a tag in a human-readable format (name: value) */
+static void print_tag_foreach (const GstTagList *tags, const gchar *tag, gpointer user_data) {
+ GValue val = { 0, };
+ gchar *str;
+ gint depth = GPOINTER_TO_INT (user_data);
+
+ gst_tag_list_copy_value (&val, tags, tag);
+
+ if (G_VALUE_HOLDS_STRING (&val))
+ str = g_value_dup_string (&val);
+ else
+ str = gst_value_serialize (&val);
+
+ g_print ("%*s%s: %s\n", 2 * depth, " ", gst_tag_get_nick (tag), str);
+ g_free (str);
+
+ g_value_unset (&val);
+}
+
+/* Print information regarding a stream */
+static void print_stream_info (GstDiscovererStreamInfo *info, gint depth) {
+ gchar *desc = NULL;
+ GstCaps *caps;
+ const GstTagList *tags;
+
+ caps = gst_discoverer_stream_info_get_caps (info);
+
+ if (caps) {
+ if (gst_caps_is_fixed (caps))
+ desc = gst_pb_utils_get_codec_description (caps);
+ else
+ desc = gst_caps_to_string (caps);
+ gst_caps_unref (caps);
+ }
+
+ g_print ("%*s%s: %s\n", 2 * depth, " ", gst_discoverer_stream_info_get_stream_type_nick (info), (desc ? desc : ""));
+
+ if (desc) {
+ g_free (desc);
+ desc = NULL;
+ }
+
+ tags = gst_discoverer_stream_info_get_tags (info);
+ if (tags) {
+ g_print ("%*sTags:\n", 2 * (depth + 1), " ");
+ gst_tag_list_foreach (tags, print_tag_foreach, GINT_TO_POINTER (depth + 2));
+ }
+}
+
+/* Print information regarding a stream and its substreams, if any */
+static void print_topology (GstDiscovererStreamInfo *info, gint depth) {
+ GstDiscovererStreamInfo *next;
+
+ if (!info)
+ return;
+
+ print_stream_info (info, depth);
+
+ next = gst_discoverer_stream_info_get_next (info);
+ if (next) {
+ print_topology (next, depth + 1);
+ gst_discoverer_stream_info_unref (next);
+ } else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) {
+ GList *tmp, *streams;
+
+ streams = gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO (info));
+ for (tmp = streams; tmp; tmp = tmp->next) {
+ GstDiscovererStreamInfo *tmpinf = (GstDiscovererStreamInfo *) tmp->data;
+ print_topology (tmpinf, depth + 1);
+ }
+ gst_discoverer_stream_info_list_free (streams);
+ }
+}
+
+/* This function is called every time the discoverer has information regarding
+ * one of the URIs we provided.*/
+static void on_discovered_cb (GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, CustomData *data) {
+ GstDiscovererResult result;
+ const gchar *uri;
+ const GstTagList *tags;
+ GstDiscovererStreamInfo *sinfo;
+
+ uri = gst_discoverer_info_get_uri (info);
+ result = gst_discoverer_info_get_result (info);
+ switch (result) {
+ case GST_DISCOVERER_URI_INVALID:
+ g_print ("Invalid URI '%s'\n", uri);
+ break;
+ case GST_DISCOVERER_ERROR:
+ g_print ("Discoverer error: %s\n", err->message);
+ break;
+ case GST_DISCOVERER_TIMEOUT:
+ g_print ("Timeout\n");
+ break;
+ case GST_DISCOVERER_BUSY:
+ g_print ("Busy\n");
+ break;
+ case GST_DISCOVERER_MISSING_PLUGINS:{
+ const GstStructure *s;
+ gchar *str;
+
+ s = gst_discoverer_info_get_misc (info);
+ str = gst_structure_to_string (s);
+
+ g_print ("Missing plugins: %s\n", str);
+ g_free (str);
+ break;
+ }
+ case GST_DISCOVERER_OK:
+ g_print ("Discovered '%s'\n", uri);
+ break;
+ }
+
+ if (result != GST_DISCOVERER_OK) {
+ g_printerr ("This URI cannot be played\n");
+ return;
+ }
+
+ /* If we got no error, show the retrieved information */
+
+ g_print ("\nDuration: %" GST_TIME_FORMAT "\n", GST_TIME_ARGS (gst_discoverer_info_get_duration (info)));
+
+ tags = gst_discoverer_info_get_tags (info);
+ if (tags) {
+ g_print ("Tags:\n");
+ gst_tag_list_foreach (tags, print_tag_foreach, GINT_TO_POINTER (1));
+ }
+
+ g_print ("Seekable: %s\n", (gst_discoverer_info_get_seekable (info) ? "yes" : "no"));
+
+ g_print ("\n");
+
+ sinfo = gst_discoverer_info_get_stream_info (info);
+ if (!sinfo)
+ return;
+
+ g_print ("Stream information:\n");
+
+ print_topology (sinfo, 1);
+
+ gst_discoverer_stream_info_unref (sinfo);
+
+ g_print ("\n");
+}
+
+/* This function is called when the discoverer has finished examining
+ * all the URIs we provided.*/
+static void on_finished_cb (GstDiscoverer *discoverer, CustomData *data) {
+ g_print ("Finished discovering\n");
+
+ g_main_loop_quit (data->loop);
+}
+
+int main (int argc, char **argv) {
+ CustomData data;
+ GError *err = NULL;
+ gchar *uri = "http://docs.gstreamer.com/media/sintel_trailer-480p.webm";
+
+ /* if a URI was provided, use it instead of the default one */
+ if (argc > 1) {
+ uri = argv[1];
+ }
+
+ /* Initialize cumstom data structure */
+ memset (&data, 0, sizeof (data));
+
+ /* Initialize GStreamer */
+ gst_init (&argc, &argv);
+
+ g_print ("Discovering '%s'\n", uri);
+
+ /* Instantiate the Discoverer */
+ data.discoverer = gst_discoverer_new (5 * GST_SECOND, &err);
+ if (!data.discoverer) {
+ g_print ("Error creating discoverer instance: %s\n", err->message);
+ g_clear_error (&err);
+ return -1;
+ }
+
+ /* Connect to the interesting signals */
+ g_signal_connect (data.discoverer, "discovered", G_CALLBACK (on_discovered_cb), &data);
+ g_signal_connect (data.discoverer, "finished", G_CALLBACK (on_finished_cb), &data);
+
+ /* Start the discoverer process (nothing to do yet) */
+ gst_discoverer_start (data.discoverer);
+
+ /* Add a request to process asynchronously the URI passed through the command line */
+ if (!gst_discoverer_discover_uri_async (data.discoverer, uri)) {
+ g_print ("Failed to start discovering URI '%s'\n", uri);
+ g_object_unref (data.discoverer);
+ return -1;
+ }
+
+ /* Create a GLib Main Loop and set it to run, so we can wait for the signals */
+ data.loop = g_main_loop_new (NULL, FALSE);
+ g_main_loop_run (data.loop);
+
+ /* Stop the discoverer process */
+ gst_discoverer_stop (data.discoverer);
+
+ /* Free resources */
+ g_object_unref (data.discoverer);
+ g_main_loop_unref (data.loop);
+
+ return 0;
+}
+```
+
+<table>
+<tbody>
+<tr class="odd">
+<td><img src="images/icons/emoticons/information.png" width="16" height="16" /></td>
+<td><div id="expander-2049220294" class="expand-container">
+<div id="expander-control-2049220294" class="expand-control">
+<span class="expand-control-icon"><img src="images/icons/grey_arrow_down.gif" class="expand-control-image" /></span><span class="expand-control-text">Need help? (Click to expand)</span>
+</div>
+<div id="expander-content-2049220294" class="expand-content">
+<p>If you need help to compile this code, refer to the <strong>Building the tutorials</strong> section for your platform: <a href="Installing%2Bon%2BLinux.html#InstallingonLinux-Build">Linux</a>, <a href="Installing%2Bon%2BMac%2BOS%2BX.html#InstallingonMacOSX-Build">Mac OS X</a> or <a href="Installing%2Bon%2BWindows.html#InstallingonWindows-Build">Windows</a>, or use this specific command on Linux:</p>
+<div class="panel" style="border-width: 1px;">
+<div class="panelContent">
+<p><code>gcc basic-tutorial-9.c -o basic-tutorial-9 `pkg-config --cflags --libs gstreamer-pbutils-1.0 gstreamer-1.0`</code></p>
+</div>
+</div>
+<p>If you need help to run this code, refer to the <strong>Running the tutorials</strong> section for your platform: <a href="Installing%2Bon%2BLinux.html#InstallingonLinux-Run">Linux</a>, <a href="Installing%2Bon%2BMac%2BOS%2BX.html#InstallingonMacOSX-Run">Mac OS X</a> or <a href="Installing%2Bon%2BWindows.html#InstallingonWindows-Run">Windows</a></p>
+<p><span>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.</span></p>
+<p>Required libraries: <code>gstreamer-pbutils-1.0 gstreamer-1.0</code></p>
+</div>
+</div></td>
+</tr>
+</tbody>
+</table>
+
+# Walkthrough
+
+These are the main steps to use the `GstDiscoverer`:
+
+``` lang=c
+/* Instantiate the Discoverer */
+data.discoverer = gst_discoverer_new (5 * GST_SECOND, &err);
+if (!data.discoverer) {
+ g_print ("Error creating discoverer instance: %s\n", err->message);
+ g_clear_error (&err);
+ return -1;
+}
+```
+
+`gst_discoverer_new()` creates a new Discoverer object. The first
+parameter is the timeout per file, in nanoseconds (use the
+`GST_SECOND` macro for simplicity).
+
+``` lang=c
+/* Connect to the interesting signals */
+g_signal_connect (data.discoverer, "discovered", G_CALLBACK (on_discovered_cb), &data);
+g_signal_connect (data.discoverer, "finished", G_CALLBACK (on_finished_cb), &data);
+```
+
+Connect to the interesting signals, as usual. We discuss them in the
+snippet for their callbacks.
+
+``` lang=c
+/* Start the discoverer process (nothing to do yet) */
+gst_discoverer_start (data.discoverer);
+```
+
+`gst_discoverer_start()` launches the discovering process, but we have
+not provided any URI to discover yet. This is done
+next:
+
+``` lang=c
+/* Add a request to process asynchronously the URI passed through the command line */
+if (!gst_discoverer_discover_uri_async (data.discoverer, uri)) {
+ g_print ("Failed to start discovering URI '%s'\n", uri);
+ g_object_unref (data.discoverer);
+ return -1;
+}
+```
+
+`gst_discoverer_discover_uri_async()` enqueues the provided URI for
+discovery. Multiple URIs can be enqueued with this function. As the
+discovery process for each of them finishes, the registered callback
+functions will be fired
+up.
+
+``` lang=c
+/* Create a GLib Main Loop and set it to run, so we can wait for the signals */
+data.loop = g_main_loop_new (NULL, FALSE);
+g_main_loop_run (data.loop);
+```
+
+The usual GLib main loop is instantiated and executed. We will get out
+of it when `g_main_loop_quit()` is called from the
+`on_finished_cb` callback.
+
+``` lang=c
+/* Stop the discoverer process */
+gst_discoverer_stop (data.discoverer);
+```
+
+Once we are done with the discoverer, we stop it with
+`gst_discoverer_stop()` and unref it with `g_object_unref()`.
+
+Let's review now the callbacks we have
+registered:
+
+``` lang=c
+/* This function is called every time the discoverer has information regarding
+ * one of the URIs we provided.*/
+static void on_discovered_cb (GstDiscoverer *discoverer, GstDiscovererInfo *info, GError *err, CustomData *data) {
+ GstDiscovererResult result;
+ const gchar *uri;
+ const GstTagList *tags;
+ GstDiscovererStreamInfo *sinfo;
+
+ uri = gst_discoverer_info_get_uri (info);
+ result = gst_discoverer_info_get_result (info);
+```
+
+We got here because the Discoverer has finished working on one URI, and
+provides us a `GstDiscovererInfo` structure with all the information.
+
+The first step is to retrieve the particular URI this call refers to (in
+case we had multiple discover process running, which is not the case in
+this example) with `gst_discoverer_info_get_uri()` and the discovery
+result with `gst_discoverer_info_get_result()`.
+
+``` lang=c
+switch (result) {
+ case GST_DISCOVERER_URI_INVALID:
+ g_print ("Invalid URI '%s'\n", uri);
+ break;
+ case GST_DISCOVERER_ERROR:
+ g_print ("Discoverer error: %s\n", err->message);
+ break;
+ case GST_DISCOVERER_TIMEOUT:
+ g_print ("Timeout\n");
+ break;
+ case GST_DISCOVERER_BUSY:
+ g_print ("Busy\n");
+ break;
+ case GST_DISCOVERER_MISSING_PLUGINS:{
+ const GstStructure *s;
+ gchar *str;
+
+ s = gst_discoverer_info_get_misc (info);
+ str = gst_structure_to_string (s);
+
+ g_print ("Missing plugins: %s\n", str);
+ g_free (str);
+ break;
+ }
+ case GST_DISCOVERER_OK:
+ g_print ("Discovered '%s'\n", uri);
+ break;
+}
+
+if (result != GST_DISCOVERER_OK) {
+ g_printerr ("This URI cannot be played\n");
+ return;
+}
+```
+
+As the code shows, any result other than `GST_DISCOVERER_OK` means that
+there has been some kind of problem, and this URI cannot be played. The
+reasons can vary, but the enum values are quite explicit
+(`GST_DISCOVERER_BUSY` can only happen when in synchronous mode, which
+is not used in this example).
+
+If no error happened, information can be retrieved from the
+`GstDiscovererInfo` structure with the different
+`gst_discoverer_info_get_*` methods (like,
+`gst_discoverer_info_get_duration()`, for example).
+
+Bits of information which are made of lists, like tags and stream info,
+needs some extra parsing:
+
+``` lang=c
+tags = gst_discoverer_info_get_tags (info);
+if (tags) {
+ g_print ("Tags:\n");
+ gst_tag_list_foreach (tags, print_tag_foreach, GINT_TO_POINTER (1));
+}
+```
+
+Tags are metadata (labels) attached to the media. They can be examined
+with `gst_tag_list_foreach()`, which will call `print_tag_foreach` for
+each tag found (the list could also be traversed manually, for example,
+or a specific tag could be searched for with
+`gst_tag_list_get_string()`). The code for `print_tag_foreach` is pretty
+much self-explicative.
+
+``` lang=c
+sinfo = gst_discoverer_info_get_stream_info (info);
+if (!sinfo)
+ return;
+
+g_print ("Stream information:\n");
+
+print_topology (sinfo, 1);
+
+gst_discoverer_stream_info_unref (sinfo);
+```
+
+`gst_discoverer_info_get_stream_info()` returns
+a `GstDiscovererStreamInfo` structure that is parsed in
+the `print_topology` function, and then discarded
+with `gst_discoverer_stream_info_unref()`.
+
+``` lang=c
+/* Print information regarding a stream and its substreams, if any */
+static void print_topology (GstDiscovererStreamInfo *info, gint depth) {
+ GstDiscovererStreamInfo *next;
+
+ if (!info)
+ return;
+
+ print_stream_info (info, depth);
+
+ next = gst_discoverer_stream_info_get_next (info);
+ if (next) {
+ print_topology (next, depth + 1);
+ gst_discoverer_stream_info_unref (next);
+ } else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) {
+ GList *tmp, *streams;
+
+ streams = gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO (info));
+ for (tmp = streams; tmp; tmp = tmp->next) {
+ GstDiscovererStreamInfo *tmpinf = (GstDiscovererStreamInfo *) tmp->data;
+ print_topology (tmpinf, depth + 1);
+ }
+ gst_discoverer_stream_info_list_free (streams);
+ }
+}
+```
+
+The `print_stream_info` function's code is also pretty much
+self-explicative: it prints the stream's capabilities and then the
+associated caps, using `print_tag_foreach` too.
+
+Then, `print_topology` looks for the next element to display. If
+`gst_discoverer_stream_info_get_next()` returns a non-NULL stream info,
+it refers to our descendant and that should be displayed. Otherwise, if
+we are a container, recursively call `print_topology` on each of our
+children obatined with `gst_discoverer_container_info_get_streams()`.
+Otherwise, we are a final stream, and do not need to recurse (This part
+of the Discoverer API is admittedly a bit obscure).
+
+# Conclusion
+
+This tutorial has shown:
+
+ - How to recover information regarding a URI using the `GstDiscoverer`
+
+ - How to find out if a URI is playable by looking at the return code
+ obtained with `gst_discoverer_info_get_result()`.
+
+It has been a pleasure having you here, and see you soon\!