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);
|
2008-06-13 15:46:03 +00:00
|
|
|
static gchar *convert_to_utf8 (const gchar * text, gint length, guint start,
|
|
|
|
const gchar * encoding, gboolean is_multibyte, GError ** error);
|
|
|
|
static gchar *get_encoding (const gchar * text, guint * start_text,
|
|
|
|
gboolean * is_multibyte);
|
|
|
|
static gchar *get_encoding_and_convert (const gchar * text, guint length);
|
2007-08-20 14:23:45 +00:00
|
|
|
|
|
|
|
#define CONTINUITY_UNSET 255
|
2007-10-01 09:21:19 +00:00
|
|
|
#define MAX_CONTINUITY 15
|
2008-04-29 09:02:35 +00:00
|
|
|
#define VERSION_NUMBER_UNSET 255
|
|
|
|
#define TABLE_ID_UNSET 0xFF
|
2007-08-20 14:23:45 +00:00
|
|
|
|
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);
|
2008-04-29 09:02:35 +00:00
|
|
|
subtable->version_number = VERSION_NUMBER_UNSET;
|
2007-12-14 17:51:49 +00:00
|
|
|
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;
|
2008-04-29 09:02:35 +00:00
|
|
|
stream->section_table_id = TABLE_ID_UNSET;
|
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;
|
2008-04-29 09:02:35 +00:00
|
|
|
stream->section_table_id = TABLE_ID_UNSET;
|
2007-08-20 14:23:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
2008-04-29 09:02:35 +00:00
|
|
|
stream->section_table_id = section->table_id;
|
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) {
|
2008-02-22 05:33:59 +00:00
|
|
|
guint8 *networkname_descriptor;
|
|
|
|
GstMPEGDescriptor *mpegdescriptor;
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
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-02-22 05:33:59 +00:00
|
|
|
mpegdescriptor = gst_mpeg_descriptor_parse (data, descriptors_loop_length);
|
2008-01-22 20:27:28 +00:00
|
|
|
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-06-13 15:46:03 +00:00
|
|
|
|
|
|
|
networkname_tmp =
|
|
|
|
get_encoding_and_convert (networkname, networkname_length);
|
2008-01-24 08:12:29 +00:00
|
|
|
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) {
|
2008-02-22 05:33:59 +00:00
|
|
|
GstMPEGDescriptor *mpegdescriptor;
|
|
|
|
guint8 *delivery;
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
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-02-22 05:33:59 +00:00
|
|
|
mpegdescriptor =
|
2008-01-23 09:49:27 +00:00
|
|
|
gst_mpeg_descriptor_parse (data, descriptors_loop_length);
|
|
|
|
|
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);
|
2008-03-04 17:49:15 +00:00
|
|
|
} else if ((delivery =
|
|
|
|
gst_mpeg_descriptor_find (mpegdescriptor,
|
|
|
|
DESC_DVB_CABLE_DELIVERY_SYSTEM))) {
|
|
|
|
|
|
|
|
guint8 *frequency_bcd =
|
|
|
|
DESC_DVB_CABLE_DELIVERY_SYSTEM_frequency (delivery);
|
2008-04-01 19:53:53 +00:00
|
|
|
/* see en 300 468 section 6.2.13.1 least significant bcd digit
|
|
|
|
* is measured in 100Hz units so multiplier needs to be 100 to get
|
|
|
|
* into Hz */
|
|
|
|
guint32 frequency = 100 *
|
|
|
|
((frequency_bcd[3] & 0x0F) +
|
2008-03-04 17:49:15 +00:00
|
|
|
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 modulation =
|
|
|
|
DESC_DVB_CABLE_DELIVERY_SYSTEM_modulation (delivery);
|
|
|
|
gchar *modulation_str;
|
|
|
|
guint8 *symbol_rate_bcd =
|
|
|
|
DESC_DVB_CABLE_DELIVERY_SYSTEM_symbol_rate (delivery);
|
|
|
|
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 = DESC_DVB_CABLE_DELIVERY_SYSTEM_fec_inner (delivery);
|
|
|
|
gchar *fec_inner_str;
|
|
|
|
|
|
|
|
switch (fec_inner) {
|
|
|
|
case 0:
|
|
|
|
fec_inner_str = "undefined";
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
fec_inner_str = "1/2";
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
fec_inner_str = "2/3";
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
fec_inner_str = "3/4";
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
fec_inner_str = "5/6";
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
fec_inner_str = "7/8";
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
fec_inner_str = "8/9";
|
|
|
|
break;
|
|
|
|
case 0xF:
|
|
|
|
fec_inner_str = "none";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
fec_inner_str = "reserved";
|
|
|
|
}
|
|
|
|
switch (modulation) {
|
|
|
|
case 0x00:
|
|
|
|
modulation_str = "undefined";
|
|
|
|
break;
|
|
|
|
case 0x01:
|
|
|
|
modulation_str = "QAM16";
|
|
|
|
break;
|
|
|
|
case 0x02:
|
|
|
|
modulation_str = "QAM32";
|
|
|
|
break;
|
|
|
|
case 0x03:
|
|
|
|
modulation_str = "QAM64";
|
|
|
|
break;
|
|
|
|
case 0x04:
|
|
|
|
modulation_str = "QAM128";
|
|
|
|
break;
|
|
|
|
case 0x05:
|
|
|
|
modulation_str = "QAM256";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
modulation_str = "reserved";
|
|
|
|
}
|
|
|
|
delivery_structure = gst_structure_new ("cable",
|
|
|
|
"modulation", G_TYPE_STRING, modulation_str,
|
|
|
|
"frequency", G_TYPE_UINT, frequency,
|
|
|
|
"symbol-rate", G_TYPE_UINT, symbol_rate,
|
|
|
|
"inner-fec", G_TYPE_STRING, fec_inner_str, NULL);
|
|
|
|
gst_structure_set (transport, "delivery", GST_TYPE_STRUCTURE,
|
|
|
|
delivery_structure, NULL);
|
2008-01-24 00:22:17 +00:00
|
|
|
}
|
|
|
|
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);
|
2008-02-22 05:33:59 +00:00
|
|
|
guint16 logical_channel_number;
|
2008-01-24 00:22:17 +00:00
|
|
|
|
|
|
|
current_pos += 2;
|
2008-02-22 05:33:59 +00:00
|
|
|
logical_channel_number = GST_READ_UINT16_BE (current_pos) & 0x03ff;
|
2008-01-24 00:22:17 +00:00
|
|
|
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) {
|
2008-02-22 05:33:59 +00:00
|
|
|
guint8 *service_descriptor;
|
|
|
|
GstMPEGDescriptor *mpegdescriptor;
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
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-02-22 05:33:59 +00:00
|
|
|
mpegdescriptor =
|
2008-01-22 18:04:04 +00:00
|
|
|
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-06-13 15:46:03 +00:00
|
|
|
|
|
|
|
servicename_tmp =
|
|
|
|
get_encoding_and_convert (servicename, servicename_length);
|
2008-01-24 00:22:17 +00:00
|
|
|
serviceprovider_name_tmp =
|
2008-06-13 15:46:03 +00:00
|
|
|
get_encoding_and_convert (serviceprovider_name,
|
|
|
|
serviceprovider_name_length);
|
|
|
|
|
2008-01-24 00:22:17 +00:00
|
|
|
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,
|
2008-05-09 10:21:07 +00:00
|
|
|
"actual-transport-stream", G_TYPE_BOOLEAN, (section->table_id == 0x4E ||
|
|
|
|
(section->table_id >= 0x50 && section->table_id <= 0x5F)),
|
|
|
|
"present-following", G_TYPE_BOOLEAN, (section->table_id == 0x4E ||
|
|
|
|
section->table_id == 0x4F),
|
2007-11-23 17:53:37 +00:00
|
|
|
"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) {
|
2008-02-22 05:33:59 +00:00
|
|
|
guint8 *event_descriptor;
|
|
|
|
GArray *component_descriptors;
|
|
|
|
GstMPEGDescriptor *mpegdescriptor;
|
|
|
|
|
2007-11-23 17:53:37 +00:00
|
|
|
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-02-22 05:33:59 +00:00
|
|
|
mpegdescriptor =
|
2008-01-22 18:40:16 +00:00
|
|
|
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-06-13 15:46:03 +00:00
|
|
|
|
|
|
|
eventname_tmp =
|
|
|
|
get_encoding_and_convert (eventname, eventname_length),
|
2008-01-24 00:22:17 +00:00
|
|
|
eventdescription_tmp =
|
2008-06-13 15:46:03 +00:00
|
|
|
get_encoding_and_convert (eventdescription,
|
|
|
|
eventdescription_length);
|
2008-01-24 00:22:17 +00:00
|
|
|
|
|
|
|
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
|
|
|
}
|
|
|
|
}
|
2008-02-07 14:45:06 +00:00
|
|
|
component_descriptors = gst_mpeg_descriptor_find_all (mpegdescriptor,
|
|
|
|
DESC_DVB_COMPONENT);
|
|
|
|
if (component_descriptors) {
|
|
|
|
int i;
|
|
|
|
guint8 *comp_descriptor;
|
|
|
|
GValue components = { 0 };
|
|
|
|
g_value_init (&components, GST_TYPE_LIST);
|
|
|
|
/* FIXME: do the component descriptor parsing less verbosely
|
|
|
|
* and better...a task for 0.10.6 */
|
|
|
|
for (i = 0; i < component_descriptors->len; i++) {
|
|
|
|
GstStructure *component = NULL;
|
|
|
|
GValue component_value = { 0 };
|
|
|
|
gint widescreen = 0; /* 0 for 4:3, 1 for 16:9, 2 for > 16:9 */
|
|
|
|
gint freq = 25; /* 25 or 30 measured in Hertz */
|
|
|
|
gboolean highdef = FALSE;
|
|
|
|
gboolean panvectors = FALSE;
|
|
|
|
gchar *comptype = "";
|
|
|
|
|
|
|
|
comp_descriptor = g_array_index (component_descriptors, guint8 *, i);
|
|
|
|
switch (DESC_DVB_COMPONENT_stream_content (comp_descriptor)) {
|
|
|
|
case 0x01:
|
|
|
|
/* video */
|
|
|
|
switch (DESC_DVB_COMPONENT_type (comp_descriptor)) {
|
|
|
|
case 0x01:
|
|
|
|
widescreen = 0;
|
|
|
|
freq = 25;
|
|
|
|
break;
|
|
|
|
case 0x02:
|
|
|
|
widescreen = 1;
|
|
|
|
panvectors = TRUE;
|
|
|
|
freq = 25;
|
|
|
|
break;
|
|
|
|
case 0x03:
|
|
|
|
widescreen = 1;
|
|
|
|
panvectors = FALSE;
|
|
|
|
freq = 25;
|
|
|
|
break;
|
|
|
|
case 0x04:
|
|
|
|
widescreen = 2;
|
|
|
|
freq = 25;
|
|
|
|
break;
|
|
|
|
case 0x05:
|
|
|
|
widescreen = 0;
|
|
|
|
freq = 30;
|
|
|
|
break;
|
|
|
|
case 0x06:
|
|
|
|
widescreen = 1;
|
|
|
|
panvectors = TRUE;
|
|
|
|
freq = 30;
|
|
|
|
break;
|
|
|
|
case 0x07:
|
|
|
|
widescreen = 1;
|
|
|
|
panvectors = FALSE;
|
|
|
|
freq = 30;
|
|
|
|
break;
|
|
|
|
case 0x08:
|
|
|
|
widescreen = 2;
|
|
|
|
freq = 30;
|
|
|
|
break;
|
|
|
|
case 0x09:
|
|
|
|
widescreen = 0;
|
|
|
|
highdef = TRUE;
|
|
|
|
freq = 25;
|
|
|
|
break;
|
|
|
|
case 0x0A:
|
|
|
|
widescreen = 1;
|
|
|
|
highdef = TRUE;
|
|
|
|
panvectors = TRUE;
|
|
|
|
freq = 25;
|
|
|
|
break;
|
|
|
|
case 0x0B:
|
|
|
|
widescreen = 1;
|
|
|
|
highdef = TRUE;
|
|
|
|
panvectors = FALSE;
|
|
|
|
freq = 25;
|
|
|
|
break;
|
|
|
|
case 0x0C:
|
|
|
|
widescreen = 2;
|
|
|
|
highdef = TRUE;
|
|
|
|
freq = 25;
|
|
|
|
break;
|
|
|
|
case 0x0D:
|
|
|
|
widescreen = 0;
|
|
|
|
highdef = TRUE;
|
|
|
|
freq = 30;
|
|
|
|
break;
|
|
|
|
case 0x0E:
|
|
|
|
widescreen = 1;
|
|
|
|
highdef = TRUE;
|
|
|
|
panvectors = TRUE;
|
|
|
|
freq = 30;
|
|
|
|
break;
|
|
|
|
case 0x0F:
|
|
|
|
widescreen = 1;
|
|
|
|
highdef = TRUE;
|
|
|
|
panvectors = FALSE;
|
|
|
|
freq = 30;
|
|
|
|
break;
|
|
|
|
case 0x10:
|
|
|
|
widescreen = 2;
|
|
|
|
highdef = TRUE;
|
|
|
|
freq = 30;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
component = gst_structure_new ("video", "high-definition",
|
|
|
|
G_TYPE_BOOLEAN, TRUE, "frequency", G_TYPE_INT, freq, NULL);
|
|
|
|
if (widescreen == 0) {
|
|
|
|
gst_structure_set (component, "aspect-ratio",
|
|
|
|
G_TYPE_STRING, "4:3", NULL);
|
|
|
|
} else if (widescreen == 2) {
|
|
|
|
gst_structure_set (component, "aspect-ratio", G_TYPE_STRING,
|
|
|
|
"> 16:9", NULL);
|
|
|
|
} else {
|
|
|
|
gst_structure_set (component, "aspect-ratio", G_TYPE_STRING,
|
|
|
|
"16:9", "pan-vectors", G_TYPE_BOOLEAN, panvectors, NULL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x02: /* audio */
|
|
|
|
comptype = "undefined";
|
|
|
|
switch (DESC_DVB_COMPONENT_type (comp_descriptor)) {
|
|
|
|
case 0x01:
|
|
|
|
comptype = "single channel mono";
|
|
|
|
break;
|
|
|
|
case 0x02:
|
|
|
|
comptype = "dual channel mono";
|
|
|
|
break;
|
|
|
|
case 0x03:
|
|
|
|
comptype = "stereo";
|
|
|
|
break;
|
|
|
|
case 0x04:
|
|
|
|
comptype = "multi-channel multi-lingual";
|
|
|
|
break;
|
|
|
|
case 0x05:
|
|
|
|
comptype = "surround";
|
|
|
|
break;
|
|
|
|
case 0x40:
|
|
|
|
comptype = "audio description for the visually impaired";
|
|
|
|
break;
|
|
|
|
case 0x41:
|
|
|
|
comptype = "audio for the hard of hearing";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
component = gst_structure_new ("audio", "type", G_TYPE_STRING,
|
|
|
|
comptype, NULL);
|
|
|
|
break;
|
|
|
|
case 0x03: /* subtitles/teletext/vbi */
|
|
|
|
comptype = "reserved";
|
|
|
|
switch (DESC_DVB_COMPONENT_type (comp_descriptor)) {
|
|
|
|
case 0x01:
|
|
|
|
comptype = "EBU Teletext subtitles";
|
|
|
|
break;
|
|
|
|
case 0x02:
|
|
|
|
comptype = "associated EBU Teletext";
|
|
|
|
break;
|
|
|
|
case 0x03:
|
|
|
|
comptype = "VBI data";
|
|
|
|
break;
|
|
|
|
case 0x10:
|
|
|
|
comptype = "Normal DVB subtitles";
|
|
|
|
break;
|
|
|
|
case 0x11:
|
|
|
|
comptype = "Normal DVB subtitles for 4:3";
|
|
|
|
break;
|
|
|
|
case 0x12:
|
|
|
|
comptype = "Normal DVB subtitles for 16:9";
|
|
|
|
break;
|
|
|
|
case 0x13:
|
|
|
|
comptype = "Normal DVB subtitles for 2.21:1";
|
|
|
|
break;
|
|
|
|
case 0x20:
|
|
|
|
comptype = "Hard of hearing DVB subtitles";
|
|
|
|
break;
|
|
|
|
case 0x21:
|
|
|
|
comptype = "Hard of hearing DVB subtitles for 4:3";
|
|
|
|
break;
|
|
|
|
case 0x22:
|
|
|
|
comptype = "Hard of hearing DVB subtitles for 16:9";
|
|
|
|
break;
|
|
|
|
case 0x23:
|
|
|
|
comptype = "Hard of hearing DVB subtitles for 2.21:1";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
component = gst_structure_new ("teletext", "type", G_TYPE_STRING,
|
|
|
|
comptype, NULL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (component) {
|
|
|
|
g_value_init (&component_value, GST_TYPE_STRUCTURE);
|
|
|
|
g_value_take_boxed (&component_value, component);
|
|
|
|
gst_value_list_append_value (&components, &component_value);
|
|
|
|
g_value_unset (&component_value);
|
|
|
|
component = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
gst_structure_set_value (event, "components", &components);
|
|
|
|
g_array_free (component_descriptors, TRUE);
|
|
|
|
}
|
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;
|
2008-04-29 09:02:35 +00:00
|
|
|
stream->section_table_id = table_id;
|
2007-08-20 14:23:45 +00:00
|
|
|
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");
|
|
|
|
}
|
2008-06-13 15:46:03 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @text: The text you want to get the encoding from
|
|
|
|
* @start_text: Location where the beginning of the actual text is stored
|
|
|
|
* @is_multibyte: Location where information whether it's a multibyte encoding
|
|
|
|
* or not is stored
|
|
|
|
* @returns: Name of encoding or NULL of encoding could not be detected.
|
|
|
|
*
|
|
|
|
* The returned string should be freed with g_free () when no longer needed.
|
|
|
|
*/
|
|
|
|
static gchar *
|
|
|
|
get_encoding (const gchar * text, guint * start_text, gboolean * is_multibyte)
|
|
|
|
{
|
|
|
|
gchar *encoding;
|
|
|
|
guint8 firstbyte;
|
|
|
|
|
|
|
|
g_return_val_if_fail (text != NULL, NULL);
|
|
|
|
|
|
|
|
firstbyte = (guint8) text[0];
|
|
|
|
|
|
|
|
if (firstbyte == 0x01) {
|
|
|
|
encoding = g_strdup ("iso8859-5");
|
|
|
|
*start_text = 1;
|
|
|
|
*is_multibyte = FALSE;
|
|
|
|
} else if (firstbyte == 0x02) {
|
|
|
|
encoding = g_strdup ("iso8859-6");
|
|
|
|
*start_text = 1;
|
|
|
|
*is_multibyte = FALSE;
|
|
|
|
} else if (firstbyte == 0x03) {
|
|
|
|
encoding = g_strdup ("iso8859-7");
|
|
|
|
*start_text = 1;
|
|
|
|
*is_multibyte = FALSE;
|
|
|
|
} else if (firstbyte == 0x04) {
|
|
|
|
encoding = g_strdup ("iso8859-8");
|
|
|
|
*start_text = 1;
|
|
|
|
*is_multibyte = FALSE;
|
|
|
|
} else if (firstbyte == 0x05) {
|
|
|
|
encoding = g_strdup ("iso8859-9");
|
|
|
|
*start_text = 1;
|
|
|
|
*is_multibyte = FALSE;
|
|
|
|
} else if (firstbyte >= 0x20) {
|
|
|
|
encoding = g_strdup ("iso6937");
|
|
|
|
*start_text = 0;
|
|
|
|
*is_multibyte = FALSE;
|
|
|
|
} else if (firstbyte == 0x10) {
|
|
|
|
guint16 table;
|
|
|
|
gchar table_str[6];
|
|
|
|
|
|
|
|
text++;
|
|
|
|
table = GST_READ_UINT16_BE (text);
|
|
|
|
g_snprintf (table_str, 6, "%d", table);
|
|
|
|
|
|
|
|
encoding = g_strconcat ("iso8859-", table_str, NULL);
|
|
|
|
*start_text = 3;
|
|
|
|
*is_multibyte = FALSE;
|
|
|
|
} else if (firstbyte == 0x11) {
|
|
|
|
encoding = g_strdup ("ISO-10646/UCS2");
|
|
|
|
*start_text = 1;
|
|
|
|
*is_multibyte = TRUE;
|
|
|
|
} else if (firstbyte == 0x12) {
|
|
|
|
// That's korean encoding.
|
|
|
|
// The spec says it's encoded in KSC 5601, but iconv only knows KSC 5636.
|
|
|
|
// Couldn't find any information about either of them.
|
|
|
|
encoding = NULL;
|
|
|
|
*start_text = 1;
|
|
|
|
*is_multibyte = TRUE;
|
|
|
|
} else {
|
|
|
|
// reserved
|
|
|
|
encoding = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return encoding;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @text: The text to convert. It may include pango markup (<b> and </b>)
|
|
|
|
* @length: The length of the string -1 if it's nul-terminated
|
|
|
|
* @start: Where to start converting in the text
|
|
|
|
* @encoding: The encoding of text
|
|
|
|
* @is_multibyte: Whether the encoding is a multibyte encoding
|
|
|
|
* @error: The location to store the error, or NULL to ignore errors
|
|
|
|
* @returns: UTF-8 encoded string
|
|
|
|
*
|
|
|
|
* Convert text to UTF-8.
|
|
|
|
*/
|
|
|
|
static gchar *
|
|
|
|
convert_to_utf8 (const gchar * text, gint length, guint start,
|
|
|
|
const gchar * encoding, gboolean is_multibyte, GError ** error)
|
|
|
|
{
|
|
|
|
gchar *new_text;
|
|
|
|
GByteArray *sb;
|
|
|
|
gint i;
|
|
|
|
|
|
|
|
g_return_val_if_fail (text != NULL, NULL);
|
|
|
|
g_return_val_if_fail (encoding != NULL, NULL);
|
|
|
|
|
|
|
|
text += start;
|
|
|
|
|
|
|
|
sb = g_byte_array_sized_new (length * 1.1);
|
|
|
|
|
|
|
|
if (is_multibyte) {
|
|
|
|
if (length == -1) {
|
|
|
|
while (*text != '\0') {
|
|
|
|
guint16 code = GST_READ_UINT16_BE (text);
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
case 0xE086:{
|
|
|
|
guint8 emph_on[] = { 0x3C, 0x00, // <
|
|
|
|
0x62, 0x00, // b
|
|
|
|
0x3E, 0x00 // >
|
|
|
|
};
|
|
|
|
g_byte_array_append (sb, emph_on, 6);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0xE087:{
|
|
|
|
guint8 emph_on[] = { 0x3C, 0x00, // <
|
|
|
|
0x2F, 0x00, // /
|
|
|
|
0x62, 0x00, // b
|
|
|
|
0x3E, 0x00 // >
|
|
|
|
};
|
|
|
|
g_byte_array_append (sb, emph_on, 8);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0xE08A:{
|
|
|
|
guint8 nl[] = { 0x0A, 0x00 }; // new line
|
|
|
|
g_byte_array_append (sb, nl, 2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
g_byte_array_append (sb, (guint8 *) text, 2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
text += 2;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < length; i += 2) {
|
|
|
|
guint16 code = GST_READ_UINT16_BE (text);
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
case 0xE086:{
|
|
|
|
guint8 emph_on[] = { 0x3C, 0x00, // <
|
|
|
|
0x62, 0x00, // b
|
|
|
|
0x3E, 0x00 // >
|
|
|
|
};
|
|
|
|
g_byte_array_append (sb, emph_on, 6);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0xE087:{
|
|
|
|
guint8 emph_on[] = { 0x3C, 0x00, // <
|
|
|
|
0x2F, 0x00, // /
|
|
|
|
0x62, 0x00, // b
|
|
|
|
0x3E, 0x00 // >
|
|
|
|
};
|
|
|
|
g_byte_array_append (sb, emph_on, 8);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 0xE08A:{
|
|
|
|
guint8 nl[] = { 0x0A, 0x00 }; // new line
|
|
|
|
g_byte_array_append (sb, nl, 2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
g_byte_array_append (sb, (guint8 *) text, 2);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
text += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (length == -1) {
|
|
|
|
while (*text != '\0') {
|
|
|
|
guint8 code = (guint8) (*text);
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
case 0x86:
|
|
|
|
g_byte_array_append (sb, (guint8 *) "<b>", 3);
|
|
|
|
break;
|
|
|
|
case 0x87:
|
|
|
|
g_byte_array_append (sb, (guint8 *) "</b>", 4);
|
|
|
|
break;
|
|
|
|
case 0x8A:
|
|
|
|
g_byte_array_append (sb, (guint8 *) "\n", 1);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_byte_array_append (sb, &code, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
text++;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (i = 0; i < length; i++) {
|
|
|
|
guint8 code = (guint8) (*text);
|
|
|
|
|
|
|
|
switch (code) {
|
|
|
|
case 0x86:
|
|
|
|
g_byte_array_append (sb, (guint8 *) "<b>", 3);
|
|
|
|
break;
|
|
|
|
case 0x87:
|
|
|
|
g_byte_array_append (sb, (guint8 *) "</b>", 4);
|
|
|
|
break;
|
|
|
|
case 0x8A:
|
|
|
|
g_byte_array_append (sb, (guint8 *) "\n", 1);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
g_byte_array_append (sb, &code, 1);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
text++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
new_text =
|
|
|
|
g_convert ((gchar *) sb->data, sb->len, "utf-8", encoding, NULL, NULL,
|
|
|
|
error);
|
|
|
|
|
|
|
|
g_byte_array_free (sb, TRUE);
|
|
|
|
|
|
|
|
return new_text;
|
|
|
|
}
|
|
|
|
|
|
|
|
static gchar *
|
|
|
|
get_encoding_and_convert (const gchar * text, guint length)
|
|
|
|
{
|
|
|
|
GError *error = NULL;
|
|
|
|
gchar *converted_str;
|
|
|
|
gchar *encoding;
|
|
|
|
guint start_text = 0;
|
|
|
|
gboolean is_multibyte;
|
|
|
|
|
|
|
|
encoding = get_encoding (text, &start_text, &is_multibyte);
|
|
|
|
|
|
|
|
if (encoding == NULL) {
|
|
|
|
converted_str = g_strndup (text, length);
|
|
|
|
} else {
|
|
|
|
converted_str = convert_to_utf8 (text, length - start_text, start_text,
|
|
|
|
encoding, is_multibyte, &error);
|
|
|
|
if (error != NULL) {
|
|
|
|
g_critical ("Could not convert string: %s", error->message);
|
|
|
|
g_error_free (error);
|
|
|
|
text += start_text;
|
|
|
|
converted_str = g_strndup (text, length - start_text);
|
|
|
|
}
|
|
|
|
|
|
|
|
g_free (encoding);
|
|
|
|
}
|
|
|
|
|
|
|
|
return converted_str;
|
|
|
|
}
|