mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-23 16:50:47 +00:00
mpegts: Add support for SCTE-35 sections
Not all commands are supported, but the most common ones are. Both parsing and packetizing is supported
This commit is contained in:
parent
5464775aef
commit
6a9108884c
8 changed files with 1021 additions and 8 deletions
659
gst-libs/gst/mpegts/gst-scte-section.c
Normal file
659
gst-libs/gst/mpegts/gst-scte-section.c
Normal file
|
@ -0,0 +1,659 @@
|
||||||
|
/*
|
||||||
|
* gst-scte-section.c -
|
||||||
|
* Copyright (C) 2019 Centricular ltd
|
||||||
|
* Author: Edward Hervey <edward@centricular.com>
|
||||||
|
*
|
||||||
|
* 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., 51 Franklin St, Fifth Floor,
|
||||||
|
* Boston, MA 02110-1301, USA.
|
||||||
|
*/
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "mpegts.h"
|
||||||
|
#include "gstmpegts-private.h"
|
||||||
|
#define MPEGTIME_TO_GSTTIME(t) ((t) * (guint64)100000 / 9)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION:gst-scte-section
|
||||||
|
* @title: SCTE variants of MPEG-TS sections
|
||||||
|
* @short_description: Sections for the various SCTE specifications
|
||||||
|
* @include: gst/mpegts/mpegts.h
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Splice Information Table (SIT) */
|
||||||
|
|
||||||
|
static GstMpegtsSCTESpliceEvent *
|
||||||
|
_gst_mpegts_scte_splice_event_copy (GstMpegtsSCTESpliceEvent * event)
|
||||||
|
{
|
||||||
|
return g_slice_dup (GstMpegtsSCTESpliceEvent, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_gst_mpegts_scte_splice_event_free (GstMpegtsSCTESpliceEvent * event)
|
||||||
|
{
|
||||||
|
g_slice_free (GstMpegtsSCTESpliceEvent, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_BOXED_TYPE (GstMpegtsSCTESpliceEvent, gst_mpegts_scte_splice_event,
|
||||||
|
(GBoxedCopyFunc) _gst_mpegts_scte_splice_event_copy,
|
||||||
|
(GFreeFunc) _gst_mpegts_scte_splice_event_free);
|
||||||
|
|
||||||
|
static GstMpegtsSCTESpliceEvent *
|
||||||
|
_parse_slice_event (guint8 ** orig_data, guint8 * end, gboolean insert_event)
|
||||||
|
{
|
||||||
|
GstMpegtsSCTESpliceEvent *event = g_slice_new0 (GstMpegtsSCTESpliceEvent);
|
||||||
|
guint8 *data = *orig_data;
|
||||||
|
|
||||||
|
/* Note : +6 is because of the final descriptor_loop_length and CRC */
|
||||||
|
if (data + 5 + 6 > end)
|
||||||
|
goto error;
|
||||||
|
|
||||||
|
event->insert_event = insert_event;
|
||||||
|
event->splice_event_id = GST_READ_UINT32_BE (data);
|
||||||
|
GST_LOG ("splice_event_id: 0x%08x", event->splice_event_id);
|
||||||
|
data += 4;
|
||||||
|
event->splice_event_cancel_indicator = *data >> 7;
|
||||||
|
GST_LOG ("splice_event_cancel_indicator: %d",
|
||||||
|
event->splice_event_cancel_indicator);
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
GST_DEBUG ("data %p", data);
|
||||||
|
|
||||||
|
if (event->splice_event_cancel_indicator == 0) {
|
||||||
|
if (data + 5 + 6 > end)
|
||||||
|
goto error;
|
||||||
|
event->out_of_network_indicator = *data >> 7;
|
||||||
|
event->program_splice_flag = (*data >> 6) & 0x01;
|
||||||
|
event->duration_flag = (*data >> 5) & 0x01;
|
||||||
|
event->splice_immediate_flag = (*data >> 4) & 0x01;
|
||||||
|
GST_LOG ("out_of_network_indicator:%d", event->out_of_network_indicator);
|
||||||
|
GST_LOG ("program_splice_flag:%d", event->program_splice_flag);
|
||||||
|
GST_LOG ("duration_flag:%d", event->duration_flag);
|
||||||
|
GST_LOG ("splice_immediate_flag:%d", event->splice_immediate_flag);
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
if (event->program_splice_flag == 0) {
|
||||||
|
GST_ERROR ("Component splice flag not supported !");
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->splice_immediate_flag == 0) {
|
||||||
|
event->program_splice_time_specified = *data >> 7;
|
||||||
|
if (event->program_splice_time_specified) {
|
||||||
|
event->program_splice_time = ((guint64) (*data & 0x01)) << 32;
|
||||||
|
data += 1;
|
||||||
|
event->program_splice_time += GST_READ_UINT32_BE (data);
|
||||||
|
data += 4;
|
||||||
|
GST_LOG ("program_splice_time %" G_GUINT64_FORMAT " (%" GST_TIME_FORMAT
|
||||||
|
")", event->program_splice_time,
|
||||||
|
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (event->program_splice_time)));
|
||||||
|
} else
|
||||||
|
data += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event->duration_flag) {
|
||||||
|
event->break_duration_auto_return = *data >> 7;
|
||||||
|
event->break_duration = ((guint64) (*data & 0x01)) << 32;
|
||||||
|
data += 1;
|
||||||
|
event->break_duration += GST_READ_UINT32_BE (data);
|
||||||
|
data += 4;
|
||||||
|
GST_LOG ("break_duration_auto_return:%d",
|
||||||
|
event->break_duration_auto_return);
|
||||||
|
GST_LOG ("break_duration %" G_GUINT64_FORMAT " (%" GST_TIME_FORMAT ")",
|
||||||
|
event->break_duration,
|
||||||
|
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (event->break_duration)));
|
||||||
|
}
|
||||||
|
|
||||||
|
event->unique_program_id = GST_READ_UINT16_BE (data);
|
||||||
|
GST_LOG ("unique_program_id:%" G_GUINT16_FORMAT, event->unique_program_id);
|
||||||
|
data += 2;
|
||||||
|
event->avail_num = *data++;
|
||||||
|
event->avails_expected = *data++;
|
||||||
|
GST_LOG ("avail %d/%d", event->avail_num, event->avails_expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_DEBUG ("done");
|
||||||
|
*orig_data = data;
|
||||||
|
return event;
|
||||||
|
|
||||||
|
error:
|
||||||
|
{
|
||||||
|
if (event)
|
||||||
|
_gst_mpegts_scte_splice_event_free (event);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static GstMpegtsSCTESIT *
|
||||||
|
_gst_mpegts_scte_sit_copy (GstMpegtsSCTESIT * sit)
|
||||||
|
{
|
||||||
|
GstMpegtsSCTESIT *copy = g_slice_dup (GstMpegtsSCTESIT, sit);
|
||||||
|
|
||||||
|
copy->splices = g_ptr_array_ref (sit->splices);
|
||||||
|
copy->descriptors = g_ptr_array_ref (sit->descriptors);
|
||||||
|
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
_gst_mpegts_scte_sit_free (GstMpegtsSCTESIT * sit)
|
||||||
|
{
|
||||||
|
g_ptr_array_unref (sit->splices);
|
||||||
|
g_ptr_array_unref (sit->descriptors);
|
||||||
|
g_slice_free (GstMpegtsSCTESIT, sit);
|
||||||
|
}
|
||||||
|
|
||||||
|
G_DEFINE_BOXED_TYPE (GstMpegtsSCTESIT, gst_mpegts_scte_sit,
|
||||||
|
(GBoxedCopyFunc) _gst_mpegts_scte_sit_copy,
|
||||||
|
(GFreeFunc) _gst_mpegts_scte_sit_free);
|
||||||
|
|
||||||
|
|
||||||
|
static gpointer
|
||||||
|
_parse_sit (GstMpegtsSection * section)
|
||||||
|
{
|
||||||
|
GstMpegtsSCTESIT *sit = NULL;
|
||||||
|
guint8 *data, *end;
|
||||||
|
guint32 tmp;
|
||||||
|
|
||||||
|
GST_DEBUG ("SIT");
|
||||||
|
|
||||||
|
/* Even if the section is not a short one, it still uses CRC */
|
||||||
|
if (_calc_crc32 (section->data, section->section_length) != 0) {
|
||||||
|
GST_WARNING ("PID:0x%04x table_id:0x%02x, Bad CRC on section", section->pid,
|
||||||
|
section->table_id);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
sit = g_slice_new0 (GstMpegtsSCTESIT);
|
||||||
|
|
||||||
|
data = section->data;
|
||||||
|
end = data + section->section_length;
|
||||||
|
|
||||||
|
GST_MEMDUMP ("section", data, section->section_length);
|
||||||
|
|
||||||
|
/* Skip already-checked fields */
|
||||||
|
data += 3;
|
||||||
|
|
||||||
|
/* Ensure protocol_version is 0 */
|
||||||
|
if (*data != 0) {
|
||||||
|
GST_WARNING ("Unsupported SCTE SIT protocol version (%d)", *data);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
/* encryption */
|
||||||
|
sit->encrypted_packet = (*data) >> 7;
|
||||||
|
sit->encryption_algorithm = (*data) & 0x3f;
|
||||||
|
sit->pts_adjustment = ((guint64) (*data & 0x01)) << 32;
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
sit->pts_adjustment += GST_READ_UINT32_BE (data);
|
||||||
|
data += 4;
|
||||||
|
|
||||||
|
sit->cw_index = *data;
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
tmp = GST_READ_UINT24_BE (data);
|
||||||
|
data += 3;
|
||||||
|
|
||||||
|
sit->tier = (tmp >> 12);
|
||||||
|
sit->splice_command_length = tmp & 0xfff;
|
||||||
|
/* 0xfff is for backwards compatibility when reading */
|
||||||
|
if (sit->splice_command_length == 0xfff)
|
||||||
|
sit->splice_command_length = 0;
|
||||||
|
GST_LOG ("command length %d", sit->splice_command_length);
|
||||||
|
sit->splice_command_type = *data;
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
if (sit->splice_command_length
|
||||||
|
&& (data + sit->splice_command_length > end - 4)) {
|
||||||
|
GST_WARNING ("PID %d invalid SCTE SIT splice command length %d",
|
||||||
|
section->pid, sit->splice_command_length);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
sit->splices = g_ptr_array_new_with_free_func ((GDestroyNotify)
|
||||||
|
_gst_mpegts_scte_splice_event_free);
|
||||||
|
switch (sit->splice_command_type) {
|
||||||
|
case GST_MTS_SCTE_SPLICE_COMMAND_NULL:
|
||||||
|
case GST_MTS_SCTE_SPLICE_COMMAND_BANDWIDTH:
|
||||||
|
/* These commands have no payload */
|
||||||
|
break;
|
||||||
|
case GST_MTS_SCTE_SPLICE_COMMAND_TIME:
|
||||||
|
{
|
||||||
|
sit->splice_time_specified = (*data >> 7);
|
||||||
|
if (sit->splice_time_specified) {
|
||||||
|
sit->splice_time = ((guint64) (*data & 0x01)) << 32;
|
||||||
|
data += 1;
|
||||||
|
sit->splice_time += GST_READ_UINT32_BE (data);
|
||||||
|
data += 4;
|
||||||
|
} else
|
||||||
|
data += 1;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_MTS_SCTE_SPLICE_COMMAND_INSERT:
|
||||||
|
{
|
||||||
|
GstMpegtsSCTESpliceEvent *event = _parse_slice_event (&data, end, TRUE);
|
||||||
|
if (event == NULL)
|
||||||
|
goto error;
|
||||||
|
g_ptr_array_add (sit->splices, event);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GST_MTS_SCTE_SPLICE_COMMAND_PRIVATE:
|
||||||
|
{
|
||||||
|
GST_FIXME ("Implement SCTE-35 private commands");
|
||||||
|
data += sit->splice_command_length;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
GST_ERROR ("Unknown SCTE splice command type (0x%02x) !",
|
||||||
|
sit->splice_command_type);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* descriptors */
|
||||||
|
tmp = GST_READ_UINT16_BE (data);
|
||||||
|
data += 2;
|
||||||
|
GST_MEMDUMP ("desc ?", data, tmp);
|
||||||
|
sit->descriptors = gst_mpegts_parse_descriptors (data, tmp);
|
||||||
|
if (!sit->descriptors) {
|
||||||
|
GST_DEBUG ("no descriptors %d", tmp);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
data += tmp;
|
||||||
|
|
||||||
|
|
||||||
|
GST_DEBUG ("%p - %p", data, end);
|
||||||
|
if (data != end - 4) {
|
||||||
|
GST_WARNING ("PID %d invalid SIT parsed %d length %d",
|
||||||
|
section->pid, (gint) (data - section->data), section->section_length);
|
||||||
|
goto error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sit;
|
||||||
|
|
||||||
|
error:
|
||||||
|
if (sit)
|
||||||
|
_gst_mpegts_scte_sit_free (sit);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_mpegts_section_get_scte_sit:
|
||||||
|
* @section: a #GstMpegtsSection of type %GST_MPEGTS_SECTION_SCTE_SIT
|
||||||
|
*
|
||||||
|
* Returns the #GstMpegtsSCTESIT contained in the @section.
|
||||||
|
*
|
||||||
|
* Returns: The #GstMpegtsSCTESIT contained in the section, or %NULL if an error
|
||||||
|
* happened.
|
||||||
|
*/
|
||||||
|
const GstMpegtsSCTESIT *
|
||||||
|
gst_mpegts_section_get_scte_sit (GstMpegtsSection * section)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (section->section_type == GST_MPEGTS_SECTION_SCTE_SIT,
|
||||||
|
NULL);
|
||||||
|
g_return_val_if_fail (section->cached_parsed || section->data, NULL);
|
||||||
|
|
||||||
|
if (!section->cached_parsed)
|
||||||
|
section->cached_parsed =
|
||||||
|
__common_section_checks (section, 18, _parse_sit,
|
||||||
|
(GDestroyNotify) _gst_mpegts_scte_sit_free);
|
||||||
|
|
||||||
|
return (const GstMpegtsSCTESIT *) section->cached_parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_mpegts_scte_sit_new:
|
||||||
|
*
|
||||||
|
* Allocates and initializes a #GstMpegtsSCTESIT.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): A newly allocated #GstMpegtsSCTESIT
|
||||||
|
*/
|
||||||
|
GstMpegtsSCTESIT *
|
||||||
|
gst_mpegts_scte_sit_new (void)
|
||||||
|
{
|
||||||
|
GstMpegtsSCTESIT *sit;
|
||||||
|
|
||||||
|
sit = g_slice_new0 (GstMpegtsSCTESIT);
|
||||||
|
|
||||||
|
/* Set all default values (which aren't already 0/NULL) */
|
||||||
|
sit->tier = 0xfff;
|
||||||
|
|
||||||
|
sit->splices = g_ptr_array_new_with_free_func ((GDestroyNotify)
|
||||||
|
_gst_mpegts_scte_splice_event_free);
|
||||||
|
sit->descriptors = g_ptr_array_new_with_free_func ((GDestroyNotify)
|
||||||
|
gst_mpegts_descriptor_free);
|
||||||
|
|
||||||
|
return sit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_mpegts_scte_null_new:
|
||||||
|
*
|
||||||
|
* Allocates and initializes a NULL command #GstMpegtsSCTESIT.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): A newly allocated #GstMpegtsSCTESIT
|
||||||
|
*/
|
||||||
|
GstMpegtsSCTESIT *
|
||||||
|
gst_mpegts_scte_null_new (void)
|
||||||
|
{
|
||||||
|
GstMpegtsSCTESIT *sit = gst_mpegts_scte_sit_new ();
|
||||||
|
|
||||||
|
sit->splice_command_type = GST_MTS_SCTE_SPLICE_COMMAND_NULL;
|
||||||
|
return sit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_mpegts_scte_cancel_new:
|
||||||
|
* @event_id: The event ID to cancel.
|
||||||
|
*
|
||||||
|
* Allocates and initializes a new INSERT command #GstMpegtsSCTESIT
|
||||||
|
* setup to cancel the specified @event_id.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): A newly allocated #GstMpegtsSCTESIT
|
||||||
|
*/
|
||||||
|
GstMpegtsSCTESIT *
|
||||||
|
gst_mpegts_scte_cancel_new (guint32 event_id)
|
||||||
|
{
|
||||||
|
GstMpegtsSCTESIT *sit = gst_mpegts_scte_sit_new ();
|
||||||
|
GstMpegtsSCTESpliceEvent *event = gst_mpegts_scte_splice_event_new ();
|
||||||
|
|
||||||
|
sit->splice_command_type = GST_MTS_SCTE_SPLICE_COMMAND_INSERT;
|
||||||
|
event->splice_event_id = event_id;
|
||||||
|
event->splice_event_cancel_indicator = TRUE;
|
||||||
|
g_ptr_array_add (sit->splices, event);
|
||||||
|
|
||||||
|
return sit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_mpegts_scte_splice_in_new:
|
||||||
|
* @event_id: The event ID.
|
||||||
|
* @splice_time: The PCR time for the splice event
|
||||||
|
*
|
||||||
|
* Allocates and initializes a new "Splice In" INSERT command
|
||||||
|
* #GstMpegtsSCTESIT for the given @event_id and @splice_time.
|
||||||
|
*
|
||||||
|
* If the @splice_time is #G_MAXUINT64 then the event will be
|
||||||
|
* immediate as opposed to for the target @splice_time.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): A newly allocated #GstMpegtsSCTESIT
|
||||||
|
*/
|
||||||
|
GstMpegtsSCTESIT *
|
||||||
|
gst_mpegts_scte_splice_in_new (guint32 event_id, guint64 splice_time)
|
||||||
|
{
|
||||||
|
GstMpegtsSCTESIT *sit = gst_mpegts_scte_sit_new ();
|
||||||
|
GstMpegtsSCTESpliceEvent *event = gst_mpegts_scte_splice_event_new ();
|
||||||
|
|
||||||
|
sit->splice_command_type = GST_MTS_SCTE_SPLICE_COMMAND_INSERT;
|
||||||
|
event->splice_event_id = event_id;
|
||||||
|
if (splice_time == G_MAXUINT64) {
|
||||||
|
event->splice_immediate_flag = TRUE;
|
||||||
|
} else {
|
||||||
|
event->program_splice_time_specified = TRUE;
|
||||||
|
event->program_splice_time = splice_time;
|
||||||
|
}
|
||||||
|
g_ptr_array_add (sit->splices, event);
|
||||||
|
|
||||||
|
return sit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_mpegts_scte_splice_out_new:
|
||||||
|
* @event_id: The event ID.
|
||||||
|
* @splice_time: The PCR time for the splice event
|
||||||
|
* @duration: The optional duration.
|
||||||
|
*
|
||||||
|
* Allocates and initializes a new "Splice Out" INSERT command
|
||||||
|
* #GstMpegtsSCTESIT for the given @event_id, @splice_time and
|
||||||
|
* duration.
|
||||||
|
*
|
||||||
|
* If the @splice_time is #G_MAXUINT64 then the event will be
|
||||||
|
* immediate as opposed to for the target @splice_time.
|
||||||
|
*
|
||||||
|
* If the @duration is 0 it won't be specified in the event.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): A newly allocated #GstMpegtsSCTESIT
|
||||||
|
*/
|
||||||
|
GstMpegtsSCTESIT *
|
||||||
|
gst_mpegts_scte_splice_out_new (guint32 event_id, guint64 splice_time,
|
||||||
|
guint64 duration)
|
||||||
|
{
|
||||||
|
GstMpegtsSCTESIT *sit = gst_mpegts_scte_sit_new ();
|
||||||
|
GstMpegtsSCTESpliceEvent *event = gst_mpegts_scte_splice_event_new ();
|
||||||
|
|
||||||
|
sit->splice_command_type = GST_MTS_SCTE_SPLICE_COMMAND_INSERT;
|
||||||
|
event->splice_event_id = event_id;
|
||||||
|
event->out_of_network_indicator = TRUE;
|
||||||
|
if (splice_time == G_MAXUINT64) {
|
||||||
|
event->splice_immediate_flag = TRUE;
|
||||||
|
} else {
|
||||||
|
event->program_splice_time_specified = TRUE;
|
||||||
|
event->program_splice_time = splice_time;
|
||||||
|
}
|
||||||
|
if (duration != 0) {
|
||||||
|
event->duration_flag = TRUE;
|
||||||
|
event->break_duration = duration;
|
||||||
|
}
|
||||||
|
g_ptr_array_add (sit->splices, event);
|
||||||
|
|
||||||
|
return sit;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_mpegts_scte_splice_event_new:
|
||||||
|
*
|
||||||
|
* Allocates and initializes a #GstMpegtsSCTESpliceEvent.
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): A newly allocated #GstMpegtsSCTESpliceEvent
|
||||||
|
*/
|
||||||
|
GstMpegtsSCTESpliceEvent *
|
||||||
|
gst_mpegts_scte_splice_event_new (void)
|
||||||
|
{
|
||||||
|
GstMpegtsSCTESpliceEvent *event = g_slice_new0 (GstMpegtsSCTESpliceEvent);
|
||||||
|
|
||||||
|
/* Non-0 Default values */
|
||||||
|
event->program_splice_flag = TRUE;
|
||||||
|
|
||||||
|
return event;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
_packetize_sit (GstMpegtsSection * section)
|
||||||
|
{
|
||||||
|
gsize length, command_length, descriptor_length;
|
||||||
|
const GstMpegtsSCTESIT *sit;
|
||||||
|
GstMpegtsDescriptor *descriptor;
|
||||||
|
guint32 tmp32;
|
||||||
|
guint i;
|
||||||
|
guint8 *data;
|
||||||
|
|
||||||
|
sit = gst_mpegts_section_get_scte_sit (section);
|
||||||
|
|
||||||
|
if (sit == NULL)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Skip cases we don't handle for now */
|
||||||
|
if (sit->encrypted_packet) {
|
||||||
|
GST_WARNING ("SCTE encrypted packet is not supported");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (sit->splice_command_type) {
|
||||||
|
case GST_MTS_SCTE_SPLICE_COMMAND_SCHEDULE:
|
||||||
|
case GST_MTS_SCTE_SPLICE_COMMAND_TIME:
|
||||||
|
case GST_MTS_SCTE_SPLICE_COMMAND_PRIVATE:
|
||||||
|
GST_WARNING ("SCTE command not supported");
|
||||||
|
return FALSE;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Smallest splice section are the NULL and bandwith command:
|
||||||
|
* 14 bytes for the header
|
||||||
|
* 0 bytes for the command
|
||||||
|
* 2 bytes for the empty descriptor loop length
|
||||||
|
* 4 bytes for the CRC */
|
||||||
|
length = 20;
|
||||||
|
|
||||||
|
command_length = 0;
|
||||||
|
/* Add the size of splices */
|
||||||
|
for (i = 0; i < sit->splices->len; i++) {
|
||||||
|
GstMpegtsSCTESpliceEvent *event = g_ptr_array_index (sit->splices, i);
|
||||||
|
/* There is at least 5 bytes */
|
||||||
|
command_length += 5;
|
||||||
|
if (!event->splice_event_cancel_indicator) {
|
||||||
|
if (!event->program_splice_flag) {
|
||||||
|
GST_WARNING ("Only SCTE program splices are supported");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
/* Add at least 5 bytes for common fields */
|
||||||
|
command_length += 5;
|
||||||
|
if (!event->splice_immediate_flag) {
|
||||||
|
if (event->program_splice_time_specified)
|
||||||
|
command_length += 5;
|
||||||
|
else
|
||||||
|
command_length += 1;
|
||||||
|
}
|
||||||
|
if (event->duration_flag)
|
||||||
|
command_length += 5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
length += command_length;
|
||||||
|
|
||||||
|
/* Calculate size of descriptors */
|
||||||
|
|
||||||
|
descriptor_length = 0;
|
||||||
|
for (i = 0; i < sit->descriptors->len; i++) {
|
||||||
|
descriptor = g_ptr_array_index (sit->descriptors, i);
|
||||||
|
descriptor_length += descriptor->length + 2;
|
||||||
|
}
|
||||||
|
length += descriptor_length;
|
||||||
|
|
||||||
|
/* Max length of SIT section is 4093 bytes */
|
||||||
|
g_return_val_if_fail (length <= 4093, FALSE);
|
||||||
|
|
||||||
|
_packetize_common_section (section, length);
|
||||||
|
|
||||||
|
data = section->data + 3;
|
||||||
|
/* Protocol version (default 0) */
|
||||||
|
*data++ = 0;
|
||||||
|
/* 7bits for encryption (not supported : 0) */
|
||||||
|
/* 33bits for pts_adjustment */
|
||||||
|
*data++ = (sit->pts_adjustment) >> 32 & 0x01;
|
||||||
|
GST_WRITE_UINT32_BE (data, sit->pts_adjustment & 0xffffffff);
|
||||||
|
data += 4;
|
||||||
|
/* cw_index : 8 bit */
|
||||||
|
*data++ = sit->cw_index;
|
||||||
|
/* tier : 12bit
|
||||||
|
* splice_command_length : 12bit
|
||||||
|
* splice_command_type : 8 bit */
|
||||||
|
tmp32 = (sit->tier & 0xfff) << 20;
|
||||||
|
tmp32 |= (command_length & 0xfff) << 8;
|
||||||
|
tmp32 |= sit->splice_command_type;
|
||||||
|
GST_WRITE_UINT32_BE (data, tmp32);
|
||||||
|
data += 4;
|
||||||
|
|
||||||
|
/* Write the events */
|
||||||
|
for (i = 0; i < sit->splices->len; i++) {
|
||||||
|
GstMpegtsSCTESpliceEvent *event = g_ptr_array_index (sit->splices, i);
|
||||||
|
|
||||||
|
/* splice_event_id : 32bit */
|
||||||
|
GST_WRITE_UINT32_BE (data, event->splice_event_id);
|
||||||
|
data += 4;
|
||||||
|
/* splice_event_cancel_indicator : 1bit
|
||||||
|
* reserved ; 7bit */
|
||||||
|
*data++ = event->splice_event_cancel_indicator ? 0xff : 0x7f;
|
||||||
|
|
||||||
|
if (!event->splice_event_cancel_indicator) {
|
||||||
|
/* out_of_network_indicator : 1bit
|
||||||
|
* program_splice_flag : 1bit
|
||||||
|
* duration_flag : 1bit
|
||||||
|
* splice_immediate_flag : 1bit
|
||||||
|
* reserved : 4bit */
|
||||||
|
*data++ = (event->out_of_network_indicator << 7) |
|
||||||
|
(event->program_splice_flag << 6) |
|
||||||
|
(event->duration_flag << 5) |
|
||||||
|
(event->splice_immediate_flag << 4) | 0x0f;
|
||||||
|
if (!event->splice_immediate_flag) {
|
||||||
|
/* program_splice_time_specified : 1bit
|
||||||
|
* reserved : 6/7 bit */
|
||||||
|
if (!event->program_splice_time_specified)
|
||||||
|
*data++ = 0x7f;
|
||||||
|
else {
|
||||||
|
/* time : 33bit */
|
||||||
|
*data++ = 0xf2 | ((event->program_splice_time >> 32) & 0x1);
|
||||||
|
GST_WRITE_UINT32_BE (data, event->program_splice_time & 0xffffffff);
|
||||||
|
data += 4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (event->duration_flag) {
|
||||||
|
*data = event->break_duration_auto_return ? 0xfe : 0x7e;
|
||||||
|
*data++ |= (event->break_duration >> 32) & 0x1;
|
||||||
|
GST_WRITE_UINT32_BE (data, event->break_duration & 0xffffffff);
|
||||||
|
data += 4;
|
||||||
|
}
|
||||||
|
/* unique_program_id : 16bit */
|
||||||
|
GST_WRITE_UINT16_BE (data, event->unique_program_id);
|
||||||
|
data += 2;
|
||||||
|
/* avail_num : 8bit */
|
||||||
|
*data++ = event->avail_num;
|
||||||
|
/* avails_expected : 8bit */
|
||||||
|
*data++ = event->avails_expected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Descriptors */
|
||||||
|
GST_WRITE_UINT16_BE (data, descriptor_length);
|
||||||
|
data += 2;
|
||||||
|
_packetize_descriptor_array (sit->descriptors, &data);
|
||||||
|
|
||||||
|
/* CALCULATE AND WRITE THE CRC ! */
|
||||||
|
GST_WRITE_UINT32_BE (data, _calc_crc32 (section->data, data - section->data));
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_mpegts_section_from_scte_sit:
|
||||||
|
* @sit: (transfer full): a #GstMpegtsSCTESIT to create the #GstMpegtsSection from
|
||||||
|
*
|
||||||
|
* Ownership of @sit is taken. The data in @sit is managed by the #GstMpegtsSection
|
||||||
|
*
|
||||||
|
* Returns: (transfer full): the #GstMpegtsSection
|
||||||
|
*/
|
||||||
|
GstMpegtsSection *
|
||||||
|
gst_mpegts_section_from_scte_sit (GstMpegtsSCTESIT * sit, guint16 pid)
|
||||||
|
{
|
||||||
|
GstMpegtsSection *section;
|
||||||
|
|
||||||
|
g_return_val_if_fail (sit != NULL, NULL);
|
||||||
|
|
||||||
|
section = _gst_mpegts_section_init (pid, GST_MTS_TABLE_ID_SCTE_SPLICE);
|
||||||
|
|
||||||
|
section->short_section = TRUE;
|
||||||
|
section->cached_parsed = (gpointer) sit;
|
||||||
|
section->packetizer = _packetize_sit;
|
||||||
|
section->destroy_parsed = (GDestroyNotify) _gst_mpegts_scte_sit_free;
|
||||||
|
section->short_section = TRUE;
|
||||||
|
|
||||||
|
return section;
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ G_BEGIN_DECLS
|
||||||
* GstMpegtsScteStreamType:
|
* GstMpegtsScteStreamType:
|
||||||
* @GST_MPEGTS_STREAM_TYPE_SCTE_SUBTITLING: SCTE-27 Subtitling
|
* @GST_MPEGTS_STREAM_TYPE_SCTE_SUBTITLING: SCTE-27 Subtitling
|
||||||
* @GST_MPEGTS_STREAM_TYPE_SCTE_ISOCH_DATA: SCTE-19 Isochronous data
|
* @GST_MPEGTS_STREAM_TYPE_SCTE_ISOCH_DATA: SCTE-19 Isochronous data
|
||||||
|
* @GST_MPEGTS_STREAM_TYPE_SCTE_SIT: SCTE-35 Splice Information Table
|
||||||
* @GST_MPEGTS_STREAM_TYPE_SCTE_DST_NRT: SCTE-07 Data Service or
|
* @GST_MPEGTS_STREAM_TYPE_SCTE_DST_NRT: SCTE-07 Data Service or
|
||||||
* Network Resource Table
|
* Network Resource Table
|
||||||
* @GST_MPEGTS_STREAM_TYPE_SCTE_DSMCC_DCB: Type B - DSM-CC Data Carousel
|
* @GST_MPEGTS_STREAM_TYPE_SCTE_DSMCC_DCB: Type B - DSM-CC Data Carousel
|
||||||
|
@ -51,7 +52,9 @@ typedef enum {
|
||||||
/* 0x01 - 0x82 : defined in other specs */
|
/* 0x01 - 0x82 : defined in other specs */
|
||||||
GST_MPEGTS_STREAM_TYPE_SCTE_SUBTITLING = 0x82, /* Subtitling data */
|
GST_MPEGTS_STREAM_TYPE_SCTE_SUBTITLING = 0x82, /* Subtitling data */
|
||||||
GST_MPEGTS_STREAM_TYPE_SCTE_ISOCH_DATA = 0x83, /* Isochronous data */
|
GST_MPEGTS_STREAM_TYPE_SCTE_ISOCH_DATA = 0x83, /* Isochronous data */
|
||||||
/* 0x84 - 0x94 : defined in other specs */
|
/* 0x84 - 0x85 : defined in other specs */
|
||||||
|
GST_MPEGTS_STREAM_TYPE_SCTE_SIT = 0x86, /* Splice Information Table */
|
||||||
|
/* 0x87 - 0x94 : defined in other specs */
|
||||||
GST_MPEGTS_STREAM_TYPE_SCTE_DST_NRT = 0x95, /* DST / NRT data */
|
GST_MPEGTS_STREAM_TYPE_SCTE_DST_NRT = 0x95, /* DST / NRT data */
|
||||||
/* 0x96 - 0xaf : defined in other specs */
|
/* 0x96 - 0xaf : defined in other specs */
|
||||||
GST_MPEGTS_STREAM_TYPE_SCTE_DSMCC_DCB = 0xb0, /* Data Carousel Type B */
|
GST_MPEGTS_STREAM_TYPE_SCTE_DSMCC_DCB = 0xb0, /* Data Carousel Type B */
|
||||||
|
@ -97,6 +100,117 @@ typedef enum {
|
||||||
|
|
||||||
} GstMpegtsSectionSCTETableID;
|
} GstMpegtsSectionSCTETableID;
|
||||||
|
|
||||||
|
/* Splice Information Table (SIT) */
|
||||||
|
#define GST_TYPE_MPEGTS_SCTE_SPLICE_EVENT (gst_mpegts_scte_splice_event_get_type);
|
||||||
|
typedef struct _GstMpegtsSCTESpliceEvent GstMpegtsSCTESpliceEvent;
|
||||||
|
|
||||||
|
struct _GstMpegtsSCTESpliceEvent {
|
||||||
|
/* TRUE if from/to an insert event (else belongs to a schedule event) */
|
||||||
|
gboolean insert_event;
|
||||||
|
|
||||||
|
guint32 splice_event_id;
|
||||||
|
gboolean splice_event_cancel_indicator;
|
||||||
|
|
||||||
|
/* If splice_event_cancel_indicator == 0 */
|
||||||
|
gboolean out_of_network_indicator;
|
||||||
|
gboolean program_splice_flag; /* NOTE: Only program splice are supported */
|
||||||
|
gboolean duration_flag;
|
||||||
|
gboolean splice_immediate_flag; /* Only valid for insert_event */
|
||||||
|
|
||||||
|
gboolean program_splice_time_specified;
|
||||||
|
guint64 program_splice_time;
|
||||||
|
|
||||||
|
gboolean break_duration_auto_return;
|
||||||
|
guint64 break_duration;
|
||||||
|
|
||||||
|
guint16 unique_program_id;
|
||||||
|
guint8 avail_num;
|
||||||
|
guint8 avails_expected;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Types of descriptors
|
||||||
|
*
|
||||||
|
* Note: These are only for the descriptors *WITHIN* a SIT */
|
||||||
|
typedef enum {
|
||||||
|
GST_MTS_SCTE_DESC_AVAIL = 0x00,
|
||||||
|
GST_MTS_SCTE_DESC_DTMF = 0x01,
|
||||||
|
GST_MTS_SCTE_DESC_SEGMENTATION = 0x02,
|
||||||
|
GST_MTS_SCTE_DESC_TIME = 0x03,
|
||||||
|
GST_MTS_SCTE_DESC_AUDIO = 0x04
|
||||||
|
} GstMpegtsSCTESpliceDescriptor;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
GST_MTS_SCTE_SPLICE_COMMAND_NULL = 0x00,
|
||||||
|
GST_MTS_SCTE_SPLICE_COMMAND_SCHEDULE = 0x04,
|
||||||
|
GST_MTS_SCTE_SPLICE_COMMAND_INSERT = 0x05,
|
||||||
|
GST_MTS_SCTE_SPLICE_COMMAND_TIME = 0x06,
|
||||||
|
GST_MTS_SCTE_SPLICE_COMMAND_BANDWIDTH = 0x07,
|
||||||
|
GST_MTS_SCTE_SPLICE_COMMAND_PRIVATE = 0xff
|
||||||
|
} GstMpegtsSCTESpliceCommandType;
|
||||||
|
|
||||||
|
#define GST_TYPE_MPEGTS_SCTE_SIT (gst_mpegts_scte_sit_get_type());
|
||||||
|
|
||||||
|
typedef struct _GstMpegtsSCTESIT GstMpegtsSCTESIT;
|
||||||
|
|
||||||
|
struct _GstMpegtsSCTESIT
|
||||||
|
{
|
||||||
|
/* Encryption not supported for now */
|
||||||
|
gboolean encrypted_packet;
|
||||||
|
guint8 encryption_algorithm;
|
||||||
|
|
||||||
|
guint64 pts_adjustment;
|
||||||
|
guint8 cw_index;
|
||||||
|
guint16 tier;
|
||||||
|
|
||||||
|
guint16 splice_command_length;
|
||||||
|
GstMpegtsSCTESpliceCommandType splice_command_type;
|
||||||
|
|
||||||
|
/* For time_signal commands */
|
||||||
|
gboolean splice_time_specified;
|
||||||
|
guint64 splice_time;
|
||||||
|
|
||||||
|
GPtrArray *splices;
|
||||||
|
|
||||||
|
GPtrArray *descriptors;
|
||||||
|
};
|
||||||
|
|
||||||
|
GST_MPEGTS_API
|
||||||
|
GType gst_mpegts_scte_sit_get_type (void);
|
||||||
|
|
||||||
|
GST_MPEGTS_API
|
||||||
|
GstMpegtsSCTESIT *gst_mpegts_scte_sit_new (void);
|
||||||
|
|
||||||
|
GST_MPEGTS_API
|
||||||
|
GstMpegtsSCTESIT *gst_mpegts_scte_null_new (void);
|
||||||
|
|
||||||
|
GST_MPEGTS_API
|
||||||
|
GstMpegtsSCTESIT *gst_mpegts_scte_cancel_new (guint32 event_id);
|
||||||
|
|
||||||
|
GST_MPEGTS_API
|
||||||
|
GstMpegtsSCTESIT *gst_mpegts_scte_splice_in_new (guint32 event_id,
|
||||||
|
guint64 splice_time);
|
||||||
|
|
||||||
|
GST_MPEGTS_API
|
||||||
|
GstMpegtsSCTESIT *gst_mpegts_scte_splice_out_new (guint32 event_id,
|
||||||
|
guint64 splice_time,
|
||||||
|
guint64 duration);
|
||||||
|
|
||||||
|
|
||||||
|
GST_MPEGTS_API
|
||||||
|
GType gst_mpegts_scte_splice_event_get_type (void);
|
||||||
|
|
||||||
|
GST_MPEGTS_API
|
||||||
|
GstMpegtsSCTESpliceEvent *gst_mpegts_scte_splice_event_new (void);
|
||||||
|
|
||||||
|
GST_MPEGTS_API
|
||||||
|
const GstMpegtsSCTESIT *gst_mpegts_section_get_scte_sit (GstMpegtsSection *section);
|
||||||
|
|
||||||
|
GST_MPEGTS_API
|
||||||
|
GstMpegtsSection *gst_mpegts_section_from_scte_sit (GstMpegtsSCTESIT * sit, guint16 pid);
|
||||||
|
|
||||||
|
|
||||||
G_END_DECLS
|
G_END_DECLS
|
||||||
|
|
||||||
#endif /* GST_SCTE_SECTION_H */
|
#endif /* GST_SCTE_SECTION_H */
|
||||||
|
|
|
@ -1126,6 +1126,9 @@ _identify_section (guint16 pid, guint8 table_id)
|
||||||
if (pid == 0x1ffb)
|
if (pid == 0x1ffb)
|
||||||
return GST_MPEGTS_SECTION_ATSC_RRT;
|
return GST_MPEGTS_SECTION_ATSC_RRT;
|
||||||
break;
|
break;
|
||||||
|
case GST_MTS_TABLE_ID_SCTE_SPLICE:
|
||||||
|
return GST_MPEGTS_SECTION_SCTE_SIT;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
/* Handle ranges */
|
/* Handle ranges */
|
||||||
if (table_id >= GST_MTS_TABLE_ID_EVENT_INFORMATION_ACTUAL_TS_PRESENT &&
|
if (table_id >= GST_MTS_TABLE_ID_EVENT_INFORMATION_ACTUAL_TS_PRESENT &&
|
||||||
|
@ -1176,8 +1179,13 @@ _packetize_common_section (GstMpegtsSection * section, gsize length)
|
||||||
case GST_MPEGTS_SECTION_PMT:
|
case GST_MPEGTS_SECTION_PMT:
|
||||||
case GST_MPEGTS_SECTION_CAT:
|
case GST_MPEGTS_SECTION_CAT:
|
||||||
case GST_MPEGTS_SECTION_TSDT:
|
case GST_MPEGTS_SECTION_TSDT:
|
||||||
|
case GST_MPEGTS_SECTION_SCTE_SIT:
|
||||||
/* Tables from ISO/IEC 13818-1 has a '0' bit
|
/* Tables from ISO/IEC 13818-1 has a '0' bit
|
||||||
* after the section_syntax_indicator */
|
* after the section_syntax_indicator */
|
||||||
|
/* FIXME : that 'bit' after the section_syntax_indicator is the
|
||||||
|
* private_indicator field. We should figure out why/when it
|
||||||
|
* needs to be set *OR* decide that by default it is set to 0
|
||||||
|
* (i.e. not private data) unless the user/caller decides */
|
||||||
GST_WRITE_UINT16_BE (data, (section->section_length - 3) | 0x3000);
|
GST_WRITE_UINT16_BE (data, (section->section_length - 3) | 0x3000);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -58,6 +58,7 @@ GType gst_mpegts_section_get_type (void);
|
||||||
* @GST_MPEGTS_SECTION_ATSC_ETT: ATSC Extended Text Table (A65)
|
* @GST_MPEGTS_SECTION_ATSC_ETT: ATSC Extended Text Table (A65)
|
||||||
* @GST_MPEGTS_SECTION_ATSC_EIT: ATSC Event Information Table (A65)
|
* @GST_MPEGTS_SECTION_ATSC_EIT: ATSC Event Information Table (A65)
|
||||||
* @GST_MPEGTS_SECTION_ATSC_STT: ATSC System Time Table (A65)
|
* @GST_MPEGTS_SECTION_ATSC_STT: ATSC System Time Table (A65)
|
||||||
|
* @GST_MPEGTS_SECTION_SCTE_SIT: SCTE Splice Information Table (SCTE-35)
|
||||||
*
|
*
|
||||||
* Types of #GstMpegtsSection that the library handles.
|
* Types of #GstMpegtsSection that the library handles.
|
||||||
*/
|
*/
|
||||||
|
@ -79,7 +80,8 @@ typedef enum {
|
||||||
GST_MPEGTS_SECTION_ATSC_ETT,
|
GST_MPEGTS_SECTION_ATSC_ETT,
|
||||||
GST_MPEGTS_SECTION_ATSC_EIT,
|
GST_MPEGTS_SECTION_ATSC_EIT,
|
||||||
GST_MPEGTS_SECTION_ATSC_STT,
|
GST_MPEGTS_SECTION_ATSC_STT,
|
||||||
GST_MPEGTS_SECTION_ATSC_RRT
|
GST_MPEGTS_SECTION_ATSC_RRT,
|
||||||
|
GST_MPEGTS_SECTION_SCTE_SIT
|
||||||
} GstMpegtsSectionType;
|
} GstMpegtsSectionType;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -4,6 +4,7 @@ mpegts_sources = [
|
||||||
'gst-dvb-descriptor.c',
|
'gst-dvb-descriptor.c',
|
||||||
'gst-dvb-section.c',
|
'gst-dvb-section.c',
|
||||||
'gst-atsc-section.c',
|
'gst-atsc-section.c',
|
||||||
|
'gst-scte-section.c',
|
||||||
]
|
]
|
||||||
|
|
||||||
mpegts_headers = [
|
mpegts_headers = [
|
||||||
|
|
|
@ -688,7 +688,8 @@ mpegts_base_update_program (MpegTSBase * base, MpegTSBaseProgram * program,
|
||||||
|
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
_stream_is_private_section (GstMpegtsPMTStream * stream)
|
_stream_is_private_section (const GstMpegtsPMT * pmt,
|
||||||
|
GstMpegtsPMTStream * stream)
|
||||||
{
|
{
|
||||||
switch (stream->stream_type) {
|
switch (stream->stream_type) {
|
||||||
case GST_MPEGTS_STREAM_TYPE_SCTE_DSMCC_DCB:
|
case GST_MPEGTS_STREAM_TYPE_SCTE_DSMCC_DCB:
|
||||||
|
@ -711,6 +712,15 @@ _stream_is_private_section (GstMpegtsPMTStream * stream)
|
||||||
case GST_MPEGTS_STREAM_TYPE_METADATA_SECTIONS:
|
case GST_MPEGTS_STREAM_TYPE_METADATA_SECTIONS:
|
||||||
/* known PSI streams */
|
/* known PSI streams */
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
case GST_MPEGTS_STREAM_TYPE_SCTE_SIT:
|
||||||
|
{
|
||||||
|
guint32 registration_id =
|
||||||
|
get_registration_from_descriptors (pmt->descriptors);
|
||||||
|
/* Not a private section stream */
|
||||||
|
if (registration_id != DRF_ID_CUEI)
|
||||||
|
return FALSE;
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
@ -818,7 +828,7 @@ mpegts_base_is_program_update (MpegTSBase * base,
|
||||||
GST_DEBUG
|
GST_DEBUG
|
||||||
("New stream 0x%04x has a different stream type (new:%d, old:%d)",
|
("New stream 0x%04x has a different stream type (new:%d, old:%d)",
|
||||||
stream->pid, stream->stream_type, oldstream->stream_type);
|
stream->pid, stream->stream_type, oldstream->stream_type);
|
||||||
} else if (!_stream_is_private_section (stream)) {
|
} else if (!_stream_is_private_section (new_pmt, stream)) {
|
||||||
/* FIXME : We should actually be checking a bit deeper,
|
/* FIXME : We should actually be checking a bit deeper,
|
||||||
* especially for private streams (where the differentiation is
|
* especially for private streams (where the differentiation is
|
||||||
* done at the registration level) */
|
* done at the registration level) */
|
||||||
|
@ -855,7 +865,7 @@ mpegts_base_deactivate_program (MpegTSBase * base, MpegTSBaseProgram * program)
|
||||||
/* Only unset the is_pes/known_psi bit if the PID isn't used in any other active
|
/* Only unset the is_pes/known_psi bit if the PID isn't used in any other active
|
||||||
* program */
|
* program */
|
||||||
if (!mpegts_pid_in_active_programs (base, stream->pid)) {
|
if (!mpegts_pid_in_active_programs (base, stream->pid)) {
|
||||||
if (_stream_is_private_section (stream)) {
|
if (_stream_is_private_section (program->pmt, stream)) {
|
||||||
if (base->parse_private_sections)
|
if (base->parse_private_sections)
|
||||||
MPEGTS_BIT_UNSET (base->known_psi, stream->pid);
|
MPEGTS_BIT_UNSET (base->known_psi, stream->pid);
|
||||||
} else {
|
} else {
|
||||||
|
@ -908,7 +918,7 @@ mpegts_base_activate_program (MpegTSBase * base, MpegTSBaseProgram * program,
|
||||||
|
|
||||||
for (i = 0; i < pmt->streams->len; ++i) {
|
for (i = 0; i < pmt->streams->len; ++i) {
|
||||||
GstMpegtsPMTStream *stream = g_ptr_array_index (pmt->streams, i);
|
GstMpegtsPMTStream *stream = g_ptr_array_index (pmt->streams, i);
|
||||||
if (_stream_is_private_section (stream)) {
|
if (_stream_is_private_section (pmt, stream)) {
|
||||||
if (base->parse_private_sections)
|
if (base->parse_private_sections)
|
||||||
MPEGTS_BIT_SET (base->known_psi, stream->pid);
|
MPEGTS_BIT_SET (base->known_psi, stream->pid);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -68,6 +68,113 @@ static const guint8 stt_data_check[] = {
|
||||||
0xc0, 0x00, 0xc4, 0x86, 0x56, 0xa5
|
0xc0, 0x00, 0xc4, 0x86, 0x56, 0xa5
|
||||||
};
|
};
|
||||||
|
|
||||||
|
GST_START_TEST (test_scte_sit)
|
||||||
|
{
|
||||||
|
GstMpegtsSCTESIT *sit;
|
||||||
|
GstMpegtsSection *sit_section;
|
||||||
|
GstMpegtsSCTESpliceEvent *event;
|
||||||
|
guint8 *data;
|
||||||
|
gsize data_size;
|
||||||
|
|
||||||
|
/* Try a simple NULL command before anything else */
|
||||||
|
sit = gst_mpegts_scte_sit_new ();
|
||||||
|
sit->tier = 123;
|
||||||
|
sit->pts_adjustment = 0x1fedcba12;
|
||||||
|
sit->splice_command_type = GST_MTS_SCTE_SPLICE_COMMAND_NULL;
|
||||||
|
|
||||||
|
sit_section = gst_mpegts_section_from_scte_sit (sit, 456);
|
||||||
|
fail_if (sit_section == NULL);
|
||||||
|
fail_unless (sit_section->short_section);
|
||||||
|
|
||||||
|
/* Serialize and check that we can re-parse it into something valid */
|
||||||
|
data = gst_mpegts_section_packetize (sit_section, &data_size);
|
||||||
|
fail_if (data == NULL);
|
||||||
|
GST_MEMDUMP ("section", data, data_size);
|
||||||
|
|
||||||
|
GST_LOG ("here");
|
||||||
|
sit_section->destroy_parsed (sit_section->cached_parsed);
|
||||||
|
sit_section->cached_parsed = NULL;
|
||||||
|
|
||||||
|
sit = (GstMpegtsSCTESIT *) gst_mpegts_section_get_scte_sit (sit_section);
|
||||||
|
fail_if (sit == NULL);
|
||||||
|
/* Check the values */
|
||||||
|
fail_unless (sit->encrypted_packet == FALSE);
|
||||||
|
fail_unless (sit->pts_adjustment == 0x1fedcba12);
|
||||||
|
fail_unless (sit->tier == 123);
|
||||||
|
fail_unless (sit->splice_command_type == GST_MTS_SCTE_SPLICE_COMMAND_NULL);
|
||||||
|
|
||||||
|
gst_mpegts_section_unref (sit_section);
|
||||||
|
|
||||||
|
|
||||||
|
/* Same thing but now with an insert command */
|
||||||
|
sit = gst_mpegts_scte_sit_new ();
|
||||||
|
sit->tier = 123;
|
||||||
|
sit->pts_adjustment = 0x1fedcba12;
|
||||||
|
sit->splice_command_type = GST_MTS_SCTE_SPLICE_COMMAND_INSERT;
|
||||||
|
|
||||||
|
event = gst_mpegts_scte_splice_event_new ();
|
||||||
|
event->insert_event = TRUE;
|
||||||
|
event->splice_event_id = 4285;
|
||||||
|
event->program_splice_flag = TRUE;
|
||||||
|
event->duration_flag = TRUE;
|
||||||
|
event->splice_immediate_flag = FALSE;
|
||||||
|
|
||||||
|
event->program_splice_time_specified = TRUE;
|
||||||
|
event->program_splice_time = 0x1fdecba12;
|
||||||
|
|
||||||
|
event->break_duration_auto_return = TRUE;
|
||||||
|
event->break_duration = 590000;
|
||||||
|
event->unique_program_id = 4256;
|
||||||
|
event->avail_num = 2;
|
||||||
|
event->avails_expected = 2;
|
||||||
|
g_ptr_array_add (sit->splices, event);
|
||||||
|
|
||||||
|
sit_section = gst_mpegts_section_from_scte_sit (sit, 456);
|
||||||
|
fail_if (sit_section == NULL);
|
||||||
|
fail_unless (sit_section->short_section);
|
||||||
|
|
||||||
|
/* Serialize and check that we can re-parse it into something valid */
|
||||||
|
data = gst_mpegts_section_packetize (sit_section, &data_size);
|
||||||
|
fail_if (data == NULL);
|
||||||
|
GST_MEMDUMP ("section", data, data_size);
|
||||||
|
|
||||||
|
GST_LOG ("here");
|
||||||
|
sit_section->destroy_parsed (sit_section->cached_parsed);
|
||||||
|
sit_section->cached_parsed = NULL;
|
||||||
|
|
||||||
|
sit = (GstMpegtsSCTESIT *) gst_mpegts_section_get_scte_sit (sit_section);
|
||||||
|
fail_if (sit == NULL);
|
||||||
|
/* Check the values */
|
||||||
|
fail_unless (sit->encrypted_packet == FALSE);
|
||||||
|
fail_unless (sit->pts_adjustment == 0x1fedcba12);
|
||||||
|
fail_unless (sit->tier == 123);
|
||||||
|
fail_unless (sit->pts_adjustment == 0x1fedcba12);
|
||||||
|
fail_unless (sit->splice_command_type == GST_MTS_SCTE_SPLICE_COMMAND_INSERT);
|
||||||
|
|
||||||
|
event = g_ptr_array_index (sit->splices, 0);
|
||||||
|
fail_unless (event->insert_event == TRUE);
|
||||||
|
fail_unless (event->splice_event_id == 4285);
|
||||||
|
fail_unless (event->program_splice_flag == TRUE);
|
||||||
|
fail_unless (event->duration_flag == TRUE);
|
||||||
|
fail_unless (event->splice_immediate_flag == FALSE);
|
||||||
|
|
||||||
|
fail_unless (event->program_splice_time_specified == TRUE);
|
||||||
|
fail_unless (event->program_splice_time == 0x1fdecba12);
|
||||||
|
|
||||||
|
fail_unless (event->break_duration_auto_return == TRUE);
|
||||||
|
fail_unless (event->break_duration == 590000);
|
||||||
|
fail_unless (event->unique_program_id == 4256);
|
||||||
|
fail_unless (event->avail_num == 2);
|
||||||
|
fail_unless (event->avails_expected == 2);
|
||||||
|
|
||||||
|
|
||||||
|
gst_mpegts_section_unref (sit_section);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_END_TEST;
|
||||||
|
|
||||||
GST_START_TEST (test_mpegts_pat)
|
GST_START_TEST (test_mpegts_pat)
|
||||||
{
|
{
|
||||||
GstMpegtsPatProgram *program;
|
GstMpegtsPatProgram *program;
|
||||||
|
@ -570,6 +677,7 @@ mpegts_suite (void)
|
||||||
gst_mpegts_initialize ();
|
gst_mpegts_initialize ();
|
||||||
|
|
||||||
suite_add_tcase (s, tc_chain);
|
suite_add_tcase (s, tc_chain);
|
||||||
|
tcase_add_test (tc_chain, test_scte_sit);
|
||||||
tcase_add_test (tc_chain, test_mpegts_pat);
|
tcase_add_test (tc_chain, test_mpegts_pat);
|
||||||
tcase_add_test (tc_chain, test_mpegts_pmt);
|
tcase_add_test (tc_chain, test_mpegts_pmt);
|
||||||
tcase_add_test (tc_chain, test_mpegts_nit);
|
tcase_add_test (tc_chain, test_mpegts_nit);
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include <glib/gprintf.h>
|
#include <glib/gprintf.h>
|
||||||
#include <gst/gst.h>
|
#include <gst/gst.h>
|
||||||
#include <gst/mpegts/mpegts.h>
|
#include <gst/mpegts/mpegts.h>
|
||||||
|
#define MPEGTIME_TO_GSTTIME(t) ((t) * (guint64)100000 / 9)
|
||||||
|
|
||||||
static void
|
static void
|
||||||
gst_info_dump_mem_line (gchar * linebuf, gsize linebuf_size,
|
gst_info_dump_mem_line (gchar * linebuf, gsize linebuf_size,
|
||||||
|
@ -130,6 +131,22 @@ table_id_name (gint val)
|
||||||
return en->value_nick;
|
return en->value_nick;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const gchar *
|
||||||
|
stream_type_name (gint val)
|
||||||
|
{
|
||||||
|
GEnumValue *en;
|
||||||
|
|
||||||
|
en = g_enum_get_value (G_ENUM_CLASS (g_type_class_peek
|
||||||
|
(GST_TYPE_MPEGTS_STREAM_TYPE)), val);
|
||||||
|
if (en == NULL)
|
||||||
|
/* Else try with SCTE enum types */
|
||||||
|
en = g_enum_get_value (G_ENUM_CLASS (g_type_class_peek
|
||||||
|
(GST_TYPE_MPEGTS_SCTE_STREAM_TYPE)), val);
|
||||||
|
if (en == NULL)
|
||||||
|
return "UNKNOWN/PRIVATE";
|
||||||
|
return en->value_nick;
|
||||||
|
}
|
||||||
|
|
||||||
static const gchar *
|
static const gchar *
|
||||||
enum_name (GType instance_type, gint val)
|
enum_name (GType instance_type, gint val)
|
||||||
{
|
{
|
||||||
|
@ -798,8 +815,7 @@ dump_pmt (GstMpegtsSection * section)
|
||||||
for (i = 0; i < len; i++) {
|
for (i = 0; i < len; i++) {
|
||||||
GstMpegtsPMTStream *stream = g_ptr_array_index (pmt->streams, i);
|
GstMpegtsPMTStream *stream = g_ptr_array_index (pmt->streams, i);
|
||||||
g_printf (" pid:0x%04x , stream_type:0x%02x (%s)\n", stream->pid,
|
g_printf (" pid:0x%04x , stream_type:0x%02x (%s)\n", stream->pid,
|
||||||
stream->stream_type,
|
stream->stream_type, stream_type_name (stream->stream_type));
|
||||||
enum_name (GST_TYPE_MPEGTS_STREAM_TYPE, stream->stream_type));
|
|
||||||
dump_descriptors (stream->descriptors, 9);
|
dump_descriptors (stream->descriptors, 9);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1111,6 +1127,96 @@ dump_cat (GstMpegtsSection * section)
|
||||||
g_ptr_array_unref (descriptors);
|
g_ptr_array_unref (descriptors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const gchar *
|
||||||
|
scte_descriptor_name (guint8 tag)
|
||||||
|
{
|
||||||
|
switch (tag) {
|
||||||
|
case 0x00:
|
||||||
|
return "avail";
|
||||||
|
case 0x01:
|
||||||
|
return "DTMF";
|
||||||
|
case 0x02:
|
||||||
|
return "segmentation";
|
||||||
|
case 0x03:
|
||||||
|
return "time";
|
||||||
|
case 0x04:
|
||||||
|
return "audio";
|
||||||
|
default:
|
||||||
|
return "UNKNOWN";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
dump_scte_descriptors (GPtrArray * descriptors, guint spacing)
|
||||||
|
{
|
||||||
|
guint i;
|
||||||
|
|
||||||
|
for (i = 0; i < descriptors->len; i++) {
|
||||||
|
GstMpegtsDescriptor *desc = g_ptr_array_index (descriptors, i);
|
||||||
|
g_printf ("%*s [scte descriptor 0x%02x (%s) length:%d]\n", spacing, "",
|
||||||
|
desc->tag, scte_descriptor_name (desc->tag), desc->length);
|
||||||
|
if (DUMP_DESCRIPTORS)
|
||||||
|
dump_memory_content (desc, spacing + 2);
|
||||||
|
/* FIXME : Add parsing of SCTE descriptors */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
dump_scte_sit (GstMpegtsSection * section)
|
||||||
|
{
|
||||||
|
const GstMpegtsSCTESIT *sit = gst_mpegts_section_get_scte_sit (section);
|
||||||
|
guint i, len;
|
||||||
|
|
||||||
|
g_assert (sit);
|
||||||
|
|
||||||
|
g_printf (" encrypted_packet : %d\n", sit->encrypted_packet);
|
||||||
|
if (sit->encrypted_packet) {
|
||||||
|
g_printf (" encryption_algorithm: %d\n", sit->encryption_algorithm);
|
||||||
|
g_printf (" cw_index : %d\n", sit->cw_index);
|
||||||
|
g_printf (" tier : %d\n", sit->tier);
|
||||||
|
}
|
||||||
|
g_printf (" pts_adjustment : %" G_GUINT64_FORMAT " (%"
|
||||||
|
GST_TIME_FORMAT ")\n", sit->pts_adjustment,
|
||||||
|
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (sit->pts_adjustment)));
|
||||||
|
g_printf (" command_type : %d\n", sit->splice_command_type);
|
||||||
|
|
||||||
|
if ((len = sit->splices->len)) {
|
||||||
|
g_printf (" %d splice(s):\n", len);
|
||||||
|
for (i = 0; i < len; i++) {
|
||||||
|
GstMpegtsSCTESpliceEvent *event = g_ptr_array_index (sit->splices, i);
|
||||||
|
g_printf (" event_id:%d event_cancel_indicator:%d\n",
|
||||||
|
event->splice_event_id, event->splice_event_cancel_indicator);
|
||||||
|
if (!event->splice_event_cancel_indicator) {
|
||||||
|
g_printf (" out_of_network_indicator:%d\n",
|
||||||
|
event->out_of_network_indicator);
|
||||||
|
if (event->program_splice_flag) {
|
||||||
|
if (event->program_splice_time_specified)
|
||||||
|
g_printf (" program_splice_time:%" G_GUINT64_FORMAT " (%"
|
||||||
|
GST_TIME_FORMAT ")\n", event->program_splice_time,
|
||||||
|
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME
|
||||||
|
(event->program_splice_time)));
|
||||||
|
else
|
||||||
|
g_printf (" program_splice_time not specified\n");
|
||||||
|
}
|
||||||
|
if (event->duration_flag) {
|
||||||
|
g_printf (" break_duration_auto_return:%d\n",
|
||||||
|
event->break_duration_auto_return);
|
||||||
|
g_printf (" break_duration:%" G_GUINT64_FORMAT " (%"
|
||||||
|
GST_TIME_FORMAT ")\n", event->break_duration,
|
||||||
|
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (event->break_duration)));
|
||||||
|
|
||||||
|
}
|
||||||
|
g_printf (" unique_program_id : %d\n", event->unique_program_id);
|
||||||
|
g_printf (" avail num/expected : %d/%d\n",
|
||||||
|
event->avail_num, event->avails_expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dump_scte_descriptors (sit->descriptors, 4);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
dump_section (GstMpegtsSection * section)
|
dump_section (GstMpegtsSection * section)
|
||||||
{
|
{
|
||||||
|
@ -1158,6 +1264,9 @@ dump_section (GstMpegtsSection * section)
|
||||||
case GST_MPEGTS_SECTION_ATSC_STT:
|
case GST_MPEGTS_SECTION_ATSC_STT:
|
||||||
dump_stt (section);
|
dump_stt (section);
|
||||||
break;
|
break;
|
||||||
|
case GST_MPEGTS_SECTION_SCTE_SIT:
|
||||||
|
dump_scte_sit (section);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
g_printf (" Unknown section type\n");
|
g_printf (" Unknown section type\n");
|
||||||
break;
|
break;
|
||||||
|
@ -1251,6 +1360,8 @@ main (int argc, gchar ** argv)
|
||||||
g_type_class_ref (GST_TYPE_MPEGTS_DVB_LINKAGE_HAND_OVER_TYPE);
|
g_type_class_ref (GST_TYPE_MPEGTS_DVB_LINKAGE_HAND_OVER_TYPE);
|
||||||
g_type_class_ref (GST_TYPE_MPEGTS_COMPONENT_STREAM_CONTENT);
|
g_type_class_ref (GST_TYPE_MPEGTS_COMPONENT_STREAM_CONTENT);
|
||||||
g_type_class_ref (GST_TYPE_MPEGTS_CONTENT_NIBBLE_HI);
|
g_type_class_ref (GST_TYPE_MPEGTS_CONTENT_NIBBLE_HI);
|
||||||
|
g_type_class_ref (GST_TYPE_MPEGTS_SCTE_STREAM_TYPE);
|
||||||
|
g_type_class_ref (GST_TYPE_MPEGTS_SECTION_SCTE_TABLE_ID);
|
||||||
|
|
||||||
mainloop = g_main_loop_new (NULL, FALSE);
|
mainloop = g_main_loop_new (NULL, FALSE);
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue