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, ×tamp, &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