From 7ced36eccd76c19692525fefdf574cd908b11e25 Mon Sep 17 00:00:00 2001 From: Thiago Santos Date: Mon, 19 May 2014 13:46:03 -0300 Subject: [PATCH] mpegts: atsc: add ETT structures and parsing ETT (extended text table) contains ATSC text information with descriptions of virtual channels and events. The text can be internationalized and also compressed. https://bugzilla.gnome.org/show_bug.cgi?id=730435 --- gst-libs/gst/mpegts/gst-atsc-section.c | 219 +++++++++++++++++++++++++++++++++ gst-libs/gst/mpegts/gst-atsc-section.h | 47 +++++++ gst-libs/gst/mpegts/gstmpegtssection.c | 3 + gst-libs/gst/mpegts/gstmpegtssection.h | 4 +- 4 files changed, 272 insertions(+), 1 deletion(-) diff --git a/gst-libs/gst/mpegts/gst-atsc-section.c b/gst-libs/gst/mpegts/gst-atsc-section.c index c77d0f8..a7118c1 100644 --- a/gst-libs/gst/mpegts/gst-atsc-section.c +++ b/gst-libs/gst/mpegts/gst-atsc-section.c @@ -396,3 +396,222 @@ gst_mpegts_section_get_atsc_mgt (GstMpegTsSection * section) return (const GstMpegTsAtscMGT *) section->cached_parsed; } + +static GstMpegTsAtscStringSegment * +_gst_mpegts_atsc_string_segment_copy (GstMpegTsAtscStringSegment * seg) +{ + GstMpegTsAtscStringSegment *copy; + + copy = g_slice_dup (GstMpegTsAtscStringSegment, seg); + + return copy; +} + +static void +_gst_mpegts_atsc_string_segment_free (GstMpegTsAtscStringSegment * seg) +{ + if (seg->cached_string) + g_free (seg->cached_string); + g_slice_free (GstMpegTsAtscStringSegment, seg); +} + +static void +_gst_mpegts_atsc_string_segment_decode_string (GstMpegTsAtscStringSegment * seg) +{ + g_return_if_fail (seg->cached_string == NULL); + + if (seg->compression_type != 0) { + GST_FIXME ("Compressed strings not yet supported"); + return; + } + /* FIXME check encoding */ + + seg->cached_string = + g_strndup ((gchar *) seg->compressed_data, seg->compressed_data_size); +} + +const gchar * +gst_mpegts_atsc_string_segment_get_string (GstMpegTsAtscStringSegment * seg) +{ + if (!seg->cached_string) + _gst_mpegts_atsc_string_segment_decode_string (seg); + + return seg->cached_string; +} + +G_DEFINE_BOXED_TYPE (GstMpegTsAtscStringSegment, gst_mpegts_atsc_string_segment, + (GBoxedCopyFunc) _gst_mpegts_atsc_string_segment_copy, + (GFreeFunc) _gst_mpegts_atsc_string_segment_free); + +static GstMpegTsAtscMultString * +_gst_mpegts_atsc_mult_string_copy (GstMpegTsAtscMultString * mstring) +{ + GstMpegTsAtscMultString *copy; + + copy = g_slice_dup (GstMpegTsAtscMultString, mstring); + copy->segments = g_ptr_array_ref (mstring->segments); + + return copy; +} + +static void +_gst_mpegts_atsc_mult_string_free (GstMpegTsAtscMultString * mstring) +{ + g_ptr_array_unref (mstring->segments); + g_slice_free (GstMpegTsAtscMultString, mstring); +} + +G_DEFINE_BOXED_TYPE (GstMpegTsAtscMultString, gst_mpegts_atsc_mult_string, + (GBoxedCopyFunc) _gst_mpegts_atsc_mult_string_copy, + (GFreeFunc) _gst_mpegts_atsc_mult_string_free); + +static GstMpegTsAtscETT * +_gst_mpegts_atsc_ett_copy (GstMpegTsAtscETT * ett) +{ + GstMpegTsAtscETT *copy; + + copy = g_slice_dup (GstMpegTsAtscETT, ett); + copy->messages = g_ptr_array_ref (ett->messages); + + return copy; +} + +static void +_gst_mpegts_atsc_ett_free (GstMpegTsAtscETT * ett) +{ + g_ptr_array_unref (ett->messages); + g_slice_free (GstMpegTsAtscETT, ett); +} + +G_DEFINE_BOXED_TYPE (GstMpegTsAtscETT, gst_mpegts_atsc_ett, + (GBoxedCopyFunc) _gst_mpegts_atsc_ett_copy, + (GFreeFunc) _gst_mpegts_atsc_ett_free); + +static gpointer +_parse_ett (GstMpegTsSection * section) +{ + GstMpegTsAtscETT *ett = NULL; + guint i = 0; + guint8 *data, *end; + guint8 num_strings; + + ett = g_slice_new0 (GstMpegTsAtscETT); + + data = section->data; + end = data + section->section_length; + + /* Skip already parsed data */ + data += 8; + + ett->protocol_version = GST_READ_UINT8 (data); + data += 1; + ett->etm_id = GST_READ_UINT32_BE (data); + data += 4; + + ett->messages = g_ptr_array_new_with_free_func ((GDestroyNotify) + _gst_mpegts_atsc_mult_string_free); + + if (end - data > 4) { + /* 1 is the minimum entry size, so no need to check here */ + num_strings = GST_READ_UINT8 (data); + data += 1; + + for (i = 0; i < num_strings; i++) { + GstMpegTsAtscMultString *mstring; + guint8 num_segments; + gint j; + + mstring = g_slice_new0 (GstMpegTsAtscMultString); + g_ptr_array_add (ett->messages, mstring); + mstring->segments = + g_ptr_array_new_full (num_strings, + (GDestroyNotify) _gst_mpegts_atsc_string_segment_free); + + /* each entry needs at least 4 bytes (lang code and segments number */ + if (end - data < 4 + 4) { + GST_WARNING ("PID %d invalid ETT entry length %d", + section->pid, (gint) (end - 4 - data)); + goto error; + } + + mstring->iso_639_langcode[0] = GST_READ_UINT8 (data); + data += 1; + mstring->iso_639_langcode[1] = GST_READ_UINT8 (data); + data += 1; + mstring->iso_639_langcode[2] = GST_READ_UINT8 (data); + data += 1; + num_segments = GST_READ_UINT8 (data); + data += 1; + + for (j = 0; j < num_segments; j++) { + GstMpegTsAtscStringSegment *seg; + + seg = g_slice_new0 (GstMpegTsAtscStringSegment); + g_ptr_array_add (mstring->segments, seg); + + /* each entry needs at least 4 bytes (lang code and segments number */ + if (end - data < 3 + 4) { + GST_WARNING ("PID %d invalid ETT entry length %d", + section->pid, (gint) (end - 4 - data)); + goto error; + } + + seg->compression_type = GST_READ_UINT8 (data); + data += 1; + seg->mode = GST_READ_UINT8 (data); + data += 1; + seg->compressed_data_size = GST_READ_UINT8 (data); + data += 1; + + if (end - data < seg->compressed_data_size + 4) { + GST_WARNING ("PID %d invalid ETT entry length %d", + section->pid, (gint) (end - 4 - data)); + goto error; + } + + if (seg->compressed_data_size) + seg->compressed_data = data; + data += seg->compressed_data_size; + } + + } + } + + if (data != end - 4) { + GST_WARNING ("PID %d invalid ETT parsed %d length %d", + section->pid, (gint) (data - section->data), section->section_length); + goto error; + } + + return (gpointer) ett; + +error: + if (ett) + _gst_mpegts_atsc_ett_free (ett); + + return NULL; + +} + +/** + * gst_mpegts_section_get_atsc_ett: + * @section: a #GstMpegTsSection of type %GST_MPEGTS_SECTION_ATSC_ETT + * + * Returns the #GstMpegTsAtscETT contained in the @section. + * + * Returns: The #GstMpegTsAtscETT contained in the section, or %NULL if an error + * happened. + */ +const GstMpegTsAtscETT * +gst_mpegts_section_get_atsc_ett (GstMpegTsSection * section) +{ + g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_ATSC_ETT, + 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_ett, + (GDestroyNotify) _gst_mpegts_atsc_ett_free); + + return (const GstMpegTsAtscETT *) section->cached_parsed; +} diff --git a/gst-libs/gst/mpegts/gst-atsc-section.h b/gst-libs/gst/mpegts/gst-atsc-section.h index caa612e..91fb265 100644 --- a/gst-libs/gst/mpegts/gst-atsc-section.h +++ b/gst-libs/gst/mpegts/gst-atsc-section.h @@ -169,6 +169,53 @@ GType gst_mpegts_atsc_mgt_table_get_type (void); const GstMpegTsAtscMGT * gst_mpegts_section_get_atsc_mgt (GstMpegTsSection * section); +/* ETT */ + +#define GST_TYPE_MPEGTS_ATSC_STRING_SEGMENT (gst_mpegts_atsc_string_segment_get_type()) +#define GST_TYPE_MPEGTS_ATSC_MULT_STRING (gst_mpegts_atsc_mult_string_get_type()) +#define GST_TYPE_MPEGTS_ATSC_ETT (gst_mpegts_atsc_ett_get_type()) + +typedef struct _GstMpegTsAtscStringSegment GstMpegTsAtscStringSegment; +typedef struct _GstMpegTsAtscMultString GstMpegTsAtscMultString; +typedef struct _GstMpegTsAtscETT GstMpegTsAtscETT; + +struct _GstMpegTsAtscStringSegment { + guint8 compression_type; + guint8 mode; + guint8 compressed_data_size; + guint8 *compressed_data; + + gchar *cached_string; +}; + +const gchar * gst_mpegts_atsc_string_segment_get_string (GstMpegTsAtscStringSegment * seg); + +struct _GstMpegTsAtscMultString { + gchar iso_639_langcode[4]; + GPtrArray *segments; +}; + +/** + * GstMpegTsAtscETT: + * @events: (element-type FIXME): List of texts + * + * Extended Text Table (ATSC) + * + */ +struct _GstMpegTsAtscETT +{ + guint16 protocol_version; + guint32 etm_id; + + GPtrArray *messages; +}; + +GType gst_mpegts_atsc_string_segment_get_type (void); +GType gst_mpegts_atsc_mult_string_get_type (void); +GType gst_mpegts_atsc_ett_get_type (void); + +const GstMpegTsAtscETT *gst_mpegts_section_get_atsc_ett (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 d294d16..97118f0 100644 --- a/gst-libs/gst/mpegts/gstmpegtssection.c +++ b/gst-libs/gst/mpegts/gstmpegtssection.c @@ -1063,6 +1063,9 @@ _identify_section (guint16 pid, guint8 table_id) case GST_MTS_TABLE_ID_ATSC_EVENT_INFORMATION: /* FIXME check pids reported on the MGT to confirm expectations */ return GST_MPEGTS_SECTION_EIT; + case GST_MTS_TABLE_ID_ATSC_CHANNEL_OR_EVENT_EXTENDED_TEXT: + /* FIXME check pids reported on the MGT to confirm expectations */ + return GST_MPEGTS_SECTION_ATSC_ETT; /* FIXME : FILL */ default: /* Handle ranges */ diff --git a/gst-libs/gst/mpegts/gstmpegtssection.h b/gst-libs/gst/mpegts/gstmpegtssection.h index 536dce3..2ee8f39 100644 --- a/gst-libs/gst/mpegts/gstmpegtssection.h +++ b/gst-libs/gst/mpegts/gstmpegtssection.h @@ -54,6 +54,7 @@ GType gst_mpegts_section_get_type (void); * @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) + * @GST_MPEGTS_SECTION_ATSC_ETT: ATSC Extended Text Table (A65) * * Types of #GstMpegTsSection that the library handles. */ @@ -71,7 +72,8 @@ typedef enum { GST_MPEGTS_SECTION_TOT, GST_MPEGTS_SECTION_ATSC_TVCT, GST_MPEGTS_SECTION_ATSC_CVCT, - GST_MPEGTS_SECTION_ATSC_MGT + GST_MPEGTS_SECTION_ATSC_MGT, + GST_MPEGTS_SECTION_ATSC_ETT } GstMpegTsSectionType; /** -- 2.7.4