04c352c9980c1bd2cfdd85251a18fa6fa382a64b
[platform/upstream/gstreamer.git] / tests / check / elements / splitmux.c
1 /* GStreamer unit test for splitmuxsrc/sink elements
2  *
3  * Copyright (C) 2007 David A. Schleef <ds@schleef.org>
4  * Copyright (C) 2015 Jan Schmidt <jan@centricular.com>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Library General Public License for more details.
15  *
16  * You should have received a copy of the GNU Library General Public
17  * License along with this library; if not, write to the
18  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19  * Boston, MA 02110-1301, USA.
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #  include "config.h"
24 #endif
25
26 #include <glib/gstdio.h>
27
28 #include <gst/check/gstcheck.h>
29 #include <gst/app/app.h>
30 #include <stdlib.h>
31
32 gchar *tmpdir = NULL;
33 GstClockTime first_ts;
34 GstClockTime last_ts;
35 gdouble current_rate;
36
37 static void
38 tempdir_setup (void)
39 {
40   const gchar *systmp = g_get_tmp_dir ();
41   tmpdir = g_build_filename (systmp, "splitmux-test-XXXXXX", NULL);
42   /* Rewrites tmpdir template input: */
43   tmpdir = g_mkdtemp (tmpdir);
44 }
45
46 static void
47 tempdir_cleanup (void)
48 {
49   GDir *d;
50   const gchar *f;
51
52   fail_if (tmpdir == NULL);
53
54   d = g_dir_open (tmpdir, 0, NULL);
55   fail_if (d == NULL);
56
57   while ((f = g_dir_read_name (d)) != NULL) {
58     gchar *fname = g_build_filename (tmpdir, f, NULL);
59     fail_if (g_remove (fname) != 0, "Failed to remove tmp file %s", fname);
60     g_free (fname);
61   }
62   g_dir_close (d);
63
64   fail_if (g_remove (tmpdir) != 0, "Failed to delete tmpdir %s", tmpdir);
65
66   g_free (tmpdir);
67   tmpdir = NULL;
68 }
69
70 static guint
71 count_files (const gchar * target)
72 {
73   GDir *d;
74   const gchar *f;
75   guint ret = 0;
76
77   d = g_dir_open (target, 0, NULL);
78   fail_if (d == NULL);
79
80   while ((f = g_dir_read_name (d)) != NULL)
81     ret++;
82   g_dir_close (d);
83
84   return ret;
85 }
86
87 static void
88 dump_error (GstMessage * msg)
89 {
90   GError *err = NULL;
91   gchar *dbg_info;
92
93   fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR);
94
95   gst_message_parse_error (msg, &err, &dbg_info);
96
97   g_printerr ("ERROR from element %s: %s\n",
98       GST_OBJECT_NAME (msg->src), err->message);
99   g_printerr ("Debugging info: %s\n", (dbg_info) ? dbg_info : "none");
100   g_error_free (err);
101   g_free (dbg_info);
102 }
103
104 static GstMessage *
105 run_pipeline (GstElement * pipeline)
106 {
107   GstBus *bus = gst_element_get_bus (GST_ELEMENT (pipeline));
108   GstMessage *msg;
109
110   gst_element_set_state (pipeline, GST_STATE_PLAYING);
111   msg = gst_bus_poll (bus, GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1);
112   gst_element_set_state (pipeline, GST_STATE_NULL);
113
114   gst_object_unref (bus);
115
116   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
117     dump_error (msg);
118
119   return msg;
120 }
121
122 static void
123 seek_pipeline (GstElement * pipeline, gdouble rate, GstClockTime start,
124     GstClockTime end)
125 {
126   /* Pause the pipeline, seek to the desired range / rate, wait for PAUSED again, then
127    * clear the tracking vars for start_ts / end_ts */
128   gst_element_set_state (pipeline, GST_STATE_PAUSED);
129   gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
130
131   /* specific end time not implemented: */
132   fail_unless (end == GST_CLOCK_TIME_NONE);
133
134   gst_element_seek (pipeline, rate, GST_FORMAT_TIME,
135       GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_SET, start,
136       GST_SEEK_TYPE_END, 0);
137
138   /* Wait for the pipeline to preroll again */
139   gst_element_get_state (pipeline, NULL, NULL, GST_CLOCK_TIME_NONE);
140
141   GST_LOG ("Seeked pipeline. Rate %f time range %" GST_TIME_FORMAT " to %"
142       GST_TIME_FORMAT, rate, GST_TIME_ARGS (start), GST_TIME_ARGS (end));
143
144   /* Clear tracking variables now that the seek is complete */
145   first_ts = last_ts = GST_CLOCK_TIME_NONE;
146   current_rate = rate;
147 };
148
149 static GstFlowReturn
150 receive_sample (GstAppSink * appsink, gpointer user_data G_GNUC_UNUSED)
151 {
152   GstSample *sample;
153   GstSegment *seg;
154   GstBuffer *buf;
155   GstClockTime start;
156   GstClockTime end;
157
158   g_signal_emit_by_name (appsink, "pull-sample", &sample);
159   fail_unless (sample != NULL);
160
161   seg = gst_sample_get_segment (sample);
162   fail_unless (seg != NULL);
163
164   buf = gst_sample_get_buffer (sample);
165   fail_unless (buf != NULL);
166
167   GST_LOG ("Got buffer %" GST_PTR_FORMAT, buf);
168
169   start = GST_BUFFER_PTS (buf);
170   end = start;
171
172   if (GST_CLOCK_TIME_IS_VALID (start))
173     start = gst_segment_to_stream_time (seg, GST_FORMAT_TIME, start);
174
175   if (GST_CLOCK_TIME_IS_VALID (end)) {
176     if (GST_BUFFER_DURATION_IS_VALID (buf))
177       end += GST_BUFFER_DURATION (buf);
178
179     end = gst_segment_to_stream_time (seg, GST_FORMAT_TIME, end);
180   }
181
182   GST_DEBUG ("Got buffer stream time %" GST_TIME_FORMAT " to %" GST_TIME_FORMAT,
183       GST_TIME_ARGS (start), GST_TIME_ARGS (end));
184
185   /* Check time is moving in the right direction */
186   if (current_rate > 0) {
187     if (GST_CLOCK_TIME_IS_VALID (first_ts))
188       fail_unless (start >= first_ts,
189           "Timestamps went backward during forward play, %" GST_TIME_FORMAT
190           " < %" GST_TIME_FORMAT, GST_TIME_ARGS (start),
191           GST_TIME_ARGS (first_ts));
192     if (GST_CLOCK_TIME_IS_VALID (last_ts))
193       fail_unless (end >= last_ts,
194           "Timestamps went backward during forward play, %" GST_TIME_FORMAT
195           " < %" GST_TIME_FORMAT, GST_TIME_ARGS (end), GST_TIME_ARGS (last_ts));
196   } else {
197     fail_unless (start <= first_ts,
198         "Timestamps went forward during reverse play, %" GST_TIME_FORMAT " > %"
199         GST_TIME_FORMAT, GST_TIME_ARGS (start), GST_TIME_ARGS (first_ts));
200     fail_unless (end <= last_ts,
201         "Timestamps went forward during reverse play, %" GST_TIME_FORMAT " > %"
202         GST_TIME_FORMAT, GST_TIME_ARGS (end), GST_TIME_ARGS (last_ts));
203   }
204
205   /* update the range of timestamps we've encountered */
206   if (!GST_CLOCK_TIME_IS_VALID (first_ts) || start < first_ts)
207     first_ts = start;
208   if (!GST_CLOCK_TIME_IS_VALID (last_ts) || end > last_ts)
209     last_ts = end;
210
211   gst_sample_unref (sample);
212
213   return GST_FLOW_OK;
214 }
215
216 static void
217 test_playback (const gchar * in_pattern, GstClockTime exp_first_time,
218     GstClockTime exp_last_time, gboolean test_reverse)
219 {
220   GstMessage *msg;
221   GstElement *pipeline;
222   GstElement *appsink;
223   GstElement *fakesink2;
224   GstAppSinkCallbacks callbacks = { NULL };
225   gchar *uri;
226
227   GST_DEBUG ("Playing back files matching %s", in_pattern);
228
229   pipeline = gst_element_factory_make ("playbin", NULL);
230   fail_if (pipeline == NULL);
231
232   appsink = gst_element_factory_make ("appsink", NULL);
233   fail_if (appsink == NULL);
234   g_object_set (G_OBJECT (appsink), "sync", FALSE, NULL);
235
236   g_object_set (G_OBJECT (pipeline), "video-sink", appsink, NULL);
237   fakesink2 = gst_element_factory_make ("fakesink", NULL);
238   fail_if (fakesink2 == NULL);
239   g_object_set (G_OBJECT (pipeline), "audio-sink", fakesink2, NULL);
240
241   uri = g_strdup_printf ("splitmux://%s", in_pattern);
242
243   g_object_set (G_OBJECT (pipeline), "uri", uri, NULL);
244   g_free (uri);
245
246   callbacks.new_sample = receive_sample;
247   gst_app_sink_set_callbacks (GST_APP_SINK (appsink), &callbacks, NULL, NULL);
248
249   /* test forwards */
250   seek_pipeline (pipeline, 1.0, 0, -1);
251   fail_unless (first_ts == GST_CLOCK_TIME_NONE);
252   msg = run_pipeline (pipeline);
253   fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
254   gst_message_unref (msg);
255
256   /* Check we saw the entire range of values */
257   fail_unless (first_ts == exp_first_time,
258       "Expected start of playback range %" GST_TIME_FORMAT ", got %"
259       GST_TIME_FORMAT, GST_TIME_ARGS (exp_first_time),
260       GST_TIME_ARGS (first_ts));
261   fail_unless (last_ts == exp_last_time,
262       "Expected end of playback range %" GST_TIME_FORMAT ", got %"
263       GST_TIME_FORMAT, GST_TIME_ARGS (exp_last_time), GST_TIME_ARGS (last_ts));
264
265   if (test_reverse) {
266     /* Test backwards */
267     seek_pipeline (pipeline, -1.0, 0, -1);
268     msg = run_pipeline (pipeline);
269     fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
270     gst_message_unref (msg);
271     /* Check we saw the entire range of values */
272     fail_unless (first_ts == exp_first_time,
273         "Expected start of playback range %" GST_TIME_FORMAT
274         ", got %" GST_TIME_FORMAT, GST_TIME_ARGS (exp_first_time),
275         GST_TIME_ARGS (first_ts));
276     fail_unless (last_ts == exp_last_time,
277         "Expected end of playback range %" GST_TIME_FORMAT
278         ", got %" GST_TIME_FORMAT, GST_TIME_ARGS (exp_last_time),
279         GST_TIME_ARGS (last_ts));
280   }
281
282   gst_object_unref (pipeline);
283 }
284
285 GST_START_TEST (test_splitmuxsrc)
286 {
287   gchar *in_pattern =
288       g_build_filename (GST_TEST_FILES_PATH, "splitvideo*.ogg", NULL);
289   test_playback (in_pattern, 0, 3 * GST_SECOND, TRUE);
290   g_free (in_pattern);
291 }
292
293 GST_END_TEST;
294
295 static gchar **
296 src_format_location_cb (GstElement * splitmuxsrc, gpointer user_data)
297 {
298   gchar **result = g_malloc0_n (4, sizeof (gchar *));
299   result[0] = g_build_filename (GST_TEST_FILES_PATH, "splitvideo00.ogg", NULL);
300   result[1] = g_build_filename (GST_TEST_FILES_PATH, "splitvideo01.ogg", NULL);
301   result[2] = g_build_filename (GST_TEST_FILES_PATH, "splitvideo02.ogg", NULL);
302   return result;
303 }
304
305 GST_START_TEST (test_splitmuxsrc_format_location)
306 {
307   GstMessage *msg;
308   GstElement *pipeline;
309   GstElement *src;
310   GError *error = NULL;
311
312   pipeline = gst_parse_launch ("splitmuxsrc name=splitsrc ! decodebin "
313       "! fakesink", &error);
314   g_assert_no_error (error);
315   fail_if (pipeline == NULL);
316
317   src = gst_bin_get_by_name (GST_BIN (pipeline), "splitsrc");
318   g_signal_connect (src, "format-location",
319       (GCallback) src_format_location_cb, NULL);
320   g_object_unref (src);
321
322   msg = run_pipeline (pipeline);
323
324   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
325     dump_error (msg);
326   fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
327   gst_message_unref (msg);
328   gst_object_unref (pipeline);
329 }
330
331 GST_END_TEST;
332
333 static gchar *
334 check_format_location (GstElement * object,
335     guint fragment_id, GstSample * first_sample)
336 {
337   GstBuffer *buf = gst_sample_get_buffer (first_sample);
338
339   /* Must have a buffer */
340   fail_if (buf == NULL);
341   GST_LOG ("New file - first buffer %" GST_TIME_FORMAT,
342       GST_TIME_ARGS (GST_BUFFER_TIMESTAMP (buf)));
343
344   return NULL;
345 }
346
347 GST_START_TEST (test_splitmuxsink)
348 {
349   GstMessage *msg;
350   GstElement *pipeline;
351   GstElement *sink;
352   GstPad *splitmux_sink_pad;
353   GstPad *enc_src_pad;
354   gchar *dest_pattern;
355   guint count;
356   gchar *in_pattern;
357
358   /* This pipeline has a small time cutoff - it should start a new file
359    * every GOP, ie 1 second */
360   pipeline =
361       gst_parse_launch
362       ("videotestsrc num-buffers=15 ! video/x-raw,width=80,height=64,framerate=5/1 ! videoconvert !"
363       " queue ! theoraenc keyframe-force=5 ! splitmuxsink name=splitsink "
364       " max-size-time=1000000 max-size-bytes=1000000 muxer=oggmux", NULL);
365   fail_if (pipeline == NULL);
366   sink = gst_bin_get_by_name (GST_BIN (pipeline), "splitsink");
367   fail_if (sink == NULL);
368   g_signal_connect (sink, "format-location-full",
369       (GCallback) check_format_location, NULL);
370   dest_pattern = g_build_filename (tmpdir, "out%05d.ogg", NULL);
371   g_object_set (G_OBJECT (sink), "location", dest_pattern, NULL);
372   g_free (dest_pattern);
373   g_object_unref (sink);
374
375   msg = run_pipeline (pipeline);
376
377   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
378     dump_error (msg);
379   fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
380   gst_message_unref (msg);
381
382   /* unlink manually and release request pad to ensure that we *can* do that
383    * - https://bugzilla.gnome.org/show_bug.cgi?id=753622 */
384   sink = gst_bin_get_by_name (GST_BIN (pipeline), "splitsink");
385   fail_if (sink == NULL);
386   splitmux_sink_pad = gst_element_get_static_pad (sink, "video");
387   fail_if (splitmux_sink_pad == NULL);
388   enc_src_pad = gst_pad_get_peer (splitmux_sink_pad);
389   fail_if (enc_src_pad == NULL);
390   fail_unless (gst_pad_unlink (enc_src_pad, splitmux_sink_pad));
391   gst_object_unref (enc_src_pad);
392   gst_element_release_request_pad (sink, splitmux_sink_pad);
393   gst_object_unref (splitmux_sink_pad);
394   /* at this point the pad must be released - try to find it again to verify */
395   splitmux_sink_pad = gst_element_get_static_pad (sink, "video");
396   fail_if (splitmux_sink_pad != NULL);
397   g_object_unref (sink);
398
399   gst_object_unref (pipeline);
400
401   count = count_files (tmpdir);
402   fail_unless (count == 3, "Expected 3 output files, got %d", count);
403
404   in_pattern = g_build_filename (tmpdir, "out*.ogg", NULL);
405   test_playback (in_pattern, 0, 3 * GST_SECOND, TRUE);
406   g_free (in_pattern);
407 }
408
409 GST_END_TEST;
410
411 GST_START_TEST (test_splitmuxsink_multivid)
412 {
413   GstMessage *msg;
414   GstElement *pipeline;
415   GstElement *sink;
416   gchar *dest_pattern;
417   guint count;
418   gchar *in_pattern;
419
420   /* This pipeline should start a new file every GOP, ie 1 second,
421    * driven by the primary video stream and with 2 auxiliary video streams */
422   pipeline =
423       gst_parse_launch
424       ("splitmuxsink name=splitsink "
425       " max-size-time=1000000 max-size-bytes=1000000 muxer=qtmux "
426       "videotestsrc num-buffers=15 ! video/x-raw,width=80,height=64,framerate=5/1 ! videoconvert !"
427       " queue ! vp8enc keyframe-max-dist=5 ! splitsink.video "
428       "videotestsrc num-buffers=15 pattern=snow ! video/x-raw,width=80,height=64,framerate=5/1 ! videoconvert !"
429       " queue ! vp8enc keyframe-max-dist=6 ! splitsink.video_aux_0 "
430       "videotestsrc num-buffers=15 pattern=ball ! video/x-raw,width=80,height=64,framerate=5/1 ! videoconvert !"
431       " queue ! vp8enc keyframe-max-dist=8 ! splitsink.video_aux_1 ", NULL);
432   fail_if (pipeline == NULL);
433   sink = gst_bin_get_by_name (GST_BIN (pipeline), "splitsink");
434   fail_if (sink == NULL);
435   g_signal_connect (sink, "format-location-full",
436       (GCallback) check_format_location, NULL);
437   dest_pattern = g_build_filename (tmpdir, "out%05d.m4v", NULL);
438   g_object_set (G_OBJECT (sink), "location", dest_pattern, NULL);
439   g_free (dest_pattern);
440   g_object_unref (sink);
441
442   msg = run_pipeline (pipeline);
443
444   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
445     dump_error (msg);
446   fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
447   gst_message_unref (msg);
448
449   gst_object_unref (pipeline);
450
451   count = count_files (tmpdir);
452   fail_unless (count == 3, "Expected 3 output files, got %d", count);
453
454   in_pattern = g_build_filename (tmpdir, "out*.m4v", NULL);
455   /* FIXME: Reverse playback works poorly with multiple video streams
456    * in qtdemux (at least, maybe other demuxers) at the time this was
457    * written, and causes test failures like buffers being output
458    * multiple times by qtdemux as it loops through GOPs. Disable that
459    * for now */
460   test_playback (in_pattern, 0, 3 * GST_SECOND, FALSE);
461   g_free (in_pattern);
462 }
463
464 GST_END_TEST;
465
466 GST_START_TEST (test_splitmuxsink_async)
467 {
468   GstMessage *msg;
469   GstElement *pipeline;
470   GstElement *sink;
471   GstPad *splitmux_sink_pad;
472   GstPad *enc_src_pad;
473   gchar *dest_pattern;
474   guint count;
475   gchar *in_pattern;
476
477   pipeline =
478       gst_parse_launch
479       ("videotestsrc num-buffers=15 ! video/x-raw,width=80,height=64,framerate=5/1 ! videoconvert !"
480       " queue ! theoraenc keyframe-force=5 ! splitmuxsink name=splitsink "
481       " max-size-time=1000000000 async-finalize=true "
482       " muxer-factory=matroskamux audiotestsrc num-buffers=15 samplesperbuffer=9600 ! "
483       " audio/x-raw,rate=48000 ! splitsink.audio_%u", NULL);
484   fail_if (pipeline == NULL);
485   sink = gst_bin_get_by_name (GST_BIN (pipeline), "splitsink");
486   fail_if (sink == NULL);
487   g_signal_connect (sink, "format-location-full",
488       (GCallback) check_format_location, NULL);
489   dest_pattern = g_build_filename (tmpdir, "matroska%05d.mkv", NULL);
490   g_object_set (G_OBJECT (sink), "location", dest_pattern, NULL);
491   g_free (dest_pattern);
492   g_object_unref (sink);
493
494   msg = run_pipeline (pipeline);
495
496   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
497     dump_error (msg);
498   fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
499   gst_message_unref (msg);
500
501   /* unlink manually and release request pad to ensure that we *can* do that
502    * - https://bugzilla.gnome.org/show_bug.cgi?id=753622 */
503   sink = gst_bin_get_by_name (GST_BIN (pipeline), "splitsink");
504   fail_if (sink == NULL);
505   splitmux_sink_pad = gst_element_get_static_pad (sink, "video");
506   fail_if (splitmux_sink_pad == NULL);
507   enc_src_pad = gst_pad_get_peer (splitmux_sink_pad);
508   fail_if (enc_src_pad == NULL);
509   fail_unless (gst_pad_unlink (enc_src_pad, splitmux_sink_pad));
510   gst_object_unref (enc_src_pad);
511   gst_element_release_request_pad (sink, splitmux_sink_pad);
512   gst_object_unref (splitmux_sink_pad);
513   /* at this point the pad must be released - try to find it again to verify */
514   splitmux_sink_pad = gst_element_get_static_pad (sink, "video");
515   fail_if (splitmux_sink_pad != NULL);
516   g_object_unref (sink);
517
518   gst_object_unref (pipeline);
519
520   count = count_files (tmpdir);
521   fail_unless (count == 3, "Expected 3 output files, got %d", count);
522
523   in_pattern = g_build_filename (tmpdir, "matroska*.mkv", NULL);
524   test_playback (in_pattern, 0, 3 * GST_SECOND, TRUE);
525   g_free (in_pattern);
526 }
527
528 GST_END_TEST;
529
530 static GstPadProbeReturn
531 intercept_stream_start (GstPad * pad, GstPadProbeInfo * info,
532     gpointer user_data)
533 {
534   GstEvent *event = gst_pad_probe_info_get_event (info);
535
536   if (GST_EVENT_TYPE (event) == GST_EVENT_STREAM_START) {
537     GstStreamFlags flags;
538     event = gst_event_make_writable (event);
539     gst_event_parse_stream_flags (event, &flags);
540     gst_event_set_stream_flags (event, flags | GST_STREAM_FLAG_SPARSE);
541     GST_PAD_PROBE_INFO_DATA (info) = event;
542   }
543
544   return GST_PAD_PROBE_OK;
545 }
546
547 static GstFlowReturn
548 new_sample_verify_continuous_timestamps (GstAppSink * appsink,
549     gpointer user_data)
550 {
551   GstSample *sample;
552   GstBuffer *buffer;
553   GstClockTime *prev_ts = user_data;
554   GstClockTime new_ts;
555
556   sample = gst_app_sink_pull_sample (appsink);
557   buffer = gst_sample_get_buffer (sample);
558
559   new_ts = GST_BUFFER_PTS (buffer);
560   if (GST_CLOCK_TIME_IS_VALID (*prev_ts)) {
561     fail_unless (*prev_ts < new_ts,
562         "%s: prev_ts (%" GST_TIME_FORMAT ") >= new_ts (%" GST_TIME_FORMAT ")",
563         GST_OBJECT_NAME (appsink), GST_TIME_ARGS (*prev_ts),
564         GST_TIME_ARGS (new_ts));
565   }
566
567   *prev_ts = new_ts;
568   gst_sample_unref (sample);
569   return GST_FLOW_OK;
570 }
571
572 static GstFlowReturn
573 new_sample_verify_1sec_offset (GstAppSink * appsink, gpointer user_data)
574 {
575   GstSample *sample;
576   GstBuffer *buffer;
577   GstClockTime *prev_ts = user_data;
578   GstClockTime new_ts;
579
580   sample = gst_app_sink_pull_sample (appsink);
581   buffer = gst_sample_get_buffer (sample);
582
583   new_ts = GST_BUFFER_PTS (buffer);
584   if (GST_CLOCK_TIME_IS_VALID (*prev_ts)) {
585     fail_unless (new_ts > (*prev_ts + 900 * GST_MSECOND),
586         "%s: prev_ts (%" GST_TIME_FORMAT ") + 0.9s >= new_ts (%"
587         GST_TIME_FORMAT ")", GST_OBJECT_NAME (appsink),
588         GST_TIME_ARGS (*prev_ts), GST_TIME_ARGS (new_ts));
589   }
590
591   *prev_ts = new_ts;
592   gst_sample_unref (sample);
593   return GST_FLOW_OK;
594 }
595
596 /* https://bugzilla.gnome.org/show_bug.cgi?id=761086 */
597 GST_START_TEST (test_splitmuxsrc_sparse_streams)
598 {
599   GstElement *pipeline;
600   GstElement *element;
601   gchar *dest_pattern;
602   GstElement *appsrc;
603   GstPad *appsrc_src;
604   GstBus *bus;
605   GstMessage *msg;
606   gint i;
607
608   /* generate files */
609
610   /* in this test, we have 5sec of data with files split at 1sec intervals */
611   pipeline =
612       gst_parse_launch
613       ("videotestsrc num-buffers=75 !"
614       "  video/x-raw,width=80,height=64,framerate=15/1 !"
615       "  theoraenc keyframe-force=5 ! splitmuxsink name=splitsink"
616       "    max-size-time=1000000000 muxer=matroskamux"
617       " audiotestsrc num-buffers=100 samplesperbuffer=1024 !"
618       "  audio/x-raw,rate=20000 ! vorbisenc ! splitsink.audio_%u"
619       " appsrc name=appsrc format=time caps=text/x-raw,format=utf8 !"
620       "  splitsink.subtitle_%u", NULL);
621   fail_if (pipeline == NULL);
622
623   element = gst_bin_get_by_name (GST_BIN (pipeline), "splitsink");
624   fail_if (element == NULL);
625   dest_pattern = g_build_filename (tmpdir, "out%05d.ogg", NULL);
626   g_object_set (G_OBJECT (element), "location", dest_pattern, NULL);
627   g_clear_pointer (&dest_pattern, g_free);
628   g_clear_object (&element);
629
630   appsrc = gst_bin_get_by_name (GST_BIN (pipeline), "appsrc");
631   fail_if (appsrc == NULL);
632
633   /* add the SPARSE flag on the stream-start event of the subtitle stream */
634   appsrc_src = gst_element_get_static_pad (appsrc, "src");
635   fail_if (appsrc_src == NULL);
636   gst_pad_add_probe (appsrc_src, GST_PAD_PROBE_TYPE_EVENT_DOWNSTREAM,
637       intercept_stream_start, NULL, NULL);
638   g_clear_object (&appsrc_src);
639
640   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
641
642   gst_element_set_state (pipeline, GST_STATE_PLAYING);
643
644   /* push subtitles, one per second, starting from t=100ms */
645   for (i = 0; i < 5; i++) {
646     GstBuffer *buffer = gst_buffer_new_allocate (NULL, 5, NULL);
647     GstMapInfo info;
648
649     gst_buffer_map (buffer, &info, GST_MAP_WRITE);
650     strcpy ((char *) info.data, "test");
651     gst_buffer_unmap (buffer, &info);
652
653     GST_BUFFER_PTS (buffer) = i * GST_SECOND + 100 * GST_MSECOND;
654     GST_BUFFER_DTS (buffer) = GST_BUFFER_PTS (buffer);
655
656     fail_if (gst_app_src_push_buffer (GST_APP_SRC (appsrc), buffer)
657         != GST_FLOW_OK);
658   }
659   fail_if (gst_app_src_end_of_stream (GST_APP_SRC (appsrc)) != GST_FLOW_OK);
660
661   msg = gst_bus_timed_pop_filtered (bus, 5 * GST_SECOND, GST_MESSAGE_EOS);
662   g_clear_pointer (&msg, gst_message_unref);
663
664   gst_element_set_state (pipeline, GST_STATE_NULL);
665
666   g_clear_object (&appsrc);
667   g_clear_object (&bus);
668   g_clear_object (&pipeline);
669
670   /* read and verify */
671
672   pipeline =
673       gst_parse_launch
674       ("splitmuxsrc name=splitsrc"
675       " splitsrc. ! theoradec ! appsink name=vsink sync=false emit-signals=true"
676       " splitsrc. ! vorbisdec ! appsink name=asink sync=false emit-signals=true"
677       " splitsrc. ! text/x-raw ! appsink name=tsink sync=false emit-signals=true",
678       NULL);
679   fail_if (pipeline == NULL);
680
681   element = gst_bin_get_by_name (GST_BIN (pipeline), "splitsrc");
682   fail_if (element == NULL);
683   dest_pattern = g_build_filename (tmpdir, "out*.ogg", NULL);
684   g_object_set (G_OBJECT (element), "location", dest_pattern, NULL);
685   g_clear_pointer (&dest_pattern, g_free);
686   g_clear_object (&element);
687
688   {
689     GstClockTime vsink_prev_ts = GST_CLOCK_TIME_NONE;
690     GstClockTime asink_prev_ts = GST_CLOCK_TIME_NONE;
691     GstClockTime tsink_prev_ts = GST_CLOCK_TIME_NONE;
692
693     /* verify that timestamps are continuously increasing for audio + video.
694      * if we hit bug 761086, timestamps will jump about -900ms after switching
695      * to a new part, because this is the difference between the last subtitle
696      * pts and the last audio/video pts */
697     element = gst_bin_get_by_name (GST_BIN (pipeline), "vsink");
698     g_signal_connect (element, "new-sample",
699         (GCallback) new_sample_verify_continuous_timestamps, &vsink_prev_ts);
700     g_clear_object (&element);
701
702     element = gst_bin_get_by_name (GST_BIN (pipeline), "asink");
703     g_signal_connect (element, "new-sample",
704         (GCallback) new_sample_verify_continuous_timestamps, &asink_prev_ts);
705     g_clear_object (&element);
706
707     /* also verify that subtitle timestamps are increasing by about 1s.
708      * if we hit bug 761086, timestamps will increase by exactly 100ms instead,
709      * because this is the relative difference between a part's start time
710      * (remember a new part starts every 1sec) and the subtitle's pts in that
711      * part, which will be added to the max_ts of the previous part, which
712      * equals the last subtitle's pts (and should not!) */
713     element = gst_bin_get_by_name (GST_BIN (pipeline), "tsink");
714     g_signal_connect (element, "new-sample",
715         (GCallback) new_sample_verify_1sec_offset, &tsink_prev_ts);
716     g_clear_object (&element);
717
718     msg = run_pipeline (pipeline);
719   }
720
721   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
722     dump_error (msg);
723   fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
724
725   g_clear_pointer (&msg, gst_message_unref);
726   g_clear_object (&pipeline);
727 }
728
729 GST_END_TEST;
730
731 struct CapsChangeData
732 {
733   guint count;
734   GstElement *cf;
735 };
736
737 static GstPadProbeReturn
738 switch_caps (GstPad * pad, GstPadProbeInfo * info, gpointer user_data)
739 {
740   struct CapsChangeData *data = (struct CapsChangeData *) (user_data);
741
742   if (data->count == 4) {
743     GST_INFO ("Saw 5 buffers to the encoder. Switching caps");
744     gst_util_set_object_arg (G_OBJECT (data->cf), "caps",
745         "video/x-raw,width=160,height=128,framerate=10/1");
746   }
747   data->count++;
748   return GST_PAD_PROBE_OK;
749 }
750
751 GST_START_TEST (test_splitmuxsrc_caps_change)
752 {
753   GstMessage *msg;
754   GstElement *pipeline;
755   GstElement *sink;
756   GstElement *cf;
757   GstPad *sinkpad;
758   gchar *dest_pattern;
759   guint count;
760   gchar *in_pattern;
761   struct CapsChangeData data;
762
763   /* This test creates a new file only by changing the caps, which
764    * qtmux will reject (for now - if qtmux starts supporting caps
765    * changes, this test will break and need fixing/disabling */
766   pipeline =
767       gst_parse_launch
768       ("videotestsrc num-buffers=10 !"
769       "  capsfilter name=c caps=video/x-raw,width=80,height=64,framerate=10/1 !"
770       "  jpegenc ! splitmuxsink name=splitsink muxer=qtmux", NULL);
771   fail_if (pipeline == NULL);
772   sink = gst_bin_get_by_name (GST_BIN (pipeline), "splitsink");
773   fail_if (sink == NULL);
774   g_signal_connect (sink, "format-location-full",
775       (GCallback) check_format_location, NULL);
776   dest_pattern = g_build_filename (tmpdir, "out%05d.mp4", NULL);
777   g_object_set (G_OBJECT (sink), "location", dest_pattern, NULL);
778   g_free (dest_pattern);
779   g_object_unref (sink);
780
781   cf = gst_bin_get_by_name (GST_BIN (pipeline), "c");
782   sinkpad = gst_element_get_static_pad (cf, "sink");
783
784   data.cf = cf;
785   data.count = 0;
786
787   gst_pad_add_probe (sinkpad, GST_PAD_PROBE_TYPE_BUFFER,
788       switch_caps, &data, NULL);
789
790   gst_object_unref (sinkpad);
791   gst_object_unref (cf);
792
793   msg = run_pipeline (pipeline);
794
795   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
796     dump_error (msg);
797   fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
798   gst_message_unref (msg);
799
800   gst_object_unref (pipeline);
801
802   count = count_files (tmpdir);
803   fail_unless (count == 2, "Expected 2 output files, got %d", count);
804
805   in_pattern = g_build_filename (tmpdir, "out*.mp4", NULL);
806   test_playback (in_pattern, 0, GST_SECOND, TRUE);
807   g_free (in_pattern);
808 }
809
810 GST_END_TEST;
811
812 GST_START_TEST (test_splitmuxsrc_robust_mux)
813 {
814   GstMessage *msg;
815   GstElement *pipeline;
816   GstElement *sink;
817   gchar *dest_pattern;
818   gchar *in_pattern;
819
820   /* This test creates a new file only by changing the caps, which
821    * qtmux will reject (for now - if qtmux starts supporting caps
822    * changes, this test will break and need fixing/disabling */
823   pipeline =
824       gst_parse_launch
825       ("videotestsrc num-buffers=10 !"
826       "  video/x-raw,width=80,height=64,framerate=10/1 !"
827       "  jpegenc ! splitmuxsink name=splitsink muxer=\"qtmux reserved-bytes-per-sec=200 reserved-moov-update-period=100000000 \" max-size-time=500000000 use-robust-muxing=true",
828       NULL);
829   fail_if (pipeline == NULL);
830   sink = gst_bin_get_by_name (GST_BIN (pipeline), "splitsink");
831   fail_if (sink == NULL);
832   g_signal_connect (sink, "format-location-full",
833       (GCallback) check_format_location, NULL);
834   dest_pattern = g_build_filename (tmpdir, "out%05d.mp4", NULL);
835   g_object_set (G_OBJECT (sink), "location", dest_pattern, NULL);
836   g_free (dest_pattern);
837   g_object_unref (sink);
838
839   msg = run_pipeline (pipeline);
840
841   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
842     dump_error (msg);
843   fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
844   gst_message_unref (msg);
845
846   gst_object_unref (pipeline);
847
848   /* Unlike other tests, we don't check an explicit file size, because the overflow detection
849    * can be racy (depends on exactly when buffers get handed to the muxer and when it updates the
850    * reserved duration property. All we care about is that the muxing didn't fail because space ran out */
851
852   in_pattern = g_build_filename (tmpdir, "out*.mp4", NULL);
853   test_playback (in_pattern, 0, GST_SECOND, TRUE);
854   g_free (in_pattern);
855 }
856
857 GST_END_TEST;
858
859 /* For verifying bug https://bugzilla.gnome.org/show_bug.cgi?id=762893 */
860 GST_START_TEST (test_splitmuxsink_reuse_simple)
861 {
862   GstElement *sink;
863   GstPad *pad;
864
865   sink = gst_element_factory_make ("splitmuxsink", NULL);
866   pad = gst_element_get_request_pad (sink, "video");
867   fail_unless (pad != NULL);
868   g_object_set (sink, "location", "/dev/null", NULL);
869
870   fail_unless (gst_element_set_state (sink,
871           GST_STATE_PLAYING) == GST_STATE_CHANGE_ASYNC);
872   fail_unless (gst_element_set_state (sink,
873           GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
874   fail_unless (gst_element_set_state (sink,
875           GST_STATE_PLAYING) == GST_STATE_CHANGE_ASYNC);
876   fail_unless (gst_element_set_state (sink,
877           GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
878
879   gst_element_release_request_pad (sink, pad);
880   gst_object_unref (pad);
881   gst_object_unref (sink);
882 }
883
884 GST_END_TEST;
885
886 GST_START_TEST (test_splitmuxsink_muxer_pad_map)
887 {
888   GstElement *sink, *muxer;
889   GstPad *muxpad;
890   GstPad *pad1 = NULL, *pad2 = NULL;
891   GstStructure *pad_map;
892
893   pad_map = gst_structure_new ("x-pad-map",
894       "video", G_TYPE_STRING, "video_100",
895       "audio_0", G_TYPE_STRING, "audio_101", NULL);
896
897   muxer = gst_element_factory_make ("qtmux", NULL);
898   fail_if (muxer == NULL);
899   sink = gst_element_factory_make ("splitmuxsink", NULL);
900   fail_if (sink == NULL);
901
902   g_object_set (sink, "muxer", muxer, "muxer-pad-map", pad_map, NULL);
903   gst_structure_free (pad_map);
904
905   pad1 = gst_element_get_request_pad (sink, "video");
906   fail_unless (g_str_equal ("video", GST_PAD_NAME (pad1)));
907   muxpad = gst_element_get_static_pad (muxer, "video_100");
908   fail_unless (muxpad != NULL);
909   gst_object_unref (muxpad);
910
911   pad2 = gst_element_get_request_pad (sink, "audio_0");
912   fail_unless (g_str_equal ("audio_0", GST_PAD_NAME (pad2)));
913   muxpad = gst_element_get_static_pad (muxer, "audio_101");
914   fail_unless (muxpad != NULL);
915   gst_object_unref (muxpad);
916
917   g_object_set (sink, "location", "/dev/null", NULL);
918
919   fail_unless (gst_element_set_state (sink,
920           GST_STATE_PLAYING) == GST_STATE_CHANGE_ASYNC);
921   fail_unless (gst_element_set_state (sink,
922           GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
923
924   gst_element_release_request_pad (sink, pad1);
925   gst_object_unref (pad1);
926   gst_element_release_request_pad (sink, pad2);
927   gst_object_unref (pad2);
928   gst_object_unref (sink);
929 }
930
931 GST_END_TEST;
932
933 static void
934 splitmuxsink_split_by_keyframe (gboolean send_keyframe_request,
935     guint max_size_time_sec, guint encoder_key_interval_sec)
936 {
937   GstMessage *msg;
938   GstElement *pipeline;
939   GstElement *sink;
940   gchar *pipeline_str;
941   gchar *dest_pattern;
942   guint count;
943   guint expected_count;
944   gchar *in_pattern;
945
946   pipeline_str = g_strdup_printf ("splitmuxsink name=splitsink "
947       "max-size-time=%" G_GUINT64_FORMAT
948       " send-keyframe-requests=%s muxer=qtmux "
949       "videotestsrc num-buffers=30 ! video/x-raw,width=80,height=64,framerate=5/1 "
950       "! videoconvert ! queue ! vp8enc keyframe-max-dist=%d ! splitsink.video ",
951       max_size_time_sec * GST_SECOND, send_keyframe_request ? "true" : "false",
952       encoder_key_interval_sec * 5);
953
954   pipeline = gst_parse_launch (pipeline_str, NULL);
955   g_free (pipeline_str);
956
957   fail_if (pipeline == NULL);
958   sink = gst_bin_get_by_name (GST_BIN (pipeline), "splitsink");
959   fail_if (sink == NULL);
960   g_signal_connect (sink, "format-location-full",
961       (GCallback) check_format_location, NULL);
962   dest_pattern = g_build_filename (tmpdir, "out%05d.m4v", NULL);
963   g_object_set (G_OBJECT (sink), "location", dest_pattern, NULL);
964   g_free (dest_pattern);
965   g_object_unref (sink);
966
967   msg = run_pipeline (pipeline);
968
969   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
970     dump_error (msg);
971   fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
972   gst_message_unref (msg);
973
974   gst_object_unref (pipeline);
975
976   count = count_files (tmpdir);
977   expected_count = 6 / max_size_time_sec;
978   fail_unless (count == expected_count,
979       "Expected %d output files, got %d", expected_count, count);
980
981   in_pattern = g_build_filename (tmpdir, "out*.m4v", NULL);
982   /* FIXME: Reverse playback works poorly with multiple video streams
983    * in qtdemux (at least, maybe other demuxers) at the time this was
984    * written, and causes test failures like buffers being output
985    * multiple times by qtdemux as it loops through GOPs. Disable that
986    * for now */
987   test_playback (in_pattern, 0, 6 * GST_SECOND, FALSE);
988   g_free (in_pattern);
989 }
990
991 GST_START_TEST (test_splitmuxsink_without_keyframe_request)
992 {
993   /* This encoding option is intended to produce keyframe per 1 seconds
994    * but splitmuxsink will split file per 2 second without keyframe request */
995   splitmuxsink_split_by_keyframe (FALSE, 2, 1);
996 }
997
998 GST_END_TEST;
999
1000 GST_START_TEST (test_splitmuxsink_keyframe_request)
1001 {
1002   /* This encoding option is intended to produce keyframe per 2 seconds
1003    * and splitmuxsink will request keyframe per 2 seconds as well.
1004    * This should produce 2 seconds long files */
1005   splitmuxsink_split_by_keyframe (TRUE, 2, 2);
1006 }
1007
1008 GST_END_TEST;
1009
1010 GST_START_TEST (test_splitmuxsink_keyframe_request_more)
1011 {
1012   /* This encoding option is intended to produce keyframe per 2 seconds
1013    * but splitmuxsink will request keyframe per 1 second. This should produce
1014    * 1 second long files */
1015   splitmuxsink_split_by_keyframe (TRUE, 1, 2);
1016 }
1017
1018 GST_END_TEST;
1019
1020 GST_START_TEST (test_splitmuxsink_keyframe_request_less)
1021 {
1022   /* This encoding option is intended to produce keyframe per 1 second
1023    * but splitmuxsink will request keyframe per 2 seconds. This should produce
1024    * 2 seconds long files */
1025   splitmuxsink_split_by_keyframe (TRUE, 2, 1);
1026 }
1027
1028 GST_END_TEST;
1029
1030 static void
1031 splitmuxsink_split_by_keyframe_timecode (gboolean send_keyframe_request,
1032     const gchar * maxsize_timecode_string, guint maxsize_timecode_in_sec,
1033     guint encoder_key_interval_sec)
1034 {
1035   GstMessage *msg;
1036   GstElement *pipeline;
1037   GstElement *sink;
1038   gchar *pipeline_str;
1039   gchar *dest_pattern;
1040   guint count;
1041   guint expected_count;
1042   gchar *in_pattern;
1043
1044   pipeline_str = g_strdup_printf ("splitmuxsink name=splitsink "
1045       "max-size-timecode=%s"
1046       " send-keyframe-requests=%s muxer=qtmux "
1047       "videotestsrc num-buffers=30 ! video/x-raw,width=80,height=64,framerate=5/1 "
1048       "! videoconvert ! timecodestamper ! queue ! vp8enc keyframe-max-dist=%d ! splitsink.video ",
1049       maxsize_timecode_string, send_keyframe_request ? "true" : "false",
1050       encoder_key_interval_sec ? encoder_key_interval_sec * 5 : 1);
1051
1052   pipeline = gst_parse_launch (pipeline_str, NULL);
1053   g_free (pipeline_str);
1054
1055   fail_if (pipeline == NULL);
1056   sink = gst_bin_get_by_name (GST_BIN (pipeline), "splitsink");
1057   fail_if (sink == NULL);
1058   g_signal_connect (sink, "format-location-full",
1059       (GCallback) check_format_location, NULL);
1060   dest_pattern = g_build_filename (tmpdir, "out%05d.m4v", NULL);
1061   g_object_set (G_OBJECT (sink), "location", dest_pattern, NULL);
1062   g_free (dest_pattern);
1063   g_object_unref (sink);
1064
1065   msg = run_pipeline (pipeline);
1066
1067   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR)
1068     dump_error (msg);
1069   fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS);
1070   gst_message_unref (msg);
1071
1072   gst_object_unref (pipeline);
1073
1074   count = count_files (tmpdir);
1075   expected_count = (6 / maxsize_timecode_in_sec) +
1076       (6 % maxsize_timecode_in_sec ? 1 : 0);
1077   fail_unless (count == expected_count,
1078       "Expected %d output files, got %d", expected_count, count);
1079
1080   in_pattern = g_build_filename (tmpdir, "out*.m4v", NULL);
1081   /* FIXME: Reverse playback works poorly with multiple video streams
1082    * in qtdemux (at least, maybe other demuxers) at the time this was
1083    * written, and causes test failures like buffers being output
1084    * multiple times by qtdemux as it loops through GOPs. Disable that
1085    * for now */
1086   test_playback (in_pattern, 0, 6 * GST_SECOND, FALSE);
1087   g_free (in_pattern);
1088 }
1089
1090 GST_START_TEST (test_splitmuxsink_keyframe_request_timecode)
1091 {
1092   /* This encoding option is intended to produce keyframe per 1 second
1093    * but splitmuxsink will request keyframe per 2 seconds. This should produce
1094    * 2 seconds long files */
1095   splitmuxsink_split_by_keyframe_timecode (TRUE, "00:00:02:00", 2, 1);
1096 }
1097
1098 GST_END_TEST;
1099
1100 GST_START_TEST
1101     (test_splitmuxsink_keyframe_request_timecode_trailing_small_segment) {
1102   /* This encoding option is intended to produce keyframe per 1 second
1103    * but splitmuxsink will request keyframe per 4 seconds. This should produce
1104    * 4 seconds long files */
1105   splitmuxsink_split_by_keyframe_timecode (TRUE, "00:00:04:00", 4, 1);
1106 }
1107
1108 GST_END_TEST;
1109
1110 GST_START_TEST (test_splitmuxsink_keyframe_request_timecode_all_intra)
1111 {
1112   /* This encoding option is intended to produce keyframe for every frame.
1113    * This should produce 1 second long files */
1114   splitmuxsink_split_by_keyframe_timecode (TRUE, "00:00:01:00", 1, 0);
1115 }
1116
1117 GST_END_TEST;
1118
1119 static Suite *
1120 splitmux_suite (void)
1121 {
1122   Suite *s = suite_create ("splitmux");
1123   TCase *tc_chain = tcase_create ("general");
1124   TCase *tc_chain_basic = tcase_create ("basic");
1125   TCase *tc_chain_complex = tcase_create ("complex");
1126   TCase *tc_chain_mp4_jpeg = tcase_create ("caps_change");
1127   gboolean have_theora, have_ogg, have_vorbis, have_matroska, have_qtmux,
1128       have_jpeg, have_vp8, have_timecodestamper;
1129
1130   /* we assume that if encoder/muxer are there, decoder/demuxer will be a well */
1131   have_theora = gst_registry_check_feature_version (gst_registry_get (),
1132       "theoraenc", GST_VERSION_MAJOR, GST_VERSION_MINOR, 0);
1133   have_ogg = gst_registry_check_feature_version (gst_registry_get (),
1134       "oggmux", GST_VERSION_MAJOR, GST_VERSION_MINOR, 0);
1135   have_vorbis = gst_registry_check_feature_version (gst_registry_get (),
1136       "vorbisenc", GST_VERSION_MAJOR, GST_VERSION_MINOR, 0);
1137   have_matroska = gst_registry_check_feature_version (gst_registry_get (),
1138       "matroskamux", GST_VERSION_MAJOR, GST_VERSION_MINOR, 0);
1139   have_qtmux = gst_registry_check_feature_version (gst_registry_get (),
1140       "qtmux", GST_VERSION_MAJOR, GST_VERSION_MINOR, 0);
1141   have_jpeg = gst_registry_check_feature_version (gst_registry_get (),
1142       "jpegenc", GST_VERSION_MAJOR, GST_VERSION_MINOR, 0);
1143   have_vp8 = gst_registry_check_feature_version (gst_registry_get (),
1144       "vp8enc", GST_VERSION_MAJOR, GST_VERSION_MINOR, 0);
1145   have_timecodestamper =
1146       gst_registry_check_feature_version (gst_registry_get (),
1147       "timecodestamper", GST_VERSION_MAJOR, GST_VERSION_MINOR, 0);
1148
1149   suite_add_tcase (s, tc_chain);
1150   suite_add_tcase (s, tc_chain_basic);
1151   suite_add_tcase (s, tc_chain_complex);
1152   suite_add_tcase (s, tc_chain_mp4_jpeg);
1153
1154   tcase_add_test (tc_chain_basic, test_splitmuxsink_reuse_simple);
1155
1156   if (have_theora && have_ogg) {
1157     tcase_add_checked_fixture (tc_chain, tempdir_setup, tempdir_cleanup);
1158
1159     tcase_add_test (tc_chain, test_splitmuxsrc);
1160     tcase_add_test (tc_chain, test_splitmuxsrc_format_location);
1161     tcase_add_test (tc_chain, test_splitmuxsink);
1162
1163     if (have_matroska && have_vorbis) {
1164       tcase_add_checked_fixture (tc_chain_complex, tempdir_setup,
1165           tempdir_cleanup);
1166
1167       tcase_add_test (tc_chain_complex, test_splitmuxsrc_sparse_streams);
1168       tcase_add_test (tc_chain, test_splitmuxsink_async);
1169     } else {
1170       GST_INFO ("Skipping tests, missing plugins: matroska and/or vorbis");
1171     }
1172   } else {
1173     GST_INFO ("Skipping tests, missing plugins: theora and/or ogg");
1174   }
1175
1176
1177   if (have_qtmux && have_jpeg) {
1178     tcase_add_checked_fixture (tc_chain_mp4_jpeg, tempdir_setup,
1179         tempdir_cleanup);
1180     tcase_add_test (tc_chain_mp4_jpeg, test_splitmuxsrc_caps_change);
1181     tcase_add_test (tc_chain_mp4_jpeg, test_splitmuxsrc_robust_mux);
1182     tcase_add_test (tc_chain_mp4_jpeg, test_splitmuxsink_muxer_pad_map);
1183   } else {
1184     GST_INFO ("Skipping tests, missing plugins: jpegenc or mp4mux");
1185   }
1186
1187   if (have_qtmux && have_vp8) {
1188     tcase_add_test (tc_chain, test_splitmuxsink_multivid);
1189     tcase_add_test (tc_chain, test_splitmuxsink_without_keyframe_request);
1190     tcase_add_test (tc_chain, test_splitmuxsink_keyframe_request);
1191     tcase_add_test (tc_chain, test_splitmuxsink_keyframe_request_more);
1192     tcase_add_test (tc_chain, test_splitmuxsink_keyframe_request_less);
1193   } else {
1194     GST_INFO ("Skipping tests, missing plugins: vp8enc or mp4mux");
1195   }
1196
1197   if (have_qtmux && have_vp8 && have_timecodestamper) {
1198     tcase_add_test (tc_chain, test_splitmuxsink_keyframe_request_timecode);
1199     tcase_add_test (tc_chain,
1200         test_splitmuxsink_keyframe_request_timecode_trailing_small_segment);
1201     tcase_add_test (tc_chain,
1202         test_splitmuxsink_keyframe_request_timecode_all_intra);
1203   } else {
1204     GST_INFO
1205         ("Skipping tests, missing plugins: vp8enc, mp4mux, or timecodestamper");
1206   }
1207
1208   return s;
1209 }
1210
1211 GST_CHECK_MAIN (splitmux);