Release 1.22.5
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / tests / examples / seek / instant-rate-change.c
1 /* Test example for instant rate changes.
2  * The example takes an input URI and runs a set of actions,
3  * seeking and pausing etc.
4  *
5  * Copyright (C) 2015-2018 Centricular Ltd
6  *  @author:  Edward Hervey <edward@centricular.com>
7  *  @author:  Jan Schmidt <jan@centricular.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public
20  * License along with this library; if not, write to the
21  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24
25 #ifdef HAVE_CONFIG_H
26 #include "config.h"
27 #endif
28
29 #include <glib.h>
30 #include <glib-object.h>
31 #include <glib/gprintf.h>
32 #include <gst/gst.h>
33 #include <stdlib.h>
34
35 /* There are several supported scenarios
36 0) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> Apply 'instant-rate-change' to 0.25x (repeat as fast as possible for 2 sec) -> let play for 2s
37 1) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> run for 10s, then pause -> wait 10s -> play
38 2) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> run for 10s, then pause -> wait 10s -> Apply 'instant-rate-change' to 1x -> play
39 3) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> run for 10s, then pause -> seeking (flush+key-unit) to 30s -> wait 10s -> play
40 4) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> Apply 'instant-rate-change' to 0.25x (toggle every 500ms)
41 */
42
43 #define PLAY_PAUSE_DELAY 10
44 #define IDLE_CYCLE_DELAY 2
45
46 #define TARGET_RATE_1 0.25
47 #define TARGET_RATE_2 2.0
48
49 /* Set DISABLE_AUDIO to run with video only */
50 // #define DISABLE_AUDIO
51
52 /* Set to force the use of the system clock on the pipeline */
53 // #define FORCE_SYSTEM_CLOCK
54
55 typedef struct _MyDataStruct
56 {
57   GMainLoop *mainloop;
58   GstElement *pipeline;
59   GstBus *bus;
60
61   gdouble rate;
62   gboolean paused;
63
64   guint scenario;
65   GstClockTime start;
66   GstClock *clock;
67
68   guint timeout_id;
69   guint idle_id;
70 } MyDataStruct;
71
72 static gboolean toggle_rate (MyDataStruct * data);
73
74 static gboolean
75 do_enable_disable_idle (MyDataStruct * data)
76 {
77   if (data->idle_id) {
78     g_print ("Disabling idle handler\n");
79     g_source_remove (data->idle_id);
80     data->idle_id = 0;
81   } else {
82     g_print ("Enabling idle handler\n");
83     data->idle_id = g_idle_add ((GSourceFunc) toggle_rate, data);
84   }
85
86   return TRUE;
87 }
88
89 static gboolean
90 do_play_pause (MyDataStruct * data)
91 {
92   data->paused = !data->paused;
93
94   switch (data->scenario) {
95     case 1:
96       g_print ("%s\n", data->paused ? "Pausing" : "Unpausing");
97       gst_element_set_state (data->pipeline,
98           data->paused ? GST_STATE_PAUSED : GST_STATE_PLAYING);
99       data->timeout_id = g_timeout_add_seconds (PLAY_PAUSE_DELAY,
100           (GSourceFunc) do_play_pause, data);
101       break;
102     case 2:
103       if (!data->paused) {
104         gint64 pos = GST_CLOCK_TIME_NONE;
105         gst_element_query_position (data->pipeline, GST_FORMAT_TIME, &pos);
106
107         /* Change rate between 2x and 1x before unpausing */
108         data->rate = (data->rate == 2.0) ? 1.0 : 2.0;
109         g_print ("Switching rate to %f (position %" GST_TIME_FORMAT ")\n",
110             data->rate, GST_TIME_ARGS (pos));
111         gst_element_send_event (data->pipeline, gst_event_new_seek (data->rate,
112                 GST_FORMAT_TIME, GST_SEEK_FLAG_INSTANT_RATE_CHANGE,
113                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE, GST_SEEK_TYPE_NONE,
114                 GST_CLOCK_TIME_NONE));
115       }
116
117       g_print ("%s\n", data->paused ? "Pausing" : "Unpausing");
118       gst_element_set_state (data->pipeline,
119           data->paused ? GST_STATE_PAUSED : GST_STATE_PLAYING);
120       data->timeout_id = g_timeout_add_seconds (PLAY_PAUSE_DELAY,
121           (GSourceFunc) do_play_pause, data);
122       break;
123     case 3:
124       g_print ("%s\n", data->paused ? "Pausing" : "Unpausing");
125       gst_element_set_state (data->pipeline,
126           data->paused ? GST_STATE_PAUSED : GST_STATE_PLAYING);
127       if (data->paused) {
128         /* On pause, seek to 30 seconds */
129         g_print ("Seeking to 30s\n");
130         gst_element_send_event (data->pipeline,
131             gst_event_new_seek (data->rate, GST_FORMAT_TIME,
132                 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
133                 GST_SEEK_TYPE_SET, 30 * GST_SECOND,
134                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE));
135       }
136       data->timeout_id = g_timeout_add_seconds (PLAY_PAUSE_DELAY,
137           (GSourceFunc) do_play_pause, data);
138       break;
139     default:
140       break;
141   }
142   return FALSE;
143 }
144
145 static gboolean
146 toggle_rate (MyDataStruct * data)
147 {
148   gint64 pos = GST_CLOCK_TIME_NONE;
149   gst_element_query_position (data->pipeline, GST_FORMAT_TIME, &pos);
150
151   /* Toggle rate between the 2 target rates */
152   if (data->rate != TARGET_RATE_2)
153     data->rate = TARGET_RATE_2;
154   else
155     data->rate = TARGET_RATE_1;
156   g_print ("Switching rate to %f (position %" GST_TIME_FORMAT ")\n", data->rate,
157       GST_TIME_ARGS (pos));
158   gst_element_send_event (data->pipeline, gst_event_new_seek (data->rate,
159           GST_FORMAT_TIME, GST_SEEK_FLAG_INSTANT_RATE_CHANGE,
160           GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE, GST_SEEK_TYPE_NONE,
161           GST_CLOCK_TIME_NONE));
162   return TRUE;
163 }
164
165 static void
166 on_preroll (MyDataStruct * data)
167 {
168   if (data->timeout_id != 0)
169     return;                     /* Already scheduled out scenario timer */
170
171   switch (data->scenario) {
172     case 0:
173       data->idle_id = g_idle_add ((GSourceFunc) toggle_rate, data);
174       data->timeout_id = g_timeout_add_seconds (IDLE_CYCLE_DELAY,
175           (GSourceFunc) do_enable_disable_idle, data);
176       break;
177     case 1:
178     case 2:
179     case 3:{
180       gint64 pos = GST_CLOCK_TIME_NONE;
181       gst_element_query_position (data->pipeline, GST_FORMAT_TIME, &pos);
182
183       /* Change rate to 2x and play for 10 sec, pause for 10 sec */
184       data->rate = 2.0;
185       g_print ("Switching rate to %f (position %" GST_TIME_FORMAT ")\n",
186           data->rate, GST_TIME_ARGS (pos));
187       gst_element_send_event (data->pipeline, gst_event_new_seek (data->rate,
188               GST_FORMAT_TIME, GST_SEEK_FLAG_INSTANT_RATE_CHANGE,
189               GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE, GST_SEEK_TYPE_NONE,
190               GST_CLOCK_TIME_NONE));
191       /* Instant rate change completed, schedule play/pause */
192       data->timeout_id = g_timeout_add_seconds (PLAY_PAUSE_DELAY,
193           (GSourceFunc) do_play_pause, data);
194       break;
195     }
196     case 4:
197       g_timeout_add (250, (GSourceFunc) toggle_rate, data);
198       break;
199     default:
200       break;
201   }
202 }
203
204 static gboolean
205 _on_bus_message (GstBus * bus, GstMessage * message, gpointer userdata)
206 {
207   MyDataStruct *data = (MyDataStruct *) (userdata);
208
209   switch (GST_MESSAGE_TYPE (message)) {
210     case GST_MESSAGE_ERROR:{
211       GError *err = NULL;
212       gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (message));
213       gst_message_parse_error (message, &err, NULL);
214
215       g_printerr ("ERROR: from element %s: %s\n", name, err->message);
216       g_error_free (err);
217       g_free (name);
218
219       g_printf ("Stopping\n");
220       g_main_loop_quit (data->mainloop);
221       break;
222     }
223     case GST_MESSAGE_EOS:
224       g_printf ("EOS ! Stopping \n");
225       g_main_loop_quit (data->mainloop);
226       break;
227     case GST_MESSAGE_ASYNC_DONE:
228       on_preroll (data);
229       break;
230     default:
231       break;
232   }
233
234   return TRUE;
235 }
236
237 static gchar *
238 cmdline_to_uri (const gchar * arg)
239 {
240   if (gst_uri_is_valid (arg))
241     return g_strdup (arg);
242
243   return gst_filename_to_uri (arg, NULL);
244 }
245
246 static void
247 print_usage (const char *arg0)
248 {
249   g_print
250       ("Usage: %s <0-4> URI\nSelect test scenario 0 to 4, and supply a URI to test\n",
251       arg0);
252   g_print ("Scenarios:\n"
253       " 0) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> Apply 'instant-rate-change' to 0.25x (repeat as fast as possible for 2 sec) -> let play for 2s\n"
254       " 1) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> run for 10s, then pause -> wait 10s -> play\n"
255       " 2) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> run for 10s, then pause -> wait 10s -> Apply 'instant-rate-change' to 1x -> play\n"
256       " 3) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> run for 10s, then pause -> seeking (flush+key-unit) to 30s -> wait 10s -> play\n"
257       " 4) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> Apply 'instant-rate-change' to 0.25x (toggle every 500ms)\n");
258 }
259
260 int
261 main (int argc, gchar ** argv)
262 {
263   GstBus *bus;
264   MyDataStruct *data;
265   gchar *uri;
266 #ifdef FORCE_SYSTEM_CLOCK
267   GstClock *clock;
268 #endif
269
270   gst_init (&argc, &argv);
271
272   data = g_new0 (MyDataStruct, 1);
273
274   if (argc < 3) {
275     print_usage (argv[0]);
276     return 1;
277   }
278
279   data->scenario = atoi (argv[1]);
280   uri = cmdline_to_uri (argv[2]);
281   if (data->scenario > 4 || uri == NULL) {
282     print_usage (argv[0]);
283     return 1;
284   }
285
286   data->pipeline = gst_element_factory_make ("playbin", NULL);
287   if (data->pipeline == NULL) {
288     g_printerr ("Failed to create playbin element. Aborting");
289     return 1;
290   }
291 #ifdef FORCE_SYSTEM_CLOCK
292   clock = gst_system_clock_obtain ();
293   gst_pipeline_use_clock (GST_PIPELINE (data->pipeline), clock);
294 #endif
295
296 #ifdef DISABLE_AUDIO
297   g_object_set (data->pipeline, "flags", 0x00000615, NULL);
298 #endif
299   g_object_set (data->pipeline, "uri", uri, NULL);
300   g_free (uri);
301
302   /* Put a bus handler */
303   bus = gst_pipeline_get_bus (GST_PIPELINE (data->pipeline));
304   gst_bus_add_watch (bus, _on_bus_message, data);
305
306   /* Start pipeline */
307   data->mainloop = g_main_loop_new (NULL, TRUE);
308   gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
309   g_main_loop_run (data->mainloop);
310
311   gst_element_set_state (data->pipeline, GST_STATE_NULL);
312
313   gst_object_unref (data->pipeline);
314   gst_object_unref (bus);
315
316   return 0;
317 }