From dc160e7ca70c4e6985be55c1b850fb829f8dcb03 Mon Sep 17 00:00:00 2001 From: Edward Hervey Date: Mon, 1 Jul 2013 08:35:26 +0200 Subject: [PATCH] examples: Add an example of a mpeg-ts SI extractor Serves as an example of usage of the new mpegts library from an application. Will parse/dump all sections received on a bus. Usage is ./tsparse Examples: ./tsparse file:///some/mpegtsfile ! tsparse ! fakesink ./tsparse dvb://CHANNEL ! tsparse ! fakesink ./tsparse playbin uri=dvb://CHANNEL ./tsparse playbin uri=file:///some/mpegtsfile ... https://bugzilla.gnome.org/show_bug.cgi?id=702724 --- configure.ac | 1 + tests/examples/Makefile.am | 4 +- tests/examples/mpegts/Makefile.am | 7 + tests/examples/mpegts/ts-parser.c | 411 ++++++++++++++++++++++++++++++ 4 files changed, 421 insertions(+), 2 deletions(-) create mode 100644 tests/examples/mpegts/Makefile.am create mode 100644 tests/examples/mpegts/ts-parser.c diff --git a/configure.ac b/configure.ac index 9dbcc8fbbc..156cca4dd0 100644 --- a/configure.ac +++ b/configure.ac @@ -2461,6 +2461,7 @@ tests/files/Makefile tests/examples/Makefile tests/examples/camerabin2/Makefile tests/examples/directfb/Makefile +tests/examples/mpegts/Makefile tests/examples/mxf/Makefile tests/examples/opencv/Makefile tests/examples/uvch264/Makefile diff --git a/tests/examples/Makefile.am b/tests/examples/Makefile.am index 3db2210e61..43513994a2 100644 --- a/tests/examples/Makefile.am +++ b/tests/examples/Makefile.am @@ -18,7 +18,7 @@ endif OPENCV_EXAMPLES=opencv -SUBDIRS= $(DIRECTFB_DIR) $(GTK_EXAMPLES) $(OPENCV_EXAMPLES) -DIST_SUBDIRS= camerabin2 directfb mxf opencv uvch264 +SUBDIRS= mpegts $(DIRECTFB_DIR) $(GTK_EXAMPLES) $(OPENCV_EXAMPLES) +DIST_SUBDIRS= mpegts camerabin2 directfb mxf opencv uvch264 include $(top_srcdir)/common/parallel-subdirs.mak diff --git a/tests/examples/mpegts/Makefile.am b/tests/examples/mpegts/Makefile.am new file mode 100644 index 0000000000..09f6e9d884 --- /dev/null +++ b/tests/examples/mpegts/Makefile.am @@ -0,0 +1,7 @@ +noinst_PROGRAMS = tsparser + +tsparser_SOURCES = ts-parser.c +tsparser_CFLAGS = $(GST_PLUGINS_BAD_CFLAGS) $(GST_CFLAGS) +tsparser_LDFLAGS = $(GST_LIBS) +tsparser_LDADD = \ + $(top_builddir)/gst-libs/gst/mpegts/libgstmpegts-$(GST_API_VERSION).la diff --git a/tests/examples/mpegts/ts-parser.c b/tests/examples/mpegts/ts-parser.c new file mode 100644 index 0000000000..402bc1d14b --- /dev/null +++ b/tests/examples/mpegts/ts-parser.c @@ -0,0 +1,411 @@ +/* GStreamer + * + * ts-parser.c: sample application to display mpeg-ts info from any pipeline + * Copyright (C) 2013 + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include + +static const gchar * +enum_name (GType instance_type, gint val) +{ + GEnumValue *en; + + en = g_enum_get_value (G_ENUM_CLASS (g_type_class_peek (instance_type)), val); + + if (!en) + return "UNKNOWN/PRIVATE"; + return en->value_nick; +} + +static void +dump_cable_delivery_descriptor (GstMpegTSDescriptor * desc, guint spacing) +{ + GstMpegTSCableDeliverySystemDescriptor res; + + if (gst_mpegts_descriptor_parse_cable_delivery_system (desc, &res)) { + g_printf ("%*s Cable Delivery Descriptor\n", spacing, ""); + g_printf ("%*s Frequency : %d Hz\n", spacing, "", res.frequency); + g_printf ("%*s Outer FEC : %d\n", spacing, "", res.outer_fec); + g_printf ("%*s modulation : %d\n", spacing, "", res.modulation); + g_printf ("%*s Symbol rate : %d sym/s\n", spacing, "", res.symbol_rate); + g_printf ("%*s Inner FEC : %d\n", spacing, "", res.fec_inner); + } +} + +static void +dump_logical_channel_descriptor (GstMpegTSDescriptor * desc, guint spacing) +{ + GstMpegTSLogicalChannelDescriptor res; + guint i; + + if (gst_mpegts_descriptor_parse_logical_channel (desc, &res)) { + g_printf ("%*s Logical Channel Descriptor (%d channels)\n", spacing, "", + res.nb_channels); + for (i = 0; i < res.nb_channels; i++) { + GstMpegTSLogicalChannel *chann = &res.channels[i]; + g_printf ("%*s service_id: 0x%04x, logical channel number:%4d\n", + spacing, "", chann->service_id, chann->logical_channel_number); + } + } +} + +static void +dump_iso_639_language (GstMpegTSDescriptor * desc, guint spacing) +{ + guint i; + GstMpegTSISO639LanguageDescriptor res; + + if (gst_mpegts_descriptor_parse_iso_639_language (desc, &res)) { + for (i = 0; i < res.nb_language; i++) + g_print + ("%*s ISO 639 Language Descriptor %c%c%c , audio_type:0x%x (%s)\n", + spacing, "", res.language[i][0], res.language[i][1], + res.language[i][2], res.audio_type[i], + enum_name (GST_TYPE_MPEG_TS_ISO639_AUDIO_TYPE, res.audio_type[i])); + } +} + + +static void +dump_descriptors (GArray * descriptors, guint spacing) +{ + guint i; + + for (i = 0; i < descriptors->len; i++) { + GstMpegTSDescriptor *desc = + &g_array_index (descriptors, GstMpegTSDescriptor, i); + g_printf ("%*s [descriptor 0x%02x (%s)]\n", spacing, "", + desc->descriptor_tag, enum_name (GST_TYPE_MPEG_TS_DESCRIPTOR_TYPE, + desc->descriptor_tag)); + switch (desc->descriptor_tag) { + case GST_MTS_DESC_DVB_NETWORK_NAME: + { + gchar *network_name; + if (gst_mpegts_descriptor_parse_dvb_network_name (desc, &network_name)) { + g_printf ("%*s Network Name : %s\n", spacing, "", network_name); + g_free (network_name); + } + break; + } + case GST_MTS_DESC_DVB_CABLE_DELIVERY_SYSTEM: + dump_cable_delivery_descriptor (desc, spacing + 2); + break; + case GST_MTS_DESC_DTG_LOGICAL_CHANNEL: + dump_logical_channel_descriptor (desc, spacing + 2); + break; + case GST_MTS_DESC_DVB_SERVICE: + { + gchar *service_name, *provider_name; + GstMpegTSDVBServiceType service_type; + if (gst_mpegts_descriptor_parse_dvb_service (desc, &service_type, + &service_name, &provider_name)) { + g_printf ("%*s Service Descriptor, type:0x%02x (%s)\n", spacing, "", + service_type, enum_name (GST_TYPE_MPEG_TSDVB_SERVICE_TYPE, + service_type)); + g_printf ("%*s service_name : %s\n", spacing, "", service_name); + g_printf ("%*s provider_name : %s\n", spacing, "", + provider_name); + g_free (service_name); + g_free (provider_name); + + } + break; + } + case GST_MTS_DESC_ISO_639_LANGUAGE: + dump_iso_639_language (desc, spacing + 2); + break; + case GST_MTS_DESC_DVB_SHORT_EVENT: + { + gchar *language_code, *event_name, *text; + if (gst_mpegts_descriptor_parse_dvb_short_event (desc, &language_code, + &event_name, &text)) { + g_printf ("%*s Short Event, language_code:%s\n", spacing, "", + language_code); + g_printf ("%*s event_name : %s\n", spacing, "", event_name); + g_printf ("%*s text : %s\n", spacing, "", text); + g_free (language_code); + g_free (event_name); + g_free (text); + } + } + break; + default: + break; + } + } +} + +static void +dump_pat (GstMpegTSSection * section) +{ + GArray *pat = gst_mpegts_section_get_pat (section); + guint i, len; + + len = pat->len; + g_printf (" %d program(s):\n", len); + + for (i = 0; i < len; i++) { + GstMpegTSPatProgram *patp = &g_array_index (pat, GstMpegTSPatProgram, i); + + g_print + (" program_number:%6d (0x%04x), network_or_program_map_PID:0x%04x\n", + patp->program_number, patp->program_number, + patp->network_or_program_map_PID); + } + + g_array_unref (pat); +} + +static void +dump_pmt (GstMpegTSSection * section) +{ + const GstMpegTSPMT *pmt = gst_mpegts_section_get_pmt (section); + guint i, len; + + g_printf (" program_number : 0x%04x\n", section->subtable_extension); + g_printf (" pcr_pid : 0x%04x\n", pmt->pcr_pid); + dump_descriptors (pmt->descriptors, 7); + len = pmt->streams->len; + g_printf (" %d Streams:\n", len); + for (i = 0; i < len; i++) { + GstMpegTSPMTStream *stream = g_ptr_array_index (pmt->streams, i); + g_printf (" pid:0x%04x , stream_type:0x%02x\n", stream->pid, + stream->stream_type); + dump_descriptors (stream->descriptors, 9); + } +} + +static void +dump_eit (GstMpegTSSection * section) +{ + const GstMpegTSEIT *eit = gst_mpegts_section_get_eit (section); + guint i, len; + + g_assert (eit); + + g_printf (" service_id : 0x%04x\n", section->subtable_extension); + g_printf (" transport_stream_id : 0x%04x\n", eit->transport_stream_id); + g_printf (" original_network_id : 0x%04x\n", eit->original_network_id); + g_printf (" segment_last_section_number:0x%02x, last_table_id:0x%02x\n", + eit->segment_last_section_number, eit->last_table_id); + g_printf (" actual_stream : %s, present_following : %s\n", + eit->actual_stream ? "TRUE" : "FALSE", + eit->present_following ? "TRUE" : "FALSE"); + + len = eit->events->len; + g_printf (" %d Event(s):\n", len); + for (i = 0; i < len; i++) { + gchar *tmp; + GstMpegTSEITEvent *event = g_ptr_array_index (eit->events, i); + + tmp = gst_date_time_to_iso8601_string (event->start_time); + g_printf (" event_id:0x%04x, start_time:%s, duration:%" + GST_TIME_FORMAT "\n", event->event_id, tmp, + GST_TIME_ARGS (event->duration * GST_SECOND)); + g_printf (" running_status:0x%02x (%s), free_CA_mode:%d (%s)\n", + event->running_status, enum_name (GST_TYPE_MPEG_TS_RUNNING_STATUS, + event->running_status), event->free_CA_mode, + event->free_CA_mode ? "MAYBE SCRAMBLED" : "NOT SCRAMBLED"); + g_free (tmp); + dump_descriptors (event->descriptors, 9); + } +} + +static void +dump_nit (GstMpegTSSection * section) +{ + const GstMpegTSNIT *nit = gst_mpegts_section_get_nit (section); + guint i, len; + + g_assert (nit); + + g_printf (" network_id : 0x%04x\n", section->subtable_extension); + g_printf (" actual_network : %s\n", + nit->actual_network ? "TRUE" : "FALSE"); + dump_descriptors (nit->descriptors, 7); + len = nit->streams->len; + g_printf (" %d Streams:\n", len); + for (i = 0; i < len; i++) { + GstMpegTSNITStream *stream = g_ptr_array_index (nit->streams, i); + g_printf + (" transport_stream_id:0x%04x , original_network_id:0x%02x\n", + stream->transport_stream_id, stream->original_network_id); + dump_descriptors (stream->descriptors, 9); + } +} + +static void +dump_sdt (GstMpegTSSection * section) +{ + const GstMpegTSSDT *sdt = gst_mpegts_section_get_sdt (section); + guint i, len; + + g_assert (sdt); + + g_printf (" original_network_id : 0x%04x\n", sdt->original_network_id); + g_printf (" actual_ts : %s\n", + sdt->actual_ts ? "TRUE" : "FALSE"); + len = sdt->services->len; + g_printf (" %d Services:\n", len); + for (i = 0; i < len; i++) { + GstMpegTSSDTService *service = g_ptr_array_index (sdt->services, i); + g_print + (" service_id:0x%04x, EIT_schedule_flag:%d, EIT_present_following_flag:%d\n", + service->service_id, service->EIT_schedule_flag, + service->EIT_present_following_flag); + g_print + (" running_status:0x%02x (%s), free_CA_mode:%d (%s)\n", + service->running_status, + enum_name (GST_TYPE_MPEG_TS_RUNNING_STATUS, service->running_status), + service->free_CA_mode, + service->free_CA_mode ? "MAYBE SCRAMBLED" : "NOT SCRAMBLED"); + dump_descriptors (service->descriptors, 9); + } +} + +static void +dump_tdt (GstMpegTSSection * section) +{ + GstDateTime *date = gst_mpegts_section_get_tdt (section); + gchar *str = gst_date_time_to_iso8601_string (date); + + g_printf (" utc_time : %s\n", str); + g_free (str); + gst_date_time_unref (date); +} + +static void +dump_section (GstMpegTSSection * section) +{ + switch (GST_MPEGTS_SECTION_TYPE (section)) { + case GST_MPEGTS_SECTION_PAT: + dump_pat (section); + break; + case GST_MPEGTS_SECTION_PMT: + dump_pmt (section); + break; + case GST_MPEGTS_SECTION_TDT: + dump_tdt (section); + break; + case GST_MPEGTS_SECTION_SDT: + dump_sdt (section); + break; + case GST_MPEGTS_SECTION_NIT: + dump_nit (section); + break; + case GST_MPEGTS_SECTION_EIT: + dump_eit (section); + break; + default: + g_printf (" Unknown section type\n"); + break; + } +} + +static void +_on_bus_message (GstBus * bus, GstMessage * message, GMainLoop * mainloop) +{ + /* g_printf ("Got message %s\n", GST_MESSAGE_TYPE_NAME (message)); */ + + switch (GST_MESSAGE_TYPE (message)) { + case GST_MESSAGE_ERROR: + case GST_MESSAGE_EOS: + g_main_loop_quit (mainloop); + break; + case GST_MESSAGE_ELEMENT: + { + GstMpegTSSection *section; + if ((section = gst_message_parse_mpegts_section (message))) { + g_print + ("Got section: PID:0x%04x type:%s (table_id 0x%02x (%s)) at offset %" + G_GUINT64_FORMAT "\n", section->pid, + enum_name (GST_TYPE_MPEG_TS_SECTION_TYPE, section->section_type), + section->table_id, enum_name (GST_TYPE_MPEG_TS_SECTION_TABLE_ID, + section->table_id), section->offset); + if (!section->short_section) { + g_print + (" subtable_extension:0x%04x, version_number:0x%02x\n", + section->subtable_extension, section->version_number); + g_print + (" section_number:0x%02x last_section_number:0x%02x crc:0x%08x\n", + section->section_number, section->last_section_number, + section->crc); + } + dump_section (section); + g_printf ("\n\n"); + gst_mpegts_section_unref (section); + } + break; + } + default: + break; + } +} + +int +main (int argc, gchar ** argv) +{ + GstElement *pipeline = NULL; + GError *error = NULL; + GstBus *bus; + GMainLoop *mainloop; + + gst_init (&argc, &argv); + + gst_mpegts_initialize (); + + pipeline = gst_parse_launchv ((const gchar **) &argv[1], &error); + if (error) { + g_printf ("pipeline could not be constructed: %s\n", error->message); + g_error_free (error); + return 1; + } + + /* Hack: ensure all enum type classes are loaded */ + g_type_class_ref (GST_TYPE_MPEG_TS_SECTION_TYPE); + g_type_class_ref (GST_TYPE_MPEG_TS_SECTION_TABLE_ID); + g_type_class_ref (GST_TYPE_MPEG_TS_RUNNING_STATUS); + g_type_class_ref (GST_TYPE_MPEG_TS_DESCRIPTOR_TYPE); + g_type_class_ref (GST_TYPE_MPEG_TS_ISO639_AUDIO_TYPE); + g_type_class_ref (GST_TYPE_MPEG_TSDVB_SERVICE_TYPE); + + mainloop = g_main_loop_new (NULL, FALSE); + + /* Put a bus handler */ + bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); + gst_bus_add_signal_watch (bus); + g_signal_connect (bus, "message", (GCallback) _on_bus_message, mainloop); + + /* Start pipeline */ + gst_element_set_state (pipeline, GST_STATE_PLAYING); + g_main_loop_run (mainloop); + + gst_element_set_state (pipeline, GST_STATE_NULL); + + return 0; +}