• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer
2  *
3  * Copyright (C) 2011 Alessandro Decina <alessandro.d@gmail.com>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public
16  * License along with this library; if not, write to the
17  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  */
20 
21 #include <gst/check/gstcheck.h>
22 #include <string.h>
23 #include <gst/video/video.h>
24 
25 static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
26     GST_PAD_SINK,
27     GST_PAD_ALWAYS,
28     GST_STATIC_CAPS_ANY);
29 
30 static GstStaticPadTemplate video_src_template = GST_STATIC_PAD_TEMPLATE ("src",
31     GST_PAD_SRC,
32     GST_PAD_ALWAYS,
33     GST_STATIC_CAPS ("video/x-h264")
34     );
35 
36 static GstStaticPadTemplate audio_src_template = GST_STATIC_PAD_TEMPLATE ("src",
37     GST_PAD_SRC,
38     GST_PAD_ALWAYS,
39     GST_STATIC_CAPS ("audio/mpeg")
40     );
41 
42 /* For ease of programming we use globals to keep refs for our floating
43  * src and sink pads we create; otherwise we always have to do get_pad,
44  * get_peer, and then remove references in every test function */
45 static GstPad *mysrcpad, *mysinkpad;
46 
47 #define AUDIO_CAPS_STRING "audio/mpeg, " \
48                         "channels = (int) 1, " \
49                         "rate = (int) 8000, " \
50                         "mpegversion = (int) 1, "\
51                         "parsed = (boolean) true "
52 #define VIDEO_CAPS_STRING "video/x-h264, " \
53                           "stream-format = (string) byte-stream, " \
54                           "alignment = (string) nal, " \
55                           "parsed = (boolean) true "
56 
57 #define KEYFRAME_DISTANCE 10
58 
59 typedef void (CheckOutputBuffersFunc) (GList * buffers);
60 
61 /* setup and teardown needs some special handling for muxer */
62 static GstPad *
setup_src_pad(GstElement * element,GstStaticPadTemplate * template,const gchar * sinkname,gchar ** padname)63 setup_src_pad (GstElement * element,
64     GstStaticPadTemplate * template, const gchar * sinkname, gchar ** padname)
65 {
66   GstPad *srcpad, *sinkpad;
67 
68   GST_DEBUG_OBJECT (element, "setting up sending pad");
69   /* sending pad */
70   srcpad = gst_pad_new_from_static_template (template, "src");
71   fail_if (srcpad == NULL, "Could not create a srcpad");
72 
73   if (!(sinkpad = gst_element_get_static_pad (element, sinkname)))
74     sinkpad = gst_element_request_pad_simple (element, sinkname);
75   fail_if (sinkpad == NULL, "Could not get sink pad from %s",
76       GST_ELEMENT_NAME (element));
77   /* we can't test the reference count of the sinkpad here because it's either
78    * 2 or 3: 1 by us, 1 by tsmux and potentially another one by the srcpad
79    * task of tsmux if it just happens to iterate over the pads */
80   fail_unless (gst_pad_link (srcpad, sinkpad) == GST_PAD_LINK_OK,
81       "Could not link source and %s sink pads", GST_ELEMENT_NAME (element));
82 
83   if (padname)
84     *padname = g_strdup (GST_PAD_NAME (sinkpad));
85 
86   gst_object_unref (sinkpad);   /* because we got it higher up */
87 
88   return srcpad;
89 }
90 
91 static void
teardown_src_pad(GstElement * element,const gchar * sinkname)92 teardown_src_pad (GstElement * element, const gchar * sinkname)
93 {
94   GstPad *srcpad, *sinkpad;
95 
96   /* clean up floating src pad */
97   if (!(sinkpad = gst_element_get_static_pad (element, sinkname)))
98     sinkpad = gst_element_request_pad_simple (element, sinkname);
99   srcpad = gst_pad_get_peer (sinkpad);
100 
101   gst_pad_unlink (srcpad, sinkpad);
102   GST_DEBUG ("src %p", srcpad);
103 
104   gst_object_unref (sinkpad);
105   gst_object_unref (srcpad);
106   gst_object_unref (srcpad);
107 
108 }
109 
110 static GstElement *
setup_tsmux(GstStaticPadTemplate * srctemplate,const gchar * sinkname,gchar ** padname)111 setup_tsmux (GstStaticPadTemplate * srctemplate, const gchar * sinkname,
112     gchar ** padname)
113 {
114   GstElement *mux;
115 
116   GST_DEBUG ("setup_tsmux");
117   mux = gst_check_setup_element ("mpegtsmux");
118   mysrcpad = setup_src_pad (mux, srctemplate, sinkname, padname);
119   mysinkpad = gst_check_setup_sink_pad (mux, &sink_template);
120   gst_pad_set_active (mysrcpad, TRUE);
121   gst_pad_set_active (mysinkpad, TRUE);
122 
123   return mux;
124 }
125 
126 static void
cleanup_tsmux(GstElement * mux,const gchar * sinkname)127 cleanup_tsmux (GstElement * mux, const gchar * sinkname)
128 {
129   GST_DEBUG ("cleanup_mux");
130   gst_element_set_state (mux, GST_STATE_NULL);
131 
132   gst_pad_set_active (mysrcpad, FALSE);
133   gst_pad_set_active (mysinkpad, FALSE);
134   teardown_src_pad (mux, sinkname);
135   gst_check_teardown_sink_pad (mux);
136   gst_check_teardown_element (mux);
137 }
138 
139 static void
check_tsmux_pad_given_muxer(GstElement * mux,const gchar * src_caps_string,gint pes_id,gint pmt_id,CheckOutputBuffersFunc check_func,guint n_bufs,gssize input_buf_size)140 check_tsmux_pad_given_muxer (GstElement * mux,
141     const gchar * src_caps_string, gint pes_id, gint pmt_id,
142     CheckOutputBuffersFunc check_func, guint n_bufs, gssize input_buf_size)
143 {
144   GstClockTime ts;
145   GstBuffer *inbuffer, *outbuffer;
146   GstCaps *caps;
147   gint num_buffers;
148   gint i;
149   gint pmt_pid = -1, el_pid = -1, pcr_pid = -1, packets = 0;
150   GstQuery *drain;
151 
152   caps = gst_caps_from_string (src_caps_string);
153   gst_check_setup_events (mysrcpad, mux, caps, GST_FORMAT_TIME);
154   gst_caps_unref (caps);
155 
156   ts = 0;
157   for (i = 0; i < n_bufs; ++i) {
158     GstFlowReturn flow;
159 
160     if (input_buf_size >= 0)
161       inbuffer = gst_buffer_new_and_alloc (input_buf_size);
162     else
163       inbuffer = gst_buffer_new_and_alloc (g_random_int_range (1, 49141));
164 
165     GST_BUFFER_TIMESTAMP (inbuffer) = ts;
166     ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
167 
168     if (i % KEYFRAME_DISTANCE == 0 && pes_id == 0xe0) {
169       GST_TRACE ("input keyframe");
170       GST_BUFFER_FLAG_UNSET (inbuffer, GST_BUFFER_FLAG_DELTA_UNIT);
171     } else {
172       GST_TRACE ("input delta");
173       GST_BUFFER_FLAG_SET (inbuffer, GST_BUFFER_FLAG_DELTA_UNIT);
174     }
175     flow = gst_pad_push (mysrcpad, inbuffer);
176     if (flow != GST_FLOW_OK)
177       fail ("Got %s flow instead of OK", gst_flow_get_name (flow));
178     ts += 40 * GST_MSECOND;
179   }
180 
181   drain = gst_query_new_drain ();
182   gst_pad_peer_query (mysrcpad, drain);
183   gst_query_unref (drain);
184 
185   if (check_func)
186     check_func (buffers);
187 
188   num_buffers = g_list_length (buffers);
189   /* all output might get aggregated */
190   fail_unless (num_buffers >= 1);
191 
192   /* collect buffers in adapter for convenience */
193   for (i = 0; i < num_buffers; ++i) {
194     guint8 *odata;
195     gint size;
196     GstMapInfo map;
197 
198     outbuffer = GST_BUFFER (buffers->data);
199     fail_if (outbuffer == NULL);
200     buffers = g_list_remove (buffers, outbuffer);
201     ASSERT_BUFFER_REFCOUNT (outbuffer, "outbuffer", 1);
202 
203     gst_buffer_map (outbuffer, &map, GST_MAP_READ);
204     odata = map.data;
205     size = map.size;
206     fail_unless (size % 188 == 0);
207 
208     for (; size; odata += 188, size -= 188) {
209       guint pid, y;
210       guint8 *data = odata;
211 
212       /* need sync_byte */
213       fail_unless (*data == 0x47);
214       data++;
215 
216       y = GST_READ_UINT16_BE (data);
217       pid = y & (0x1FFF);
218       data += 2;
219       GST_TRACE ("pid: %d", pid);
220 
221       y = (y >> 14) & 0x1;
222       /* only check packets with payload_start_indicator == 1 */
223       if (!y) {
224         GST_TRACE ("not at start");
225         continue;
226       }
227 
228       y = *data;
229       data++;
230 
231       if (y & 0x20) {
232         /* adaptation field */
233         y = *data;
234         data++;
235         data += y;
236         GST_TRACE ("adaptation %d", y);
237       }
238 
239       if (pid == 0) {
240         /* look for PAT */
241         /* pointer field */
242         y = *data;
243         data++;
244         data += y;
245         /* table_id */
246         y = *data;
247         data++;
248         fail_unless (y == 0x0);
249         /* skip */
250         data += 5;
251         /* section_number */
252         y = *data;
253         fail_unless (y == 0);
254         data++;
255         /* last_section_number */
256         y = *data;
257         fail_unless (y == 0);
258         data++;
259         /* program_number */
260         y = GST_READ_UINT16_BE (data);
261         fail_unless (y != 0);
262         data += 2;
263         /* program_map_PID */
264         y = GST_READ_UINT16_BE (data);
265         pmt_pid = y & 0x1FFF;
266         fail_unless (pmt_pid > 0x10 && pmt_pid != 0x1FF);
267       } else if (pid == pmt_pid) {
268         /* look for PMT */
269         /* pointer field */
270         y = *data;
271         data++;
272         data += y;
273         /* table_id */
274         y = *data;
275         data++;
276         fail_unless (y == 0x2);
277         /* skip */
278         data += 5;
279         /* section_number */
280         y = *data;
281         fail_unless (y == 0);
282         data++;
283         /* last_section_number */
284         y = *data;
285         fail_unless (y == 0);
286         data++;
287         /* PCR_PID */
288         y = GST_READ_UINT16_BE (data);
289         data += 2;
290         pcr_pid = y & 0x1FFF;
291         /* program_info_length */
292         y = GST_READ_UINT16_BE (data);
293         data += 2;
294         y = y & 0x0FFF;
295         data += y;
296         /* parsing only ES stream */
297         /* stream_type */
298         y = *data;
299         data++;
300         fail_unless (y == pmt_id);
301         /* elementary_PID */
302         y = GST_READ_UINT16_BE (data);
303         data += 2;
304         el_pid = y & 0x1FFF;
305         fail_unless (el_pid > 0x10 && el_pid != 0x1FF);
306       } else if (pid == el_pid) {
307         packets++;
308         /* expect to see a PES packet start */
309         y = GST_READ_UINT32_BE (data);
310         fail_unless (y >> 8 == 0x1);
311         /* stream_id */
312         y = y & 0xFF;
313         fail_unless ((pes_id & 0xF0) == (y & 0xF0));
314       }
315     }
316     gst_buffer_unmap (outbuffer, &map);
317     gst_buffer_unref (outbuffer);
318     outbuffer = NULL;
319   }
320 
321   fail_unless (pmt_pid > 0);
322   fail_unless (el_pid > 0);
323   fail_unless (pcr_pid == el_pid);
324   fail_unless (packets > 0);
325 
326   g_list_free (buffers);
327   buffers = NULL;
328 }
329 
330 static void
check_tsmux_pad(GstStaticPadTemplate * srctemplate,const gchar * src_caps_string,gint pes_id,gint pmt_id,const gchar * sinkname,CheckOutputBuffersFunc check_func,guint n_bufs,gssize input_buf_size,guint alignment)331 check_tsmux_pad (GstStaticPadTemplate * srctemplate,
332     const gchar * src_caps_string, gint pes_id, gint pmt_id,
333     const gchar * sinkname, CheckOutputBuffersFunc check_func, guint n_bufs,
334     gssize input_buf_size, guint alignment)
335 {
336   gchar *padname;
337   GstElement *mux;
338 
339   mux = setup_tsmux (srctemplate, sinkname, &padname);
340 
341   if (alignment != 0)
342     g_object_set (mux, "alignment", alignment, NULL);
343 
344   fail_unless (gst_element_set_state (mux,
345           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
346       "could not set to playing");
347 
348   check_tsmux_pad_given_muxer (mux, src_caps_string, pes_id, pmt_id,
349       check_func, n_bufs, input_buf_size);
350 
351   cleanup_tsmux (mux, padname);
352   g_free (padname);
353 }
354 
GST_START_TEST(test_reappearing_pad_while_playing)355 GST_START_TEST (test_reappearing_pad_while_playing)
356 {
357   gchar *padname;
358   GstElement *mux;
359   GstPad *pad;
360 
361   mux = gst_check_setup_element ("mpegtsmux");
362   mysrcpad = setup_src_pad (mux, &video_src_template, "sink_%d", &padname);
363   mysinkpad = gst_check_setup_sink_pad (mux, &sink_template);
364   gst_pad_set_active (mysrcpad, TRUE);
365   gst_pad_set_active (mysinkpad, TRUE);
366 
367   fail_unless (gst_element_set_state (mux,
368           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
369       "could not set to playing");
370 
371   check_tsmux_pad_given_muxer (mux, VIDEO_CAPS_STRING, 0xE0, 0x1b, NULL, 1, 1);
372 
373   pad = gst_element_get_static_pad (mux, padname);
374   gst_pad_set_active (mysrcpad, FALSE);
375   teardown_src_pad (mux, padname);
376   gst_element_release_request_pad (mux, pad);
377   gst_object_unref (pad);
378   g_free (padname);
379 
380   mysrcpad = setup_src_pad (mux, &video_src_template, "sink_%d", &padname);
381   gst_pad_set_active (mysrcpad, TRUE);
382 
383   check_tsmux_pad_given_muxer (mux, VIDEO_CAPS_STRING, 0xE0, 0x1b, NULL, 1, 1);
384 
385   cleanup_tsmux (mux, padname);
386   g_free (padname);
387 }
388 
389 GST_END_TEST;
390 
GST_START_TEST(test_reappearing_pad_while_stopped)391 GST_START_TEST (test_reappearing_pad_while_stopped)
392 {
393   gchar *padname;
394   GstElement *mux;
395   GstPad *pad;
396 
397   mux = gst_check_setup_element ("mpegtsmux");
398   mysrcpad = setup_src_pad (mux, &video_src_template, "sink_%d", &padname);
399   mysinkpad = gst_check_setup_sink_pad (mux, &sink_template);
400   gst_pad_set_active (mysrcpad, TRUE);
401   gst_pad_set_active (mysinkpad, TRUE);
402 
403   fail_unless (gst_element_set_state (mux,
404           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
405       "could not set to playing");
406 
407   check_tsmux_pad_given_muxer (mux, VIDEO_CAPS_STRING, 0xE0, 0x1b, NULL, 1, 1);
408 
409   gst_element_set_state (mux, GST_STATE_NULL);
410 
411   pad = gst_element_get_static_pad (mux, padname);
412   gst_pad_set_active (mysrcpad, FALSE);
413   teardown_src_pad (mux, padname);
414   gst_element_release_request_pad (mux, pad);
415   gst_object_unref (pad);
416   g_free (padname);
417 
418   mysrcpad = setup_src_pad (mux, &video_src_template, "sink_%d", &padname);
419   gst_pad_set_active (mysrcpad, TRUE);
420 
421   fail_unless (gst_element_set_state (mux,
422           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
423       "could not set to playing");
424 
425   check_tsmux_pad_given_muxer (mux, VIDEO_CAPS_STRING, 0xE0, 0x1b, NULL, 1, 1);
426 
427   cleanup_tsmux (mux, padname);
428   g_free (padname);
429 }
430 
431 GST_END_TEST;
432 
GST_START_TEST(test_unused_pad)433 GST_START_TEST (test_unused_pad)
434 {
435   gchar *padname;
436   GstElement *mux;
437   GstPad *pad;
438 
439   mux = gst_check_setup_element ("mpegtsmux");
440   mysrcpad = setup_src_pad (mux, &video_src_template, "sink_%d", &padname);
441   mysinkpad = gst_check_setup_sink_pad (mux, &sink_template);
442   gst_pad_set_active (mysrcpad, TRUE);
443   gst_pad_set_active (mysinkpad, TRUE);
444 
445   fail_unless (gst_element_set_state (mux,
446           GST_STATE_PLAYING) == GST_STATE_CHANGE_SUCCESS,
447       "could not set to playing");
448 
449   pad = gst_element_get_static_pad (mux, padname);
450   gst_pad_set_active (mysrcpad, FALSE);
451   teardown_src_pad (mux, padname);
452   gst_element_release_request_pad (mux, pad);
453   gst_object_unref (pad);
454   g_free (padname);
455 
456   mysrcpad = setup_src_pad (mux, &video_src_template, "sink_%d", &padname);
457   gst_pad_set_active (mysrcpad, TRUE);
458 
459   cleanup_tsmux (mux, padname);
460   g_free (padname);
461 }
462 
463 GST_END_TEST;
464 
GST_START_TEST(test_video)465 GST_START_TEST (test_video)
466 {
467   check_tsmux_pad (&video_src_template, VIDEO_CAPS_STRING, 0xE0, 0x1b,
468       "sink_%d", NULL, 1, 1, 0);
469 }
470 
471 GST_END_TEST;
472 
473 
GST_START_TEST(test_audio)474 GST_START_TEST (test_audio)
475 {
476   check_tsmux_pad (&audio_src_template, AUDIO_CAPS_STRING, 0xC0, 0x03,
477       "sink_%d", NULL, 1, 1, 0);
478 }
479 
480 GST_END_TEST;
481 
GST_START_TEST(test_multiple_state_change)482 GST_START_TEST (test_multiple_state_change)
483 {
484   GstElement *mux;
485   gchar *padname;
486   GstSegment segment;
487   GstCaps *caps;
488   size_t i;
489 
490   /* it's just a sample of all possible permutations of all states and their
491    * transitions */
492   GstState states[] = { GST_STATE_PLAYING, GST_STATE_PAUSED, GST_STATE_PLAYING,
493     GST_STATE_READY, GST_STATE_PAUSED, GST_STATE_PLAYING, GST_STATE_NULL
494   };
495 
496   size_t num_transitions_to_test = 10;
497 
498   mux = setup_tsmux (&video_src_template, "sink_%d", &padname);
499   gst_segment_init (&segment, GST_FORMAT_TIME);
500 
501   caps = gst_caps_from_string (VIDEO_CAPS_STRING);
502   gst_check_setup_events (mysrcpad, mux, caps, GST_FORMAT_TIME);
503   gst_caps_unref (caps);
504 
505   for (i = 0; i < num_transitions_to_test; ++i) {
506     GstQuery *drain;
507     GstState next_state = states[i % G_N_ELEMENTS (states)];
508     fail_unless (gst_element_set_state (mux,
509             next_state) == GST_STATE_CHANGE_SUCCESS,
510         "could not set to %s", gst_element_state_get_name (next_state));
511 
512     /* push some buffers when playing - this triggers a lot of activity */
513     if (GST_STATE_PLAYING == next_state) {
514       GstBuffer *inbuffer;
515 
516       fail_unless (gst_pad_push_event (mysrcpad,
517               gst_event_new_segment (&segment)));
518 
519       inbuffer = gst_buffer_new_and_alloc (1);
520       ASSERT_BUFFER_REFCOUNT (inbuffer, "inbuffer", 1);
521 
522       GST_BUFFER_PTS (inbuffer) = 0;
523       fail_unless (GST_FLOW_OK == gst_pad_push (mysrcpad, inbuffer));
524 
525       drain = gst_query_new_drain ();
526       gst_pad_peer_query (mysrcpad, drain);
527       gst_query_unref (drain);
528     }
529   }
530 
531   cleanup_tsmux (mux, padname);
532   g_free (padname);
533 }
534 
535 GST_END_TEST;
536 
537 static void
test_align_check_output(GList * bufs)538 test_align_check_output (GList * bufs)
539 {
540   GST_LOG ("%u buffers", g_list_length (bufs));
541   while (bufs != NULL) {
542     GstBuffer *buf = bufs->data;
543     gsize size;
544 
545     size = gst_buffer_get_size (buf);
546     GST_LOG ("buffer, size = %5u", (guint) size);
547     fail_unless_equals_int (size, 7 * 188);
548     bufs = bufs->next;
549   }
550 }
551 
GST_START_TEST(test_align)552 GST_START_TEST (test_align)
553 {
554   check_tsmux_pad (&video_src_template, VIDEO_CAPS_STRING, 0xE0, 0x1b,
555       "sink_%d", test_align_check_output, 817, -1, 7);
556 }
557 
558 GST_END_TEST;
559 
560 static void
test_keyframe_propagation_check_output(GList * bufs)561 test_keyframe_propagation_check_output (GList * bufs)
562 {
563   guint keyframe_count = 0;
564 
565   GST_LOG ("%u buffers", g_list_length (bufs));
566   while (bufs != NULL) {
567     GstBuffer *buf = bufs->data;
568     gboolean keyunit;
569 
570     keyunit = !GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT);
571 
572     if (keyunit)
573       ++keyframe_count;
574 
575     GST_LOG ("buffer, keyframe=%d", keyunit);
576     bufs = bufs->next;
577   }
578   fail_unless_equals_int (keyframe_count, 50 / KEYFRAME_DISTANCE);
579 }
580 
GST_START_TEST(test_keyframe_flag_propagation)581 GST_START_TEST (test_keyframe_flag_propagation)
582 {
583   check_tsmux_pad (&video_src_template, VIDEO_CAPS_STRING, 0xE0, 0x1b,
584       "sink_%d", test_keyframe_propagation_check_output, 50, -1, 0);
585 }
586 
587 GST_END_TEST;
588 
589 static Suite *
mpegtsmux_suite(void)590 mpegtsmux_suite (void)
591 {
592   Suite *s = suite_create ("mpegtsmux");
593   TCase *tc_chain = tcase_create ("general");
594 
595   suite_add_tcase (s, tc_chain);
596 
597   tcase_add_test (tc_chain, test_audio);
598   tcase_add_test (tc_chain, test_video);
599   tcase_add_test (tc_chain, test_multiple_state_change);
600   tcase_add_test (tc_chain, test_align);
601   tcase_add_test (tc_chain, test_keyframe_flag_propagation);
602   tcase_add_test (tc_chain, test_reappearing_pad_while_playing);
603   tcase_add_test (tc_chain, test_reappearing_pad_while_stopped);
604   tcase_add_test (tc_chain, test_unused_pad);
605 
606   return s;
607 }
608 
609 GST_CHECK_MAIN (mpegtsmux);
610