1 # Playback tutorial 2: Subtitle management
5 This tutorial is very similar to the previous one, but instead of
6 switching among different audio streams, we will use subtitle streams.
7 This will allow us to learn:
9 - How to choose the subtitle stream
11 - How to add external subtitles
13 - How to customize the font used for the subtitles
17 We already know (from the previous tutorial) that container files can
18 hold multiple audio and video streams, and that we can very easily
19 choose among them by changing the `current-audio` or
20 `current-video` `playbin` property. Switching subtitles is just as
23 It is worth noting that, just like it happens with audio and video,
24 `playbin` takes care of choosing the right decoder for the subtitles,
25 and that the plugin structure of GStreamer allows adding support for new
26 formats as easily as copying a file. Everything is invisible to the
27 application developer.
29 Besides subtitles embedded in the container, `playbin` offers the
30 possibility to add an extra subtitle stream from an external URI.
32 This tutorial opens a file which already contains 5 subtitle streams,
33 and adds another one from another file (for the Greek language).
35 ## The multilingual player with subtitles
37 Copy this code into a text file named `playback-tutorial-2.c` (or find
38 it in the GStreamer installation).
40 **playback-tutorial-2.c**
46 /* Structure to contain all our information, so we can pass it around */
47 typedef struct _CustomData {
48 GstElement *playbin; /* Our one and only element */
50 gint n_video; /* Number of embedded video streams */
51 gint n_audio; /* Number of embedded audio streams */
52 gint n_text; /* Number of embedded subtitle streams */
54 gint current_video; /* Currently playing video stream */
55 gint current_audio; /* Currently playing audio stream */
56 gint current_text; /* Currently playing subtitle stream */
58 GMainLoop *main_loop; /* GLib's Main Loop */
63 GST_PLAY_FLAG_VIDEO = (1 << 0), /* We want video output */
64 GST_PLAY_FLAG_AUDIO = (1 << 1), /* We want audio output */
65 GST_PLAY_FLAG_TEXT = (1 << 2) /* We want subtitle output */
68 /* Forward definition for the message and keyboard processing functions */
69 static gboolean handle_message (GstBus *bus, GstMessage *msg, CustomData *data);
70 static gboolean handle_keyboard (GIOChannel *source, GIOCondition cond, CustomData *data);
72 int main(int argc, char *argv[]) {
75 GstStateChangeReturn ret;
79 /* Initialize GStreamer */
80 gst_init (&argc, &argv);
82 /* Create the elements */
83 data.playbin = gst_element_factory_make ("playbin", "playbin");
86 g_printerr ("Not all elements could be created.\n");
90 /* Set the URI to play */
91 g_object_set (data.playbin, "uri", "http://docs.gstreamer.com/media/sintel_trailer-480p.ogv", NULL);
93 /* Set the subtitle URI to play and some font description */
94 g_object_set (data.playbin, "suburi", "http://docs.gstreamer.com/media/sintel_trailer_gr.srt", NULL);
95 g_object_set (data.playbin, "subtitle-font-desc", "Sans, 18", NULL);
97 /* Set flags to show Audio, Video and Subtitles */
98 g_object_get (data.playbin, "flags", &flags, NULL);
99 flags |= GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_TEXT;
100 g_object_set (data.playbin, "flags", flags, NULL);
102 /* Add a bus watch, so we get notified when a message arrives */
103 bus = gst_element_get_bus (data.playbin);
104 gst_bus_add_watch (bus, (GstBusFunc)handle_message, &data);
106 /* Add a keyboard watch so we get notified of keystrokes */
108 io_stdin = g_io_channel_win32_new_fd (fileno (stdin));
110 io_stdin = g_io_channel_unix_new (fileno (stdin));
112 g_io_add_watch (io_stdin, G_IO_IN, (GIOFunc)handle_keyboard, &data);
115 ret = gst_element_set_state (data.playbin, GST_STATE_PLAYING);
116 if (ret == GST_STATE_CHANGE_FAILURE) {
117 g_printerr ("Unable to set the pipeline to the playing state.\n");
118 gst_object_unref (data.playbin);
122 /* Create a GLib Main Loop and set it to run */
123 data.main_loop = g_main_loop_new (NULL, FALSE);
124 g_main_loop_run (data.main_loop);
127 g_main_loop_unref (data.main_loop);
128 g_io_channel_unref (io_stdin);
129 gst_object_unref (bus);
130 gst_element_set_state (data.playbin, GST_STATE_NULL);
131 gst_object_unref (data.playbin);
135 /* Extract some metadata from the streams and print it on the screen */
136 static void analyze_streams (CustomData *data) {
142 /* Read some properties */
143 g_object_get (data->playbin, "n-video", &data->n_video, NULL);
144 g_object_get (data->playbin, "n-audio", &data->n_audio, NULL);
145 g_object_get (data->playbin, "n-text", &data->n_text, NULL);
147 g_print ("%d video stream(s), %d audio stream(s), %d text stream(s)\n",
148 data->n_video, data->n_audio, data->n_text);
151 for (i = 0; i < data->n_video; i++) {
153 /* Retrieve the stream's video tags */
154 g_signal_emit_by_name (data->playbin, "get-video-tags", i, &tags);
156 g_print ("video stream %d:\n", i);
157 gst_tag_list_get_string (tags, GST_TAG_VIDEO_CODEC, &str);
158 g_print (" codec: %s\n", str ? str : "unknown");
160 gst_tag_list_free (tags);
165 for (i = 0; i < data->n_audio; i++) {
167 /* Retrieve the stream's audio tags */
168 g_signal_emit_by_name (data->playbin, "get-audio-tags", i, &tags);
170 g_print ("audio stream %d:\n", i);
171 if (gst_tag_list_get_string (tags, GST_TAG_AUDIO_CODEC, &str)) {
172 g_print (" codec: %s\n", str);
175 if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &str)) {
176 g_print (" language: %s\n", str);
179 if (gst_tag_list_get_uint (tags, GST_TAG_BITRATE, &rate)) {
180 g_print (" bitrate: %d\n", rate);
182 gst_tag_list_free (tags);
187 for (i = 0; i < data->n_text; i++) {
189 /* Retrieve the stream's subtitle tags */
190 g_print ("subtitle stream %d:\n", i);
191 g_signal_emit_by_name (data->playbin, "get-text-tags", i, &tags);
193 if (gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &str)) {
194 g_print (" language: %s\n", str);
197 gst_tag_list_free (tags);
199 g_print (" no tags found\n");
203 g_object_get (data->playbin, "current-video", &data->current_video, NULL);
204 g_object_get (data->playbin, "current-audio", &data->current_audio, NULL);
205 g_object_get (data->playbin, "current-text", &data->current_text, NULL);
208 g_print ("Currently playing video stream %d, audio stream %d and subtitle stream %d\n",
209 data->current_video, data->current_audio, data->current_text);
210 g_print ("Type any number and hit ENTER to select a different subtitle stream\n");
213 /* Process messages from GStreamer */
214 static gboolean handle_message (GstBus *bus, GstMessage *msg, CustomData *data) {
218 switch (GST_MESSAGE_TYPE (msg)) {
219 case GST_MESSAGE_ERROR:
220 gst_message_parse_error (msg, &err, &debug_info);
221 g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
222 g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
223 g_clear_error (&err);
225 g_main_loop_quit (data->main_loop);
227 case GST_MESSAGE_EOS:
228 g_print ("End-Of-Stream reached.\n");
229 g_main_loop_quit (data->main_loop);
231 case GST_MESSAGE_STATE_CHANGED: {
232 GstState old_state, new_state, pending_state;
233 gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
234 if (GST_MESSAGE_SRC (msg) == GST_OBJECT (data->playbin)) {
235 if (new_state == GST_STATE_PLAYING) {
236 /* Once we are in the playing state, analyze the streams */
237 analyze_streams (data);
243 /* We want to keep receiving messages */
247 /* Process keyboard input */
248 static gboolean handle_keyboard (GIOChannel *source, GIOCondition cond, CustomData *data) {
251 if (g_io_channel_read_line (source, &str, NULL, NULL, NULL) == G_IO_STATUS_NORMAL) {
252 int index = atoi (str);
253 if (index < 0 || index >= data->n_text) {
254 g_printerr ("Index out of bounds\n");
256 /* If the input was a valid subtitle stream index, set the current subtitle stream */
257 g_print ("Setting current subtitle stream to %d\n", index);
258 g_object_set (data->playbin, "current-text", index, NULL);
267 > ![information] Need help?
269 > If you need help to compile this code, refer to the **Building the
270 > tutorials** section for your platform: [Linux], [Mac OS X] or
271 > [Windows], or use this specific command on Linux:
273 > `` gcc playback-tutorial-2.c -o playback-tutorial-2 `pkg-config --cflags --libs gstreamer-1.0` ``
275 > If you need help to run this code, refer to the **Running the
276 > tutorials** section for your platform: [Linux][1], [Mac OS X][2] or
279 > This tutorial opens a window and displays a movie, with accompanying
280 > audio. The media is fetched from the Internet, so the window might
281 > take a few seconds to appear, depending on your connection
282 > speed. The number of subtitle streams is shown in the terminal, and
283 > the user can switch from one to another by entering a number and
284 > pressing enter. A small delay is to be
285 > expected. _Please read the note at the bottom of this
286 > page._ Bear in mind that
287 > there is no latency management (buffering), so on slow connections,
288 > the movie might stop after a few seconds. See how
289 > [](sdk-basic-tutorial-streaming.md) solves this issue.
291 > Required libraries: `gstreamer-1.0`
295 This tutorial is copied from
296 [](sdk-playback-tutorial-playbin-usage.md) with some changes, so let's
297 review only the changes.
300 /* Set the subtitle URI to play and some font description */
301 g_object_set (data.playbin, "suburi", "http://docs.gstreamer.com/media/sintel_trailer_gr.srt", NULL);
302 g_object_set (data.playbin, "subtitle-font-desc", "Sans, 18", NULL);
305 After setting the media URI, we set the `suburi` property, which points
306 `playbin` to a file containing a subtitle stream. In this case, the
307 media file already contains multiple subtitle streams, so the one
308 provided in the `suburi` is added to the list, and will be the currently
311 Note that metadata concerning a subtitle stream (like its language)
312 resides in the container file, therefore, subtitles not embedded in a
313 container will not have metadata. When running this tutorial you will
314 find that the first subtitle stream does not have a language tag.
316 The `subtitle-font-desc` property allows specifying the font to render
317 the subtitles. Since [Pango](http://www.pango.org/) is the library used
318 to render fonts, you can check its documentation to see how this font
319 should be specified, in particular, the
320 [pango-font-description-from-string](http://developer.gnome.org/pango/stable/pango-Fonts.html#pango-font-description-from-string) function.
322 In a nutshell, the format of the string representation is `[FAMILY-LIST]
323 [STYLE-OPTIONS] [SIZE]` where `FAMILY-LIST` is a comma separated list of
324 families optionally terminated by a comma, `STYLE_OPTIONS` is a
325 whitespace separated list of words where each word describes one of
326 style, variant, weight, or stretch, and `SIZE` is an decimal number
327 (size in points). For example the following are all valid string
331 - serif, monospace bold italic condensed 16
334 The commonly available font families are: Normal, Sans, Serif and
337 The available styles are: Normal (the font is upright), Oblique (the
338 font is slanted, but in a roman style), Italic (the font is slanted in
341 The available weights are: Ultra-Light, Light, Normal, Bold, Ultra-Bold,
344 The available variants are: Normal, Small\_Caps (A font with the lower
345 case characters replaced by smaller variants of the capital characters)
347 The available stretch styles
348 are: Ultra-Condensed, Extra-Condensed, Condensed, Semi-Condensed, Normal, Semi-Expanded, Expanded,
349 Extra-Expanded, Ultra-Expanded
354 /* Set flags to show Audio, Video and Subtitles */
355 g_object_get (data.playbin, "flags", &flags, NULL);
356 flags |= GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_TEXT;
357 g_object_set (data.playbin, "flags", flags, NULL);
360 We set the `flags` property to allow Audio, Video and Text (Subtitles).
362 The rest of the tutorial is the same as [](sdk-playback-tutorial-playbin-usage.md), except
363 that the keyboard input changes the `current-text` property instead of
364 the `current-audio`. As before, keep in mind that stream changes are not
365 immediate, since there is a lot of information flowing through the
366 pipeline that needs to reach the end of it before the new stream shows
371 This tutorial showed how to handle subtitles from `playbin`, whether
372 they are embedded in the container or in a different file:
374 - Subtitles are chosen using the `current-tex`t and `n-tex`t
375 properties of `playbin`.
377 - External subtitle files can be selected using the `suburi` property.
379 - Subtitle appearance can be customized with the
380 `subtitle-font-desc` property.
382 The next playback tutorial shows how to change the playback speed.
384 Remember that attached to this page you should find the complete source
385 code of the tutorial and any accessory files needed to build it.
386 It has been a pleasure having you here, and see you soon\!
388 [information]: images/icons/emoticons/information.png