[FFmpeg-devel] [PATCH] Add optional NIT table generation
Ubaldo Porcheddu
ubaldo at eja.it
Tue Apr 27 18:15:20 EEST 2021
Signed-off-by: Ubaldo Porcheddu <ubaldo at eja.it>
---
libavformat/mpegtsenc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 106 insertions(+), 4 deletions(-)
diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index a357f3a6aa..d03eaaa009 100644
--- a/libavformat/mpegtsenc.c
+++ b/libavformat/mpegtsenc.c
@@ -76,10 +76,12 @@ typedef struct MpegTSWrite {
const AVClass *av_class;
MpegTSSection pat; /* MPEG-2 PAT table */
MpegTSSection sdt; /* MPEG-2 SDT table context */
+ MpegTSSection nit; /* MPEG-2 NIT table context */
MpegTSService **services;
AVPacket *pkt;
int64_t sdt_period; /* SDT period in PCR time base */
int64_t pat_period; /* PAT/PMT period in PCR time base */
+ int64_t nit_period; /* NIT period in PCR time base */
int nb_services;
int64_t first_pcr;
int first_dts_checked;
@@ -112,10 +114,13 @@ typedef struct MpegTSWrite {
int tables_version;
int64_t pat_period_us;
int64_t sdt_period_us;
+ int64_t nit_period_us;
int64_t last_pat_ts;
int64_t last_sdt_ts;
+ int64_t last_nit_ts;
int omit_video_pes_length;
+ int nit_enable;
} MpegTSWrite;
/* a PES packet header is generated every DEFAULT_PES_HEADER_FREQ packets */
@@ -227,6 +232,7 @@ static int mpegts_write_section1(MpegTSSection *s, int tid, int id,
#define SDT_RETRANS_TIME 500
#define PAT_RETRANS_TIME 100
#define PCR_RETRANS_TIME 20
+#define NIT_RETRANS_TIME 500
typedef struct MpegTSWriteStream {
int pid; /* stream associated pid */
@@ -796,6 +802,75 @@ static void mpegts_write_sdt(AVFormatContext *s)
data, q - data);
}
+static void mpegts_write_nit(AVFormatContext *s)
+{
+ int i, len=0, provider_len=0, desc_len=0, virtual_channel_value;
+ MpegTSWrite *ts = s->priv_data;
+ uint8_t data[SECTION_LENGTH], *q, *desc_len_ptr, *loop_len_ptr;
+ AVDictionaryEntry *provider, *virtual_channel;
+ AVProgram *program;
+ const char *provider_name;
+
+ q = data;
+
+ //network name
+ provider = av_dict_get(s->metadata, "service_provider", NULL, 0);
+ provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME;
+ provider_len = strlen(provider_name);
+ put16(&q, 0xf000 | provider_len);
+ *q++ = 0x40;
+ *q++ = provider_len;
+ putbuf(&q, provider_name, provider_len);
+
+ //TS loop
+ loop_len_ptr = q;
+ q += 2;
+ put16(&q, ts->transport_stream_id);
+ put16(&q, ts->original_network_id);
+
+ //transport descriptor
+ desc_len_ptr = q;
+ q += 2;
+
+ //service list
+ len = 3 * ts->nb_services;
+ desc_len += len;
+ *q++ = 0x41; //tag
+ *q++ = len;
+ for(i = 0; i < ts->nb_services; i++) {
+ put16(&q, ts->services[i]->sid);//service_ID
+ *q++ = 0x01; //service type 0x01 for Digital TV Service
+ }
+
+ //private data
+ desc_len += 6 + 2;
+ *q++ = 0x5F;
+ *q++ = 4;
+ *q++ = 0x00;
+ *q++ = 0x00;
+ put16(&q, 40);
+
+ //virtual channel
+ len = 4 * ts->nb_services;
+ desc_len += len + 2;
+ *q++ = 0x83;
+ *q++ = len;
+ for (i = 0; i < ts->nb_services; i++) {
+ program = s->programs[i];
+ virtual_channel = av_dict_get(program->metadata, "virtual_channel", NULL, 0);
+ virtual_channel_value = virtual_channel ? atoi(virtual_channel->value) : 1000+i;
+ put16(&q, ts->services[i]->sid);
+ put16(&q, 0xfc00 | virtual_channel_value);
+ }
+
+ //calculate lengths
+ put16(&desc_len_ptr, 0xf000 | desc_len);
+ put16(&loop_len_ptr, 0xf000 | desc_len+6);
+
+ mpegts_write_section1(&ts->nit, NIT_TID, ts->original_network_id, ts->tables_version, 0, 0,
+ data, q - data);
+}
+
/* This stores a string in buf with the correct encoding and also sets the
* first byte as the length. !str is accepted for an empty string.
* If the string is already encoded, invalid UTF-8 or has no multibyte sequence
@@ -1022,6 +1097,12 @@ static int mpegts_init(AVFormatContext *s)
ts->sdt.write_packet = section_write_packet;
ts->sdt.opaque = s;
+ ts->nit.pid = NIT_PID;
+ ts->nit.cc = 15;
+ ts->nit.discontinuity= ts->flags & MPEGTS_FLAG_DISCONT;
+ ts->nit.write_packet = section_write_packet;
+ ts->nit.opaque = s;
+
ts->pkt = av_packet_alloc();
if (!ts->pkt)
return AVERROR(ENOMEM);
@@ -1143,8 +1224,10 @@ static int mpegts_init(AVFormatContext *s)
ts->last_pat_ts = AV_NOPTS_VALUE;
ts->last_sdt_ts = AV_NOPTS_VALUE;
+ ts->last_nit_ts = AV_NOPTS_VALUE;
ts->pat_period = av_rescale(ts->pat_period_us, PCR_TIME_BASE, AV_TIME_BASE);
ts->sdt_period = av_rescale(ts->sdt_period_us, PCR_TIME_BASE, AV_TIME_BASE);
+ ts->nit_period = av_rescale(ts->nit_period_us, PCR_TIME_BASE, AV_TIME_BASE);
if (ts->mux_rate == 1)
av_log(s, AV_LOG_VERBOSE, "muxrate VBR, ");
@@ -1154,12 +1237,14 @@ static int mpegts_init(AVFormatContext *s)
"sdt every %"PRId64" ms, pat/pmt every %"PRId64" ms\n",
av_rescale(ts->sdt_period, 1000, PCR_TIME_BASE),
av_rescale(ts->pat_period, 1000, PCR_TIME_BASE));
+ if (ts->nit_enable > 0)
+ av_log(s, AV_LOG_VERBOSE, "nit every %"PRId64" ms\n", av_rescale(ts->nit_period, 1000, PCR_TIME_BASE));
return 0;
}
-/* send SDT, PAT and PMT tables regularly */
-static void retransmit_si_info(AVFormatContext *s, int force_pat, int force_sdt, int64_t pcr)
+/* send SDT, NIT, PAT and PMT tables regularly */
+static void retransmit_si_info(AVFormatContext *s, int force_pat, int force_sdt, int force_nit, int64_t pcr)
{
MpegTSWrite *ts = s->priv_data;
int i;
@@ -1172,6 +1257,16 @@ static void retransmit_si_info(AVFormatContext *s, int force_pat, int force_sdt,
ts->last_sdt_ts = FFMAX(pcr, ts->last_sdt_ts);
mpegts_write_sdt(s);
}
+ if ((pcr != AV_NOPTS_VALUE && ts->last_nit_ts == AV_NOPTS_VALUE) ||
+ (pcr != AV_NOPTS_VALUE && pcr - ts->last_nit_ts >= ts->nit_period) ||
+ force_nit
+ ) {
+ if (pcr != AV_NOPTS_VALUE)
+ ts->last_nit_ts = FFMAX(pcr, ts->last_nit_ts);
+ if (ts->nit_enable > 0) {
+ mpegts_write_nit(s);
+ }
+ }
if ((pcr != AV_NOPTS_VALUE && ts->last_pat_ts == AV_NOPTS_VALUE) ||
(pcr != AV_NOPTS_VALUE && pcr - ts->last_pat_ts >= ts->pat_period) ||
force_pat) {
@@ -1305,6 +1400,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
int64_t delay = av_rescale(s->max_delay, 90000, AV_TIME_BASE);
int force_pat = st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && key && !ts_st->prev_payload_key;
int force_sdt = 0;
+ int force_nit = 0;
av_assert0(ts_st->payload != buf || st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO);
if (ts->flags & MPEGTS_FLAG_PAT_PMT_AT_FRAMES && st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
@@ -1314,6 +1410,7 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
if (ts->flags & MPEGTS_FLAG_REEMIT_PAT_PMT) {
force_pat = 1;
force_sdt = 1;
+ force_nit = 1;
ts->flags &= ~MPEGTS_FLAG_REEMIT_PAT_PMT;
}
@@ -1325,9 +1422,10 @@ static void mpegts_write_pes(AVFormatContext *s, AVStream *st,
else if (dts != AV_NOPTS_VALUE)
pcr = (dts - delay) * 300;
- retransmit_si_info(s, force_pat, force_sdt, pcr);
+ retransmit_si_info(s, force_pat, force_sdt, force_nit, pcr);
force_pat = 0;
force_sdt = 0;
+ force_nit = 0;
write_pcr = 0;
if (ts->mux_rate > 1) {
@@ -2115,7 +2213,7 @@ static const AVOption options[] = {
{ "initial_discontinuity", "Mark initial packets as discontinuous",
0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_DISCONT }, 0, INT_MAX, ENC, "mpegts_flags" },
{ "mpegts_copyts", "don't offset dts/pts", OFFSET(copyts), AV_OPT_TYPE_BOOL, { .i64 = -1 }, -1, 1, ENC },
- { "tables_version", "set PAT, PMT and SDT version", OFFSET(tables_version), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 31, ENC },
+ { "tables_version", "set PAT, PMT, SDT and NIT version", OFFSET(tables_version), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 31, ENC },
{ "omit_video_pes_length", "Omit the PES packet length for video packets",
OFFSET(omit_video_pes_length), AV_OPT_TYPE_BOOL, { .i64 = 1 }, 0, 1, ENC },
{ "pcr_period", "PCR retransmission time in milliseconds",
@@ -2124,6 +2222,10 @@ static const AVOption options[] = {
OFFSET(pat_period_us), AV_OPT_TYPE_DURATION, { .i64 = PAT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC },
{ "sdt_period", "SDT retransmission time limit in seconds",
OFFSET(sdt_period_us), AV_OPT_TYPE_DURATION, { .i64 = SDT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC },
+ { "nit_period", "NIT retransmission time limit in seconds",
+ OFFSET(nit_period_us), AV_OPT_TYPE_DURATION, { .i64 = NIT_RETRANS_TIME * 1000LL }, 0, INT64_MAX, ENC },
+ { "nit_enable", "Enable NIT transmission",
+ OFFSET(nit_enable), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, ENC },
{ NULL },
};
--
2.11.0
More information about the ffmpeg-devel
mailing list