From patchwork Wed Feb 3 14:12:06 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gyan Doshi X-Patchwork-Id: 25372 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id 7334044985A for ; Wed, 3 Feb 2021 16:12:41 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 450FC6898D8; Wed, 3 Feb 2021 16:12:41 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mout-p-102.mailbox.org (mout-p-102.mailbox.org [80.241.56.152]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 545B56881A2 for ; Wed, 3 Feb 2021 16:12:34 +0200 (EET) Received: from smtp2.mailbox.org (smtp2.mailbox.org [IPv6:2001:67c:2050:105:465:1:2:0]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange ECDHE (P-384) server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by mout-p-102.mailbox.org (Postfix) with ESMTPS id 4DW3Xm4mKYzQkm3 for ; Wed, 3 Feb 2021 15:12:24 +0100 (CET) X-Virus-Scanned: amavisd-new at heinlein-support.de Received: from smtp2.mailbox.org ([80.241.60.241]) by spamfilter04.heinlein-hosting.de (spamfilter04.heinlein-hosting.de [80.241.56.122]) (amavisd-new, port 10030) with ESMTP id 5Ies5FGr9THz for ; Wed, 3 Feb 2021 15:12:21 +0100 (CET) From: Gyan Doshi To: ffmpeg-devel@ffmpeg.org Date: Wed, 3 Feb 2021 19:42:06 +0530 Message-Id: <20210203141206.1853-1-ffmpeg@gyani.pro> In-Reply-To: <161235001389.13923.11374388814564495596@lain.red.khirnov.net> References: <161235001389.13923.11374388814564495596@lain.red.khirnov.net> MIME-Version: 1.0 X-MBO-SPAM-Probability: * X-Rspamd-Score: 0.12 / 15.00 / 15.00 X-Rspamd-Queue-Id: B390817DE X-Rspamd-UID: e84839 Subject: [FFmpeg-devel] [PATCH v3] ffmpeg: add -fpsmax to clamp output framerate X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" Useful when encoding in batch or with aberrant inputs. --- doc/ffmpeg.texi | 7 +++++++ fftools/ffmpeg.c | 7 ++++++- fftools/ffmpeg.h | 3 +++ fftools/ffmpeg_opt.c | 23 ++++++++++++++++++++--- 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/doc/ffmpeg.texi b/doc/ffmpeg.texi index 8eb012b7c0..b0d1cf0710 100644 --- a/doc/ffmpeg.texi +++ b/doc/ffmpeg.texi @@ -759,6 +759,13 @@ If in doubt use @option{-framerate} instead of the input option @option{-r}. As an output option, duplicate or drop input frames to achieve constant output frame rate @var{fps}. +@item -fpsmax[:@var{stream_specifier}] @var{fps} (@emph{output,per-stream}) +Set maximum frame rate (Hz value, fraction or abbreviation). + +Clamps output frame rate when output framerate is auto-set and is higher than this value. +Useful in batch processing or when input framerate is wrongly detected as very high. +It cannot be set together with @code{-r}. It is ignored during streamcopy. + @item -s[:@var{stream_specifier}] @var{size} (@emph{input/output,per-stream}) Set frame size. diff --git a/fftools/ffmpeg.c b/fftools/ffmpeg.c index d7c833be63..add5a3e505 100644 --- a/fftools/ffmpeg.c +++ b/fftools/ffmpeg.c @@ -3376,7 +3376,7 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame) ost->frame_rate = ist->framerate; if (ist && !ost->frame_rate.num) ost->frame_rate = ist->st->r_frame_rate; - if (ist && !ost->frame_rate.num) { + if (ist && !ost->frame_rate.num && !ost->max_frame_rate.num) { ost->frame_rate = (AVRational){25, 1}; av_log(NULL, AV_LOG_WARNING, "No information " @@ -3386,6 +3386,11 @@ static int init_output_stream_encode(OutputStream *ost, AVFrame *frame) ost->file_index, ost->index); } + if (ost->max_frame_rate.num && + (av_q2d(ost->frame_rate) > av_q2d(ost->max_frame_rate) || + !ost->frame_rate.den)) + ost->frame_rate = ost->max_frame_rate; + if (ost->enc->supported_framerates && !ost->force_fps) { int idx = av_find_nearest_q_idx(ost->frame_rate, ost->enc->supported_framerates); ost->frame_rate = ost->enc->supported_framerates[idx]; diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 5aeceae6b7..423da071dc 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -108,6 +108,8 @@ typedef struct OptionsContext { int nb_audio_sample_rate; SpecifierOpt *frame_rates; int nb_frame_rates; + SpecifierOpt *max_frame_rates; + int nb_max_frame_rates; SpecifierOpt *frame_sizes; int nb_frame_sizes; SpecifierOpt *frame_pix_fmts; @@ -479,6 +481,7 @@ typedef struct OutputStream { /* video only */ AVRational frame_rate; + AVRational max_frame_rate; int is_cfr; int force_fps; int top_field_first; diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index bf2eb26246..805754953e 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -55,6 +55,7 @@ static const char *const opt_name_codec_names[] = {"c", "codec", " static const char *const opt_name_audio_channels[] = {"ac", NULL}; static const char *const opt_name_audio_sample_rate[] = {"ar", NULL}; static const char *const opt_name_frame_rates[] = {"r", NULL}; +static const char *const opt_name_max_frame_rates[] = {"fpsmax", NULL}; static const char *const opt_name_frame_sizes[] = {"s", NULL}; static const char *const opt_name_frame_pix_fmts[] = {"pix_fmt", NULL}; static const char *const opt_name_ts_scale[] = {"itsscale", NULL}; @@ -1688,7 +1689,7 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, in AVStream *st; OutputStream *ost; AVCodecContext *video_enc; - char *frame_rate = NULL, *frame_aspect_ratio = NULL; + char *frame_rate = NULL, *max_frame_rate = NULL, *frame_aspect_ratio = NULL; ost = new_output_stream(o, oc, AVMEDIA_TYPE_VIDEO, source_index); st = ost->st; @@ -1699,8 +1700,21 @@ static OutputStream *new_video_stream(OptionsContext *o, AVFormatContext *oc, in av_log(NULL, AV_LOG_FATAL, "Invalid framerate value: %s\n", frame_rate); exit_program(1); } - if (frame_rate && video_sync_method == VSYNC_PASSTHROUGH) - av_log(NULL, AV_LOG_ERROR, "Using -vsync 0 and -r can produce invalid output files\n"); + + MATCH_PER_STREAM_OPT(max_frame_rates, str, max_frame_rate, oc, st); + if (max_frame_rate && av_parse_video_rate(&ost->max_frame_rate, max_frame_rate) < 0) { + av_log(NULL, AV_LOG_FATAL, "Invalid maximum framerate value: %s\n", max_frame_rate); + exit_program(1); + } + + if (frame_rate && max_frame_rate) { + av_log(NULL, AV_LOG_ERROR, "Only one of -fpsmax and -r can be set for a stream.\n"); + exit_program(1); + } + + if ((frame_rate || max_frame_rate) && + video_sync_method == VSYNC_PASSTHROUGH) + av_log(NULL, AV_LOG_ERROR, "Using -vsync 0 and -r/-fpsmax can produce invalid output files\n"); MATCH_PER_STREAM_OPT(frame_aspect_ratios, str, frame_aspect_ratio, oc, st); if (frame_aspect_ratio) { @@ -3596,6 +3610,9 @@ const OptionDef options[] = { { "r", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_SPEC | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(frame_rates) }, "set frame rate (Hz value, fraction or abbreviation)", "rate" }, + { "fpsmax", OPT_VIDEO | HAS_ARG | OPT_STRING | OPT_SPEC | + OPT_OUTPUT, { .off = OFFSET(max_frame_rates) }, + "set max frame rate (Hz value, fraction or abbreviation)", "rate" }, { "s", OPT_VIDEO | HAS_ARG | OPT_SUBTITLE | OPT_STRING | OPT_SPEC | OPT_INPUT | OPT_OUTPUT, { .off = OFFSET(frame_sizes) }, "set frame size (WxH or abbreviation)", "size" },