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