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