• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  *
3  * unit test for qtmux
4  *
5  * Copyright (C) <2008> Mark Nauwelaerts <mnauw@users.sf.net>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26 
27 #include <glib/gstdio.h>
28 
29 #include <gst/check/gstcheck.h>
30 #include <gst/check/gstharness.h>
31 #include <gst/pbutils/encoding-profile.h>
32 
33 /* For ease of programming we use globals to keep refs for our floating
34  * src and sink pads we create; otherwise we always have to do get_pad,
35  * get_peer, and then remove references in every test function */
36 static GstPad *mysrcpad, *mysinkpad;
37 
38 #define VIDEO_RAW_CAPS_STRING "video/x-raw"
39 
40 #define AUDIO_CAPS_STRING "audio/mpeg, " \
41                         "mpegversion = (int) 1, " \
42                         "layer = (int) 3, " \
43                         "channels = (int) 2, " \
44                         "rate = (int) 48000"
45 
46 #define AUDIO_AAC_TMPL_CAPS_STRING "audio/mpeg, " \
47                                    "mpegversion=(int)4, " \
48                                    "channels=(int)1, " \
49                                    "rate=(int)44100, " \
50                                    "stream-format=(string)raw, " \
51                                    "level=(string)2, " \
52                                    "base-profile=(string)lc, " \
53                                    "profile=(string)lc"
54 /* codec_data shouldn't be in the template caps, only in the actual caps */
55 #define AUDIO_AAC_CAPS_STRING AUDIO_AAC_TMPL_CAPS_STRING \
56                               ", codec_data=(buffer)1208"
57 
58 #define VIDEO_CAPS_STRING "video/mpeg, " \
59                            "mpegversion = (int) 4, " \
60                            "systemstream = (boolean) false, " \
61                            "width = (int) 384, " \
62                            "height = (int) 288, " \
63                            "framerate = (fraction) 25/1"
64 
65 #define VIDEO_TMPL_CAPS_H264_STRING "video/x-h264, " \
66                                     "width=(int)320, " \
67                                     "height=(int)240, " \
68                                     "framerate=(fraction)30/1, " \
69                                     "pixel-aspect-ratio=(fraction)1/1, " \
70                                     "stream-format=(string)avc, " \
71                                     "alignment=(string)au, " \
72                                     "level=(string)2, " \
73                                     "profile=(string)high"
74 /* codec_data shouldn't be in the template caps, only in the actual caps */
75 #define VIDEO_CAPS_H264_STRING VIDEO_TMPL_CAPS_H264_STRING \
76                                ", codec_data=(buffer)01640014ffe1001867640014a" \
77                                    "cd94141fb0110000003001773594000f14299600" \
78                                    "1000568ebecb22c"
79 
80 static GstStaticPadTemplate sinktemplate = GST_STATIC_PAD_TEMPLATE ("sink",
81     GST_PAD_SINK,
82     GST_PAD_ALWAYS,
83     GST_STATIC_CAPS ("video/quicktime"));
84 
85 static GstStaticPadTemplate srcvideotemplate = GST_STATIC_PAD_TEMPLATE ("src",
86     GST_PAD_SRC,
87     GST_PAD_ALWAYS,
88     GST_STATIC_CAPS (VIDEO_CAPS_STRING));
89 
90 static GstStaticPadTemplate srcvideoh264template =
91 GST_STATIC_PAD_TEMPLATE ("src",
92     GST_PAD_SRC,
93     GST_PAD_ALWAYS,
94     GST_STATIC_CAPS (VIDEO_TMPL_CAPS_H264_STRING));
95 
96 static GstStaticPadTemplate srcvideorawtemplate =
97 GST_STATIC_PAD_TEMPLATE ("src",
98     GST_PAD_SRC,
99     GST_PAD_ALWAYS,
100     GST_STATIC_CAPS (VIDEO_RAW_CAPS_STRING));
101 
102 static GstStaticPadTemplate srcaudiotemplate = GST_STATIC_PAD_TEMPLATE ("src",
103     GST_PAD_SRC,
104     GST_PAD_ALWAYS,
105     GST_STATIC_CAPS (AUDIO_CAPS_STRING));
106 
107 static GstStaticPadTemplate srcaudioaactemplate =
108 GST_STATIC_PAD_TEMPLATE ("src",
109     GST_PAD_SRC,
110     GST_PAD_ALWAYS,
111     GST_STATIC_CAPS (AUDIO_AAC_TMPL_CAPS_STRING));
112 
113 /* setup and teardown needs some special handling for muxer */
114 static GstPad *
setup_src_pad(GstElement * element,GstStaticPadTemplate * template,const gchar * sinkname)115 setup_src_pad (GstElement * element,
116     GstStaticPadTemplate * template, const gchar * sinkname)
117 {
118   GstPad *srcpad, *sinkpad;
119 
120   GST_DEBUG_OBJECT (element, "setting up sending pad");
121   /* sending pad */
122   srcpad = gst_pad_new_from_static_template (template, "src");
123   fail_if (srcpad == NULL, "Could not create a srcpad");
124   ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1);
125 
126   if (!(sinkpad = gst_element_get_static_pad (element, sinkname)))
127     sinkpad = gst_element_request_pad_simple (element, sinkname);
128   fail_if (sinkpad == NULL, "Could not get sink pad from %s",
129       GST_ELEMENT_NAME (element));
130   /* references are owned by: 1) us, 2) qtmux */
131   ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
132   fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
133       "Could not link source and %s sink pads", GST_ELEMENT_NAME (element));
134   gst_object_unref (sinkpad);   /* because we got it higher up */
135 
136   /* references are owned by: 1) qtmux */
137   ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 1);
138 
139   return srcpad;
140 }
141 
142 static void
teardown_src_pad(GstPad * srcpad)143 teardown_src_pad (GstPad * srcpad)
144 {
145   GstPad *sinkpad;
146 
147   /* clean up floating src pad */
148   sinkpad = gst_pad_get_peer (srcpad);
149   fail_if (sinkpad == NULL);
150   /* pad refs held by 1) qtmux 2) us (through _get_peer) */
151   ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
152 
153   gst_pad_unlink (srcpad, sinkpad);
154 
155   /* after unlinking, pad refs still held by
156    * 1) qtmux and 2) us (through _get_peer) */
157   ASSERT_OBJECT_REFCOUNT (sinkpad, "sinkpad", 2);
158   gst_object_unref (sinkpad);
159   /* one more ref is held by element itself */
160 
161   /* pad refs held by creator */
162   ASSERT_OBJECT_REFCOUNT (srcpad, "srcpad", 1);
163   gst_object_unref (srcpad);
164 }
165 
166 gboolean downstream_is_seekable;
167 static gboolean
qtmux_sinkpad_query(GstPad * pad,GstObject * parent,GstQuery * query)168 qtmux_sinkpad_query (GstPad * pad, GstObject * parent, GstQuery * query)
169 {
170   gboolean ret = FALSE;
171 
172   if (GST_QUERY_TYPE (query) == GST_QUERY_SEEKING) {
173     gst_query_set_seeking (query, GST_FORMAT_BYTES, downstream_is_seekable, 0,
174         -1);
175     ret = TRUE;
176   }
177 
178   return ret;
179 }
180 
181 static gboolean have_eos;
182 static GCond eos_cond;
183 static GMutex event_mutex;
184 
185 static gboolean
qtmux_sinkpad_event(GstPad * pad,GstObject * parent,GstEvent * event)186 qtmux_sinkpad_event (GstPad * pad, GstObject * parent, GstEvent * event)
187 {
188   gboolean res = TRUE;
189 
190   g_mutex_lock (&event_mutex);
191   if (GST_EVENT_TYPE (event) == GST_EVENT_EOS) {
192     have_eos = TRUE;
193     GST_DEBUG ("signal EOS");
194     g_cond_broadcast (&eos_cond);
195   }
196   g_mutex_unlock (&event_mutex);
197 
198   gst_event_unref (event);
199 
200   return res;
201 }
202 
203 static GstElement *
setup_qtmux(GstStaticPadTemplate * srctemplate,const gchar * sinkname,gboolean seekable)204 setup_qtmux (GstStaticPadTemplate * srctemplate, const gchar * sinkname,
205     gboolean seekable)
206 {
207   GstElement *qtmux;
208 
209   GST_DEBUG ("setup_qtmux");
210 
211   g_cond_init (&eos_cond);
212   g_mutex_init (&event_mutex);
213   have_eos = FALSE;
214 
215   qtmux = gst_check_setup_element ("qtmux");
216   mysrcpad = setup_src_pad (qtmux, srctemplate, sinkname);
217   mysinkpad = gst_check_setup_sink_pad (qtmux, &sinktemplate);
218 
219   downstream_is_seekable = seekable;
220   gst_pad_set_query_function (mysinkpad, qtmux_sinkpad_query);
221   gst_pad_set_event_function (mysinkpad, qtmux_sinkpad_event);
222 
223   gst_pad_set_active (mysrcpad, TRUE);
224   gst_pad_set_active (mysinkpad, TRUE);
225 
226   return qtmux;
227 }
228 
229 static void
wait_for_eos(void)230 wait_for_eos (void)
231 {
232   g_mutex_lock (&event_mutex);
233   while (!have_eos)
234     g_cond_wait (&eos_cond, &event_mutex);
235   g_mutex_unlock (&event_mutex);
236 }
237 
238 
239 static void
cleanup_qtmux(GstElement * qtmux,const gchar * sinkname)240 cleanup_qtmux (GstElement * qtmux, const gchar * sinkname)
241 {
242   GST_DEBUG ("cleanup_qtmux");
243   gst_element_set_state (qtmux, GST_STATE_NULL);
244 
245   gst_pad_set_active (mysrcpad, FALSE);
246   gst_pad_set_active (mysinkpad, FALSE);
247   teardown_src_pad (mysrcpad);
248   gst_check_teardown_sink_pad (qtmux);
249   gst_check_teardown_element (qtmux);
250 }
251 
252 static void
check_qtmux_pad(GstStaticPadTemplate * srctemplate,const gchar * sinkname,guint32 dts_method)253 check_qtmux_pad (GstStaticPadTemplate * srctemplate, const gchar * sinkname,
254     guint32 dts_method)
255 {
256   GstElement *qtmux;
257   GstBuffer *inbuffer, *outbuffer;
258   GstCaps *caps;
259   int num_buffers;
260   int i;
261   guint8 data0[12] = "\000\000\000\024ftypqt  ";
262   guint8 data1[16] = "\000\000\000\010free\000\000\000\000mdat";
263   guint8 data2[4] = "moov";
264   GstSegment segment;
265 
266   qtmux = setup_qtmux (srctemplate, sinkname, TRUE);
267   g_object_set (qtmux, "dts-method", dts_method, NULL);
268   fail_unless (gst_element_set_state (qtmux,
269           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
270       "could not set to playing");
271 
272   gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
273 
274   caps = gst_pad_get_pad_template_caps (mysrcpad);
275   gst_pad_set_caps (mysrcpad, caps);
276   gst_caps_unref (caps);
277 
278   /* ensure segment (format) properly setup */
279   gst_segment_init (&segment, GST_FORMAT_TIME);
280   fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
281 
282   inbuffer = gst_buffer_new_and_alloc (1);
283   gst_buffer_memset (inbuffer, 0, 0, 1);
284   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
285   GST_BUFFER_DURATION (inbuffer) = 40 * GST_MSECOND;
286   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
287   fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
288 
289   /* send eos to have moov written */
290   fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
291 
292   /* Muxing occurs on the aggregate thread */
293   wait_for_eos ();
294 
295   num_buffers = g_list_length (buffers);
296   /* at least expect ftyp, mdat header, buffer chunk and moov */
297   fail_unless (num_buffers >= 4);
298 
299   /* clean up first to clear any pending refs in sticky caps */
300   cleanup_qtmux (qtmux, sinkname);
301 
302   for (i = 0; i < num_buffers; ++i) {
303     outbuffer = GST_BUFFER (buffers->data);
304     fail_if (outbuffer == NULL);
305     buffers = g_list_remove (buffers, outbuffer);
306 
307     switch (i) {
308       case 0:
309       {
310         /* ftyp header */
311         fail_unless (gst_buffer_get_size (outbuffer) >= 20);
312         fail_unless (gst_buffer_memcmp (outbuffer, 0, data0,
313                 sizeof (data0)) == 0);
314         fail_unless (gst_buffer_memcmp (outbuffer, 16, data0 + 8, 4) == 0);
315         break;
316       }
317       case 1:                  /* mdat header */
318         fail_unless (gst_buffer_get_size (outbuffer) == 16);
319         fail_unless (gst_buffer_memcmp (outbuffer, 0, data1, sizeof (data1))
320             == 0);
321         break;
322       case 2:                  /* buffer we put in */
323         fail_unless (gst_buffer_get_size (outbuffer) == 1);
324         break;
325       case 3:                  /* moov */
326         fail_unless (gst_buffer_get_size (outbuffer) > 8);
327         fail_unless (gst_buffer_memcmp (outbuffer, 4, data2,
328                 sizeof (data2)) == 0);
329         break;
330       default:
331         break;
332     }
333 
334     ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
335     gst_buffer_unref (outbuffer);
336     outbuffer = NULL;
337   }
338 
339   g_list_free (buffers);
340   buffers = NULL;
341 }
342 
343 static void
check_qtmux_pad_fragmented(GstStaticPadTemplate * srctemplate,const gchar * sinkname,guint32 dts_method,gboolean streamable)344 check_qtmux_pad_fragmented (GstStaticPadTemplate * srctemplate,
345     const gchar * sinkname, guint32 dts_method, gboolean streamable)
346 {
347   GstElement *qtmux;
348   GstBuffer *inbuffer, *outbuffer;
349   GstCaps *caps;
350   int num_buffers;
351   int i;
352   guint8 data0[12] = "\000\000\000\024ftypqt  ";
353   guint8 data1[4] = "mdat";
354   guint8 data2[4] = "moov";
355   guint8 data3[4] = "moof";
356   guint8 data4[4] = "mfra";
357   GstSegment segment;
358 
359   qtmux = setup_qtmux (srctemplate, sinkname, !streamable);
360   g_object_set (qtmux, "dts-method", dts_method, NULL);
361   g_object_set (qtmux, "fragment-duration", 2000, NULL);
362   g_object_set (qtmux, "streamable", streamable, NULL);
363   fail_unless (gst_element_set_state (qtmux,
364           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
365       "could not set to playing");
366 
367   gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
368 
369   caps = gst_pad_get_pad_template_caps (mysrcpad);
370   gst_pad_set_caps (mysrcpad, caps);
371   gst_caps_unref (caps);
372 
373   /* ensure segment (format) properly setup */
374   gst_segment_init (&segment, GST_FORMAT_TIME);
375   fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
376 
377   inbuffer = gst_buffer_new_and_alloc (1);
378   gst_buffer_memset (inbuffer, 0, 0, 1);
379   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
380   GST_BUFFER_DURATION (inbuffer) = 40 * GST_MSECOND;
381   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
382   fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
383 
384   /* send eos to have all written */
385   fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
386 
387   wait_for_eos ();
388 
389   num_buffers = g_list_length (buffers);
390   /* at least expect ftyp, moov, moof, mdat header, buffer chunk
391    * and optionally mfra */
392   fail_unless (num_buffers >= 5);
393 
394   /* clean up first to clear any pending refs in sticky caps */
395   cleanup_qtmux (qtmux, sinkname);
396 
397   for (i = 0; i < num_buffers; ++i) {
398     outbuffer = GST_BUFFER (buffers->data);
399     fail_if (outbuffer == NULL);
400     buffers = g_list_remove (buffers, outbuffer);
401 
402     switch (i) {
403       case 0:
404       {
405         /* ftyp header */
406         fail_unless (gst_buffer_get_size (outbuffer) >= 20);
407         fail_unless (gst_buffer_memcmp (outbuffer, 0, data0,
408                 sizeof (data0)) == 0);
409         fail_unless (gst_buffer_memcmp (outbuffer, 16, data0 + 8, 4) == 0);
410         break;
411       }
412       case 1:                  /* moov */
413         fail_unless (gst_buffer_get_size (outbuffer) > 8);
414         fail_unless (gst_buffer_memcmp (outbuffer, 4, data2,
415                 sizeof (data2)) == 0);
416         break;
417       case 2:                  /* moof */
418         fail_unless (gst_buffer_get_size (outbuffer) > 8);
419         fail_unless (gst_buffer_memcmp (outbuffer, 4, data3,
420                 sizeof (data3)) == 0);
421         break;
422       case 3:                  /* mdat header */
423         fail_unless (gst_buffer_get_size (outbuffer) == 8);
424         fail_unless (gst_buffer_memcmp (outbuffer, 4, data1,
425                 sizeof (data1)) == 0);
426         break;
427       case 4:                  /* buffer we put in */
428         fail_unless (gst_buffer_get_size (outbuffer) == 1);
429         break;
430       case 5:                  /* mfra */
431         fail_unless (gst_buffer_get_size (outbuffer) > 8);
432         fail_unless (gst_buffer_memcmp (outbuffer, 4, data4,
433                 sizeof (data4)) == 0);
434         break;
435       default:
436         break;
437     }
438 
439     ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
440     gst_buffer_unref (outbuffer);
441     outbuffer = NULL;
442   }
443 
444   g_list_free (buffers);
445   buffers = NULL;
446 }
447 
448 static void
check_qtmux_pad_fragmented_finalise(GstStaticPadTemplate * srctemplate,const gchar * sinkname,guint32 dts_method,gboolean streamable)449 check_qtmux_pad_fragmented_finalise (GstStaticPadTemplate * srctemplate,
450     const gchar * sinkname, guint32 dts_method, gboolean streamable)
451 {
452   GstElement *qtmux;
453   GstBuffer *inbuffer, *outbuffer;
454   GstCaps *caps;
455   int num_buffers;
456   int i;
457   guint8 data0[12] = "\000\000\000\024ftypqt  ";
458   guint8 data1[4] = "mdat";
459   guint8 data2[4] = "moov";
460   guint8 data3[4] = "moof";
461   GstSegment segment;
462 
463   qtmux = setup_qtmux (srctemplate, sinkname, !streamable);
464   g_object_set (qtmux, "dts-method", dts_method, NULL);
465   g_object_set (qtmux, "fragment-duration", 40, NULL);
466   g_object_set (qtmux, "fragment-mode", 1, NULL);
467   g_object_set (qtmux, "streamable", streamable, NULL);
468   fail_unless (gst_element_set_state (qtmux,
469           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
470       "could not set to playing");
471 
472   gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
473 
474   caps = gst_pad_get_pad_template_caps (mysrcpad);
475   gst_pad_set_caps (mysrcpad, caps);
476   gst_caps_unref (caps);
477 
478   /* ensure segment (format) properly setup */
479   gst_segment_init (&segment, GST_FORMAT_TIME);
480   fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
481 
482   inbuffer = gst_buffer_new_and_alloc (1);
483   gst_buffer_memset (inbuffer, 0, 0, 1);
484   GST_BUFFER_TIMESTAMP (inbuffer) = 0 * 40 * GST_MSECOND;
485   GST_BUFFER_DURATION (inbuffer) = 40 * GST_MSECOND;
486   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
487   fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
488 
489   inbuffer = gst_buffer_new_and_alloc (1);
490   gst_buffer_memset (inbuffer, 0, 0, 1);
491   GST_BUFFER_TIMESTAMP (inbuffer) = 1 * 40 * GST_MSECOND;
492   GST_BUFFER_DURATION (inbuffer) = 40 * GST_MSECOND;
493   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
494   fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
495 
496   inbuffer = gst_buffer_new_and_alloc (1);
497   gst_buffer_memset (inbuffer, 0, 0, 1);
498   GST_BUFFER_TIMESTAMP (inbuffer) = 2 * 40 * GST_MSECOND;
499   GST_BUFFER_DURATION (inbuffer) = 40 * GST_MSECOND;
500   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
501   fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
502 
503   /* send eos to have all written */
504   fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
505 
506   wait_for_eos ();
507 
508   num_buffers = g_list_length (buffers);
509   fail_unless (num_buffers >= 13);
510 
511   /* clean up first to clear any pending refs in sticky caps */
512   cleanup_qtmux (qtmux, sinkname);
513 
514   for (i = 0; i < num_buffers; ++i) {
515     outbuffer = GST_BUFFER (buffers->data);
516     fail_if (outbuffer == NULL);
517     buffers = g_list_remove (buffers, outbuffer);
518 
519     switch (i) {
520       case 0:
521       {
522         /* ftyp header */
523         fail_unless (gst_buffer_get_size (outbuffer) >= 20);
524         fail_unless (gst_buffer_memcmp (outbuffer, 0, data0,
525                 sizeof (data0)) == 0);
526         fail_unless (gst_buffer_memcmp (outbuffer, 16, data0 + 8, 4) == 0);
527         break;
528       }
529       case 1:                  /* first moov mdat header */
530         fail_unless_equals_int (gst_buffer_get_size (outbuffer), 16);
531         fail_unless (gst_buffer_memcmp (outbuffer, 12, data1,
532                 sizeof (data1)) == 0);
533         break;
534       case 2:                  /* buffer we put in */
535         fail_unless_equals_int (gst_buffer_get_size (outbuffer), 1);
536         break;
537       case 3:                  /* first moov mdat header rewrite */
538         fail_unless_equals_int (gst_buffer_get_size (outbuffer), 16);
539         fail_unless (gst_buffer_memcmp (outbuffer, 12, data1,
540                 sizeof (data1)) == 0);
541         break;
542       case 4:                  /* moov */
543         fail_unless (gst_buffer_get_size (outbuffer) > 8);
544         fail_unless (gst_buffer_memcmp (outbuffer, 4, data2,
545                 sizeof (data2)) == 0);
546         break;
547       case 5:                  /* fragment mdat header size == 0 */
548         fail_unless_equals_int (gst_buffer_get_size (outbuffer), 16);
549         fail_unless (gst_buffer_memcmp (outbuffer, 12, data1,
550                 sizeof (data1)) == 0);
551         break;
552       case 6:                  /* buffer we put in */
553         fail_unless_equals_int (gst_buffer_get_size (outbuffer), 1);
554         break;
555       case 7:                  /* fragment mdat header size */
556         fail_unless_equals_int (gst_buffer_get_size (outbuffer), 16);
557         fail_unless (gst_buffer_memcmp (outbuffer, 12, data1,
558                 sizeof (data1)) == 0);
559         break;
560       case 8:                  /* moof */
561         fail_unless (gst_buffer_get_size (outbuffer) > 8);
562         fail_unless (gst_buffer_memcmp (outbuffer, 4, data3,
563                 sizeof (data3)) == 0);
564         break;
565       case 9:                  /* fragment mdat header size = 0 */
566         fail_unless_equals_int (gst_buffer_get_size (outbuffer), 16);
567         fail_unless (gst_buffer_memcmp (outbuffer, 12, data1,
568                 sizeof (data1)) == 0);
569         break;
570       case 10:                 /* buffer we put in */
571         fail_unless_equals_int (gst_buffer_get_size (outbuffer), 1);
572         break;
573       case 11:                 /* initial moov->hoov */
574         fail_unless_equals_int (gst_buffer_get_size (outbuffer), 1);
575         fail_unless (gst_buffer_memcmp (outbuffer, 0, "h", 1) == 0);
576         break;
577       case 12:                 /* final moov mdat header size */
578         fail_unless_equals_int (gst_buffer_get_size (outbuffer), 16);
579         fail_unless (gst_buffer_memcmp (outbuffer, 12, data1,
580                 sizeof (data1)) == 0);
581         break;
582       case 13:                 /* final moov */
583         fail_unless (gst_buffer_get_size (outbuffer) > 8);
584         fail_unless (gst_buffer_memcmp (outbuffer, 4, data2,
585                 sizeof (data2)) == 0);
586         break;
587       default:
588         break;
589     }
590 
591     ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
592     gst_buffer_unref (outbuffer);
593     outbuffer = NULL;
594   }
595 
596   g_list_free (buffers);
597   buffers = NULL;
598 }
599 
600 /* dts-method dd */
601 
GST_START_TEST(test_video_pad_dd)602 GST_START_TEST (test_video_pad_dd)
603 {
604   check_qtmux_pad (&srcvideotemplate, "video_%u", 0);
605 }
606 
607 GST_END_TEST;
608 
GST_START_TEST(test_audio_pad_dd)609 GST_START_TEST (test_audio_pad_dd)
610 {
611   check_qtmux_pad (&srcaudiotemplate, "audio_%u", 0);
612 }
613 
614 GST_END_TEST;
615 
616 
GST_START_TEST(test_video_pad_frag_dd)617 GST_START_TEST (test_video_pad_frag_dd)
618 {
619   check_qtmux_pad_fragmented (&srcvideotemplate, "video_%u", 0, FALSE);
620 }
621 
622 GST_END_TEST;
623 
GST_START_TEST(test_audio_pad_frag_dd)624 GST_START_TEST (test_audio_pad_frag_dd)
625 {
626   check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%u", 0, FALSE);
627 }
628 
629 GST_END_TEST;
630 
GST_START_TEST(test_video_pad_frag_dd_streamable)631 GST_START_TEST (test_video_pad_frag_dd_streamable)
632 {
633   check_qtmux_pad_fragmented (&srcvideotemplate, "video_%u", 0, TRUE);
634 }
635 
636 GST_END_TEST;
637 
GST_START_TEST(test_audio_pad_frag_dd_streamable)638 GST_START_TEST (test_audio_pad_frag_dd_streamable)
639 {
640   check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%u", 0, TRUE);
641 }
642 
643 GST_END_TEST;
644 
GST_START_TEST(test_video_pad_frag_dd_finalise)645 GST_START_TEST (test_video_pad_frag_dd_finalise)
646 {
647   check_qtmux_pad_fragmented_finalise (&srcvideotemplate, "video_%u", 0, FALSE);
648 }
649 
650 GST_END_TEST;
651 
652 /* dts-method reorder */
653 
GST_START_TEST(test_video_pad_reorder)654 GST_START_TEST (test_video_pad_reorder)
655 {
656   check_qtmux_pad (&srcvideotemplate, "video_%u", 1);
657 }
658 
659 GST_END_TEST;
660 
GST_START_TEST(test_audio_pad_reorder)661 GST_START_TEST (test_audio_pad_reorder)
662 {
663   check_qtmux_pad (&srcaudiotemplate, "audio_%u", 1);
664 }
665 
666 GST_END_TEST;
667 
668 
GST_START_TEST(test_video_pad_frag_reorder)669 GST_START_TEST (test_video_pad_frag_reorder)
670 {
671   check_qtmux_pad_fragmented (&srcvideotemplate, "video_%u", 1, FALSE);
672 }
673 
674 GST_END_TEST;
675 
GST_START_TEST(test_audio_pad_frag_reorder)676 GST_START_TEST (test_audio_pad_frag_reorder)
677 {
678   check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%u", 1, FALSE);
679 }
680 
681 GST_END_TEST;
682 
683 
GST_START_TEST(test_video_pad_frag_reorder_streamable)684 GST_START_TEST (test_video_pad_frag_reorder_streamable)
685 {
686   check_qtmux_pad_fragmented (&srcvideotemplate, "video_%u", 1, TRUE);
687 }
688 
689 GST_END_TEST;
690 
691 
GST_START_TEST(test_audio_pad_frag_reorder_streamable)692 GST_START_TEST (test_audio_pad_frag_reorder_streamable)
693 {
694   check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%u", 1, TRUE);
695 }
696 
697 GST_END_TEST;
698 
GST_START_TEST(test_video_pad_frag_reorder_finalise)699 GST_START_TEST (test_video_pad_frag_reorder_finalise)
700 {
701   check_qtmux_pad_fragmented_finalise (&srcvideotemplate, "video_%u", 1, FALSE);
702 }
703 
704 GST_END_TEST;
705 
706 /* dts-method asc */
707 
GST_START_TEST(test_video_pad_asc)708 GST_START_TEST (test_video_pad_asc)
709 {
710   check_qtmux_pad (&srcvideotemplate, "video_%u", 2);
711 }
712 
713 GST_END_TEST;
714 
GST_START_TEST(test_audio_pad_asc)715 GST_START_TEST (test_audio_pad_asc)
716 {
717   check_qtmux_pad (&srcaudiotemplate, "audio_%u", 2);
718 }
719 
720 GST_END_TEST;
721 
722 
GST_START_TEST(test_video_pad_frag_asc)723 GST_START_TEST (test_video_pad_frag_asc)
724 {
725   check_qtmux_pad_fragmented (&srcvideotemplate, "video_%u", 2, FALSE);
726 }
727 
728 GST_END_TEST;
729 
GST_START_TEST(test_audio_pad_frag_asc)730 GST_START_TEST (test_audio_pad_frag_asc)
731 {
732   check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%u", 2, FALSE);
733 }
734 
735 GST_END_TEST;
736 
737 
GST_START_TEST(test_video_pad_frag_asc_streamable)738 GST_START_TEST (test_video_pad_frag_asc_streamable)
739 {
740   check_qtmux_pad_fragmented (&srcvideotemplate, "video_%u", 2, TRUE);
741 }
742 
743 GST_END_TEST;
744 
745 
GST_START_TEST(test_audio_pad_frag_asc_streamable)746 GST_START_TEST (test_audio_pad_frag_asc_streamable)
747 {
748   check_qtmux_pad_fragmented (&srcaudiotemplate, "audio_%u", 2, TRUE);
749 }
750 
751 GST_END_TEST;
752 
GST_START_TEST(test_video_pad_frag_asc_finalise)753 GST_START_TEST (test_video_pad_frag_asc_finalise)
754 {
755   check_qtmux_pad_fragmented_finalise (&srcvideotemplate, "video_%u", 2, FALSE);
756 }
757 
758 GST_END_TEST;
759 
GST_START_TEST(test_reuse)760 GST_START_TEST (test_reuse)
761 {
762   GstElement *qtmux = setup_qtmux (&srcvideotemplate, "video_%u", TRUE);
763   GstBuffer *inbuffer;
764   GstCaps *caps;
765   GstSegment segment;
766 
767   gst_element_set_state (qtmux, GST_STATE_PLAYING);
768   gst_element_set_state (qtmux, GST_STATE_NULL);
769   gst_element_set_state (qtmux, GST_STATE_PLAYING);
770   gst_pad_set_active (mysrcpad, TRUE);
771   gst_pad_set_active (mysinkpad, TRUE);
772 
773   gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
774 
775   caps = gst_pad_get_pad_template_caps (mysrcpad);
776   gst_pad_set_caps (mysrcpad, caps);
777   gst_caps_unref (caps);
778 
779   /* ensure segment (format) properly setup */
780   gst_segment_init (&segment, GST_FORMAT_TIME);
781   fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
782 
783   inbuffer = gst_buffer_new_and_alloc (1);
784   fail_unless (inbuffer != NULL);
785   gst_buffer_memset (inbuffer, 0, 0, 1);
786   GST_BUFFER_TIMESTAMP (inbuffer) = 0;
787   GST_BUFFER_DURATION (inbuffer) = 40 * GST_MSECOND;
788   ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
789   fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
790 
791   /* send eos to have all written */
792   fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
793 
794   cleanup_qtmux (qtmux, "video_%u");
795   gst_check_drop_buffers ();
796 }
797 
798 GST_END_TEST;
799 
800 static GstEncodingContainerProfile *
create_qtmux_profile(const gchar * variant)801 create_qtmux_profile (const gchar * variant)
802 {
803   GstEncodingContainerProfile *cprof;
804   GstCaps *caps;
805 
806   if (variant == NULL) {
807     caps = gst_caps_new_empty_simple ("video/quicktime");
808   } else {
809     caps = gst_caps_new_simple ("video/quicktime",
810         "variant", G_TYPE_STRING, variant, NULL);
811   }
812 
813   cprof = gst_encoding_container_profile_new ("Name", "blah", caps, NULL);
814   gst_caps_unref (caps);
815 
816   caps = gst_caps_new_simple ("audio/x-raw",
817       "format", G_TYPE_STRING, "S16BE",
818       "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT, 44100, NULL);
819   gst_encoding_container_profile_add_profile (cprof,
820       GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps, NULL, NULL,
821               1)));
822   gst_caps_unref (caps);
823 
824   return cprof;
825 }
826 
GST_START_TEST(test_encodebin_qtmux)827 GST_START_TEST (test_encodebin_qtmux)
828 {
829   GstEncodingContainerProfile *cprof;
830   GstElement *enc;
831   GstPad *pad;
832 
833   enc = gst_element_factory_make ("encodebin", NULL);
834   if (enc == NULL)
835     return;
836 
837   /* Make sure encodebin finds a muxer for a profile with a variant field .. */
838   cprof = create_qtmux_profile ("apple");
839   g_object_set (enc, "profile", cprof, NULL);
840   gst_encoding_profile_unref (cprof);
841 
842   /* should have created a pad after setting the profile */
843   pad = gst_element_get_static_pad (enc, "audio_0");
844   fail_unless (pad != NULL);
845   gst_object_unref (pad);
846   gst_object_unref (enc);
847 
848   /* ... and for a profile without a variant field */
849   enc = gst_element_factory_make ("encodebin", NULL);
850   cprof = create_qtmux_profile (NULL);
851   g_object_set (enc, "profile", cprof, NULL);
852   gst_encoding_profile_unref (cprof);
853 
854   /* should have created a pad after setting the profile */
855   pad = gst_element_get_static_pad (enc, "audio_0");
856   fail_unless (pad != NULL);
857   gst_object_unref (pad);
858   gst_object_unref (enc);
859 }
860 
861 GST_END_TEST;
862 
863 /* Fake mp3 encoder for test */
864 typedef GstElement TestMp3Enc;
865 typedef GstElementClass TestMp3EncClass;
866 
867 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
868     GST_PAD_SRC,
869     GST_PAD_ALWAYS,
870     GST_STATIC_CAPS ("audio/mpeg, mpegversion=1, layer=[1,3]")
871     );
872 
873 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
874     GST_PAD_SINK,
875     GST_PAD_ALWAYS,
876     GST_STATIC_CAPS ("audio/x-raw")
877     );
878 
879 static GType test_mp3_enc_get_type (void);
880 static void test_input_push_segment_start (gpointer user_data,
881     GstClockTime start);
882 
883 G_DEFINE_TYPE (TestMp3Enc, test_mp3_enc, GST_TYPE_ELEMENT);
884 
885 static void
test_mp3_enc_class_init(TestMp3EncClass * klass)886 test_mp3_enc_class_init (TestMp3EncClass * klass)
887 {
888   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
889 
890   gst_element_class_add_static_pad_template (element_class, &sink_template);
891   gst_element_class_add_static_pad_template (element_class, &src_template);
892 
893   gst_element_class_set_metadata (element_class, "MPEG1 Audio Encoder",
894       "Codec/Encoder/Audio", "Pretends to encode mp3", "Foo Bar <foo@bar.com>");
895 }
896 
897 static void
test_mp3_enc_init(TestMp3Enc * mp3enc)898 test_mp3_enc_init (TestMp3Enc * mp3enc)
899 {
900   GstPad *pad;
901 
902   pad = gst_pad_new_from_static_template (&sink_template, "sink");
903   gst_element_add_pad (mp3enc, pad);
904 
905   pad = gst_pad_new_from_static_template (&src_template, "src");
906   gst_element_add_pad (mp3enc, pad);
907 }
908 
909 static gboolean
plugin_init(GstPlugin * plugin)910 plugin_init (GstPlugin * plugin)
911 {
912   return gst_element_register (plugin, "testmp3enc", GST_RANK_NONE,
913       test_mp3_enc_get_type ());
914 }
915 
916 static GstEncodingContainerProfile *
create_mp4mux_profile(void)917 create_mp4mux_profile (void)
918 {
919   GstEncodingContainerProfile *cprof;
920   GstCaps *caps;
921 
922   caps = gst_caps_new_simple ("video/quicktime",
923       "variant", G_TYPE_STRING, "iso", NULL);
924 
925   cprof = gst_encoding_container_profile_new ("Name", "blah", caps, NULL);
926   gst_caps_unref (caps);
927 
928   caps = gst_caps_new_simple ("audio/mpeg", "mpegversion", G_TYPE_INT, 1,
929       "layer", G_TYPE_INT, 3, "channels", G_TYPE_INT, 2, "rate", G_TYPE_INT,
930       44100, NULL);
931   gst_encoding_container_profile_add_profile (cprof,
932       GST_ENCODING_PROFILE (gst_encoding_audio_profile_new (caps, NULL, NULL,
933               1)));
934   gst_caps_unref (caps);
935 
936   return cprof;
937 }
938 
GST_START_TEST(test_encodebin_mp4mux)939 GST_START_TEST (test_encodebin_mp4mux)
940 {
941   GstEncodingContainerProfile *cprof;
942   GstPluginFeature *feature;
943   GstElement *enc, *mux;
944   GstPad *pad;
945 
946   /* need a fake mp3 encoder because mp4 only accepts encoded formats */
947   gst_plugin_register_static (GST_VERSION_MAJOR, GST_VERSION_MINOR,
948       "fakemp3enc", "fakemp3enc", plugin_init, VERSION, "LGPL",
949       "gst-plugins-good", GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
950 
951   feature = gst_registry_find_feature (gst_registry_get (), "testmp3enc",
952       GST_TYPE_ELEMENT_FACTORY);
953   gst_plugin_feature_set_rank (feature, GST_RANK_PRIMARY + 100);
954 
955   enc = gst_element_factory_make ("encodebin", NULL);
956   if (enc == NULL)
957     return;
958 
959   /* Make sure encodebin finds mp4mux even though qtmux outputs a superset */
960   cprof = create_mp4mux_profile ();
961   g_object_set (enc, "profile", cprof, NULL);
962   gst_encoding_profile_unref (cprof);
963 
964   /* should have created a pad after setting the profile */
965   pad = gst_element_get_static_pad (enc, "audio_0");
966   fail_unless (pad != NULL);
967   gst_object_unref (pad);
968 
969   mux = gst_bin_get_by_interface (GST_BIN (enc), GST_TYPE_TAG_SETTER);
970   fail_unless (mux != NULL);
971   {
972     GstElementFactory *f = gst_element_get_factory (mux);
973 
974     /* make sure we got mp4mux for variant=iso */
975     GST_INFO ("muxer: %s", G_OBJECT_TYPE_NAME (mux));
976     fail_unless_equals_string (GST_OBJECT_NAME (f), "mp4mux");
977   }
978   gst_object_unref (mux);
979   gst_object_unref (enc);
980 
981   gst_plugin_feature_set_rank (feature, GST_RANK_NONE);
982   gst_object_unref (feature);
983 }
984 
985 GST_END_TEST;
986 
987 static gboolean
extract_tags(const gchar * location,GstTagList ** taglist)988 extract_tags (const gchar * location, GstTagList ** taglist)
989 {
990   gboolean ret = TRUE;
991   GstElement *src;
992   GstBus *bus;
993   GstElement *pipeline =
994       gst_parse_launch ("filesrc name=src ! qtdemux ! fakesink", NULL);
995 
996   src = gst_bin_get_by_name (GST_BIN (pipeline), "src");
997   g_object_set (src, "location", location, NULL);
998 
999   bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline));
1000   fail_unless (gst_element_set_state (pipeline, GST_STATE_PLAYING)
1001       != GST_STATE_CHANGE_FAILURE);
1002 
1003   if (*taglist == NULL) {
1004     *taglist = gst_tag_list_new_empty ();
1005   }
1006 
1007   while (1) {
1008     GstMessage *msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
1009         GST_MESSAGE_TAG | GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
1010 
1011     if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) {
1012       gst_message_unref (msg);
1013       break;
1014     } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
1015       ret = FALSE;
1016       gst_message_unref (msg);
1017       break;
1018     } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_TAG) {
1019       GstTagList *tags;
1020 
1021       gst_message_parse_tag (msg, &tags);
1022       gst_tag_list_insert (*taglist, tags, GST_TAG_MERGE_REPLACE);
1023       gst_tag_list_unref (tags);
1024     }
1025     gst_message_unref (msg);
1026   }
1027 
1028   gst_object_unref (bus);
1029   gst_element_set_state (pipeline, GST_STATE_NULL);
1030   gst_object_unref (src);
1031   gst_object_unref (pipeline);
1032   return ret;
1033 }
1034 
1035 static void
test_average_bitrate_custom(const gchar * elementname,GstStaticPadTemplate * tmpl,const gchar * caps_str,const gchar * sinkpadname)1036 test_average_bitrate_custom (const gchar * elementname,
1037     GstStaticPadTemplate * tmpl, const gchar * caps_str,
1038     const gchar * sinkpadname)
1039 {
1040   gchar *location;
1041   GstElement *qtmux;
1042   GstElement *filesink;
1043   GstBuffer *inbuffer;
1044   GstCaps *caps;
1045   int i;
1046   gint bytes[] = { 16, 22, 12 };
1047   gint64 durations[] = { GST_SECOND * 3, GST_SECOND * 5, GST_SECOND * 2 };
1048   gint64 total_bytes = 0;
1049   GstClockTime total_duration = 0;
1050   GstSegment segment;
1051   GstBus *bus;
1052 
1053   location = g_strdup_printf ("%s/%s-%d", g_get_tmp_dir (), "qtmuxtest",
1054       g_random_int ());
1055   GST_INFO ("Using location %s for bitrate test", location);
1056   qtmux = gst_check_setup_element (elementname);
1057   filesink = gst_element_factory_make ("filesink", NULL);
1058   g_object_set (filesink, "location", location, NULL);
1059   gst_element_link (qtmux, filesink);
1060   mysrcpad = setup_src_pad (qtmux, tmpl, sinkpadname);
1061   fail_unless (mysrcpad != NULL);
1062   gst_pad_set_active (mysrcpad, TRUE);
1063 
1064   bus = gst_bus_new ();
1065   gst_element_set_bus (filesink, bus);
1066   gst_object_unref (bus);
1067 
1068   fail_unless (gst_element_set_state (filesink,
1069           GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE,
1070       "could not set filesink to playing");
1071   fail_unless (gst_element_set_state (qtmux,
1072           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
1073       "could not set to playing");
1074 
1075   gst_pad_push_event (mysrcpad, gst_event_new_stream_start ("test"));
1076 
1077   caps = gst_caps_from_string (caps_str);
1078   gst_pad_set_caps (mysrcpad, caps);
1079   gst_caps_unref (caps);
1080 
1081   /* ensure segment (format) properly setup */
1082   gst_segment_init (&segment, GST_FORMAT_TIME);
1083   fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_segment (&segment)));
1084 
1085   for (i = 0; i < 3; i++) {
1086     inbuffer = gst_buffer_new_and_alloc (bytes[i]);
1087     gst_buffer_memset (inbuffer, 0, 0, bytes[i]);
1088     GST_BUFFER_TIMESTAMP (inbuffer) = total_duration;
1089     GST_BUFFER_DURATION (inbuffer) = (GstClockTime) durations[i];
1090     ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
1091 
1092     total_bytes += gst_buffer_get_size (inbuffer);
1093     total_duration += GST_BUFFER_DURATION (inbuffer);
1094     fail_unless (gst_pad_push (mysrcpad, inbuffer) == GST_FLOW_OK);
1095   }
1096 
1097   /* send eos to have moov written */
1098   fail_unless (gst_pad_push_event (mysrcpad, gst_event_new_eos ()) == TRUE);
1099 
1100   gst_message_unref (gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
1101           GST_MESSAGE_EOS));
1102 
1103   gst_element_set_state (qtmux, GST_STATE_NULL);
1104   gst_element_set_state (filesink, GST_STATE_NULL);
1105 
1106   gst_element_set_bus (filesink, NULL);
1107 
1108   gst_check_drop_buffers ();
1109   gst_pad_set_active (mysrcpad, FALSE);
1110   teardown_src_pad (mysrcpad);
1111   gst_object_unref (filesink);
1112   gst_check_teardown_element (qtmux);
1113 
1114   /* check the bitrate tag */
1115   {
1116     GstTagList *taglist = NULL;
1117     guint bitrate = 0;
1118     guint expected;
1119 
1120     fail_unless (extract_tags (location, &taglist));
1121 
1122     fail_unless (gst_tag_list_get_uint (taglist, GST_TAG_BITRATE, &bitrate));
1123 
1124     expected =
1125         (guint) gst_util_uint64_scale_round ((guint64) total_bytes,
1126         (guint64) 8 * GST_SECOND, (guint64) total_duration);
1127     fail_unless (bitrate == expected);
1128     gst_tag_list_unref (taglist);
1129   }
1130 
1131   /* delete file */
1132   g_unlink (location);
1133   g_free (location);
1134 }
1135 
GST_START_TEST(test_average_bitrate)1136 GST_START_TEST (test_average_bitrate)
1137 {
1138   test_average_bitrate_custom ("mp4mux", &srcaudioaactemplate,
1139       AUDIO_AAC_CAPS_STRING, "audio_%u");
1140   test_average_bitrate_custom ("mp4mux", &srcvideoh264template,
1141       VIDEO_CAPS_H264_STRING, "video_%u");
1142 
1143   test_average_bitrate_custom ("qtmux", &srcaudioaactemplate,
1144       AUDIO_AAC_CAPS_STRING, "audio_%u");
1145   test_average_bitrate_custom ("qtmux", &srcvideoh264template,
1146       VIDEO_CAPS_H264_STRING, "video_%u");
1147 }
1148 
1149 GST_END_TEST;
1150 
1151 struct TestInputData
1152 {
1153   GstPad *srcpad;
1154   GstSegment segment;
1155   GList *input;
1156   GThread *thread;
1157 
1158   /* When comparing ts, the input will be subtracted from this */
1159   gint64 ts_offset;
1160   /* Due to DTS, the segment start might be shifted so this list
1161    * is used to vefity each received segments */
1162   GList *expected_segment_start;
1163 
1164   GstClockTime expected_gap_ts;
1165   GstClockTime expected_gap_duration;
1166   gboolean gap_received;
1167 
1168   GstPad *sinkpad;
1169 
1170   GList *output_iter;
1171 };
1172 
1173 static void
test_input_data_init(struct TestInputData * data)1174 test_input_data_init (struct TestInputData *data)
1175 {
1176   data->ts_offset = 0;
1177   data->expected_segment_start = NULL;
1178   data->expected_gap_ts = 0;
1179   data->expected_gap_duration = 0;
1180   data->gap_received = FALSE;
1181   data->srcpad = NULL;
1182   data->sinkpad = NULL;
1183   data->input = NULL;
1184   data->thread = NULL;
1185 
1186   test_input_push_segment_start (data, 0);
1187 }
1188 
1189 static void
test_input_data_clean(struct TestInputData * data)1190 test_input_data_clean (struct TestInputData *data)
1191 {
1192   g_list_free_full (data->input, (GDestroyNotify) gst_mini_object_unref);
1193 
1194   if (data->sinkpad) {
1195     gst_pad_set_active (data->sinkpad, FALSE);
1196     gst_object_unref (data->sinkpad);
1197   }
1198 
1199   gst_pad_set_active (data->srcpad, FALSE);
1200   teardown_src_pad (data->srcpad);
1201 }
1202 
1203 static gpointer
test_input_push_data(gpointer user_data)1204 test_input_push_data (gpointer user_data)
1205 {
1206   struct TestInputData *data = user_data;
1207   GList *iter;
1208   GstFlowReturn flow;
1209 
1210   for (iter = data->input; iter; iter = g_list_next (iter)) {
1211     if (GST_IS_BUFFER (iter->data)) {
1212       GST_INFO ("Pushing buffer %" GST_PTR_FORMAT " on pad: %s:%s", iter->data,
1213           GST_DEBUG_PAD_NAME (data->srcpad));
1214       flow =
1215           gst_pad_push (data->srcpad,
1216           gst_buffer_ref ((GstBuffer *) iter->data));
1217       fail_unless (flow == GST_FLOW_OK);
1218     } else {
1219       GST_INFO_OBJECT (data->srcpad, "Pushing event: %"
1220           GST_PTR_FORMAT, iter->data);
1221       fail_unless (gst_pad_push_event (data->srcpad,
1222               gst_event_ref ((GstEvent *) iter->data)) == TRUE);
1223     }
1224   }
1225   return NULL;
1226 }
1227 
1228 static void
test_input_push_segment_start(gpointer user_data,GstClockTime start)1229 test_input_push_segment_start (gpointer user_data, GstClockTime start)
1230 {
1231   struct TestInputData *data = user_data;
1232   GstClockTime *start_data = g_malloc (sizeof (GstClockTime));
1233 
1234   *start_data = start;
1235   data->expected_segment_start = g_list_append (data->expected_segment_start,
1236       start_data);
1237 }
1238 
1239 static GstClockTime
test_input_pop_segment_start(gpointer user_data)1240 test_input_pop_segment_start (gpointer user_data)
1241 {
1242   struct TestInputData *data = user_data;
1243   GstClockTime start = GST_CLOCK_TIME_NONE;
1244   GstClockTime *start_data;
1245 
1246   if (data->expected_segment_start) {
1247     start_data = data->expected_segment_start->data;
1248     data->expected_segment_start =
1249         g_list_delete_link (data->expected_segment_start,
1250         data->expected_segment_start);
1251     start = *start_data;
1252     g_free (start_data);
1253   }
1254 
1255   return start;
1256 }
1257 
1258 static GstBuffer *
create_buffer(GstClockTime pts,GstClockTime dts,GstClockTime duration,guint bytes)1259 create_buffer (GstClockTime pts, GstClockTime dts, GstClockTime duration,
1260     guint bytes)
1261 {
1262   GstBuffer *buf;
1263   guint8 *data;
1264 
1265   data = g_malloc0 (bytes);
1266   buf = gst_buffer_new_wrapped (data, bytes);
1267   GST_BUFFER_PTS (buf) = pts;
1268   GST_BUFFER_DTS (buf) = dts;
1269   GST_BUFFER_DURATION (buf) = duration;
1270   return buf;
1271 }
1272 
1273 static GstFlowReturn
_test_sink_pad_chain(GstPad * pad,GstObject * parent,GstBuffer * buffer)1274 _test_sink_pad_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer)
1275 {
1276   struct TestInputData *test_data = g_object_get_qdata (G_OBJECT (pad),
1277       g_quark_from_static_string ("test-mux-pad"));
1278   GstBuffer *expected_buffer;
1279 
1280   fail_unless (test_data->output_iter);
1281   fail_unless (GST_IS_BUFFER (test_data->output_iter->data));
1282   expected_buffer = test_data->output_iter->data;
1283 
1284   fail_unless (GST_BUFFER_PTS (buffer) ==
1285       (GST_BUFFER_PTS_IS_VALID (expected_buffer) ?
1286           GST_BUFFER_PTS (expected_buffer) -
1287           test_data->ts_offset : GST_BUFFER_PTS (expected_buffer)));
1288   fail_unless (GST_BUFFER_DTS (buffer) ==
1289       (GST_BUFFER_DTS_IS_VALID (expected_buffer) ?
1290           GST_BUFFER_DTS (expected_buffer) -
1291           test_data->ts_offset : GST_BUFFER_DTS (buffer)));
1292   fail_unless (GST_BUFFER_DURATION (buffer) ==
1293       GST_BUFFER_DURATION (expected_buffer));
1294 
1295   test_data->output_iter = g_list_next (test_data->output_iter);
1296 
1297   gst_buffer_unref (buffer);
1298   return GST_FLOW_OK;
1299 }
1300 
1301 static void
compare_event(GstEvent * event,GstEvent * expected)1302 compare_event (GstEvent * event, GstEvent * expected)
1303 {
1304   fail_unless (GST_EVENT_TYPE (event) == GST_EVENT_TYPE (expected));
1305   switch (GST_EVENT_TYPE (event)) {
1306     case GST_EVENT_CAPS:{
1307       GstCaps *caps, *expected_caps;
1308 
1309       gst_event_parse_caps (event, &caps);
1310       gst_event_parse_caps (expected, &expected_caps);
1311       fail_unless (gst_caps_can_intersect (caps, expected_caps));
1312     }
1313       break;
1314     default:
1315       break;
1316   }
1317 }
1318 
1319 static gboolean
_test_sink_pad_event(GstPad * pad,GstObject * parent,GstEvent * event)1320 _test_sink_pad_event (GstPad * pad, GstObject * parent, GstEvent * event)
1321 {
1322   struct TestInputData *test_data = g_object_get_qdata (G_OBJECT (pad),
1323       g_quark_from_static_string ("test-mux-pad"));
1324 
1325   switch (GST_EVENT_TYPE (event)) {
1326     case GST_EVENT_STREAM_START:
1327     case GST_EVENT_CAPS:
1328     case GST_EVENT_EOS:
1329       fail_unless (test_data->output_iter);
1330       fail_unless (GST_IS_EVENT (test_data->output_iter->data));
1331       compare_event (event, test_data->output_iter->data);
1332       test_data->output_iter = g_list_next (test_data->output_iter);
1333       break;
1334     case GST_EVENT_SEGMENT:{
1335       const GstSegment *segment;
1336 
1337       fail_unless (test_data->output_iter);
1338       fail_unless (GST_IS_EVENT (test_data->output_iter->data));
1339       gst_event_parse_segment (event, &segment);
1340       fail_unless (segment->start == test_input_pop_segment_start (test_data));
1341       test_data->output_iter = g_list_next (test_data->output_iter);
1342       break;
1343     }
1344     case GST_EVENT_GAP:{
1345       GstClockTime timestamp;
1346       GstClockTime duration;
1347       gst_event_parse_gap (event, &timestamp, &duration);
1348       fail_unless (timestamp == test_data->expected_gap_ts);
1349       fail_unless (duration == test_data->expected_gap_duration);
1350       test_data->gap_received = TRUE;
1351       break;
1352     }
1353     case GST_EVENT_TAG:
1354       /* ignore this event */
1355       break;
1356     default:
1357       GST_ERROR_OBJECT (pad, "Unexpected event: %" GST_PTR_FORMAT, event);
1358       fail ("Unexpected event received %s", GST_EVENT_TYPE_NAME (event));
1359       break;
1360   }
1361 
1362   gst_event_unref (event);
1363   return TRUE;
1364 }
1365 
1366 static void
_test_pad_added_cb(GstElement * element,GstPad * pad,gpointer udata)1367 _test_pad_added_cb (GstElement * element, GstPad * pad, gpointer udata)
1368 {
1369   GstCaps *caps;
1370   struct TestInputData **inputs = udata;
1371   gint i = -1;
1372   const gchar *name;
1373   const gchar *strname;
1374 
1375   caps = gst_pad_get_current_caps (pad);
1376   strname = gst_structure_get_name (gst_caps_get_structure (caps, 0));
1377   if (g_str_has_prefix (strname, "video/")) {
1378     i = 0;                      /* video is 0, audio is 1 */
1379     name = "videosink";
1380   } else {
1381     i = 1;
1382     name = "audiosink";
1383   }
1384   gst_caps_unref (caps);
1385 
1386   fail_unless (i != -1);
1387   fail_unless (inputs[i]->sinkpad == NULL);
1388   inputs[i]->sinkpad = gst_pad_new (name, GST_PAD_SINK);
1389   inputs[i]->output_iter = inputs[i]->input;
1390   g_object_set_qdata (G_OBJECT (inputs[i]->sinkpad),
1391       g_quark_from_static_string ("test-mux-pad"), inputs[i]);
1392   gst_pad_set_chain_function (inputs[i]->sinkpad, _test_sink_pad_chain);
1393   gst_pad_set_event_function (inputs[i]->sinkpad, _test_sink_pad_event);
1394   gst_pad_set_active (inputs[i]->sinkpad, TRUE);
1395   fail_unless (gst_pad_link (pad, inputs[i]->sinkpad) == GST_PAD_LINK_OK);
1396 }
1397 
1398 static void
check_output(const gchar * location,struct TestInputData * input1,struct TestInputData * input2)1399 check_output (const gchar * location, struct TestInputData *input1,
1400     struct TestInputData *input2)
1401 {
1402   GstElement *filesrc;
1403   GstElement *demux;
1404   struct TestInputData *inputs[2] = { input1, input2 };
1405 
1406   filesrc = gst_element_factory_make ("filesrc", NULL);
1407   demux = gst_element_factory_make ("qtdemux", NULL);
1408 
1409   fail_unless (gst_element_link (filesrc, demux));
1410 
1411   g_object_set (filesrc, "location", location, NULL);
1412   g_signal_connect (demux, "pad-added", (GCallback) _test_pad_added_cb, inputs);
1413 
1414   fail_unless (gst_element_set_state (demux,
1415           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
1416   fail_unless (gst_element_set_state (filesrc,
1417           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS);
1418 
1419   /* FIXME use a main loop */
1420   g_usleep (2 * G_USEC_PER_SEC);
1421 
1422   fail_unless (gst_element_set_state (demux,
1423           GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
1424   fail_unless (gst_element_set_state (filesrc,
1425           GST_STATE_NULL) == GST_STATE_CHANGE_SUCCESS);
1426   gst_object_unref (filesrc);
1427   gst_object_unref (demux);
1428 }
1429 
1430 /* Muxes a file with qtmux using the inputs provided and
1431  * then verifies that the generated file corresponds to the
1432  * data in the inputs */
1433 static void
run_muxing_test(struct TestInputData * input1,struct TestInputData * input2)1434 run_muxing_test (struct TestInputData *input1, struct TestInputData *input2)
1435 {
1436   gchar *location;
1437   GstElement *qtmux;
1438   GstElement *filesink;
1439   GstBus *bus;
1440 
1441   location = g_strdup_printf ("%s/%s-%d", g_get_tmp_dir (), "qtmuxtest",
1442       g_random_int ());
1443   qtmux = gst_check_setup_element ("qtmux");
1444   filesink = gst_element_factory_make ("filesink", NULL);
1445   g_object_set (filesink, "location", location, NULL);
1446   gst_element_link (qtmux, filesink);
1447 
1448   bus = gst_bus_new ();
1449   gst_element_set_bus (filesink, bus);
1450   gst_object_unref (bus);
1451 
1452   input1->srcpad = setup_src_pad (qtmux, &srcvideorawtemplate, "video_%u");
1453   fail_unless (input1->srcpad != NULL);
1454   gst_pad_set_active (input1->srcpad, TRUE);
1455 
1456   input2->srcpad = setup_src_pad (qtmux, &srcaudioaactemplate, "audio_%u");
1457   fail_unless (input2->srcpad != NULL);
1458   gst_pad_set_active (input2->srcpad, TRUE);
1459 
1460   fail_unless (gst_element_set_state (filesink,
1461           GST_STATE_PLAYING) != GST_STATE_CHANGE_FAILURE,
1462       "could not set filesink to playing");
1463   fail_unless (gst_element_set_state (qtmux,
1464           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
1465       "could not set to playing");
1466 
1467   input1->thread =
1468       g_thread_new ("test-push-data-1", test_input_push_data, input1);
1469   input2->thread =
1470       g_thread_new ("test-push-data-2", test_input_push_data, input2);
1471 
1472   g_thread_join (input1->thread);
1473   g_thread_join (input2->thread);
1474   input1->thread = NULL;
1475   input2->thread = NULL;
1476 
1477   gst_message_unref (gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE,
1478           GST_MESSAGE_EOS));
1479 
1480   gst_element_set_state (qtmux, GST_STATE_NULL);
1481   gst_element_set_state (filesink, GST_STATE_NULL);
1482 
1483   gst_element_set_bus (filesink, NULL);
1484 
1485   check_output (location, input1, input2);
1486 
1487   gst_object_unref (filesink);
1488   test_input_data_clean (input1);
1489   test_input_data_clean (input2);
1490   gst_check_teardown_element (qtmux);
1491 
1492   /* delete file */
1493   g_unlink (location);
1494   g_free (location);
1495 }
1496 
GST_START_TEST(test_muxing)1497 GST_START_TEST (test_muxing)
1498 {
1499   struct TestInputData input1, input2;
1500   GstCaps *caps;
1501 
1502   test_input_data_init (&input1);
1503   test_input_data_init (&input2);
1504 
1505   /* Create the inputs, after calling the run below, all this data is
1506    * transferred to it and we have no need to clean up */
1507   input1.input = NULL;
1508   input1.input =
1509       g_list_append (input1.input, gst_event_new_stream_start ("test-1"));
1510   caps = gst_caps_from_string
1511       ("video/x-raw, width=(int)800, height=(int)600, "
1512       "framerate=(fraction)1/1, format=(string)RGB");
1513   input1.input = g_list_append (input1.input, gst_event_new_caps (caps));
1514   gst_caps_unref (caps);
1515   gst_segment_init (&input1.segment, GST_FORMAT_TIME);
1516   input1.input =
1517       g_list_append (input1.input, gst_event_new_segment (&input1.segment));
1518   input1.input =
1519       g_list_append (input1.input, create_buffer (0, GST_CLOCK_TIME_NONE,
1520           GST_SECOND, 800 * 600 * 3));
1521   input1.input =
1522       g_list_append (input1.input, create_buffer (1 * GST_SECOND,
1523           GST_CLOCK_TIME_NONE, GST_SECOND, 800 * 600 * 3));
1524   input1.input =
1525       g_list_append (input1.input, create_buffer (2 * GST_SECOND,
1526           GST_CLOCK_TIME_NONE, GST_SECOND, 800 * 600 * 3));
1527   input1.input = g_list_append (input1.input, gst_event_new_eos ());
1528 
1529   input2.input = NULL;
1530   input2.input =
1531       g_list_append (input2.input, gst_event_new_stream_start ("test-2"));
1532   caps = gst_caps_from_string (AUDIO_AAC_CAPS_STRING);
1533   input2.input = g_list_append (input2.input, gst_event_new_caps (caps));
1534   gst_caps_unref (caps);
1535   gst_segment_init (&input2.segment, GST_FORMAT_TIME);
1536   input2.input =
1537       g_list_append (input2.input, gst_event_new_segment (&input2.segment));
1538   input2.input =
1539       g_list_append (input2.input, create_buffer (0, 0, GST_SECOND, 4096));
1540   input2.input =
1541       g_list_append (input2.input, create_buffer (1 * GST_SECOND,
1542           1 * GST_SECOND, GST_SECOND, 4096));
1543   input2.input =
1544       g_list_append (input2.input, create_buffer (2 * GST_SECOND,
1545           2 * GST_SECOND, GST_SECOND, 4096));
1546   input2.input = g_list_append (input2.input, gst_event_new_eos ());
1547 
1548   run_muxing_test (&input1, &input2);
1549 }
1550 
1551 GST_END_TEST;
1552 
1553 
GST_START_TEST(test_muxing_non_zero_segment)1554 GST_START_TEST (test_muxing_non_zero_segment)
1555 {
1556   struct TestInputData input1, input2;
1557   GstCaps *caps;
1558 
1559   test_input_data_init (&input1);
1560   test_input_data_init (&input2);
1561 
1562   /* Create the inputs, after calling the run below, all this data is
1563    * transferred to it and we have no need to clean up */
1564   input1.input = NULL;
1565   input1.input =
1566       g_list_append (input1.input, gst_event_new_stream_start ("test-1"));
1567   caps = gst_caps_from_string
1568       ("video/x-raw, width=(int)800, height=(int)600, "
1569       "framerate=(fraction)1/1, format=(string)RGB");
1570   input1.input = g_list_append (input1.input, gst_event_new_caps (caps));
1571   gst_caps_unref (caps);
1572   gst_segment_init (&input1.segment, GST_FORMAT_TIME);
1573   input1.segment.start = 10 * GST_SECOND;
1574   input1.input =
1575       g_list_append (input1.input, gst_event_new_segment (&input1.segment));
1576   input1.input =
1577       g_list_append (input1.input, create_buffer (10 * GST_SECOND,
1578           GST_CLOCK_TIME_NONE, GST_SECOND, 800 * 600 * 3));
1579   input1.input =
1580       g_list_append (input1.input, create_buffer (11 * GST_SECOND,
1581           GST_CLOCK_TIME_NONE, GST_SECOND, 800 * 600 * 3));
1582   input1.input =
1583       g_list_append (input1.input, create_buffer (12 * GST_SECOND,
1584           GST_CLOCK_TIME_NONE, GST_SECOND, 800 * 600 * 3));
1585   input1.input = g_list_append (input1.input, gst_event_new_eos ());
1586   input1.ts_offset = GST_SECOND * 10;
1587 
1588   input2.input = NULL;
1589   input2.input =
1590       g_list_append (input2.input, gst_event_new_stream_start ("test-2"));
1591   caps = gst_caps_from_string (AUDIO_AAC_CAPS_STRING);
1592   input2.input = g_list_append (input2.input, gst_event_new_caps (caps));
1593   gst_caps_unref (caps);
1594   gst_segment_init (&input2.segment, GST_FORMAT_TIME);
1595   input2.segment.start = 10 * GST_SECOND;
1596   input2.input =
1597       g_list_append (input2.input, gst_event_new_segment (&input2.segment));
1598   input2.input =
1599       g_list_append (input2.input, create_buffer (10 * GST_SECOND,
1600           10 * GST_SECOND, GST_SECOND, 4096));
1601   input2.input =
1602       g_list_append (input2.input, create_buffer (11 * GST_SECOND,
1603           11 * GST_SECOND, GST_SECOND, 4096));
1604   input2.input =
1605       g_list_append (input2.input, create_buffer (12 * GST_SECOND,
1606           12 * GST_SECOND, GST_SECOND, 4096));
1607   input2.input = g_list_append (input2.input, gst_event_new_eos ());
1608   input2.ts_offset = GST_SECOND * 10;
1609 
1610   run_muxing_test (&input1, &input2);
1611 }
1612 
1613 GST_END_TEST;
1614 
1615 
GST_START_TEST(test_muxing_non_zero_segment_different)1616 GST_START_TEST (test_muxing_non_zero_segment_different)
1617 {
1618   struct TestInputData input1, input2;
1619   GstCaps *caps;
1620 
1621   test_input_data_init (&input1);
1622   test_input_data_init (&input2);
1623 
1624   /* Create the inputs, after calling the run below, all this data is
1625    * transferred to it and we have no need to clean up */
1626   input1.input = NULL;
1627   input1.input =
1628       g_list_append (input1.input, gst_event_new_stream_start ("test-1"));
1629   caps = gst_caps_from_string
1630       ("video/x-raw, width=(int)800, height=(int)600, "
1631       "framerate=(fraction)1/1, format=(string)RGB");
1632   input1.input = g_list_append (input1.input, gst_event_new_caps (caps));
1633   gst_caps_unref (caps);
1634   gst_segment_init (&input1.segment, GST_FORMAT_TIME);
1635   input1.segment.start = 5 * GST_SECOND;
1636   input1.input =
1637       g_list_append (input1.input, gst_event_new_segment (&input1.segment));
1638   input1.input =
1639       g_list_append (input1.input, create_buffer (5 * GST_SECOND,
1640           GST_CLOCK_TIME_NONE, GST_SECOND, 800 * 600 * 3));
1641   input1.input =
1642       g_list_append (input1.input, create_buffer (6 * GST_SECOND,
1643           GST_CLOCK_TIME_NONE, GST_SECOND, 800 * 600 * 3));
1644   input1.input =
1645       g_list_append (input1.input, create_buffer (7 * GST_SECOND,
1646           GST_CLOCK_TIME_NONE, GST_SECOND, 800 * 600 * 3));
1647   input1.input = g_list_append (input1.input, gst_event_new_eos ());
1648   input1.ts_offset = GST_SECOND * 5;
1649 
1650   input2.input = NULL;
1651   input2.input =
1652       g_list_append (input2.input, gst_event_new_stream_start ("test-2"));
1653   caps = gst_caps_from_string (AUDIO_AAC_CAPS_STRING);
1654   input2.input = g_list_append (input2.input, gst_event_new_caps (caps));
1655   gst_caps_unref (caps);
1656   gst_segment_init (&input2.segment, GST_FORMAT_TIME);
1657   input2.segment.start = 10 * GST_SECOND;
1658   input2.input =
1659       g_list_append (input2.input, gst_event_new_segment (&input2.segment));
1660   input2.input =
1661       g_list_append (input2.input, create_buffer (10 * GST_SECOND,
1662           10 * GST_SECOND, GST_SECOND, 4096));
1663   input2.input =
1664       g_list_append (input2.input, create_buffer (11 * GST_SECOND,
1665           11 * GST_SECOND, GST_SECOND, 4096));
1666   input2.input =
1667       g_list_append (input2.input, create_buffer (12 * GST_SECOND,
1668           12 * GST_SECOND, GST_SECOND, 4096));
1669   input2.input = g_list_append (input2.input, gst_event_new_eos ());
1670   input2.ts_offset = GST_SECOND * 10;
1671 
1672   run_muxing_test (&input1, &input2);
1673 }
1674 
1675 GST_END_TEST;
1676 
GST_START_TEST(test_muxing_dts_outside_segment)1677 GST_START_TEST (test_muxing_dts_outside_segment)
1678 {
1679   struct TestInputData input1, input2;
1680   GstCaps *caps;
1681 
1682   test_input_data_init (&input1);
1683   test_input_data_init (&input2);
1684 
1685   /* Create the inputs, after calling the run below, all this data is
1686    * transferred to it and we have no need to clean up */
1687   input1.input = NULL;
1688   input1.input =
1689       g_list_append (input1.input, gst_event_new_stream_start ("test-1"));
1690   caps = gst_caps_from_string
1691       ("video/x-h264, width=(int)800, height=(int)600, "
1692       "framerate=(fraction)1/1, stream-format=(string)avc, codec_data=(buffer)0000,"
1693       " alignment=(string)au, level=(int)2, profile=(string)high");
1694   input1.input = g_list_append (input1.input, gst_event_new_caps (caps));
1695   gst_caps_unref (caps);
1696   gst_segment_init (&input1.segment, GST_FORMAT_TIME);
1697   input1.segment.start = 1 * GST_SECOND;
1698   input1.input =
1699       g_list_append (input1.input, gst_event_new_segment (&input1.segment));
1700   input1.input =
1701       g_list_append (input1.input, create_buffer (1 * GST_SECOND,
1702           0, GST_SECOND, 4096));
1703   input1.input =
1704       g_list_append (input1.input, create_buffer (2 * GST_SECOND,
1705           1 * GST_SECOND, GST_SECOND, 4096));
1706   input1.input =
1707       g_list_append (input1.input, create_buffer (3 * GST_SECOND,
1708           2 * GST_SECOND, GST_SECOND, 4096));
1709   input1.input = g_list_append (input1.input, gst_event_new_eos ());
1710   /* First DTS is 0, first PTS is 1s. The segment start being 1, this means
1711    * running time -1s and 0. So the output segment should start from 1s to keep
1712    * the same running time */
1713   test_input_pop_segment_start (&input1);
1714   test_input_push_segment_start (&input1, GST_SECOND);
1715 
1716   input2.input = NULL;
1717   input2.input =
1718       g_list_append (input2.input, gst_event_new_stream_start ("test-2"));
1719   caps = gst_caps_from_string (AUDIO_AAC_CAPS_STRING);
1720   input2.input = g_list_append (input2.input, gst_event_new_caps (caps));
1721   gst_caps_unref (caps);
1722   gst_segment_init (&input2.segment, GST_FORMAT_TIME);
1723   input2.input =
1724       g_list_append (input2.input, gst_event_new_segment (&input2.segment));
1725   input2.input =
1726       g_list_append (input2.input, create_buffer (0, 0, GST_SECOND,
1727           44100 * 4 * 2));
1728   input2.input =
1729       g_list_append (input2.input, create_buffer (GST_SECOND, GST_SECOND,
1730           GST_SECOND, 44100 * 4 * 2));
1731   input2.input =
1732       g_list_append (input2.input, create_buffer (2 * GST_SECOND,
1733           2 * GST_SECOND, GST_SECOND, 44100 * 4 * 2));
1734   input2.input = g_list_append (input2.input, gst_event_new_eos ());
1735 
1736   run_muxing_test (&input1, &input2);
1737 }
1738 
1739 GST_END_TEST;
1740 
GST_START_TEST(test_muxing_initial_gap)1741 GST_START_TEST (test_muxing_initial_gap)
1742 {
1743   struct TestInputData input1, input2;
1744   GstCaps *caps;
1745 
1746   test_input_data_init (&input1);
1747   test_input_data_init (&input2);
1748 
1749   /* Create the inputs, after calling the run below, all this data is
1750    * transferred to it and we have no need to clean up */
1751   input1.input = NULL;
1752   input1.input =
1753       g_list_append (input1.input, gst_event_new_stream_start ("test-1"));
1754   caps = gst_caps_from_string
1755       ("video/x-h264, width=(int)800, height=(int)600, "
1756       "framerate=(fraction)1/1, stream-format=(string)avc, codec_data=(buffer)0000,"
1757       " alignment=(string)au, level=(int)2, profile=(string)high");
1758   input1.input = g_list_append (input1.input, gst_event_new_caps (caps));
1759   gst_caps_unref (caps);
1760   gst_segment_init (&input1.segment, GST_FORMAT_TIME);
1761   input1.input =
1762       g_list_append (input1.input, gst_event_new_segment (&input1.segment));
1763   /* Duplicate the segment to please the harness */
1764   input1.input =
1765       g_list_append (input1.input, gst_event_new_segment (&input1.segment));
1766   input1.input =
1767       g_list_append (input1.input, create_buffer (1 * GST_SECOND,
1768           0, GST_SECOND, 4096));
1769   input1.input =
1770       g_list_append (input1.input, create_buffer (2 * GST_SECOND,
1771           1 * GST_SECOND, GST_SECOND, 4096));
1772   input1.input =
1773       g_list_append (input1.input, create_buffer (3 * GST_SECOND,
1774           2 * GST_SECOND, GST_SECOND, 4096));
1775   input1.input = g_list_append (input1.input, gst_event_new_eos ());
1776 
1777   /* We expect a 1s gap at the start */
1778   input1.expected_gap_duration = GST_SECOND;
1779   /* There will be two segments, first is 0, so leave it there, second should
1780    * match the first CTTS (PTS - DTS) */
1781   test_input_push_segment_start (&input1, GST_SECOND);
1782 
1783   input2.input = NULL;
1784   input2.input =
1785       g_list_append (input2.input, gst_event_new_stream_start ("test-2"));
1786   caps = gst_caps_from_string (AUDIO_AAC_CAPS_STRING);
1787   input2.input = g_list_append (input2.input, gst_event_new_caps (caps));
1788   gst_caps_unref (caps);
1789   gst_segment_init (&input2.segment, GST_FORMAT_TIME);
1790   input2.input =
1791       g_list_append (input2.input, gst_event_new_segment (&input2.segment));
1792   input2.input =
1793       g_list_append (input2.input, create_buffer (0, 0, GST_SECOND,
1794           44100 * 4 * 2));
1795   input2.input =
1796       g_list_append (input2.input, create_buffer (GST_SECOND, GST_SECOND,
1797           GST_SECOND, 44100 * 4 * 2));
1798   input2.input =
1799       g_list_append (input2.input, create_buffer (2 * GST_SECOND,
1800           2 * GST_SECOND, GST_SECOND, 44100 * 4 * 2));
1801   input2.input = g_list_append (input2.input, gst_event_new_eos ());
1802 
1803   run_muxing_test (&input1, &input2);
1804 
1805   fail_unless (input1.gap_received);
1806 }
1807 
1808 GST_END_TEST;
1809 
GST_START_TEST(test_caps_renego)1810 GST_START_TEST (test_caps_renego)
1811 {
1812   GstHarness *h;
1813   GstBuffer *buf;
1814   GstCaps *caps;
1815   GstSegment segment;
1816   GstPad *pad;
1817 
1818   h = gst_harness_new_with_padnames ("qtmux", "video_0", "src");
1819   /* appease the harness */
1820   pad = gst_element_get_static_pad (h->element, "video_0");
1821 
1822   fail_unless (gst_harness_push_event (h,
1823           gst_event_new_stream_start ("random")));
1824 
1825   /* caps event with not enough information, should probably fail but
1826    * currently only does from aggregate() */
1827   caps = gst_caps_from_string
1828       ("video/x-h264, stream-format=(string)avc, alignment=(string)au, width=(int)16, height=(int)16");
1829   fail_unless (gst_harness_push_event (h, gst_event_new_caps (caps)));
1830   gst_caps_unref (caps);
1831   /* subsequent caps event with enough information should succeed */
1832   caps = gst_caps_from_string
1833       ("video/x-h264, width=(int)800, height=(int)600, "
1834       "framerate=(fraction)1/1, stream-format=(string)avc, codec_data=(buffer)0000,"
1835       " alignment=(string)au, level=(int)2, profile=(string)high");
1836   fail_unless (gst_harness_push_event (h, gst_event_new_caps (caps)));
1837   gst_caps_unref (caps);
1838   gst_segment_init (&segment, GST_FORMAT_TIME);
1839   fail_unless (gst_harness_push_event (h, gst_event_new_segment (&segment)));
1840 
1841   buf = create_buffer (0 * GST_SECOND, 0, GST_SECOND, 4096);
1842   fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (h, buf));
1843 
1844   fail_unless (gst_harness_push_event (h, gst_event_new_eos ()));
1845 
1846   buf = gst_harness_pull (h);
1847   fail_unless (buf != NULL);
1848   gst_buffer_unref (buf);
1849 
1850   gst_harness_teardown (h);
1851   gst_object_unref (pad);
1852 }
1853 
1854 GST_END_TEST;
1855 
1856 static Suite *
qtmux_suite(void)1857 qtmux_suite (void)
1858 {
1859   Suite *s = suite_create ("qtmux");
1860   TCase *tc_chain = tcase_create ("general");
1861 
1862   /* avoid glib warnings when setting deprecated dts-method property */
1863   g_setenv ("G_ENABLE_DIAGNOSTIC", "0", TRUE);
1864 
1865   suite_add_tcase (s, tc_chain);
1866   tcase_add_test (tc_chain, test_video_pad_dd);
1867   tcase_add_test (tc_chain, test_audio_pad_dd);
1868   tcase_add_test (tc_chain, test_video_pad_frag_dd);
1869   tcase_add_test (tc_chain, test_audio_pad_frag_dd);
1870   tcase_add_test (tc_chain, test_video_pad_frag_dd_streamable);
1871   tcase_add_test (tc_chain, test_audio_pad_frag_dd_streamable);
1872   tcase_add_test (tc_chain, test_video_pad_frag_dd_finalise);
1873 
1874   tcase_add_test (tc_chain, test_video_pad_reorder);
1875   tcase_add_test (tc_chain, test_audio_pad_reorder);
1876   tcase_add_test (tc_chain, test_video_pad_frag_reorder);
1877   tcase_add_test (tc_chain, test_audio_pad_frag_reorder);
1878   tcase_add_test (tc_chain, test_video_pad_frag_reorder_streamable);
1879   tcase_add_test (tc_chain, test_audio_pad_frag_reorder_streamable);
1880   tcase_add_test (tc_chain, test_video_pad_frag_reorder_finalise);
1881 
1882   tcase_add_test (tc_chain, test_video_pad_asc);
1883   tcase_add_test (tc_chain, test_audio_pad_asc);
1884   tcase_add_test (tc_chain, test_video_pad_frag_asc);
1885   tcase_add_test (tc_chain, test_audio_pad_frag_asc);
1886   tcase_add_test (tc_chain, test_video_pad_frag_asc_streamable);
1887   tcase_add_test (tc_chain, test_audio_pad_frag_asc_streamable);
1888   tcase_add_test (tc_chain, test_video_pad_frag_asc_finalise);
1889 
1890   tcase_add_test (tc_chain, test_average_bitrate);
1891 
1892   tcase_add_test (tc_chain, test_reuse);
1893   tcase_add_test (tc_chain, test_encodebin_qtmux);
1894   tcase_add_test (tc_chain, test_encodebin_mp4mux);
1895 
1896   tcase_add_test (tc_chain, test_muxing);
1897   tcase_add_test (tc_chain, test_muxing_non_zero_segment);
1898   tcase_add_test (tc_chain, test_muxing_non_zero_segment_different);
1899   tcase_add_test (tc_chain, test_muxing_dts_outside_segment);
1900   tcase_add_test (tc_chain, test_muxing_initial_gap);
1901 
1902   tcase_add_test (tc_chain, test_caps_renego);
1903 
1904   return s;
1905 }
1906 
1907 GST_CHECK_MAIN (qtmux)
1908