mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-30 04:00:37 +00:00
909 lines
26 KiB
C
909 lines
26 KiB
C
/*
|
|
* 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
|
|
*
|
|
* This contains the %GstMpegtsSection relevent to SCTE specifications.
|
|
*/
|
|
|
|
/* TODO: port to gst_bit_reader / gst_bit_writer */
|
|
|
|
/* Splice Information Table (SIT) */
|
|
|
|
static GstMpegtsSCTESpliceEvent *
|
|
_gst_mpegts_scte_splice_event_copy (GstMpegtsSCTESpliceEvent * event)
|
|
{
|
|
GstMpegtsSCTESpliceEvent *copy =
|
|
g_slice_dup (GstMpegtsSCTESpliceEvent, event);
|
|
|
|
copy->components = g_ptr_array_ref (event->components);
|
|
|
|
return copy;
|
|
}
|
|
|
|
static void
|
|
_gst_mpegts_scte_splice_event_free (GstMpegtsSCTESpliceEvent * event)
|
|
{
|
|
g_ptr_array_unref (event->components);
|
|
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 GstMpegtsSCTESpliceComponent *
|
|
_gst_mpegts_scte_splice_component_copy (GstMpegtsSCTESpliceComponent *
|
|
component)
|
|
{
|
|
return g_slice_dup (GstMpegtsSCTESpliceComponent, component);
|
|
}
|
|
|
|
static void
|
|
_gst_mpegts_scte_splice_component_free (GstMpegtsSCTESpliceComponent *
|
|
component)
|
|
{
|
|
g_slice_free (GstMpegtsSCTESpliceComponent, component);
|
|
}
|
|
|
|
G_DEFINE_BOXED_TYPE (GstMpegtsSCTESpliceComponent,
|
|
gst_mpegts_scte_splice_component,
|
|
(GBoxedCopyFunc) _gst_mpegts_scte_splice_component_copy,
|
|
(GFreeFunc) _gst_mpegts_scte_splice_component_free);
|
|
|
|
static GstMpegtsSCTESpliceComponent *
|
|
_parse_splice_component (GstMpegtsSCTESpliceEvent * event, guint8 ** orig_data,
|
|
guint8 * end)
|
|
{
|
|
GstMpegtsSCTESpliceComponent *component =
|
|
g_slice_new0 (GstMpegtsSCTESpliceComponent);
|
|
guint8 *data = *orig_data;
|
|
|
|
if (data + 1 + 6 > end)
|
|
goto error;
|
|
|
|
component->tag = *data;
|
|
data += 1;
|
|
|
|
if (event->insert_event && event->splice_immediate_flag == 0) {
|
|
component->splice_time_specified = *data >> 7;
|
|
if (component->splice_time_specified) {
|
|
component->splice_time = ((guint64) (*data & 0x01)) << 32;
|
|
data += 1;
|
|
component->splice_time += GST_READ_UINT32_BE (data);
|
|
data += 4;
|
|
GST_LOG ("component %u splice_time %" G_GUINT64_FORMAT " (%"
|
|
GST_TIME_FORMAT ")", component->tag, component->splice_time,
|
|
GST_TIME_ARGS (MPEGTIME_TO_GSTTIME (component->splice_time)));
|
|
} else {
|
|
data += 1;
|
|
}
|
|
} else if (!event->insert_event) {
|
|
component->utc_splice_time = GST_READ_UINT32_BE (data);
|
|
GST_LOG ("component %u utc_splice_time %u", component->tag,
|
|
component->utc_splice_time);
|
|
data += 4;
|
|
}
|
|
|
|
*orig_data = data;
|
|
|
|
return component;
|
|
|
|
error:
|
|
{
|
|
if (event)
|
|
_gst_mpegts_scte_splice_event_free (event);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
static GstMpegtsSCTESpliceEvent *
|
|
_parse_splice_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->components = g_ptr_array_new_with_free_func ((GDestroyNotify)
|
|
_gst_mpegts_scte_splice_component_free);
|
|
|
|
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;
|
|
|
|
if (insert_event)
|
|
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);
|
|
|
|
if (insert_event)
|
|
GST_LOG ("splice_immediate_flag:%d", event->splice_immediate_flag);
|
|
|
|
data += 1;
|
|
|
|
if (event->program_splice_flag == 0) {
|
|
guint component_count = *data;
|
|
guint i;
|
|
|
|
data += 1;
|
|
|
|
for (i = 0; i < component_count; i++) {
|
|
GstMpegtsSCTESpliceComponent *component =
|
|
_parse_splice_component (event, &data, end);
|
|
if (component == NULL)
|
|
goto error;
|
|
g_ptr_array_add (event->components, component);
|
|
}
|
|
} else {
|
|
if (insert_event && 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;
|
|
} else if (!insert_event) {
|
|
event->utc_splice_time = GST_READ_UINT32_BE (data);
|
|
GST_LOG ("utc_splice_time %u", event->utc_splice_time);
|
|
data += 4;
|
|
}
|
|
}
|
|
|
|
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);
|
|
|
|
sit->fully_parsed = FALSE;
|
|
|
|
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);
|
|
|
|
if (sit->encrypted_packet) {
|
|
GST_LOG ("Encrypted SIT, parsed partially");
|
|
goto done;
|
|
}
|
|
|
|
if (sit->splice_command_length
|
|
&& (data + sit->splice_command_length > end - 5)) {
|
|
GST_WARNING ("PID %d invalid SCTE SIT splice command length %d",
|
|
section->pid, sit->splice_command_length);
|
|
goto error;
|
|
}
|
|
|
|
sit->splice_command_type = *data;
|
|
data += 1;
|
|
|
|
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_SCHEDULE:
|
|
{
|
|
guint i;
|
|
guint splice_count = *data;
|
|
data += 1;
|
|
|
|
for (i = 0; i < splice_count; i++) {
|
|
GstMpegtsSCTESpliceEvent *event =
|
|
_parse_splice_event (&data, end, FALSE);
|
|
if (event == NULL)
|
|
goto error;
|
|
g_ptr_array_add (sit->splices, event);
|
|
}
|
|
}
|
|
break;
|
|
case GST_MTS_SCTE_SPLICE_COMMAND_INSERT:
|
|
{
|
|
GstMpegtsSCTESpliceEvent *event = _parse_splice_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_WARNING ("Unknown SCTE splice command type (0x%02x) !",
|
|
sit->splice_command_type);
|
|
break;;
|
|
}
|
|
|
|
/* 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;
|
|
}
|
|
|
|
sit->fully_parsed = TRUE;
|
|
|
|
done:
|
|
return sit;
|
|
|
|
error:
|
|
if (sit) {
|
|
_gst_mpegts_scte_sit_free (sit);
|
|
sit = NULL;
|
|
}
|
|
|
|
goto done;
|
|
}
|
|
|
|
/**
|
|
* 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->fully_parsed = TRUE;
|
|
|
|
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);
|
|
|
|
sit->is_running_time = TRUE;
|
|
|
|
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;
|
|
|
|
sit->is_running_time = TRUE;
|
|
|
|
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);
|
|
|
|
sit->is_running_time = TRUE;
|
|
|
|
return sit;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_scte_splice_in_new:
|
|
* @event_id: The event ID.
|
|
* @splice_time: The running 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, GstClockTime 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;
|
|
event->insert_event = TRUE;
|
|
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);
|
|
|
|
sit->is_running_time = TRUE;
|
|
|
|
return sit;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_scte_splice_out_new:
|
|
* @event_id: The event ID.
|
|
* @splice_time: The running 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, GstClockTime splice_time,
|
|
GstClockTime 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;
|
|
event->insert_event = 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);
|
|
|
|
sit->is_running_time = TRUE;
|
|
|
|
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;
|
|
event->components = g_ptr_array_new_with_free_func ((GDestroyNotify)
|
|
_gst_mpegts_scte_splice_component_free);
|
|
|
|
|
|
return event;
|
|
}
|
|
|
|
/**
|
|
* gst_mpegts_scte_splice_component_new:
|
|
* @tag: the elementary PID stream identifier
|
|
*
|
|
* Allocates and initializes a #GstMpegtsSCTESpliceComponent.
|
|
*
|
|
* Returns: (transfer full): A newly allocated #GstMpegtsSCTESpliceComponent
|
|
* Since: 1.20
|
|
*/
|
|
GstMpegtsSCTESpliceComponent *
|
|
gst_mpegts_scte_splice_component_new (guint8 tag)
|
|
{
|
|
GstMpegtsSCTESpliceComponent *component =
|
|
g_slice_new0 (GstMpegtsSCTESpliceComponent);
|
|
|
|
component->tag = tag;
|
|
|
|
return component;
|
|
}
|
|
|
|
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;
|
|
|
|
if (sit->fully_parsed == FALSE) {
|
|
GST_WARNING ("Attempted to packetize an incompletely parsed SIT");
|
|
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_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) {
|
|
/* Add at least 5 bytes for common fields */
|
|
command_length += 5;
|
|
|
|
if (event->program_splice_flag) {
|
|
if (event->insert_event) {
|
|
if (!event->splice_immediate_flag) {
|
|
if (event->program_splice_time_specified)
|
|
command_length += 5;
|
|
else
|
|
command_length += 1;
|
|
}
|
|
} else {
|
|
/* Schedule events, 4 bytes for utc_splice_time */
|
|
command_length += 4;
|
|
}
|
|
} else {
|
|
guint j;
|
|
|
|
/* component_count */
|
|
command_length += 1;
|
|
|
|
for (j = 0; j < event->components->len; j++) {
|
|
GstMpegtsSCTESpliceComponent *component =
|
|
g_ptr_array_index (event->components, j);
|
|
|
|
/* component_tag */
|
|
command_length += 1;
|
|
if (event->insert_event) {
|
|
if (!event->splice_immediate_flag) {
|
|
if (component->splice_time_specified)
|
|
command_length += 5;
|
|
else
|
|
command_length += 1;
|
|
}
|
|
} else {
|
|
/* utc_splice_time */
|
|
command_length += 4;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (event->duration_flag)
|
|
command_length += 5;
|
|
}
|
|
}
|
|
|
|
if (sit->splice_command_type == GST_MTS_SCTE_SPLICE_COMMAND_TIME) {
|
|
if (sit->splice_time_specified)
|
|
command_length += 5;
|
|
else
|
|
command_length += 1;
|
|
}
|
|
|
|
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;
|
|
|
|
if (sit->splice_command_type == GST_MTS_SCTE_SPLICE_COMMAND_TIME) {
|
|
if (!sit->splice_time_specified) {
|
|
*data++ = 0x7f;
|
|
} else {
|
|
*data++ = 0xf2 | ((sit->splice_time >> 32) & 0x1);
|
|
GST_WRITE_UINT32_BE (data, sit->splice_time & 0xffffffff);
|
|
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->insert_event ? (event->splice_immediate_flag << 4) : 0) |
|
|
0x0f;
|
|
if (event->program_splice_flag) {
|
|
if (event->insert_event) {
|
|
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;
|
|
}
|
|
}
|
|
} else {
|
|
GST_WRITE_UINT32_BE (data, event->utc_splice_time);
|
|
data += 4;
|
|
}
|
|
} else {
|
|
guint j;
|
|
|
|
*data++ = event->components->len & 0xff;
|
|
|
|
for (j = 0; j < event->components->len; j++) {
|
|
GstMpegtsSCTESpliceComponent *component =
|
|
g_ptr_array_index (event->components, j);
|
|
|
|
*data++ = component->tag;
|
|
|
|
if (event->insert_event) {
|
|
if (!event->splice_immediate_flag) {
|
|
/* program_splice_time_specified : 1bit
|
|
* reserved : 6/7 bit */
|
|
if (!component->splice_time_specified)
|
|
*data++ = 0x7f;
|
|
else {
|
|
/* time : 33bit */
|
|
*data++ = 0xf2 | ((component->splice_time >> 32) & 0x1);
|
|
GST_WRITE_UINT32_BE (data, component->splice_time & 0xffffffff);
|
|
data += 4;
|
|
}
|
|
}
|
|
} else {
|
|
GST_WRITE_UINT32_BE (data, component->utc_splice_time);
|
|
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;
|
|
}
|