f6d1d5c221564bce1c1b7bdca22e48717776379e
[platform/upstream/gstreamer.git] / tutorial-basic-streaming.md
1 # Basic tutorial 12: Streaming
2
3 ## Goal
4
5 Playing media straight from the Internet without storing it locally is
6 known as Streaming. We have been doing it throughout the tutorials
7 whenever we used a URI starting with `http://`. This tutorial shows a
8 couple of additional points to keep in mind when streaming. In
9 particular:
10
11   - How to enable buffering (to alleviate network problems)
12   - How to recover from interruptions (lost clock)
13
14 ## Introduction
15
16 When streaming, media chunks are decoded and queued for presentation as
17 soon as they arrive form the network. This means that if a chunk is
18 delayed (which is not an uncommon situation at all on the Internet) the
19 presentation queue might run dry and media playback could stall.
20
21 The universal solution is to build a “buffer”, this is, allow a certain
22 number of media chunks to be queued before starting playback. In this
23 way, playback start is delayed a bit, but, if some chunks are late,
24 reproduction is not impacted as there are more chunks in the queue,
25 waiting.
26
27 As it turns out, this solution is already implemented in GStreamer, but
28 the previous tutorials have not been benefiting from it. Some elements,
29 like the `queue2` and `multiqueue` found inside `playbin`, are capable
30 of building this buffer and post bus messages regarding the buffer level
31 (the state of the queue). An application wanting to have more network
32 resilience, then, should listen to these messages and pause playback if
33 the buffer level is not high enough (usually, whenever it is below
34 100%).
35
36 To achieve synchronization among multiple sinks (for example and audio
37 and a video sink) a global clock is used. This clock is selected by
38 GStreamer among all elements which can provide one. Under some
39 circumstances, for example, an RTP source switching streams or changing
40 the output device, this clock can be lost and a new one needs to be
41 selected. This happens mostly when dealing with streaming, so the
42 process is explained in this tutorial.
43
44 When the clock is lost, the application receives a message on the bus;
45 to select a new one, the application just needs to set the pipeline to
46 PAUSED and then to PLAYING again.
47
48 ## A network-resilient example
49
50 Copy this code into a text file named `basic-tutorial-12.c`.
51
52 **basic-tutorial-12.c**
53
54 ``` c
55 #include <gst/gst.h>
56 #include <string.h>
57
58 typedef struct _CustomData {
59   gboolean is_live;
60   GstElement *pipeline;
61   GMainLoop *loop;
62 } CustomData;
63
64 static void cb_message (GstBus *bus, GstMessage *msg, CustomData *data) {
65
66   switch (GST_MESSAGE_TYPE (msg)) {
67     case GST_MESSAGE_ERROR: {
68       GError *err;
69       gchar *debug;
70
71       gst_message_parse_error (msg, &err, &debug);
72       g_print ("Error: %s\n", err->message);
73       g_error_free (err);
74       g_free (debug);
75
76       gst_element_set_state (data->pipeline, GST_STATE_READY);
77       g_main_loop_quit (data->loop);
78       break;
79     }
80     case GST_MESSAGE_EOS:
81       /* end-of-stream */
82       gst_element_set_state (data->pipeline, GST_STATE_READY);
83       g_main_loop_quit (data->loop);
84       break;
85     case GST_MESSAGE_BUFFERING: {
86       gint percent = 0;
87
88       /* If the stream is live, we do not care about buffering. */
89       if (data->is_live) break;
90
91       gst_message_parse_buffering (msg, &percent);
92       g_print ("Buffering (%3d%%)\r", percent);
93       /* Wait until buffering is complete before start/resume playing */
94       if (percent < 100)
95         gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
96       else
97         gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
98       break;
99     }
100     case GST_MESSAGE_CLOCK_LOST:
101       /* Get a new clock */
102       gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
103       gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
104       break;
105     default:
106       /* Unhandled message */
107       break;
108     }
109 }
110
111 int main(int argc, char *argv[]) {
112   GstElement *pipeline;
113   GstBus *bus;
114   GstStateChangeReturn ret;
115   GMainLoop *main_loop;
116   CustomData data;
117
118   /* Initialize GStreamer */
119   gst_init (&argc, &argv);
120
121   /* Initialize our data structure */
122   memset (&data, 0, sizeof (data));
123
124   /* Build the pipeline */
125   pipeline = gst_parse_launch ("playbin uri=https://www.freedesktop.org/software/gstreamer-sdk/data/media/sintel_trailer-480p.webm", NULL);
126   bus = gst_element_get_bus (pipeline);
127
128   /* Start playing */
129   ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
130   if (ret == GST_STATE_CHANGE_FAILURE) {
131     g_printerr ("Unable to set the pipeline to the playing state.\n");
132     gst_object_unref (pipeline);
133     return -1;
134   } else if (ret == GST_STATE_CHANGE_NO_PREROLL) {
135     data.is_live = TRUE;
136   }
137
138   main_loop = g_main_loop_new (NULL, FALSE);
139   data.loop = main_loop;
140   data.pipeline = pipeline;
141
142   gst_bus_add_signal_watch (bus);
143   g_signal_connect (bus, "message", G_CALLBACK (cb_message), &data);
144
145   g_main_loop_run (main_loop);
146
147   /* Free resources */
148   g_main_loop_unref (main_loop);
149   gst_object_unref (bus);
150   gst_element_set_state (pipeline, GST_STATE_NULL);
151   gst_object_unref (pipeline);
152   return 0;
153 }
154 ```
155
156 > ![Information](images/icons/emoticons/information.png)
157 > Need help?
158 >
159 > 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:
160 >
161 > `` gcc basic-tutorial-12.c -o basic-tutorial-12 `pkg-config --cflags --libs gstreamer-1.0` ``
162 >
163 >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).
164 >
165 > This tutorial opens a window and displays a movie, with accompanying audio. The media is fetched from the Internet, so the window might take a few seconds to appear, depending on your connection speed. In the console window, you should see a buffering message, and playback should only start when the buffering reaches 100%. This percentage might not change at all if your connection is fast enough and buffering is not required.
166 >
167 > Required libraries: `gstreamer-1.0`
168
169 ## Walkthrough
170
171 The only special thing this tutorial does is react to certain messages;
172 therefore, the initialization code is very simple and should be
173 self-explanative by now. The only new bit is the detection of live
174 streams:
175
176 ``` c
177 /* Start playing */
178 ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
179 if (ret == GST_STATE_CHANGE_FAILURE) {
180   g_printerr ("Unable to set the pipeline to the playing state.\n");
181   gst_object_unref (pipeline);
182   return -1;
183 } else if (ret == GST_STATE_CHANGE_NO_PREROLL) {
184   data.is_live = TRUE;
185 }
186 ```
187
188 Live streams cannot be paused, so they behave in PAUSED state as if they
189 were in the PLAYING state. Setting live streams to PAUSED succeeds, but
190 returns `GST_STATE_CHANGE_NO_PREROLL`, instead of
191 `GST_STATE_CHANGE_SUCCESS` to indicate that this is a live stream. We
192 are receiving the NO\_PROROLL return code even though we are trying to
193 set the pipeline to PLAYING, because state changes happen progressively
194 (from NULL to READY, to PAUSED and then to PLAYING).
195
196 We care about live streams because we want to disable buffering for
197 them, so we take note of the result of `gst_element_set_state()` in the
198 `is_live` variable.
199
200 Let’s now review the interesting parts of the message parsing callback:
201
202 ``` c
203 case GST_MESSAGE_BUFFERING: {
204   gint percent = 0;
205
206   /* If the stream is live, we do not care about buffering. */
207   if (data->is_live) break;
208
209   gst_message_parse_buffering (msg, &percent);
210   g_print ("Buffering (%3d%%)\r", percent);
211   /* Wait until buffering is complete before start/resume playing */
212   if (percent < 100)
213     gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
214   else
215     gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
216   break;
217 }
218 ```
219
220 First, if this is a live source, ignore buffering messages.
221
222 We parse the buffering message with `gst_message_parse_buffering()` to
223 retrieve the buffering level.
224
225 Then, we print the buffering level on the console and set the pipeline
226 to PAUSED if it is below 100%. Otherwise, we set the pipeline to
227 PLAYING.
228
229 At startup, we will see the buffering level rise up to 100% before
230 playback starts, which is what we wanted to achieve. If, later on, the
231 network becomes slow or unresponsive and our buffer depletes, we will
232 receive new buffering messages with levels below 100% so we will pause
233 the pipeline again until enough buffer has been built up.
234
235 ``` c
236 case GST_MESSAGE_CLOCK_LOST:
237   /* Get a new clock */
238   gst_element_set_state (data->pipeline, GST_STATE_PAUSED);
239   gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
240   break;
241 ```
242
243 For the second network issue, the loss of clock, we simply set the
244 pipeline to PAUSED and back to PLAYING, so a new clock is selected,
245 waiting for new media chunks to be received if necessary.
246
247 ## Conclusion
248
249 This tutorial has described how to add network resilience to your
250 application with two very simple precautions:
251
252   - Taking care of buffering messages sent by the pipeline
253   - Taking care of clock loss
254
255 Handling these messages improves the application’s response to network
256 problems, increasing the overall playback smoothness.
257
258 It has been a pleasure having you here, and see you soon!