From 9eed28d3534913f8012eb4cf101b6bd8d79d7932 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Thu, 15 May 2014 12:31:05 -0300 Subject: [PATCH] mpegts: add atsc MGT section parsing Add a parsing function for MGT and also detect the EIT tables for ATSC, the EIT pids are reported inside the MGT and we are still only relying only on the table id for detecting it. In the future we would want to also check the pid and compare with whatever the MGT previously reported to confirm that it is indeed the EIT. https://bugzilla.gnome.org/show_bug.cgi?id=730435 --- gst-libs/gst/mpegts/gst-atsc-section.c | 146 +++++++++++++++++++++++++ gst-libs/gst/mpegts/gst-atsc-section.h | 47 ++++++++ gst-libs/gst/mpegts/gstmpegtssection.c | 7 ++ gst-libs/gst/mpegts/gstmpegtssection.h | 4 +- tests/examples/mpegts/ts-parser.c | 23 ++++ 5 files changed, 226 insertions(+), 1 deletion(-) diff --git a/gst-libs/gst/mpegts/gst-atsc-section.c b/gst-libs/gst/mpegts/gst-atsc-section.c index 553c65fe32..c77d0f8a11 100644 --- a/gst-libs/gst/mpegts/gst-atsc-section.c +++ b/gst-libs/gst/mpegts/gst-atsc-section.c @@ -250,3 +250,149 @@ gst_mpegts_section_get_atsc_cvct (GstMpegTsSection * section) return (const GstMpegTsAtscVCT *) section->cached_parsed; } + +/* MGT */ + +static GstMpegTsAtscMGTTable * +_gst_mpegts_atsc_mgt_table_copy (GstMpegTsAtscMGTTable * mgt_table) +{ + GstMpegTsAtscMGTTable *copy; + + copy = g_slice_dup (GstMpegTsAtscMGTTable, mgt_table); + copy->descriptors = g_ptr_array_ref (mgt_table->descriptors); + + return copy; +} + +static void +_gst_mpegts_atsc_mgt_table_free (GstMpegTsAtscMGTTable * mgt_table) +{ + g_ptr_array_unref (mgt_table->descriptors); + g_slice_free (GstMpegTsAtscMGTTable, mgt_table); +} + +G_DEFINE_BOXED_TYPE (GstMpegTsAtscMGTTable, gst_mpegts_atsc_mgt_table, + (GBoxedCopyFunc) _gst_mpegts_atsc_mgt_table_copy, + (GFreeFunc) _gst_mpegts_atsc_mgt_table_free); + +static GstMpegTsAtscMGT * +_gst_mpegts_atsc_mgt_copy (GstMpegTsAtscMGT * mgt) +{ + GstMpegTsAtscMGT *copy; + + copy = g_slice_dup (GstMpegTsAtscMGT, mgt); + copy->tables = g_ptr_array_ref (mgt->tables); + copy->descriptors = g_ptr_array_ref (mgt->descriptors); + + return copy; +} + +static void +_gst_mpegts_atsc_mgt_free (GstMpegTsAtscMGT * mgt) +{ + g_ptr_array_unref (mgt->tables); + g_ptr_array_unref (mgt->descriptors); + g_slice_free (GstMpegTsAtscMGT, mgt); +} + +G_DEFINE_BOXED_TYPE (GstMpegTsAtscMGT, gst_mpegts_atsc_mgt, + (GBoxedCopyFunc) _gst_mpegts_atsc_mgt_copy, + (GFreeFunc) _gst_mpegts_atsc_mgt_free); + +static gpointer +_parse_atsc_mgt (GstMpegTsSection * section) +{ + GstMpegTsAtscMGT *mgt = NULL; + guint i = 0; + guint8 *data, *end; + guint16 descriptors_loop_length; + + mgt = g_slice_new0 (GstMpegTsAtscMGT); + + data = section->data; + end = data + section->section_length; + + /* Skip already parsed data */ + data += 8; + + mgt->protocol_version = GST_READ_UINT8 (data); + data += 1; + mgt->tables_defined = GST_READ_UINT16_BE (data); + data += 2; + mgt->tables = g_ptr_array_new_full (mgt->tables_defined, + (GDestroyNotify) _gst_mpegts_atsc_mgt_table_free); + for (i = 0; i < mgt->tables_defined && data + 11 < end; i++) { + GstMpegTsAtscMGTTable *mgt_table; + + if (data + 11 >= end) { + GST_WARNING ("MGT data too short to parse inner table num %d", i); + goto error; + } + + mgt_table = g_slice_new0 (GstMpegTsAtscMGTTable); + g_ptr_array_add (mgt->tables, mgt_table); + + mgt_table->table_type = GST_READ_UINT16_BE (data); + data += 2; + mgt_table->pid = GST_READ_UINT16_BE (data) & 0x1FFF; + data += 2; + mgt_table->version_number = GST_READ_UINT8 (data) & 0x1F; + data += 1; + mgt_table->number_bytes = GST_READ_UINT32_BE (data); + data += 4; + descriptors_loop_length = GST_READ_UINT16_BE (data) & 0x0FFF; + data += 2; + + if (data + descriptors_loop_length >= end) { + GST_WARNING ("MGT data too short to parse inner table descriptors (table " + "num %d", i); + goto error; + } + mgt_table->descriptors = + gst_mpegts_parse_descriptors (data, descriptors_loop_length); + data += descriptors_loop_length; + } + + descriptors_loop_length = GST_READ_UINT16_BE (data) & 0xFFF; + data += 2; + if (data + descriptors_loop_length >= end) { + GST_WARNING ("MGT data too short to parse descriptors"); + goto error; + } + mgt->descriptors = + gst_mpegts_parse_descriptors (data, descriptors_loop_length); + data += descriptors_loop_length; + + return (gpointer) mgt; + +error: + if (mgt) + _gst_mpegts_atsc_mgt_free (mgt); + + return NULL; +} + + +/** + * gst_mpegts_section_get_atsc_mgt: + * @section: a #GstMpegTsSection of type %GST_MPEGTS_SECTION_ATSC_MGT + * + * Returns the #GstMpegTsAtscMGT contained in the @section. + * + * Returns: The #GstMpegTsAtscMGT contained in the section, or %NULL if an error + * happened. + */ +const GstMpegTsAtscMGT * +gst_mpegts_section_get_atsc_mgt (GstMpegTsSection * section) +{ + g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_ATSC_MGT, + NULL); + g_return_val_if_fail (section->cached_parsed || section->data, NULL); + + if (!section->cached_parsed) + section->cached_parsed = + __common_section_checks (section, 17, _parse_atsc_mgt, + (GDestroyNotify) _gst_mpegts_atsc_mgt_free); + + return (const GstMpegTsAtscMGT *) section->cached_parsed; +} diff --git a/gst-libs/gst/mpegts/gst-atsc-section.h b/gst-libs/gst/mpegts/gst-atsc-section.h index b7a5796d7f..caa612ef3e 100644 --- a/gst-libs/gst/mpegts/gst-atsc-section.h +++ b/gst-libs/gst/mpegts/gst-atsc-section.h @@ -122,6 +122,53 @@ GType gst_mpegts_atsc_vct_source_get_type (void); const GstMpegTsAtscVCT * gst_mpegts_section_get_atsc_tvct (GstMpegTsSection * section); const GstMpegTsAtscVCT * gst_mpegts_section_get_atsc_cvct (GstMpegTsSection * section); +/* MGT */ +#define GST_TYPE_MPEGTS_ATSC_MGT (gst_mpegts_atsc_mgt_get_type ()) +#define GST_TYPE_MPEGTS_ATSC_MGT_TABLE (gst_mpegts_atsc_mgt_table_get_type ()) + +typedef struct _GstMpegTsAtscMGTTable GstMpegTsAtscMGTTable; +typedef struct _GstMpegTsAtscMGT GstMpegTsAtscMGT; + +typedef enum { + GST_MPEG_TS_ATSC_MGT_TABLE_TYPE_EIT0 = 0x0100, + GST_MPEG_TS_ATSC_MGT_TABLE_TYPE_EIT127 = 0x017F, + GST_MPEG_TS_ATSC_MGT_TABLE_TYPE_ETT0 = 0x0200, + GST_MPEG_TS_ATSC_MGT_TABLE_TYPE_ETT127 = 0x027F +} GstMpegTsAtscMGTTableType; + +/** + * GstMpegTsAtscMGTTable: + * + * Source from a @GstMpegTsAtscMGT + */ +struct _GstMpegTsAtscMGTTable +{ + guint16 table_type; + guint16 pid; + guint8 version_number; + guint32 number_bytes; + GPtrArray *descriptors; +}; + +/** + * GstMpegTsAtscMGT: + * + * Terrestrial Virtual Channel Table (A65) + * + */ +struct _GstMpegTsAtscMGT +{ + guint8 protocol_version; + guint16 tables_defined; + GPtrArray *tables; + GPtrArray *descriptors; +}; + +GType gst_mpegts_atsc_mgt_get_type (void); +GType gst_mpegts_atsc_mgt_table_get_type (void); + +const GstMpegTsAtscMGT * gst_mpegts_section_get_atsc_mgt (GstMpegTsSection * section); + G_END_DECLS #endif /* GST_MPEGTS_SECTION_H */ diff --git a/gst-libs/gst/mpegts/gstmpegtssection.c b/gst-libs/gst/mpegts/gstmpegtssection.c index 70035c7622..d294d16d6b 100644 --- a/gst-libs/gst/mpegts/gstmpegtssection.c +++ b/gst-libs/gst/mpegts/gstmpegtssection.c @@ -1056,6 +1056,13 @@ _identify_section (guint16 pid, guint8 table_id) if (pid == 0x1ffb) return GST_MPEGTS_SECTION_ATSC_CVCT; break; + case GST_MTS_TABLE_ID_ATSC_MASTER_GUIDE: + if (pid == 0x1ffb) + return GST_MPEGTS_SECTION_ATSC_MGT; + break; + case GST_MTS_TABLE_ID_ATSC_EVENT_INFORMATION: + /* FIXME check pids reported on the MGT to confirm expectations */ + return GST_MPEGTS_SECTION_EIT; /* FIXME : FILL */ default: /* Handle ranges */ diff --git a/gst-libs/gst/mpegts/gstmpegtssection.h b/gst-libs/gst/mpegts/gstmpegtssection.h index 2fd9df6a27..536dce3865 100644 --- a/gst-libs/gst/mpegts/gstmpegtssection.h +++ b/gst-libs/gst/mpegts/gstmpegtssection.h @@ -53,6 +53,7 @@ GType gst_mpegts_section_get_type (void); * @GST_MPEGTS_SECTION_TOT: Time Offset Table (EN 300 468) * @GST_MPEGTS_SECTION_ATSC_TVCT: ATSC Terrestrial Virtual Channel Table (A65) * @GST_MPEGTS_SECTION_ATSC_CVCT: ATSC Cable Virtual Channel Table (A65) + * @GST_MPEGTS_SECTION_ATSC_MGT: ATSC Master Guide Table (A65) * * Types of #GstMpegTsSection that the library handles. */ @@ -69,7 +70,8 @@ typedef enum { GST_MPEGTS_SECTION_TDT, GST_MPEGTS_SECTION_TOT, GST_MPEGTS_SECTION_ATSC_TVCT, - GST_MPEGTS_SECTION_ATSC_CVCT + GST_MPEGTS_SECTION_ATSC_CVCT, + GST_MPEGTS_SECTION_ATSC_MGT } GstMpegTsSectionType; /** diff --git a/tests/examples/mpegts/ts-parser.c b/tests/examples/mpegts/ts-parser.c index c510e169a7..f10f403f5f 100644 --- a/tests/examples/mpegts/ts-parser.c +++ b/tests/examples/mpegts/ts-parser.c @@ -702,6 +702,26 @@ dump_tot (GstMpegTsSection * section) g_free (str); } +static void +dump_mgt (GstMpegTsSection * section) +{ + const GstMpegTsAtscMGT *mgt = gst_mpegts_section_get_atsc_mgt (section); + gint i; + + g_printf (" protocol_version : %u\n", mgt->protocol_version); + g_printf (" tables number : %d\n", mgt->tables->len); + for (i = 0; i < mgt->tables->len; i++) { + GstMpegTsAtscMGTTable *table = g_ptr_array_index (mgt->tables, i); + g_printf (" table %d)\n", i); + g_printf (" table_type : %u\n", table->table_type); + g_printf (" pid : 0x%x\n", table->pid); + g_printf (" version_number: %u\n", table->version_number); + g_printf (" number_bytes : %u\n", table->number_bytes); + dump_descriptors (table->descriptors, 9); + } + dump_descriptors (mgt->descriptors, 7); +} + static void dump_vct (GstMpegTsSection * section) { @@ -773,6 +793,9 @@ dump_section (GstMpegTsSection * section) case GST_MPEGTS_SECTION_EIT: dump_eit (section); break; + case GST_MPEGTS_SECTION_ATSC_MGT: + dump_mgt (section); + break; case GST_MPEGTS_SECTION_ATSC_CVCT: case GST_MPEGTS_SECTION_ATSC_TVCT: dump_vct (section);