mpegts: atsc: add atsc's EIT table parsing

ATSC has its own version of the EIT table (DVB also has one).

This patch adds parsing for the ATSC EIT table and also fixed
the section identification to mark it as the ATSC one.

The implementation aws refactored to reuse some common internal
structures from ETT.

Also adds its dumping function to ts-parser example

https://bugzilla.gnome.org/show_bug.cgi?id=730435
This commit is contained in:
Thiago Santos 2014-05-28 15:59:23 -03:00 committed by Edward Hervey
parent 89fadbcfe4
commit 1cab60b713
5 changed files with 352 additions and 84 deletions

View file

@ -397,6 +397,8 @@ gst_mpegts_section_get_atsc_mgt (GstMpegTsSection * section)
return (const GstMpegTsAtscMGT *) section->cached_parsed; return (const GstMpegTsAtscMGT *) section->cached_parsed;
} }
/* Multi string structure */
static GstMpegTsAtscStringSegment * static GstMpegTsAtscStringSegment *
_gst_mpegts_atsc_string_segment_copy (GstMpegTsAtscStringSegment * seg) _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, (GBoxedCopyFunc) _gst_mpegts_atsc_mult_string_copy,
(GFreeFunc) _gst_mpegts_atsc_mult_string_free); (GFreeFunc) _gst_mpegts_atsc_mult_string_free);
static GstMpegTsAtscETT * static GPtrArray *
_gst_mpegts_atsc_ett_copy (GstMpegTsAtscETT * ett) _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; guint8 num_strings;
GPtrArray *res = NULL;
guint8 *end = data + datasize;
gint i;
ett = g_slice_new0 (GstMpegTsAtscETT); if (datasize > 0) {
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 */ /* 1 is the minimum entry size, so no need to check here */
num_strings = GST_READ_UINT8 (data); num_strings = GST_READ_UINT8 (data);
data += 1; data += 1;
res =
g_ptr_array_new_full (num_strings,
(GDestroyNotify) _gst_mpegts_atsc_mult_string_free);
for (i = 0; i < num_strings; i++) { for (i = 0; i < num_strings; i++) {
GstMpegTsAtscMultString *mstring; GstMpegTsAtscMultString *mstring;
guint8 num_segments; guint8 num_segments;
gint j; gint j;
mstring = g_slice_new0 (GstMpegTsAtscMultString); mstring = g_slice_new0 (GstMpegTsAtscMultString);
g_ptr_array_add (ett->messages, mstring); g_ptr_array_add (res, mstring);
mstring->segments = mstring->segments =
g_ptr_array_new_full (num_strings, g_ptr_array_new_full (num_strings,
(GDestroyNotify) _gst_mpegts_atsc_string_segment_free); (GDestroyNotify) _gst_mpegts_atsc_string_segment_free);
/* each entry needs at least 4 bytes (lang code and segments number */ /* each entry needs at least 4 bytes (lang code and segments number) */
if (end - data < 4 + 4) { if (end - data < 4) {
GST_WARNING ("PID %d invalid ETT entry length %d", GST_WARNING ("Data too short for multstring parsing %d",
section->pid, (gint) (end - 4 - data)); (gint) (end - data));
goto error; goto error;
} }
@ -575,10 +543,9 @@ _parse_ett (GstMpegTsSection * section)
seg = g_slice_new0 (GstMpegTsAtscStringSegment); seg = g_slice_new0 (GstMpegTsAtscStringSegment);
g_ptr_array_add (mstring->segments, seg); g_ptr_array_add (mstring->segments, seg);
/* each entry needs at least 4 bytes (lang code and segments number */ /* each entry needs at least 3 bytes */
if (end - data < 3 + 4) { if (end - data < 3) {
GST_WARNING ("PID %d invalid ETT entry length %d", GST_WARNING ("Data too short for multstring parsing %d", datasize);
section->pid, (gint) (end - 4 - data));
goto error; goto error;
} }
@ -589,9 +556,8 @@ _parse_ett (GstMpegTsSection * section)
seg->compressed_data_size = GST_READ_UINT8 (data); seg->compressed_data_size = GST_READ_UINT8 (data);
data += 1; data += 1;
if (end - data < seg->compressed_data_size + 4) { if (end - data < seg->compressed_data_size) {
GST_WARNING ("PID %d invalid ETT entry length %d", GST_WARNING ("Data too short for multstring parsing %d", datasize);
section->pid, (gint) (end - 4 - data));
goto error; 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) { if (data != end - 4) {
GST_WARNING ("PID %d invalid ETT parsed %d length %d", GST_WARNING ("PID %d invalid ETT parsed %d length %d",

View file

@ -169,15 +169,13 @@ GType gst_mpegts_atsc_mgt_table_get_type (void);
const GstMpegTsAtscMGT * gst_mpegts_section_get_atsc_mgt (GstMpegTsSection * section); 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_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_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 _GstMpegTsAtscStringSegment GstMpegTsAtscStringSegment;
typedef struct _GstMpegTsAtscMultString GstMpegTsAtscMultString; typedef struct _GstMpegTsAtscMultString GstMpegTsAtscMultString;
typedef struct _GstMpegTsAtscETT GstMpegTsAtscETT;
struct _GstMpegTsAtscStringSegment { struct _GstMpegTsAtscStringSegment {
guint8 compression_type; guint8 compression_type;
@ -195,9 +193,56 @@ struct _GstMpegTsAtscMultString {
GPtrArray *segments; 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: * GstMpegTsAtscETT:
* @events: (element-type FIXME): List of texts * @messages: (element-type FIXME): List of texts
* *
* Extended Text Table (ATSC) * Extended Text Table (ATSC)
* *
@ -210,8 +255,6 @@ struct _GstMpegTsAtscETT
GPtrArray *messages; 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); GType gst_mpegts_atsc_ett_get_type (void);
const GstMpegTsAtscETT *gst_mpegts_section_get_atsc_ett (GstMpegTsSection *section); const GstMpegTsAtscETT *gst_mpegts_section_get_atsc_ett (GstMpegTsSection *section);

View file

@ -1062,7 +1062,7 @@ _identify_section (guint16 pid, guint8 table_id)
break; break;
case GST_MTS_TABLE_ID_ATSC_EVENT_INFORMATION: case GST_MTS_TABLE_ID_ATSC_EVENT_INFORMATION:
/* FIXME check pids reported on the MGT to confirm expectations */ /* 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: case GST_MTS_TABLE_ID_ATSC_CHANNEL_OR_EVENT_EXTENDED_TEXT:
/* FIXME check pids reported on the MGT to confirm expectations */ /* FIXME check pids reported on the MGT to confirm expectations */
return GST_MPEGTS_SECTION_ATSC_ETT; return GST_MPEGTS_SECTION_ATSC_ETT;

View file

@ -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_CVCT: ATSC Cable Virtual Channel Table (A65)
* @GST_MPEGTS_SECTION_ATSC_MGT: ATSC Master Guide 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_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) * @GST_MPEGTS_SECTION_ATSC_STT: ATSC System Time Table (A65)
* *
* Types of #GstMpegTsSection that the library handles. * Types of #GstMpegTsSection that the library handles.
@ -75,6 +76,7 @@ typedef enum {
GST_MPEGTS_SECTION_ATSC_CVCT, GST_MPEGTS_SECTION_ATSC_CVCT,
GST_MPEGTS_SECTION_ATSC_MGT, GST_MPEGTS_SECTION_ATSC_MGT,
GST_MPEGTS_SECTION_ATSC_ETT, GST_MPEGTS_SECTION_ATSC_ETT,
GST_MPEGTS_SECTION_ATSC_EIT,
GST_MPEGTS_SECTION_ATSC_STT GST_MPEGTS_SECTION_ATSC_STT
} GstMpegTsSectionType; } GstMpegTsSectionType;

View file

@ -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 static void
dump_ett (GstMpegTsSection * section) dump_ett (GstMpegTsSection * section)
{ {
const GstMpegTsAtscETT *ett = gst_mpegts_section_get_atsc_ett (section); const GstMpegTsAtscETT *ett = gst_mpegts_section_get_atsc_ett (section);
guint i, len; guint len;
g_assert (ett); g_assert (ett);
@ -617,26 +672,7 @@ dump_ett (GstMpegTsSection * section)
len = ett->messages->len; len = ett->messages->len;
g_printf (" %d Messages(s):\n", len); g_printf (" %d Messages(s):\n", len);
for (i = 0; i < len; i++) { dump_atsc_mult_string (ett->messages, 9);
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));
}
}
} }
static void static void
@ -860,6 +896,9 @@ dump_section (GstMpegTsSection * section)
case GST_MPEGTS_SECTION_ATSC_TVCT: case GST_MPEGTS_SECTION_ATSC_TVCT:
dump_vct (section); dump_vct (section);
break; break;
case GST_MPEGTS_SECTION_ATSC_EIT:
dump_atsc_eit (section);
break;
case GST_MPEGTS_SECTION_ATSC_ETT: case GST_MPEGTS_SECTION_ATSC_ETT:
dump_ett (section); dump_ett (section);
break; break;