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