• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
34 /* There are several supported scenarios
35 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
36 1) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> run for 10s, then pause -> wait 10s -> play
37 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
38 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
39 4) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> Apply 'instant-rate-change' to 0.25x (toggle every 500ms)
40 */
41 
42 #define PLAY_PAUSE_DELAY 10
43 #define IDLE_CYCLE_DELAY 2
44 
45 #define TARGET_RATE_1 0.25
46 #define TARGET_RATE_2 2.0
47 
48 /* Set DISABLE_AUDIO to run with video only */
49 // #define DISABLE_AUDIO
50 
51 /* Set to force the use of the system clock on the pipeline */
52 // #define FORCE_SYSTEM_CLOCK
53 
54 typedef struct _MyDataStruct
55 {
56   GMainLoop *mainloop;
57   GstElement *pipeline;
58   GstBus *bus;
59 
60   gdouble rate;
61   gboolean paused;
62 
63   guint scenario;
64   GstClockTime start;
65   GstClock *clock;
66 
67   guint timeout_id;
68   guint idle_id;
69 } MyDataStruct;
70 
71 static gboolean toggle_rate (MyDataStruct * data);
72 
73 static gboolean
do_enable_disable_idle(MyDataStruct * data)74 do_enable_disable_idle (MyDataStruct * data)
75 {
76   if (data->idle_id) {
77     g_print ("Disabling idle handler\n");
78     g_source_remove (data->idle_id);
79     data->idle_id = 0;
80   } else {
81     g_print ("Enabling idle handler\n");
82     data->idle_id = g_idle_add ((GSourceFunc) toggle_rate, data);
83   }
84 
85   return TRUE;
86 }
87 
88 static gboolean
do_play_pause(MyDataStruct * data)89 do_play_pause (MyDataStruct * data)
90 {
91   data->paused = !data->paused;
92 
93   switch (data->scenario) {
94     case 1:
95       g_print ("%s\n", data->paused ? "Pausing" : "Unpausing");
96       gst_element_set_state (data->pipeline,
97           data->paused ? GST_STATE_PAUSED : GST_STATE_PLAYING);
98       data->timeout_id = g_timeout_add_seconds (PLAY_PAUSE_DELAY,
99           (GSourceFunc) do_play_pause, data);
100       break;
101     case 2:
102       if (!data->paused) {
103         gint64 pos = GST_CLOCK_TIME_NONE;
104         gst_element_query_position (data->pipeline, GST_FORMAT_TIME, &pos);
105 
106         /* Change rate between 2x and 1x before unpausing */
107         data->rate = (data->rate == 2.0) ? 1.0 : 2.0;
108         g_print ("Switching rate to %f (position %" GST_TIME_FORMAT ")\n",
109             data->rate, GST_TIME_ARGS (pos));
110         gst_element_send_event (data->pipeline, gst_event_new_seek (data->rate,
111                 GST_FORMAT_TIME, GST_SEEK_FLAG_INSTANT_RATE_CHANGE,
112                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE, GST_SEEK_TYPE_NONE,
113                 GST_CLOCK_TIME_NONE));
114       }
115 
116       g_print ("%s\n", data->paused ? "Pausing" : "Unpausing");
117       gst_element_set_state (data->pipeline,
118           data->paused ? GST_STATE_PAUSED : GST_STATE_PLAYING);
119       data->timeout_id = g_timeout_add_seconds (PLAY_PAUSE_DELAY,
120           (GSourceFunc) do_play_pause, data);
121       break;
122     case 3:
123       g_print ("%s\n", data->paused ? "Pausing" : "Unpausing");
124       gst_element_set_state (data->pipeline,
125           data->paused ? GST_STATE_PAUSED : GST_STATE_PLAYING);
126       if (data->paused) {
127         /* On pause, seek to 30 seconds */
128         g_print ("Seeking to 30s\n");
129         gst_element_send_event (data->pipeline,
130             gst_event_new_seek (data->rate, GST_FORMAT_TIME,
131                 GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
132                 GST_SEEK_TYPE_SET, 30 * GST_SECOND,
133                 GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE));
134       }
135       data->timeout_id = g_timeout_add_seconds (PLAY_PAUSE_DELAY,
136           (GSourceFunc) do_play_pause, data);
137       break;
138     default:
139       break;
140   }
141   return FALSE;
142 }
143 
144 static gboolean
toggle_rate(MyDataStruct * data)145 toggle_rate (MyDataStruct * data)
146 {
147   gint64 pos = GST_CLOCK_TIME_NONE;
148   gst_element_query_position (data->pipeline, GST_FORMAT_TIME, &pos);
149 
150   /* Toggle rate between the 2 target rates */
151   if (data->rate != TARGET_RATE_2)
152     data->rate = TARGET_RATE_2;
153   else
154     data->rate = TARGET_RATE_1;
155   g_print ("Switching rate to %f (position %" GST_TIME_FORMAT ")\n", data->rate,
156       GST_TIME_ARGS (pos));
157   gst_element_send_event (data->pipeline, gst_event_new_seek (data->rate,
158           GST_FORMAT_TIME, GST_SEEK_FLAG_INSTANT_RATE_CHANGE,
159           GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE, GST_SEEK_TYPE_NONE,
160           GST_CLOCK_TIME_NONE));
161   return TRUE;
162 }
163 
164 static void
on_preroll(MyDataStruct * data)165 on_preroll (MyDataStruct * data)
166 {
167   if (data->timeout_id != 0)
168     return;                     /* Already scheduled out scenario timer */
169 
170   switch (data->scenario) {
171     case 0:
172       data->idle_id = g_idle_add ((GSourceFunc) toggle_rate, data);
173       data->timeout_id = g_timeout_add_seconds (IDLE_CYCLE_DELAY,
174           (GSourceFunc) do_enable_disable_idle, data);
175       break;
176     case 1:
177     case 2:
178     case 3:{
179       gint64 pos = GST_CLOCK_TIME_NONE;
180       gst_element_query_position (data->pipeline, GST_FORMAT_TIME, &pos);
181 
182       /* Change rate to 2x and play for 10 sec, pause for 10 sec */
183       data->rate = 2.0;
184       g_print ("Switching rate to %f (position %" GST_TIME_FORMAT ")\n",
185           data->rate, GST_TIME_ARGS (pos));
186       gst_element_send_event (data->pipeline, gst_event_new_seek (data->rate,
187               GST_FORMAT_TIME, GST_SEEK_FLAG_INSTANT_RATE_CHANGE,
188               GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE, GST_SEEK_TYPE_NONE,
189               GST_CLOCK_TIME_NONE));
190       /* Instant rate change completed, schedule play/pause */
191       data->timeout_id = g_timeout_add_seconds (PLAY_PAUSE_DELAY,
192           (GSourceFunc) do_play_pause, data);
193       break;
194     }
195     case 4:
196       g_timeout_add (250, (GSourceFunc) toggle_rate, data);
197       break;
198     default:
199       break;
200   }
201 }
202 
203 static gboolean
_on_bus_message(GstBus * bus,GstMessage * message,gpointer userdata)204 _on_bus_message (GstBus * bus, GstMessage * message, gpointer userdata)
205 {
206   MyDataStruct *data = (MyDataStruct *) (userdata);
207 
208   switch (GST_MESSAGE_TYPE (message)) {
209     case GST_MESSAGE_ERROR:{
210       GError *err = NULL;
211       gchar *name = gst_object_get_path_string (GST_MESSAGE_SRC (message));
212       gst_message_parse_error (message, &err, NULL);
213 
214       g_printerr ("ERROR: from element %s: %s\n", name, err->message);
215       g_error_free (err);
216       g_free (name);
217 
218       g_printf ("Stopping\n");
219       g_main_loop_quit (data->mainloop);
220       break;
221     }
222     case GST_MESSAGE_EOS:
223       g_printf ("EOS ! Stopping \n");
224       g_main_loop_quit (data->mainloop);
225       break;
226     case GST_MESSAGE_ASYNC_DONE:
227       on_preroll (data);
228       break;
229     default:
230       break;
231   }
232 
233   return TRUE;
234 }
235 
236 static gchar *
cmdline_to_uri(const gchar * arg)237 cmdline_to_uri (const gchar * arg)
238 {
239   if (gst_uri_is_valid (arg))
240     return g_strdup (arg);
241 
242   return gst_filename_to_uri (arg, NULL);
243 }
244 
245 static void
print_usage(const char * arg0)246 print_usage (const char *arg0)
247 {
248   g_print
249       ("Usage: %s <0-4> URI\nSelect test scenario 0 to 4, and supply a URI to test\n",
250       arg0);
251   g_print ("Scenarios:\n"
252       " 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"
253       " 1) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> run for 10s, then pause -> wait 10s -> play\n"
254       " 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"
255       " 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"
256       " 4) Play rate to 1x -> Apply 'instant-rate-change' to 2x -> Apply 'instant-rate-change' to 0.25x (toggle every 500ms)\n");
257 }
258 
259 int
main(int argc,gchar ** argv)260 main (int argc, gchar ** argv)
261 {
262   GstBus *bus;
263   MyDataStruct *data;
264   gchar *uri;
265 #ifdef FORCE_SYSTEM_CLOCK
266   GstClock *clock;
267 #endif
268 
269   gst_init (&argc, &argv);
270 
271   data = g_new0 (MyDataStruct, 1);
272 
273   if (argc < 3) {
274     print_usage (argv[0]);
275     return 1;
276   }
277 
278   data->scenario = atoi (argv[1]);
279   uri = cmdline_to_uri (argv[2]);
280   if (data->scenario > 4 || uri == NULL) {
281     print_usage (argv[0]);
282     return 1;
283   }
284 
285   data->pipeline = gst_element_factory_make ("playbin", NULL);
286   if (data->pipeline == NULL) {
287     g_printerr ("Failed to create playbin element. Aborting");
288     return 1;
289   }
290 #ifdef FORCE_SYSTEM_CLOCK
291   clock = gst_system_clock_obtain ();
292   gst_pipeline_use_clock (GST_PIPELINE (data->pipeline), clock);
293 #endif
294 
295 #ifdef DISABLE_AUDIO
296   g_object_set (data->pipeline, "flags", 0x00000615, NULL);
297 #endif
298   g_object_set (data->pipeline, "uri", uri, NULL);
299   g_free (uri);
300 
301   /* Put a bus handler */
302   bus = gst_pipeline_get_bus (GST_PIPELINE (data->pipeline));
303   gst_bus_add_watch (bus, _on_bus_message, data);
304 
305   /* Start pipeline */
306   data->mainloop = g_main_loop_new (NULL, TRUE);
307   gst_element_set_state (data->pipeline, GST_STATE_PLAYING);
308   g_main_loop_run (data->mainloop);
309 
310   gst_element_set_state (data->pipeline, GST_STATE_NULL);
311 
312   gst_object_unref (data->pipeline);
313   gst_object_unref (bus);
314 
315   return 0;
316 }
317