examples: Add an example of a mpeg-ts SI extractor
authorEdward Hervey <edward@collabora.com>
Mon, 1 Jul 2013 06:35:26 +0000 (08:35 +0200)
committerEdward Hervey <edward@collabora.com>
Wed, 3 Jul 2013 07:17:25 +0000 (09:17 +0200)
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 <any gst-launch line using tsdemux or 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
tests/examples/Makefile.am
tests/examples/mpegts/Makefile.am [new file with mode: 0644]
tests/examples/mpegts/ts-parser.c [new file with mode: 0644]

index 9dbcc8f..156cca4 100644 (file)
@@ -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
index 3db2210..4351399 100644 (file)
@@ -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 (file)
index 0000000..09f6e9d
--- /dev/null
@@ -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 (file)
index 0000000..402bc1d
--- /dev/null
@@ -0,0 +1,411 @@
+/* GStreamer
+ *
+ * ts-parser.c: sample application to display mpeg-ts info from any pipeline
+ * Copyright (C) 2013
+ *           Edward Hervey <bilboed@gmail.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.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <glib.h>
+#include <glib-object.h>
+#include <glib/gprintf.h>
+#include <gst/gst.h>
+#include <gst/mpegts/mpegts.h>
+
+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;
+}