[FFmpeg-devel] [PATCH v4 3/4] mpegtsenc: Add support for output of SCTE-35 streams over TS
Devin Heitmueller
devin.heitmueller at ltnglobal.com
Mon Jul 31 16:38:05 EEST 2023
Introduce the ability to pass through SCTE-35 packets when creating
MPEG transport streams. Note that this patch makes no effort to
change the PTS values in the SCTE-35 packets, and thus only works
when not reclocking the stream (i.e. using -copyts). A subsequent
patch includes a BSF to recompute the PTS values.
Signed-off-by: Devin Heitmueller <dheitmueller at ltnglobal.com>
---
libavformat/mpegts.h | 1 +
libavformat/mpegtsenc.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++---
libavformat/mux.c | 6 ++--
3 files changed, 75 insertions(+), 6 deletions(-)
diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h
index a48f14e..a7aaaba 100644
--- a/libavformat/mpegts.h
+++ b/libavformat/mpegts.h
@@ -137,6 +137,7 @@
#define STREAM_TYPE_AUDIO_AC3 0x81
#define STREAM_TYPE_AUDIO_DTS 0x82
#define STREAM_TYPE_AUDIO_TRUEHD 0x83
+#define STREAM_TYPE_SCTE_35 0x86
#define STREAM_TYPE_AUDIO_EAC3 0x87
/* ISO/IEC 13818-1 Table 2-22 */
diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index 700fc54..c6cd1fd 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -425,6 +425,9 @@ static int get_dvb_stream_type(AVFormatContext *s, AVStream *st)
case AV_CODEC_ID_SMPTE_2038:
stream_type = STREAM_TYPE_PRIVATE_DATA;
break;
+ case AV_CODEC_ID_SCTE_35:
+ stream_type = STREAM_TYPE_SCTE_35;
+ break;
case AV_CODEC_ID_DVB_SUBTITLE:
case AV_CODEC_ID_DVB_TELETEXT:
case AV_CODEC_ID_ARIB_CAPTION:
@@ -522,6 +525,16 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
*q++ = 0xfc; // private_data_byte
}
+ /* If there is an SCTE-35 stream, we need a registration descriptor
+ at the program level (SCTE 35 2016 Sec 8.1) */
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ if (st->codecpar->codec_id == AV_CODEC_ID_SCTE_35) {
+ put_registration_descriptor(&q, MKTAG('C', 'U', 'E', 'I'));
+ break;
+ }
+ }
+
val = 0xf000 | (q - program_info_length_ptr - 2);
program_info_length_ptr[0] = val >> 8;
program_info_length_ptr[1] = val;
@@ -533,6 +546,14 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
const char default_language[] = "und";
const char *language = lang && strlen(lang->value) >= 3 ? lang->value : default_language;
enum AVCodecID codec_id = st->codecpar->codec_id;
+ uint16_t pid;
+
+ if (st->codecpar->codec_id == AV_CODEC_ID_SCTE_35) {
+ MpegTSSection *sect = st->priv_data;
+ pid = sect->pid;
+ } else {
+ pid = ts_st->pid;
+ }
if (s->nb_programs) {
int k, found = 0;
@@ -556,7 +577,7 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
stream_type = ts->m2ts_mode ? get_m2ts_stream_type(s, st) : get_dvb_stream_type(s, st);
*q++ = stream_type;
- put16(&q, 0xe000 | ts_st->pid);
+ put16(&q, 0xe000 | pid);
desc_length_ptr = q;
q += 2; /* patched after */
@@ -819,6 +840,10 @@ static int mpegts_write_pmt(AVFormatContext *s, MpegTSService *service)
putbuf(&q, tag, strlen(tag));
*q++ = 0; /* metadata service ID */
*q++ = 0xF; /* metadata_locator_record_flag|MPEG_carriage_flags|reserved */
+ } else if (st->codecpar->codec_id == AV_CODEC_ID_SCTE_35) {
+ *q++ = 0x8a; /* Cue Identifier Descriptor */
+ *q++ = 0x01; /* length */
+ *q++ = 0x01; /* Cue Stream Type (see Sec 8.2) */
}
break;
}
@@ -1159,6 +1184,33 @@ static int mpegts_init(AVFormatContext *s)
AVStream *st = s->streams[i];
MpegTSWriteStream *ts_st;
+ if (st->codecpar->codec_id == AV_CODEC_ID_SCTE_35) {
+ struct MpegTSSection *sect;
+ sect = av_mallocz(sizeof(MpegTSSection));
+ if (!sect) {
+ ret = AVERROR(ENOMEM);
+ continue;
+ }
+
+ if (st->id < 16) {
+ sect->pid = ts->start_pid + i;
+ } else if (st->id < 0x1FFF) {
+ sect->pid = st->id;
+ } else {
+ av_log(s, AV_LOG_ERROR,
+ "Invalid stream id %d, must be less than 8191\n", st->id);
+ ret = AVERROR(EINVAL);
+ continue;
+ }
+
+ sect->write_packet = section_write_packet;
+ sect->opaque = s;
+ sect->cc = 15;
+ sect->discontinuity= ts->flags & MPEGTS_FLAG_DISCONT;
+ st->priv_data = sect;
+ continue;
+ }
+
ts_st = av_mallocz(sizeof(MpegTSWriteStream));
if (!ts_st) {
return AVERROR(ENOMEM);
@@ -1877,6 +1929,19 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
dts += delay;
}
+ if (st->codecpar->codec_id == AV_CODEC_ID_SCTE_35) {
+ MpegTSSection *s = st->priv_data;
+ uint8_t data[SECTION_LENGTH];
+
+ if (size > SECTION_LENGTH) {
+ av_log(s, AV_LOG_ERROR, "SCTE-35 section too long\n");
+ return AVERROR_INVALIDDATA;
+ }
+ memcpy(data, buf, size);
+ mpegts_write_section(s, data, size);
+ return 0;
+ }
+
if (!ts_st->first_timestamp_checked && (pts == AV_NOPTS_VALUE || dts == AV_NOPTS_VALUE)) {
av_log(s, AV_LOG_ERROR, "first pts and dts value must be set\n");
return AVERROR_INVALIDDATA;
@@ -2149,7 +2214,8 @@ static int mpegts_write_packet_internal(AVFormatContext *s, AVPacket *pkt)
return 0;
}
- if (ts_st->payload_size && (ts_st->payload_size + size > ts->pes_payload_size ||
+ if (st->codecpar->codec_id != AV_CODEC_ID_SCTE_35 &&
+ ts_st->payload_size && (ts_st->payload_size + size > ts->pes_payload_size ||
(dts != AV_NOPTS_VALUE && ts_st->payload_dts != AV_NOPTS_VALUE &&
dts - ts_st->payload_dts >= max_audio_delay) ||
ts_st->opus_queued_samples + opus_samples >= 5760 /* 120ms */)) {
@@ -2194,7 +2260,7 @@ static void mpegts_write_flush(AVFormatContext *s)
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MpegTSWriteStream *ts_st = st->priv_data;
- if (ts_st->payload_size > 0) {
+ if (st->codecpar->codec_id != AV_CODEC_ID_SCTE_35 && ts_st->payload_size > 0) {
mpegts_write_pes(s, st, ts_st->payload, ts_st->payload_size,
ts_st->payload_pts, ts_st->payload_dts,
ts_st->payload_flags & AV_PKT_FLAG_KEY, -1);
@@ -2237,7 +2303,7 @@ static void mpegts_deinit(AVFormatContext *s)
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MpegTSWriteStream *ts_st = st->priv_data;
- if (ts_st) {
+ if (ts_st && st->codecpar->codec_id != AV_CODEC_ID_SCTE_35) {
av_freep(&ts_st->dvb_ac3_desc);
av_freep(&ts_st->payload);
if (ts_st->amux) {
diff --git a/libavformat/mux.c b/libavformat/mux.c
index 415bd39..55bec25 100644
--- a/libavformat/mux.c
+++ b/libavformat/mux.c
@@ -307,7 +307,8 @@ FF_ENABLE_DEPRECATION_WARNINGS
}
if (par->codec_type != AVMEDIA_TYPE_ATTACHMENT &&
- par->codec_id != AV_CODEC_ID_SMPTE_2038)
+ par->codec_id != AV_CODEC_ID_SMPTE_2038 &&
+ par->codec_id != AV_CODEC_ID_SCTE_35)
si->nb_interleaved_streams++;
}
si->interleave_packet = of->interleave_packet;
@@ -946,7 +947,8 @@ int ff_interleave_packet_per_dts(AVFormatContext *s, AVPacket *pkt,
} else if (par->codec_type != AVMEDIA_TYPE_ATTACHMENT &&
par->codec_id != AV_CODEC_ID_VP8 &&
par->codec_id != AV_CODEC_ID_VP9 &&
- par->codec_id != AV_CODEC_ID_SMPTE_2038) {
+ par->codec_id != AV_CODEC_ID_SMPTE_2038 &&
+ par->codec_id != AV_CODEC_ID_SCTE_35) {
++noninterleaved_count;
}
}
--
1.8.3.1
More information about the ffmpeg-devel
mailing list