gstreamer/subprojects/gst-plugins-bad/gst-libs/gst/mpegts/gst-scte-section.c
Mathieu Duponchelle c3a161f287 basetsmux: rework SCTE section handling to handle passthrough
mpegtsmux can receive SCTE sections from two origins: events
created by the application, and events forwarded downstream by
mpegtsdemux, containing sections that may not have been fully
parsed, and additional data to help tsmux translate times to
the correct domain, both for requesting keyframes and calculating
an accurate pts_adjustment.

The complete approach is documented further in a comment above
the relevant function.

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/913>
2021-09-25 01:29:38 +00:00

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;
}