[FFmpeg-devel] [PATCH] Add nit flag support
Marton Balint
cus at passwd.hu
Tue May 18 00:35:52 EEST 2021
On Mon, 17 May 2021, Ubaldo Porcheddu wrote:
> Signed-off-by: Ubaldo Porcheddu <ubaldo at eja.it>
> ---
> doc/muxers.texi | 2 ++
> libavformat/mpegtsenc.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++---
> 2 files changed, 93 insertions(+), 5 deletions(-)
>
> diff --git a/doc/muxers.texi b/doc/muxers.texi
> index e1c6ad0829..f774d972a6 100644
> --- a/doc/muxers.texi
> +++ b/doc/muxers.texi
> @@ -1876,6 +1876,8 @@ Reemit PAT and PMT at each video frame.
> Conform to System B (DVB) instead of System A (ATSC).
> @item initial_discontinuity
> Mark the initial packet of each stream as discontinuity.
> + at item nit
> +Emit NIT table.
> @end table
>
The new option nit_period is still missing from the docs, please add that
as well.
> @item mpegts_copyts @var{boolean}
> diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
> index e3f9d9ad50..68abccf9ca 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;
> @@ -107,13 +109,18 @@ typedef struct MpegTSWrite {
> #define MPEGTS_FLAG_PAT_PMT_AT_FRAMES 0x04
> #define MPEGTS_FLAG_SYSTEM_B 0x08
> #define MPEGTS_FLAG_DISCONT 0x10
> +#define MPEGTS_FLAG_NIT 0x20
> int flags;
> int copyts;
> 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;
> +
> + uint8_t provider_name[256];
>
> int omit_video_pes_length;
> } MpegTSWrite;
> @@ -227,6 +234,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 +804,47 @@ static void mpegts_write_sdt(AVFormatContext *s)
> data, q - data);
> }
>
> +static void mpegts_write_nit(AVFormatContext *s)
> +{
> + int i;
> + MpegTSWrite *ts = s->priv_data;
> + uint8_t data[SECTION_LENGTH], *q, *desc_len_ptr, *loop_len_ptr;
> + AVProgram *program;
> +
> + q = data;
> +
> + //network name
Use the exact descriptor and field names, e.g.:
// network_descriptors_length
> + put16(&q, 0xf000 | ts->provider_name[0]);
// network_name_descriptor
> + *q++ = 0x40;
> + putbuf(&q, ts->provider_name, ts->provider_name[0]+1);
> +
> + //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 descriptor
> + *q++ = 0x41;
> + *q++ = 3 * ts->nb_services;
> + for(i = 0; i < ts->nb_services; i++) {
> + put16(&q, ts->services[i]->sid);
> + *q++ = ts->service_type;
> + program = s->programs[i];
> + }
> +
> + //calculate lengths
> + put16(&desc_len_ptr, 0xf000 | q - (desc_len_ptr+2));
> + put16(&loop_len_ptr, 0xf000 | q - (loop_len_ptr+2));
> +
> + mpegts_write_section1(&ts->nit, NIT_TID, ts->original_network_id, ts->tables_version, 0, 0,
> + data, q - data);
> +}
If NIT generation is enabled, then you should also include the NIT PID in
PAT for program 0. Please add that.
> +
> /* 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
> @@ -966,6 +1015,8 @@ static void select_pcr_streams(AVFormatContext *s)
> static int mpegts_init(AVFormatContext *s)
> {
> MpegTSWrite *ts = s->priv_data;
> + AVDictionaryEntry *provider;
> + const char *provider_name;
> int i, j;
> int ret;
>
> @@ -1022,6 +1073,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,23 +1200,36 @@ 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);
> +
> + /* assign provider name */
> + provider = av_dict_get(s->metadata, "service_provider", NULL, 0);
> + provider_name = provider ? provider->value : DEFAULT_PROVIDER_NAME;
> + if (encode_str8(ts->provider_name, provider_name) < 0) {
> + av_log(s, AV_LOG_ERROR, "Too long provider name\n");
> + return AVERROR(EINVAL);
> + }
>
> if (ts->mux_rate == 1)
> av_log(s, AV_LOG_VERBOSE, "muxrate VBR, ");
> else
> av_log(s, AV_LOG_VERBOSE, "muxrate %d, ", ts->mux_rate);
> av_log(s, AV_LOG_VERBOSE,
> - "sdt every %"PRId64" ms, pat/pmt every %"PRId64" ms\n",
> + "sdt every %"PRId64" ms, pat/pmt every %"PRId64" ms",
> av_rescale(ts->sdt_period, 1000, PCR_TIME_BASE),
> av_rescale(ts->pat_period, 1000, PCR_TIME_BASE));
> + if (ts->flags & MPEGTS_FLAG_NIT)
> + av_log(s, AV_LOG_VERBOSE, ", nit every %"PRId64" ms", av_rescale(ts->nit_period, 1000, PCR_TIME_BASE));
> + av_log(s, AV_LOG_VERBOSE, "\n");
>
> 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 +1242,15 @@ 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->flags & MPEGTS_FLAG_NIT)
> + 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) {
> @@ -1337,6 +1416,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) {
> @@ -1346,6 +1426,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;
> }
>
> @@ -1357,9 +1438,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) {
> @@ -2133,7 +2215,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",
> @@ -2142,6 +2224,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 NIT transmission",
> + 0, AV_OPT_TYPE_CONST, { .i64 = MPEGTS_FLAG_NIT}, 0, INT_MAX, ENC, "mpegts_flags" },
> { NULL },
> };
>
Thanks,
Marton
More information about the ffmpeg-devel
mailing list