mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-22 17:51:16 +00:00
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>
This commit is contained in:
parent
e4f40ba526
commit
c3a161f287
3 changed files with 309 additions and 104 deletions
|
@ -467,6 +467,8 @@ gst_mpegts_scte_sit_new (void)
|
||||||
sit->descriptors = g_ptr_array_new_with_free_func ((GDestroyNotify)
|
sit->descriptors = g_ptr_array_new_with_free_func ((GDestroyNotify)
|
||||||
gst_mpegts_descriptor_free);
|
gst_mpegts_descriptor_free);
|
||||||
|
|
||||||
|
sit->is_running_time = TRUE;
|
||||||
|
|
||||||
return sit;
|
return sit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -483,6 +485,9 @@ gst_mpegts_scte_null_new (void)
|
||||||
GstMpegtsSCTESIT *sit = gst_mpegts_scte_sit_new ();
|
GstMpegtsSCTESIT *sit = gst_mpegts_scte_sit_new ();
|
||||||
|
|
||||||
sit->splice_command_type = GST_MTS_SCTE_SPLICE_COMMAND_NULL;
|
sit->splice_command_type = GST_MTS_SCTE_SPLICE_COMMAND_NULL;
|
||||||
|
|
||||||
|
sit->is_running_time = TRUE;
|
||||||
|
|
||||||
return sit;
|
return sit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -506,6 +511,8 @@ gst_mpegts_scte_cancel_new (guint32 event_id)
|
||||||
event->splice_event_cancel_indicator = TRUE;
|
event->splice_event_cancel_indicator = TRUE;
|
||||||
g_ptr_array_add (sit->splices, event);
|
g_ptr_array_add (sit->splices, event);
|
||||||
|
|
||||||
|
sit->is_running_time = TRUE;
|
||||||
|
|
||||||
return sit;
|
return sit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -539,6 +546,8 @@ gst_mpegts_scte_splice_in_new (guint32 event_id, GstClockTime splice_time)
|
||||||
}
|
}
|
||||||
g_ptr_array_add (sit->splices, event);
|
g_ptr_array_add (sit->splices, event);
|
||||||
|
|
||||||
|
sit->is_running_time = TRUE;
|
||||||
|
|
||||||
return sit;
|
return sit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -582,6 +591,8 @@ gst_mpegts_scte_splice_out_new (guint32 event_id, GstClockTime splice_time,
|
||||||
}
|
}
|
||||||
g_ptr_array_add (sit->splices, event);
|
g_ptr_array_add (sit->splices, event);
|
||||||
|
|
||||||
|
sit->is_running_time = TRUE;
|
||||||
|
|
||||||
return sit;
|
return sit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -185,10 +185,6 @@ struct _GstMpegtsSCTESIT
|
||||||
|
|
||||||
guint16 splice_command_length;
|
guint16 splice_command_length;
|
||||||
|
|
||||||
/* When encrypted, or when encountering an unknown command type,
|
|
||||||
* we may still want to pass the sit through */
|
|
||||||
gboolean fully_parsed;
|
|
||||||
|
|
||||||
GstMpegtsSCTESpliceCommandType splice_command_type;
|
GstMpegtsSCTESpliceCommandType splice_command_type;
|
||||||
|
|
||||||
/* For time_signal commands */
|
/* For time_signal commands */
|
||||||
|
@ -198,6 +194,13 @@ struct _GstMpegtsSCTESIT
|
||||||
GPtrArray *splices;
|
GPtrArray *splices;
|
||||||
|
|
||||||
GPtrArray *descriptors;
|
GPtrArray *descriptors;
|
||||||
|
|
||||||
|
/* When encrypted, or when encountering an unknown command type,
|
||||||
|
* we may still want to pass the sit through */
|
||||||
|
gboolean fully_parsed;
|
||||||
|
/* When the SIT was constructed by the application, splice times
|
||||||
|
* are in running_time and must be translated before packetizing */
|
||||||
|
gboolean is_running_time;
|
||||||
};
|
};
|
||||||
|
|
||||||
GST_MPEGTS_API
|
GST_MPEGTS_API
|
||||||
|
|
|
@ -1420,6 +1420,82 @@ gst_base_ts_mux_release_pad (GstElement * element, GstPad * pad)
|
||||||
GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
|
GST_ELEMENT_CLASS (parent_class)->release_pad (element, pad);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* GstAggregator implementation */
|
||||||
|
|
||||||
|
static void
|
||||||
|
request_keyframe (GstBaseTsMux * mux, GstClockTime running_time)
|
||||||
|
{
|
||||||
|
GList *l;
|
||||||
|
GST_OBJECT_LOCK (mux);
|
||||||
|
|
||||||
|
for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
|
||||||
|
gst_pad_push_event (GST_PAD (l->data),
|
||||||
|
gst_video_event_new_upstream_force_key_unit (running_time, TRUE, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
GST_OBJECT_UNLOCK (mux);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const guint32 crc_tab[256] = {
|
||||||
|
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
|
||||||
|
0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
|
||||||
|
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
|
||||||
|
0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
|
||||||
|
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
|
||||||
|
0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
|
||||||
|
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
|
||||||
|
0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
|
||||||
|
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
|
||||||
|
0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
|
||||||
|
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
|
||||||
|
0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
|
||||||
|
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
|
||||||
|
0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
|
||||||
|
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
|
||||||
|
0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
|
||||||
|
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
|
||||||
|
0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
|
||||||
|
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
|
||||||
|
0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
|
||||||
|
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
|
||||||
|
0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
|
||||||
|
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
|
||||||
|
0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
|
||||||
|
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
|
||||||
|
0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
|
||||||
|
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
|
||||||
|
0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
|
||||||
|
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
|
||||||
|
0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
|
||||||
|
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
|
||||||
|
0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
|
||||||
|
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
|
||||||
|
0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
|
||||||
|
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
|
||||||
|
0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
|
||||||
|
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
|
||||||
|
0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
|
||||||
|
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
|
||||||
|
0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
|
||||||
|
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
|
||||||
|
0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
|
||||||
|
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint32
|
||||||
|
_calc_crc32 (const guint8 * data, guint datalen)
|
||||||
|
{
|
||||||
|
gint i;
|
||||||
|
guint32 crc = 0xffffffff;
|
||||||
|
|
||||||
|
for (i = 0; i < datalen; i++) {
|
||||||
|
crc = (crc << 8) ^ crc_tab[((crc >> 24) ^ *data++) & 0xff];
|
||||||
|
}
|
||||||
|
return crc;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define MPEGTIME_TO_GSTTIME(t) ((t) * (guint64)100000 / 9)
|
||||||
|
|
||||||
static GstMpegtsSCTESpliceEvent *
|
static GstMpegtsSCTESpliceEvent *
|
||||||
copy_splice (GstMpegtsSCTESpliceEvent * splice)
|
copy_splice (GstMpegtsSCTESpliceEvent * splice)
|
||||||
{
|
{
|
||||||
|
@ -1439,94 +1515,149 @@ deep_copy_sit (const GstMpegtsSCTESIT * sit)
|
||||||
return sit_copy;
|
return sit_copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* GstAggregator implementation */
|
/* Takes ownership of @section.
|
||||||
|
*
|
||||||
|
* This function is a bit complex because the SCTE sections can
|
||||||
|
* have various origins:
|
||||||
|
*
|
||||||
|
* * Sections created by the application with the gst_mpegts_scte_*_new()
|
||||||
|
* API. The splice times / durations contained by these are expressed
|
||||||
|
* in the GStreamer running time domain, and must be translated to
|
||||||
|
* our local PES time domain. In this case, we will packetize the section
|
||||||
|
* ourselves.
|
||||||
|
*
|
||||||
|
* * Sections passed through from tsdemux: this case is complicated as
|
||||||
|
* splice times in the incoming stream may be encrypted, with pts_adjustment
|
||||||
|
* being the only timing field guaranteed *not* to be encrypted. In this
|
||||||
|
* case, the original binary data (section->data) will be reinjected as is
|
||||||
|
* in the output stream, with pts_adjustment adjusted. tsdemux provides us
|
||||||
|
* with the pts_offset it introduces, the difference between the original
|
||||||
|
* PES PTSs and the running times it outputs.
|
||||||
|
*
|
||||||
|
* Additionally, in either of these cases when the splice times aren't encrypted
|
||||||
|
* we want to make use of those to request keyframes. For the passthrough case,
|
||||||
|
* as the splice times are left untouched tsdemux provides us with the running
|
||||||
|
* times the section originally referred to. We cannot calculate it locally
|
||||||
|
* because we would need to have access to the information that the timestamps
|
||||||
|
* in the original PES domain have wrapped around, and how many times they have
|
||||||
|
* done so. While we could probably make educated guesses, tsdemux (more specifically
|
||||||
|
* mpegtspacketizer) already keeps track of that, and it seemed more logical to
|
||||||
|
* perform the calculation there and forward it alongside the downstream events.
|
||||||
|
*
|
||||||
|
* Finally, while we can't request keyframes at splice points in the encrypted
|
||||||
|
* case, if the input stream was compliant in that regard and no reencoding took
|
||||||
|
* place the splice times will still match with valid splice points, it is up
|
||||||
|
* to the application to ensure that that is the case.
|
||||||
|
*/
|
||||||
static void
|
static void
|
||||||
request_keyframe (GstBaseTsMux * mux, GstClockTime running_time)
|
handle_scte35_section (GstBaseTsMux * mux, GstMpegtsSection * section,
|
||||||
|
guint64 mpeg_pts_offset, GstStructure * rtime_map)
|
||||||
{
|
{
|
||||||
GList *l;
|
GstMpegtsSCTESIT *sit;
|
||||||
GST_OBJECT_LOCK (mux);
|
|
||||||
|
|
||||||
for (l = GST_ELEMENT_CAST (mux)->sinkpads; l; l = l->next) {
|
|
||||||
gst_pad_push_event (GST_PAD (l->data),
|
|
||||||
gst_video_event_new_upstream_force_key_unit (running_time, TRUE, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
GST_OBJECT_UNLOCK (mux);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Takes ownership of @section */
|
|
||||||
static void
|
|
||||||
handle_scte35_section (GstBaseTsMux * mux, GstMpegtsSection * section)
|
|
||||||
{
|
|
||||||
const GstMpegtsSCTESIT *sit;
|
|
||||||
GstMpegtsSCTESIT *sit_copy;
|
|
||||||
guint i;
|
guint i;
|
||||||
gboolean forward = TRUE;
|
gboolean forward = TRUE;
|
||||||
|
guint64 pts_adjust;
|
||||||
|
guint8 *section_data;
|
||||||
|
guint8 *crc;
|
||||||
|
gboolean translate = FALSE;
|
||||||
|
|
||||||
sit = gst_mpegts_section_get_scte_sit (section);
|
sit = (GstMpegtsSCTESIT *) gst_mpegts_section_get_scte_sit (section);
|
||||||
sit_copy = deep_copy_sit (sit);
|
|
||||||
|
|
||||||
switch (sit_copy->splice_command_type) {
|
/* When the application injects manually constructed splice events,
|
||||||
|
* their time domain is the GStreamer running time, we receive them
|
||||||
|
* unpacketized and translate the fields in the SIT to local PTS.
|
||||||
|
*
|
||||||
|
* We make a copy of the SIT in order to make sure we can rewrite it.
|
||||||
|
*/
|
||||||
|
if (sit->is_running_time) {
|
||||||
|
sit = deep_copy_sit (sit);
|
||||||
|
translate = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (sit->splice_command_type) {
|
||||||
case GST_MTS_SCTE_SPLICE_COMMAND_NULL:
|
case GST_MTS_SCTE_SPLICE_COMMAND_NULL:
|
||||||
/* We implement heartbeating ourselves */
|
/* We implement heartbeating ourselves */
|
||||||
forward = FALSE;
|
forward = FALSE;
|
||||||
break;
|
break;
|
||||||
case GST_MTS_SCTE_SPLICE_COMMAND_SCHEDULE:
|
case GST_MTS_SCTE_SPLICE_COMMAND_SCHEDULE:
|
||||||
/* Only translate timestamps and forward, splice_insert
|
/* No need to request keyframes at this point, splice_insert
|
||||||
* messages will precede the future splice points and we
|
* messages will precede the future splice points and we
|
||||||
* can request keyframes then.
|
* can request keyframes then. Only translate if needed.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < sit_copy->splices->len; i++) {
|
if (translate) {
|
||||||
GstMpegtsSCTESpliceEvent *sevent =
|
for (i = 0; i < sit->splices->len; i++) {
|
||||||
g_ptr_array_index (sit_copy->splices, i);
|
GstMpegtsSCTESpliceEvent *sevent =
|
||||||
if (sevent->program_splice_time_specified) {
|
g_ptr_array_index (sit->splices, i);
|
||||||
sevent->program_splice_time =
|
|
||||||
GSTTIME_TO_MPEGTIME (sevent->program_splice_time) +
|
if (sevent->program_splice_time_specified)
|
||||||
TS_MUX_CLOCK_BASE;
|
sevent->program_splice_time =
|
||||||
}
|
GSTTIME_TO_MPEGTIME (sevent->program_splice_time) +
|
||||||
if (sevent->duration_flag) {
|
TS_MUX_CLOCK_BASE;
|
||||||
sevent->break_duration = GSTTIME_TO_MPEGTIME (sevent->break_duration);
|
|
||||||
|
if (sevent->duration_flag)
|
||||||
|
sevent->break_duration =
|
||||||
|
GSTTIME_TO_MPEGTIME (sevent->break_duration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case GST_MTS_SCTE_SPLICE_COMMAND_INSERT:
|
case GST_MTS_SCTE_SPLICE_COMMAND_INSERT:
|
||||||
/* We want keyframes at splice points */
|
/* We want keyframes at splice points */
|
||||||
for (i = 0; i < sit_copy->splices->len; i++) {
|
if (sit->fully_parsed && (rtime_map || translate)) {
|
||||||
guint64 running_time = GST_CLOCK_TIME_NONE;
|
|
||||||
|
|
||||||
GstMpegtsSCTESpliceEvent *sevent =
|
for (i = 0; i < sit->splices->len; i++) {
|
||||||
g_ptr_array_index (sit_copy->splices, i);
|
guint64 running_time = GST_CLOCK_TIME_NONE;
|
||||||
if (sevent->program_splice_time_specified) {
|
|
||||||
GST_DEBUG_OBJECT (mux,
|
|
||||||
"Requesting keyframe for splice point at %" GST_TIME_FORMAT,
|
|
||||||
GST_TIME_ARGS (sevent->program_splice_time));
|
|
||||||
running_time = sevent->program_splice_time;
|
|
||||||
request_keyframe (mux, running_time);
|
|
||||||
sevent->program_splice_time =
|
|
||||||
GSTTIME_TO_MPEGTIME (sevent->program_splice_time) +
|
|
||||||
TS_MUX_CLOCK_BASE;
|
|
||||||
} else {
|
|
||||||
GST_DEBUG_OBJECT (mux,
|
|
||||||
"Requesting keyframe for immediate splice point");
|
|
||||||
request_keyframe (mux, GST_CLOCK_TIME_NONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sevent->duration_flag) {
|
GstMpegtsSCTESpliceEvent *sevent =
|
||||||
/* Even if auto_return is FALSE, when a break_duration is specified it
|
g_ptr_array_index (sit->splices, i);
|
||||||
* is intended as a redundancy mechanism in case the follow-up
|
if (sevent->program_splice_time_specified) {
|
||||||
* splice insert goes missing.
|
if (rtime_map) {
|
||||||
*
|
gchar *field_name = g_strdup_printf ("event-%u-splice-time",
|
||||||
* Schedule a keyframe at that point (if we can calculate its position
|
sevent->splice_event_id);
|
||||||
* accurately).
|
if (gst_structure_get_uint64 (rtime_map, field_name,
|
||||||
*/
|
&running_time)) {
|
||||||
if (GST_CLOCK_STIME_IS_VALID (running_time)) {
|
GST_DEBUG_OBJECT (mux,
|
||||||
|
"Requesting keyframe for splice point at %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (running_time));
|
||||||
|
request_keyframe (mux, running_time);
|
||||||
|
}
|
||||||
|
g_free (field_name);
|
||||||
|
} else {
|
||||||
|
g_assert (translate == TRUE);
|
||||||
|
running_time = sevent->program_splice_time;
|
||||||
|
GST_DEBUG_OBJECT (mux,
|
||||||
|
"Requesting keyframe for splice point at %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (running_time));
|
||||||
|
request_keyframe (mux, running_time);
|
||||||
|
sevent->program_splice_time =
|
||||||
|
GSTTIME_TO_MPEGTIME (running_time) + TS_MUX_CLOCK_BASE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
GST_DEBUG_OBJECT (mux,
|
GST_DEBUG_OBJECT (mux,
|
||||||
"Requesting keyframe for end of break at %" GST_TIME_FORMAT,
|
"Requesting keyframe for immediate splice point");
|
||||||
GST_TIME_ARGS (running_time + sevent->break_duration));
|
request_keyframe (mux, GST_CLOCK_TIME_NONE);
|
||||||
request_keyframe (mux, running_time + sevent->break_duration);
|
}
|
||||||
|
|
||||||
|
if (sevent->duration_flag) {
|
||||||
|
if (translate) {
|
||||||
|
sevent->break_duration =
|
||||||
|
GSTTIME_TO_MPEGTIME (sevent->break_duration);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Even if auto_return is FALSE, when a break_duration is specified it
|
||||||
|
* is intended as a redundancy mechanism in case the follow-up
|
||||||
|
* splice insert goes missing.
|
||||||
|
*
|
||||||
|
* Schedule a keyframe at that point (if we can calculate its position
|
||||||
|
* accurately).
|
||||||
|
*/
|
||||||
|
if (GST_CLOCK_TIME_IS_VALID (running_time)) {
|
||||||
|
running_time += MPEGTIME_TO_GSTTIME (sevent->break_duration);
|
||||||
|
GST_DEBUG_OBJECT (mux,
|
||||||
|
"Requesting keyframe for end of break at %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (running_time));
|
||||||
|
request_keyframe (mux, running_time);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sevent->break_duration = GSTTIME_TO_MPEGTIME (sevent->break_duration);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -1539,37 +1670,55 @@ handle_scte35_section (GstBaseTsMux * mux, GstMpegtsSection * section)
|
||||||
* of the requirement in 10.3.4 that a keyframe should not be created
|
* of the requirement in 10.3.4 that a keyframe should not be created
|
||||||
* when the signal contains only a time_descriptor.
|
* when the signal contains only a time_descriptor.
|
||||||
*/
|
*/
|
||||||
for (i = 0; i < sit_copy->descriptors->len; i++) {
|
if (sit->fully_parsed && (rtime_map || translate)) {
|
||||||
GstMpegtsDescriptor *descriptor =
|
for (i = 0; i < sit->descriptors->len; i++) {
|
||||||
g_ptr_array_index (sit_copy->descriptors, i);
|
GstMpegtsDescriptor *descriptor =
|
||||||
|
g_ptr_array_index (sit->descriptors, i);
|
||||||
|
|
||||||
switch (descriptor->tag) {
|
switch (descriptor->tag) {
|
||||||
case GST_MTS_SCTE_DESC_AVAIL:
|
case GST_MTS_SCTE_DESC_AVAIL:
|
||||||
case GST_MTS_SCTE_DESC_DTMF:
|
case GST_MTS_SCTE_DESC_DTMF:
|
||||||
case GST_MTS_SCTE_DESC_SEGMENTATION:
|
case GST_MTS_SCTE_DESC_SEGMENTATION:
|
||||||
do_request_keyframes = TRUE;
|
do_request_keyframes = TRUE;
|
||||||
break;
|
break;
|
||||||
case GST_MTS_SCTE_DESC_TIME:
|
case GST_MTS_SCTE_DESC_TIME:
|
||||||
case GST_MTS_SCTE_DESC_AUDIO:
|
case GST_MTS_SCTE_DESC_AUDIO:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_request_keyframes)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_request_keyframes)
|
if (sit->splice_time_specified) {
|
||||||
break;
|
GstClockTime running_time = GST_CLOCK_TIME_NONE;
|
||||||
}
|
|
||||||
|
|
||||||
if (sit_copy->splice_time_specified) {
|
if (rtime_map) {
|
||||||
if (do_request_keyframes) {
|
if (do_request_keyframes
|
||||||
|
&& gst_structure_get_uint64 (rtime_map, "splice-time",
|
||||||
|
&running_time)) {
|
||||||
|
GST_DEBUG_OBJECT (mux,
|
||||||
|
"Requesting keyframe for time signal at %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (running_time));
|
||||||
|
request_keyframe (mux, running_time);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
g_assert (translate);
|
||||||
|
running_time = sit->splice_time;
|
||||||
|
sit->splice_time =
|
||||||
|
GSTTIME_TO_MPEGTIME (running_time) + TS_MUX_CLOCK_BASE;
|
||||||
|
if (do_request_keyframes) {
|
||||||
|
GST_DEBUG_OBJECT (mux,
|
||||||
|
"Requesting keyframe for time signal at %" GST_TIME_FORMAT,
|
||||||
|
GST_TIME_ARGS (running_time));
|
||||||
|
request_keyframe (mux, running_time);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (do_request_keyframes) {
|
||||||
GST_DEBUG_OBJECT (mux,
|
GST_DEBUG_OBJECT (mux,
|
||||||
"Requesting keyframe for time signal at %" GST_TIME_FORMAT,
|
"Requesting keyframe for immediate time signal");
|
||||||
GST_TIME_ARGS (sit_copy->splice_time));
|
request_keyframe (mux, GST_CLOCK_TIME_NONE);
|
||||||
request_keyframe (mux, sit_copy->splice_time);
|
|
||||||
}
|
}
|
||||||
sit_copy->splice_time =
|
|
||||||
GSTTIME_TO_MPEGTIME (sit_copy->splice_time) + TS_MUX_CLOCK_BASE;
|
|
||||||
} else if (do_request_keyframes) {
|
|
||||||
GST_DEBUG_OBJECT (mux, "Requesting keyframe for immediate time signal");
|
|
||||||
request_keyframe (mux, GST_CLOCK_TIME_NONE);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1586,13 +1735,47 @@ handle_scte35_section (GstBaseTsMux * mux, GstMpegtsSection * section)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GST_OBJECT_LOCK (mux);
|
if (!translate) {
|
||||||
GST_DEBUG_OBJECT (mux, "Storing SCTE section");
|
g_assert (section->data);
|
||||||
if (mux->pending_scte35_section)
|
/* Calculate the final adjustment, as a sum of:
|
||||||
gst_mpegts_section_unref (mux->pending_scte35_section);
|
* - The adjustment in the original packet
|
||||||
mux->pending_scte35_section =
|
* - The offset introduced between the original local PTS
|
||||||
gst_mpegts_section_from_scte_sit (sit_copy, mux->scte35_pid);
|
* and the GStreamer PTS output by tsdemux
|
||||||
GST_OBJECT_UNLOCK (mux);
|
* - Our own 1-hour offset
|
||||||
|
*/
|
||||||
|
pts_adjust = sit->pts_adjustment + mpeg_pts_offset + TS_MUX_CLOCK_BASE;
|
||||||
|
pts_adjust &= 0x1ffffffff;
|
||||||
|
section_data = g_memdup (section->data, section->section_length);
|
||||||
|
section_data[4] |= pts_adjust >> 32;
|
||||||
|
section_data[5] = pts_adjust >> 24;
|
||||||
|
section_data[6] = pts_adjust >> 16;
|
||||||
|
section_data[7] = pts_adjust >> 8;
|
||||||
|
section_data[8] = pts_adjust;
|
||||||
|
|
||||||
|
/* Now rewrite our checksum */
|
||||||
|
crc = section_data + section->section_length - 4;
|
||||||
|
GST_WRITE_UINT32_BE (crc, _calc_crc32 (section_data, crc - section_data));
|
||||||
|
|
||||||
|
GST_OBJECT_LOCK (mux);
|
||||||
|
GST_DEBUG_OBJECT (mux, "Storing SCTE section");
|
||||||
|
if (mux->pending_scte35_section)
|
||||||
|
gst_mpegts_section_unref (mux->pending_scte35_section);
|
||||||
|
mux->pending_scte35_section =
|
||||||
|
gst_mpegts_section_new (mux->scte35_pid, section_data,
|
||||||
|
section->section_length);
|
||||||
|
GST_OBJECT_UNLOCK (mux);
|
||||||
|
|
||||||
|
gst_mpegts_section_unref (section);
|
||||||
|
} else {
|
||||||
|
GST_OBJECT_LOCK (mux);
|
||||||
|
GST_DEBUG_OBJECT (mux, "Storing SCTE section");
|
||||||
|
gst_mpegts_section_unref (section);
|
||||||
|
if (mux->pending_scte35_section)
|
||||||
|
gst_mpegts_section_unref (mux->pending_scte35_section);
|
||||||
|
mux->pending_scte35_section =
|
||||||
|
gst_mpegts_section_from_scte_sit (sit, mux->scte35_pid);;
|
||||||
|
GST_OBJECT_UNLOCK (mux);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
|
@ -1607,7 +1790,7 @@ gst_base_ts_mux_send_event (GstElement * element, GstEvent * event)
|
||||||
GST_DEBUG ("Received event with mpegts section");
|
GST_DEBUG ("Received event with mpegts section");
|
||||||
|
|
||||||
if (section->section_type == GST_MPEGTS_SECTION_SCTE_SIT) {
|
if (section->section_type == GST_MPEGTS_SECTION_SCTE_SIT) {
|
||||||
handle_scte35_section (mux, section);
|
handle_scte35_section (mux, section, 0, NULL);
|
||||||
} else {
|
} else {
|
||||||
/* TODO: Check that the section type is supported */
|
/* TODO: Check that the section type is supported */
|
||||||
tsmux_add_mpegts_si_section (mux->tsmux, section);
|
tsmux_add_mpegts_si_section (mux->tsmux, section);
|
||||||
|
@ -1656,9 +1839,17 @@ gst_base_ts_mux_sink_event (GstAggregator * agg, GstAggregatorPad * agg_pad,
|
||||||
|
|
||||||
gst_structure_get (s, "section", GST_TYPE_MPEGTS_SECTION, §ion,
|
gst_structure_get (s, "section", GST_TYPE_MPEGTS_SECTION, §ion,
|
||||||
NULL);
|
NULL);
|
||||||
|
|
||||||
if (section) {
|
if (section) {
|
||||||
handle_scte35_section (mux, section);
|
guint64 mpeg_pts_offset = 0;
|
||||||
|
GstStructure *rtime_map = NULL;
|
||||||
|
|
||||||
|
gst_structure_get (s, "running-time-map", GST_TYPE_STRUCTURE,
|
||||||
|
&rtime_map, NULL);
|
||||||
|
gst_structure_get_uint64 (s, "mpeg-pts-offset", &mpeg_pts_offset);
|
||||||
|
|
||||||
|
handle_scte35_section (mux, section, mpeg_pts_offset, rtime_map);
|
||||||
|
if (rtime_map)
|
||||||
|
gst_structure_free (rtime_map);
|
||||||
mux->last_scte35_event_seqnum = gst_event_get_seqnum (event);
|
mux->last_scte35_event_seqnum = gst_event_get_seqnum (event);
|
||||||
} else {
|
} else {
|
||||||
GST_WARNING_OBJECT (ts_pad,
|
GST_WARNING_OBJECT (ts_pad,
|
||||||
|
|
Loading…
Reference in a new issue