637 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			637 lines
		
	
	
		
			23 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* GStreamer unit test for MSS
 | |
|  *
 | |
|  * Copyright (C) 2016 Samsung Electronics. All rights reserved.
 | |
|  *   Author: Thiago Santos <thiagoss@osg.samsung.com>
 | |
|  *
 | |
|  * 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 <gst/check/gstcheck.h>
 | |
| #include "adaptive_demux_common.h"
 | |
| 
 | |
| #define DEMUX_ELEMENT_NAME "mssdemux"
 | |
| 
 | |
| #define COPY_OUTPUT_TEST_DATA(outputTestData,testData) do { \
 | |
|     guint otdPos, otdLen = sizeof((outputTestData)) / sizeof((outputTestData)[0]); \
 | |
|     for(otdPos=0; otdPos<otdLen; ++otdPos){ \
 | |
|   (testData)->output_streams = g_list_append ((testData)->output_streams, &(outputTestData)[otdPos]); \
 | |
|     } \
 | |
|   } while(0)
 | |
| 
 | |
| typedef struct _GstMssDemuxTestInputData
 | |
| {
 | |
|   const gchar *uri;
 | |
|   const guint8 *payload;
 | |
|   guint64 size;
 | |
| } GstMssDemuxTestInputData;
 | |
| 
 | |
| static gboolean
 | |
| gst_mssdemux_http_src_start (GstTestHTTPSrc * src,
 | |
|     const gchar * uri, GstTestHTTPSrcInput * input_data, gpointer user_data)
 | |
| {
 | |
|   const GstMssDemuxTestInputData *input =
 | |
|       (const GstMssDemuxTestInputData *) user_data;
 | |
|   guint i;
 | |
| 
 | |
| 
 | |
|   for (i = 0; input[i].uri; ++i) {
 | |
|     if (strcmp (input[i].uri, uri) == 0) {
 | |
|       input_data->context = (gpointer) & input[i];
 | |
|       input_data->size = input[i].size;
 | |
|       if (input[i].size == 0)
 | |
|         input_data->size = strlen ((gchar *) input[i].payload);
 | |
|       return TRUE;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return FALSE;
 | |
| }
 | |
| 
 | |
| static GstFlowReturn
 | |
| gst_mssdemux_http_src_create (GstTestHTTPSrc * src,
 | |
|     guint64 offset,
 | |
|     guint length, GstBuffer ** retbuf, gpointer context, gpointer user_data)
 | |
| {
 | |
|   /*  const GstMssDemuxTestInputData *input =
 | |
|      (const GstMssDemuxTestInputData *) user_data; */
 | |
|   const GstMssDemuxTestInputData *input =
 | |
|       (const GstMssDemuxTestInputData *) context;
 | |
|   GstBuffer *buf;
 | |
| 
 | |
|   buf = gst_buffer_new_allocate (NULL, length, NULL);
 | |
|   fail_if (buf == NULL, "Not enough memory to allocate buffer");
 | |
| 
 | |
|   if (input->payload) {
 | |
|     gst_buffer_fill (buf, 0, input->payload + offset, length);
 | |
|   } else {
 | |
|     GstMapInfo info;
 | |
|     guint pattern;
 | |
|     guint64 i;
 | |
| 
 | |
|     pattern = offset - offset % sizeof (pattern);
 | |
| 
 | |
|     gst_buffer_map (buf, &info, GST_MAP_WRITE);
 | |
|     for (i = 0; i < length; ++i) {
 | |
|       gchar pattern_byte_to_write = (offset + i) % sizeof (pattern);
 | |
|       if (pattern_byte_to_write == 0) {
 | |
|         pattern = offset + i;
 | |
|       }
 | |
|       info.data[i] = (pattern >> (pattern_byte_to_write * 8)) & 0xFF;
 | |
|     }
 | |
|     gst_buffer_unmap (buf, &info);
 | |
|   }
 | |
|   *retbuf = buf;
 | |
|   return GST_FLOW_OK;
 | |
| }
 | |
| 
 | |
| /******************** Test specific code starts here **************************/
 | |
| 
 | |
| /*
 | |
|  * Test an mpd with an audio and a video stream
 | |
|  *
 | |
|  */
 | |
| GST_START_TEST (simpleTest)
 | |
| {
 | |
|   const gchar *mpd =
 | |
|       "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
 | |
|       "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" Duration=\"40000000\">"
 | |
|       "<StreamIndex Type=\"video\" QualityLevels=\"1\" Chunks=\"1\" Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">"
 | |
|       "<QualityLevel Index=\"0\" Bitrate=\"480111\" FourCC=\"H264\" MaxWidth=\"1024\" MaxHeight=\"436\" CodecPrivateData=\"000\" />"
 | |
|       "<c n=\"0\" d=\"10000000\" />"
 | |
|       "<c n=\"1\" d=\"10000000\" />"
 | |
|       "<c n=\"2\" d=\"10000000\" />"
 | |
|       "<c n=\"3\" d=\"10000000\" />"
 | |
|       "</StreamIndex>"
 | |
|       "<StreamIndex Type=\"audio\" Language=\"eng\" QualityLevels=\"1\" Chunks=\"1\" Url=\"QualityLevels({bitrate})/Fragments(audio_eng={start time})\">"
 | |
|       "<QualityLevel Index=\"0\" Bitrate=\"200029\" FourCC=\"AACL\" SamplingRate=\"48000\" Channels=\"2\" BitsPerSample=\"16\" PacketSize=\"4\" AudioTag=\"255\" CodecPrivateData=\"1190\" />"
 | |
|       "<c n=\"0\" d=\"40000000\" />" "</StreamIndex>" "</SmoothStreamingMedia>";
 | |
| 
 | |
|   GstMssDemuxTestInputData inputTestData[] = {
 | |
|     {"http://unit.test/Manifest", (guint8 *) mpd, 0},
 | |
|     {"http://unit.test/QualityLevels(480111)/Fragments(video=0)", NULL, 9000},
 | |
|     {"http://unit.test/QualityLevels(480111)/Fragments(video=10000000)", NULL,
 | |
|         9000},
 | |
|     {"http://unit.test/QualityLevels(480111)/Fragments(video=20000000)", NULL,
 | |
|         9000},
 | |
|     {"http://unit.test/QualityLevels(480111)/Fragments(video=30000000)", NULL,
 | |
|         9000},
 | |
|     {"http://unit.test/QualityLevels(200029)/Fragments(audio_eng=0)", NULL,
 | |
|         5000},
 | |
|     {NULL, NULL, 0},
 | |
|   };
 | |
|   GstTestHTTPSrcCallbacks http_src_callbacks = { 0 };
 | |
|   GstAdaptiveDemuxTestExpectedOutput outputTestData[] = {
 | |
|     {"audio_00", 5000, NULL},
 | |
|     {"video_00", 4 * 9000, NULL}
 | |
|   };
 | |
|   GstAdaptiveDemuxTestCallbacks test_callbacks = { 0 };
 | |
|   GstAdaptiveDemuxTestCase *testData;
 | |
| 
 | |
|   testData = gst_adaptive_demux_test_case_new ();
 | |
|   http_src_callbacks.src_start = gst_mssdemux_http_src_start;
 | |
|   http_src_callbacks.src_create = gst_mssdemux_http_src_create;
 | |
|   gst_test_http_src_install_callbacks (&http_src_callbacks, inputTestData);
 | |
| 
 | |
|   COPY_OUTPUT_TEST_DATA (outputTestData, testData);
 | |
|   test_callbacks.appsink_received_data =
 | |
|       gst_adaptive_demux_test_check_received_data;
 | |
|   test_callbacks.appsink_eos =
 | |
|       gst_adaptive_demux_test_check_size_of_received_data;
 | |
| 
 | |
|   gst_adaptive_demux_test_run (DEMUX_ELEMENT_NAME, "http://unit.test/Manifest",
 | |
|       &test_callbacks, testData);
 | |
|   g_object_unref (testData);
 | |
| }
 | |
| 
 | |
| GST_END_TEST;
 | |
| 
 | |
| /*
 | |
|  * Test seeking
 | |
|  *
 | |
|  */
 | |
| GST_START_TEST (testSeek)
 | |
| {
 | |
|   const gchar *mpd =
 | |
|       "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
 | |
|       "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" Duration=\"40000000\">"
 | |
|       "<StreamIndex Type=\"audio\" Language=\"eng\" QualityLevels=\"1\" Chunks=\"1\" Url=\"QualityLevels({bitrate})/Fragments(audio_eng={start time})\">"
 | |
|       "<QualityLevel Index=\"0\" Bitrate=\"200029\" FourCC=\"AACL\" SamplingRate=\"48000\" Channels=\"2\" BitsPerSample=\"16\" PacketSize=\"4\" AudioTag=\"255\" CodecPrivateData=\"1190\" />"
 | |
|       "<c n=\"0\" d=\"450346666\" />"
 | |
|       "</StreamIndex>" "</SmoothStreamingMedia>";
 | |
|   GstMssDemuxTestInputData inputTestData[] = {
 | |
|     {"http://unit.test/Manifest", (guint8 *) mpd, 0},
 | |
|     {"http://unit.test/QualityLevels(200029)/Fragments(audio_eng=0)", NULL,
 | |
|         10000},
 | |
|     {NULL, NULL, 0},
 | |
|   };
 | |
|   GstTestHTTPSrcCallbacks http_src_callbacks = { 0 };
 | |
|   GstAdaptiveDemuxTestExpectedOutput outputTestData[] = {
 | |
|     {"audio_00", 10000, NULL},
 | |
|   };
 | |
|   GstAdaptiveDemuxTestCase *testData;
 | |
| 
 | |
|   testData = gst_adaptive_demux_test_case_new ();
 | |
| 
 | |
|   http_src_callbacks.src_start = gst_mssdemux_http_src_start;
 | |
|   http_src_callbacks.src_create = gst_mssdemux_http_src_create;
 | |
|   COPY_OUTPUT_TEST_DATA (outputTestData, testData);
 | |
| 
 | |
|   /* media segment starts at 4687
 | |
|    * Issue a seek request after media segment has started to be downloaded
 | |
|    * on the first pad listed in GstAdaptiveDemuxTestOutputStreamData and the
 | |
|    * first chunk of at least one byte has already arrived in AppSink
 | |
|    */
 | |
|   testData->threshold_for_seek = 4687 + 1;
 | |
| 
 | |
|   /* seek to 5ms.
 | |
|    * Because there is only one fragment, we expect the whole file to be
 | |
|    * downloaded again
 | |
|    */
 | |
|   testData->seek_event =
 | |
|       gst_event_new_seek (1.0, GST_FORMAT_TIME,
 | |
|       GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT, GST_SEEK_TYPE_SET,
 | |
|       5 * GST_MSECOND, GST_SEEK_TYPE_NONE, 0);
 | |
| 
 | |
|   gst_test_http_src_install_callbacks (&http_src_callbacks, inputTestData);
 | |
|   gst_adaptive_demux_test_seek (DEMUX_ELEMENT_NAME,
 | |
|       "http://unit.test/Manifest", testData);
 | |
|   g_object_unref (testData);
 | |
| }
 | |
| 
 | |
| GST_END_TEST;
 | |
| 
 | |
| 
 | |
| static void
 | |
| run_seek_position_test (gdouble rate, GstSeekType start_type,
 | |
|     guint64 seek_start, GstSeekType stop_type, guint64 seek_stop,
 | |
|     GstSeekFlags flags, guint64 segment_start, guint64 segment_stop,
 | |
|     gint segments)
 | |
| {
 | |
|   const gchar *mpd =
 | |
|       "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
 | |
|       "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" Duration=\"40000000\">"
 | |
|       "<StreamIndex Type=\"audio\" Language=\"eng\" QualityLevels=\"1\" Chunks=\"1\" Url=\"QualityLevels({bitrate})/Fragments(audio_eng={start time})\">"
 | |
|       "<QualityLevel Index=\"0\" Bitrate=\"200029\" FourCC=\"AACL\" SamplingRate=\"48000\" Channels=\"2\" BitsPerSample=\"16\" PacketSize=\"4\" AudioTag=\"255\" CodecPrivateData=\"1190\" />"
 | |
|       "<c n=\"0\" d=\"10000000\" />"
 | |
|       "<c n=\"1\" d=\"10000000\" />"
 | |
|       "<c n=\"2\" d=\"10000000\" />"
 | |
|       "<c n=\"3\" d=\"10000000\" />" "</StreamIndex>" "</SmoothStreamingMedia>";
 | |
|   GstMssDemuxTestInputData inputTestData[] = {
 | |
|     {"http://unit.test/Manifest", (guint8 *) mpd, 0},
 | |
|     {"http://unit.test/QualityLevels(200029)/Fragments(audio_eng=0)", NULL,
 | |
|         10000},
 | |
|     {"http://unit.test/QualityLevels(200029)/Fragments(audio_eng=10000000)",
 | |
|         NULL, 10000},
 | |
|     {"http://unit.test/QualityLevels(200029)/Fragments(audio_eng=20000000)",
 | |
|         NULL, 10000},
 | |
|     {"http://unit.test/QualityLevels(200029)/Fragments(audio_eng=30000000)",
 | |
|         NULL, 10000},
 | |
|     {NULL, NULL, 0},
 | |
|   };
 | |
|   GstTestHTTPSrcCallbacks http_src_callbacks = { 0 };
 | |
|   GstAdaptiveDemuxTestExpectedOutput outputTestData[] = {
 | |
|     /* 1 from the init segment */
 | |
|     {"audio_00", segments * 10000, NULL},
 | |
|   };
 | |
|   GstAdaptiveDemuxTestCase *testData;
 | |
| 
 | |
|   testData = gst_adaptive_demux_test_case_new ();
 | |
| 
 | |
|   http_src_callbacks.src_start = gst_mssdemux_http_src_start;
 | |
|   http_src_callbacks.src_create = gst_mssdemux_http_src_create;
 | |
|   COPY_OUTPUT_TEST_DATA (outputTestData, testData);
 | |
| 
 | |
|   /* media segment starts at 4687
 | |
|    * Issue a seek request after media segment has started to be downloaded
 | |
|    * on the first pad listed in GstAdaptiveDemuxTestOutputStreamData and the
 | |
|    * first chunk of at least one byte has already arrived in AppSink
 | |
|    */
 | |
|   testData->threshold_for_seek = 4687 + 1;
 | |
| 
 | |
|   /* FIXME hack to avoid having a 0 seqnum */
 | |
|   gst_util_seqnum_next ();
 | |
| 
 | |
|   /* seek to 5ms.
 | |
|    * Because there is only one fragment, we expect the whole file to be
 | |
|    * downloaded again
 | |
|    */
 | |
|   testData->seek_event =
 | |
|       gst_event_new_seek (rate, GST_FORMAT_TIME, flags, start_type,
 | |
|       seek_start, stop_type, seek_stop);
 | |
| 
 | |
|   gst_test_http_src_install_callbacks (&http_src_callbacks, inputTestData);
 | |
|   gst_adaptive_demux_test_seek (DEMUX_ELEMENT_NAME,
 | |
|       "http://unit.test/Manifest", testData);
 | |
|   g_object_unref (testData);
 | |
| }
 | |
| 
 | |
| GST_START_TEST (testSeekKeyUnitPosition)
 | |
| {
 | |
|   /* Seek to 1.5s with key unit, it should go back to 1.0s. 3 segments will be
 | |
|    * pushed */
 | |
|   run_seek_position_test (1.0, GST_SEEK_TYPE_SET, 1500 * GST_MSECOND,
 | |
|       GST_SEEK_TYPE_NONE, 0, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT,
 | |
|       1000 * GST_MSECOND, -1, 3);
 | |
| }
 | |
| 
 | |
| GST_END_TEST;
 | |
| 
 | |
| 
 | |
| GST_START_TEST (testSeekUpdateStopPosition)
 | |
| {
 | |
|   run_seek_position_test (1.0, GST_SEEK_TYPE_NONE, 1500 * GST_MSECOND,
 | |
|       GST_SEEK_TYPE_SET, 3000 * GST_MSECOND, 0, 0, 3000 * GST_MSECOND, 3);
 | |
| }
 | |
| 
 | |
| GST_END_TEST;
 | |
| 
 | |
| GST_START_TEST (testSeekPosition)
 | |
| {
 | |
|   /* Seek to 1.5s without key unit, it should keep the 1.5s, but still push
 | |
|    * from the 1st segment, so 3 segments will be
 | |
|    * pushed */
 | |
|   run_seek_position_test (1.0, GST_SEEK_TYPE_SET, 1500 * GST_MSECOND,
 | |
|       GST_SEEK_TYPE_NONE, 0, GST_SEEK_FLAG_FLUSH, 1500 * GST_MSECOND, -1, 3);
 | |
| }
 | |
| 
 | |
| GST_END_TEST;
 | |
| 
 | |
| GST_START_TEST (testSeekSnapBeforePosition)
 | |
| {
 | |
|   /* Seek to 1.5s, snap before, it go to 1s */
 | |
|   run_seek_position_test (1.0, GST_SEEK_TYPE_SET, 1500 * GST_MSECOND,
 | |
|       GST_SEEK_TYPE_NONE, 0,
 | |
|       GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_SNAP_BEFORE,
 | |
|       1000 * GST_MSECOND, -1, 3);
 | |
| }
 | |
| 
 | |
| GST_END_TEST;
 | |
| 
 | |
| 
 | |
| GST_START_TEST (testSeekSnapAfterPosition)
 | |
| {
 | |
|   /* Seek to 1.5s with snap after, it should move to 2s */
 | |
|   run_seek_position_test (1.0, GST_SEEK_TYPE_SET, 1500 * GST_MSECOND,
 | |
|       GST_SEEK_TYPE_NONE, 0,
 | |
|       GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_SNAP_AFTER,
 | |
|       2000 * GST_MSECOND, -1, 2);
 | |
| }
 | |
| 
 | |
| GST_END_TEST;
 | |
| 
 | |
| 
 | |
| GST_START_TEST (testReverseSeekSnapBeforePosition)
 | |
| {
 | |
|   run_seek_position_test (-1.0, GST_SEEK_TYPE_SET, 1000 * GST_MSECOND,
 | |
|       GST_SEEK_TYPE_SET, 2500 * GST_MSECOND,
 | |
|       GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_SNAP_BEFORE,
 | |
|       1000 * GST_MSECOND, 3000 * GST_MSECOND, 2);
 | |
| }
 | |
| 
 | |
| GST_END_TEST;
 | |
| 
 | |
| 
 | |
| GST_START_TEST (testReverseSeekSnapAfterPosition)
 | |
| {
 | |
|   run_seek_position_test (-1.0, GST_SEEK_TYPE_SET, 1000 * GST_MSECOND,
 | |
|       GST_SEEK_TYPE_SET, 2500 * GST_MSECOND,
 | |
|       GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT | GST_SEEK_FLAG_SNAP_AFTER,
 | |
|       1000 * GST_MSECOND, 2000 * GST_MSECOND, 1);
 | |
| }
 | |
| 
 | |
| GST_END_TEST;
 | |
| 
 | |
| static void
 | |
| testDownloadErrorMessageCallback (GstAdaptiveDemuxTestEngine * engine,
 | |
|     GstMessage * msg, gpointer user_data)
 | |
| {
 | |
|   GError *err = NULL;
 | |
|   gchar *dbg_info = NULL;
 | |
| 
 | |
|   fail_unless (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR);
 | |
|   gst_message_parse_error (msg, &err, &dbg_info);
 | |
|   GST_DEBUG ("Error from element %s : %s",
 | |
|       GST_OBJECT_NAME (msg->src), err->message);
 | |
|   fail_unless_equals_string (GST_OBJECT_NAME (msg->src), DEMUX_ELEMENT_NAME);
 | |
|   /*GST_DEBUG ("dbg_info=%s", dbg_info); */
 | |
|   g_error_free (err);
 | |
|   g_free (dbg_info);
 | |
|   g_main_loop_quit (engine->loop);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test error case of failing to download a segment
 | |
|  */
 | |
| GST_START_TEST (testDownloadError)
 | |
| {
 | |
|   const gchar *mpd =
 | |
|       "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
 | |
|       "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" Duration=\"40000000\">"
 | |
|       "<StreamIndex Type=\"audio\" Language=\"eng\" QualityLevels=\"1\" Chunks=\"1\" Url=\"QualityLevels({bitrate})/Fragments(audio_eng={start time})\">"
 | |
|       "<QualityLevel Index=\"0\" Bitrate=\"200029\" FourCC=\"AACL\" SamplingRate=\"48000\" Channels=\"2\" BitsPerSample=\"16\" PacketSize=\"4\" AudioTag=\"255\" CodecPrivateData=\"1190\" />"
 | |
|       "<c n=\"0\" d=\"40000000\" />" "</StreamIndex>" "</SmoothStreamingMedia>";
 | |
| 
 | |
|   GstMssDemuxTestInputData inputTestData[] = {
 | |
|     {"http://unit.test/Manifest", (guint8 *) mpd, 0},
 | |
|     {NULL, NULL, 0},
 | |
|   };
 | |
|   GstTestHTTPSrcCallbacks http_src_callbacks = { 0 };
 | |
|   GstAdaptiveDemuxTestExpectedOutput outputTestData[] = {
 | |
|     {"audio_00", 0, NULL},
 | |
|   };
 | |
|   GstAdaptiveDemuxTestCallbacks test_callbacks = { 0 };
 | |
|   GstAdaptiveDemuxTestCase *testData;
 | |
| 
 | |
|   testData = gst_adaptive_demux_test_case_new ();
 | |
|   http_src_callbacks.src_start = gst_mssdemux_http_src_start;
 | |
|   http_src_callbacks.src_create = gst_mssdemux_http_src_create;
 | |
|   COPY_OUTPUT_TEST_DATA (outputTestData, testData);
 | |
|   test_callbacks.appsink_received_data =
 | |
|       gst_adaptive_demux_test_check_received_data;
 | |
|   test_callbacks.bus_error_message = testDownloadErrorMessageCallback;
 | |
|   test_callbacks.appsink_eos =
 | |
|       gst_adaptive_demux_test_check_size_of_received_data;
 | |
| 
 | |
|   gst_test_http_src_install_callbacks (&http_src_callbacks, inputTestData);
 | |
|   gst_adaptive_demux_test_run (DEMUX_ELEMENT_NAME, "http://unit.test/Manifest",
 | |
|       &test_callbacks, testData);
 | |
|   g_object_unref (testData);
 | |
| }
 | |
| 
 | |
| GST_END_TEST;
 | |
| 
 | |
| /* generate queries to adaptive demux */
 | |
| static gboolean
 | |
| testQueryCheckDataReceived (GstAdaptiveDemuxTestEngine * engine,
 | |
|     GstAdaptiveDemuxTestOutputStream * stream,
 | |
|     GstBuffer * buffer, gpointer user_data)
 | |
| {
 | |
|   GList *pads;
 | |
|   GstPad *pad;
 | |
|   GstQuery *query;
 | |
|   gboolean ret;
 | |
|   gint64 duration;
 | |
|   gboolean seekable;
 | |
|   gint64 segment_start;
 | |
|   gint64 segment_end;
 | |
|   gchar *uri;
 | |
|   gchar *redirect_uri;
 | |
|   gboolean redirect_permanent;
 | |
| 
 | |
|   pads = GST_ELEMENT_PADS (stream->appsink);
 | |
| 
 | |
|   /* AppSink should have only 1 pad */
 | |
|   fail_unless (pads != NULL);
 | |
|   fail_unless (g_list_length (pads) == 1);
 | |
|   pad = GST_PAD (pads->data);
 | |
| 
 | |
|   query = gst_query_new_duration (GST_FORMAT_TIME);
 | |
|   ret = gst_pad_peer_query (pad, query);
 | |
|   fail_unless (ret == TRUE);
 | |
|   gst_query_parse_duration (query, NULL, &duration);
 | |
|   fail_unless (duration == GST_SECOND);
 | |
|   gst_query_unref (query);
 | |
| 
 | |
|   query = gst_query_new_seeking (GST_FORMAT_TIME);
 | |
|   ret = gst_pad_peer_query (pad, query);
 | |
|   fail_unless (ret == TRUE);
 | |
|   gst_query_parse_seeking (query, NULL, &seekable, &segment_start,
 | |
|       &segment_end);
 | |
|   fail_unless (seekable == TRUE);
 | |
|   fail_unless (segment_start == 0);
 | |
|   fail_unless (segment_end == duration);
 | |
|   gst_query_unref (query);
 | |
| 
 | |
|   query = gst_query_new_uri ();
 | |
|   ret = gst_pad_peer_query (pad, query);
 | |
|   fail_unless (ret == TRUE);
 | |
|   gst_query_parse_uri (query, &uri);
 | |
|   gst_query_parse_uri_redirection (query, &redirect_uri);
 | |
|   gst_query_parse_uri_redirection_permanent (query, &redirect_permanent);
 | |
|   fail_unless (strcmp (uri, "http://unit.test/Manifest") == 0);
 | |
|   /* adaptive demux does not reply with redirect information */
 | |
|   fail_unless (redirect_uri == NULL);
 | |
|   fail_unless (redirect_permanent == FALSE);
 | |
|   g_free (uri);
 | |
|   g_free (redirect_uri);
 | |
|   gst_query_unref (query);
 | |
| 
 | |
|   return gst_adaptive_demux_test_check_received_data (engine,
 | |
|       stream, buffer, user_data);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test queries
 | |
|  *
 | |
|  */
 | |
| GST_START_TEST (testQuery)
 | |
| {
 | |
|   const gchar *mpd =
 | |
|       "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
 | |
|       "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" Duration=\"10000000\">"
 | |
|       "<StreamIndex Type=\"audio\" Language=\"eng\" QualityLevels=\"1\" Chunks=\"1\" Url=\"QualityLevels({bitrate})/Fragments(audio_eng={start time})\">"
 | |
|       "<QualityLevel Index=\"0\" Bitrate=\"200029\" FourCC=\"AACL\" SamplingRate=\"48000\" Channels=\"2\" BitsPerSample=\"16\" PacketSize=\"4\" AudioTag=\"255\" CodecPrivateData=\"1190\" />"
 | |
|       "<c n=\"0\" d=\"10000000\" />" "</StreamIndex>" "</SmoothStreamingMedia>";
 | |
|   GstMssDemuxTestInputData inputTestData[] = {
 | |
|     {"http://unit.test/Manifest", (guint8 *) mpd, 0},
 | |
|     {"http://unit.test/QualityLevels(200029)/Fragments(audio_eng=0)", NULL,
 | |
|         5000},
 | |
|     {NULL, NULL, 0},
 | |
|   };
 | |
|   GstTestHTTPSrcCallbacks http_src_callbacks = { 0 };
 | |
|   GstAdaptiveDemuxTestExpectedOutput outputTestData[] = {
 | |
|     {"audio_00", 5000, NULL},
 | |
|   };
 | |
|   GstAdaptiveDemuxTestCallbacks test_callbacks = { 0 };
 | |
|   GstAdaptiveDemuxTestCase *testData;
 | |
| 
 | |
|   testData = gst_adaptive_demux_test_case_new ();
 | |
|   http_src_callbacks.src_start = gst_mssdemux_http_src_start;
 | |
|   http_src_callbacks.src_create = gst_mssdemux_http_src_create;
 | |
|   COPY_OUTPUT_TEST_DATA (outputTestData, testData);
 | |
|   test_callbacks.appsink_received_data = testQueryCheckDataReceived;
 | |
|   test_callbacks.appsink_eos =
 | |
|       gst_adaptive_demux_test_check_size_of_received_data;
 | |
| 
 | |
|   gst_test_http_src_install_callbacks (&http_src_callbacks, inputTestData);
 | |
|   gst_adaptive_demux_test_run (DEMUX_ELEMENT_NAME,
 | |
|       "http://unit.test/Manifest", &test_callbacks, testData);
 | |
|   g_object_unref (testData);
 | |
| }
 | |
| 
 | |
| GST_END_TEST;
 | |
| 
 | |
| static GstFlowReturn
 | |
| test_fragment_download_error_src_create (GstTestHTTPSrc * src,
 | |
|     guint64 offset,
 | |
|     guint length, GstBuffer ** retbuf, gpointer context, gpointer user_data)
 | |
| {
 | |
|   const GstMssDemuxTestInputData *input =
 | |
|       (const GstMssDemuxTestInputData *) context;
 | |
|   fail_unless (input != NULL);
 | |
|   if (!g_str_has_suffix (input->uri, ".mpd") && offset > 2000) {
 | |
|     GST_DEBUG ("network_error %s %" G_GUINT64_FORMAT " @ %d",
 | |
|         input->uri, offset, 2000);
 | |
|     GST_ELEMENT_ERROR (src, RESOURCE, READ,
 | |
|         (("A network error occurred, or the server closed the connection unexpectedly.")), ("A network error occurred, or the server closed the connection unexpectedly."));
 | |
|     return GST_FLOW_ERROR;
 | |
|   }
 | |
|   return gst_mssdemux_http_src_create (src, offset, length, retbuf, context,
 | |
|       user_data);
 | |
| }
 | |
| 
 | |
| /* function to check total size of data received by AppSink
 | |
|  * will be called when AppSink receives eos.
 | |
|  */
 | |
| static void
 | |
| testFragmentDownloadErrorCheckSizeOfDataReceived (GstAdaptiveDemuxTestEngine *
 | |
|     engine, GstAdaptiveDemuxTestOutputStream * stream, gpointer user_data)
 | |
| {
 | |
|   GstAdaptiveDemuxTestCase *testData = GST_ADAPTIVE_DEMUX_TEST_CASE (user_data);
 | |
|   GstAdaptiveDemuxTestExpectedOutput *testOutputStreamData;
 | |
| 
 | |
|   testOutputStreamData =
 | |
|       gst_adaptive_demux_test_find_test_data_by_stream (testData, stream, NULL);
 | |
|   fail_unless (testOutputStreamData != NULL);
 | |
| 
 | |
|   /* expect to receive more than 0 */
 | |
|   fail_unless (stream->total_received_size > 0,
 | |
|       "size validation failed for %s, expected > 0, received %d",
 | |
|       testOutputStreamData->name, stream->total_received_size);
 | |
| 
 | |
|   /* expect to receive less than file size */
 | |
|   fail_unless (stream->total_received_size <
 | |
|       testOutputStreamData->expected_size,
 | |
|       "size validation failed for %s, expected < %d received %d",
 | |
|       testOutputStreamData->name, testOutputStreamData->expected_size,
 | |
|       stream->total_received_size);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test fragment download error
 | |
|  * Let the adaptive demux download a few bytes, then instruct the
 | |
|  * GstTestHTTPSrc element to generate an error.
 | |
|  */
 | |
| GST_START_TEST (testFragmentDownloadError)
 | |
| {
 | |
|   const gchar *mpd =
 | |
|       "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
 | |
|       "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" Duration=\"4000000\">"
 | |
|       "<StreamIndex Type=\"audio\" Language=\"eng\" QualityLevels=\"1\" Chunks=\"1\" Url=\"QualityLevels({bitrate})/Fragments(audio_eng={start time})\">"
 | |
|       "<QualityLevel Index=\"0\" Bitrate=\"200029\" FourCC=\"AACL\" SamplingRate=\"48000\" Channels=\"2\" BitsPerSample=\"16\" PacketSize=\"4\" AudioTag=\"255\" CodecPrivateData=\"1190\" />"
 | |
|       "<c n=\"0\" d=\"10000000\" />" "</StreamIndex>" "</SmoothStreamingMedia>";
 | |
| 
 | |
|   GstMssDemuxTestInputData inputTestData[] = {
 | |
|     {"http://unit.test/Manifest", (guint8 *) mpd, 0},
 | |
|     {"http://unit.test/QualityLevels(200029)/Fragments(audio_eng=0)", NULL,
 | |
|         5000},
 | |
|     {NULL, NULL, 0},
 | |
|   };
 | |
|   GstTestHTTPSrcCallbacks http_src_callbacks = { 0 };
 | |
|   GstAdaptiveDemuxTestExpectedOutput outputTestData[] = {
 | |
|     {"audio_00", 5000, NULL},
 | |
|   };
 | |
|   GstAdaptiveDemuxTestCallbacks test_callbacks = { 0 };
 | |
|   GstAdaptiveDemuxTestCase *testData;
 | |
| 
 | |
|   testData = gst_adaptive_demux_test_case_new ();
 | |
|   http_src_callbacks.src_start = gst_mssdemux_http_src_start;
 | |
|   http_src_callbacks.src_create = test_fragment_download_error_src_create;
 | |
|   COPY_OUTPUT_TEST_DATA (outputTestData, testData);
 | |
|   test_callbacks.appsink_received_data =
 | |
|       gst_adaptive_demux_test_check_received_data;
 | |
|   test_callbacks.appsink_eos = testFragmentDownloadErrorCheckSizeOfDataReceived;
 | |
|   /*  test_callbacks.demux_sent_eos = gst_adaptive_demux_test_check_size_of_received_data; */
 | |
| 
 | |
|   test_callbacks.bus_error_message = testDownloadErrorMessageCallback;
 | |
| 
 | |
|   gst_test_http_src_install_callbacks (&http_src_callbacks, inputTestData);
 | |
|   gst_adaptive_demux_test_run (DEMUX_ELEMENT_NAME,
 | |
|       "http://unit.test/Manifest", &test_callbacks, testData);
 | |
|   g_object_unref (testData);
 | |
| }
 | |
| 
 | |
| GST_END_TEST;
 | |
| 
 | |
| static Suite *
 | |
| mss_demux_suite (void)
 | |
| {
 | |
|   Suite *s = suite_create ("mss_demux");
 | |
|   TCase *tc_basicTest = tcase_create ("basicTest");
 | |
| 
 | |
|   tcase_add_test (tc_basicTest, simpleTest);
 | |
|   tcase_add_test (tc_basicTest, testSeek);
 | |
|   tcase_add_test (tc_basicTest, testSeekKeyUnitPosition);
 | |
|   tcase_add_test (tc_basicTest, testSeekPosition);
 | |
|   tcase_add_test (tc_basicTest, testSeekUpdateStopPosition);
 | |
|   tcase_add_test (tc_basicTest, testSeekSnapBeforePosition);
 | |
|   tcase_add_test (tc_basicTest, testSeekSnapAfterPosition);
 | |
|   tcase_add_test (tc_basicTest, testReverseSeekSnapBeforePosition);
 | |
|   tcase_add_test (tc_basicTest, testReverseSeekSnapAfterPosition);
 | |
|   tcase_add_test (tc_basicTest, testDownloadError);
 | |
|   tcase_add_test (tc_basicTest, testFragmentDownloadError);
 | |
|   tcase_add_test (tc_basicTest, testQuery);
 | |
| 
 | |
|   tcase_add_unchecked_fixture (tc_basicTest, gst_adaptive_demux_test_setup,
 | |
|       gst_adaptive_demux_test_teardown);
 | |
| 
 | |
|   suite_add_tcase (s, tc_basicTest);
 | |
| 
 | |
|   return s;
 | |
| }
 | |
| 
 | |
| GST_CHECK_MAIN (mss_demux);
 |