Message ID | 20210427151520.10044-1-ubaldo@eja.it |
---|---|
State | New |
Headers | show |
Series | [FFmpeg-devel] Add optional NIT table generation | expand |
Context | Check | Description |
---|---|---|
andriy/x86_make | success | Make finished |
andriy/x86_make_fate | success | Make fate finished |
andriy/PPC64_make | success | Make finished |
andriy/PPC64_make_fate | success | Make fate finished |
On Tue, 27 Apr 2021, Ubaldo Porcheddu wrote: > Signed-off-by: Ubaldo Porcheddu <ubaldo@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; Instead of this, you should add a new MPEGTS_FLAG. > } 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); encode_str8 should be used instead. Better yet, you should pre-calculate that at init time, and used the already encoded value here directly. > + > + //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 ts->service_type Preferably you should use 4 space indentation. > + } > + > + //private data > + desc_len += 6 + 2; > + *q++ = 0x5F; > + *q++ = 4; > + *q++ = 0x00; > + *q++ = 0x00; > + put16(&q, 40); What are these? > + > + //virtual channel > + len = 4 * ts->nb_services; > + desc_len += len + 2; > + *q++ = 0x83; > + *q++ = len; Simply *q++ = 4 * ts->nb_services > + 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); indentation > + } > + > + //calculate lengths > + put16(&desc_len_ptr, 0xf000 | desc_len); > + put16(&loop_len_ptr, 0xf000 | desc_len+6); You should not calculate desc_len, you should use (q - desc_len_ptr) directly, it is less error-prone that way. > + > + 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)); Maybe you should continue the last log line which already describes the interval of various tables, and not start a new line. > > 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); > + } > + } indentation > 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 }, As I wrote earlier, instead of a separate option, you should introduce a new flag in mpegts_flags. Also docs/muxers.texi update is missing for the new feature. Regards, Marton
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 }, };
Signed-off-by: Ubaldo Porcheddu <ubaldo@eja.it> --- libavformat/mpegtsenc.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 106 insertions(+), 4 deletions(-)