From 1ca08bff5717e313481eb3cd5196df1cf5f5225f Mon Sep 17 00:00:00 2001 From: Mathieu Duponchelle Date: Tue, 6 Apr 2021 00:36:43 +0200 Subject: [PATCH] tsdemux: Expose send-scte35-events property When enabled, SCTE 35 sections (eg ad placement opportunities) are forwarded as events donwstream. Part-of: --- .../docs/plugins/gst_plugins_cache.json | 12 ++ .../gst-plugins-bad/gst/mpegtsdemux/tsdemux.c | 115 ++++++++++++++++++ .../gst-plugins-bad/gst/mpegtsdemux/tsdemux.h | 1 + 3 files changed, 128 insertions(+) diff --git a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json index e3ad9e352b..9d9e94eccf 100644 --- a/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json +++ b/subprojects/gst-plugins-bad/docs/plugins/gst_plugins_cache.json @@ -210818,6 +210818,18 @@ "readable": true, "type": "gint", "writable": true + }, + "send-scte35-events": { + "blurb": "Whether SCTE 35 sections should be forwarded as events", + "conditionally-available": false, + "construct": false, + "construct-only": false, + "controllable": false, + "default": "false", + "mutable": "null", + "readable": true, + "type": "gboolean", + "writable": true } }, "rank": "primary", diff --git a/subprojects/gst-plugins-bad/gst/mpegtsdemux/tsdemux.c b/subprojects/gst-plugins-bad/gst/mpegtsdemux/tsdemux.c index d648faa6fb..238c708976 100644 --- a/subprojects/gst-plugins-bad/gst/mpegtsdemux/tsdemux.c +++ b/subprojects/gst-plugins-bad/gst/mpegtsdemux/tsdemux.c @@ -297,6 +297,7 @@ enum PROP_PROGRAM_NUMBER, PROP_EMIT_STATS, PROP_LATENCY, + PROP_SEND_SCTE35_EVENTS, /* FILL ME */ }; @@ -340,6 +341,7 @@ static gboolean push_event (MpegTSBase * base, GstEvent * event); static gboolean sink_query (MpegTSBase * base, GstQuery * query); static void gst_ts_demux_check_and_sync_streams (GstTSDemux * demux, GstClockTime time); +static void handle_psi (MpegTSBase * base, GstMpegtsSection * section); static void _extra_init (void) @@ -395,6 +397,22 @@ gst_ts_demux_class_init (GstTSDemuxClass * klass) "Emit messages for every pcr/opcr/pts/dts", FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + /** + * mpegtsdemux:send-scte35-events: + * + * Whether SCTE 35 sections should be forwarded as events. + * + * When forwarding those, potential splice times are converted + * to running time, and can be used by a downstream muxer to reinject + * the sections. + * + * Since: 1.20 + */ + g_object_class_install_property (gobject_class, PROP_SEND_SCTE35_EVENTS, + g_param_spec_boolean ("send-scte35-events", "Send SCTE 35 events", + "Whether SCTE 35 sections should be forwarded as events", FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, PROP_LATENCY, g_param_spec_int ("latency", "Latency", "Latency to add for smooth demuxing (in ms)", -1, @@ -422,6 +440,7 @@ gst_ts_demux_class_init (GstTSDemuxClass * klass) ts_class->reset = GST_DEBUG_FUNCPTR (gst_ts_demux_reset); ts_class->push = GST_DEBUG_FUNCPTR (gst_ts_demux_push); ts_class->push_event = GST_DEBUG_FUNCPTR (push_event); + ts_class->handle_psi = GST_DEBUG_FUNCPTR (handle_psi); ts_class->sink_query = GST_DEBUG_FUNCPTR (sink_query); ts_class->program_started = GST_DEBUG_FUNCPTR (gst_ts_demux_program_started); ts_class->program_stopped = GST_DEBUG_FUNCPTR (gst_ts_demux_program_stopped); @@ -495,6 +514,9 @@ gst_ts_demux_set_property (GObject * object, guint prop_id, case PROP_EMIT_STATS: demux->emit_statistics = g_value_get_boolean (value); break; + case PROP_SEND_SCTE35_EVENTS: + demux->send_scte35_events = g_value_get_boolean (value); + break; case PROP_LATENCY: demux->latency = g_value_get_int (value); break; @@ -516,6 +538,9 @@ gst_ts_demux_get_property (GObject * object, guint prop_id, case PROP_EMIT_STATS: g_value_set_boolean (value, demux->emit_statistics); break; + case PROP_SEND_SCTE35_EVENTS: + g_value_set_boolean (value, demux->send_scte35_events); + break; case PROP_LATENCY: g_value_set_int (value, demux->latency); break; @@ -1055,6 +1080,96 @@ push_event (MpegTSBase * base, GstEvent * event) return TRUE; } +static GstMpegtsSCTESpliceEvent * +copy_splice (GstMpegtsSCTESpliceEvent * splice) +{ + return g_boxed_copy (GST_TYPE_MPEGTS_SCTE_SPLICE_EVENT, splice); +} + +static GstMpegtsSCTESIT * +deep_copy_sit (const GstMpegtsSCTESIT * sit) +{ + GstMpegtsSCTESIT *sit_copy = g_boxed_copy (GST_TYPE_MPEGTS_SCTE_SIT, sit); + GPtrArray *splices_copy = + g_ptr_array_copy (sit_copy->splices, (GCopyFunc) copy_splice, NULL); + + g_ptr_array_unref (sit_copy->splices); + sit_copy->splices = splices_copy; + + return sit_copy; +} + +static void +handle_psi (MpegTSBase * base, GstMpegtsSection * section) +{ + GstTSDemux *demux = (GstTSDemux *) base; + + if (section->section_type == GST_MPEGTS_SECTION_SCTE_SIT) { + GList *tmp; + gboolean forward = FALSE; + + if (demux->send_scte35_events) { + for (tmp = demux->program->stream_list; tmp; tmp = tmp->next) { + TSDemuxStream *stream = (TSDemuxStream *) tmp->data; + + if (stream->stream.pid == section->pid) { + forward = TRUE; + break; + } + } + } + + /* Create a new section to travel through the pipeline, with splice + * times translated from local time to running time */ + if (forward) { + GstEvent *event; + GstClockTime pts; + guint i = 0; + const GstMpegtsSCTESIT *sit = gst_mpegts_section_get_scte_sit (section); + GstMpegtsSCTESIT *sit_copy = deep_copy_sit (sit); + GstMpegtsSection *new_section; + + if (sit_copy->splice_time_specified) { + pts = + mpegts_packetizer_pts_to_ts (base->packetizer, + MPEGTIME_TO_GSTTIME (sit_copy->splice_time + + sit_copy->pts_adjustment), demux->program->pcr_pid); + sit_copy->splice_time = + gst_segment_to_running_time (&base->out_segment, GST_FORMAT_TIME, + pts); + } + + for (i = 0; i < sit_copy->splices->len; i++) { + GstMpegtsSCTESpliceEvent *sevent = + g_ptr_array_index (sit_copy->splices, i); + if (sevent->program_splice_time_specified) { + pts = + mpegts_packetizer_pts_to_ts (base->packetizer, + MPEGTIME_TO_GSTTIME (sevent->program_splice_time), + demux->program->pcr_pid); + sevent->program_splice_time = + gst_segment_to_running_time (&base->out_segment, GST_FORMAT_TIME, + pts); + if (sevent->duration_flag) { + sevent->break_duration = + MPEGTIME_TO_GSTTIME (sevent->break_duration); + } + } + } + + sit_copy->pts_adjustment = 0; + + new_section = gst_mpegts_section_from_scte_sit (sit_copy, section->pid); + + event = gst_event_new_mpegts_section (new_section); + + gst_mpegts_section_unref (new_section); + + push_event (base, event); + } + } +} + static gboolean sink_query (MpegTSBase * base, GstQuery * query) { diff --git a/subprojects/gst-plugins-bad/gst/mpegtsdemux/tsdemux.h b/subprojects/gst-plugins-bad/gst/mpegtsdemux/tsdemux.h index dd0275aa6c..dc514b23d1 100644 --- a/subprojects/gst-plugins-bad/gst/mpegtsdemux/tsdemux.h +++ b/subprojects/gst-plugins-bad/gst/mpegtsdemux/tsdemux.h @@ -76,6 +76,7 @@ struct _GstTSDemux gint requested_program_number; /* Required program number (ignore:-1) */ guint program_number; gboolean emit_statistics; + gboolean send_scte35_events; gint latency; /* latency in ms */ /*< private >*/