/* GStreamer * * unit test for qtdemux * * Copyright (C) <2016> Edward Hervey * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "qtdemux.h" typedef struct { GstPad *srcpad; guint expected_size; GstClockTime expected_time; } CommonTestData; static GstPadProbeReturn qtdemux_probe (GstPad * pad, GstPadProbeInfo * info, CommonTestData * data) { GstBuffer *buf = GST_PAD_PROBE_INFO_BUFFER (info); fail_unless_equals_int (gst_buffer_get_size (buf), data->expected_size); fail_unless_equals_uint64 (GST_BUFFER_PTS (buf), data->expected_time); gst_buffer_unref (buf); return GST_PAD_PROBE_HANDLED; } static void qtdemux_pad_added_cb (GstElement * element, GstPad * pad, CommonTestData * data) { data->srcpad = pad; gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER, (GstPadProbeCallback) qtdemux_probe, data, NULL); } GST_START_TEST (test_qtdemux_input_gap) { GstElement *qtdemux; GstPad *sinkpad; CommonTestData data = { 0, }; GstBuffer *inbuf; GstSegment segment; GstEvent *event; guint i, offset; GstClockTime pts; /* The goal of this test is to check that qtdemux can properly handle * fragmented input from dashdemux, with gaps in it. * * Input segment : * - TIME * Input buffers : * - The offset is set on buffers, it corresponds to the offset * within the current fragment. * - Buffer of the beginning of a fragment has the PTS set, others * don't. * - By extension, the beginning of a fragment also has an offset * of 0. */ qtdemux = gst_element_factory_make ("qtdemux", NULL); gst_element_set_state (qtdemux, GST_STATE_PLAYING); sinkpad = gst_element_get_static_pad (qtdemux, "sink"); /* We'll want to know when the source pad is added */ g_signal_connect (qtdemux, "pad-added", (GCallback) qtdemux_pad_added_cb, &data); /* Send the initial STREAM_START and segment (TIME) event */ event = gst_event_new_stream_start ("TEST"); GST_DEBUG ("Pushing stream-start event"); fail_unless (gst_pad_send_event (sinkpad, event) == TRUE); gst_segment_init (&segment, GST_FORMAT_TIME); event = gst_event_new_segment (&segment); GST_DEBUG ("Pushing segment event"); fail_unless (gst_pad_send_event (sinkpad, event) == TRUE); /* Feed the init buffer, should create the source pad */ inbuf = gst_buffer_new_and_alloc (init_mp4_len); gst_buffer_fill (inbuf, 0, init_mp4, init_mp4_len); GST_BUFFER_PTS (inbuf) = 0; GST_BUFFER_OFFSET (inbuf) = 0; GST_DEBUG ("Pushing header buffer"); fail_unless (gst_pad_chain (sinkpad, inbuf) == GST_FLOW_OK); /* Now send the trun of the first fragment */ inbuf = gst_buffer_new_and_alloc (seg_1_moof_size); gst_buffer_fill (inbuf, 0, seg_1_m4f, seg_1_moof_size); GST_BUFFER_PTS (inbuf) = 0; GST_BUFFER_OFFSET (inbuf) = 0; /* We are simulating that this fragment can happen at any point */ GST_BUFFER_FLAG_SET (inbuf, GST_BUFFER_FLAG_DISCONT); GST_DEBUG ("Pushing trun buffer"); fail_unless (gst_pad_chain (sinkpad, inbuf) == GST_FLOW_OK); fail_if (data.srcpad == NULL); /* We are now ready to send some buffers with gaps */ offset = seg_1_sample_0_offset; pts = 0; GST_DEBUG ("Pushing gap'ed buffers"); for (i = 0; i < 129; i++) { /* Let's send one every 3 */ if ((i % 3) == 0) { GST_DEBUG ("Pushing buffer #%d offset:%" G_GUINT32_FORMAT, i, offset); inbuf = gst_buffer_new_and_alloc (seg_1_sample_sizes[i]); gst_buffer_fill (inbuf, 0, seg_1_m4f + offset, seg_1_sample_sizes[i]); GST_BUFFER_OFFSET (inbuf) = offset; GST_BUFFER_FLAG_SET (inbuf, GST_BUFFER_FLAG_DISCONT); data.expected_time = gst_util_uint64_scale (pts, GST_SECOND, seg_1_timescale); data.expected_size = seg_1_sample_sizes[i]; fail_unless (gst_pad_chain (sinkpad, inbuf) == GST_FLOW_OK); } /* Finally move offset forward */ offset += seg_1_sample_sizes[i]; pts += seg_1_sample_duration; } gst_object_unref (sinkpad); gst_element_set_state (qtdemux, GST_STATE_NULL); gst_object_unref (qtdemux); } GST_END_TEST; static Suite * qtdemux_suite (void) { Suite *s = suite_create ("qtdemux"); TCase *tc_chain = tcase_create ("general"); suite_add_tcase (s, tc_chain); tcase_add_test (tc_chain, test_qtdemux_input_gap); return s; } GST_CHECK_MAIN (qtdemux)