1 /*  Copyright (C) 2015 Centricular Ltd
2  *
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions
5  * are met:
6  * 1. Redistributions of source code must retain the above copyright
7  *    notice, this list of conditions and the following disclaimer.
8  * 2. Redistributions in binary form must reproduce the above copyright
9  *    notice, this list of conditions and the following disclaimer in the
10  *    documentation and/or other materials provided with the distribution.
11  *
12  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
13  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
14  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
15  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
16  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
17  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
18  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
19  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
20  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
21  * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
22  * POSSIBILITY OF SUCH DAMAGE.
23  */
24 
25 #include <gst/gst.h>
26 #include <gst/video/gstvideosink.h>
27 
28 #define STR_HELPER(x) #x
29 #define STR(x) STR_HELPER(x)
30 
31 /* Change this to set the output resolution */
32 #define OUTPUT_VIDEO_WIDTH  1280
33 #define OUTPUT_VIDEO_HEIGHT 720
34 
35 /* Video and audio caps outputted by the mixers */
36 #define RAW_AUDIO_CAPS_STR "audio/x-raw, format=(string)S16LE, " \
37 "layout=(string)interleaved, rate=(int)44100, channels=(int)2, " \
38 "channel-mask=(bitmask)0x03"
39 
40 #define RAW_VIDEO_CAPS_STR "video/x-raw, width=(int)" STR(OUTPUT_VIDEO_WIDTH) \
41 ", height=(int)" STR(OUTPUT_VIDEO_HEIGHT) ", framerate=(fraction)25/1, " \
42 "format=I420, pixel-aspect-ratio=(fraction)1/1, " \
43 "interlace-mode=(string)progressive"
44 
45 GST_DEBUG_CATEGORY_STATIC (playout);
46 #define GST_CAT_DEFAULT playout
47 
48 typedef enum
49 {
50   PLAYOUT_APP_STATE_READY,      /* Newly created */
51   PLAYOUT_APP_STATE_PLAYING,    /* Playing an item */
52   PLAYOUT_APP_STATE_EOS         /* Finished playing, all items EOS */
53 } PlayoutAppState;
54 
55 typedef struct
56 {
57   /* Application state */
58   PlayoutAppState state;
59 
60   /* An array of PlayoutItems that will be played in sequence */
61   GPtrArray *play_queue;
62   /* Index of the currently-playing item */
63   gint play_queue_current;
64   /* Lock access to the play queue */
65   GMutex play_queue_lock;
66 
67   GMainLoop *main_loop;
68 
69   /* Pipeline */
70   GstElement *pipeline;
71 
72   /* Output */
73   GstElement *video_mixer;
74   GstElement *video_sink;
75   GstVideoRectangle video_orect;        /* w/h/x/y of the output */
76 
77   GstElement *audio_mixer;
78   GstElement *audio_sink;
79 
80   /* The duration of all items that have been played in ns.
81    * Only updates when a new item is activated. */
82   guint64 elapsed_duration;
83 } PlayoutApp;
84 
85 typedef enum
86 {
87   PLAYOUT_ITEM_STATE_NEW,       /* Newly created */
88   PLAYOUT_ITEM_STATE_PREPARED,  /* Prepared and ready to activate */
89   PLAYOUT_ITEM_STATE_ACTIVATED, /* Activated */
90   PLAYOUT_ITEM_STATE_FIRST_VBUFFER,     /* First video buffer has gone through */
91   PLAYOUT_ITEM_STATE_AGGREGATING,       /* Audio & video buffers are aggregating */
92   PLAYOUT_ITEM_STATE_EOS        /* At least one pad is EOS */
93 } PlayoutItemState;
94 
95 typedef struct
96 {
97   PlayoutApp *app;
98   PlayoutItemState state;
99 
100   gchar *fn;
101 
102   GstElement *decoder;          /* bin with uridecodebin + converters */
103 
104   /* We just use the first audio stream and ignore the rest (if there is audio) */
105   GstPad *audio_pad;            /* decoder bin audio src ghostpad */
106   GstPad *video_pad;            /* decoder bin video src ghostpad */
107   GstVideoRectangle video_irect;        /* input w/h/x/y of the item */
108   GstVideoRectangle video_orect;        /* output w/h/x/y of the item */
109 
110   /* When this item has finished preparing and all pads have been connected to
111    * mixers, the pads will be blocked till it's this item's turn to be played */
112   gulong audio_pad_probe_block_id;
113   gulong video_pad_probe_block_id;
114 
115   /* The current running time of this item; updated with every audio buffer if
116    * this item has audio; otherwise it's updated withe very video buffer */
117   guint64 running_time;
118 } PlayoutItem;
119 
120 static PlayoutApp *playout_app_new (void);
121 static void playout_app_free (PlayoutApp * app);
122 static PlayoutItem *playout_item_new (PlayoutApp * app, const gchar * fn);
123 static void playout_item_free (PlayoutItem * item);
124 
125 static void playout_app_add_item (PlayoutApp * app, const gchar * fn);
126 static gboolean playout_app_prepare_item (PlayoutItem * item);
127 static gboolean playout_app_activate_item (PlayoutItem * item);
128 static gboolean playout_app_activate_next_item (PlayoutApp * app);
129 static gboolean playout_app_activate_next_item_early (PlayoutApp * app);
130 static PlayoutItem *playout_app_get_current_item (PlayoutApp * app);
131 static gboolean playout_app_remove_item (PlayoutItem * item);
132 
133 static void
playout_app_add_audio_sink(PlayoutApp * app)134 playout_app_add_audio_sink (PlayoutApp * app)
135 {
136   GstElement *audio_resample, *audio_conv, *queue;
137 
138   /* audiomixer doesn't do conversion yet, so we don't need an output capsfilter
139    * for this branch. The output format is decided by the sink pads, which all
140    * have to have the same format. */
141   app->audio_mixer = gst_element_factory_make ("audiomixer", "audio_mixer");
142   audio_conv = gst_element_factory_make ("audioconvert", "mixer_audioconvert");
143   audio_resample = gst_element_factory_make ("audioresample",
144       "audio_mixer_audioresample");
145   queue = gst_element_factory_make ("queue", "asink_queue");
146   app->audio_sink = gst_element_factory_make ("autoaudiosink", NULL);
147   g_object_set (app->audio_sink, "async-handling", TRUE, NULL);
148   gst_bin_add_many (GST_BIN (app->pipeline), app->audio_mixer, audio_conv,
149       audio_resample, queue, app->audio_sink, NULL);
150   gst_element_link_many (app->audio_mixer, audio_conv, audio_resample,
151       queue, app->audio_sink, NULL);
152 
153   if (!gst_element_sync_state_with_parent (app->audio_mixer) ||
154       !gst_element_sync_state_with_parent (audio_conv) ||
155       !gst_element_sync_state_with_parent (audio_resample) ||
156       !gst_element_sync_state_with_parent (queue) ||
157       !gst_element_sync_state_with_parent (app->audio_sink))
158     GST_ERROR ("app: unable to sync audio mixer + sink state with pipeline");
159 }
160 
161 static PlayoutApp *
playout_app_new(void)162 playout_app_new (void)
163 {
164   GstElement *video_capsfilter, *queue;
165   GstCaps *caps;
166   PlayoutApp *app;
167 
168   app = g_new0 (PlayoutApp, 1);
169 
170   app->state = PLAYOUT_APP_STATE_READY;
171 
172   app->play_queue =
173       g_ptr_array_new_with_free_func ((GDestroyNotify) playout_item_free);
174   app->play_queue_current = -1;
175   g_mutex_init (&app->play_queue_lock);
176 
177   app->main_loop = g_main_loop_new (NULL, FALSE);
178 
179   app->pipeline = gst_pipeline_new ("pipeline");
180 
181   /* It's best to set a caps filter for the video output format */
182   app->video_orect.w = OUTPUT_VIDEO_WIDTH;
183   app->video_orect.h = OUTPUT_VIDEO_HEIGHT;
184   app->video_orect.x = 0;
185   app->video_orect.y = 0;
186   app->video_mixer = gst_element_factory_make ("compositor", "video_mixer");
187   /* Set the background as black; faster while compositing, and allows us to
188    * rescale videos with a different aspect ratio than the output in a way that
189    * adds black borders automatically */
190   g_object_set (app->video_mixer, "background", 1, NULL);
191   queue = gst_element_factory_make ("queue", "vsink_queue");
192   app->video_sink = gst_element_factory_make ("autovideosink", NULL);
193   g_object_set (app->video_sink, "async-handling", TRUE, NULL);
194   video_capsfilter = gst_element_factory_make ("capsfilter",
195       "video_mixer_capsfilter");
196   caps = gst_caps_from_string (RAW_VIDEO_CAPS_STR);
197   g_object_set (video_capsfilter, "caps", caps, NULL);
198   gst_caps_unref (caps);
199   gst_bin_add_many (GST_BIN (app->pipeline), app->video_mixer, video_capsfilter,
200       queue, app->video_sink, NULL);
201   gst_element_link_many (app->video_mixer, video_capsfilter, queue,
202       app->video_sink, NULL);
203 
204   return app;
205 }
206 
207 static void
playout_app_free(PlayoutApp * app)208 playout_app_free (PlayoutApp * app)
209 {
210   GST_DEBUG ("Freeing app");
211   g_ptr_array_unref (app->play_queue);
212   g_main_loop_unref (app->main_loop);
213   gst_element_set_state (app->pipeline, GST_STATE_NULL);
214   gst_object_unref (app->pipeline);
215   g_free (app);
216 }
217 
218 static void
playout_app_eos(GstBus * bus,GstMessage * msg,PlayoutApp * app)219 playout_app_eos (GstBus * bus, GstMessage * msg, PlayoutApp * app)
220 {
221   g_print ("All streams EOS, exiting...\n");
222   g_main_loop_quit (app->main_loop);
223 }
224 
225 static PlayoutItem *
playout_item_new(PlayoutApp * app,const gchar * fn)226 playout_item_new (PlayoutApp * app, const gchar * fn)
227 {
228   PlayoutItem *item = g_new0 (PlayoutItem, 1);
229 
230   item->app = app;
231   item->state = PLAYOUT_ITEM_STATE_NEW;
232   item->fn = g_strdup (fn);
233 
234   return item;
235 }
236 
237 /* Unlink and release the pad */
238 static gboolean
playout_remove_pad(GstPad * srcpad)239 playout_remove_pad (GstPad * srcpad)
240 {
241   GstPad *sinkpad;
242   GstElement *mixer;
243 
244   sinkpad = gst_pad_get_peer (srcpad);
245   if (!sinkpad)
246     return FALSE;
247   if (!gst_pad_unlink (srcpad, sinkpad))
248     return FALSE;
249   mixer = gst_pad_get_parent_element (sinkpad);
250   gst_element_release_request_pad (mixer, sinkpad);
251   GST_DEBUG ("Released some pad");
252 
253   gst_object_unref (sinkpad);
254   gst_object_unref (mixer);
255   return FALSE;
256 }
257 
258 static GstPadProbeReturn
playout_item_pad_probe_blocked(GstPad * srcpad,GstPadProbeInfo * info,PlayoutItem * item)259 playout_item_pad_probe_blocked (GstPad * srcpad, GstPadProbeInfo * info,
260     PlayoutItem * item)
261 {
262   if (srcpad == item->audio_pad) {
263     item->audio_pad_probe_block_id = GST_PAD_PROBE_INFO_ID (info);
264   } else if (srcpad == item->video_pad) {
265     item->video_pad_probe_block_id = GST_PAD_PROBE_INFO_ID (info);
266   } else {
267     g_assert_not_reached ();
268   }
269 
270   return GST_PAD_PROBE_OK;
271 }
272 
273 static GstPadProbeReturn
playout_item_pad_probe_pad_running_time(GstPad * srcpad,GstPadProbeInfo * info,PlayoutItem * item)274 playout_item_pad_probe_pad_running_time (GstPad * srcpad,
275     GstPadProbeInfo * info, PlayoutItem * item)
276 {
277   GstEvent *event;
278   GstBuffer *buffer;
279   guint64 running_time;
280   const GstSegment *segment;
281 
282   buffer = GST_PAD_PROBE_INFO_BUFFER (info);
283   event = gst_pad_get_sticky_event (srcpad, GST_EVENT_SEGMENT, 0);
284   GST_TRACE ("%s: pad sticky event: %" GST_PTR_FORMAT, item->fn, event);
285 
286   if (event) {
287     gst_event_parse_segment (event, &segment);
288     gst_event_unref (event);
289     running_time = gst_segment_to_running_time (segment, GST_FORMAT_TIME,
290         GST_BUFFER_PTS (buffer));
291   } else {
292     GST_WARNING ("%s: unable to parse event for segment; falling back to pts. "
293         "Output will probably have glitches.", item->fn);
294     running_time = GST_BUFFER_PTS (buffer);
295   }
296 
297   item->running_time = running_time + GST_BUFFER_DURATION (buffer);
298   GST_TRACE ("%s: running time is %" G_GUINT64_FORMAT ", duration is %"
299       G_GUINT64_FORMAT, item->fn, item->running_time,
300       GST_BUFFER_DURATION (buffer));
301 
302   return GST_PAD_PROBE_PASS;
303 }
304 
305 static GstPadProbeReturn
playout_item_pad_probe_video_pad_eos_on_buffer(GstPad * srcpad,GstPadProbeInfo * info,PlayoutItem * prev_item)306 playout_item_pad_probe_video_pad_eos_on_buffer (GstPad * srcpad,
307     GstPadProbeInfo * info, PlayoutItem * prev_item)
308 {
309   PlayoutItem *current_item;
310 
311   current_item = playout_app_get_current_item (prev_item->app);
312 
313   if (!current_item)
314     return GST_PAD_PROBE_REMOVE;
315 
316   /* Step through the item's states as buffers pass through. The first buffer
317    * will be taken by the video_mixer, and kept till the audio running time
318    * matches the video buffer running time. When the second buffer gets through,
319    * we know that this pad has begun aggregating. */
320   switch (current_item->state) {
321     case PLAYOUT_ITEM_STATE_NEW:
322     case PLAYOUT_ITEM_STATE_PREPARED:
323       GST_DEBUG ("%s: new/prepared", current_item->fn);
324       break;
325     case PLAYOUT_ITEM_STATE_ACTIVATED:
326       GST_DEBUG ("%s: activated -> first vbuffer", current_item->fn);
327       current_item->state = PLAYOUT_ITEM_STATE_FIRST_VBUFFER;
328       break;
329     case PLAYOUT_ITEM_STATE_FIRST_VBUFFER:
330       GST_DEBUG ("%s: first vbuffer -> aggregating", current_item->fn);
331       current_item->state = PLAYOUT_ITEM_STATE_AGGREGATING;
332       gst_pad_remove_probe (srcpad, GST_PAD_PROBE_INFO_ID (info));
333       /* Item is aggregating, release the previous item's video pad */
334       goto release;
335       break;
336     case PLAYOUT_ITEM_STATE_EOS:
337       return GST_PAD_PROBE_REMOVE;
338     default:
339       g_assert_not_reached ();
340   }
341 
342   return GST_PAD_PROBE_PASS;
343 
344 release:
345   {
346     playout_remove_pad (prev_item->video_pad);
347     GST_DEBUG ("%s: released video pad", prev_item->fn);
348     prev_item->video_pad = NULL;
349 
350     /* If there's no audio pad, or if the audio pad is already EOS, we can
351      * remove this item from the queue which will free it. Need to free the
352      * item from the main thread because it causes the item's decoder bin
353      * to be removed from the pipeline, which cannot be done in the
354      * streaming thread */
355     if (prev_item->audio_pad == NULL) {
356       GST_DEBUG ("%s: queued item removal (last pad is video)", prev_item->fn);
357       g_main_context_invoke (NULL, (GSourceFunc) playout_app_remove_item,
358           prev_item);
359     }
360 
361     /* Pad probe has already been removed above */
362     return GST_PAD_PROBE_PASS;
363   }
364 }
365 
366 /* This is called on EOS for both item->audio_pad and item->video_pad
367  *
368  * FIXME: Add locking. Both pads could go EOS at the exact same time. */
369 static GstPadProbeReturn
playout_item_pad_probe_event(GstPad * srcpad,GstPadProbeInfo * info,PlayoutItem * item)370 playout_item_pad_probe_event (GstPad * srcpad, GstPadProbeInfo * info,
371     PlayoutItem * item)
372 {
373   GstEventType type;
374   gboolean ret = TRUE;
375   GstPadProbeReturn probe_ret = GST_PAD_PROBE_DROP;
376 
377   type = GST_EVENT_TYPE (GST_PAD_PROBE_INFO_DATA (info));
378   if (type != GST_EVENT_EOS)
379     return GST_PAD_PROBE_PASS;
380 
381   /* We might get two EOSes on this pad if we send an artificial EOS. Remove
382    * the probe so this is only called once for each pad */
383   gst_pad_remove_probe (srcpad, GST_PAD_PROBE_INFO_ID (info));
384 
385   GST_DEBUG ("%s: recvd some EOS", item->fn);
386 
387   if (item->state != PLAYOUT_ITEM_STATE_EOS) {
388     /* We have more than one pad per item (video + audio item), and this is the
389      * first pad to go EOS or we have only one pad per item, and that pad has
390      * gone EOS. For the first case, the other pad might still have some buffers
391      * to output before going EOS, but we need to activate the next item and
392      * start outputting buffers from that immediately. */
393 
394     /* Update the total elapsed duration from the item's current running time */
395     item->app->elapsed_duration += item->running_time;
396 
397     GST_DEBUG ("%s: activating next item", item->fn);
398     /* Activate the next item if and only if this is the first pad to go EOS */
399     ret = playout_app_activate_next_item (item->app);
400     if (!ret) {
401       GST_DEBUG ("%s: App is going EOS", item->fn);
402       item->state = PLAYOUT_ITEM_STATE_EOS;
403       item->app->state = PLAYOUT_APP_STATE_EOS;
404       /* If we couldn't activate the next item, pass the EOS event onward,
405        * ending the stream */
406       probe_ret = GST_PAD_PROBE_PASS;
407     }
408   }
409 
410   g_assert (srcpad != NULL);
411 
412   if (srcpad == item->audio_pad) {
413     GST_DEBUG ("%s: audio pad was EOS", item->fn);
414 
415     if (item->app->state != PLAYOUT_APP_STATE_EOS) {
416       /* While activating the next item, we ensure that there's no offset mismatch
417        * which would cause audiomixer to output silence, so we can release the
418        * previous item's audio request pad here. We also unlink the audio pad;
419        * nothing else is needed from it */
420       playout_remove_pad (srcpad);
421       GST_DEBUG ("%s: released audio pad", item->fn);
422 
423       /* If there's no video pad, or if the video pad is already EOS, we can
424        * remove this item from the queue which will free it. Need to free the
425        * item from the main thread because it causes the item's decoder bin
426        * to be removed from the pipeline, which cannot be done in the
427        * streaming thread */
428       if (item->video_pad == NULL) {
429         GST_DEBUG ("%s: queued item removal (last pad is audio)", item->fn);
430         g_main_context_invoke (NULL, (GSourceFunc) playout_app_remove_item,
431             item);
432       }
433     } else {
434       /* If this is the last pad on audio_mixer, let the EOS go through
435        * before unlinking/releasing the pad. This should happen within 500ms. */
436       g_timeout_add (500, (GSourceFunc) playout_remove_pad, srcpad);
437       GST_DEBUG ("%s: queued audio pad release", item->fn);
438 
439       if (item->video_pad == NULL) {
440         /* Unlike above, we need to wait till the pad is removed before removing
441          * the item from the app, so we queue it for 100ms afterwards */
442         GST_DEBUG ("%s: queued last item removal (last pad is audio)",
443             item->fn);
444         g_timeout_add (600, (GSourceFunc) playout_app_remove_item, item);
445       }
446     }
447     item->audio_pad = NULL;
448   } else if (srcpad == item->video_pad) {
449 
450     GST_DEBUG ("%s: video pad was EOS", item->fn);
451 
452     if (item->audio_pad != NULL)
453       GST_WARNING ("%s: video pad went EOS before audio pad! "
454           "There will be audio/video glitches while switching.", item->fn);
455 
456     if (item->app->state != PLAYOUT_APP_STATE_EOS) {
457       PlayoutItem *next_item;
458 
459       next_item = playout_app_get_current_item (item->app);
460       GST_DEBUG ("%s: next item is %s, %i/%i", item->fn, next_item->fn,
461           next_item->state, PLAYOUT_ITEM_STATE_ACTIVATED);
462 
463       g_assert (next_item != NULL);
464       /* If there's another item being activated, release this video pad only
465        * when the next item's video pad starts being aggregated; that happens
466        * when this probe receives its 2nd buffer from the next item */
467       gst_pad_add_probe (next_item->video_pad, GST_PAD_PROBE_TYPE_BUFFER,
468           (GstPadProbeCallback) playout_item_pad_probe_video_pad_eos_on_buffer,
469           item, NULL);
470     } else {
471       /* If this is the last pad on video_mixer, let the EOS go through
472        * before unlinking/releasing the pad. This should happen within 500ms. */
473       g_timeout_add (500, (GSourceFunc) playout_remove_pad, srcpad);
474       GST_DEBUG ("%s: queued video pad release", item->fn);
475       item->video_pad = NULL;
476     }
477     probe_ret = GST_PAD_PROBE_PASS;
478   } else {
479     g_assert_not_reached ();
480   }
481 
482   item->state = PLAYOUT_ITEM_STATE_EOS;
483 
484   /* NOTE: If the srcpad has been unlinked, the return value is useless */
485   return probe_ret;
486 }
487 
488 /* On the "pad-added" signal of uridecodebin, add converters and connect to
489  * audio/video mixers */
490 static void
playout_item_new_pad(GstElement * uridecodebin,GstPad * pad,PlayoutItem * item)491 playout_item_new_pad (GstElement * uridecodebin, GstPad * pad,
492     PlayoutItem * item)
493 {
494   GstStructure *s;
495   GstCaps *caps;
496   GstPad *sinkpad, *srcpad;
497   GstElement *queue;
498   GstPadProbeType block_probe_type;
499 
500   caps = gst_pad_get_current_caps (pad);
501   s = gst_caps_get_structure (caps, 0);
502   GST_DEBUG ("%s: new pad: %p, caps: %s", item->fn, pad,
503       gst_structure_get_name (s));
504 
505   if (gst_structure_has_name (s, "audio/x-raw")) {
506     if (item->audio_pad != NULL)
507       /* Ignore all audio pads after the first one */
508       goto out;
509     goto audio;
510   } else if (gst_structure_has_name (s, "video/x-raw")) {
511     if (item->video_pad != NULL)
512       /* Ignore all video pads after the first one */
513       goto out;
514     goto video;
515   } else {
516     goto out;
517   }
518 
519 audio:
520   {
521     GstCaps *wanted_caps;
522     GstElement *audioconvert, *audioresample, *capsfilter;
523 
524     /* Audio pad found; add audio mixer and audio sink to the pipeline.
525      * NOTE: If any items after this do not have an audio pad, the pipeline will
526      * mess up because the audio sink will not receive any data. */
527     if (item->app->audio_sink == NULL)
528       playout_app_add_audio_sink (item->app);
529 
530     wanted_caps = gst_caps_from_string (RAW_AUDIO_CAPS_STR);
531 
532     if (!gst_caps_is_equal (caps, wanted_caps)) {
533       GST_DEBUG ("%s: converting audio caps", item->fn);
534       /* We need to convert the audio to the wanted format because
535        * audiomixer doesn't do format conversion */
536       audioresample = gst_element_factory_make ("audioresample", NULL);
537       audioconvert = gst_element_factory_make ("audioconvert", NULL);
538       capsfilter = gst_element_factory_make ("capsfilter", NULL);
539       g_object_set (capsfilter, "caps", wanted_caps, NULL);
540       queue = gst_element_factory_make ("queue", NULL);
541       gst_bin_add_many (GST_BIN (item->decoder), audioresample, audioconvert,
542           capsfilter, queue, NULL);
543 
544       sinkpad = gst_element_get_static_pad (audioresample, "sink");
545       gst_pad_link (pad, sinkpad);
546       gst_object_unref (sinkpad);
547       gst_element_link_many (audioresample, audioconvert, capsfilter, queue,
548           NULL);
549       srcpad = gst_element_get_static_pad (queue, "src");
550 
551       if (!gst_element_sync_state_with_parent (audioresample) ||
552           !gst_element_sync_state_with_parent (audioconvert) ||
553           !gst_element_sync_state_with_parent (capsfilter) ||
554           !gst_element_sync_state_with_parent (queue)) {
555         GST_ERROR ("%s: unable to sync audio converter state with decoder",
556             item->fn);
557         goto out;
558       }
559     } else {
560       queue = gst_element_factory_make ("queue", NULL);
561       gst_bin_add (GST_BIN (item->decoder), queue);
562       sinkpad = gst_element_get_static_pad (queue, "sink");
563       gst_pad_link (pad, sinkpad);
564       gst_object_unref (sinkpad);
565 
566       srcpad = gst_element_get_static_pad (queue, "src");
567 
568       if (!gst_element_sync_state_with_parent (queue)) {
569         GST_ERROR ("%s: unable to sync audio queue state with decoder",
570             item->fn);
571         goto out;
572       }
573     }
574     gst_caps_unref (wanted_caps);
575 
576     /* Convert the audioconvert src pad to a ghostpad on the bin */
577     item->audio_pad = gst_ghost_pad_new (NULL, srcpad);
578     gst_pad_set_active (item->audio_pad, TRUE);
579     gst_element_add_pad (item->decoder, item->audio_pad);
580     gst_object_unref (srcpad);
581 
582     srcpad = item->audio_pad;
583     GST_DEBUG ("%s: created audio pad", item->fn);
584     goto done;
585   }
586 
587 video:
588   {
589     if (!gst_structure_get_int (s, "width", &item->video_irect.w) ||
590         !gst_structure_get_int (s, "height", &item->video_irect.h))
591       GST_WARNING ("%s: unable to set width/height from caps", item->fn);
592     item->video_irect.x = item->video_irect.y = 0;
593 
594     queue = gst_element_factory_make ("queue", "video-decoder-queue-%u");
595     gst_bin_add (GST_BIN (item->decoder), queue);
596 
597     if (!gst_element_sync_state_with_parent (queue)) {
598       GST_ERROR ("%s: unable to sync video queue state with decoder", item->fn);
599       goto out;
600     }
601 
602     sinkpad = gst_element_get_static_pad (queue, "sink");
603     gst_pad_link (pad, sinkpad);
604     gst_object_unref (sinkpad);
605 
606     /* Convert the queue src pad to a ghostpad on the bin */
607     srcpad = gst_element_get_static_pad (queue, "src");
608     item->video_pad = gst_ghost_pad_new (NULL, srcpad);
609     gst_pad_set_active (item->video_pad, TRUE);
610     gst_element_add_pad (item->decoder, item->video_pad);
611     gst_object_unref (srcpad);
612 
613     srcpad = item->video_pad;
614     GST_DEBUG ("%s: created video pad", item->fn);
615     goto done;
616   }
617 
618 done:
619   /* We let events and queries through */
620   block_probe_type = GST_PAD_PROBE_TYPE_BLOCK |
621       GST_PAD_PROBE_TYPE_BUFFER | GST_PAD_PROBE_TYPE_BUFFER_LIST;
622   /* If the app is already playing an item, block everything except queries
623    * till we need to play this item */
624   if (item->app->state != PLAYOUT_APP_STATE_READY)
625     gst_pad_add_probe (srcpad, block_probe_type,
626         (GstPadProbeCallback) playout_item_pad_probe_blocked, item, NULL);
627   /* Probe events for EOS */
628   gst_pad_add_probe (srcpad, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
629       (GstPadProbeCallback) playout_item_pad_probe_event, item, NULL);
630 
631 out:
632   gst_caps_unref (caps);
633 }
634 
635 /* All pads on uridecodebin have finished being populated; the item has been
636  * prepared and is ready to be activated */
637 static void
playout_item_no_more_pads(GstElement * uridecodebin,PlayoutItem * item)638 playout_item_no_more_pads (GstElement * uridecodebin, PlayoutItem * item)
639 {
640   /* Set a buffer pad probe that constantly updates the item's
641    * elapsed_duration using the duration of each audio buffer */
642   if (item->audio_pad) {
643     gst_pad_add_probe (item->audio_pad, GST_PAD_PROBE_TYPE_BUFFER,
644         (GstPadProbeCallback) playout_item_pad_probe_pad_running_time,
645         item, NULL);
646   } else if (item->video_pad) {
647     gst_pad_add_probe (item->video_pad, GST_PAD_PROBE_TYPE_BUFFER,
648         (GstPadProbeCallback) playout_item_pad_probe_pad_running_time,
649         item, NULL);
650   } else {
651     GST_ERROR ("%s: no pads were generated! Can't continue playing!", item->fn);
652     return;
653   }
654 
655   item->state = PLAYOUT_ITEM_STATE_PREPARED;
656   GST_DEBUG ("%s: prepared", item->fn);
657 
658   if (item->app->state != PLAYOUT_APP_STATE_READY)
659     /* This item will be activated when the previous one is EOS */
660     return;
661 
662   GST_DEBUG ("Application isn't already playing; activate the item and prepare"
663       " the next one");
664 
665   playout_app_activate_item (item);
666   item->state = PLAYOUT_ITEM_STATE_ACTIVATED;
667   item->app->state = PLAYOUT_APP_STATE_PLAYING;
668 
669   if (item->app->play_queue->len > 1)
670     playout_app_prepare_item (g_ptr_array_index (item->app->play_queue, 1));
671 }
672 
673 static GstElement *
playout_item_create_decoder(PlayoutItem * item)674 playout_item_create_decoder (PlayoutItem * item)
675 {
676   GstElement *bin, *dec;
677   GError *err = NULL;
678   gchar *uri;
679 
680   uri = gst_filename_to_uri (item->fn, &err);
681   if (err != NULL) {
682     GST_WARNING ("Could not convert '%s' to uri: %s", item->fn, err->message);
683     g_clear_error (&err);
684     return NULL;
685   }
686 
687   bin = gst_bin_new (NULL);
688   dec = gst_element_factory_make ("uridecodebin", NULL);
689   g_object_set (dec, "uri", uri, NULL);
690   g_free (uri);
691 
692   gst_bin_add (GST_BIN (bin), dec);
693 
694   g_signal_connect (dec, "pad-added", G_CALLBACK (playout_item_new_pad), item);
695   g_signal_connect (dec, "no-more-pads", G_CALLBACK (playout_item_no_more_pads),
696       item);
697 
698   return bin;
699 }
700 
701 static void
playout_item_free(PlayoutItem * item)702 playout_item_free (PlayoutItem * item)
703 {
704   GST_DEBUG ("Entering free");
705   switch (gst_element_set_state (item->decoder, GST_STATE_NULL)) {
706     case GST_STATE_CHANGE_FAILURE:
707       GST_ERROR ("%s: Unable to change state to NULL", item->fn);
708       break;
709     case GST_STATE_CHANGE_SUCCESS:
710       GST_DEBUG ("%s: State change success", item->fn);
711       break;
712     default:
713       GST_DEBUG ("%s: Some async/no-preroll", item->fn);
714   }
715 
716   gst_bin_remove (GST_BIN (item->app->pipeline), item->decoder);
717   GST_DEBUG ("%s: bin removed", item->fn);
718 
719   g_free (item->fn);
720   g_free (item);
721   GST_DEBUG ("item freed");
722 }
723 
724 static guint64
playout_item_pad_get_segment_time(GstPad * srcpad)725 playout_item_pad_get_segment_time (GstPad * srcpad)
726 {
727   GstEvent *event;
728   const GstSegment *segment;
729 
730   event = gst_pad_get_sticky_event (srcpad, GST_EVENT_SEGMENT, 0);
731   if (!event)
732     return 0;
733   gst_event_parse_segment (event, &segment);
734   gst_event_unref (event);
735   return segment->time;
736 }
737 
738 static void
playout_app_add_item(PlayoutApp * app,const gchar * fn)739 playout_app_add_item (PlayoutApp * app, const gchar * fn)
740 {
741   PlayoutItem *item;
742 
743   item = playout_item_new (app, fn);
744 
745   g_mutex_lock (&app->play_queue_lock);
746   g_ptr_array_add (app->play_queue, item);
747   g_mutex_unlock (&app->play_queue_lock);
748 }
749 
750 static gboolean
playout_app_remove_item(PlayoutItem * item)751 playout_app_remove_item (PlayoutItem * item)
752 {
753   PlayoutApp *app;
754   GST_DEBUG ("%s: removing and freeing", item->fn);
755 
756   app = item->app;
757 
758   g_mutex_lock (&app->play_queue_lock);
759   g_ptr_array_remove (app->play_queue, item);
760   if (item->state >= PLAYOUT_ITEM_STATE_ACTIVATED)
761     /* Removed item was playing; decrement the current-play-queue index */
762     app->play_queue_current--;
763   g_mutex_unlock (&app->play_queue_lock);
764 
765   /* Don't call this again */
766   return FALSE;
767 }
768 
769 static PlayoutItem *
playout_app_get_current_item(PlayoutApp * app)770 playout_app_get_current_item (PlayoutApp * app)
771 {
772   if (app->play_queue_current < 0 ||
773       app->play_queue->len < (app->play_queue_current + 1))
774     return NULL;
775 
776   return g_ptr_array_index (app->play_queue, app->play_queue_current);
777 }
778 
779 static gboolean
playout_app_prepare_item(PlayoutItem * item)780 playout_app_prepare_item (PlayoutItem * item)
781 {
782   PlayoutApp *app = item->app;
783 
784   if (item->decoder != NULL)
785     return TRUE;
786 
787   item->decoder = playout_item_create_decoder (item);
788 
789   if (item->decoder == NULL)
790     return FALSE;
791 
792   gst_bin_add (GST_BIN (app->pipeline), item->decoder);
793 
794   if (!gst_element_sync_state_with_parent (item->decoder)) {
795     GST_ERROR ("%s: unable to sync state with parent", item->fn);
796     return FALSE;
797   }
798 
799   GST_DEBUG ("%s: preparing", item->fn);
800 
801   /* All further processing is done in the "no-more-pads" callback of
802    * uridecodebin */
803   return TRUE;
804 }
805 
806 /* Called exactly once for each item */
807 static gboolean
playout_app_activate_item(PlayoutItem * item)808 playout_app_activate_item (PlayoutItem * item)
809 {
810   GstPad *sinkpad;
811   guint64 segment_time;
812   PlayoutApp *app = item->app;
813 
814   if (item->state != PLAYOUT_ITEM_STATE_PREPARED) {
815     GST_ERROR ("Item %s is not ready to be activated!", item->fn);
816     return FALSE;
817   }
818 
819   if (!item->audio_pad && !item->video_pad) {
820     GST_ERROR ("Item %s has no pads! Can't activate it!", item->fn);
821     return FALSE;
822   }
823 
824   /* Hook up to mixers and remove the probes blocking the pads */
825   if (item->audio_pad) {
826     GST_DEBUG ("%s: hooking up audio pad to mixer", item->fn);
827     sinkpad = gst_element_get_request_pad (app->audio_mixer, "sink_%u");
828     gst_pad_link (item->audio_pad, sinkpad);
829 
830     segment_time = playout_item_pad_get_segment_time (item->audio_pad);
831     if (segment_time > 0) {
832       /* If the segment time is > 0, the new pad wants audiomixer to output audio
833        * silence for that duration. This will cause audio glitches, so we  move
834        * the pad offset back by that amount and tell audiomixer to start mixing
835        * our buffers immediately. */
836       GST_DEBUG ("%s: subtracting segment time %" G_GUINT64_FORMAT " from "
837           "elapsed duration before setting it as the pad offset", item->fn,
838           segment_time);
839       if (app->elapsed_duration > segment_time)
840         app->elapsed_duration -= segment_time;
841       else
842         app->elapsed_duration = 0;
843     }
844 
845     if (app->elapsed_duration > 0) {
846       GST_DEBUG ("%s: set audio pad offset to %" G_GUINT64_FORMAT "ms",
847           item->fn, app->elapsed_duration / GST_MSECOND);
848       gst_pad_set_offset (item->audio_pad, app->elapsed_duration);
849     }
850 
851     if (item->audio_pad_probe_block_id > 0) {
852       GST_DEBUG ("%s: removing audio pad block probe", item->fn);
853       gst_pad_remove_probe (item->audio_pad, item->audio_pad_probe_block_id);
854     }
855     gst_object_unref (sinkpad);
856   }
857 
858   if (item->video_pad) {
859     GST_DEBUG ("%s: hooking up video pad to mixer", item->fn);
860     sinkpad = gst_element_get_request_pad (app->video_mixer, "sink_%u");
861 
862     /* Get new height/width/xpos/ypos such that the video scales up or down to
863      * fit within the output video size without any cropping */
864     gst_video_sink_center_rect (item->video_irect, item->app->video_orect,
865         &item->video_orect, TRUE);
866     GST_DEBUG ("%s: w: %i, h: %i, x: %i, y: %i\n", item->fn,
867         item->video_orect.w, item->video_orect.h, item->video_orect.x,
868         item->video_orect.y);
869     g_object_set (sinkpad, "width", item->video_orect.w, "height",
870         item->video_orect.h, "xpos", item->video_orect.x, "ypos",
871         item->video_orect.y, NULL);
872 
873     /* If this is not the last item, on EOS, continue to aggregate using the
874      * last buffer till the pad is released */
875     if (item->app->play_queue->len != (item->app->play_queue_current + 2))
876       g_object_set (sinkpad, "repeat-after-eos", TRUE, NULL);
877     else
878       GST_DEBUG ("%s: last item, not setting repeat-after-eos", item->fn);
879     gst_pad_link (item->video_pad, sinkpad);
880 
881     if (app->elapsed_duration > 0) {
882       GST_DEBUG ("%s: set video pad offset to %" G_GUINT64_FORMAT "ms",
883           item->fn, app->elapsed_duration / GST_MSECOND);
884       gst_pad_set_offset (item->video_pad, app->elapsed_duration);
885     }
886 
887     if (item->video_pad_probe_block_id > 0) {
888       GST_DEBUG ("%s: removing video pad block probe", item->fn);
889       gst_pad_remove_probe (item->video_pad, item->video_pad_probe_block_id);
890     }
891     gst_object_unref (sinkpad);
892   }
893 
894   item->state = PLAYOUT_ITEM_STATE_ACTIVATED;
895   g_mutex_lock (&item->app->play_queue_lock);
896   item->app->play_queue_current++;
897   g_mutex_unlock (&item->app->play_queue_lock);
898 
899   GST_DEBUG ("%s: activated", item->fn);
900 
901   return TRUE;
902 }
903 
904 /* Activate the next item, and prepare the one after that for later activation */
905 static gboolean
playout_app_activate_next_item(PlayoutApp * app)906 playout_app_activate_next_item (PlayoutApp * app)
907 {
908   PlayoutItem *item;
909   gboolean ret;
910 
911   if (app->play_queue->len < (app->play_queue_current + 2)) {
912     g_print ("No more items to play\n");
913     return FALSE;
914   }
915 
916   item = g_ptr_array_index (app->play_queue, app->play_queue_current + 1);
917   ret = playout_app_activate_item (item);
918   if (!ret) {
919     /* Tell caller, who can then decide whether to skip or error out */
920     GST_ERROR ("%s: unable to activate", item->fn);
921     return FALSE;
922   }
923   if (app->play_queue->len > (app->play_queue_current + 1)) {
924     item = g_ptr_array_index (app->play_queue, app->play_queue_current + 1);
925     /* FIXME: What if this fails? Prepare the next one in the queue? */
926     ret = playout_app_prepare_item (item);
927     if (!ret)
928       GST_ERROR ("%s: unable to prepare", item->fn);
929   }
930   return ret;
931 }
932 
933 static GstPadProbeReturn
playout_item_pad_probe_video_pad_running_time(GstPad * srcpad,GstPadProbeInfo * info,PlayoutItem * item)934 playout_item_pad_probe_video_pad_running_time (GstPad * srcpad,
935     GstPadProbeInfo * info, PlayoutItem * item)
936 {
937   GstEvent *event;
938   GstBuffer *buffer;
939   guint64 running_time;
940   const GstSegment *segment;
941 
942   buffer = GST_PAD_PROBE_INFO_BUFFER (info);
943   event = gst_pad_get_sticky_event (srcpad, GST_EVENT_SEGMENT, 0);
944   GST_TRACE ("%s: video sticky event: %" GST_PTR_FORMAT, item->fn, event);
945 
946   if (event) {
947     gst_event_parse_segment (event, &segment);
948     gst_event_unref (event);
949     running_time = gst_segment_to_running_time (segment, GST_FORMAT_TIME,
950         GST_BUFFER_PTS (buffer));
951   } else {
952     GST_WARNING ("%s: unable to parse video event for segment; falling back to "
953         "pts", item->fn);
954     running_time = GST_BUFFER_PTS (buffer);
955   }
956 
957   if (running_time >= item->running_time) {
958     /* The video buffer passing through video_mixer now matches the audio buffer
959      * that passed through audio_mixer when the early switch was requested, so
960      * this is the time to send an EOS to video_pad, which will complete the
961      * switch */
962     GST_DEBUG ("Sending video EOS to %s", item->fn);
963     gst_pad_push_event (item->video_pad, gst_event_new_eos ());
964     return GST_PAD_PROBE_DROP;
965   } else {
966     return GST_PAD_PROBE_PASS;
967   }
968 }
969 
970 static gboolean
playout_app_activate_next_item_early(PlayoutApp * app)971 playout_app_activate_next_item_early (PlayoutApp * app)
972 {
973   PlayoutItem *item;
974 
975   item = playout_app_get_current_item (app);
976   if (!item) {
977     GST_WARNING ("Unable to switch early, no current item");
978     return FALSE;
979   }
980 
981   if (item->audio_pad) {
982     /* If we have an audio pad, EOS audio first, always */
983     GST_DEBUG ("Sending audio EOS to %s", item->fn);
984     gst_pad_push_event (item->audio_pad, gst_event_new_eos ());
985     /* We can't send the EOS to the video_pad yet because the running times for
986      * both mixers are different due to buffering at the audio sink. So we wait
987      * till the running time of the video_pad matches that of the audio_pad at
988      * the time the audio EOS was sent, and then EOS video as well. */
989     gst_pad_add_probe (item->video_pad, GST_PAD_PROBE_TYPE_BUFFER,
990         (GstPadProbeCallback) playout_item_pad_probe_video_pad_running_time,
991         item, NULL);
992   } else if (item->video_pad) {
993     /* If we have a video pad, EOS audio first, always */
994     GST_DEBUG ("Sending video EOS to %s", item->fn);
995     gst_pad_push_event (item->video_pad, gst_event_new_eos ());
996   } else {
997     g_assert_not_reached ();
998   }
999 
1000   /* Return FALSE so this function is called only once */
1001   return FALSE;
1002 }
1003 
1004 static gboolean
playout_app_play(PlayoutApp * app)1005 playout_app_play (PlayoutApp * app)
1006 {
1007   PlayoutItem *item;
1008 
1009   item = app->play_queue->len ? g_ptr_array_index (app->play_queue, 0) : NULL;
1010   if (!item) {
1011     g_printerr ("Nothing to play\n");
1012     return FALSE;
1013   }
1014 
1015   playout_app_prepare_item (item);
1016   return TRUE;
1017 }
1018 
1019 /*
1020  * playout: An example application to sequentially and seamlessly play a list of
1021  * audio-video or video-only files.
1022  *
1023  * This example application uses the compositor and audiomixer elements combined
1024  * with pad probes to stitch together a list of A/V or V-only files in such
1025  * a way that audio and video glitching is minimised. Mixing A/V and V-only
1026  * files is not supported because it complicates the architecture quite a bit.
1027  *
1028  * Due to the fundamental difference in the representation of audio and video
1029  * data, unless constructed specifically for the purpose of being stitched back,
1030  * the audio and video tracks of files will rarely end at the same PTS. There is
1031  * usually a sync difference of a few frames. This application tries to stitch
1032  * together the audio tracks as perfectly as possible, and duplicates/drops
1033  * video frames if there is an underrun/overrun. Even when audio samples are
1034  * played back-to-back, there might be glitches due to quirks in the decoder.
1035  *
1036  * The list of PlayoutItems can be edited and added to dynamically; except the
1037  * currently-playing item and the next one (which has been prepared already).
1038  */
1039 int
main(int argc,char ** argv)1040 main (int argc, char **argv)
1041 {
1042   GstBus *bus;
1043   gint switch_after_ms = 0;
1044   gchar **f, **filenames = NULL;
1045   GOptionEntry options[] = {
1046     {"switch-after", 's', 0, G_OPTION_ARG_INT, &switch_after_ms, "Time after "
1047           "which the next file will be forcibly activated", "MILLISECONDS"},
1048     {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL,
1049         "FILENAME1 [FILENAME2] [FILENAME3] ..."},
1050     {NULL}
1051   };
1052   GOptionContext *ctx;
1053   PlayoutApp *app;
1054   GError *err = NULL;
1055 
1056   ctx = g_option_context_new (NULL);
1057   g_option_context_set_summary (ctx, "An example application to sequentially "
1058       "and seamlessly play a list of audio-video or video-only files.");
1059   g_option_context_add_main_entries (ctx, options, NULL);
1060   g_option_context_add_group (ctx, gst_init_get_option_group ());
1061 
1062   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
1063     if (err)
1064       g_printerr ("Error initializing: %s\n", err->message);
1065     else
1066       g_printerr ("Error initializing: Unknown error!\n");
1067     g_option_context_free (ctx);
1068     g_clear_error (&err);
1069     return 1;
1070   }
1071 
1072   if (filenames == NULL || *filenames == NULL) {
1073     g_printerr ("%s", g_option_context_get_help (ctx, TRUE, NULL));
1074     return 1;
1075   }
1076 
1077   g_option_context_free (ctx);
1078 
1079   GST_DEBUG_CATEGORY_INIT (playout, "playout", 0, "Playout example app");
1080 
1081   app = playout_app_new ();
1082 
1083   for (f = filenames; f != NULL && *f != NULL; ++f)
1084     playout_app_add_item (app, *f);
1085 
1086   g_strfreev (filenames);
1087 
1088   if (!playout_app_play (app))
1089     return 1;
1090 
1091   GST_DEBUG ("Setting pipeline to PLAYING");
1092 
1093   bus = gst_pipeline_get_bus (GST_PIPELINE (app->pipeline));
1094   gst_bus_add_signal_watch (bus);
1095   g_signal_connect (bus, "message::eos", G_CALLBACK (playout_app_eos), app);
1096   gst_object_unref (bus);
1097 
1098   gst_element_set_state (app->pipeline, GST_STATE_PLAYING);
1099 
1100   if (switch_after_ms)
1101     g_timeout_add (switch_after_ms,
1102         (GSourceFunc) playout_app_activate_next_item_early, app);
1103 
1104   GST_DEBUG ("Running mainloop");
1105   g_main_loop_run (app->main_loop);
1106 
1107   playout_app_free (app);
1108 
1109   return 0;
1110 }
1111