From patchwork Tue Apr 27 15:15:20 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ubaldo Porcheddu X-Patchwork-Id: 27438 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a6b:6109:0:0:0:0:0 with SMTP id v9csp1062416iob; Tue, 27 Apr 2021 08:15:47 -0700 (PDT) X-Google-Smtp-Source: ABdhPJyFEYI4/zwMLsBlOCTfP/Lj7qCJByiig0ClH2x2LBRb6+Q0a1vmnJ8nprX6bIQ3wIIy0zhy X-Received: by 2002:a05:6402:6d4:: with SMTP id n20mr5009445edy.134.1619536547726; Tue, 27 Apr 2021 08:15:47 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1619536547; cv=none; d=google.com; s=arc-20160816; b=At0JZQSqqje1TTrCXk7RF+0vBqO3V0/XVxfS2Vrp6Ui/5YRtuEg/d6UjSezrqxxlIc nWMH0NUA6I8xZjgiadxjud2TD57CXE0ZjzdD9p4/V+KZFi/7IZr2O3CmUKd3OjMNh4Y1 oRoHpte7wIrpKKZ9H/EIVOm62XltgzSLfDievnMe5AfpLHS63KgytgATeFTBkMvjPQYJ MkCGS+8uFvaYMJwvf4cIXP71sp8ndbN7BIHTLQXQinayX36JhKRVcYmvFK+zY0l+Gnbx vYHbQioV+TyhG0emFivUgJpcfrOuxWKQ3b3qu4VabCtnciMW9cZEY+PfrxqAEgJntDJ2 3ebw== 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=BhCCjhGIhLeUadmNxcWA2l5R5267wjWp2HheaVC9MyY=; b=WHn1JlUad4XdTTlAAkmYUQxcuB+heGzSy2VaSBMa8xxErpKNuLiv4WZlvSrusnj1p3 iwqHH3FsMjJVdEjGoCELfGB1ipFSdZydzx+OSjxkRJJ1DyJNmFsitgBbYqvY703udSRO RNysVPVnKfDoGig84WxhexqChV6HI26f0JjacTMN5IYwazug68S0jTO+oUUhTeyEfNHw 0Ps3e94m+fog7TtLcvP9mByeJizDVt1oiT7EEfesX7id17DVHqT73X21OmYOQN//hPhN DmDgT1xwdK4H2qAFLy0YXXSXCvNC4y3bubevcFg+aYm5CSKNxF6F8P5ZICJVIy6E3q0R Vk2A== 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 jp4si124480ejb.420.2021.04.27.08.15.46; Tue, 27 Apr 2021 08:15:47 -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 0C59B689FFE; Tue, 27 Apr 2021 18:15:42 +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 09A33689966 for ; Tue, 27 Apr 2021 18:15:34 +0300 (EEST) Received: by eja.it (Postfix, from userid 1000) id 2C67120E2C; Tue, 27 Apr 2021 17:15:34 +0200 (CEST) From: Ubaldo Porcheddu To: ffmpeg-devel@ffmpeg.org Date: Tue, 27 Apr 2021 17:15:20 +0200 Message-Id: <20210427151520.10044-1-ubaldo@eja.it> X-Mailer: git-send-email 2.11.0 Subject: [FFmpeg-devel] [PATCH] Add optional NIT table generation 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: yX6iOVDPeDPX 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(&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 }, };