mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-09 19:09:41 +00:00
412 lines
13 KiB
C
412 lines
13 KiB
C
|
/* 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;
|
||
|
}
|