1 /* GStreamer unit tests for flvmux
2  *
3  * Copyright (C) 2009 Tim-Philipp Müller  <tim centricular net>
4  * Copyright (C) 2016 Havard Graff <havard@pexip.com>
5  * Copyright (C) 2016 David Buchmann <david@pexip.com>
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 #ifdef HAVE_VALGRIND
28 # include <valgrind/valgrind.h>
29 #endif
30 
31 #include <gst/check/gstcheck.h>
32 #include <gst/check/gstharness.h>
33 
34 #include <gst/gst.h>
35 
36 static GstBusSyncReply
error_cb(GstBus * bus,GstMessage * msg,gpointer user_data)37 error_cb (GstBus * bus, GstMessage * msg, gpointer user_data)
38 {
39   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) {
40     GError *err = NULL;
41     gchar *dbg = NULL;
42 
43     gst_message_parse_error (msg, &err, &dbg);
44     g_error ("ERROR: %s\n%s\n", err->message, dbg);
45   }
46 
47   return GST_BUS_PASS;
48 }
49 
50 static void
handoff_cb(GstElement * element,GstBuffer * buf,GstPad * pad,gint * p_counter)51 handoff_cb (GstElement * element, GstBuffer * buf, GstPad * pad,
52     gint * p_counter)
53 {
54   *p_counter += 1;
55   GST_LOG ("counter = %d", *p_counter);
56 }
57 
58 static void
mux_pcm_audio(guint num_buffers,guint repeat)59 mux_pcm_audio (guint num_buffers, guint repeat)
60 {
61   GstElement *src, *sink, *flvmux, *conv, *pipeline;
62   GstPad *sinkpad, *srcpad;
63   gint counter;
64 
65   GST_LOG ("num_buffers = %u", num_buffers);
66 
67   pipeline = gst_pipeline_new ("pipeline");
68   fail_unless (pipeline != NULL, "Failed to create pipeline!");
69 
70   /* kids, don't use a sync handler for this at home, really; we do because
71    * we just want to abort and nothing else */
72   gst_bus_set_sync_handler (GST_ELEMENT_BUS (pipeline), error_cb, NULL, NULL);
73 
74   src = gst_element_factory_make ("audiotestsrc", "audiotestsrc");
75   fail_unless (src != NULL, "Failed to create 'audiotestsrc' element!");
76 
77   g_object_set (src, "num-buffers", num_buffers, NULL);
78 
79   conv = gst_element_factory_make ("audioconvert", "audioconvert");
80   fail_unless (conv != NULL, "Failed to create 'audioconvert' element!");
81 
82   flvmux = gst_element_factory_make ("flvmux", "flvmux");
83   fail_unless (flvmux != NULL, "Failed to create 'flvmux' element!");
84 
85   sink = gst_element_factory_make ("fakesink", "fakesink");
86   fail_unless (sink != NULL, "Failed to create 'fakesink' element!");
87 
88   g_object_set (sink, "signal-handoffs", TRUE, NULL);
89   g_signal_connect (sink, "handoff", G_CALLBACK (handoff_cb), &counter);
90 
91   gst_bin_add_many (GST_BIN (pipeline), src, conv, flvmux, sink, NULL);
92 
93   fail_unless (gst_element_link (src, conv));
94   fail_unless (gst_element_link (flvmux, sink));
95 
96   /* now link the elements */
97   sinkpad = gst_element_get_request_pad (flvmux, "audio");
98   fail_unless (sinkpad != NULL, "Could not get audio request pad");
99 
100   srcpad = gst_element_get_static_pad (conv, "src");
101   fail_unless (srcpad != NULL, "Could not get audioconvert's source pad");
102 
103   fail_unless_equals_int (gst_pad_link (srcpad, sinkpad), GST_PAD_LINK_OK);
104 
105   gst_object_unref (srcpad);
106   gst_object_unref (sinkpad);
107 
108   do {
109     GstStateChangeReturn state_ret;
110     GstMessage *msg;
111 
112     GST_LOG ("repeat=%d", repeat);
113 
114     counter = 0;
115 
116     state_ret = gst_element_set_state (pipeline, GST_STATE_PAUSED);
117     fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
118 
119     if (state_ret == GST_STATE_CHANGE_ASYNC) {
120       GST_LOG ("waiting for pipeline to reach PAUSED state");
121       state_ret = gst_element_get_state (pipeline, NULL, NULL, -1);
122       fail_unless_equals_int (state_ret, GST_STATE_CHANGE_SUCCESS);
123     }
124 
125     GST_LOG ("PAUSED, let's do the rest of it");
126 
127     state_ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
128     fail_unless (state_ret != GST_STATE_CHANGE_FAILURE);
129 
130     msg = gst_bus_poll (GST_ELEMENT_BUS (pipeline), GST_MESSAGE_EOS, -1);
131     fail_unless (msg != NULL, "Expected EOS message on bus!");
132 
133     GST_LOG ("EOS");
134     gst_message_unref (msg);
135 
136     /* should have some output */
137     fail_unless (counter > 2);
138 
139     fail_unless_equals_int (gst_element_set_state (pipeline, GST_STATE_NULL),
140         GST_STATE_CHANGE_SUCCESS);
141 
142     /* repeat = test re-usability */
143     --repeat;
144   } while (repeat > 0);
145 
146   gst_object_unref (pipeline);
147 }
148 
GST_START_TEST(test_index_writing)149 GST_START_TEST (test_index_writing)
150 {
151   /* note: there's a magic 128 value in flvmux when doing index writing */
152   mux_pcm_audio (__i__ * 33 + 1, 2);
153 }
154 
155 GST_END_TEST;
156 
157 static GstBuffer *
create_buffer(guint8 * data,gsize size,GstClockTime timestamp,GstClockTime duration)158 create_buffer (guint8 * data, gsize size,
159     GstClockTime timestamp, GstClockTime duration)
160 {
161   GstBuffer *buf = gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
162       data, size, 0, size, NULL, NULL);
163   GST_BUFFER_PTS (buf) = timestamp;
164   GST_BUFFER_DTS (buf) = timestamp;
165   GST_BUFFER_DURATION (buf) = duration;
166   GST_BUFFER_OFFSET (buf) = 0;
167   GST_BUFFER_OFFSET_END (buf) = 0;
168   return buf;
169 }
170 
171 guint8 speex_hdr0[] = {
172   0x53, 0x70, 0x65, 0x65, 0x78, 0x20, 0x20, 0x20,
173   0x31, 0x2e, 0x32, 0x72, 0x63, 0x31, 0x00, 0x00,
174   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
175   0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
176   0x50, 0x00, 0x00, 0x00, 0x80, 0x3e, 0x00, 0x00,
177   0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00,
178   0x01, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff,
179   0x40, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
180   0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
181   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
182 };
183 
184 guint8 speex_hdr1[] = {
185   0x1f, 0x00, 0x00, 0x00, 0x45, 0x6e, 0x63, 0x6f,
186   0x64, 0x65, 0x64, 0x20, 0x77, 0x69, 0x74, 0x68,
187   0x20, 0x47, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d,
188   0x65, 0x72, 0x20, 0x53, 0x70, 0x65, 0x65, 0x78,
189   0x65, 0x6e, 0x63, 0x00, 0x00, 0x00, 0x00, 0x01
190 };
191 
192 guint8 speex_buf[] = {
193   0x36, 0x9d, 0x1b, 0x9a, 0x20, 0x00, 0x01, 0x68,
194   0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0xe8, 0x84,
195   0x00, 0xb4, 0x74, 0x74, 0x74, 0x74, 0x74, 0x74,
196   0x74, 0x42, 0x00, 0x5a, 0x3a, 0x3a, 0x3a, 0x3a,
197   0x3a, 0x3a, 0x3a, 0x21, 0x00, 0x2d, 0x1d, 0x1d,
198   0x1d, 0x1d, 0x1d, 0x1d, 0x1d, 0x1b, 0x3b, 0x60,
199   0xab, 0xab, 0xab, 0xab, 0xab, 0x0a, 0xba, 0xba,
200   0xba, 0xba, 0xb0, 0xab, 0xab, 0xab, 0xab, 0xab,
201   0x0a, 0xba, 0xba, 0xba, 0xba, 0xb7
202 };
203 
204 guint8 h264_buf[] = {
205   0x00, 0x00, 0x00, 0x0b, 0x67, 0x42, 0xc0, 0x0c,
206   0x95, 0xa7, 0x20, 0x1e, 0x11, 0x08, 0xd4, 0x00,
207   0x00, 0x00, 0x04, 0x68, 0xce, 0x3c, 0x80, 0x00,
208   0x00, 0x00, 0x55, 0x65, 0xb8, 0x04, 0x0e, 0x7e,
209   0x1f, 0x22, 0x60, 0x34, 0x01, 0xe2, 0x00, 0x3c,
210   0xe1, 0xfc, 0x91, 0x40, 0xa6, 0x9e, 0x07, 0x42,
211   0x56, 0x44, 0x73, 0x75, 0x40, 0x9f, 0x0c, 0x87,
212   0x83, 0xc9, 0x52, 0x60, 0x6d, 0xd8, 0x98, 0x01,
213   0x16, 0xbd, 0x0f, 0xa6, 0xaf, 0x75, 0x83, 0xdd,
214   0xfa, 0xe7, 0x8f, 0xe3, 0x58, 0x10, 0x0f, 0x5c,
215   0x18, 0x2f, 0x41, 0x40, 0x23, 0x0b, 0x03, 0x70,
216   0x00, 0xff, 0xe4, 0xa6, 0x7d, 0x7f, 0x3f, 0x76,
217   0x01, 0xd0, 0x98, 0x2a, 0x0c, 0xb8, 0x02, 0x32,
218   0xbc, 0x56, 0xfd, 0x34, 0x4f, 0xcf, 0xfe, 0xa0,
219 };
220 
GST_START_TEST(test_speex_streamable)221 GST_START_TEST (test_speex_streamable)
222 {
223   GstBuffer *buf;
224   GstMapInfo map = GST_MAP_INFO_INIT;
225 
226 
227   GstCaps *caps = gst_caps_new_simple ("audio/x-speex",
228       "rate", G_TYPE_INT, 16000,
229       "channels", G_TYPE_INT, 1,
230       NULL);
231 
232   const GstClockTime base_time = 123456789;
233   const GstClockTime duration_ms = 20;
234   const GstClockTime duration = duration_ms * GST_MSECOND;
235 
236   GstHarness *h = gst_harness_new_with_padnames ("flvmux", "audio", "src");
237   gst_harness_set_src_caps (h, caps);
238   g_object_set (h->element, "streamable", 1, NULL);
239 
240   /* push speex header0 */
241   gst_harness_push (h, create_buffer (speex_hdr0,
242           sizeof (speex_hdr0), base_time, 0));
243 
244   /* push speex header1 */
245   gst_harness_push (h, create_buffer (speex_hdr1,
246           sizeof (speex_hdr1), base_time, 0));
247 
248   /* push speex data */
249   gst_harness_push (h, create_buffer (speex_buf,
250           sizeof (speex_buf), base_time, duration));
251 
252   /* push speex data 2 */
253   gst_harness_push (h, create_buffer (speex_buf,
254           sizeof (speex_buf), base_time + duration, duration));
255 
256   /* pull out stream-start event */
257   gst_event_unref (gst_harness_pull_event (h));
258 
259   /* pull out caps event */
260   gst_event_unref (gst_harness_pull_event (h));
261 
262   /* pull out segment event and verify we are using GST_FORMAT_TIME */
263   {
264     GstEvent *event = gst_harness_pull_event (h);
265     const GstSegment *segment;
266     gst_event_parse_segment (event, &segment);
267     fail_unless_equals_int (GST_FORMAT_TIME, segment->format);
268     gst_event_unref (event);
269   }
270 
271   /* pull FLV header buffer */
272   buf = gst_harness_pull (h);
273   gst_buffer_unref (buf);
274 
275   /* pull Metadata buffer */
276   buf = gst_harness_pull (h);
277   gst_buffer_unref (buf);
278 
279   /* pull header0 */
280   buf = gst_harness_pull (h);
281   fail_unless_equals_uint64 (base_time, GST_BUFFER_PTS (buf));
282   fail_unless_equals_uint64 (GST_CLOCK_TIME_NONE, GST_BUFFER_DTS (buf));
283   fail_unless_equals_uint64 (0, GST_BUFFER_DURATION (buf));
284   gst_buffer_map (buf, &map, GST_MAP_READ);
285   /* 0x08 means it is audio */
286   fail_unless_equals_int (0x08, map.data[0]);
287   /* timestamp should be starting from 0 */
288   fail_unless_equals_int (0x00, map.data[6]);
289   /* 0xb2 means Speex, 16000Hz, Mono */
290   fail_unless_equals_int (0xb2, map.data[11]);
291   /* verify content is intact */
292   fail_unless_equals_int (0, memcmp (&map.data[12], speex_hdr0,
293           sizeof (speex_hdr0)));
294   gst_buffer_unmap (buf, &map);
295   gst_buffer_unref (buf);
296 
297   /* pull header1 */
298   buf = gst_harness_pull (h);
299   fail_unless_equals_uint64 (base_time, GST_BUFFER_PTS (buf));
300   fail_unless_equals_uint64 (GST_CLOCK_TIME_NONE, GST_BUFFER_DTS (buf));
301   fail_unless_equals_uint64 (0, GST_BUFFER_DURATION (buf));
302   gst_buffer_map (buf, &map, GST_MAP_READ);
303   /* 0x08 means it is audio */
304   fail_unless_equals_int (0x08, map.data[0]);
305   /* timestamp should be starting from 0 */
306   fail_unless_equals_int (0x00, map.data[6]);
307   /* 0xb2 means Speex, 16000Hz, Mono */
308   fail_unless_equals_int (0xb2, map.data[11]);
309   /* verify content is intact */
310   fail_unless_equals_int (0, memcmp (&map.data[12], speex_hdr1,
311           sizeof (speex_hdr1)));
312   gst_buffer_unmap (buf, &map);
313   gst_buffer_unref (buf);
314 
315   /* pull data */
316   buf = gst_harness_pull (h);
317   fail_unless_equals_uint64 (base_time, GST_BUFFER_PTS (buf));
318   fail_unless_equals_uint64 (GST_CLOCK_TIME_NONE, GST_BUFFER_DTS (buf));
319   fail_unless_equals_uint64 (duration, GST_BUFFER_DURATION (buf));
320   fail_unless_equals_uint64 (GST_BUFFER_OFFSET_NONE, GST_BUFFER_OFFSET (buf));
321   fail_unless_equals_uint64 (GST_BUFFER_OFFSET_NONE,
322       GST_BUFFER_OFFSET_END (buf));
323   gst_buffer_map (buf, &map, GST_MAP_READ);
324   /* 0x08 means it is audio */
325   fail_unless_equals_int (0x08, map.data[0]);
326   /* timestamp should be starting from 0 */
327   fail_unless_equals_int (0x00, map.data[6]);
328   /* 0xb2 means Speex, 16000Hz, Mono */
329   fail_unless_equals_int (0xb2, map.data[11]);
330   /* verify content is intact */
331   fail_unless_equals_int (0, memcmp (&map.data[12], speex_buf,
332           sizeof (speex_buf)));
333   gst_buffer_unmap (buf, &map);
334   gst_buffer_unref (buf);
335 
336   /* pull data */
337   buf = gst_harness_pull (h);
338   fail_unless_equals_uint64 (base_time + duration, GST_BUFFER_PTS (buf));
339   fail_unless_equals_uint64 (GST_CLOCK_TIME_NONE, GST_BUFFER_DTS (buf));
340   fail_unless_equals_uint64 (duration, GST_BUFFER_DURATION (buf));
341   fail_unless_equals_uint64 (GST_BUFFER_OFFSET_NONE, GST_BUFFER_OFFSET (buf));
342   fail_unless_equals_uint64 (GST_BUFFER_OFFSET_NONE,
343       GST_BUFFER_OFFSET_END (buf));
344   gst_buffer_map (buf, &map, GST_MAP_READ);
345   /* 0x08 means it is audio */
346   fail_unless_equals_int (0x08, map.data[0]);
347   /* timestamp should reflect the duration_ms */
348   fail_unless_equals_int (duration_ms, map.data[6]);
349   /* 0xb2 means Speex, 16000Hz, Mono */
350   fail_unless_equals_int (0xb2, map.data[11]);
351   /* verify content is intact */
352   fail_unless_equals_int (0, memcmp (&map.data[12], speex_buf,
353           sizeof (speex_buf)));
354   gst_buffer_unmap (buf, &map);
355   gst_buffer_unref (buf);
356 
357   gst_harness_teardown (h);
358 }
359 
360 GST_END_TEST;
361 
362 static void
check_buf_type_timestamp(GstBuffer * buf,gint packet_type,gint timestamp)363 check_buf_type_timestamp (GstBuffer * buf, gint packet_type, gint timestamp)
364 {
365   GstMapInfo map = GST_MAP_INFO_INIT;
366   gst_buffer_map (buf, &map, GST_MAP_READ);
367   fail_unless_equals_int (packet_type, map.data[0]);
368   fail_unless_equals_int (timestamp, map.data[6]);
369   gst_buffer_unmap (buf, &map);
370   gst_buffer_unref (buf);
371 }
372 
373 static const gint AUDIO = 0x08;
374 static const gint VIDEO = 0x09;
375 
GST_START_TEST(test_increasing_timestamp_when_pts_none)376 GST_START_TEST (test_increasing_timestamp_when_pts_none)
377 {
378   gint timestamp = 3;
379   GstClockTime base_time = 42 * GST_SECOND;
380   GstPad *audio_sink, *video_sink, *audio_src, *video_src;
381   GstHarness *h, *audio, *video, *audio_q, *video_q;
382   GstCaps *audio_caps, *video_caps;
383   GstBuffer *buf;
384 
385   h = gst_harness_new_with_padnames ("flvmux", NULL, "src");
386   audio = gst_harness_new_with_element (h->element, "audio", NULL);
387   video = gst_harness_new_with_element (h->element, "video", NULL);
388   audio_q = gst_harness_new ("queue");
389   video_q = gst_harness_new ("queue");
390 
391   audio_sink = GST_PAD_PEER (audio->srcpad);
392   video_sink = GST_PAD_PEER (video->srcpad);
393   audio_src = GST_PAD_PEER (audio_q->sinkpad);
394   video_src = GST_PAD_PEER (video_q->sinkpad);
395 
396   gst_pad_unlink (audio->srcpad, audio_sink);
397   gst_pad_unlink (video->srcpad, video_sink);
398   gst_pad_unlink (audio_src, audio_q->sinkpad);
399   gst_pad_unlink (video_src, video_q->sinkpad);
400   gst_pad_link (audio_src, audio_sink);
401   gst_pad_link (video_src, video_sink);
402 
403   audio_caps = gst_caps_new_simple ("audio/x-speex",
404       "rate", G_TYPE_INT, 16000, "channels", G_TYPE_INT, 1, NULL);
405   gst_harness_set_src_caps (audio_q, audio_caps);
406   video_caps = gst_caps_new_simple ("video/x-h264",
407       "stream-format", G_TYPE_STRING, "avc", NULL);
408   gst_harness_set_src_caps (video_q, video_caps);
409 
410   /* Push audio + video + audio with increasing DTS, but PTS for video is
411    * GST_CLOCK_TIME_NONE
412    */
413   buf = gst_buffer_new ();
414   GST_BUFFER_DTS (buf) = timestamp * GST_MSECOND + base_time;
415   GST_BUFFER_PTS (buf) = timestamp * GST_MSECOND + base_time;
416   gst_harness_push (audio_q, buf);
417 
418   buf = gst_buffer_new ();
419   GST_BUFFER_DTS (buf) = (timestamp + 1) * GST_MSECOND + base_time;
420   GST_BUFFER_PTS (buf) = GST_CLOCK_TIME_NONE;
421   gst_harness_push (video_q, buf);
422 
423   buf = gst_buffer_new ();
424   GST_BUFFER_DTS (buf) = (timestamp + 2) * GST_MSECOND + base_time;
425   GST_BUFFER_PTS (buf) = (timestamp + 2) * GST_MSECOND + base_time;
426   gst_harness_push (audio_q, buf);
427 
428   /* Pull two metadata packets out */
429   gst_buffer_unref (gst_harness_pull (h));
430   gst_buffer_unref (gst_harness_pull (h));
431 
432   /* Check that we receive the packets in monotonically increasing order and
433    * that their timestamps are correct (should start at 0)
434    */
435   buf = gst_harness_pull (h);
436   check_buf_type_timestamp (buf, AUDIO, 0);
437   buf = gst_harness_pull (h);
438   check_buf_type_timestamp (buf, VIDEO, 1);
439 
440   /* teardown */
441   gst_harness_teardown (h);
442   gst_harness_teardown (audio);
443   gst_harness_teardown (video);
444   gst_harness_teardown (audio_q);
445   gst_harness_teardown (video_q);
446 }
447 
448 GST_END_TEST;
449 
450 typedef struct
451 {
452   GstHarness *a_sink;
453   GstHarness *v_sink;
454 } DemuxHarnesses;
455 
456 static void
flvdemux_pad_added(GstElement * flvdemux,GstPad * srcpad,DemuxHarnesses * h)457 flvdemux_pad_added (GstElement * flvdemux, GstPad * srcpad, DemuxHarnesses * h)
458 {
459   GstCaps *caps = gst_pad_get_current_caps (srcpad);
460   const gchar *name = gst_structure_get_name (gst_caps_get_structure (caps, 0));
461 
462   if (g_ascii_strncasecmp ("audio", name, 5) == 0)
463     gst_harness_add_element_src_pad (h->a_sink, srcpad);
464   else
465     gst_harness_add_element_src_pad (h->v_sink, srcpad);
466 
467   gst_caps_unref (caps);
468 }
469 
GST_START_TEST(test_video_caps_late)470 GST_START_TEST (test_video_caps_late)
471 {
472   GstHarness *mux = gst_harness_new_with_padnames ("flvmux", NULL, "src");
473   GstHarness *a_src =
474       gst_harness_new_with_element (mux->element, "audio", NULL);
475   GstHarness *v_src =
476       gst_harness_new_with_element (mux->element, "video", NULL);
477   GstHarness *demux = gst_harness_new_with_padnames ("flvdemux", "sink", NULL);
478   GstHarness *a_sink =
479       gst_harness_new_with_element (demux->element, NULL, NULL);
480   GstHarness *v_sink =
481       gst_harness_new_with_element (demux->element, NULL, NULL);
482   DemuxHarnesses harnesses = { a_sink, v_sink };
483   guint i;
484   GstTestClock *tclock;
485 
486   g_object_set (mux->element, "streamable", TRUE,
487       "latency", G_GUINT64_CONSTANT (1), NULL);
488   gst_harness_use_testclock (mux);
489 
490   g_signal_connect (demux->element, "pad-added",
491       G_CALLBACK (flvdemux_pad_added), &harnesses);
492   gst_harness_add_sink_harness (mux, demux);
493 
494   gst_harness_set_src_caps_str (a_src,
495       "audio/x-speex, rate=(int)16000, channels=(int)1");
496 
497   fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (a_src,
498           gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
499               speex_hdr0, sizeof (speex_hdr0), 0, sizeof (speex_hdr0), NULL,
500               NULL)));
501 
502   fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (a_src,
503           gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
504               speex_hdr1, sizeof (speex_hdr1), 0, sizeof (speex_hdr1), NULL,
505               NULL)));
506 
507   /* Wait a little and make sure no clock was scheduled as this shouldn't happen
508    * before the caps are set */
509   g_usleep (40 * 1000);
510   tclock = gst_harness_get_testclock (mux);
511   fail_unless (gst_test_clock_get_next_entry_time (tclock) ==
512       GST_CLOCK_TIME_NONE);
513 
514   gst_harness_set_src_caps_str (v_src,
515       "video/x-h264, stream-format=(string)avc, alignment=(string)au, "
516       "codec_data=(buffer)0142c00cffe1000b6742c00c95a7201e1108d401000468ce3c80");
517 
518   gst_harness_crank_single_clock_wait (mux);
519 
520   fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (a_src,
521           gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
522               speex_buf, sizeof (speex_buf), 0, sizeof (speex_buf), NULL,
523               NULL)));
524 
525   fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (v_src,
526           gst_buffer_new_wrapped_full (GST_MEMORY_FLAG_READONLY,
527               h264_buf, sizeof (h264_buf), 0, sizeof (h264_buf), NULL, NULL)));
528 
529   gst_harness_crank_single_clock_wait (mux);
530   gst_harness_crank_single_clock_wait (mux);
531   gst_harness_crank_single_clock_wait (mux);
532 
533 
534   /* push from flvmux to demux */
535   for (i = 0; i < 6; i++)
536     gst_harness_push_to_sink (mux);
537 
538   /* verify we got 2x audio and 1x video buffers out of flvdemux */
539   gst_buffer_unref (gst_harness_pull (a_sink));
540   gst_buffer_unref (gst_harness_pull (a_sink));
541   gst_buffer_unref (gst_harness_pull (v_sink));
542 
543   fail_unless (gst_test_clock_get_next_entry_time (tclock) ==
544       GST_CLOCK_TIME_NONE);
545 
546   g_clear_object (&tclock);
547   gst_harness_teardown (a_src);
548   gst_harness_teardown (v_src);
549   gst_harness_teardown (mux);
550   gst_harness_teardown (a_sink);
551   gst_harness_teardown (v_sink);
552 }
553 
554 GST_END_TEST;
555 
556 guint8 raw_frame_short[] = {
557   0x27, 0x00, 0x03, 0x20, 0x64, 0x1c
558 };
559 
GST_START_TEST(test_video_caps_change_streamable)560 GST_START_TEST (test_video_caps_change_streamable)
561 {
562   GstEvent *event;
563   GstCaps *a_caps1, *v_caps1, *v_caps2, *ret_caps;
564   GstHarness *mux = gst_harness_new_with_padnames ("flvmux", NULL, "src");
565   GstHarness *a_src =
566       gst_harness_new_with_element (mux->element, "audio", NULL);
567   GstHarness *v_src =
568       gst_harness_new_with_element (mux->element, "video", NULL);
569   GstHarness *demux = gst_harness_new_with_padnames ("flvdemux", "sink", NULL);
570   GstHarness *a_sink =
571       gst_harness_new_with_element (demux->element, NULL, NULL);
572   GstHarness *v_sink =
573       gst_harness_new_with_element (demux->element, NULL, NULL);
574   DemuxHarnesses harnesses = { a_sink, v_sink };
575   guint i;
576 
577   const GstClockTime base_time = 123456789;
578   const GstClockTime duration_ms = 20;
579   const GstClockTime duration = duration_ms * GST_MSECOND;
580 
581   g_object_set (mux->element, "streamable", TRUE, NULL);
582 
583   g_signal_connect (demux->element, "pad-added",
584       G_CALLBACK (flvdemux_pad_added), &harnesses);
585   gst_harness_add_sink_harness (mux, demux);
586 
587   a_caps1 =
588       gst_caps_from_string
589       ("audio/mpeg, mpegversion=(int)4, framed=(boolean)true, stream-format=(string)raw, "
590       "rate=(int)44100, channels=(int)1, codec_data=(buffer)1208");
591 
592   v_caps1 = gst_caps_from_string ("video/x-h264, stream-format=(string)avc, "
593       "codec_data=(buffer)0142c00cffe1000b6742c00c95a7201e1108d401000468ce3c80");
594 
595   gst_harness_set_src_caps (a_src, gst_caps_ref (a_caps1));
596   gst_harness_set_src_caps (v_src, gst_caps_ref (v_caps1));
597 
598   fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (a_src,
599           create_buffer (raw_frame_short, sizeof (raw_frame_short), base_time,
600               duration)));
601 
602   fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (v_src,
603           create_buffer (h264_buf, sizeof (h264_buf), base_time, duration)));
604 
605   gst_harness_crank_single_clock_wait (mux);
606 
607   /* push from flvmux to demux */
608   for (i = 0; i < 6; i++) {
609     gst_harness_push_to_sink (mux);
610   }
611 
612   /* should accept without the constraint */
613   while ((event = gst_harness_try_pull_event (v_sink))) {
614     if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
615       gst_event_parse_caps (event, &ret_caps);
616       GST_LOG ("v_caps1 %" GST_PTR_FORMAT ", ret caps %" GST_PTR_FORMAT,
617           v_caps1, ret_caps);
618       fail_unless (gst_caps_is_equal (v_caps1, ret_caps));
619     }
620     gst_event_unref (event);
621   }
622 
623   /* caps change */
624   v_caps2 = gst_caps_from_string ("video/x-h264, stream-format=(string)avc, "
625       "codec_data=(buffer)0164001fffe1001c6764001facd9405005bb016a02020280000003008000001e478c18cb01000568ebecb22c");
626 
627   gst_harness_set_src_caps (v_src, gst_caps_ref (v_caps2));
628 
629   fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (v_src,
630           create_buffer (h264_buf, sizeof (h264_buf), base_time + duration,
631               duration)));
632 
633   gst_harness_crank_single_clock_wait (mux);
634 
635   /* push from flvmux to demux */
636   for (i = 0; i < 2; i++) {
637     gst_harness_push_to_sink (mux);
638   }
639 
640   /* should accept without the constraint */
641   while ((event = gst_harness_try_pull_event (v_sink))) {
642     if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
643       gst_event_parse_caps (event, &ret_caps);
644       GST_LOG ("v_caps2 %" GST_PTR_FORMAT ", ret caps %" GST_PTR_FORMAT,
645           v_caps2, ret_caps);
646       fail_unless (gst_caps_is_equal (v_caps2, ret_caps));
647     }
648     gst_event_unref (event);
649   }
650 
651   /* verify we got 1x audio and 2x video buffers out of flvdemux */
652   gst_buffer_unref (gst_harness_pull (a_sink));
653   gst_buffer_unref (gst_harness_pull (v_sink));
654   gst_buffer_unref (gst_harness_pull (v_sink));
655   gst_caps_unref (a_caps1);
656   gst_caps_unref (v_caps1);
657   gst_caps_unref (v_caps2);
658 
659   gst_harness_teardown (a_src);
660   gst_harness_teardown (v_src);
661   gst_harness_teardown (mux);
662   gst_harness_teardown (a_sink);
663   gst_harness_teardown (v_sink);
664 }
665 
666 GST_END_TEST;
667 
GST_START_TEST(test_audio_caps_change_streamable)668 GST_START_TEST (test_audio_caps_change_streamable)
669 {
670   GstEvent *event;
671   GstCaps *a_caps1, *a_caps2, *v_caps1, *ret_caps;
672   GstHarness *mux = gst_harness_new_with_padnames ("flvmux", NULL, "src");
673   GstHarness *a_src =
674       gst_harness_new_with_element (mux->element, "audio", NULL);
675   GstHarness *v_src =
676       gst_harness_new_with_element (mux->element, "video", NULL);
677   GstHarness *demux = gst_harness_new_with_padnames ("flvdemux", "sink", NULL);
678   GstHarness *a_sink =
679       gst_harness_new_with_element (demux->element, NULL, NULL);
680   GstHarness *v_sink =
681       gst_harness_new_with_element (demux->element, NULL, NULL);
682   DemuxHarnesses harnesses = { a_sink, v_sink };
683   guint i;
684 
685   const GstClockTime base_time = 123456789;
686   const GstClockTime duration_ms = 20;
687   const GstClockTime duration = duration_ms * GST_MSECOND;
688 
689   g_object_set (mux->element, "streamable", TRUE, NULL);
690 
691   g_signal_connect (demux->element, "pad-added",
692       G_CALLBACK (flvdemux_pad_added), &harnesses);
693   gst_harness_add_sink_harness (mux, demux);
694 
695   a_caps1 =
696       gst_caps_from_string
697       ("audio/mpeg, mpegversion=(int)4, framed=(boolean)true, stream-format=(string)raw, "
698       "rate=(int)44100, channels=(int)1, codec_data=(buffer)1208");
699 
700   v_caps1 = gst_caps_from_string ("video/x-h264, stream-format=(string)avc, "
701       "codec_data=(buffer)0142c00cffe1000b6742c00c95a7201e1108d401000468ce3c80");
702 
703   gst_harness_set_src_caps (a_src, gst_caps_ref (a_caps1));
704   gst_harness_set_src_caps (v_src, gst_caps_ref (v_caps1));
705 
706   fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (a_src,
707           create_buffer (raw_frame_short, sizeof (raw_frame_short), base_time,
708               duration)));
709 
710   fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (v_src,
711           create_buffer (h264_buf, sizeof (h264_buf), base_time, duration)));
712 
713   gst_harness_crank_single_clock_wait (mux);
714 
715   /* push from flvmux to demux */
716   for (i = 0; i < 6; i++) {
717     gst_harness_push_to_sink (mux);
718   }
719 
720   /* should accept without the constraint */
721   while ((event = gst_harness_try_pull_event (a_sink))) {
722     if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
723       gst_event_parse_caps (event, &ret_caps);
724       GST_LOG ("a_caps1 %" GST_PTR_FORMAT ", ret caps %" GST_PTR_FORMAT,
725           a_caps1, ret_caps);
726       fail_unless (gst_caps_is_equal (a_caps1, ret_caps));
727     }
728     gst_event_unref (event);
729   }
730 
731   /* caps change */
732   a_caps2 =
733       gst_caps_from_string
734       ("audio/mpeg, mpegversion=(int)4, framed=(boolean)true, stream-format=(string)raw, "
735       "rate=(int)22050, channels=(int)1, codec_data=(buffer)1388");
736 
737   gst_harness_set_src_caps (a_src, gst_caps_ref (a_caps2));
738 
739   fail_unless_equals_int (GST_FLOW_OK, gst_harness_push (a_src,
740           create_buffer (raw_frame_short, sizeof (raw_frame_short),
741               base_time + duration, duration)));
742 
743   gst_harness_crank_single_clock_wait (mux);
744 
745   /* push from flvmux to demux */
746   for (i = 0; i < 2; i++) {
747     gst_harness_push_to_sink (mux);
748   }
749 
750   /* should accept without the constraint */
751   while ((event = gst_harness_try_pull_event (a_sink))) {
752     if (GST_EVENT_TYPE (event) == GST_EVENT_CAPS) {
753       gst_event_parse_caps (event, &ret_caps);
754       GST_LOG ("a_caps2 %" GST_PTR_FORMAT ", ret caps %" GST_PTR_FORMAT,
755           a_caps2, ret_caps);
756       fail_unless (gst_caps_is_equal (a_caps2, ret_caps));
757     }
758     gst_event_unref (event);
759   }
760 
761   /* verify we got 2x audio and 1x video buffers out of flvdemux */
762   gst_buffer_unref (gst_harness_pull (a_sink));
763   gst_buffer_unref (gst_harness_pull (a_sink));
764   gst_buffer_unref (gst_harness_pull (v_sink));
765   gst_caps_unref (a_caps1);
766   gst_caps_unref (a_caps2);
767   gst_caps_unref (v_caps1);
768 
769   gst_harness_teardown (a_src);
770   gst_harness_teardown (v_src);
771   gst_harness_teardown (mux);
772   gst_harness_teardown (a_sink);
773   gst_harness_teardown (v_sink);
774 }
775 
776 GST_END_TEST;
777 
778 typedef struct
779 {
780   guint media_type;
781   gint ts;                      /* timestamp in ms */
782   gint rt;                      /* running_time in ms */
783 } InputData;
784 
GST_START_TEST(test_incrementing_timestamps)785 GST_START_TEST (test_incrementing_timestamps)
786 {
787   GstPad *audio_sink, *video_sink, *audio_src, *video_src;
788   GstHarness *h, *audio, *video, *audio_q, *video_q;
789   GstTestClock *tclock;
790   guint i;
791   guint32 prev_pts;
792   InputData input[] = {
793     {AUDIO, 155, 175},
794     {VIDEO, 156, 191},
795     {VIDEO, 190, 191},
796     {AUDIO, 176, 195},
797     {AUDIO, 197, 215},
798   };
799 
800   /* setup flvmuxer with queues in front */
801   h = gst_harness_new_with_padnames ("flvmux", NULL, "src");
802   audio = gst_harness_new_with_element (h->element, "audio", NULL);
803   video = gst_harness_new_with_element (h->element, "video", NULL);
804   audio_q = gst_harness_new ("queue");
805   video_q = gst_harness_new ("queue");
806   audio_sink = GST_PAD_PEER (audio->srcpad);
807   video_sink = GST_PAD_PEER (video->srcpad);
808   audio_src = GST_PAD_PEER (audio_q->sinkpad);
809   video_src = GST_PAD_PEER (video_q->sinkpad);
810   gst_pad_unlink (audio->srcpad, audio_sink);
811   gst_pad_unlink (video->srcpad, video_sink);
812   gst_pad_unlink (audio_src, audio_q->sinkpad);
813   gst_pad_unlink (video_src, video_q->sinkpad);
814   gst_pad_link (audio_src, audio_sink);
815   gst_pad_link (video_src, video_sink);
816   g_object_set (h->element, "streamable", TRUE, NULL);
817 
818   gst_harness_set_src_caps_str (audio_q,
819       "audio/mpeg, mpegversion=(int)4, "
820       "rate=(int)44100, channels=(int)1, "
821       "stream-format=(string)raw, codec_data=(buffer)1208");
822 
823   gst_harness_set_src_caps_str (video_q,
824       "video/x-h264, stream-format=(string)avc, alignment=(string)au, "
825       "codec_data=(buffer)0142c00dffe1000d6742c00d95a0507c807844235001000468ce3c80");
826 
827   tclock = gst_harness_get_testclock (h);
828 
829   for (i = 0; i < G_N_ELEMENTS (input); i++) {
830     InputData *d = &input[i];
831     GstBuffer *buf = gst_buffer_new ();
832     GstClockTime now = d->rt * GST_MSECOND;
833     GstClockID pending, res;
834 
835     GST_BUFFER_DTS (buf) = GST_BUFFER_PTS (buf) = d->ts * GST_MSECOND;
836     gst_test_clock_set_time (tclock, now);
837 
838     if (d->media_type == AUDIO)
839       gst_harness_push (audio_q, buf);
840     else
841       gst_harness_push (video_q, buf);
842 
843     gst_test_clock_wait_for_next_pending_id (tclock, &pending);
844     res = gst_test_clock_process_next_clock_id (tclock);
845     gst_clock_id_unref (pending);
846     gst_clock_id_unref (res);
847   }
848 
849   /* pull the flv metadata */
850   gst_buffer_unref (gst_harness_pull (h));
851   gst_buffer_unref (gst_harness_pull (h));
852   gst_buffer_unref (gst_harness_pull (h));
853 
854   /* verify pts in the flvheader is increasing */
855   prev_pts = 0;
856   for (i = 0; i < G_N_ELEMENTS (input); i++) {
857     GstBuffer *buf = gst_harness_pull (h);
858     GstMapInfo map;
859     guint32 pts;
860     gst_buffer_map (buf, &map, GST_MAP_READ);
861     pts = GST_READ_UINT24_BE (map.data + 4);
862     GST_DEBUG ("media=%u, pts = %u\n", map.data[0], pts);
863     fail_unless (pts >= prev_pts);
864     prev_pts = pts;
865     gst_buffer_unmap (buf, &map);
866     gst_buffer_unref (buf);
867   }
868 
869   /* teardown */
870   gst_object_unref (tclock);
871   gst_harness_teardown (h);
872   gst_harness_teardown (audio);
873   gst_harness_teardown (video);
874   gst_harness_teardown (audio_q);
875   gst_harness_teardown (video_q);
876 }
877 
878 GST_END_TEST;
879 
880 static Suite *
flvmux_suite(void)881 flvmux_suite (void)
882 {
883   Suite *s = suite_create ("flvmux");
884   TCase *tc_chain = tcase_create ("general");
885   gint loop = 16;
886 
887   suite_add_tcase (s, tc_chain);
888 
889 #ifdef HAVE_VALGRIND
890   if (RUNNING_ON_VALGRIND) {
891     loop = 1;
892   }
893 #endif
894 
895   tcase_add_loop_test (tc_chain, test_index_writing, 0, loop);
896 
897   tcase_add_test (tc_chain, test_speex_streamable);
898   tcase_add_test (tc_chain, test_increasing_timestamp_when_pts_none);
899   tcase_add_test (tc_chain, test_video_caps_late);
900   tcase_add_test (tc_chain, test_audio_caps_change_streamable);
901   tcase_add_test (tc_chain, test_video_caps_change_streamable);
902   tcase_add_test (tc_chain, test_incrementing_timestamps);
903 
904   return s;
905 }
906 
907 GST_CHECK_MAIN (flvmux)
908