From patchwork Tue May 18 11:56:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ubaldo Porcheddu X-Patchwork-Id: 27830 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:b214:0:0:0:0:0 with SMTP id b20csp302954iof; Tue, 18 May 2021 04:56:16 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxDbdK5DYtIvRF55ymbHk1XJae8wRpSJBHQ1+tS7gMNbU8JpfqQPHDukxEigUKtJ/CAyk+i X-Received: by 2002:a17:906:2dd3:: with SMTP id h19mr5565632eji.520.1621338976389; Tue, 18 May 2021 04:56:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1621338976; cv=none; d=google.com; s=arc-20160816; b=SEC8AqLJzs9CejMvteQPL8yvtLf5z+6kebMvLjPnClgimhRKf8KkxAttFIu2OjSl0+ iWbrIjDgp9Tn9Z3q5LDoFdq6xRaEI9V/Onuu+GyLMDDigUcIqMamCd36JG8Zt0mi+K/A qcWZabFoDKiJH1ExXabaTvMCkJ+bdh17cGfyLct4qRX5TyWooWQOS9subTp6kg+L9aA0 tRBUNepuaRMpRbvuycQ+DcFqoOHbQMYhH1ViKG+yxP2lmZAGlG8XJYexq7NSZN/FB4cu w3+OYEYGqpqnhPsY3vbXld6E09JBhOHSQCdo9lfIJ8qrkaSoOYclRCX9r27TvjSTZlkJ kYlQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:message-id:date:to:from:delivered-to; bh=2ZcV/MPEbNoWHxFqoYG5AedvrHCUEZ8IkXu95ylMXUc=; b=hkLvIUqsy6st2U9eDPtLTDKzHe6yqNwMLZgo1/EaEf3GgH4ndYHv+/OpmOdp8/JRhb 9KAIHQmcu7tn6ZqyF0FSeSywh9D9u7BhyhnmE2nADQG+N1FquH6xTxsdvuoMk3joQNUD jve79W/0DtO1/nbtQdWbPoEV0TVSDacwCofOGSv+wWk5pvnxGJHTkfjqKE3eKahpx5d5 g6G+m3MpoPoQJdZE21+Qr/lgKFI5FaqXjJtXGJM0cBJBNhCT/wtkByckebz4h2+2YkYD lRKTHETbjXZ8r8E3AvtsM0Cv2v1xaRCTKWin4i5BPJYl908XP3jWcKHwMTitwrGzI4JL i8KA== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id a65si8541540edf.266.2021.05.18.04.56.15; Tue, 18 May 2021 04:56:16 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 777DB688385; Tue, 18 May 2021 14:56:10 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from eja.it (static.143.38.203.116.clients.your-server.de [116.203.38.143]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 977E36818DB for ; Tue, 18 May 2021 14:56:03 +0300 (EEST) Received: by eja.it (Postfix, from userid 1000) id 9148820E2C; Tue, 18 May 2021 13:56:02 +0200 (CEST) From: Ubaldo Porcheddu To: ffmpeg-devel@ffmpeg.org Date: Tue, 18 May 2021 13:56:00 +0200 Message-Id: <20210518115600.10117-1-ubaldo@eja.it> X-Mailer: git-send-email 2.11.0 Subject: [FFmpeg-devel] [PATCH] Add nit flag support v.2 X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Ubaldo Porcheddu MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: w5kB9Hw+itTL 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_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 */ @@ -260,6 +268,10 @@ static void mpegts_write_pat(AVFormatContext *s) int i; q = data; + if (ts->flags & MPEGTS_FLAG_NIT) { + put16(&q, 0x0000); + put16(&q, 0x0010); + } for (i = 0; i < ts->nb_services; i++) { service = ts->services[i]; put16(&q, 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(&q, 0xf000 | ts->provider_name[0]); + *q++ = 0x40; + putbuf(&q, ts->provider_name, ts->provider_name[0]+1); + + //transport_stream_loop_length + loop_len_ptr = q; + q += 2; + put16(&q, ts->transport_stream_id); + put16(&q, 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(&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); +} + /* 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_name; int i, j; int ret; @@ -1022,6 +1077,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 +1204,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 +1246,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 +1420,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 +1430,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 +1442,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 +2219,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 +2228,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 }, };