1 /* Test example which plays a given file forward, then
2 * at EOS, plays the entire file in reverse
3 * and checks that reverse playback generates the same
4 * output as forward playback but reversed
5 */
6 /* GStreamer
7 * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
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 #include <gst/gst.h>
25 #include <string.h>
26
27 typedef struct _PlayState PlayState;
28
29 typedef struct _StreamTSRange
30 {
31 GstClockTime start;
32 GstClockTime end;
33 } StreamTSRange;
34
35 typedef struct _StreamInfo
36 {
37 PlayState *state;
38 GstPad *pad;
39
40 GstSegment seg;
41
42 GArray *fwd_times;
43 GArray *bkwd_times;
44 } StreamInfo;
45
46 struct _PlayState
47 {
48 GstElement *pipe;
49 GMainLoop *loop;
50 gboolean fwd_play;
51 gint n_sinks;
52
53 GMutex output_lock;
54 };
55
56 static void
warning_cb(GstBus * bus,GstMessage * msg,gpointer foo)57 warning_cb (GstBus * bus, GstMessage * msg, gpointer foo)
58 {
59 GError *err = NULL;
60 gchar *dbg = NULL;
61
62 gst_message_parse_warning (msg, &err, &dbg);
63
64 g_printerr ("WARNING: %s (%s)\n", err->message, (dbg) ? dbg : "no details");
65
66 g_error_free (err);
67 g_free (dbg);
68 }
69
70 static void
error_cb(GstBus * bus,GstMessage * msg,PlayState * state)71 error_cb (GstBus * bus, GstMessage * msg, PlayState * state)
72 {
73 GError *err = NULL;
74 gchar *dbg = NULL;
75
76 gst_message_parse_error (msg, &err, &dbg);
77
78 g_printerr ("ERROR: %s (%s)\n", err->message, (dbg) ? dbg : "no details");
79
80 g_main_loop_quit (state->loop);
81
82 g_error_free (err);
83 g_free (dbg);
84 }
85
86 static void
eos_cb(GstBus * bus,GstMessage * msg,PlayState * state)87 eos_cb (GstBus * bus, GstMessage * msg, PlayState * state)
88 {
89 if (state->fwd_play) {
90 g_print ("EOS - finished forward play. Starting reverse\n");
91 state->fwd_play = FALSE;
92 gst_element_seek (state->pipe, -1.0, GST_FORMAT_TIME,
93 GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH,
94 GST_SEEK_TYPE_SET, 0, GST_SEEK_TYPE_END, 0);
95
96 return;
97 }
98 g_print ("EOS - exiting\n");
99 g_main_loop_quit (state->loop);
100 }
101
102 static void
state_cb(GstBus * bus,GstMessage * msg,PlayState * state)103 state_cb (GstBus * bus, GstMessage * msg, PlayState * state)
104 {
105 if (msg->src == GST_OBJECT (state->pipe)) {
106 GstState old_state, new_state, pending_state;
107
108 gst_message_parse_state_changed (msg, &old_state, &new_state,
109 &pending_state);
110 if (new_state == GST_STATE_PLAYING)
111 g_print ("Decoding ...\n");
112 }
113 }
114
115 static void
_destroy_stream_info(StreamInfo * si)116 _destroy_stream_info (StreamInfo * si)
117 {
118 g_array_free (si->fwd_times, TRUE);
119 g_array_free (si->bkwd_times, TRUE);
120 g_object_unref (si->pad);
121 g_free (si);
122 }
123
124 static void
extend_times(StreamInfo * si,GstClockTime start,GstClockTime end)125 extend_times (StreamInfo * si, GstClockTime start, GstClockTime end)
126 {
127 PlayState *state = si->state;
128 StreamTSRange *ts = NULL;
129 StreamTSRange tsn;
130 GArray *a;
131 guint i, n;
132
133 /* Set up new entry, in case we need it */
134 tsn.start = start;
135 tsn.end = end;
136
137 if (state->fwd_play) {
138 a = si->fwd_times;
139 n = a->len;
140 /* if playing forward, see if this new time extends the last entry */
141 i = n - 1;
142 } else {
143 a = si->bkwd_times;
144 n = a->len;
145 /* if playing backward, see if this new time extends the earliest entry */
146 i = 0;
147 }
148
149 if (n > 0) {
150 ts = &g_array_index (a, StreamTSRange, i);
151 if (start > ts->start) {
152 /* This entry is after the most recent entry */
153 /* Tolerance of 1 millisecond allowed for imprecision */
154 if (ts->end + GST_MSECOND >= start) {
155 GST_LOG ("%p extending entry %d to %" GST_TIME_FORMAT,
156 si, i, GST_TIME_ARGS (end));
157 ts->end = end;
158 return;
159 }
160
161 /* new start > ts->end, so this new entry goes after the first one */
162 GST_LOG ("%p inserting new entry %d %" GST_TIME_FORMAT
163 " to %" GST_TIME_FORMAT, si, i + 1, GST_TIME_ARGS (start),
164 GST_TIME_ARGS (end));
165 g_array_insert_val (a, i + 1, tsn);
166 return;
167 } else if (end + GST_MSECOND > ts->start) {
168 /* This entry precedes the current one, but overlaps it */
169 GST_LOG ("%p pre-extending entry %d to %" GST_TIME_FORMAT,
170 si, i, GST_TIME_ARGS (start));
171 ts->start = start;
172 return;
173 }
174 } else {
175 i = 0;
176 }
177
178 /* otherwise insert a new entry before/at the start */
179 GST_LOG ("%p New entry %d - %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
180 si, i, GST_TIME_ARGS (start), GST_TIME_ARGS (end));
181 g_array_insert_val (a, i, tsn);
182 }
183
184 static void
dump_times(StreamInfo * si)185 dump_times (StreamInfo * si)
186 {
187 PlayState *state = si->state;
188 guint i;
189 GArray *a;
190
191 g_mutex_lock (&state->output_lock);
192 if (state->fwd_play)
193 a = si->fwd_times;
194 else
195 a = si->bkwd_times;
196
197 g_print ("Pad %s times:\n", GST_PAD_NAME (si->pad));
198 for (i = 0; i < a->len; i++) {
199 StreamTSRange *ts = &g_array_index (a, StreamTSRange, i);
200
201 g_print (" %u %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT "\n",
202 i, GST_TIME_ARGS (ts->start), GST_TIME_ARGS (ts->end));
203 }
204 g_mutex_unlock (&state->output_lock);
205 }
206
207 static GstPadProbeReturn
handle_output(GstPad * pad,GstPadProbeInfo * info,StreamInfo * si)208 handle_output (GstPad * pad, GstPadProbeInfo * info, StreamInfo * si)
209 {
210 GstClockTime start, end;
211 GstBuffer *buf;
212
213 GST_LOG_OBJECT (pad, "Fired probe type 0x%x", info->type);
214
215 if (info->type & GST_PAD_PROBE_TYPE_BUFFER_LIST) {
216 g_warning ("Buffer list handling not implemented");
217 return GST_PAD_PROBE_DROP;
218 }
219
220 if (info->type & GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM) {
221 GstEvent *event = gst_pad_probe_info_get_event (info);
222 switch (GST_EVENT_TYPE (event)) {
223 case GST_EVENT_SEGMENT:
224 gst_event_copy_segment (event, &si->seg);
225 break;
226 case GST_EVENT_EOS:
227 dump_times (si);
228 break;
229 default:
230 break;
231 }
232 return GST_PAD_PROBE_PASS;
233 }
234
235 buf = gst_pad_probe_info_get_buffer (info);
236 if (!GST_BUFFER_PTS_IS_VALID (buf))
237 goto done;
238 end = start = GST_BUFFER_PTS (buf);
239
240 if (GST_BUFFER_DURATION_IS_VALID (buf))
241 end += GST_BUFFER_DURATION (buf);
242
243 gst_segment_clip (&si->seg, GST_FORMAT_TIME, start, end, &start, &end);
244 start = gst_segment_to_stream_time (&si->seg, GST_FORMAT_TIME, start);
245 end = gst_segment_to_stream_time (&si->seg, GST_FORMAT_TIME, end);
246
247 GST_DEBUG_OBJECT (pad, "new buffer %" GST_TIME_FORMAT
248 " to %" GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (end));
249
250 /* Now extend measured time range to include new times */
251 extend_times (si, start, end);
252
253 done:
254 return GST_PAD_PROBE_PASS;
255 }
256
257 static void
pad_added_cb(GstElement * decodebin,GstPad * pad,PlayState * state)258 pad_added_cb (GstElement * decodebin, GstPad * pad, PlayState * state)
259 {
260 GstPadLinkReturn ret;
261 GstElement *fakesink;
262 GstPad *fakesink_pad;
263 StreamInfo *si;
264
265 fakesink = gst_element_factory_make ("fakesink", NULL);
266 #if 0
267 if (state->n_sinks == 1)
268 g_object_set (fakesink, "silent", FALSE, NULL);
269 #endif
270
271 si = g_new0 (StreamInfo, 1);
272 si->pad = g_object_ref (pad);
273 si->state = state;
274 si->fwd_times = g_array_new (FALSE, TRUE, sizeof (StreamTSRange));
275 si->bkwd_times = g_array_new (FALSE, TRUE, sizeof (StreamTSRange));
276
277 gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_DATA_DOWNSTREAM,
278 (GstPadProbeCallback) handle_output, si, (GDestroyNotify)
279 _destroy_stream_info);
280
281 state->n_sinks++;
282 gst_bin_add (GST_BIN (state->pipe), fakesink);
283
284 gst_element_sync_state_with_parent (fakesink);
285
286 fakesink_pad = gst_element_get_static_pad (fakesink, "sink");
287
288 ret = gst_pad_link (pad, fakesink_pad);
289 if (!GST_PAD_LINK_SUCCESSFUL (ret)) {
290 g_printerr ("Failed to link %s:%s to %s:%s (ret = %d)\n",
291 GST_DEBUG_PAD_NAME (pad), GST_DEBUG_PAD_NAME (fakesink_pad), ret);
292 } else {
293 GstCaps *caps = gst_pad_get_current_caps (pad);
294 gchar *s = gst_caps_to_string (caps);
295
296 g_print ("Linked %s:%s to %s:%s caps %s\n", GST_DEBUG_PAD_NAME (pad),
297 GST_DEBUG_PAD_NAME (fakesink_pad), s);
298 gst_caps_unref (caps);
299 g_free (s);
300 }
301
302 gst_object_unref (fakesink_pad);
303 }
304
305 gint
main(gint argc,gchar * argv[])306 main (gint argc, gchar * argv[])
307 {
308 PlayState state;
309 GstElement *decoder;
310 GstStateChangeReturn res;
311 GstBus *bus;
312
313 gst_init (&argc, &argv);
314
315 if (argc != 2) {
316 g_printerr ("Decode file from start to end.\n");
317 g_printerr ("Usage: %s URI\n\n", argv[0]);
318 return 1;
319 }
320 /* Start with zeroed-state */
321 memset (&state, 0, sizeof (PlayState));
322
323 state.loop = g_main_loop_new (NULL, TRUE);
324 state.pipe = gst_pipeline_new ("pipeline");
325 state.fwd_play = TRUE;
326 g_mutex_init (&state.output_lock);
327
328 bus = gst_pipeline_get_bus (GST_PIPELINE (state.pipe));
329 gst_bus_add_signal_watch (bus);
330
331 g_signal_connect (bus, "message::eos", G_CALLBACK (eos_cb), &state);
332 g_signal_connect (bus, "message::error", G_CALLBACK (error_cb), &state);
333 g_signal_connect (bus, "message::warning", G_CALLBACK (warning_cb), NULL);
334 g_signal_connect (bus, "message::state-changed", G_CALLBACK (state_cb),
335 &state);
336
337 #if 0
338 g_signal_connect (state.pipe, "deep-notify",
339 G_CALLBACK (gst_object_default_deep_notify), NULL);
340 #endif
341
342 decoder = gst_element_factory_make ("uridecodebin", "decoder");
343 g_assert (decoder);
344 gst_bin_add (GST_BIN (state.pipe), decoder);
345
346 if (argv[1] && strstr (argv[1], "://") != NULL) {
347 g_object_set (G_OBJECT (decoder), "uri", argv[1], NULL);
348 } else if (argv[1]) {
349 gchar *uri = g_strdup_printf ("file://%s", argv[1]);
350 g_object_set (G_OBJECT (decoder), "uri", uri, NULL);
351 g_free (uri);
352 } else {
353 g_print ("Usage: %s <filename|uri>\n", argv[0]);
354 return -1;
355 }
356
357 g_signal_connect (decoder, "pad-added", G_CALLBACK (pad_added_cb), &state);
358
359 res = gst_element_set_state (state.pipe, GST_STATE_PLAYING);
360 if (res == GST_STATE_CHANGE_FAILURE) {
361 g_print ("could not play\n");
362 return -1;
363 }
364
365 g_main_loop_run (state.loop);
366
367 /* tidy up */
368 gst_element_set_state (state.pipe, GST_STATE_NULL);
369 gst_object_unref (state.pipe);
370 gst_object_unref (bus);
371
372 return 0;
373 }
374