/* Gstreamer * Copyright (C) <2014> Jesper Larsen * * 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 #include static const guint8 pat_data_check[] = { 0x00, 0xB0, 0x11, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x30, 0x00, 0x01, 0xe0, 0x31, 0x98, 0xdf, 0x37, 0xc4 }; static const guint8 pmt_data_check[] = { 0x02, 0xb0, 0x29, 0x00, 0x01, 0xc1, 0x00, 0x00, 0xff, 0xff, 0xf0, 0x06, 0x05, 0x04, 0x48, 0x44, 0x4d, 0x56, 0x1b, 0xe0, 0x40, 0xF0, 0x06, 0x05, 0x04, 0x48, 0x44, 0x4d, 0x56, 0x1b, 0xe0, 0x41, 0xF0, 0x06, 0x05, 0x04, 0x48, 0x44, 0x4d, 0x56, 0x15, 0x41, 0x5f, 0x5b }; static const guint8 nit_data_check[] = { 0x40, 0xf0, 0x49, 0x1f, 0xff, 0xc1, 0x00, 0x00, 0xf0, 0x0e, 0x40, 0x0c, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x20, 0x6e, 0x61, 0x6d, 0x65, 0xf0, 0x2e, 0x1f, 0xff, 0x1f, 0xfe, 0xf0, 0x11, 0x40, 0x0f, 0x41, 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x1f, 0xff, 0x1f, 0xfe, 0xf0, 0x11, 0x40, 0x0f, 0x41, 0x6e, 0x6f, 0x74, 0x68, 0x65, 0x72, 0x20, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0xce, 0x03, 0xf5, 0x94 }; static const guint8 sdt_data_check[] = { 0x42, 0xf0, 0x38, 0x1f, 0xff, 0xc1, 0x00, 0x00, 0x1f, 0xff, 0xff, 0x00, 0x00, 0xFF, 0x90, 0x11, 0x48, 0x0f, 0x01, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x00, 0x01, 0xFF, 0xB0, 0x11, 0x48, 0x0f, 0x01, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x04, 0x4e, 0x61, 0x6d, 0x65, 0x25, 0xe5, 0x02, 0xd9 }; static const guint8 stt_data_check[] = { 0xcd, 0xf0, 0x11, 0x00, 0x00, 0xc1, 0x00, 0x00, 0x00, 0x23, 0xb4, 0xe6, 0x5C, 0x0c, 0xc0, 0x00, 0xc4, 0x86, 0x56, 0xa5 }; GST_START_TEST (test_mpegts_pat) { GstMpegtsPatProgram *program; GPtrArray *pat; GstMpegtsSection *pat_section; gint i; guint8 *data; gsize data_size; /* Check creation of PAT */ pat = gst_mpegts_pat_new (); for (i = 0; i < 2; i++) { program = gst_mpegts_pat_program_new (); program->program_number = i; program->network_or_program_map_PID = 0x30 + i; g_ptr_array_add (pat, program); } pat_section = gst_mpegts_section_from_pat (pat, 0); fail_if (pat_section == NULL); /* Re-parse the PAT from section */ pat = gst_mpegts_section_get_pat (pat_section); fail_unless (pat->len == 2); for (i = 0; i < 2; i++) { program = g_ptr_array_index (pat, i); assert_equals_int (program->program_number, i); assert_equals_int (program->network_or_program_map_PID, 0x30 + i); } g_ptr_array_unref (pat); pat = NULL; /* Packetize the section, and check the data integrity */ data = gst_mpegts_section_packetize (pat_section, &data_size); fail_if (data == NULL); for (i = 0; i < data_size; i++) { if (data[i] != pat_data_check[i]) fail ("0x%X != 0x%X in byte %d of PAT section", data[i], pat_data_check[i], i); } /* Check assertion on bad CRC. Reset parsed data, and make the CRC corrupt */ pat_section->data[pat_section->section_length - 1]++; pat_section->destroy_parsed (pat_section->cached_parsed); pat_section->cached_parsed = NULL; pat = gst_mpegts_section_get_pat (pat_section); fail_unless (pat == NULL); gst_mpegts_section_unref (pat_section); } GST_END_TEST; GST_START_TEST (test_mpegts_pmt) { GstMpegtsPMT *pmt; GstMpegtsPMTStream *stream; GstMpegtsDescriptor *desc; GstMpegtsSection *pmt_section; guint8 *data; gsize data_size; gint i; /* Check creation of PMT */ pmt = gst_mpegts_pmt_new (); pmt->pcr_pid = 0x1FFF; pmt->program_number = 1; desc = gst_mpegts_descriptor_from_registration ("HDMV", NULL, 0); g_ptr_array_add (pmt->descriptors, desc); for (i = 0; i < 2; i++) { stream = gst_mpegts_pmt_stream_new (); stream->stream_type = GST_MPEGTS_STREAM_TYPE_VIDEO_H264; stream->pid = 0x40 + i; desc = gst_mpegts_descriptor_from_registration ("HDMV", NULL, 0); g_ptr_array_add (stream->descriptors, desc); g_ptr_array_add (pmt->streams, stream); } pmt_section = gst_mpegts_section_from_pmt (pmt, 0x30); fail_if (pmt_section == NULL); /* Re-parse the PMT from section */ pmt = (GstMpegtsPMT *) gst_mpegts_section_get_pmt (pmt_section); fail_unless (pmt->pcr_pid == 0x1FFF); fail_unless (pmt->program_number == 1); fail_unless (pmt->descriptors->len == 1); fail_unless (pmt->streams->len == 2); desc = (GstMpegtsDescriptor *) gst_mpegts_find_descriptor (pmt->descriptors, GST_MTS_DESC_REGISTRATION); fail_if (desc == NULL); for (i = 0; i < 2; i++) { stream = g_ptr_array_index (pmt->streams, i); fail_unless (stream->stream_type == GST_MPEGTS_STREAM_TYPE_VIDEO_H264); fail_unless (stream->pid == 0x40 + i); fail_unless (stream->descriptors->len == 1); desc = (GstMpegtsDescriptor *) gst_mpegts_find_descriptor (stream->descriptors, GST_MTS_DESC_REGISTRATION); fail_if (desc == NULL); } /* Packetize the section, and check data integrity */ data = gst_mpegts_section_packetize (pmt_section, &data_size); for (i = 0; i < data_size; i++) { if (data[i] != pmt_data_check[i]) fail ("0x%X != 0x%X in byte %d of PMT section", data[i], pmt_data_check[i], i); } /* Check assertion on bad CRC. Reset parsed data, and make the CRC corrupt */ pmt_section->data[pmt_section->section_length - 1]++; pmt_section->destroy_parsed (pmt_section->cached_parsed); pmt_section->cached_parsed = NULL; pmt = (GstMpegtsPMT *) gst_mpegts_section_get_pmt (pmt_section); fail_unless (pmt == NULL); gst_mpegts_section_unref (pmt_section); } GST_END_TEST; GST_START_TEST (test_mpegts_nit) { GstMpegtsNITStream *stream; GstMpegtsNIT *nit; GstMpegtsDescriptor *desc; GstMpegtsSection *nit_section; gchar *name; guint8 *data; gsize data_size; gint i; /* Check creation of NIT */ nit = gst_mpegts_nit_new (); nit->actual_network = TRUE; nit->network_id = 0x1FFF; desc = gst_mpegts_descriptor_from_dvb_network_name ("Network name"); fail_if (desc == NULL); g_ptr_array_add (nit->descriptors, desc); for (i = 0; i < 2; i++) { stream = gst_mpegts_nit_stream_new (); stream->transport_stream_id = 0x1FFF; stream->original_network_id = 0x1FFE; desc = gst_mpegts_descriptor_from_dvb_network_name ("Another network"); g_ptr_array_add (stream->descriptors, desc); g_ptr_array_add (nit->streams, stream); } nit_section = gst_mpegts_section_from_nit (nit); fail_if (nit_section == NULL); /* Re-parse the NIT from section */ nit = (GstMpegtsNIT *) gst_mpegts_section_get_nit (nit_section); fail_unless (nit->descriptors->len == 1); fail_unless (nit->streams->len == 2); fail_unless (nit->actual_network == TRUE); fail_unless (nit->network_id == 0x1FFF); desc = (GstMpegtsDescriptor *) gst_mpegts_find_descriptor (nit->descriptors, GST_MTS_DESC_DVB_NETWORK_NAME); fail_if (desc == NULL); fail_unless (gst_mpegts_descriptor_parse_dvb_network_name (desc, &name) == TRUE); g_free (name); for (i = 0; i < 2; i++) { stream = g_ptr_array_index (nit->streams, i); fail_unless (stream->transport_stream_id == 0x1FFF); fail_unless (stream->original_network_id == 0x1FFE); fail_unless (stream->descriptors->len == 1); desc = (GstMpegtsDescriptor *) gst_mpegts_find_descriptor (stream->descriptors, GST_MTS_DESC_DVB_NETWORK_NAME); fail_unless (gst_mpegts_descriptor_parse_dvb_network_name (desc, &name) == TRUE); g_free (name); } /* Packetize the section, and check data integrity */ data = gst_mpegts_section_packetize (nit_section, &data_size); fail_if (data == NULL); for (i = 0; i < data_size; i++) { if (data[i] != nit_data_check[i]) fail ("0x%X != 0x%X in byte %d of NIT section", data[i], nit_data_check[i], i); } /* Check assertion on bad CRC. Reset parsed data, and make the CRC corrupt */ nit_section->data[nit_section->section_length - 1]++; nit_section->destroy_parsed (nit_section->cached_parsed); nit_section->cached_parsed = NULL; nit = (GstMpegtsNIT *) gst_mpegts_section_get_nit (nit_section); fail_unless (nit == NULL); gst_mpegts_section_unref (nit_section); } GST_END_TEST; GST_START_TEST (test_mpegts_sdt) { GstMpegtsSDTService *service; GstMpegtsSDT *sdt; GstMpegtsDescriptor *desc; GstMpegtsSection *sdt_section; guint8 *data; gsize data_size; gint i; /* Check creation of SDT */ sdt = gst_mpegts_sdt_new (); sdt->actual_ts = TRUE; sdt->original_network_id = 0x1FFF; sdt->transport_stream_id = 0x1FFF; for (i = 0; i < 2; i++) { service = gst_mpegts_sdt_service_new (); service->service_id = i; service->EIT_schedule_flag = TRUE; service->EIT_present_following_flag = TRUE; service->running_status = GST_MPEGTS_RUNNING_STATUS_RUNNING + i; service->free_CA_mode = TRUE; desc = gst_mpegts_descriptor_from_dvb_service (GST_DVB_SERVICE_DIGITAL_TELEVISION, "Name", "Provider"); g_ptr_array_add (service->descriptors, desc); g_ptr_array_add (sdt->services, service); } sdt_section = gst_mpegts_section_from_sdt (sdt); fail_if (sdt_section == NULL); /* Re-parse the SDT from section */ sdt = (GstMpegtsSDT *) gst_mpegts_section_get_sdt (sdt_section); fail_unless (sdt->services->len == 2); fail_unless (sdt->actual_ts == TRUE); fail_unless (sdt->original_network_id == 0x1FFF); fail_unless (sdt->transport_stream_id == 0x1FFF); for (i = 0; i < 2; i++) { service = g_ptr_array_index (sdt->services, i); fail_if (service == NULL); fail_unless (service->descriptors->len == 1); fail_unless (service->service_id == i); fail_unless (service->EIT_schedule_flag == TRUE); fail_unless (service->EIT_present_following_flag == TRUE); fail_unless (service->running_status == GST_MPEGTS_RUNNING_STATUS_RUNNING + i); fail_unless (service->free_CA_mode == TRUE); desc = (GstMpegtsDescriptor *) gst_mpegts_find_descriptor (service->descriptors, GST_MTS_DESC_DVB_SERVICE); fail_if (desc == NULL); fail_unless (gst_mpegts_descriptor_parse_dvb_service (desc, NULL, NULL, NULL) == TRUE); } /* Packetize the section, and check data integrity */ data = gst_mpegts_section_packetize (sdt_section, &data_size); fail_if (data == NULL); for (i = 0; i < data_size; i++) { if (data[i] != sdt_data_check[i]) fail ("0x%X != 0x%X in byte %d of SDT section", data[i], sdt_data_check[i], i); } /* Check assertion on bad CRC. Reset parsed data, and make the CRC corrupt */ sdt_section->data[sdt_section->section_length - 1]++; sdt_section->destroy_parsed (sdt_section->cached_parsed); sdt_section->cached_parsed = NULL; sdt = (GstMpegtsSDT *) gst_mpegts_section_get_sdt (sdt_section); fail_unless (sdt == NULL); gst_mpegts_section_unref (sdt_section); } GST_END_TEST; GST_START_TEST (test_mpegts_atsc_stt) { const GstMpegtsAtscSTT *stt; GstMpegtsSection *section; guint8 *data; GstDateTime *dt; data = g_memdup (stt_data_check, 20); section = gst_mpegts_section_new (0x1ffb, data, 20); stt = gst_mpegts_section_get_atsc_stt (section); fail_if (stt == NULL); fail_unless (stt->protocol_version == 0); fail_unless (stt->system_time == 0x23b4e65c); fail_unless (stt->gps_utc_offset == 12); fail_unless (stt->ds_status == 1); fail_unless (stt->ds_dayofmonth == 0); fail_unless (stt->ds_hour == 0); dt = gst_mpegts_atsc_stt_get_datetime_utc ((GstMpegtsAtscSTT *) stt); fail_unless (gst_date_time_get_day (dt) == 30); fail_unless (gst_date_time_get_month (dt) == 12); fail_unless (gst_date_time_get_year (dt) == 1998); fail_unless (gst_date_time_get_hour (dt) == 13); fail_unless (gst_date_time_get_minute (dt) == 0); fail_unless (gst_date_time_get_second (dt) == 0); gst_date_time_unref (dt); gst_mpegts_section_unref (section); } GST_END_TEST; static const guint8 registration_descriptor[] = { 0x05, 0x04, 0x48, 0x44, 0x4d, 0x56 }; GST_START_TEST (test_mpegts_descriptors) { GstMpegtsDescriptor *desc; guint i; /* * Registration descriptor (0x05) */ /* Check creation of descriptor */ desc = gst_mpegts_descriptor_from_registration ("HDMV", NULL, 0); fail_if (desc == NULL); fail_unless (desc->length == 4); fail_unless (desc->tag == 0x05); for (i = 0; i < 6; i++) { if (registration_descriptor[i] != desc->data[i]) fail ("0x%X != 0x%X in byte %d of registration descriptor", desc->data[i], registration_descriptor[i], i); } gst_mpegts_descriptor_free (desc); } GST_END_TEST; static const guint8 network_name_descriptor[] = { 0x40, 0x04, 0x4e, 0x61, 0x6d, 0x65 }; static const guint8 service_descriptor[] = { 0x48, 0x0f, 0x01, 0x08, 0x50, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x72, 0x04, 0x4e, 0x61, 0x6d, 0x65 }; GST_START_TEST (test_mpegts_dvb_descriptors) { GstMpegtsDescriptor *desc; GstMpegtsDVBServiceType service_type; gchar *string, *provider; gchar long_string[257]; gboolean ret; guint i; /* * Network name descriptor (0x40) */ /* Check creation of descriptor */ desc = gst_mpegts_descriptor_from_dvb_network_name ("Name"); fail_if (desc == NULL); fail_unless (desc->length == 4); fail_unless (desc->tag == 0x40); for (i = 0; i < 6; i++) { if (desc->data[i] != network_name_descriptor[i]) fail ("0x%X != 0x%X in byte %d of network name descriptor", desc->data[i], network_name_descriptor[i], i); } /* Check parsing of descriptor */ ret = gst_mpegts_descriptor_parse_dvb_network_name (desc, &string); fail_unless (ret == TRUE); fail_unless (strcmp (string, "Name") == 0); g_free (string); gst_mpegts_descriptor_free (desc); /* Descriptor should fail if string is more than 255 bytes */ memset (long_string, 0x41, 256); long_string[256] = 0x00; fail_if (gst_mpegts_descriptor_from_dvb_network_name (long_string) != NULL); /* * Service descriptor (0x48) */ /* Check creation of descriptor with data */ desc = gst_mpegts_descriptor_from_dvb_service (GST_DVB_SERVICE_DIGITAL_TELEVISION, "Name", "Provider"); fail_if (desc == NULL); fail_unless (desc->length == 15); fail_unless (desc->tag == 0x48); for (i = 0; i < 17; i++) { if (desc->data[i] != service_descriptor[i]) fail ("0x%X != 0x%X in byte %d of service descriptor", desc->data[i], service_descriptor[i], i); } /* Check parsing of descriptor with data */ ret = gst_mpegts_descriptor_parse_dvb_service (desc, &service_type, &string, &provider); fail_unless (ret == TRUE); fail_unless (service_type == GST_DVB_SERVICE_DIGITAL_TELEVISION); fail_unless (strcmp (string, "Name") == 0); fail_unless (strcmp (provider, "Provider") == 0); g_free (string); g_free (provider); gst_mpegts_descriptor_free (desc); /* Check creation of descriptor without data */ desc = gst_mpegts_descriptor_from_dvb_service (GST_DVB_SERVICE_DIGITAL_TELEVISION, NULL, NULL); fail_if (desc == NULL); fail_unless (desc->length == 3); fail_unless (desc->tag == 0x48); /* Check parsing of descriptor without data */ ret = gst_mpegts_descriptor_parse_dvb_service (desc, NULL, NULL, NULL); fail_unless (ret == TRUE); gst_mpegts_descriptor_free (desc); /* Descriptor should fail if string is more than 255 bytes */ memset (long_string, 0x41, 256); long_string[256] = 0x00; desc = gst_mpegts_descriptor_from_dvb_service (GST_DVB_SERVICE_DIGITAL_TELEVISION, long_string, NULL); fail_if (desc != NULL); desc = gst_mpegts_descriptor_from_dvb_service (GST_DVB_SERVICE_DIGITAL_TELEVISION, NULL, long_string); fail_if (desc != NULL); } GST_END_TEST; static Suite * mpegts_suite (void) { Suite *s = suite_create ("MPEG Transport Stream helper library"); TCase *tc_chain = tcase_create ("general"); gst_mpegts_initialize (); suite_add_tcase (s, tc_chain); tcase_add_test (tc_chain, test_mpegts_pat); tcase_add_test (tc_chain, test_mpegts_pmt); tcase_add_test (tc_chain, test_mpegts_nit); tcase_add_test (tc_chain, test_mpegts_sdt); tcase_add_test (tc_chain, test_mpegts_atsc_stt); tcase_add_test (tc_chain, test_mpegts_descriptors); tcase_add_test (tc_chain, test_mpegts_dvb_descriptors); return s; } int main (int argc, char **argv) { int nf; Suite *s = mpegts_suite (); SRunner *sr = srunner_create (s); gst_check_init (&argc, &argv); srunner_run_all (sr, CK_NORMAL); nf = srunner_ntests_failed (sr); srunner_free (sr); return nf; }