[FFmpeg-devel] [PATCH] Add nit flag support v.2

2021-05-18 Thread Ubaldo Porcheddu
Signed-off-by: Ubaldo Porcheddu 
---
 doc/muxers.texi |   7 +++-
 libavformat/mpegtsenc.c | 100 +---
 2 files changed, 101 insertions(+), 6 deletions(-)

diff --git a/doc/muxers.texi b/doc/muxers.texi
index e1c6ad0829..e77055e7ef 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.
+@item nit
+Emit NIT table.
 @end table
 
 @item mpegts_copyts @var{boolean}
@@ -1897,8 +1899,11 @@ Maximum time in seconds between PAT/PMT tables. Default 
is @code{0.1}.
 @item sdt_period @var{duration}
 Maximum time in seconds between SDT tables. Default is @code{0.5}.
 
+@item nit_period @var{duration}
+Maximum time in seconds between NIT tables. Default is @code{0.5}.
+
 @item tables_version @var{integer}
-Set PAT, PMT and SDT version (default @code{0}, valid values are from 0 to 31, 
inclusively).
+Set PAT, PMT, SDT and NIT version (default @code{0}, valid values are from 0 
to 31, inclusively).
 This option allows updating stream structure so that standard consumer may
 detect the change. To do so, reopen output @code{AVFormatContext} (in case of 
API
 usage) or restart @command{ffmpeg} instance, cyclically changing
diff --git a/libavformat/mpegtsenc.c b/libavformat/mpegtsenc.c
index e3f9d9ad50..47c8e7337a 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_B0x08
 #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 */
@@ -260,6 +268,10 @@ static void mpegts_write_pat(AVFormatContext *s)
 int i;
 
 q = data;
+if (ts->flags & MPEGTS_FLAG_NIT) {
+put16(, 0x);
+put16(, 0x0010);
+}
 for (i = 0; i < ts->nb_services; i++) {
 service = ts->services[i];
 put16(, service->sid);
@@ -796,6 +808,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_descriptor
+put16(, 0xf000 | ts->provider_name[0]);
+*q++ = 0x40;
+putbuf(, ts->provider_name, ts->provider_name[0]+1);
+
+//transport_stream_loop_length
+loop_len_ptr = q;  
+q += 2;
+put16(, ts->transport_stream_id);
+put16(, ts->original_network_id);
+   
+//transport_descriptors_length
+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(, ts->services[i]->sid);
+*q++ = ts->service_type;
+program = s->programs[i];
+}
+   
+//calculate lengths
+put16(_len_ptr, 0xf000 | q - (desc_len_ptr+2));
+put16(_len_ptr, 0xf000 | q - (loop_len_ptr+2));
+
+mpegts_write_section1(>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
@@ -966,6 +1019,8 @@ static void select_pcr_streams(AVFormatContext *s)
 static int mpegts_init(AVFormatContext *s)
 {
 MpegTSWrite *ts = s->priv_data;
+AVDictionaryEntry *provider;
+const char *provider_

[FFmpeg-devel] [PATCH] Add nit flag support

2021-05-17 Thread Ubaldo Porcheddu
Signed-off-by: Ubaldo Porcheddu 
---
 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.
+@item nit
+Emit NIT table.
 @end table
 
 @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_B0x08
 #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
+put16(, 0xf000 | ts->provider_name[0]);
+*q++ = 0x40;
+putbuf(, ts->provider_name, ts->provider_name[0]+1);
+
+//TS loop
+loop_len_ptr = q;  
+q += 2;
+put16(, ts->transport_stream_id);
+put16(, 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(, ts->services[i]->sid);
+*q++ = ts->service_type;
+program = s->programs[i];
+}
+   
+//calculate lengths
+put16(_len_ptr, 0xf000 | q - (desc_len_ptr+2));
+put16(_len_ptr, 0xf000 | q - (loop_len_ptr+2));
+
+mpegts_write_section1(>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
@@ -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 = provid

Re: [FFmpeg-devel] [PATCH] Add optional NIT table generation

2021-05-14 Thread Ubaldo Porcheddu

Hola David,

thank you for clarifying it, maybe we leave 0x28 by default and add a 
"nit_private" option for: none, NorDig, UK ?


Here is the function so far:

+static void mpegts_write_nit(AVFormatContext *s)
+{
+int i, lcn_count=0;
+MpegTSWrite *ts = s->priv_data;
+uint8_t data[SECTION_LENGTH], *q, *desc_len_ptr, *loop_len_ptr, 
*lcn_len_ptr;

+AVDictionaryEntry *logical_channel;
+AVProgram *program;
+
+q = data;
+
+//network name
+put16(, 0xf000 | ts->provider_name[0]);
+*q++ = 0x40;
+putbuf(, ts->provider_name, ts->provider_name[0]+1);
+
+//TS loop
+loop_len_ptr = q;
+q += 2;
+put16(, ts->transport_stream_id);
+put16(, 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(, ts->services[i]->sid);
+*q++ = ts->service_type;
+program = s->programs[i];
+if (av_dict_get(program->metadata, "logical_channel_number", 
NULL, 0))

+lcn_count++;
+}
+
+if (lcn_count > 0) {
+//logical channel descriptor
+*q++ = 0x83;
+lcn_len_ptr = q++;
+for (i = 0; i < ts->nb_services; i++) {
+program = s->programs[i];
+logical_channel = av_dict_get(program->metadata, 
"logical_channel_number", NULL, 0);

+if (logical_channel) {
+put16(, ts->services[i]->sid);
+put16(, 0xfc00 | atoi(logical_channel->value));
+}
+}
+*lcn_len_ptr = lcn_count * 4;
+//private data specifier descriptor
+*q++ = 0x5F;
+*q++ = 0x04;
+put16(, 0x00);
+put16(, 0x28);
+}
+
+//calculate lengths
+put16(_len_ptr, 0xf000 | q - (desc_len_ptr+2));
+put16(_len_ptr, 0xf000 | q - (loop_len_ptr+2));
+
+mpegts_write_section1(>nit, NIT_TID, ts->original_network_id, 
ts->tables_version, 0, 0,

+  data, q - data);
+}



Hi,


Hi Marton,

Il 2021-05-12 19:18 Marton Balint ha scritto:
> On Wed, 12 May 2021, Ubaldo Porcheddu wrote:
>
>> Hi Marton,
>>
>>>> +}
>>>> +
>>>> +//private data
>>>> +desc_len += 6 + 2;
>>>> +*q++ = 0x5F;
>>>> +*q++ = 4;
>>>> +*q++ = 0x00;
>>>> +*q++ = 0x00;
>>>> +put16(, 40);
>>>
>>> What are these?
>>
>> I didn't find any official document about it but this seems to be the
>> way many national (FR,IT,UK) broadcasters are writing the virtual
>> channel private data header and the one recognized by many popular tv
>> on the market.
>
> But this looks like a separate descriptor from the virtual channels
> (logical_channel_descriptor).

I think it is better to remove it until it is more clear what it does,
or maybe we add an extra mpegts flag like "nit_lcn_extra" ?



The private data specifier descriptor is needed for the TV to
correctly process the LCN descriptor. When it is needed, you have to
include both or none.
Also, in the Scandinavian countries, that use the NORDIG spec, the LCN
descriptor has a different format, though I think the European format
may still be understood.

Specs can be found here for NORDIG :
https://nordig.org/wp-content/uploads/2017/03/NorDig-Unified_ver_2_6.pdf

Specs for the European LCN can be found here, it's the French spec,
but the rest are the same:
https://www.csa.fr/web/index.php/content/download/253685/723620/version/1/file/CSA-Signalling-Profilev3.4.pdf

Specs for the Australian LCN descriptor can be found here:
https://www.freetv.com.au/wp-content/uploads/2019/08/OP-41-LCN-Descriptor-Issue-8-July-2016.pdf

The rules, as far as I know them are:

NORDIG --> Private Data Specifier = 0x0029,  LCN Descriptor Tag =
0x83 (legacy), 0x87 (Nordig version)
United Kingdom --> Private Data Specifier = 0x233A,  LCN
Descriptor Tag = 0x83
Rest of Europe + Most of the World --> Private Data Specifier =
0x0028,  LCN Descriptor Tag = 0x83
Australia --> _Do_not_include_Private_data_specifier_descriptor_ , LCN
Descriptor Tag = 0x83

My suggestion would be to add an "LCN_mode" parameters, with values:
NO_LCN, Australia,UK,Nordig, etc

Regards

David


___
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".

___
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".


Re: [FFmpeg-devel] [PATCH] Add optional NIT table generation

2021-05-14 Thread Ubaldo Porcheddu

Hi Marton,

Il 2021-05-12 19:18 Marton Balint ha scritto:

On Wed, 12 May 2021, Ubaldo Porcheddu wrote:


Hi Marton,


+}
+
+//private data
+desc_len += 6 + 2;
+*q++ = 0x5F;
+*q++ = 4;
+*q++ = 0x00;
+*q++ = 0x00;
+put16(, 40);


What are these?


I didn't find any official document about it but this seems to be the 
way many national (FR,IT,UK) broadcasters are writing the virtual 
channel private data header and the one recognized by many popular tv 
on the market.


But this looks like a separate descriptor from the virtual channels
(logical_channel_descriptor).


I think it is better to remove it until it is more clear what it does, 
or maybe we add an extra mpegts flag like "nit_lcn_extra" ?



logical_channel_descriptor() is documented for example here:
https://forums.mediaspy.org/uploads/short-url/2wA2rGhOkh2yjlbcWMtcQizBv8L.pdf

So you should use the terminology that is used in the document above.


Ok, rewritten accordly.


+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.


The idea is to have the NIT emission optional so I don't know if is a 
good idea to stat its periodical transmission on the same line, when 
disabled?


I meant something like:

av_log(s, AV_LOG_VERBOSE,
   "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 (nit_enabled)
 av_log(s, AV_LOG_VERBOSE, ", nit every %"PRId64" ms")
av_log(s, AV_LOG_VERBOSE, "\n")


Ok, clear now :)


Also docs/muxers.texi update is missing for the new feature.


is there any guide on what an how to add it? :)


No guide, but should be straightforward based on the existing
documentation of the mpegts muxer.


You mean something like this (I am changing "nit_enable" option with 
"nit_emit" flag):


--- ffmpeg.old/doc/muxers.texi  2021-05-09 18:20:04.0 +0200
+++ ffmpeg.new/doc/muxers.texi  2021-05-12 16:52:38.368198223 +0200
@@ -1876,6 +1876,8 @@
 Conform to System B (DVB) instead of System A (ATSC).
 @item initial_discontinuity
 Mark the initial packet of each stream as discontinuity.
+@item nit_emit
+Emit NIT table.
 @end table

___
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".


Re: [FFmpeg-devel] [PATCH] Add optional NIT table generation

2021-05-12 Thread Ubaldo Porcheddu

Hi Marton,


+}
+
+//private data
+desc_len += 6 + 2;
+*q++ = 0x5F;
+*q++ = 4;
+*q++ = 0x00;
+*q++ = 0x00;
+put16(, 40);


What are these?


I didn't find any official document about it but this seems to be the 
way many national (FR,IT,UK) broadcasters are writing the virtual 
channel private data header and the one recognized by many popular tv on 
the market.



+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.


The idea is to have the NIT emission optional so I don't know if is a 
good idea to stat its periodical transmission on the same line, when 
disabled?



+{ "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.


ok almost ready also with this


Also docs/muxers.texi update is missing for the new feature.


is there any guide on what an how to add it? :)

___
ffmpeg-devel mailing list
ffmpeg-devel@ffmpeg.org
https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

To unsubscribe, visit link above, or email
ffmpeg-devel-requ...@ffmpeg.org with subject "unsubscribe".


[FFmpeg-devel] [PATCH] Add optional NIT table generation

2021-04-27 Thread Ubaldo Porcheddu
Signed-off-by: Ubaldo Porcheddu 
---
 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(, 0xf000 | provider_len);
+*q++ = 0x40;
+*q++ = provider_len;
+putbuf(, provider_name, provider_len);
+
+//TS loop
+loop_len_ptr = q;  
+q += 2;
+put16(, ts->transport_stream_id);
+put16(, 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(, 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(, 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(, ts->services[i]->sid); 
+  put16(, 0xfc00 | virtual_channel_value);
+}
+
+//calculate lengths
+put16(_len_ptr, 0xf000 | desc_len);
+put16(_len_ptr, 0xf000 | desc_len+6);
+
+mpegts_write_section1(>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_