diff --git a/gst-libs/gst/mpegts/gst-atsc-section.c b/gst-libs/gst/mpegts/gst-atsc-section.c index 5d02dbdb7f..95de50f4f3 100644 --- a/gst-libs/gst/mpegts/gst-atsc-section.c +++ b/gst-libs/gst/mpegts/gst-atsc-section.c @@ -397,6 +397,8 @@ gst_mpegts_section_get_atsc_mgt (GstMpegTsSection * section) return (const GstMpegTsAtscMGT *) section->cached_parsed; } +/* Multi string structure */ + static GstMpegTsAtscStringSegment * _gst_mpegts_atsc_string_segment_copy (GstMpegTsAtscStringSegment * seg) { @@ -491,72 +493,38 @@ 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) +static GPtrArray * +_parse_atsc_mult_string (guint8 * data, guint datasize) { - 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; + GPtrArray *res = NULL; + guint8 *end = data + datasize; + gint i; - 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) { + if (datasize > 0) { /* 1 is the minimum entry size, so no need to check here */ num_strings = GST_READ_UINT8 (data); data += 1; + res = + g_ptr_array_new_full (num_strings, + (GDestroyNotify) _gst_mpegts_atsc_mult_string_free); + 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); + g_ptr_array_add (res, 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)); + /* each entry needs at least 4 bytes (lang code and segments number) */ + if (end - data < 4) { + GST_WARNING ("Data too short for multstring parsing %d", + (gint) (end - data)); goto error; } @@ -575,10 +543,9 @@ _parse_ett (GstMpegTsSection * section) 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)); + /* each entry needs at least 3 bytes */ + if (end - data < 3) { + GST_WARNING ("Data too short for multstring parsing %d", datasize); goto error; } @@ -589,9 +556,8 @@ _parse_ett (GstMpegTsSection * section) 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)); + if (end - data < seg->compressed_data_size) { + GST_WARNING ("Data too short for multstring parsing %d", datasize); goto error; } @@ -602,6 +568,224 @@ _parse_ett (GstMpegTsSection * section) } } + return res; + +error: + if (res) + g_ptr_array_unref (res); + return NULL; +} + +/* EIT */ + +static GstMpegTsAtscEITEvent * +_gst_mpegts_atsc_eit_event_copy (GstMpegTsAtscEITEvent * event) +{ + GstMpegTsAtscEITEvent *copy; + + copy = g_slice_dup (GstMpegTsAtscEITEvent, event); + copy->titles = g_ptr_array_ref (event->titles); + copy->descriptors = g_ptr_array_ref (event->descriptors); + + return copy; +} + +static void +_gst_mpegts_atsc_eit_event_free (GstMpegTsAtscEITEvent * event) +{ + if (event->titles) + g_ptr_array_unref (event->titles); + if (event->descriptors) + g_ptr_array_unref (event->descriptors); + g_slice_free (GstMpegTsAtscEITEvent, event); +} + +G_DEFINE_BOXED_TYPE (GstMpegTsAtscEITEvent, gst_mpegts_atsc_eit_event, + (GBoxedCopyFunc) _gst_mpegts_atsc_eit_event_copy, + (GFreeFunc) _gst_mpegts_atsc_eit_event_free); + +static GstMpegTsAtscEIT * +_gst_mpegts_atsc_eit_copy (GstMpegTsAtscEIT * eit) +{ + GstMpegTsAtscEIT *copy; + + copy = g_slice_dup (GstMpegTsAtscEIT, eit); + copy->events = g_ptr_array_ref (eit->events); + + return copy; +} + +static void +_gst_mpegts_atsc_eit_free (GstMpegTsAtscEIT * eit) +{ + if (eit->events) + g_ptr_array_unref (eit->events); + g_slice_free (GstMpegTsAtscEIT, eit); +} + +G_DEFINE_BOXED_TYPE (GstMpegTsAtscEIT, gst_mpegts_atsc_eit, + (GBoxedCopyFunc) _gst_mpegts_atsc_eit_copy, + (GFreeFunc) _gst_mpegts_atsc_eit_free); + +static gpointer +_parse_atsc_eit (GstMpegTsSection * section) +{ + GstMpegTsAtscEIT *eit = NULL; + guint i = 0; + guint8 *data, *end; + guint8 num_events; + + eit = g_slice_new0 (GstMpegTsAtscEIT); + + data = section->data; + end = data + section->section_length; + + eit->source_id = section->subtable_extension; + + /* Skip already parsed data */ + data += 8; + + eit->protocol_version = GST_READ_UINT8 (data); + data += 1; + num_events = GST_READ_UINT8 (data); + data += 1; + + eit->events = g_ptr_array_new_with_free_func ((GDestroyNotify) + _gst_mpegts_atsc_eit_event_free); + + for (i = 0; i < num_events; i++) { + GstMpegTsAtscEITEvent *event; + guint32 tmp; + guint8 text_length; + guint16 descriptors_loop_length; + + if (end - data < 12) { + GST_WARNING ("PID %d invalid EIT entry length %d with %u events", + section->pid, (gint) (end - 4 - data), num_events); + goto error; + } + + event = g_slice_new0 (GstMpegTsAtscEITEvent); + g_ptr_array_add (eit->events, event); + + event->event_id = GST_READ_UINT16_BE (data) & 0x3FFF; + data += 2; + event->start_time = GST_READ_UINT32_BE (data); + data += 4; + + tmp = GST_READ_UINT32_BE (data); + data += 4; + event->etm_location = (tmp >> 28) & 0x3; + event->length_in_seconds = (tmp >> 8) & 0x0FFFFF; + text_length = tmp & 0xFF; + + if (text_length > end - data - 4 - 2) { + GST_WARNING ("PID %d invalid EIT entry length %d with %u events", + section->pid, (gint) (end - 4 - data), num_events); + goto error; + } + event->titles = _parse_atsc_mult_string (data, text_length); + data += text_length; + + descriptors_loop_length = GST_READ_UINT16_BE (data) & 0x0FFF; + data += 2; + + if (end - data - 4 < descriptors_loop_length) { + GST_WARNING ("PID %d invalid EIT entry length %d with %u events", + section->pid, (gint) (end - 4 - data), num_events); + goto error; + } + + event->descriptors = + gst_mpegts_parse_descriptors (data, descriptors_loop_length); + data += descriptors_loop_length; + } + + if (data != end - 4) { + GST_WARNING ("PID %d invalid EIT parsed %d length %d", + section->pid, (gint) (data - section->data), section->section_length); + goto error; + } + + return (gpointer) eit; + +error: + if (eit) + _gst_mpegts_atsc_eit_free (eit); + + return NULL; + +} + +/** + * gst_mpegts_section_get_atsc_eit: + * @section: a #GstMpegTsSection of type %GST_MPEGTS_SECTION_ATSC_EIT + * + * Returns the #GstMpegTsAtscEIT contained in the @section. + * + * Returns: The #GstMpegTsAtscEIT contained in the section, or %NULL if an error + * happened. + */ +const GstMpegTsAtscEIT * +gst_mpegts_section_get_atsc_eit (GstMpegTsSection * section) +{ + g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_ATSC_EIT, + NULL); + g_return_val_if_fail (section->cached_parsed || section->data, NULL); + + if (!section->cached_parsed) + section->cached_parsed = + __common_section_checks (section, 14, _parse_atsc_eit, + (GDestroyNotify) _gst_mpegts_atsc_eit_free); + + return (const GstMpegTsAtscEIT *) section->cached_parsed; +} + + +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) +{ + if (ett->messages) + 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; + guint8 *data, *end; + + 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 = _parse_atsc_mult_string (data, end - data - 4); + data += end - data - 4; if (data != end - 4) { GST_WARNING ("PID %d invalid ETT parsed %d length %d", diff --git a/gst-libs/gst/mpegts/gst-atsc-section.h b/gst-libs/gst/mpegts/gst-atsc-section.h index 185479b8e8..8d9db071ce 100644 --- a/gst-libs/gst/mpegts/gst-atsc-section.h +++ b/gst-libs/gst/mpegts/gst-atsc-section.h @@ -169,15 +169,13 @@ GType gst_mpegts_atsc_mgt_table_get_type (void); const GstMpegTsAtscMGT * gst_mpegts_section_get_atsc_mgt (GstMpegTsSection * section); -/* ETT */ +/* Multiple string structure (used in ETT and EIT */ #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; @@ -195,9 +193,56 @@ struct _GstMpegTsAtscMultString { GPtrArray *segments; }; +GType gst_mpegts_atsc_string_segment_get_type (void); +GType gst_mpegts_atsc_mult_string_get_type (void); + +/* EIT */ + +#define GST_TYPE_MPEGTS_ATSC_EIT_EVENT (gst_mpegts_atsc_eit_event_get_type()) +#define GST_TYPE_MPEGTS_ATSC_EIT (gst_mpegts_atsc_eit_get_type()) + +typedef struct _GstMpegTsAtscEITEvent GstMpegTsAtscEITEvent; +typedef struct _GstMpegTsAtscEIT GstMpegTsAtscEIT; + +struct _GstMpegTsAtscEITEvent { + guint16 event_id; + guint32 start_time; + guint8 etm_location; + guint32 length_in_seconds; + GPtrArray *titles; + + GPtrArray *descriptors; +}; + +/** + * GstMpegTsAtscEIT: + * @events: (element-type FIXME): Events + * + * Event Information Table (ATSC) + * + */ +struct _GstMpegTsAtscEIT +{ + guint16 source_id; + guint8 protocol_version; + + GPtrArray *events; +}; + +GType gst_mpegts_atsc_eit_event_get_type (void); +GType gst_mpegts_atsc_eit_get_type (void); + +const GstMpegTsAtscEIT *gst_mpegts_section_get_atsc_eit (GstMpegTsSection *section); + +/* ETT */ + +#define GST_TYPE_MPEGTS_ATSC_ETT (gst_mpegts_atsc_ett_get_type()) + +typedef struct _GstMpegTsAtscETT GstMpegTsAtscETT; + /** * GstMpegTsAtscETT: - * @events: (element-type FIXME): List of texts + * @messages: (element-type FIXME): List of texts * * Extended Text Table (ATSC) * @@ -210,8 +255,6 @@ struct _GstMpegTsAtscETT 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); diff --git a/gst-libs/gst/mpegts/gstmpegtssection.c b/gst-libs/gst/mpegts/gstmpegtssection.c index 71191b581c..5167a39b46 100644 --- a/gst-libs/gst/mpegts/gstmpegtssection.c +++ b/gst-libs/gst/mpegts/gstmpegtssection.c @@ -1062,7 +1062,7 @@ _identify_section (guint16 pid, guint8 table_id) break; case GST_MTS_TABLE_ID_ATSC_EVENT_INFORMATION: /* FIXME check pids reported on the MGT to confirm expectations */ - return GST_MPEGTS_SECTION_EIT; + return GST_MPEGTS_SECTION_ATSC_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; diff --git a/gst-libs/gst/mpegts/gstmpegtssection.h b/gst-libs/gst/mpegts/gstmpegtssection.h index 640b0cdbf3..291caacac7 100644 --- a/gst-libs/gst/mpegts/gstmpegtssection.h +++ b/gst-libs/gst/mpegts/gstmpegtssection.h @@ -55,6 +55,7 @@ GType gst_mpegts_section_get_type (void); * @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) + * @GST_MPEGTS_SECTION_ATSC_EIT: ATSC Event Information Table (A65) * @GST_MPEGTS_SECTION_ATSC_STT: ATSC System Time Table (A65) * * Types of #GstMpegTsSection that the library handles. @@ -75,6 +76,7 @@ typedef enum { GST_MPEGTS_SECTION_ATSC_CVCT, GST_MPEGTS_SECTION_ATSC_MGT, GST_MPEGTS_SECTION_ATSC_ETT, + GST_MPEGTS_SECTION_ATSC_EIT, GST_MPEGTS_SECTION_ATSC_STT } GstMpegTsSectionType; diff --git a/tests/examples/mpegts/ts-parser.c b/tests/examples/mpegts/ts-parser.c index 617e8207fc..173bcb4bac 100644 --- a/tests/examples/mpegts/ts-parser.c +++ b/tests/examples/mpegts/ts-parser.c @@ -603,11 +603,66 @@ dump_eit (GstMpegTsSection * section) } } +static void +dump_atsc_mult_string (GPtrArray * mstrings, guint spacing) +{ + guint i; + + for (i = 0; i < mstrings->len; i++) { + GstMpegTsAtscMultString *mstring = g_ptr_array_index (mstrings, i); + gint j, n; + + n = mstring->segments->len; + + g_printf ("%*s [multstring entry (%d) iso_639 langcode: %s]\n", spacing, "", + i, mstring->iso_639_langcode); + g_printf ("%*s segments:%d\n", spacing, "", n); + for (j = 0; j < n; j++) { + GstMpegTsAtscStringSegment *segment = + g_ptr_array_index (mstring->segments, j); + + g_printf ("%*s Compression:0x%x\n", spacing, "", + segment->compression_type); + g_printf ("%*s Mode:0x%x\n", spacing, "", segment->mode); + g_printf ("%*s Len:%u\n", spacing, "", segment->compressed_data_size); + g_printf ("%*s %s\n", spacing, "", + gst_mpegts_atsc_string_segment_get_string (segment)); + } + } +} + +static void +dump_atsc_eit (GstMpegTsSection * section) +{ + const GstMpegTsAtscEIT *eit = gst_mpegts_section_get_atsc_eit (section); + guint i, len; + + g_assert (eit); + + g_printf (" event_id : 0x%04x\n", eit->source_id); + g_printf (" protocol_version : %u\n", eit->protocol_version); + + len = eit->events->len; + g_printf (" %d Event(s):\n", len); + for (i = 0; i < len; i++) { + GstMpegTsAtscEITEvent *event = g_ptr_array_index (eit->events, i); + + g_printf (" %d)\n", i); + g_printf (" event_id: 0x%04x\n", event->event_id); + g_printf (" start_time: %u\n", event->start_time); + g_printf (" etm_location: 0x%x\n", event->etm_location); + g_printf (" length_in_seconds: %u\n", event->length_in_seconds); + g_printf (" Title(s):\n"); + dump_atsc_mult_string (event->titles, 9); + dump_descriptors (event->descriptors, 9); + } +} + static void dump_ett (GstMpegTsSection * section) { const GstMpegTsAtscETT *ett = gst_mpegts_section_get_atsc_ett (section); - guint i, len; + guint len; g_assert (ett); @@ -617,26 +672,7 @@ dump_ett (GstMpegTsSection * section) len = ett->messages->len; g_printf (" %d Messages(s):\n", len); - for (i = 0; i < len; i++) { - gint j, n; - GstMpegTsAtscMultString *mstring = g_ptr_array_index (ett->messages, i); - - n = mstring->segments->len; - g_printf (" iso_639_langcode:%s\n", mstring->iso_639_langcode); - g_printf (" segments:%d\n", n); - for (j = 0; j < n; j++) { - GstMpegTsAtscStringSegment *segment = - g_ptr_array_index (mstring->segments, j); - - g_printf (" Compression:0x%x\n", - segment->compression_type); - g_printf (" Mode:0x%x\n", segment->mode); - g_printf (" Len:%u\n", - segment->compressed_data_size); - g_printf (" %s\n", - gst_mpegts_atsc_string_segment_get_string (segment)); - } - } + dump_atsc_mult_string (ett->messages, 9); } static void @@ -860,6 +896,9 @@ dump_section (GstMpegTsSection * section) case GST_MPEGTS_SECTION_ATSC_TVCT: dump_vct (section); break; + case GST_MPEGTS_SECTION_ATSC_EIT: + dump_atsc_eit (section); + break; case GST_MPEGTS_SECTION_ATSC_ETT: dump_ett (section); break;