2007-08-20 14:23:45 +00:00
|
|
|
/*
|
|
|
|
* mpegtspacketizer.c -
|
|
|
|
* Copyright (C) 2007 Alessandro Decina
|
|
|
|
*
|
|
|
|
* Authors:
|
|
|
|
* Alessandro Decina <alessandro@nnva.org>
|
|
|
|
*
|
|
|
|
* 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., 59 Temple Place - Suite 330,
|
|
|
|
* Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "mpegtspacketizer.h"
|
2008-01-22 18:04:04 +00:00
|
|
|
#include "gstmpegdesc.h"
|
2007-08-20 14:23:45 +00:00
|
|
|
|
|
|
|
GST_DEBUG_CATEGORY_STATIC (mpegts_packetizer_debug);
|
|
|
|
#define GST_CAT_DEFAULT mpegts_packetizer_debug
|
|
|
|
|
|
|
|
G_DEFINE_TYPE (MpegTSPacketizer, mpegts_packetizer, G_TYPE_OBJECT);
|
|
|
|
|
|
|
|
static void mpegts_packetizer_dispose (GObject * object);
|
|
|
|
static void mpegts_packetizer_finalize (GObject * object);
|
|
|
|
|
|
|
|
#define CONTINUITY_UNSET 255
|
2007-10-01 09:21:19 +00:00
|
|
|
#define MAX_CONTINUITY 15
|
2007-12-14 17:51:49 +00:00
|
|
|
#define VERSION_NUMBER_NOTSET 255
|
2007-08-20 14:23:45 +00:00
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
2007-12-05 12:40:05 +00:00
|
|
|
guint8 table_id;
|
|
|
|
/* the spec says sub_table_extension is the fourth and fifth byte of a
|
|
|
|
* section when the section_syntax_indicator is set to a value of "1". If
|
|
|
|
* section_syntax_indicator is 0, sub_table_extension will be set to 0 */
|
2007-12-14 17:51:49 +00:00
|
|
|
guint16 subtable_extension;
|
|
|
|
guint8 version_number;
|
|
|
|
} MpegTSPacketizerStreamSubtable;
|
|
|
|
|
|
|
|
typedef struct
|
|
|
|
{
|
2007-08-20 14:23:45 +00:00
|
|
|
guint continuity_counter;
|
|
|
|
GstAdapter *section_adapter;
|
|
|
|
guint section_length;
|
2007-12-14 17:51:49 +00:00
|
|
|
GSList *subtables;
|
2007-08-20 14:23:45 +00:00
|
|
|
} MpegTSPacketizerStream;
|
|
|
|
|
2007-12-14 17:51:49 +00:00
|
|
|
static gint
|
|
|
|
mpegts_packetizer_stream_subtable_compare (gconstpointer a, gconstpointer b)
|
|
|
|
{
|
|
|
|
MpegTSPacketizerStreamSubtable *asub, *bsub;
|
|
|
|
|
|
|
|
asub = (MpegTSPacketizerStreamSubtable *) a;
|
|
|
|
bsub = (MpegTSPacketizerStreamSubtable *) b;
|
|
|
|
|
|
|
|
if (asub->table_id == bsub->table_id &&
|
|
|
|
asub->subtable_extension == bsub->subtable_extension)
|
|
|
|
return 0;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static MpegTSPacketizerStreamSubtable *
|
|
|
|
mpegts_packetizer_stream_subtable_new (guint8 table_id,
|
|
|
|
guint16 subtable_extension)
|
|
|
|
{
|
|
|
|
MpegTSPacketizerStreamSubtable *subtable;
|
|
|
|
|
|
|
|
subtable = g_new0 (MpegTSPacketizerStreamSubtable, 1);
|
|
|
|
subtable->version_number = VERSION_NUMBER_NOTSET;
|
|
|
|
subtable->table_id = table_id;
|
|
|
|
subtable->subtable_extension = subtable_extension;
|
|
|
|
return subtable;
|
|
|
|
}
|
|
|
|
|
2007-08-20 14:23:45 +00:00
|
|
|
static MpegTSPacketizerStream *
|
2007-12-14 17:51:49 +00:00
|
|
|
mpegts_packetizer_stream_new ()
|
2007-08-20 14:23:45 +00:00
|
|
|
{
|
|
|
|
MpegTSPacketizerStream *stream;
|
|
|
|
|
|
|
|
stream = (MpegTSPacketizerStream *) g_new0 (MpegTSPacketizerStream, 1);
|
|
|
|
stream->section_adapter = gst_adapter_new ();
|
|
|
|
stream->continuity_counter = CONTINUITY_UNSET;
|
2007-12-14 17:51:49 +00:00
|
|
|
stream->subtables = NULL;
|
2007-08-20 14:23:45 +00:00
|
|
|
return stream;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mpegts_packetizer_stream_free (MpegTSPacketizerStream * stream)
|
|
|
|
{
|
|
|
|
gst_adapter_clear (stream->section_adapter);
|
|
|
|
g_object_unref (stream->section_adapter);
|
2007-12-14 17:51:49 +00:00
|
|
|
g_slist_foreach (stream->subtables, (GFunc) g_free, NULL);
|
|
|
|
g_slist_free (stream->subtables);
|
2007-08-20 14:23:45 +00:00
|
|
|
g_free (stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mpegts_packetizer_clear_section (MpegTSPacketizer * packetizer,
|
|
|
|
MpegTSPacketizerStream * stream)
|
|
|
|
{
|
|
|
|
gst_adapter_clear (stream->section_adapter);
|
|
|
|
stream->continuity_counter = CONTINUITY_UNSET;
|
|
|
|
stream->section_length = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mpegts_packetizer_class_init (MpegTSPacketizerClass * klass)
|
|
|
|
{
|
|
|
|
GObjectClass *gobject_class;
|
|
|
|
|
|
|
|
gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
|
|
|
|
gobject_class->dispose = mpegts_packetizer_dispose;
|
|
|
|
gobject_class->finalize = mpegts_packetizer_finalize;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mpegts_packetizer_init (MpegTSPacketizer * packetizer)
|
|
|
|
{
|
|
|
|
packetizer->adapter = gst_adapter_new ();
|
2007-12-14 17:51:49 +00:00
|
|
|
packetizer->streams = g_hash_table_new (g_direct_hash, g_direct_equal);
|
2007-08-20 14:23:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mpegts_packetizer_dispose (GObject * object)
|
|
|
|
{
|
|
|
|
MpegTSPacketizer *packetizer = GST_MPEGTS_PACKETIZER (object);
|
|
|
|
|
|
|
|
if (!packetizer->disposed) {
|
|
|
|
gst_adapter_clear (packetizer->adapter);
|
|
|
|
g_object_unref (packetizer->adapter);
|
|
|
|
packetizer->disposed = TRUE;
|
|
|
|
}
|
|
|
|
|
2007-10-16 16:51:23 +00:00
|
|
|
if (G_OBJECT_CLASS (mpegts_packetizer_parent_class)->dispose)
|
|
|
|
G_OBJECT_CLASS (mpegts_packetizer_parent_class)->dispose (object);
|
2007-08-20 14:23:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
stream_foreach_remove (gpointer key, gpointer value, gpointer data)
|
|
|
|
{
|
|
|
|
MpegTSPacketizerStream *stream;
|
|
|
|
|
|
|
|
stream = (MpegTSPacketizerStream *) value;
|
|
|
|
mpegts_packetizer_stream_free (stream);
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mpegts_packetizer_finalize (GObject * object)
|
|
|
|
{
|
|
|
|
MpegTSPacketizer *packetizer = GST_MPEGTS_PACKETIZER (object);
|
|
|
|
|
|
|
|
g_hash_table_foreach_remove (packetizer->streams,
|
|
|
|
stream_foreach_remove, packetizer);
|
|
|
|
g_hash_table_destroy (packetizer->streams);
|
|
|
|
|
2007-10-16 16:51:23 +00:00
|
|
|
if (G_OBJECT_CLASS (mpegts_packetizer_parent_class)->finalize)
|
|
|
|
G_OBJECT_CLASS (mpegts_packetizer_parent_class)->finalize (object);
|
2007-08-20 14:23:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
mpegts_packetizer_parse_adaptation_field_control (MpegTSPacketizer * packetizer,
|
|
|
|
MpegTSPacketizerPacket * packet)
|
|
|
|
{
|
|
|
|
guint8 length;
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
length = *packet->data;
|
|
|
|
packet->data += 1;
|
2007-08-20 14:23:45 +00:00
|
|
|
|
|
|
|
if (packet->adaptation_field_control == 0x02) {
|
|
|
|
/* no payload, adaptation field of 183 bytes */
|
|
|
|
if (length != 183) {
|
2007-10-16 16:51:23 +00:00
|
|
|
GST_DEBUG ("PID %d afc == 0x%x and length %d != 183",
|
2007-08-20 14:23:45 +00:00
|
|
|
packet->pid, packet->adaptation_field_control, length);
|
|
|
|
}
|
|
|
|
} else if (length > 182) {
|
2007-10-16 16:51:23 +00:00
|
|
|
GST_DEBUG ("PID %d afc == 0x%01x and length %d > 182",
|
2007-08-20 14:23:45 +00:00
|
|
|
packet->pid, packet->adaptation_field_control, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* skip the adaptation field body for now */
|
|
|
|
if (packet->data + length > packet->data_end) {
|
2007-11-23 17:53:37 +00:00
|
|
|
GST_DEBUG ("PID %d afc length %d overflows the buffer current %d max %d",
|
|
|
|
packet->pid, length, packet->data - packet->data_start,
|
|
|
|
packet->data_end - packet->data_start);
|
2007-08-20 14:23:45 +00:00
|
|
|
return FALSE;
|
|
|
|
}
|
2007-10-16 16:51:23 +00:00
|
|
|
|
2007-10-01 09:21:19 +00:00
|
|
|
packet->data += length;
|
2007-08-20 14:23:45 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
mpegts_packetizer_parse_packet (MpegTSPacketizer * packetizer,
|
|
|
|
MpegTSPacketizerPacket * packet)
|
|
|
|
{
|
|
|
|
guint8 *data;
|
|
|
|
|
|
|
|
data = GST_BUFFER_DATA (packet->buffer);
|
|
|
|
/* skip sync_byte */
|
|
|
|
data++;
|
|
|
|
|
|
|
|
packet->payload_unit_start_indicator = (*data >> 6) & 0x01;
|
|
|
|
packet->pid = GST_READ_UINT16_BE (data) & 0x1FFF;
|
|
|
|
data += 2;
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
packet->adaptation_field_control = (*data >> 4) & 0x03;
|
2007-08-20 14:23:45 +00:00
|
|
|
packet->continuity_counter = *data & 0x0F;
|
|
|
|
data += 1;
|
|
|
|
|
|
|
|
packet->data = data;
|
|
|
|
|
|
|
|
if (packet->adaptation_field_control & 0x02)
|
|
|
|
if (!mpegts_packetizer_parse_adaptation_field_control (packetizer, packet))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
if (packet->adaptation_field_control & 0x01)
|
|
|
|
packet->payload = packet->data;
|
|
|
|
else
|
|
|
|
packet->payload = NULL;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
mpegts_packetizer_parse_section_header (MpegTSPacketizer * packetizer,
|
|
|
|
MpegTSPacketizerStream * stream, MpegTSPacketizerSection * section)
|
|
|
|
{
|
|
|
|
guint8 tmp;
|
|
|
|
guint8 *data;
|
2007-12-14 17:51:49 +00:00
|
|
|
MpegTSPacketizerStreamSubtable *subtable;
|
|
|
|
GSList *subtable_list = NULL;
|
2007-08-20 14:23:45 +00:00
|
|
|
|
|
|
|
section->complete = TRUE;
|
|
|
|
/* get the section buffer, pass the ownership to the caller */
|
|
|
|
section->buffer = gst_adapter_take_buffer (stream->section_adapter,
|
|
|
|
3 + stream->section_length);
|
|
|
|
data = GST_BUFFER_DATA (section->buffer);
|
|
|
|
|
|
|
|
section->table_id = *data++;
|
2007-12-14 17:51:49 +00:00
|
|
|
if ((data[0] & 0x80) == 0)
|
|
|
|
section->subtable_extension = 0;
|
|
|
|
else
|
|
|
|
section->subtable_extension = GST_READ_UINT16_BE (data + 2);
|
|
|
|
|
|
|
|
subtable = mpegts_packetizer_stream_subtable_new (section->table_id,
|
|
|
|
section->subtable_extension);
|
|
|
|
|
|
|
|
subtable_list = g_slist_find_custom (stream->subtables, subtable,
|
|
|
|
mpegts_packetizer_stream_subtable_compare);
|
|
|
|
if (subtable_list) {
|
|
|
|
g_free (subtable);
|
|
|
|
subtable = (MpegTSPacketizerStreamSubtable *) (subtable_list->data);
|
|
|
|
} else {
|
|
|
|
stream->subtables = g_slist_prepend (stream->subtables, subtable);
|
|
|
|
}
|
|
|
|
|
2007-08-20 14:23:45 +00:00
|
|
|
section->section_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
/* skip to the version byte */
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
tmp = *data++;
|
|
|
|
section->version_number = (tmp >> 1) & 0x1F;
|
|
|
|
section->current_next_indicator = tmp & 0x01;
|
|
|
|
if (!section->current_next_indicator)
|
|
|
|
goto not_applicable;
|
|
|
|
|
2007-12-14 17:51:49 +00:00
|
|
|
if (section->version_number == subtable->version_number)
|
2007-08-20 14:23:45 +00:00
|
|
|
goto not_applicable;
|
2007-12-14 17:51:49 +00:00
|
|
|
subtable->version_number = section->version_number;
|
2007-08-20 14:23:45 +00:00
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
not_applicable:
|
2007-12-14 17:51:49 +00:00
|
|
|
GST_LOG
|
|
|
|
("not applicable pid %d table_id %d subtable_extension %d, current_next %d version %d",
|
|
|
|
section->pid, section->table_id, section->subtable_extension,
|
|
|
|
section->current_next_indicator, section->version_number);
|
2007-08-20 14:23:45 +00:00
|
|
|
section->complete = FALSE;
|
|
|
|
gst_buffer_unref (section->buffer);
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
static gboolean
|
|
|
|
mpegts_packetizer_parse_descriptors (MpegTSPacketizer * packetizer,
|
|
|
|
guint8 ** buffer, guint8 * buffer_end, GValueArray * descriptors)
|
|
|
|
{
|
|
|
|
guint8 tag, length;
|
|
|
|
guint8 *data;
|
|
|
|
GValue value = { 0 };
|
|
|
|
GString *desc;
|
|
|
|
|
|
|
|
data = *buffer;
|
|
|
|
|
|
|
|
while (data < buffer_end) {
|
|
|
|
tag = *data++;
|
|
|
|
length = *data++;
|
|
|
|
|
|
|
|
if (data + length > buffer_end) {
|
|
|
|
GST_WARNING ("invalid descriptor length %d now at %d max %d",
|
|
|
|
length, data - *buffer, buffer_end - *buffer);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* include tag and length */
|
|
|
|
desc = g_string_new_len ((gchar *) data - 2, length + 2);
|
|
|
|
data += length;
|
2008-01-22 18:04:04 +00:00
|
|
|
/* G_TYPE_GSTING is a GBoxed type and is used so properly marshalled from python */
|
2007-11-23 17:53:37 +00:00
|
|
|
g_value_init (&value, G_TYPE_GSTRING);
|
|
|
|
g_value_take_boxed (&value, desc);
|
|
|
|
g_value_array_append (descriptors, &value);
|
|
|
|
g_value_unset (&value);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data != buffer_end) {
|
|
|
|
GST_WARNING ("descriptors size %d expected %d",
|
|
|
|
data - *buffer, buffer_end - *buffer);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
*buffer = data;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
error:
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
GstStructure *
|
2007-08-20 14:23:45 +00:00
|
|
|
mpegts_packetizer_parse_pat (MpegTSPacketizer * packetizer,
|
|
|
|
MpegTSPacketizerSection * section)
|
|
|
|
{
|
2007-11-23 17:53:37 +00:00
|
|
|
GstStructure *pat_info = NULL;
|
2007-08-20 14:23:45 +00:00
|
|
|
guint8 *data, *end;
|
2007-11-23 17:53:37 +00:00
|
|
|
guint transport_stream_id;
|
2007-08-20 14:23:45 +00:00
|
|
|
guint8 tmp;
|
|
|
|
guint program_number;
|
2007-11-23 17:53:37 +00:00
|
|
|
guint pmt_pid;
|
|
|
|
GValue entries = { 0 };
|
2007-08-20 14:23:45 +00:00
|
|
|
GValue value = { 0 };
|
2007-11-23 17:53:37 +00:00
|
|
|
GstStructure *entry = NULL;
|
|
|
|
gchar *struct_name;
|
2007-08-20 14:23:45 +00:00
|
|
|
|
|
|
|
data = GST_BUFFER_DATA (section->buffer);
|
|
|
|
|
|
|
|
section->table_id = *data++;
|
|
|
|
section->section_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
transport_stream_id = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
tmp = *data++;
|
|
|
|
section->version_number = (tmp >> 1) & 0x1F;
|
|
|
|
section->current_next_indicator = tmp & 0x01;
|
|
|
|
|
|
|
|
/* skip section_number and last_section_number */
|
|
|
|
data += 2;
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
pat_info = gst_structure_new ("pat",
|
|
|
|
"transport-stream-id", G_TYPE_UINT, transport_stream_id, NULL);
|
|
|
|
g_value_init (&entries, GST_TYPE_LIST);
|
2007-08-20 14:23:45 +00:00
|
|
|
/* stop at the CRC */
|
|
|
|
end = GST_BUFFER_DATA (section->buffer) + GST_BUFFER_SIZE (section->buffer);
|
|
|
|
while (data < end - 4) {
|
|
|
|
program_number = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
pmt_pid = GST_READ_UINT16_BE (data) & 0x1FFF;
|
|
|
|
data += 2;
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
struct_name = g_strdup_printf ("program-%d", program_number);
|
|
|
|
entry = gst_structure_new (struct_name,
|
|
|
|
"program-number", G_TYPE_UINT, program_number,
|
|
|
|
"pid", G_TYPE_UINT, pmt_pid, NULL);
|
|
|
|
g_free (struct_name);
|
2007-08-20 14:23:45 +00:00
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
g_value_init (&value, GST_TYPE_STRUCTURE);
|
|
|
|
g_value_take_boxed (&value, entry);
|
|
|
|
gst_value_list_append_value (&entries, &value);
|
2007-08-20 14:23:45 +00:00
|
|
|
g_value_unset (&value);
|
|
|
|
}
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
gst_structure_set_value (pat_info, "programs", &entries);
|
|
|
|
g_value_unset (&entries);
|
|
|
|
|
2007-10-16 16:51:23 +00:00
|
|
|
if (data != end - 4) {
|
|
|
|
/* FIXME: check the CRC before parsing the packet */
|
|
|
|
GST_ERROR ("at the end of PAT data != end - 4");
|
2007-11-23 17:53:37 +00:00
|
|
|
gst_structure_free (pat_info);
|
2007-10-16 16:51:23 +00:00
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-08-20 14:23:45 +00:00
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
return pat_info;
|
2007-08-20 14:23:45 +00:00
|
|
|
}
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
GstStructure *
|
2007-08-20 14:23:45 +00:00
|
|
|
mpegts_packetizer_parse_pmt (MpegTSPacketizer * packetizer,
|
|
|
|
MpegTSPacketizerSection * section)
|
|
|
|
{
|
2007-11-23 17:53:37 +00:00
|
|
|
GstStructure *pmt = NULL;
|
2007-08-20 14:23:45 +00:00
|
|
|
guint8 *data, *end;
|
|
|
|
guint16 program_number;
|
|
|
|
guint8 tmp;
|
2007-11-23 17:53:37 +00:00
|
|
|
guint pcr_pid;
|
2007-08-20 14:23:45 +00:00
|
|
|
guint program_info_length;
|
|
|
|
guint8 stream_type;
|
|
|
|
guint16 pid;
|
|
|
|
guint stream_info_length;
|
2007-11-23 17:53:37 +00:00
|
|
|
GValueArray *descriptors;
|
|
|
|
GValue stream_value = { 0 };
|
|
|
|
GValue programs = { 0 };
|
|
|
|
GstStructure *stream_info = NULL;
|
|
|
|
gchar *struct_name;
|
2007-08-20 14:23:45 +00:00
|
|
|
|
|
|
|
/* fixed header + CRC == 16 */
|
|
|
|
if (GST_BUFFER_SIZE (section->buffer) < 16) {
|
|
|
|
GST_WARNING ("PID %d invalid PMT size %d",
|
|
|
|
section->pid, section->section_length);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = GST_BUFFER_DATA (section->buffer);
|
|
|
|
end = data + GST_BUFFER_SIZE (section->buffer);
|
|
|
|
|
|
|
|
section->table_id = *data++;
|
|
|
|
section->section_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
program_number = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
tmp = *data++;
|
|
|
|
section->version_number = (tmp >> 1) & 0x1F;
|
|
|
|
section->current_next_indicator = tmp & 0x01;
|
|
|
|
|
|
|
|
/* skip section_number and last_section_number */
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
pcr_pid = GST_READ_UINT16_BE (data) & 0x1FFF;
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
program_info_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
data += 2;
|
|
|
|
|
gst/mpegtsparse/: Remove signals for pat, pmt, nit, eit, sdt. Replace with bus messages.
Original commit message from CVS:
* gst/mpegtsparse/Makefile.am:
* gst/mpegtsparse/mpegtspacketizer.c:
* gst/mpegtsparse/mpegtsparse.c:
Remove signals for pat, pmt, nit, eit, sdt. Replace with bus
messages.
* sys/dvb/dvbbasebin.c:
Instead of attaching to signals, use the bus messages.
Also fix up so the dvbsrc starts only outputting the info tables
like PAT, CAT, NIT, SDT, EIT instead of the whole ts.
2007-12-03 18:28:32 +00:00
|
|
|
struct_name = g_strdup ("pmt");
|
2007-11-23 17:53:37 +00:00
|
|
|
pmt = gst_structure_new (struct_name,
|
|
|
|
"program-number", G_TYPE_UINT, program_number,
|
|
|
|
"pcr-pid", G_TYPE_UINT, pcr_pid,
|
|
|
|
"version-number", G_TYPE_UINT, section->version_number, NULL);
|
|
|
|
g_free (struct_name);
|
|
|
|
|
|
|
|
if (program_info_length) {
|
|
|
|
/* check that the buffer is large enough to contain at least
|
|
|
|
* program_info_length bytes + CRC */
|
|
|
|
if (data + program_info_length + 4 > end) {
|
|
|
|
GST_WARNING ("PID %d invalid program info length %d "
|
|
|
|
"left %d", section->pid, program_info_length, end - data);
|
|
|
|
goto error;
|
|
|
|
}
|
2007-08-20 14:23:45 +00:00
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
descriptors = g_value_array_new (0);
|
|
|
|
if (!mpegts_packetizer_parse_descriptors (packetizer,
|
|
|
|
&data, data + program_info_length, descriptors)) {
|
|
|
|
g_value_array_free (descriptors);
|
2007-08-20 14:23:45 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
gst_structure_set (pmt, "descriptors", G_TYPE_VALUE_ARRAY, descriptors,
|
|
|
|
NULL);
|
|
|
|
g_value_array_free (descriptors);
|
2007-08-20 14:23:45 +00:00
|
|
|
}
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
g_value_init (&programs, GST_TYPE_LIST);
|
2007-08-20 14:23:45 +00:00
|
|
|
/* parse entries, cycle until there's space for another entry (at least 5
|
|
|
|
* bytes) plus the CRC */
|
|
|
|
while (data <= end - 4 - 5) {
|
|
|
|
stream_type = *data++;
|
|
|
|
|
|
|
|
pid = GST_READ_UINT16_BE (data) & 0x1FFF;
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
stream_info_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
if (data + stream_info_length + 4 > end) {
|
|
|
|
GST_WARNING ("PID %d invalid stream info length %d "
|
|
|
|
"left %d", section->pid, stream_info_length, end - data);
|
2007-11-23 17:53:37 +00:00
|
|
|
g_value_unset (&programs);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct_name = g_strdup_printf ("pid-%d", pid);
|
|
|
|
stream_info = gst_structure_new (struct_name,
|
|
|
|
"pid", G_TYPE_UINT, pid, "stream-type", G_TYPE_UINT, stream_type, NULL);
|
|
|
|
g_free (struct_name);
|
|
|
|
|
|
|
|
if (stream_info_length) {
|
|
|
|
descriptors = g_value_array_new (0);
|
|
|
|
if (!mpegts_packetizer_parse_descriptors (packetizer,
|
|
|
|
&data, data + stream_info_length, descriptors)) {
|
|
|
|
g_value_unset (&programs);
|
|
|
|
gst_structure_free (stream_info);
|
|
|
|
g_value_array_free (descriptors);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_structure_set (stream_info,
|
|
|
|
"descriptors", G_TYPE_VALUE_ARRAY, descriptors, NULL);
|
|
|
|
g_value_array_free (descriptors);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_value_init (&stream_value, GST_TYPE_STRUCTURE);
|
|
|
|
g_value_take_boxed (&stream_value, stream_info);
|
|
|
|
gst_value_list_append_value (&programs, &stream_value);
|
|
|
|
g_value_unset (&stream_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_structure_set_value (pmt, "streams", &programs);
|
|
|
|
g_value_unset (&programs);
|
|
|
|
|
|
|
|
g_assert (data == end - 4);
|
|
|
|
|
|
|
|
return pmt;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (pmt)
|
|
|
|
gst_structure_free (pmt);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GstStructure *
|
|
|
|
mpegts_packetizer_parse_nit (MpegTSPacketizer * packetizer,
|
|
|
|
MpegTSPacketizerSection * section)
|
|
|
|
{
|
2008-01-24 00:22:17 +00:00
|
|
|
GstStructure *nit = NULL, *transport = NULL, *delivery_structure = NULL;
|
2007-11-23 17:53:37 +00:00
|
|
|
guint8 *data, *end, *entry_begin;
|
|
|
|
guint16 network_id, transport_stream_id, original_network_id;
|
|
|
|
guint tmp;
|
|
|
|
guint16 descriptors_loop_length, transport_stream_loop_length;
|
|
|
|
GValue transports = { 0 };
|
|
|
|
GValue transport_value = { 0 };
|
|
|
|
GValueArray *descriptors = NULL;
|
|
|
|
gchar *dbg_str;
|
|
|
|
|
2007-12-14 17:51:49 +00:00
|
|
|
GST_DEBUG ("NIT");
|
2007-11-23 17:53:37 +00:00
|
|
|
/* fixed header + CRC == 16 */
|
|
|
|
if (GST_BUFFER_SIZE (section->buffer) < 23) {
|
|
|
|
GST_WARNING ("PID %d invalid NIT size %d",
|
|
|
|
section->pid, section->section_length);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = GST_BUFFER_DATA (section->buffer);
|
|
|
|
end = data + GST_BUFFER_SIZE (section->buffer);
|
|
|
|
|
|
|
|
section->table_id = *data++;
|
|
|
|
section->section_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
if (data + section->section_length != end) {
|
|
|
|
GST_WARNING ("PID %d invalid NIT section length %d expected %d",
|
|
|
|
section->pid, section->section_length, end - data);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
network_id = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
tmp = *data++;
|
|
|
|
section->version_number = (tmp >> 1) & 0x1F;
|
|
|
|
section->current_next_indicator = tmp & 0x01;
|
|
|
|
|
|
|
|
/* skip section_number and last_section_number */
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
descriptors_loop_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
nit = gst_structure_new ("nit",
|
|
|
|
"network-id", G_TYPE_UINT, network_id,
|
|
|
|
"version-number", G_TYPE_UINT, section->version_number,
|
|
|
|
"current-next-indicator", G_TYPE_UINT, section->current_next_indicator,
|
2008-02-05 11:40:43 +00:00
|
|
|
"actual-network", G_TYPE_BOOLEAN, section->table_id == 0x40, NULL);
|
2007-11-23 17:53:37 +00:00
|
|
|
|
|
|
|
/* see if the buffer is large enough */
|
|
|
|
if (descriptors_loop_length) {
|
|
|
|
if (data + descriptors_loop_length > end - 4) {
|
|
|
|
GST_WARNING ("PID %d invalid NIT descriptors loop length %d",
|
|
|
|
section->pid, descriptors_loop_length);
|
|
|
|
gst_structure_free (nit);
|
|
|
|
goto error;
|
|
|
|
}
|
2008-01-22 20:27:28 +00:00
|
|
|
guint8 *networkname_descriptor;
|
|
|
|
GstMPEGDescriptor *mpegdescriptor =
|
|
|
|
gst_mpeg_descriptor_parse (data, descriptors_loop_length);
|
|
|
|
networkname_descriptor =
|
|
|
|
gst_mpeg_descriptor_find (mpegdescriptor, DESC_DVB_NETWORK_NAME);
|
|
|
|
if (networkname_descriptor != NULL) {
|
|
|
|
gchar *networkname_tmp;
|
2008-01-24 08:12:29 +00:00
|
|
|
|
|
|
|
/* No need to bounds check this value as it comes from the descriptor length itself */
|
|
|
|
guint8 networkname_length =
|
2008-01-22 20:27:28 +00:00
|
|
|
DESC_DVB_NETWORK_NAME_length (networkname_descriptor);
|
|
|
|
gchar *networkname =
|
|
|
|
(gchar *) DESC_DVB_NETWORK_NAME_text (networkname_descriptor);
|
2008-01-24 08:12:29 +00:00
|
|
|
if (networkname[0] < 0x20) {
|
|
|
|
networkname_length -= 1;
|
|
|
|
networkname += 1;
|
2008-01-22 20:27:28 +00:00
|
|
|
}
|
2008-01-24 08:12:29 +00:00
|
|
|
networkname_tmp = g_strndup (networkname, networkname_length);
|
|
|
|
gst_structure_set (nit, "network-name", G_TYPE_STRING, networkname_tmp,
|
|
|
|
NULL);
|
|
|
|
g_free (networkname_tmp);
|
2008-01-22 20:27:28 +00:00
|
|
|
}
|
|
|
|
gst_mpeg_descriptor_free (mpegdescriptor);
|
2007-11-23 17:53:37 +00:00
|
|
|
|
|
|
|
descriptors = g_value_array_new (0);
|
|
|
|
if (!mpegts_packetizer_parse_descriptors (packetizer,
|
|
|
|
&data, data + descriptors_loop_length, descriptors)) {
|
|
|
|
gst_structure_free (nit);
|
|
|
|
g_value_array_free (descriptors);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_structure_set (nit, "descriptors", G_TYPE_VALUE_ARRAY, descriptors,
|
|
|
|
NULL);
|
|
|
|
g_value_array_free (descriptors);
|
|
|
|
}
|
|
|
|
|
|
|
|
transport_stream_loop_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
g_value_init (&transports, GST_TYPE_LIST);
|
|
|
|
/* read up to the CRC */
|
|
|
|
while (transport_stream_loop_length - 4 > 0) {
|
|
|
|
gchar *transport_name;
|
|
|
|
|
|
|
|
entry_begin = data;
|
|
|
|
|
|
|
|
if (transport_stream_loop_length < 10) {
|
|
|
|
/* each entry must be at least 6 bytes (+ 4bytes CRC) */
|
|
|
|
GST_WARNING ("PID %d invalid NIT entry size %d",
|
|
|
|
section->pid, transport_stream_loop_length);
|
2007-08-20 14:23:45 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
transport_stream_id = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
original_network_id = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
2007-08-20 14:23:45 +00:00
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
descriptors_loop_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
data += 2;
|
2007-08-20 14:23:45 +00:00
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
transport_name = g_strdup_printf ("transport-%d", transport_stream_id);
|
|
|
|
transport = gst_structure_new (transport_name,
|
|
|
|
"transport-stream-id", G_TYPE_UINT, transport_stream_id,
|
|
|
|
"original-network-id", G_TYPE_UINT, original_network_id, NULL);
|
|
|
|
g_free (transport_name);
|
|
|
|
|
|
|
|
if (descriptors_loop_length) {
|
|
|
|
if (data + descriptors_loop_length > end - 4) {
|
|
|
|
GST_WARNING ("PID %d invalid NIT entry %d descriptors loop length %d",
|
|
|
|
section->pid, transport_stream_id, descriptors_loop_length);
|
|
|
|
gst_structure_free (transport);
|
|
|
|
goto error;
|
|
|
|
}
|
2008-01-23 09:49:27 +00:00
|
|
|
GstMPEGDescriptor *mpegdescriptor =
|
|
|
|
gst_mpeg_descriptor_parse (data, descriptors_loop_length);
|
2008-01-24 00:22:17 +00:00
|
|
|
guint8 *delivery;
|
2008-01-23 09:49:27 +00:00
|
|
|
|
2008-01-24 00:22:17 +00:00
|
|
|
if ((delivery =
|
2008-01-23 09:49:27 +00:00
|
|
|
gst_mpeg_descriptor_find (mpegdescriptor,
|
|
|
|
DESC_DVB_SATELLITE_DELIVERY_SYSTEM))) {
|
|
|
|
|
|
|
|
guint8 *frequency_bcd =
|
2008-01-24 00:22:17 +00:00
|
|
|
DESC_DVB_SATELLITE_DELIVERY_SYSTEM_frequency (delivery);
|
2008-01-23 09:49:27 +00:00
|
|
|
guint32 frequency =
|
|
|
|
10 * ((frequency_bcd[3] & 0x0F) +
|
|
|
|
10 * ((frequency_bcd[3] & 0xF0) >> 4) +
|
|
|
|
100 * (frequency_bcd[2] & 0x0F) +
|
|
|
|
1000 * ((frequency_bcd[2] & 0xF0) >> 4) +
|
|
|
|
10000 * (frequency_bcd[1] & 0x0F) +
|
|
|
|
100000 * ((frequency_bcd[1] & 0xF0) >> 4) +
|
|
|
|
1000000 * (frequency_bcd[0] & 0x0F) +
|
|
|
|
10000000 * ((frequency_bcd[0] & 0xF0) >> 4));
|
|
|
|
guint8 *orbital_bcd =
|
2008-01-24 00:22:17 +00:00
|
|
|
DESC_DVB_SATELLITE_DELIVERY_SYSTEM_orbital_position (delivery);
|
2008-01-23 09:49:27 +00:00
|
|
|
gfloat orbital =
|
|
|
|
(orbital_bcd[1] & 0x0F) / 10. + ((orbital_bcd[1] & 0xF0) >> 4) +
|
|
|
|
10 * (orbital_bcd[0] & 0x0F) + 100 * ((orbital_bcd[0] & 0xF0) >> 4);
|
|
|
|
gboolean east =
|
2008-01-24 00:22:17 +00:00
|
|
|
DESC_DVB_SATELLITE_DELIVERY_SYSTEM_west_east_flag (delivery);
|
2008-01-23 09:49:27 +00:00
|
|
|
guint8 polarization =
|
2008-01-24 00:22:17 +00:00
|
|
|
DESC_DVB_SATELLITE_DELIVERY_SYSTEM_polarization (delivery);
|
2008-01-23 09:49:27 +00:00
|
|
|
gchar *polarization_str;
|
|
|
|
guint8 modulation =
|
2008-01-24 00:22:17 +00:00
|
|
|
DESC_DVB_SATELLITE_DELIVERY_SYSTEM_modulation (delivery);
|
2008-01-23 09:49:27 +00:00
|
|
|
gchar *modulation_str;
|
|
|
|
guint8 *symbol_rate_bcd =
|
2008-01-24 00:22:17 +00:00
|
|
|
DESC_DVB_SATELLITE_DELIVERY_SYSTEM_symbol_rate (delivery);
|
2008-01-23 09:49:27 +00:00
|
|
|
guint32 symbol_rate =
|
|
|
|
(symbol_rate_bcd[2] & 0x0F) +
|
|
|
|
10 * ((symbol_rate_bcd[2] & 0xF0) >> 4) +
|
|
|
|
100 * (symbol_rate_bcd[1] & 0x0F) +
|
|
|
|
1000 * ((symbol_rate_bcd[1] & 0xF0) >> 4) +
|
|
|
|
10000 * (symbol_rate_bcd[0] & 0x0F) +
|
|
|
|
100000 * ((symbol_rate_bcd[0] & 0xF0) >> 4);
|
|
|
|
guint8 fec_inner =
|
2008-01-24 00:22:17 +00:00
|
|
|
DESC_DVB_SATELLITE_DELIVERY_SYSTEM_fec_inner (delivery);
|
2008-01-23 09:49:27 +00:00
|
|
|
gchar *fec_inner_str;
|
|
|
|
|
|
|
|
switch (polarization) {
|
|
|
|
case 0:
|
2008-01-24 00:22:17 +00:00
|
|
|
polarization_str = "horizontal";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 1:
|
2008-01-24 00:22:17 +00:00
|
|
|
polarization_str = "vertical";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
2008-01-24 00:22:17 +00:00
|
|
|
polarization_str = "left";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
2008-01-24 00:22:17 +00:00
|
|
|
polarization_str = "right";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
default:
|
2008-01-24 00:22:17 +00:00
|
|
|
polarization_str = "";
|
2008-01-23 09:49:27 +00:00
|
|
|
}
|
|
|
|
switch (fec_inner) {
|
|
|
|
case 0:
|
2008-01-24 00:22:17 +00:00
|
|
|
fec_inner_str = "undefined";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 1:
|
2008-01-24 00:22:17 +00:00
|
|
|
fec_inner_str = "1/2";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 2:
|
2008-01-24 00:22:17 +00:00
|
|
|
fec_inner_str = "2/3";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 3:
|
2008-01-24 00:22:17 +00:00
|
|
|
fec_inner_str = "3/4";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 4:
|
2008-01-24 00:22:17 +00:00
|
|
|
fec_inner_str = "5/6";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 5:
|
2008-01-24 00:22:17 +00:00
|
|
|
fec_inner_str = "7/8";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 6:
|
2008-01-24 00:22:17 +00:00
|
|
|
fec_inner_str = "8/9";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 0xF:
|
2008-01-24 00:22:17 +00:00
|
|
|
fec_inner_str = "none";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
default:
|
2008-01-24 00:22:17 +00:00
|
|
|
fec_inner_str = "reserved";
|
2008-01-23 09:49:27 +00:00
|
|
|
}
|
|
|
|
switch (modulation) {
|
|
|
|
case 0x00:
|
2008-01-24 00:22:17 +00:00
|
|
|
modulation_str = "undefined";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 0x01:
|
2008-01-24 00:22:17 +00:00
|
|
|
modulation_str = "QAM16";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 0x02:
|
2008-01-24 00:22:17 +00:00
|
|
|
modulation_str = "QAM32";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 0x03:
|
2008-01-24 00:22:17 +00:00
|
|
|
modulation_str = "QAM64";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 0x04:
|
2008-01-24 00:22:17 +00:00
|
|
|
modulation_str = "QAM128";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
case 0x05:
|
2008-01-24 00:22:17 +00:00
|
|
|
modulation_str = "QAM256";
|
2008-01-23 09:49:27 +00:00
|
|
|
break;
|
|
|
|
default:
|
2008-01-24 00:22:17 +00:00
|
|
|
modulation_str = "reserved";
|
2008-01-23 09:49:27 +00:00
|
|
|
}
|
2008-01-24 00:22:17 +00:00
|
|
|
delivery_structure = gst_structure_new ("satellite",
|
2008-01-23 09:49:27 +00:00
|
|
|
"orbital", G_TYPE_FLOAT, orbital,
|
|
|
|
"east-or-west", G_TYPE_STRING, east ? "east" : "west",
|
|
|
|
"modulation", G_TYPE_STRING, modulation_str,
|
|
|
|
"frequency", G_TYPE_UINT, frequency,
|
|
|
|
"polarization", G_TYPE_STRING, polarization_str,
|
|
|
|
"symbol-rate", G_TYPE_UINT, symbol_rate,
|
|
|
|
"inner-fec", G_TYPE_STRING, fec_inner_str, NULL);
|
2008-01-24 00:22:17 +00:00
|
|
|
gst_structure_set (transport, "delivery", GST_TYPE_STRUCTURE,
|
|
|
|
delivery_structure, NULL);
|
|
|
|
} else if ((delivery =
|
|
|
|
gst_mpeg_descriptor_find (mpegdescriptor,
|
|
|
|
DESC_DVB_TERRESTRIAL_DELIVERY_SYSTEM))) {
|
|
|
|
|
|
|
|
guint32 frequency =
|
|
|
|
DESC_DVB_TERRESTRIAL_DELIVERY_SYSTEM_frequency (delivery) * 10;
|
|
|
|
guint8 bandwidth =
|
|
|
|
DESC_DVB_TERRESTRIAL_DELIVERY_SYSTEM_bandwidth (delivery);
|
|
|
|
guint8 constellation =
|
|
|
|
DESC_DVB_TERRESTRIAL_DELIVERY_SYSTEM_constellation (delivery);
|
|
|
|
guint8 hierarchy =
|
|
|
|
DESC_DVB_TERRESTRIAL_DELIVERY_SYSTEM_hierarchy (delivery);
|
|
|
|
guint8 code_rate_hp =
|
|
|
|
DESC_DVB_TERRESTRIAL_DELIVERY_SYSTEM_code_rate_hp (delivery);
|
|
|
|
guint8 code_rate_lp =
|
|
|
|
DESC_DVB_TERRESTRIAL_DELIVERY_SYSTEM_code_rate_lp (delivery);
|
|
|
|
guint8 guard_interval =
|
|
|
|
DESC_DVB_TERRESTRIAL_DELIVERY_SYSTEM_guard_interval (delivery);
|
|
|
|
guint8 transmission_mode =
|
|
|
|
DESC_DVB_TERRESTRIAL_DELIVERY_SYSTEM_transmission_mode (delivery);
|
|
|
|
gboolean other_frequency =
|
|
|
|
DESC_DVB_TERRESTRIAL_DELIVERY_SYSTEM_other_frequency (delivery);
|
|
|
|
gchar *constellation_str, *code_rate_hp_str, *code_rate_lp_str,
|
|
|
|
*transmission_mode_str;
|
|
|
|
/* do the stuff */
|
|
|
|
/* bandwidth is 8 if 0, 7 if 1, 6 if 2, reserved otherwise */
|
|
|
|
if (bandwidth <= 2)
|
|
|
|
bandwidth = 8 - bandwidth;
|
|
|
|
else
|
|
|
|
bandwidth = 0;
|
|
|
|
switch (constellation) {
|
|
|
|
case 0:
|
|
|
|
constellation_str = "QPSK";
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
constellation_str = "QAM16";
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
constellation_str = "QAM64";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
constellation_str = "reserved";
|
|
|
|
}
|
|
|
|
/* hierarchy is 4 if 3, 2 if 2, 1 if 1, 0 if 0, reserved if > 3 */
|
|
|
|
if (hierarchy <= 3) {
|
|
|
|
if (hierarchy == 3)
|
|
|
|
hierarchy = 4;
|
|
|
|
} else {
|
|
|
|
hierarchy = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (code_rate_hp) {
|
|
|
|
case 0:
|
|
|
|
code_rate_hp_str = "1/2";
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
code_rate_hp_str = "2/3";
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
code_rate_hp_str = "3/4";
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
code_rate_hp_str = "5/6";
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
code_rate_hp_str = "7/8";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
code_rate_hp_str = "reserved";
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (code_rate_lp) {
|
|
|
|
case 0:
|
|
|
|
code_rate_lp_str = "1/2";
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
code_rate_lp_str = "2/3";
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
code_rate_lp_str = "3/4";
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
code_rate_lp_str = "5/6";
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
code_rate_lp_str = "7/8";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
code_rate_lp_str = "reserved";
|
|
|
|
}
|
|
|
|
/* guard is 32 if 0, 16 if 1, 8 if 2, 4 if 4 */
|
2008-01-25 09:50:07 +00:00
|
|
|
switch (guard_interval) {
|
|
|
|
case 0:
|
|
|
|
guard_interval = 32;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
guard_interval = 16;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
guard_interval = 8;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
guard_interval = 4;
|
|
|
|
break;
|
|
|
|
default: /* make it default to 32 */
|
|
|
|
guard_interval = 32;
|
2008-01-24 00:22:17 +00:00
|
|
|
}
|
|
|
|
switch (transmission_mode) {
|
|
|
|
case 0:
|
|
|
|
transmission_mode_str = "2k";
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
transmission_mode_str = "8k";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
transmission_mode_str = "reserved";
|
|
|
|
}
|
|
|
|
delivery_structure = gst_structure_new ("terrestrial",
|
|
|
|
"frequency", G_TYPE_UINT, frequency,
|
|
|
|
"bandwidth", G_TYPE_UINT, bandwidth,
|
|
|
|
"constellation", G_TYPE_STRING, constellation_str,
|
|
|
|
"hierarchy", G_TYPE_UINT, hierarchy,
|
|
|
|
"code-rate-hp", G_TYPE_STRING, code_rate_hp_str,
|
|
|
|
"code-rate-lp", G_TYPE_STRING, code_rate_lp_str,
|
|
|
|
"guard-interval", G_TYPE_UINT, guard_interval,
|
|
|
|
"transmission-mode", G_TYPE_STRING, transmission_mode_str,
|
|
|
|
"other-frequency", G_TYPE_BOOLEAN, other_frequency, NULL);
|
|
|
|
gst_structure_set (transport, "delivery", GST_TYPE_STRUCTURE,
|
|
|
|
delivery_structure, NULL);
|
|
|
|
}
|
|
|
|
if ((delivery =
|
|
|
|
gst_mpeg_descriptor_find (mpegdescriptor,
|
|
|
|
DESC_DTG_LOGICAL_CHANNEL))) {
|
|
|
|
guint8 *current_pos = delivery + 2;
|
|
|
|
GValue channel_numbers = { 0 };
|
|
|
|
|
|
|
|
g_value_init (&channel_numbers, GST_TYPE_LIST);
|
|
|
|
while (current_pos < delivery + DESC_LENGTH (delivery)) {
|
|
|
|
GstStructure *channel;
|
|
|
|
GValue channel_value = { 0 };
|
|
|
|
guint16 service_id = GST_READ_UINT16_BE (current_pos);
|
|
|
|
|
|
|
|
current_pos += 2;
|
|
|
|
guint16 logical_channel_number =
|
|
|
|
GST_READ_UINT16_BE (current_pos) & 0x03ff;
|
|
|
|
channel =
|
|
|
|
gst_structure_new ("channels", "service-id", G_TYPE_UINT,
|
|
|
|
service_id, "logical-channel-number", G_TYPE_UINT,
|
|
|
|
logical_channel_number, NULL);
|
|
|
|
g_value_init (&channel_value, GST_TYPE_STRUCTURE);
|
|
|
|
g_value_take_boxed (&channel_value, channel);
|
|
|
|
gst_value_list_append_value (&channel_numbers, &channel_value);
|
|
|
|
g_value_unset (&channel_value);
|
|
|
|
current_pos += 2;
|
|
|
|
}
|
|
|
|
gst_structure_set_value (transport, "channels", &channel_numbers);
|
|
|
|
g_value_unset (&channel_numbers);
|
2008-01-23 09:49:27 +00:00
|
|
|
}
|
|
|
|
gst_mpeg_descriptor_free (mpegdescriptor);
|
2007-08-20 14:23:45 +00:00
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
descriptors = g_value_array_new (0);
|
|
|
|
if (!mpegts_packetizer_parse_descriptors (packetizer,
|
|
|
|
&data, data + descriptors_loop_length, descriptors)) {
|
|
|
|
gst_structure_free (transport);
|
|
|
|
g_value_array_free (descriptors);
|
2007-08-20 14:23:45 +00:00
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
gst_structure_set (transport, "descriptors", G_TYPE_VALUE_ARRAY,
|
|
|
|
descriptors, NULL);
|
|
|
|
g_value_array_free (descriptors);
|
2007-08-20 14:23:45 +00:00
|
|
|
}
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
g_value_init (&transport_value, GST_TYPE_STRUCTURE);
|
|
|
|
g_value_take_boxed (&transport_value, transport);
|
|
|
|
gst_value_list_append_value (&transports, &transport_value);
|
|
|
|
g_value_unset (&transport_value);
|
|
|
|
|
|
|
|
transport_stream_loop_length -= data - entry_begin;
|
2007-08-20 14:23:45 +00:00
|
|
|
}
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
if (data != end - 4) {
|
|
|
|
GST_WARNING ("PID %d invalid NIT parsed %d length %d",
|
|
|
|
section->pid, data - GST_BUFFER_DATA (section->buffer),
|
|
|
|
GST_BUFFER_SIZE (section->buffer));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_structure_set_value (nit, "transports", &transports);
|
|
|
|
g_value_unset (&transports);
|
2007-08-20 14:23:45 +00:00
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
dbg_str = gst_structure_to_string (nit);
|
|
|
|
GST_DEBUG ("NIT %s", dbg_str);
|
|
|
|
g_free (dbg_str);
|
|
|
|
|
|
|
|
return nit;
|
2007-08-20 14:23:45 +00:00
|
|
|
|
|
|
|
error:
|
2007-11-23 17:53:37 +00:00
|
|
|
if (nit)
|
|
|
|
gst_structure_free (nit);
|
|
|
|
|
|
|
|
if (GST_VALUE_HOLDS_LIST (&transports))
|
|
|
|
g_value_unset (&transports);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GstStructure *
|
|
|
|
mpegts_packetizer_parse_sdt (MpegTSPacketizer * packetizer,
|
|
|
|
MpegTSPacketizerSection * section)
|
|
|
|
{
|
|
|
|
GstStructure *sdt = NULL, *service = NULL;
|
|
|
|
guint8 *data, *end, *entry_begin;
|
|
|
|
guint16 transport_stream_id, original_network_id, service_id;
|
|
|
|
guint tmp;
|
|
|
|
guint sdt_info_length;
|
|
|
|
gboolean EIT_schedule, EIT_present_following;
|
|
|
|
guint8 running_status;
|
|
|
|
gboolean scrambled;
|
|
|
|
guint descriptors_loop_length;
|
|
|
|
GValue services = { 0 };
|
|
|
|
GValueArray *descriptors = NULL;
|
|
|
|
GValue service_value = { 0 };
|
|
|
|
gchar *dbg_str;
|
|
|
|
|
2007-12-14 17:51:49 +00:00
|
|
|
GST_DEBUG ("SDT");
|
2007-11-23 17:53:37 +00:00
|
|
|
/* fixed header + CRC == 16 */
|
|
|
|
if (GST_BUFFER_SIZE (section->buffer) < 14) {
|
|
|
|
GST_WARNING ("PID %d invalid SDT size %d",
|
|
|
|
section->pid, section->section_length);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = GST_BUFFER_DATA (section->buffer);
|
|
|
|
end = data + GST_BUFFER_SIZE (section->buffer);
|
|
|
|
|
|
|
|
section->table_id = *data++;
|
|
|
|
section->section_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
if (data + section->section_length != end) {
|
|
|
|
GST_WARNING ("PID %d invalid SDT section length %d expected %d",
|
|
|
|
section->pid, section->section_length, end - data);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
transport_stream_id = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
tmp = *data++;
|
|
|
|
section->version_number = (tmp >> 1) & 0x1F;
|
|
|
|
section->current_next_indicator = tmp & 0x01;
|
|
|
|
|
|
|
|
/* skip section_number and last_section_number */
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
original_network_id = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
/* skip reserved byte */
|
|
|
|
data += 1;
|
|
|
|
|
|
|
|
sdt = gst_structure_new ("sdt",
|
|
|
|
"transport-stream-id", G_TYPE_UINT, transport_stream_id,
|
|
|
|
"version-number", G_TYPE_UINT, section->version_number,
|
|
|
|
"current-next-indicator", G_TYPE_UINT, section->current_next_indicator,
|
2008-02-05 11:40:43 +00:00
|
|
|
"original-network-id", G_TYPE_UINT, original_network_id,
|
|
|
|
"actual-transport-stream", G_TYPE_BOOLEAN, section->table_id == 0x42,
|
|
|
|
NULL);
|
2007-11-23 17:53:37 +00:00
|
|
|
|
|
|
|
sdt_info_length = section->section_length - 8;
|
|
|
|
g_value_init (&services, GST_TYPE_LIST);
|
|
|
|
/* read up to the CRC */
|
|
|
|
while (sdt_info_length - 4 > 0) {
|
|
|
|
gchar *service_name;
|
|
|
|
|
|
|
|
entry_begin = data;
|
|
|
|
|
|
|
|
if (sdt_info_length < 9) {
|
|
|
|
/* each entry must be at least 5 bytes (+4 bytes for the CRC) */
|
|
|
|
GST_WARNING ("PID %d invalid SDT entry size %d",
|
|
|
|
section->pid, sdt_info_length);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
service_id = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
/* reserved */
|
|
|
|
data += 1;
|
|
|
|
|
|
|
|
tmp = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
EIT_schedule = (tmp >> 15);
|
|
|
|
EIT_present_following = (tmp >> 14) & 0x01;
|
|
|
|
running_status = (tmp >> 5) & 0x03;
|
|
|
|
scrambled = (tmp >> 4) & 0x01;
|
|
|
|
descriptors_loop_length = tmp & 0x0FFF;
|
|
|
|
|
2008-01-22 19:59:39 +00:00
|
|
|
/* TODO send tag event down relevant pad for channel name and provider */
|
2007-11-23 17:53:37 +00:00
|
|
|
service_name = g_strdup_printf ("service-%d", service_id);
|
|
|
|
service = gst_structure_new (service_name, NULL);
|
|
|
|
g_free (service_name);
|
|
|
|
|
|
|
|
if (descriptors_loop_length) {
|
|
|
|
if (data + descriptors_loop_length > end - 4) {
|
|
|
|
GST_WARNING ("PID %d invalid SDT entry %d descriptors loop length %d",
|
|
|
|
section->pid, service_id, descriptors_loop_length);
|
|
|
|
gst_structure_free (service);
|
|
|
|
goto error;
|
|
|
|
}
|
2008-01-22 18:04:04 +00:00
|
|
|
guint8 *service_descriptor;
|
|
|
|
GstMPEGDescriptor *mpegdescriptor =
|
|
|
|
gst_mpeg_descriptor_parse (data, descriptors_loop_length);
|
|
|
|
service_descriptor =
|
|
|
|
gst_mpeg_descriptor_find (mpegdescriptor, DESC_DVB_SERVICE);
|
|
|
|
if (service_descriptor != NULL) {
|
2008-01-22 19:59:39 +00:00
|
|
|
gchar *servicename_tmp, *serviceprovider_name_tmp;
|
2008-01-24 00:22:17 +00:00
|
|
|
guint8 serviceprovider_name_length =
|
2008-01-22 18:04:04 +00:00
|
|
|
DESC_DVB_SERVICE_provider_name_length (service_descriptor);
|
|
|
|
gchar *serviceprovider_name =
|
|
|
|
(gchar *) DESC_DVB_SERVICE_provider_name_text (service_descriptor);
|
2008-01-24 00:22:17 +00:00
|
|
|
guint8 servicename_length =
|
2008-01-22 18:04:04 +00:00
|
|
|
DESC_DVB_SERVICE_name_length (service_descriptor);
|
|
|
|
gchar *servicename =
|
|
|
|
(gchar *) DESC_DVB_SERVICE_name_text (service_descriptor);
|
2008-01-24 08:12:29 +00:00
|
|
|
if (servicename_length + serviceprovider_name_length + 2 <=
|
|
|
|
DESC_LENGTH (service_descriptor)) {
|
2008-01-24 00:22:17 +00:00
|
|
|
if (servicename[0] < 0x20) {
|
|
|
|
servicename_length -= 1;
|
|
|
|
servicename += 1;
|
|
|
|
}
|
|
|
|
if (serviceprovider_name[0] < 0x20) {
|
|
|
|
serviceprovider_name_length -= 1;
|
|
|
|
serviceprovider_name += 1;
|
|
|
|
}
|
|
|
|
servicename_tmp = g_strndup (servicename, servicename_length);
|
|
|
|
serviceprovider_name_tmp =
|
|
|
|
g_strndup (serviceprovider_name, serviceprovider_name_length);
|
|
|
|
gst_structure_set (service, "name", G_TYPE_STRING, servicename_tmp,
|
|
|
|
NULL);
|
|
|
|
gst_structure_set (service, "provider-name", G_TYPE_STRING,
|
|
|
|
serviceprovider_name_tmp, NULL);
|
|
|
|
g_free (servicename_tmp);
|
|
|
|
g_free (serviceprovider_name_tmp);
|
2008-01-22 18:04:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
gst_mpeg_descriptor_free (mpegdescriptor);
|
2007-11-23 17:53:37 +00:00
|
|
|
|
|
|
|
descriptors = g_value_array_new (0);
|
|
|
|
if (!mpegts_packetizer_parse_descriptors (packetizer,
|
|
|
|
&data, data + descriptors_loop_length, descriptors)) {
|
|
|
|
gst_structure_free (service);
|
|
|
|
g_value_array_free (descriptors);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_structure_set (service, "descriptors", G_TYPE_VALUE_ARRAY,
|
|
|
|
descriptors, NULL);
|
2008-01-22 18:04:04 +00:00
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
g_value_array_free (descriptors);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_value_init (&service_value, GST_TYPE_STRUCTURE);
|
|
|
|
g_value_take_boxed (&service_value, service);
|
|
|
|
gst_value_list_append_value (&services, &service_value);
|
|
|
|
g_value_unset (&service_value);
|
|
|
|
|
|
|
|
sdt_info_length -= data - entry_begin;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data != end - 4) {
|
|
|
|
GST_WARNING ("PID %d invalid SDT parsed %d length %d",
|
|
|
|
section->pid, data - GST_BUFFER_DATA (section->buffer),
|
|
|
|
GST_BUFFER_SIZE (section->buffer));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_structure_set_value (sdt, "services", &services);
|
|
|
|
g_value_unset (&services);
|
|
|
|
|
|
|
|
dbg_str = gst_structure_to_string (sdt);
|
|
|
|
g_free (dbg_str);
|
|
|
|
|
|
|
|
return sdt;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (sdt)
|
|
|
|
gst_structure_free (sdt);
|
|
|
|
|
|
|
|
if (GST_VALUE_HOLDS_LIST (&services))
|
|
|
|
g_value_unset (&services);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
GstStructure *
|
|
|
|
mpegts_packetizer_parse_eit (MpegTSPacketizer * packetizer,
|
|
|
|
MpegTSPacketizerSection * section)
|
|
|
|
{
|
|
|
|
GstStructure *eit = NULL, *event = NULL;
|
|
|
|
guint service_id, last_table_id, segment_last_section_number;
|
|
|
|
guint transport_stream_id, original_network_id;
|
|
|
|
gboolean free_ca_mode;
|
|
|
|
guint event_id, running_status;
|
|
|
|
guint64 start_and_duration;
|
2008-01-22 19:59:39 +00:00
|
|
|
guint16 mjd;
|
|
|
|
guint year, month, day, hour, minute, second;
|
|
|
|
guint duration;
|
|
|
|
guint8 *data, *end, *duration_ptr, *utc_ptr;
|
2007-11-23 17:53:37 +00:00
|
|
|
guint16 descriptors_loop_length;
|
|
|
|
GValue events = { 0 };
|
|
|
|
GValue event_value = { 0 };
|
|
|
|
GValueArray *descriptors = NULL;
|
|
|
|
gchar *dbg_str, *event_name;
|
|
|
|
guint tmp;
|
|
|
|
|
|
|
|
/* fixed header + CRC == 16 */
|
|
|
|
if (GST_BUFFER_SIZE (section->buffer) < 18) {
|
|
|
|
GST_WARNING ("PID %d invalid EIT size %d",
|
|
|
|
section->pid, section->section_length);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = GST_BUFFER_DATA (section->buffer);
|
|
|
|
end = data + GST_BUFFER_SIZE (section->buffer);
|
|
|
|
|
|
|
|
section->table_id = *data++;
|
|
|
|
section->section_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
if (data + section->section_length != end) {
|
|
|
|
GST_WARNING ("PID %d invalid EIT section length %d expected %d",
|
|
|
|
section->pid, section->section_length, end - data);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
service_id = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
tmp = *data++;
|
|
|
|
section->version_number = (tmp >> 1) & 0x1F;
|
|
|
|
section->current_next_indicator = tmp & 0x01;
|
|
|
|
|
|
|
|
/* skip section_number and last_section_number */
|
|
|
|
data += 2;
|
|
|
|
|
|
|
|
transport_stream_id = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
original_network_id = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
segment_last_section_number = *data;
|
|
|
|
data += 1;
|
|
|
|
last_table_id = *data;
|
|
|
|
data += 1;
|
|
|
|
|
|
|
|
eit = gst_structure_new ("eit",
|
|
|
|
"version-number", G_TYPE_UINT, section->version_number,
|
|
|
|
"current-next-indicator", G_TYPE_UINT, section->current_next_indicator,
|
|
|
|
"service-id", G_TYPE_UINT, service_id,
|
|
|
|
"transport-stream-id", G_TYPE_UINT, transport_stream_id,
|
|
|
|
"original-network-id", G_TYPE_UINT, original_network_id,
|
|
|
|
"segment-last-section-number", G_TYPE_UINT, segment_last_section_number,
|
|
|
|
"last-table-id", G_TYPE_UINT, last_table_id, NULL);
|
|
|
|
|
|
|
|
g_value_init (&events, GST_TYPE_LIST);
|
|
|
|
while (data < end - 4) {
|
|
|
|
/* 12 is the minimum entry size + CRC */
|
|
|
|
if (end - data < 12 + 4) {
|
|
|
|
GST_WARNING ("PID %d invalid EIT entry length %d",
|
|
|
|
section->pid, end - 4 - data);
|
|
|
|
gst_structure_free (eit);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
event_id = GST_READ_UINT16_BE (data);
|
|
|
|
data += 2;
|
|
|
|
start_and_duration = GST_READ_UINT64_BE (data);
|
2008-01-22 19:59:39 +00:00
|
|
|
duration_ptr = data + 5;
|
|
|
|
utc_ptr = data + 2;
|
|
|
|
mjd = GST_READ_UINT16_BE (data);
|
|
|
|
if (mjd == G_MAXUINT16) {
|
|
|
|
year = 1900;
|
|
|
|
month = day = hour = minute = second = 0;
|
|
|
|
} else {
|
|
|
|
/* See EN 300 468 Annex C */
|
|
|
|
year = (guint32) (((mjd - 15078.2) / 365.25));
|
|
|
|
month = (guint8) ((mjd - 14956.1 - (guint) (year * 365.25)) / 30.6001);
|
|
|
|
day = mjd - 14956 - (guint) (year * 365.25) - (guint) (month * 30.6001);
|
|
|
|
if (month == 14 || month == 15) {
|
|
|
|
year++;
|
|
|
|
month = month - 1 - 12;
|
|
|
|
} else {
|
|
|
|
month--;
|
|
|
|
}
|
|
|
|
year += 1900;
|
|
|
|
hour = ((utc_ptr[0] & 0xF0) >> 4) * 10 + (utc_ptr[0] & 0x0F);
|
|
|
|
minute = ((utc_ptr[1] & 0xF0) >> 4) * 10 + (utc_ptr[1] & 0x0F);
|
|
|
|
second = ((utc_ptr[2] & 0xF0) >> 4) * 10 + (utc_ptr[2] & 0x0F);
|
|
|
|
}
|
|
|
|
|
|
|
|
duration = (((duration_ptr[0] & 0xF0) >> 4) * 10 +
|
|
|
|
(duration_ptr[0] & 0x0F)) * 60 * 60 +
|
|
|
|
(((duration_ptr[1] & 0xF0) >> 4) * 10 +
|
|
|
|
(duration_ptr[1] & 0x0F)) * 60 +
|
|
|
|
((duration_ptr[2] & 0xF0) >> 4) * 10 + (duration_ptr[2] & 0x0F);
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
data += 8;
|
|
|
|
running_status = *data >> 5;
|
|
|
|
free_ca_mode = (*data >> 4) & 0x01;
|
|
|
|
descriptors_loop_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
data += 2;
|
|
|
|
|
2008-01-22 19:59:39 +00:00
|
|
|
/* TODO: send tag event down relevant pad saying what is currently playing */
|
2007-11-23 17:53:37 +00:00
|
|
|
event_name = g_strdup_printf ("event-%d", event_id);
|
|
|
|
event = gst_structure_new (event_name,
|
|
|
|
"event-id", G_TYPE_UINT, event_id,
|
2008-01-22 19:59:39 +00:00
|
|
|
"year", G_TYPE_UINT, year,
|
|
|
|
"month", G_TYPE_UINT, month,
|
|
|
|
"day", G_TYPE_UINT, day,
|
|
|
|
"hour", G_TYPE_UINT, hour,
|
|
|
|
"minute", G_TYPE_UINT, minute,
|
|
|
|
"second", G_TYPE_UINT, second,
|
|
|
|
"duration", G_TYPE_UINT, duration,
|
2007-11-23 17:53:37 +00:00
|
|
|
"running-status", G_TYPE_UINT, running_status,
|
|
|
|
"free-ca-mode", G_TYPE_BOOLEAN, free_ca_mode, NULL);
|
|
|
|
g_free (event_name);
|
|
|
|
|
|
|
|
if (descriptors_loop_length) {
|
|
|
|
if (data + descriptors_loop_length > end - 4) {
|
|
|
|
GST_WARNING ("PID %d invalid EIT descriptors loop length %d",
|
|
|
|
section->pid, descriptors_loop_length);
|
|
|
|
gst_structure_free (event);
|
|
|
|
goto error;
|
|
|
|
}
|
2008-01-22 18:40:16 +00:00
|
|
|
guint8 *event_descriptor;
|
|
|
|
GstMPEGDescriptor *mpegdescriptor =
|
|
|
|
gst_mpeg_descriptor_parse (data, descriptors_loop_length);
|
|
|
|
event_descriptor =
|
|
|
|
gst_mpeg_descriptor_find (mpegdescriptor, DESC_DVB_SHORT_EVENT);
|
|
|
|
if (event_descriptor != NULL) {
|
2008-01-22 19:59:39 +00:00
|
|
|
gchar *eventname_tmp, *eventdescription_tmp;
|
2008-01-24 00:22:17 +00:00
|
|
|
guint8 eventname_length =
|
2008-01-22 18:40:16 +00:00
|
|
|
DESC_DVB_SHORT_EVENT_name_length (event_descriptor);
|
|
|
|
gchar *eventname =
|
|
|
|
(gchar *) DESC_DVB_SHORT_EVENT_name_text (event_descriptor);
|
2008-01-24 00:22:17 +00:00
|
|
|
guint8 eventdescription_length =
|
2008-01-22 18:40:16 +00:00
|
|
|
DESC_DVB_SHORT_EVENT_description_length (event_descriptor);
|
|
|
|
gchar *eventdescription =
|
|
|
|
(gchar *) DESC_DVB_SHORT_EVENT_description_text (event_descriptor);
|
2008-01-24 08:12:29 +00:00
|
|
|
if (eventname_length + eventdescription_length + 2 <=
|
|
|
|
DESC_LENGTH (event_descriptor)) {
|
2008-01-24 00:22:17 +00:00
|
|
|
if (eventname[0] < 0x20) {
|
|
|
|
eventname_length -= 1;
|
|
|
|
eventname += 1;
|
|
|
|
}
|
|
|
|
if (eventdescription[0] < 0x20) {
|
|
|
|
eventdescription_length -= 1;
|
|
|
|
eventdescription += 1;
|
|
|
|
}
|
|
|
|
eventname_tmp = g_strndup (eventname, eventname_length),
|
|
|
|
eventdescription_tmp =
|
|
|
|
g_strndup (eventdescription, eventdescription_length);
|
|
|
|
|
|
|
|
gst_structure_set (event, "name", G_TYPE_STRING, eventname_tmp, NULL);
|
|
|
|
gst_structure_set (event, "description", G_TYPE_STRING,
|
|
|
|
eventdescription_tmp, NULL);
|
|
|
|
g_free (eventname_tmp);
|
|
|
|
g_free (eventdescription_tmp);
|
2008-01-22 18:40:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_mpeg_descriptor_free (mpegdescriptor);
|
2007-11-23 17:53:37 +00:00
|
|
|
|
|
|
|
descriptors = g_value_array_new (0);
|
|
|
|
if (!mpegts_packetizer_parse_descriptors (packetizer,
|
|
|
|
&data, data + descriptors_loop_length, descriptors)) {
|
|
|
|
gst_structure_free (event);
|
|
|
|
g_value_array_free (descriptors);
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
gst_structure_set (event, "descriptors", G_TYPE_VALUE_ARRAY, descriptors,
|
|
|
|
NULL);
|
|
|
|
g_value_array_free (descriptors);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_value_init (&event_value, GST_TYPE_STRUCTURE);
|
|
|
|
g_value_take_boxed (&event_value, event);
|
|
|
|
gst_value_list_append_value (&events, &event_value);
|
|
|
|
g_value_unset (&event_value);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (data != end - 4) {
|
|
|
|
GST_WARNING ("PID %d invalid EIT parsed %d length %d",
|
|
|
|
section->pid, data - GST_BUFFER_DATA (section->buffer),
|
|
|
|
GST_BUFFER_SIZE (section->buffer));
|
|
|
|
goto error;
|
|
|
|
}
|
|
|
|
|
|
|
|
gst_structure_set_value (eit, "events", &events);
|
|
|
|
g_value_unset (&events);
|
|
|
|
|
|
|
|
dbg_str = gst_structure_to_string (eit);
|
|
|
|
GST_DEBUG ("EIT %s", dbg_str);
|
|
|
|
g_free (dbg_str);
|
|
|
|
|
|
|
|
return eit;
|
|
|
|
|
|
|
|
error:
|
|
|
|
if (eit)
|
|
|
|
gst_structure_free (eit);
|
|
|
|
|
|
|
|
if (GST_VALUE_HOLDS_LIST (&events))
|
|
|
|
g_value_unset (&events);
|
|
|
|
|
2007-08-20 14:23:45 +00:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
foreach_stream_clear (gpointer key, gpointer value, gpointer data)
|
|
|
|
{
|
|
|
|
MpegTSPacketizerStream *stream = (MpegTSPacketizerStream *) value;
|
|
|
|
|
2007-11-15 19:03:33 +00:00
|
|
|
/* remove the stream */
|
|
|
|
g_object_unref (stream->section_adapter);
|
|
|
|
g_free (stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
static gboolean
|
|
|
|
remove_all (gpointer key, gpointer value, gpointer user_data)
|
|
|
|
{
|
|
|
|
return TRUE;
|
2007-08-20 14:23:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
mpegts_packetizer_clear (MpegTSPacketizer * packetizer)
|
|
|
|
{
|
|
|
|
g_hash_table_foreach (packetizer->streams, foreach_stream_clear, packetizer);
|
2007-11-15 19:03:33 +00:00
|
|
|
|
|
|
|
/* FIXME can't use remove_all because we don't depend on 2.12 yet */
|
|
|
|
g_hash_table_foreach_remove (packetizer->streams, remove_all, NULL);
|
2007-08-20 14:23:45 +00:00
|
|
|
gst_adapter_clear (packetizer->adapter);
|
|
|
|
}
|
|
|
|
|
|
|
|
MpegTSPacketizer *
|
|
|
|
mpegts_packetizer_new ()
|
|
|
|
{
|
|
|
|
MpegTSPacketizer *packetizer;
|
|
|
|
|
|
|
|
packetizer =
|
|
|
|
GST_MPEGTS_PACKETIZER (g_object_new (GST_TYPE_MPEGTS_PACKETIZER, NULL));
|
|
|
|
|
|
|
|
return packetizer;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
mpegts_packetizer_push (MpegTSPacketizer * packetizer, GstBuffer * buffer)
|
|
|
|
{
|
|
|
|
g_return_if_fail (GST_IS_MPEGTS_PACKETIZER (packetizer));
|
|
|
|
g_return_if_fail (GST_IS_BUFFER (buffer));
|
|
|
|
|
|
|
|
gst_adapter_push (packetizer->adapter, buffer);
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
mpegts_packetizer_has_packets (MpegTSPacketizer * packetizer)
|
|
|
|
{
|
|
|
|
g_return_val_if_fail (GST_IS_MPEGTS_PACKETIZER (packetizer), FALSE);
|
|
|
|
|
|
|
|
return gst_adapter_available (packetizer->adapter) >= 188;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
mpegts_packetizer_next_packet (MpegTSPacketizer * packetizer,
|
|
|
|
MpegTSPacketizerPacket * packet)
|
|
|
|
{
|
|
|
|
guint8 sync_byte;
|
|
|
|
gboolean ret = FALSE;
|
|
|
|
|
|
|
|
g_return_val_if_fail (GST_IS_MPEGTS_PACKETIZER (packetizer), FALSE);
|
|
|
|
g_return_val_if_fail (packet != NULL, FALSE);
|
|
|
|
|
|
|
|
packet->buffer = NULL;
|
|
|
|
while (gst_adapter_available (packetizer->adapter) >= 188) {
|
|
|
|
sync_byte = *gst_adapter_peek (packetizer->adapter, 1);
|
|
|
|
if (sync_byte != 0x47) {
|
|
|
|
GST_DEBUG ("lost sync %02x", sync_byte);
|
|
|
|
gst_adapter_flush (packetizer->adapter, 1);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
packet->buffer = gst_adapter_take_buffer (packetizer->adapter, 188);
|
|
|
|
packet->data_start = GST_BUFFER_DATA (packet->buffer);
|
|
|
|
packet->data_end =
|
|
|
|
GST_BUFFER_DATA (packet->buffer) + GST_BUFFER_SIZE (packet->buffer);
|
|
|
|
ret = mpegts_packetizer_parse_packet (packetizer, packet);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
mpegts_packetizer_clear_packet (MpegTSPacketizer * packetizer,
|
|
|
|
MpegTSPacketizerPacket * packet)
|
|
|
|
{
|
|
|
|
g_return_if_fail (GST_IS_MPEGTS_PACKETIZER (packetizer));
|
|
|
|
g_return_if_fail (packet != NULL);
|
|
|
|
|
|
|
|
if (packet->buffer)
|
|
|
|
gst_buffer_unref (packet->buffer);
|
|
|
|
packet->buffer = NULL;
|
|
|
|
packet->continuity_counter = 0;
|
|
|
|
packet->payload_unit_start_indicator = 0;
|
|
|
|
packet->payload = NULL;
|
|
|
|
packet->data_start = NULL;
|
|
|
|
packet->data_end = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
gboolean
|
|
|
|
mpegts_packetizer_push_section (MpegTSPacketizer * packetizer,
|
|
|
|
MpegTSPacketizerPacket * packet, MpegTSPacketizerSection * section)
|
|
|
|
{
|
|
|
|
gboolean res = FALSE;
|
|
|
|
MpegTSPacketizerStream *stream;
|
|
|
|
guint8 pointer, table_id;
|
2007-12-14 17:51:49 +00:00
|
|
|
guint16 subtable_extension;
|
2007-08-20 14:23:45 +00:00
|
|
|
guint section_length;
|
|
|
|
GstBuffer *sub_buf;
|
|
|
|
guint8 *data;
|
|
|
|
|
|
|
|
g_return_val_if_fail (GST_IS_MPEGTS_PACKETIZER (packetizer), FALSE);
|
|
|
|
g_return_val_if_fail (packet != NULL, FALSE);
|
|
|
|
g_return_val_if_fail (section != NULL, FALSE);
|
|
|
|
|
|
|
|
data = packet->data;
|
|
|
|
section->pid = packet->pid;
|
|
|
|
|
|
|
|
if (packet->payload_unit_start_indicator == 1) {
|
|
|
|
pointer = *data++;
|
|
|
|
if (data + pointer > packet->data_end) {
|
|
|
|
GST_WARNING ("PID %d PSI section pointer points past the end "
|
|
|
|
"of the buffer", packet->pid);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
data += pointer;
|
|
|
|
}
|
|
|
|
/* create a sub buffer from the start of the section (table_id and
|
|
|
|
* section_length included) to the end */
|
|
|
|
sub_buf = gst_buffer_create_sub (packet->buffer,
|
2007-12-14 17:51:49 +00:00
|
|
|
data - GST_BUFFER_DATA (packet->buffer), packet->data_end - data);
|
|
|
|
|
|
|
|
stream = (MpegTSPacketizerStream *) g_hash_table_lookup (packetizer->streams,
|
|
|
|
GINT_TO_POINTER ((gint) packet->pid));
|
|
|
|
if (stream == NULL) {
|
|
|
|
stream = mpegts_packetizer_stream_new ();
|
|
|
|
g_hash_table_insert (packetizer->streams,
|
|
|
|
GINT_TO_POINTER ((gint) packet->pid), stream);
|
|
|
|
}
|
2007-08-20 14:23:45 +00:00
|
|
|
|
|
|
|
if (packet->payload_unit_start_indicator) {
|
2007-12-14 17:51:49 +00:00
|
|
|
table_id = *data++;
|
|
|
|
/* subtable_extension should be read from 4th and 5th bytes only if
|
|
|
|
* section_syntax_indicator is 1 */
|
|
|
|
if ((data[0] & 0x80) == 0)
|
|
|
|
subtable_extension = 0;
|
|
|
|
else
|
|
|
|
subtable_extension = GST_READ_UINT16_BE (data + 2);
|
|
|
|
GST_DEBUG ("pid: %d table_id %d sub_table_extension %d",
|
|
|
|
packet->pid, table_id, subtable_extension);
|
|
|
|
|
|
|
|
section_length = GST_READ_UINT16_BE (data) & 0x0FFF;
|
|
|
|
|
2007-08-20 14:23:45 +00:00
|
|
|
if (stream->continuity_counter != CONTINUITY_UNSET) {
|
2007-12-14 17:51:49 +00:00
|
|
|
GST_DEBUG
|
|
|
|
("PID %d table_id %d sub_table_extension %d payload_unit_start_indicator set but section "
|
2007-08-20 14:23:45 +00:00
|
|
|
"not complete (last_continuity: %d continuity: %d sec len %d buffer %d avail %d",
|
2007-12-14 17:51:49 +00:00
|
|
|
packet->pid, table_id, subtable_extension, stream->continuity_counter,
|
|
|
|
packet->continuity_counter, section_length, GST_BUFFER_SIZE (sub_buf),
|
2007-08-20 14:23:45 +00:00
|
|
|
gst_adapter_available (stream->section_adapter));
|
|
|
|
mpegts_packetizer_clear_section (packetizer, stream);
|
2007-12-14 17:51:49 +00:00
|
|
|
} else {
|
|
|
|
GST_DEBUG
|
|
|
|
("pusi set and new stream section is %d long and data we have is: %d",
|
|
|
|
section_length, packet->data_end - packet->data);
|
2007-08-20 14:23:45 +00:00
|
|
|
}
|
|
|
|
stream->continuity_counter = packet->continuity_counter;
|
|
|
|
stream->section_length = section_length;
|
|
|
|
gst_adapter_push (stream->section_adapter, sub_buf);
|
|
|
|
|
|
|
|
res = TRUE;
|
2007-12-14 17:51:49 +00:00
|
|
|
} else if (stream->continuity_counter != CONTINUITY_UNSET &&
|
|
|
|
(packet->continuity_counter == stream->continuity_counter + 1 ||
|
|
|
|
(stream->continuity_counter == MAX_CONTINUITY &&
|
|
|
|
packet->continuity_counter == 0))) {
|
2007-08-20 14:23:45 +00:00
|
|
|
stream->continuity_counter = packet->continuity_counter;
|
|
|
|
gst_adapter_push (stream->section_adapter, sub_buf);
|
|
|
|
|
|
|
|
res = TRUE;
|
|
|
|
} else {
|
2007-12-14 17:51:49 +00:00
|
|
|
if (stream->continuity_counter == CONTINUITY_UNSET)
|
|
|
|
GST_DEBUG ("PID %d waiting for pusi", packet->pid);
|
|
|
|
else
|
|
|
|
GST_DEBUG ("PID %d section discontinuity "
|
|
|
|
"(last_continuity: %d continuity: %d", packet->pid,
|
|
|
|
stream->continuity_counter, packet->continuity_counter);
|
2007-08-20 14:23:45 +00:00
|
|
|
mpegts_packetizer_clear_section (packetizer, stream);
|
|
|
|
gst_buffer_unref (sub_buf);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (res) {
|
|
|
|
/* we pushed some data in the section adapter, see if the section is
|
|
|
|
* complete now */
|
|
|
|
|
|
|
|
/* >= as sections can be padded and padding is not included in
|
|
|
|
* section_length */
|
|
|
|
if (gst_adapter_available (stream->section_adapter) >=
|
|
|
|
stream->section_length + 3) {
|
|
|
|
res = mpegts_packetizer_parse_section_header (packetizer,
|
|
|
|
stream, section);
|
|
|
|
|
|
|
|
/* flush stuffing bytes */
|
|
|
|
mpegts_packetizer_clear_section (packetizer, stream);
|
|
|
|
} else {
|
|
|
|
/* section not complete yet */
|
|
|
|
section->complete = FALSE;
|
|
|
|
}
|
|
|
|
} else {
|
2007-12-14 17:51:49 +00:00
|
|
|
GST_WARNING ("section not complete");
|
2007-08-20 14:23:45 +00:00
|
|
|
section->complete = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
packet->data = data;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
mpegts_packetizer_init_debug ()
|
|
|
|
{
|
|
|
|
GST_DEBUG_CATEGORY_INIT (mpegts_packetizer_debug, "mpegtspacketizer", 0,
|
|
|
|
"MPEG transport stream parser");
|
|
|
|
}
|