From patchwork Sat Aug 10 17:16:16 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Khirnov X-Patchwork-Id: 50968 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:612c:1ff2:b0:489:2eb3:e4c4 with SMTP id ks18csp527286vqb; Sat, 10 Aug 2024 10:17:36 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCU9vZ+Xbyc3rbBWEkowsmkN9deYhQyENWOJw8q/ASJNw/WeU2M5oMd9RP0sYtP6NieQjyVWQycPZoEq5llLl3amni9zndf7ZkjP1A== X-Google-Smtp-Source: AGHT+IGI5bIaWESmWkS5CWVDjUsLX+l60SnGQy/l5R6HzsHYd9lDoVSqiz5XW/pbWM/L55HFhU1C X-Received: by 2002:a05:6512:282a:b0:52e:91ff:4709 with SMTP id 2adb3069b0e04-530ee988633mr3837042e87.21.1723310255857; Sat, 10 Aug 2024 10:17:35 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1723310255; cv=none; d=google.com; s=arc-20160816; b=tvwLmM8lC4hghQW7+R1UyJDIxjvxix6xqbR1EJPyj94Pe1frv38k2eG8/rxv6+dza3 IwbDxenhuOxhVEk+HG2iRPKlbuQGBDYjEm/upzduiRpjceiuFc4rbNJXIqXrSKq13hV1 qgB9PvOgRnXrgtyVUsaWSLJ3yy79aFbZ5T2wlkempeGvkFKlxAEak39BHqImGLy4c44v W4bdgo8ZBxnN3S1jEWCeeAveM7KHpDVO4VfTETe1v6ATK8WAzwNnGVqPqNpXcCptDlTF W1gG7+b34QYQH/BoFb6kk0JBMfjw9OxCYWnzm8JUxdvgLGrj8Sjii6NWgQBp1PPdAN03 E3Iw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:message-id:date:to:from :dkim-signature:delivered-to; bh=SDLoZcoBrxNGo9ElJ1AKAwPpWj6gR7Q8syOW1Xzh5ZA=; fh=YOA8vD9MJZuwZ71F/05pj6KdCjf6jQRmzLS+CATXUQk=; b=UXoFFkj/YsFQ5ZJT9b3VXzU39btSZ4Kbmx4F3yFTtDnT8H59Ih5zZ7O0d/tCnMfuBy B4q2af7j8l0V5sAzeQ8Lxwn53q1Ty/d6lLfeKUZGTtY4i6FrBPgb3L8NVddfWH4Oggz/ 1wuCqWAg0wDXc1zFH215BF+QPttjkIDg+ZwCrLsAA33DZtUznuON4FYTi84VVO+6IGNW eyuGIVOxzwbGb6uHPBbZ79gg9rJFHW/NmumwrpTrcz1oAcksqc8JRHLAcJqYK8zoygQW DlKoRIkMcy61y3uSu6aYhnJWDaZWzgJhHht0OYzWVdsNXcVhP1ljJhrEKfJ3HPcHE7ng QqhQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b=O+DaEH30; 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 2adb3069b0e04-53200e9619asi546937e87.19.2024.08.10.10.17.35; Sat, 10 Aug 2024 10:17:35 -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; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b=O+DaEH30; 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 52B0968DB04; Sat, 10 Aug 2024 20:16:47 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail1.khirnov.net (quelana.khirnov.net [94.230.150.81]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 4113468DAE4 for ; Sat, 10 Aug 2024 20:16:40 +0300 (EEST) Authentication-Results: mail1.khirnov.net; dkim=pass (2048-bit key; unprotected) header.d=khirnov.net header.i=@khirnov.net header.a=rsa-sha256 header.s=mail header.b=O+DaEH30; dkim-atps=neutral Received: from localhost (mail1.khirnov.net [IPv6:::1]) by mail1.khirnov.net (Postfix) with ESMTP id 9611F4DBA for ; Sat, 10 Aug 2024 19:16:36 +0200 (CEST) Received: from mail1.khirnov.net ([IPv6:::1]) by localhost (mail1.khirnov.net [IPv6:::1]) (amavis, port 10024) with ESMTP id M2bScrSoWn6w for ; Sat, 10 Aug 2024 19:16:35 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=khirnov.net; s=mail; t=1723310193; bh=IAnqmF6zLriV23q33XvJjY7WqREH7ZXQrmT773j7dd0=; h=From:To:Subject:Date:From; b=O+DaEH30qkx5AGRZfQCdJl0s1m0zNWmZXMDu93iwJLYHOCGfgZTRP4eoryk+95uzl xdz0oQbGbvJbYpB5ABpWb34k4Nh07iJbMLuEVXqj5ZzNUKMRgxBbCBUTcLHVH5YOK2 yAMHv/HfWypt1X3wQYebjKK4KUBJFBaVJTpkZQR2AyfCtp3QauHdXUjqqlgRhPFuWO 2Q/zM5W8bDW/EdliLkaiKPkSLXFrdMNPyQOcFSB9lrAg51pUNY6XI/YoEEruj99fOz yvvKx+TIZukzgdK5Elua5L04DT8evfKg8EB6jBxvfjyC4tCsWItyrt/7Ka/tj/a7nV mhvJeJWRdv71A== Received: from libav.khirnov.net (libav.khirnov.net [IPv6:2a00:c500:561:201::7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256 client-signature RSA-PSS (2048 bits) client-digest SHA256) (Client CN "libav.khirnov.net", Issuer "smtp.khirnov.net SMTP CA" (verified OK)) by mail1.khirnov.net (Postfix) with ESMTPS id A9D494DA7 for ; Sat, 10 Aug 2024 19:16:32 +0200 (CEST) Received: from libav.khirnov.net (libav.khirnov.net [IPv6:::1]) by libav.khirnov.net (Postfix) with ESMTP id 36F4A3A02CA for ; Sat, 10 Aug 2024 19:16:26 +0200 (CEST) From: Anton Khirnov To: ffmpeg-devel@ffmpeg.org Date: Sat, 10 Aug 2024 19:16:16 +0200 Message-ID: <20240810171621.26757-1-anton@khirnov.net> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 1/6] fftools/ffmpeg: replace MATCH_PER_STREAM_OPT(.., str, ..) with a function 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: 0kJbkhwj/QZM This has multiple advantages: * The macro has multiple parameters that often have similar or identical values, yet very different meanings (one is the name of the OptionsContext member where the parsed options are stored, the other the name of the variable into which the result is written); this change makes each of these explicit. * The macro returns on failure, which may cause leaks - this was the reason for adding MATCH_PER_STREAM_OPT_CLEAN(), also ost_add() currently leaks encoder_opts. The new function returns failure to its caller, which decides how to deal with it. While that adds a lot of error checks/forwards for now, those will be reduced in following commits. * new code is type- and const- correct Invocations of MATCH_PER_STREAM_OPT() with other types will be converted in following commits. --- fftools/cmdutils.c | 6 +- fftools/cmdutils.h | 3 + fftools/ffmpeg.h | 4 +- fftools/ffmpeg_demux.c | 74 ++++++++++++------ fftools/ffmpeg_mux.h | 2 +- fftools/ffmpeg_mux_init.c | 156 +++++++++++++++++++++++++++----------- fftools/ffmpeg_opt.c | 67 ++++++++++++++++ 7 files changed, 242 insertions(+), 70 deletions(-) diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index 9b18cf5e4d..6286fd87f7 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -246,6 +246,8 @@ static int write_option(void *optctx, const OptionDef *po, const char *opt, (uint8_t *)optctx + po->u.off : po->u.dst_ptr; char *arg_allocated = NULL; + enum OptionType so_type = po->type; + SpecifierOptList *sol = NULL; double num; int ret = 0; @@ -310,6 +312,7 @@ static int write_option(void *optctx, const OptionDef *po, const char *opt, goto finish; *(int *)dst = num; + so_type = OPT_TYPE_INT; } else if (po->type == OPT_TYPE_INT64) { ret = parse_number(opt, arg, OPT_TYPE_INT64, INT64_MIN, (double)INT64_MAX, &num); if (ret < 0) @@ -323,6 +326,7 @@ static int write_option(void *optctx, const OptionDef *po, const char *opt, opt, arg); goto finish; } + so_type = OPT_TYPE_INT64; } else if (po->type == OPT_TYPE_FLOAT) { ret = parse_number(opt, arg, OPT_TYPE_FLOAT, -INFINITY, INFINITY, &num); if (ret < 0) @@ -352,7 +356,7 @@ static int write_option(void *optctx, const OptionDef *po, const char *opt, } if (sol) { - sol->type = po->type; + sol->type = so_type; sol->opt_canon = (po->flags & OPT_HAS_CANON) ? find_option(defs, po->u1.name_canon) : po; } diff --git a/fftools/cmdutils.h b/fftools/cmdutils.h index 14340dff7d..abc8d26607 100644 --- a/fftools/cmdutils.h +++ b/fftools/cmdutils.h @@ -120,6 +120,9 @@ typedef struct SpecifierOptList { /* Canonical option definition that was parsed into this list. */ const struct OptionDef *opt_canon; + /* Type corresponding to the field that should be used from SpecifierOpt.u. + * May not match the option type, e.g. OPT_TYPE_BOOL options are stored as + * int, so this field would be OPT_TYPE_INT for them */ enum OptionType type; } SpecifierOptList; diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index d0298d53cf..7d82d7d7c2 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -593,7 +593,7 @@ typedef struct OutputStream { KeyframeForceCtx kf; - char *logfile_prefix; + const char *logfile_prefix; FILE *logfile; // simple filtergraph feeding this stream, if any @@ -902,6 +902,8 @@ void update_benchmark(const char *fmt, ...); const char *opt_match_per_type_str(const SpecifierOptList *sol, char mediatype); +int opt_match_per_stream_str(void *logctx, const SpecifierOptList *sol, + AVFormatContext *fc, AVStream *st, const char **out); int muxer_thread(void *arg); int encoder_thread(void *arg); diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c index 6f7d78c896..0c92d31c10 100644 --- a/fftools/ffmpeg_demux.c +++ b/fftools/ffmpeg_demux.c @@ -1077,14 +1077,19 @@ int ist_filter_add(InputStream *ist, InputFilter *ifilter, int is_simple, return ds->sch_idx_dec; } -static int choose_decoder(const OptionsContext *o, AVFormatContext *s, AVStream *st, +static int choose_decoder(const OptionsContext *o, void *logctx, + AVFormatContext *s, AVStream *st, enum HWAccelID hwaccel_id, enum AVHWDeviceType hwaccel_device_type, const AVCodec **pcodec) { - char *codec_name = NULL; + const char *codec_name = NULL; + int ret; + + ret = opt_match_per_stream_str(logctx, &o->codec_names, s, st, &codec_name); + if (ret < 0) + return ret; - MATCH_PER_STREAM_OPT(codec_names, str, codec_name, s, st); if (codec_name) { int ret = find_codec(NULL, codec_name, st->codecpar->codec_type, 0, pcodec); if (ret < 0) @@ -1226,14 +1231,14 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona AVCodecParameters *par = st->codecpar; DemuxStream *ds; InputStream *ist; - char *framerate = NULL, *hwaccel_device = NULL; + const char *framerate = NULL, *hwaccel_device = NULL; const char *hwaccel = NULL; const char *apply_cropping = NULL; - char *hwaccel_output_format = NULL; - char *codec_tag = NULL; - char *bsfs = NULL; + const char *hwaccel_output_format = NULL; + const char *codec_tag = NULL; + const char *bsfs = NULL; char *next; - char *discard_str = NULL; + const char *discard_str = NULL; int ret; ds = demux_stream_alloc(d, st); @@ -1256,7 +1261,9 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona MATCH_PER_STREAM_OPT(autorotate, i, ds->autorotate, ic, st); ds->apply_cropping = CROP_ALL; - MATCH_PER_STREAM_OPT(apply_cropping, str, apply_cropping, ic, st); + ret = opt_match_per_stream_str(ist, &o->apply_cropping, ic, st, &apply_cropping); + if (ret < 0) + return ret; if (apply_cropping) { const AVOption opts[] = { { "apply_cropping", NULL, 0, AV_OPT_TYPE_INT, @@ -1282,7 +1289,9 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona } } - MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, ic, st); + ret = opt_match_per_stream_str(ist, &o->codec_tags, ic, st, &codec_tag); + if (ret < 0) + return ret; if (codec_tag) { uint32_t tag = strtol(codec_tag, &next, 0); if (*next) { @@ -1299,9 +1308,14 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona if (ret < 0) return ret; - MATCH_PER_STREAM_OPT(hwaccels, str, hwaccel, ic, st); - MATCH_PER_STREAM_OPT(hwaccel_output_formats, str, - hwaccel_output_format, ic, st); + ret = opt_match_per_stream_str(ist, &o->hwaccels, ic, st, &hwaccel); + if (ret < 0) + return ret; + + ret = opt_match_per_stream_str(ist, &o->hwaccel_output_formats, ic, st, + &hwaccel_output_format); + if (ret < 0) + return ret; if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "cuvid")) { av_log(ist, AV_LOG_WARNING, @@ -1360,7 +1374,9 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona } } - MATCH_PER_STREAM_OPT(hwaccel_devices, str, hwaccel_device, ic, st); + ret = opt_match_per_stream_str(ist, &o->hwaccel_devices, ic, st, &hwaccel_device); + if (ret < 0) + return ret; if (hwaccel_device) { ds->dec_opts.hwaccel_device = av_strdup(hwaccel_device); if (!ds->dec_opts.hwaccel_device) @@ -1368,7 +1384,7 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona } } - ret = choose_decoder(o, ic, st, ds->dec_opts.hwaccel_id, + ret = choose_decoder(o, ist, ic, st, ds->dec_opts.hwaccel_id, ds->dec_opts.hwaccel_device_type, &ist->dec); if (ret < 0) return ret; @@ -1391,7 +1407,9 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona (o->data_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_DATA)) ist->user_set_discard = AVDISCARD_ALL; - MATCH_PER_STREAM_OPT(discard, str, discard_str, ic, st); + ret = opt_match_per_stream_str(ist, &o->discard, ic, st, &discard_str); + if (ret < 0) + return ret; if (discard_str) { ret = av_opt_set(ist->st, "discard", discard_str, 0); if (ret < 0) { @@ -1413,7 +1431,9 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona switch (par->codec_type) { case AVMEDIA_TYPE_VIDEO: - MATCH_PER_STREAM_OPT(frame_rates, str, framerate, ic, st); + ret = opt_match_per_stream_str(ist, &o->frame_rates, ic, st, &framerate); + if (ret < 0) + return ret; if (framerate) { ret = av_parse_video_rate(&ist->framerate, framerate); if (ret < 0) { @@ -1430,8 +1450,11 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona break; case AVMEDIA_TYPE_AUDIO: { - char *ch_layout_str = NULL; - MATCH_PER_STREAM_OPT(audio_ch_layouts, str, ch_layout_str, ic, st); + const char *ch_layout_str = NULL; + + ret = opt_match_per_stream_str(ist, &o->audio_ch_layouts, ic, st, &ch_layout_str); + if (ret < 0) + return ret; if (ch_layout_str) { AVChannelLayout ch_layout; ret = av_channel_layout_from_string(&ch_layout, ch_layout_str); @@ -1458,9 +1481,12 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona } case AVMEDIA_TYPE_DATA: case AVMEDIA_TYPE_SUBTITLE: { - char *canvas_size = NULL; + const char *canvas_size = NULL; MATCH_PER_STREAM_OPT(fix_sub_duration, i, ist->fix_sub_duration, ic, st); - MATCH_PER_STREAM_OPT(canvas_sizes, str, canvas_size, ic, st); + + ret = opt_match_per_stream_str(ist, &o->canvas_sizes, ic, st, &canvas_size); + if (ret < 0) + return ret; if (canvas_size) { ret = av_parse_video_size(&par->width, &par->height, canvas_size); @@ -1490,7 +1516,9 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona if (ist->st->sample_aspect_ratio.num) ist->par->sample_aspect_ratio = ist->st->sample_aspect_ratio; - MATCH_PER_STREAM_OPT(bitstream_filters, str, bsfs, ic, st); + ret = opt_match_per_stream_str(ist, &o->bitstream_filters, ic, st, &bsfs); + if (ret < 0) + return ret; if (bsfs) { ret = av_bsf_list_parse_str(bsfs, &ds->bsf); if (ret < 0) { @@ -1752,7 +1780,7 @@ int ifile_open(const OptionsContext *o, const char *filename, Scheduler *sch) /* apply forced codec ids */ for (i = 0; i < ic->nb_streams; i++) { const AVCodec *dummy; - ret = choose_decoder(o, ic, ic->streams[i], HWACCEL_NONE, AV_HWDEVICE_TYPE_NONE, + ret = choose_decoder(o, f, ic, ic->streams[i], HWACCEL_NONE, AV_HWDEVICE_TYPE_NONE, &dummy); if (ret < 0) return ret; diff --git a/fftools/ffmpeg_mux.h b/fftools/ffmpeg_mux.h index 1c1b407484..22d728a919 100644 --- a/fftools/ffmpeg_mux.h +++ b/fftools/ffmpeg_mux.h @@ -79,7 +79,7 @@ typedef struct MuxStream { int ts_drop; #endif - char *apad; + const char *apad; } MuxStream; typedef struct Muxer { diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c index 4a2b5924a4..c7efeda7bf 100644 --- a/fftools/ffmpeg_mux_init.c +++ b/fftools/ffmpeg_mux_init.c @@ -70,11 +70,14 @@ static int choose_encoder(const OptionsContext *o, AVFormatContext *s, OutputStream *ost, const AVCodec **enc) { enum AVMediaType type = ost->type; - char *codec_name = NULL; + const char *codec_name = NULL; + int ret; *enc = NULL; - MATCH_PER_STREAM_OPT(codec_names, str, codec_name, s, ost->st); + ret = opt_match_per_stream_str(ost, &o->codec_names, s, ost->st, &codec_name); + if (ret < 0) + return ret; if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && @@ -416,12 +419,17 @@ static int ost_get_filters(const OptionsContext *o, AVFormatContext *oc, OutputStream *ost, char **dst) { const char *filters = NULL; + int ret; #if FFMPEG_OPT_FILTER_SCRIPT const char *filters_script = NULL; - MATCH_PER_STREAM_OPT(filter_scripts, str, filters_script, oc, ost->st); + ret = opt_match_per_stream_str(ost, &o->filter_scripts, oc, ost->st, &filters_script); + if (ret < 0) + return ret; #endif - MATCH_PER_STREAM_OPT(filters, str, filters, oc, ost->st); + ret = opt_match_per_stream_str(ost, &o->filters, oc, ost->st, &filters); + if (ret < 0) + return ret; if (!ost->enc) { if ( @@ -586,18 +594,22 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, MuxStream *ms = ms_from_ost(ost); AVFormatContext *oc = mux->fc; AVStream *st; - char *frame_rate = NULL, *max_frame_rate = NULL, *frame_aspect_ratio = NULL; + const char *frame_rate = NULL, *max_frame_rate = NULL, *frame_aspect_ratio = NULL; int ret = 0; st = ost->st; - MATCH_PER_STREAM_OPT(frame_rates, str, frame_rate, oc, st); + ret = opt_match_per_stream_str(ost, &o->frame_rates, oc, st, &frame_rate); + if (ret < 0) + return ret; if (frame_rate && av_parse_video_rate(&ost->frame_rate, frame_rate) < 0) { av_log(ost, AV_LOG_FATAL, "Invalid framerate value: %s\n", frame_rate); return AVERROR(EINVAL); } - MATCH_PER_STREAM_OPT(max_frame_rates, str, max_frame_rate, oc, st); + ret = opt_match_per_stream_str(ost, &o->max_frame_rates, oc, st, &max_frame_rate); + if (ret < 0) + return ret; if (max_frame_rate && av_parse_video_rate(&ost->max_frame_rate, max_frame_rate) < 0) { av_log(ost, AV_LOG_FATAL, "Invalid maximum framerate value: %s\n", max_frame_rate); return AVERROR(EINVAL); @@ -608,7 +620,9 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, return AVERROR(EINVAL); } - MATCH_PER_STREAM_OPT(frame_aspect_ratios, str, frame_aspect_ratio, oc, st); + ret = opt_match_per_stream_str(ost, &o->frame_aspect_ratios, oc, st, &frame_aspect_ratio); + if (ret < 0) + return ret; if (frame_aspect_ratio) { AVRational q; if (av_parse_ratio(&q, frame_aspect_ratio, 255, 0, NULL) < 0 || @@ -622,14 +636,16 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, if (ost->enc_ctx) { AVCodecContext *video_enc = ost->enc_ctx; const char *p = NULL, *fps_mode = NULL; - char *frame_size = NULL; - char *frame_pix_fmt = NULL; - char *intra_matrix = NULL, *inter_matrix = NULL; - char *chroma_intra_matrix = NULL; + const char *frame_size = NULL; + const char *frame_pix_fmt = NULL; + const char *intra_matrix = NULL, *inter_matrix = NULL; + const char *chroma_intra_matrix = NULL; int do_pass = 0; int i; - MATCH_PER_STREAM_OPT(frame_sizes, str, frame_size, oc, st); + ret = opt_match_per_stream_str(ost, &o->frame_sizes, oc, st, &frame_size); + if (ret < 0) + return ret; if (frame_size) { ret = av_parse_video_size(&video_enc->width, &video_enc->height, frame_size); if (ret < 0) { @@ -638,7 +654,9 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, } } - MATCH_PER_STREAM_OPT(frame_pix_fmts, str, frame_pix_fmt, oc, st); + ret = opt_match_per_stream_str(ost, &o->frame_pix_fmts, oc, st, &frame_pix_fmt); + if (ret < 0) + return ret; if (frame_pix_fmt && *frame_pix_fmt == '+') { *keep_pix_fmt = 1; if (!*++frame_pix_fmt) @@ -650,7 +668,9 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, return AVERROR(EINVAL); } - MATCH_PER_STREAM_OPT(intra_matrices, str, intra_matrix, oc, st); + ret = opt_match_per_stream_str(ost, &o->intra_matrices, oc, st, &intra_matrix); + if (ret < 0) + return ret; if (intra_matrix) { if (!(video_enc->intra_matrix = av_mallocz(sizeof(*video_enc->intra_matrix) * 64))) return AVERROR(ENOMEM); @@ -659,7 +679,9 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, if (ret < 0) return ret; } - MATCH_PER_STREAM_OPT(chroma_intra_matrices, str, chroma_intra_matrix, oc, st); + ret = opt_match_per_stream_str(ost, &o->chroma_intra_matrices, oc, st, &chroma_intra_matrix); + if (ret < 0) + return ret; if (chroma_intra_matrix) { uint16_t *p = av_mallocz(sizeof(*video_enc->chroma_intra_matrix) * 64); if (!p) @@ -669,7 +691,9 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, if (ret < 0) return ret; } - MATCH_PER_STREAM_OPT(inter_matrices, str, inter_matrix, oc, st); + ret = opt_match_per_stream_str(ost, &o->inter_matrices, oc, st, &inter_matrix); + if (ret < 0) + return ret; if (inter_matrix) { if (!(video_enc->inter_matrix = av_mallocz(sizeof(*video_enc->inter_matrix) * 64))) return AVERROR(ENOMEM); @@ -678,7 +702,9 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, return ret; } - MATCH_PER_STREAM_OPT(rc_overrides, str, p, oc, st); + ret = opt_match_per_stream_str(ost, &o->rc_overrides, oc, st, &p); + if (ret < 0) + return ret; for (i = 0; p; i++) { int start, end, q; int e = sscanf(p, "%d,%d,%d", &start, &end, &q); @@ -717,7 +743,9 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, video_enc->flags |= AV_CODEC_FLAG_PASS2; } - MATCH_PER_STREAM_OPT(passlogfiles, str, ost->logfile_prefix, oc, st); + ret = opt_match_per_stream_str(ost, &o->passlogfiles, oc, st, &ost->logfile_prefix); + if (ret < 0) + return ret; if (ost->logfile_prefix && !(ost->logfile_prefix = av_strdup(ost->logfile_prefix))) return AVERROR(ENOMEM); @@ -778,7 +806,9 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, #else *vsync_method = VSYNC_AUTO; #endif - MATCH_PER_STREAM_OPT(fps_mode, str, fps_mode, oc, st); + ret = opt_match_per_stream_str(ost, &o->fps_mode, oc, st, &fps_mode); + if (ret < 0) + return ret; if (fps_mode) { ret = parse_and_set_vsync(fps_mode, vsync_method, ost->file->index, ost->index, 0); if (ret < 0) @@ -834,8 +864,9 @@ static int new_stream_audio(Muxer *mux, const OptionsContext *o, if (ost->enc_ctx) { AVCodecContext *audio_enc = ost->enc_ctx; int channels = 0; - char *layout = NULL; - char *sample_fmt = NULL; + const char *layout = NULL; + const char *sample_fmt = NULL; + int ret; MATCH_PER_STREAM_OPT(audio_channels, i, channels, oc, st); if (channels) { @@ -843,13 +874,17 @@ static int new_stream_audio(Muxer *mux, const OptionsContext *o, audio_enc->ch_layout.nb_channels = channels; } - MATCH_PER_STREAM_OPT(audio_ch_layouts, str, layout, oc, st); + ret = opt_match_per_stream_str(ost, &o->audio_ch_layouts, oc, st, &layout); + if (ret < 0) + return ret; if (layout && av_channel_layout_from_string(&audio_enc->ch_layout, layout) < 0) { av_log(ost, AV_LOG_FATAL, "Unknown channel layout: %s\n", layout); return AVERROR(EINVAL); } - MATCH_PER_STREAM_OPT(sample_fmts, str, sample_fmt, oc, st); + ret = opt_match_per_stream_str(ost, &o->sample_fmts, oc, st, &sample_fmt); + if (ret < 0) + return ret; if (sample_fmt && (audio_enc->sample_fmt = av_get_sample_fmt(sample_fmt)) == AV_SAMPLE_FMT_NONE) { av_log(ost, AV_LOG_FATAL, "Invalid sample format '%s'\n", sample_fmt); @@ -858,7 +893,9 @@ static int new_stream_audio(Muxer *mux, const OptionsContext *o, MATCH_PER_STREAM_OPT(audio_sample_rate, i, audio_enc->sample_rate, oc, st); - MATCH_PER_STREAM_OPT(apad, str, ms->apad, oc, st); + ret = opt_match_per_stream_str(ost, &o->apad, oc, st, &ms->apad); + if (ret < 0) + return ret; } return 0; @@ -880,9 +917,12 @@ static int new_stream_subtitle(Muxer *mux, const OptionsContext *o, avcodec_descriptor_get(subtitle_enc->codec_id); int input_props = 0, output_props = 0; - char *frame_size = NULL; + const char *frame_size = NULL; + int ret; - MATCH_PER_STREAM_OPT(frame_sizes, str, frame_size, mux->fc, st); + ret = opt_match_per_stream_str(ost, &o->frame_sizes, mux->fc, st, &frame_size); + if (ret < 0) + return ret; if (frame_size) { int ret = av_parse_video_size(&subtitle_enc->width, &subtitle_enc->height, frame_size); if (ret < 0) { @@ -1039,8 +1079,8 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, int threads_manual = 0; AVRational enc_tb = { 0, 0 }; enum VideoSyncMethod vsync_method = VSYNC_AUTO; - const char *bsfs = NULL, *time_base = NULL; - char *filters = NULL, *next, *codec_tag = NULL; + const char *bsfs = NULL, *time_base = NULL, *codec_tag = NULL; + char *filters = NULL, *next; double qscale = -1; st = avformat_new_stream(oc, NULL); @@ -1151,9 +1191,9 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, if (ost->enc_ctx) { AVCodecContext *enc = ost->enc_ctx; AVIOContext *s = NULL; - char *buf = NULL, *arg = NULL, *preset = NULL; + char *buf = NULL, *arg = NULL; const char *enc_stats_pre = NULL, *enc_stats_post = NULL, *mux_stats = NULL; - const char *enc_time_base = NULL; + const char *enc_time_base = NULL, *preset = NULL; ret = filter_codec_opts(o->g->codec_opts, enc->codec_id, oc, st, enc->codec, &encoder_opts, @@ -1161,7 +1201,9 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, if (ret < 0) goto fail; - MATCH_PER_STREAM_OPT(presets, str, preset, oc, st); + ret = opt_match_per_stream_str(ost, &o->presets, oc, st, &preset); + if (ret < 0) + goto fail; MATCH_PER_STREAM_OPT(autoscale, i, autoscale, oc, st); if (preset && (!(ret = get_preset_file_2(preset, enc->codec->name, &s)))) { @@ -1194,43 +1236,57 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, goto fail; } - MATCH_PER_STREAM_OPT(enc_stats_pre, str, enc_stats_pre, oc, st); + ret = opt_match_per_stream_str(ost, &o->enc_stats_pre, oc, st, &enc_stats_pre); + if (ret < 0) + goto fail; if (enc_stats_pre && (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { const char *format = "{fidx} {sidx} {n} {t}"; - MATCH_PER_STREAM_OPT(enc_stats_pre_fmt, str, format, oc, st); + ret = opt_match_per_stream_str(ost, &o->enc_stats_pre_fmt, oc, st, &format); + if (ret < 0) + goto fail; ret = enc_stats_init(ost, &ost->enc_stats_pre, 1, enc_stats_pre, format); if (ret < 0) goto fail; } - MATCH_PER_STREAM_OPT(enc_stats_post, str, enc_stats_post, oc, st); + ret = opt_match_per_stream_str(ost, &o->enc_stats_post, oc, st, &enc_stats_post); + if (ret < 0) + goto fail; if (enc_stats_post && (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { const char *format = "{fidx} {sidx} {n} {t}"; - MATCH_PER_STREAM_OPT(enc_stats_post_fmt, str, format, oc, st); + ret = opt_match_per_stream_str(ost, &o->enc_stats_post_fmt, oc, st, &format); + if (ret < 0) + goto fail; ret = enc_stats_init(ost, &ost->enc_stats_post, 0, enc_stats_post, format); if (ret < 0) goto fail; } - MATCH_PER_STREAM_OPT(mux_stats, str, mux_stats, oc, st); + ret = opt_match_per_stream_str(ost, &o->mux_stats, oc, st, &mux_stats); + if (ret < 0) + goto fail; if (mux_stats && (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { const char *format = "{fidx} {sidx} {n} {t}"; - MATCH_PER_STREAM_OPT(mux_stats_fmt, str, format, oc, st); + ret = opt_match_per_stream_str(ost, &o->mux_stats_fmt, oc, st, &format); + if (ret < 0) + goto fail; ret = enc_stats_init(ost, &ms->stats, 0, mux_stats, format); if (ret < 0) goto fail; } - MATCH_PER_STREAM_OPT(enc_time_bases, str, enc_time_base, oc, st); + ret = opt_match_per_stream_str(ost, &o->enc_time_bases, oc, st, &enc_time_base); + if (ret < 0) + goto fail; if (enc_time_base && type == AVMEDIA_TYPE_SUBTITLE) av_log(ost, AV_LOG_WARNING, "-enc_time_base not supported for subtitles, ignoring\n"); @@ -1293,7 +1349,9 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, ost->bitexact = !!(ost->enc_ctx->flags & AV_CODEC_FLAG_BITEXACT); } - MATCH_PER_STREAM_OPT(time_bases, str, time_base, oc, st); + ret = opt_match_per_stream_str(ost, &o->time_bases, oc, st, &time_base); + if (ret < 0) + return ret; if (time_base) { AVRational q; if (av_parse_ratio(&q, time_base, INT_MAX, 0, NULL) < 0 || @@ -1318,7 +1376,9 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, ms->copy_prior_start = -1; MATCH_PER_STREAM_OPT(copy_prior_start, i, ms->copy_prior_start, oc ,st); - MATCH_PER_STREAM_OPT(bitstream_filters, str, bsfs, oc, st); + ret = opt_match_per_stream_str(ost, &o->bitstream_filters, oc, st, &bsfs); + if (ret < 0) + return ret; if (bsfs && *bsfs) { ret = av_bsf_list_parse_str(bsfs, &ms->bsf_ctx); if (ret < 0) { @@ -1327,7 +1387,9 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, } } - MATCH_PER_STREAM_OPT(codec_tags, str, codec_tag, oc, st); + ret = opt_match_per_stream_str(ost, &o->codec_tags, oc, st, &codec_tag); + if (ret < 0) + return ret; if (codec_tag) { uint32_t tag = strtol(codec_tag, &next, 0); if (*next) { @@ -2943,7 +3005,9 @@ static int set_dispositions(Muxer *mux, const OptionsContext *o) nb_streams[ost->type + 1]++; - MATCH_PER_STREAM_OPT_CLEAN(disposition, str, dispositions[i], ctx, ost->st, goto finish); + ret = opt_match_per_stream_str(ost, &o->disposition, ctx, ost->st, &dispositions[i]); + if (ret < 0) + goto finish; have_manual |= !!dispositions[i]; @@ -3088,8 +3152,12 @@ static int process_forced_keyframes(Muxer *mux, const OptionsContext *o) for (int i = 0; i < mux->of.nb_streams; i++) { OutputStream *ost = mux->of.streams[i]; const char *forced_keyframes = NULL; + int ret; - MATCH_PER_STREAM_OPT(forced_key_frames, str, forced_keyframes, mux->fc, ost->st); + ret = opt_match_per_stream_str(ost, &o->forced_key_frames, + mux->fc, ost->st, &forced_keyframes); + if (ret < 0) + return ret; if (!(ost->type == AVMEDIA_TYPE_VIDEO && ost->enc_ctx && forced_keyframes)) diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 2c201c74b2..736b3ee743 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -164,6 +164,73 @@ const char *opt_match_per_type_str(const SpecifierOptList *sol, return NULL; } +static int opt_match_per_stream(void *logctx, enum OptionType type, + const SpecifierOptList *sol, + AVFormatContext *fc, AVStream *st) +{ + int matches = 0, match_idx = -1; + + av_assert0((type == sol->type) || !sol->nb_opt); + + for (int i = 0; i < sol->nb_opt; i++) { + const char *spec = sol->opt[i].specifier; + int ret = check_stream_specifier(fc, st, spec); + if (ret > 0) { + match_idx = i; + matches++; + } else if (ret < 0) + return ret; + } + + if (matches > 1 && sol->opt_canon) { + const SpecifierOpt *so = &sol->opt[match_idx]; + const char *spec = so->specifier && so->specifier[0] ? so->specifier : ""; + + char namestr[128] = ""; + char optval_buf[32]; + const char *optval = optval_buf; + + snprintf(namestr, sizeof(namestr), "-%s", sol->opt_canon->name); + if (sol->opt_canon->flags & OPT_HAS_ALT) { + const char * const *names_alt = sol->opt_canon->u1.names_alt; + for (int i = 0; names_alt[i]; i++) + av_strlcatf(namestr, sizeof(namestr), "/-%s", names_alt[i]); + } + + switch (sol->type) { + case OPT_TYPE_STRING: optval = so->u.str; break; + case OPT_TYPE_INT: snprintf(optval_buf, sizeof(optval_buf), "%d", so->u.i); break; + case OPT_TYPE_INT64: snprintf(optval_buf, sizeof(optval_buf), "%"PRId64, so->u.i64); break; + case OPT_TYPE_FLOAT: snprintf(optval_buf, sizeof(optval_buf), "%f", so->u.f); break; + case OPT_TYPE_DOUBLE: snprintf(optval_buf, sizeof(optval_buf), "%f", so->u.dbl); break; + default: av_assert0(0); + } + + av_log(logctx, AV_LOG_WARNING, "Multiple %s options specified for " + "stream %d, only the last option '-%s%s%s %s' will be used.\n", + namestr, st->index, sol->opt_canon->name, spec[0] ? ":" : "", + spec, optval); + } + + return match_idx + 1; +} + +#define OPT_MATCH_PER_STREAM(name, type, opt_type, m) \ +int opt_match_per_stream_ ## name(void *logctx, const SpecifierOptList *sol, \ + AVFormatContext *fc, AVStream *st, type *out) \ +{ \ + int ret = opt_match_per_stream(logctx, opt_type, sol, fc, st); \ + \ + if (ret <= 0) \ + return ret; \ + \ + *out = sol->opt[ret - 1].u.m; \ + \ + return 0; \ +} + +OPT_MATCH_PER_STREAM(str, const char *, OPT_TYPE_STRING, str); + int parse_and_set_vsync(const char *arg, int *vsync_var, int file_idx, int st_idx, int is_global) { if (!av_strcasecmp(arg, "cfr")) *vsync_var = VSYNC_CFR; From patchwork Sat Aug 10 17:16:17 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Khirnov X-Patchwork-Id: 50969 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:612c:1ff2:b0:489:2eb3:e4c4 with SMTP id ks18csp530185vqb; Sat, 10 Aug 2024 10:26:11 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCW1LACDCd9bME/dKi/eQhpKd4M4Jq8SiXDkHJj6jUINWLyskix0CpBinu74x7mamLy9SC6+jZNAwa/q6EiTA6teQqN4tqBydlfDLw== X-Google-Smtp-Source: AGHT+IEtHZfF6sDwTnktkC0MSnJRGHkNmHi69JCUWNRrReYh4CQmYiLc792hGMuVY83ggZa3oebi X-Received: by 2002:a05:651c:508:b0:2f1:750d:53a7 with SMTP id 38308e7fff4ca-2f1a6c4d77dmr49175021fa.8.1723310771462; Sat, 10 Aug 2024 10:26:11 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1723310771; cv=none; d=google.com; s=arc-20160816; b=S+kucXZ4H541cB1GZmexY3cctv2eVd9WIPg2IzmE9C1vJek/lZo7Q6PbBEz2vmXOfK bC2XkwnJrJKVSzxxrhIN2SOa/CTQ+l/pT7FOuRA3T9/C/hGf+qQYeU3vBikriIpoPph3 7JqTbY5NINPbvFkcvk+ZNVA3GwWBl82RqEsRRyszpBe7CWfbuyP45YFhVHoZysi4fHyS 5Jo5Ue5tI4QC4VXLf/q/KlR8ejJEz6rjZhexYzaFSJkvhprrBc4Cy70Z6AmdgIfJ7iX1 KmCSNulED0esV+k8QQWuRXyuajsRUtmGKRFZe6QpZDQhCCf5w/wgGzcOpQXJW1JGTYBd hM7Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:references:in-reply-to:message-id :date:to:from:dkim-signature:delivered-to; bh=5pEggS838mzxARw9WeI0I+vBevmKn1ageiI61Y+w25A=; fh=YOA8vD9MJZuwZ71F/05pj6KdCjf6jQRmzLS+CATXUQk=; b=d5jPBRgNORWZbUUfKEfPHxX8aMOGGhaZVDIZcCaY/Fs2lKKqrjb4TzxnBC1hXkOyUl 3KCuNHsvMuPzPiql7Gi0Mhsz66OSxONRDmBdfysIAUt8Szr1Lc7KbG+YaeheyMy4CyBR edEcEomgixI67ztJyB6QeCol4d9WOF9OQKKAYonoXh01wQZUbGPyUFrozfsvpYismITo oq2ae0Ks97BiNTm5NmO3wLHdLNhS4lpZ7e9hqoVrkYQy62WNVcCJpZt3ijWEGZnGdUQA rajY3fUd46LjqhAUx/iuwofKG/gybaAaJOHD0gFQv4dhv037zLldPZZKg/maPJ43BTzK fuXA==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b=e2giyEhP; 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 38308e7fff4ca-2f291ddc128si5460641fa.98.2024.08.10.10.26.11; Sat, 10 Aug 2024 10:26:11 -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; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b=e2giyEhP; 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 BD43068DA6E; Sat, 10 Aug 2024 20:16:42 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail1.khirnov.net (quelana.khirnov.net [94.230.150.81]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id E728E68D93B for ; Sat, 10 Aug 2024 20:16:34 +0300 (EEST) Authentication-Results: mail1.khirnov.net; dkim=pass (2048-bit key; unprotected) header.d=khirnov.net header.i=@khirnov.net header.a=rsa-sha256 header.s=mail header.b=e2giyEhP; dkim-atps=neutral Received: from localhost (mail1.khirnov.net [IPv6:::1]) by mail1.khirnov.net (Postfix) with ESMTP id 5088D4DF8 for ; Sat, 10 Aug 2024 19:16:34 +0200 (CEST) Received: from mail1.khirnov.net ([IPv6:::1]) by localhost (mail1.khirnov.net [IPv6:::1]) (amavis, port 10024) with ESMTP id 2mPfcc0E-en9 for ; Sat, 10 Aug 2024 19:16:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=khirnov.net; s=mail; t=1723310193; bh=pbEAhCIJ9el2i82Xe/Zcl7QyBB2GA21c0wPXhC9BPlo=; h=From:To:Subject:Date:In-Reply-To:References:From; b=e2giyEhPFKCmZ47QKtBy9VhUe41Bam8j6os5dAiuGPQpe4yuvamUPenKEQIisbKBm ATZf0/9CqFb/RTE4Zzt0moHzjFsmgwKwoAkkol+5cssmQ1jaysQkqQiRIIZyfNhF6c gQSqMtpSmZLNIAuKYzn5U+/1y6pFuq+oEjWMVLjattOT7lllVk8P5S0a6fVmA2psuJ dfmEj7hkMVfkK1hOZ7NqbOr0nbI6GohSJ+d4rfA57wKMupqz+qV7EkGPM8JRg9eYHw DDyvgpp5GcV+peZa45crk2IQnsv9k1YGVtYwlrUu4GYDHZgwW5T4+fMKidPvplA5Ou GhpNUNxJzYhxw== Received: from libav.khirnov.net (libav.khirnov.net [IPv6:2a00:c500:561:201::7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256 client-signature RSA-PSS (2048 bits) client-digest SHA256) (Client CN "libav.khirnov.net", Issuer "smtp.khirnov.net SMTP CA" (verified OK)) by mail1.khirnov.net (Postfix) with ESMTPS id 978A6EE6 for ; Sat, 10 Aug 2024 19:16:32 +0200 (CEST) Received: from libav.khirnov.net (libav.khirnov.net [IPv6:::1]) by libav.khirnov.net (Postfix) with ESMTP id 464393A05A2 for ; Sat, 10 Aug 2024 19:16:26 +0200 (CEST) From: Anton Khirnov To: ffmpeg-devel@ffmpeg.org Date: Sat, 10 Aug 2024 19:16:17 +0200 Message-ID: <20240810171621.26757-2-anton@khirnov.net> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240810171621.26757-1-anton@khirnov.net> References: <20240810171621.26757-1-anton@khirnov.net> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 2/6] fftools/ffmpeg: replace remaining uses of MATCH_PER_STREAM_OPT() 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: RsMzzWCCJsI0 --- fftools/ffmpeg.h | 49 ++++----------------------- fftools/ffmpeg_demux.c | 39 ++++++++++++++++----- fftools/ffmpeg_mux_init.c | 71 ++++++++++++++++++++++++++++----------- fftools/ffmpeg_opt.c | 5 ++- 4 files changed, 91 insertions(+), 73 deletions(-) diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 7d82d7d7c2..674ae340f2 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -857,53 +857,16 @@ OutputStream *ost_iter(OutputStream *prev); void update_benchmark(const char *fmt, ...); -#define SPECIFIER_OPT_FMT_str "%s" -#define SPECIFIER_OPT_FMT_i "%i" -#define SPECIFIER_OPT_FMT_i64 "%"PRId64 -#define SPECIFIER_OPT_FMT_ui64 "%"PRIu64 -#define SPECIFIER_OPT_FMT_f "%f" -#define SPECIFIER_OPT_FMT_dbl "%lf" - -#define WARN_MULTIPLE_OPT_USAGE(optname, type, idx, st)\ -{\ - char namestr[128] = "";\ - const SpecifierOpt *so = &o->optname.opt[idx];\ - const char *spec = so->specifier && so->specifier[0] ? so->specifier : "";\ - snprintf(namestr, sizeof(namestr), "-%s", o->optname.opt_canon->name);\ - if (o->optname.opt_canon->flags & OPT_HAS_ALT) {\ - const char * const *names_alt = o->optname.opt_canon->u1.names_alt;\ - for (int _i = 0; names_alt[_i]; _i++)\ - av_strlcatf(namestr, sizeof(namestr), "/-%s", names_alt[_i]);\ - }\ - av_log(NULL, AV_LOG_WARNING, "Multiple %s options specified for stream %d, only the last option '-%s%s%s "SPECIFIER_OPT_FMT_##type"' will be used.\n",\ - namestr, st->index, o->optname.opt_canon->name, spec[0] ? ":" : "", spec, so->u.type);\ -} - -#define MATCH_PER_STREAM_OPT_CLEAN(name, type, outvar, fmtctx, st, clean)\ -{\ - int _ret, _matches = 0, _match_idx;\ - for (int _i = 0; _i < o->name.nb_opt; _i++) {\ - char *spec = o->name.opt[_i].specifier;\ - if ((_ret = check_stream_specifier(fmtctx, st, spec)) > 0) {\ - outvar = o->name.opt[_i].u.type;\ - _match_idx = _i;\ - _matches++;\ - } else if (_ret < 0)\ - clean;\ - }\ - if (_matches > 1 && o->name.opt_canon)\ - WARN_MULTIPLE_OPT_USAGE(name, type, _match_idx, st);\ -} - -#define MATCH_PER_STREAM_OPT(name, type, outvar, fmtctx, st)\ -{\ - MATCH_PER_STREAM_OPT_CLEAN(name, type, outvar, fmtctx, st, return _ret)\ -} - const char *opt_match_per_type_str(const SpecifierOptList *sol, char mediatype); int opt_match_per_stream_str(void *logctx, const SpecifierOptList *sol, AVFormatContext *fc, AVStream *st, const char **out); +int opt_match_per_stream_int(void *logctx, const SpecifierOptList *sol, + AVFormatContext *fc, AVStream *st, int *out); +int opt_match_per_stream_int64(void *logctx, const SpecifierOptList *sol, + AVFormatContext *fc, AVStream *st, int64_t *out); +int opt_match_per_stream_dbl(void *logctx, const SpecifierOptList *sol, + AVFormatContext *fc, AVStream *st, double *out); int muxer_thread(void *arg); int encoder_thread(void *arg); diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c index 0c92d31c10..e9a4b5c22a 100644 --- a/fftools/ffmpeg_demux.c +++ b/fftools/ffmpeg_demux.c @@ -1153,11 +1153,18 @@ static int add_display_matrix_to_stream(const OptionsContext *o, double rotation = DBL_MAX; int hflip = -1, vflip = -1; int hflip_set = 0, vflip_set = 0, rotation_set = 0; + int ret; int32_t *buf; - MATCH_PER_STREAM_OPT(display_rotations, dbl, rotation, ctx, st); - MATCH_PER_STREAM_OPT(display_hflips, i, hflip, ctx, st); - MATCH_PER_STREAM_OPT(display_vflips, i, vflip, ctx, st); + ret = opt_match_per_stream_dbl(ist, &o->display_rotations, ctx, st, &rotation); + if (ret < 0) + return ret; + ret = opt_match_per_stream_int(ist, &o->display_hflips, ctx, st, &hflip); + if (ret < 0) + return ret; + ret = opt_match_per_stream_int(ist, &o->display_vflips, ctx, st, &vflip); + if (ret < 0) + return ret; rotation_set = rotation != DBL_MAX; hflip_set = hflip != -1; @@ -1255,10 +1262,14 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona ds->dec_opts.time_base = st->time_base; ds->ts_scale = 1.0; - MATCH_PER_STREAM_OPT(ts_scale, dbl, ds->ts_scale, ic, st); + ret = opt_match_per_stream_dbl(ist, &o->ts_scale, ic, st, &ds->ts_scale); + if (ret < 0) + return ret; ds->autorotate = 1; - MATCH_PER_STREAM_OPT(autorotate, i, ds->autorotate, ic, st); + ret = opt_match_per_stream_int(ist, &o->autorotate, ic, st, &ds->autorotate); + if (ret < 0) + return ret; ds->apply_cropping = CROP_ALL; ret = opt_match_per_stream_str(ist, &o->apply_cropping, ic, st, &apply_cropping); @@ -1397,7 +1408,9 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona } ds->reinit_filters = -1; - MATCH_PER_STREAM_OPT(reinit_filters, i, ds->reinit_filters, ic, st); + ret = opt_match_per_stream_int(ist, &o->reinit_filters, ic, st, &ds->reinit_filters); + if (ret < 0) + return ret; ist->user_set_discard = AVDISCARD_NONE; @@ -1445,7 +1458,9 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona #if FFMPEG_OPT_TOP ist->top_field_first = -1; - MATCH_PER_STREAM_OPT(top_field_first, i, ist->top_field_first, ic, st); + ret = opt_match_per_stream_int(ist, &o->top_field_first, ic, st, &ist->top_field_first); + if (ret < 0) + return ret; #endif break; @@ -1474,7 +1489,10 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona } } else { int guess_layout_max = INT_MAX; - MATCH_PER_STREAM_OPT(guess_layout_max, i, guess_layout_max, ic, st); + ret = opt_match_per_stream_int(ist, &o->guess_layout_max, ic, st, &guess_layout_max); + if (ret < 0) + return ret; + guess_input_channel_layout(ist, par, guess_layout_max); } break; @@ -1482,7 +1500,10 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona case AVMEDIA_TYPE_DATA: case AVMEDIA_TYPE_SUBTITLE: { const char *canvas_size = NULL; - MATCH_PER_STREAM_OPT(fix_sub_duration, i, ist->fix_sub_duration, ic, st); + + ret = opt_match_per_stream_int(ist, &o->fix_sub_duration, ic, st, &ist->fix_sub_duration); + if (ret < 0) + return ret; ret = opt_match_per_stream_str(ist, &o->canvas_sizes, ic, st, &canvas_size); if (ret < 0) diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c index c7efeda7bf..2b3a4ddcf9 100644 --- a/fftools/ffmpeg_mux_init.c +++ b/fftools/ffmpeg_mux_init.c @@ -735,7 +735,9 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, video_enc->rc_override_count = i; /* two pass mode */ - MATCH_PER_STREAM_OPT(pass, i, do_pass, oc, st); + ret = opt_match_per_stream_int(ost, &o->pass, oc, st, &do_pass); + if (ret < 0) + return ret; if (do_pass) { if (do_pass & 1) video_enc->flags |= AV_CODEC_FLAG_PASS1; @@ -792,11 +794,15 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, } } - MATCH_PER_STREAM_OPT(force_fps, i, ost->force_fps, oc, st); + ret = opt_match_per_stream_int(ost, &o->force_fps, oc, st, &ost->force_fps); + if (ret < 0) + return ret; #if FFMPEG_OPT_TOP ost->top_field_first = -1; - MATCH_PER_STREAM_OPT(top_field_first, i, ost->top_field_first, oc, st); + ret = opt_match_per_stream_int(ost, &o->top_field_first, oc, st, &ost->top_field_first); + if (ret < 0) + return ret; if (ost->top_field_first >= 0) av_log(ost, AV_LOG_WARNING, "-top is deprecated, use the setfield filter instead\n"); #endif @@ -868,7 +874,9 @@ static int new_stream_audio(Muxer *mux, const OptionsContext *o, const char *sample_fmt = NULL; int ret; - MATCH_PER_STREAM_OPT(audio_channels, i, channels, oc, st); + ret = opt_match_per_stream_int(ost, &o->audio_channels, oc, st, &channels); + if (ret < 0) + return ret; if (channels) { audio_enc->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; audio_enc->ch_layout.nb_channels = channels; @@ -891,7 +899,9 @@ static int new_stream_audio(Muxer *mux, const OptionsContext *o, return AVERROR(EINVAL); } - MATCH_PER_STREAM_OPT(audio_sample_rate, i, audio_enc->sample_rate, oc, st); + ret = opt_match_per_stream_int(ost, &o->audio_sample_rate, oc, st, &audio_enc->sample_rate); + if (ret < 0) + return ret; ret = opt_match_per_stream_str(ost, &o->apad, oc, st, &ms->apad); if (ret < 0) @@ -1205,7 +1215,9 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, if (ret < 0) goto fail; - MATCH_PER_STREAM_OPT(autoscale, i, autoscale, oc, st); + ret = opt_match_per_stream_int(ost, &o->autoscale, oc, st, &autoscale); + if (ret < 0) + goto fail; if (preset && (!(ret = get_preset_file_2(preset, enc->codec->name, &s)))) { AVBPrint bprint; av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED); @@ -1351,7 +1363,7 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, ret = opt_match_per_stream_str(ost, &o->time_bases, oc, st, &time_base); if (ret < 0) - return ret; + goto fail; if (time_base) { AVRational q; if (av_parse_ratio(&q, time_base, INT_MAX, 0, NULL) < 0 || @@ -1364,7 +1376,9 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, } ms->max_frames = INT64_MAX; - MATCH_PER_STREAM_OPT(max_frames, i64, ms->max_frames, oc, st); + ret = opt_match_per_stream_int64(ost, &o->max_frames, oc, st, &ms->max_frames); + if (ret < 0) + return ret; for (int i = 0; i < o->max_frames.nb_opt; i++) { char *p = o->max_frames.opt[i].specifier; if (!*p && type != AVMEDIA_TYPE_VIDEO) { @@ -1374,11 +1388,13 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, } ms->copy_prior_start = -1; - MATCH_PER_STREAM_OPT(copy_prior_start, i, ms->copy_prior_start, oc ,st); + ret = opt_match_per_stream_int(ost, &o->copy_prior_start, oc, st, &ms->copy_prior_start); + if (ret < 0) + goto fail; ret = opt_match_per_stream_str(ost, &o->bitstream_filters, oc, st, &bsfs); if (ret < 0) - return ret; + goto fail; if (bsfs && *bsfs) { ret = av_bsf_list_parse_str(bsfs, &ms->bsf_ctx); if (ret < 0) { @@ -1389,7 +1405,7 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, ret = opt_match_per_stream_str(ost, &o->codec_tags, oc, st, &codec_tag); if (ret < 0) - return ret; + goto fail; if (codec_tag) { uint32_t tag = strtol(codec_tag, &next, 0); if (*next) { @@ -1403,7 +1419,9 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, ost->enc_ctx->codec_tag = tag; } - MATCH_PER_STREAM_OPT(qscale, dbl, qscale, oc, st); + ret = opt_match_per_stream_dbl(ost, &o->qscale, oc, st, &qscale); + if (ret < 0) + return ret; if (ost->enc_ctx && qscale >= 0) { ost->enc_ctx->flags |= AV_CODEC_FLAG_QSCALE; ost->enc_ctx->global_quality = FF_QP2LAMBDA * qscale; @@ -1413,24 +1431,37 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, int max_muxing_queue_size = 128; int muxing_queue_data_threshold = 50 * 1024 * 1024; - MATCH_PER_STREAM_OPT(max_muxing_queue_size, i, max_muxing_queue_size, oc, st); - MATCH_PER_STREAM_OPT(muxing_queue_data_threshold, i, muxing_queue_data_threshold, oc, st); + ret = opt_match_per_stream_int(ost, &o->max_muxing_queue_size, oc, st, + &max_muxing_queue_size); + if (ret < 0) + goto fail; + + ret = opt_match_per_stream_int(ost, &o->muxing_queue_data_threshold, + oc, st, &muxing_queue_data_threshold); + if (ret < 0) + goto fail; sch_mux_stream_buffering(mux->sch, mux->sch_idx, ms->sch_idx, max_muxing_queue_size, muxing_queue_data_threshold); } - MATCH_PER_STREAM_OPT(bits_per_raw_sample, i, ost->bits_per_raw_sample, - oc, st); + ret = opt_match_per_stream_int(ost, &o->bits_per_raw_sample, oc, st, + &ost->bits_per_raw_sample); + if (ret < 0) + goto fail; - MATCH_PER_STREAM_OPT(fix_sub_duration_heartbeat, i, ost->fix_sub_duration_heartbeat, - oc, st); + ret = opt_match_per_stream_int(ost, &o->fix_sub_duration_heartbeat, + oc, st, &ost->fix_sub_duration_heartbeat); + if (ret < 0) + goto fail; if (oc->oformat->flags & AVFMT_GLOBALHEADER && ost->enc_ctx) ost->enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; - MATCH_PER_STREAM_OPT(copy_initial_nonkeyframes, i, - ms->copy_initial_nonkeyframes, oc, st); + ret = opt_match_per_stream_int(ost, &o->copy_initial_nonkeyframes, + oc, st, &ms->copy_initial_nonkeyframes); + if (ret < 0) + goto fail; switch (type) { case AVMEDIA_TYPE_VIDEO: ret = new_stream_video (mux, o, ost, &keep_pix_fmt, &vsync_method); break; diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index 736b3ee743..c32d3ddc55 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -229,7 +229,10 @@ int opt_match_per_stream_ ## name(void *logctx, const SpecifierOptList *sol, return 0; \ } -OPT_MATCH_PER_STREAM(str, const char *, OPT_TYPE_STRING, str); +OPT_MATCH_PER_STREAM(str, const char *, OPT_TYPE_STRING, str); +OPT_MATCH_PER_STREAM(int, int, OPT_TYPE_INT, i); +OPT_MATCH_PER_STREAM(int64, int64_t, OPT_TYPE_INT64, i64); +OPT_MATCH_PER_STREAM(dbl, double, OPT_TYPE_DOUBLE, dbl); int parse_and_set_vsync(const char *arg, int *vsync_var, int file_idx, int st_idx, int is_global) { From patchwork Sat Aug 10 17:16:18 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Khirnov X-Patchwork-Id: 50967 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:612c:1ff2:b0:489:2eb3:e4c4 with SMTP id ks18csp527176vqb; Sat, 10 Aug 2024 10:17:16 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCXIHR0lAXzykyKrlSRr5MVjFGXSOjpN+eTe38E22pAIpq2AgKeuJLBkEYJGRasmJTWvD6LfKAn4UubiGQl0l2Cbtyp52/pxWBHnxA== X-Google-Smtp-Source: AGHT+IECWE3mVxgyfdBdr/hfFo79xRf3i05WobQxz7VeG4Vts33nyepD+og4q+XH0HA+y9Cci6v/ X-Received: by 2002:a05:6512:15a3:b0:52c:e402:4dc1 with SMTP id 2adb3069b0e04-530eea425b6mr3792061e87.55.1723310236275; Sat, 10 Aug 2024 10:17:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1723310236; cv=none; d=google.com; s=arc-20160816; b=yrrOmT5Yb6E1jzYRrmGaYOoYMz4mAeuauiTr6amnqBoINDVNZZwl8WcBN/uOQNMOSv 2DWoYcC1qgq8UsmZJLigevv+FaResYHwei3X3aq5l0a+LM1eEkFWuj3tSgiPhM3YXscX BD/CFTuHQvdO/qVVoL5PHYL9h0FVfa7fc9oakJFi+SrWgE3IiGHUI1ngqulV2T+dUMvl 4wVROImAT8e885j0YIB5WZPZN44CLMrW17fv7YbEjezqe5acBKVah08Ff5itrH/CBJfP t9AORIv1GLRBTtBK3oaYg1uBpS9cmla3U+j0fq1ShDMjO9nGBFioCUi40VAoWnE3uS4h AHCQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:references:in-reply-to:message-id :date:to:from:dkim-signature:delivered-to; bh=ZzwXZa78szPlTjzjHJ+j4Z225FYlp9z4izZRLtF6D4U=; fh=YOA8vD9MJZuwZ71F/05pj6KdCjf6jQRmzLS+CATXUQk=; b=sKugARZs6I2ztplEu/cENjrX/+uaXnx+rQVNr+2/5wrGuoqW9rGozPZRk+fYc1l19k mGexZCRJEQ9+hNYw1ak5HeLis+anh1qCnycButftlpQhOVDblO0HWcH1IuBcTuM+lUWh O3yQI3yNCQ6002S5sbDGC8rhjnUBNvyS2sRmjB/aMOp+mAXN61MoWssHGkItvl4xuENx x3t+yLNWgKV3CiNvBxKQt+mRTZ35BxxF04no68x+CatFEopUzVKoriI0Y192gY9IjWJQ UoZU2FhY0CYqKI8EbUFOecRQC6oDdAxW2sjx4Ch2tFkjqnCxeCmxu55U29hJcdyjiXYa 8jYQ==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b=HKSdp2gy; 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 4fb4d7f45d1cf-5bd1a5f8da4si1010084a12.401.2024.08.10.10.17.15; Sat, 10 Aug 2024 10:17: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; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b=HKSdp2gy; 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 D9D9968DAE2; Sat, 10 Aug 2024 20:16:44 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail1.khirnov.net (quelana.khirnov.net [94.230.150.81]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id DC63968D7EF for ; Sat, 10 Aug 2024 20:16:35 +0300 (EEST) Authentication-Results: mail1.khirnov.net; dkim=pass (2048-bit key; unprotected) header.d=khirnov.net header.i=@khirnov.net header.a=rsa-sha256 header.s=mail header.b=HKSdp2gy; dkim-atps=neutral Received: from localhost (mail1.khirnov.net [IPv6:::1]) by mail1.khirnov.net (Postfix) with ESMTP id 74E484DDB for ; Sat, 10 Aug 2024 19:16:35 +0200 (CEST) Received: from mail1.khirnov.net ([IPv6:::1]) by localhost (mail1.khirnov.net [IPv6:::1]) (amavis, port 10024) with ESMTP id ue0K3l8mpuwE for ; Sat, 10 Aug 2024 19:16:35 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=khirnov.net; s=mail; t=1723310193; bh=Qu2Ib4djTwse5rNTVe1VMztTjXNXFwnxUGqjChm3vAM=; h=From:To:Subject:Date:In-Reply-To:References:From; b=HKSdp2gyYYqtuNOex+FLLkSZU2Pk8hIVbSrkQXX9fv4unBamSkawlUmxyHqXQB00Y JG9ie7sSttuSBn8/cLvLWkc/JNW2Sg/ooyRKg8MYjOnO3vim0aFCbiC5irTMCZOcC4 USPPlr+rafiuRFGA16S8ZEwK9fQxP40aDVMNALbA9DjvRnW/xJnaj4Huiq0qKjXtji iblD0bv1pz7YzK8dIUBLlE8maxzJICfaU0PmTDXp02PK+dtl/kQEZoxXOobcNHzZgk 6bk1jx4aHk21w+mgWCirnCp5Vk63F29CxIDmb8qRpOKJEjklaGZMlpLr6bfWDQsvZW ATp2UGmo6cjQA== Received: from libav.khirnov.net (libav.khirnov.net [IPv6:2a00:c500:561:201::7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256 client-signature RSA-PSS (2048 bits) client-digest SHA256) (Client CN "libav.khirnov.net", Issuer "smtp.khirnov.net SMTP CA" (verified OK)) by mail1.khirnov.net (Postfix) with ESMTPS id 0E8714DD7 for ; Sat, 10 Aug 2024 19:16:33 +0200 (CEST) Received: from libav.khirnov.net (libav.khirnov.net [IPv6:::1]) by libav.khirnov.net (Postfix) with ESMTP id 51EF83A0F81 for ; Sat, 10 Aug 2024 19:16:26 +0200 (CEST) From: Anton Khirnov To: ffmpeg-devel@ffmpeg.org Date: Sat, 10 Aug 2024 19:16:18 +0200 Message-ID: <20240810171621.26757-3-anton@khirnov.net> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240810171621.26757-1-anton@khirnov.net> References: <20240810171621.26757-1-anton@khirnov.net> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 3/6] fftools/cmdutils: put stream specifier handling back into cmdutils 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: POlZkBWfzHL0 Stream specifiers were originally designed exclusively for CLI use and were not intended to be public API. Handling them in avformat places major restrictions on how they are used. E.g. if ffmpeg CLI wishes to override some stream parameters, it has to change the demuxer fields (since avformat_match_stream_specifier() does not have access to anything else). However, such fields are supposed to be read-only for the caller. Furthermore having this code in avformat restricts extending the specifier syntax. An example of such an extension will be added in following commits. --- fftools/cmdutils.c | 211 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 210 insertions(+), 1 deletion(-) diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index 6286fd87f7..ffdcc85494 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -980,10 +980,219 @@ FILE *get_preset_file(char *filename, size_t filename_size, return f; } +/** + * Matches a stream specifier (but ignores requested index). + * + * @param indexptr set to point to the requested stream index if there is one + * + * @return <0 on error + * 0 if st is NOT a matching stream + * >0 if st is a matching stream + */ +static int match_stream_specifier(const AVFormatContext *s, const AVStream *st, + const char *spec, const char **indexptr, + const AVStreamGroup **g, const AVProgram **p) +{ + int match = 1; /* Stores if the specifier matches so far. */ + while (*spec) { + if (*spec <= '9' && *spec >= '0') { /* opt:index */ + if (indexptr) + *indexptr = spec; + return match; + } else if (*spec == 'v' || *spec == 'a' || *spec == 's' || *spec == 'd' || + *spec == 't' || *spec == 'V') { /* opt:[vasdtV] */ + enum AVMediaType type; + int nopic = 0; + + switch (*spec++) { + case 'v': type = AVMEDIA_TYPE_VIDEO; break; + case 'a': type = AVMEDIA_TYPE_AUDIO; break; + case 's': type = AVMEDIA_TYPE_SUBTITLE; break; + case 'd': type = AVMEDIA_TYPE_DATA; break; + case 't': type = AVMEDIA_TYPE_ATTACHMENT; break; + case 'V': type = AVMEDIA_TYPE_VIDEO; nopic = 1; break; + default: av_assert0(0); + } + if (*spec && *spec++ != ':') /* If we are not at the end, then another specifier must follow. */ + return AVERROR(EINVAL); + + if (type != st->codecpar->codec_type) + match = 0; + if (nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC)) + match = 0; + } else if (*spec == 'g' && *(spec + 1) == ':') { + int64_t group_idx = -1, group_id = -1; + int found = 0; + char *endptr; + spec += 2; + if (*spec == '#' || (*spec == 'i' && *(spec + 1) == ':')) { + spec += 1 + (*spec == 'i'); + group_id = strtol(spec, &endptr, 0); + if (spec == endptr || (*endptr && *endptr++ != ':')) + return AVERROR(EINVAL); + spec = endptr; + } else { + group_idx = strtol(spec, &endptr, 0); + /* Disallow empty id and make sure that if we are not at the end, then another specifier must follow. */ + if (spec == endptr || (*endptr && *endptr++ != ':')) + return AVERROR(EINVAL); + spec = endptr; + } + if (match) { + if (group_id > 0) { + for (unsigned i = 0; i < s->nb_stream_groups; i++) { + if (group_id == s->stream_groups[i]->id) { + group_idx = i; + break; + } + } + } + if (group_idx < 0 || group_idx >= s->nb_stream_groups) + return AVERROR(EINVAL); + for (unsigned j = 0; j < s->stream_groups[group_idx]->nb_streams; j++) { + if (st->index == s->stream_groups[group_idx]->streams[j]->index) { + found = 1; + if (g) + *g = s->stream_groups[group_idx]; + break; + } + } + } + if (!found) + match = 0; + } else if (*spec == 'p' && *(spec + 1) == ':') { + int prog_id; + int found = 0; + char *endptr; + spec += 2; + prog_id = strtol(spec, &endptr, 0); + /* Disallow empty id and make sure that if we are not at the end, then another specifier must follow. */ + if (spec == endptr || (*endptr && *endptr++ != ':')) + return AVERROR(EINVAL); + spec = endptr; + if (match) { + for (unsigned i = 0; i < s->nb_programs; i++) { + if (s->programs[i]->id != prog_id) + continue; + + for (unsigned j = 0; j < s->programs[i]->nb_stream_indexes; j++) { + if (st->index == s->programs[i]->stream_index[j]) { + found = 1; + if (p) + *p = s->programs[i]; + i = s->nb_programs; + break; + } + } + } + } + if (!found) + match = 0; + } else if (*spec == '#' || + (*spec == 'i' && *(spec + 1) == ':')) { + int stream_id; + char *endptr; + spec += 1 + (*spec == 'i'); + stream_id = strtol(spec, &endptr, 0); + if (spec == endptr || *endptr) /* Disallow empty id and make sure we are at the end. */ + return AVERROR(EINVAL); + return match && (stream_id == st->id); + } else if (*spec == 'm' && *(spec + 1) == ':') { + const AVDictionaryEntry *tag; + char *key, *val; + int ret; + + if (match) { + spec += 2; + val = strchr(spec, ':'); + + key = val ? av_strndup(spec, val - spec) : av_strdup(spec); + if (!key) + return AVERROR(ENOMEM); + + tag = av_dict_get(st->metadata, key, NULL, 0); + if (tag) { + if (!val || !strcmp(tag->value, val + 1)) + ret = 1; + else + ret = 0; + } else + ret = 0; + + av_freep(&key); + } + return match && ret; + } else if (*spec == 'u' && *(spec + 1) == '\0') { + const AVCodecParameters *par = st->codecpar; + int val; + switch (par->codec_type) { + case AVMEDIA_TYPE_AUDIO: + val = par->sample_rate && par->ch_layout.nb_channels; + if (par->format == AV_SAMPLE_FMT_NONE) + return 0; + break; + case AVMEDIA_TYPE_VIDEO: + val = par->width && par->height; + if (par->format == AV_PIX_FMT_NONE) + return 0; + break; + case AVMEDIA_TYPE_UNKNOWN: + val = 0; + break; + default: + val = 1; + break; + } + return match && (par->codec_id != AV_CODEC_ID_NONE && val != 0); + } else { + return AVERROR(EINVAL); + } + } + + return match; +} + int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec) { - int ret = avformat_match_stream_specifier(s, st, spec); + int ret, index; + char *endptr; + const char *indexptr = NULL; + const AVStreamGroup *g = NULL; + const AVProgram *p = NULL; + int nb_streams; + + ret = match_stream_specifier(s, st, spec, &indexptr, &g, &p); if (ret < 0) + goto error; + + if (!indexptr) + return ret; + + index = strtol(indexptr, &endptr, 0); + if (*endptr) { /* We can't have anything after the requested index. */ + ret = AVERROR(EINVAL); + goto error; + } + + /* This is not really needed but saves us a loop for simple stream index specifiers. */ + if (spec == indexptr) + return (index == st->index); + + /* If we requested a matching stream index, we have to ensure st is that. */ + nb_streams = g ? g->nb_streams : (p ? p->nb_stream_indexes : s->nb_streams); + for (int i = 0; i < nb_streams && index >= 0; i++) { + unsigned idx = g ? g->streams[i]->index : (p ? p->stream_index[i] : i); + const AVStream *candidate = s->streams[idx]; + ret = match_stream_specifier(s, candidate, spec, NULL, NULL, NULL); + if (ret < 0) + goto error; + if (ret > 0 && index-- == 0 && st == candidate) + return 1; + } + return 0; + +error: + if (ret == AVERROR(EINVAL)) av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec); return ret; } From patchwork Sat Aug 10 17:16:19 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Khirnov X-Patchwork-Id: 50965 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:612c:1ff2:b0:489:2eb3:e4c4 with SMTP id ks18csp526992vqb; Sat, 10 Aug 2024 10:16:45 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCUtP5XRzgYS489SS06qPv208AeoFmMTaOkw+dpDnMMDWvJGfIiXxkRT8GEleqmOMFY+u8kO9IiuxW4KQ4V3XxK+OTvSopr0G9QVCw== X-Google-Smtp-Source: AGHT+IFC+7zK+AjJ1nLDHs/wc4PGvkP0/TEvnDXVLNw5eFu5iU9pnhplMDWsWmKqhb188udYahvC X-Received: by 2002:a05:6512:3e15:b0:530:aa53:60f6 with SMTP id 2adb3069b0e04-530ee998ec6mr3505457e87.15.1723310205660; Sat, 10 Aug 2024 10:16:45 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1723310205; cv=none; d=google.com; s=arc-20160816; b=PmZ0g7bo8jVhw93uFxCgqNWgiZnpnrx9N30630AoocsOsrfdQDyGODNSdCGd2vhy1j M7HrX8Qikj9nzj+UMZm2h8gYTrTXXuzo0Zl4Kbs9sCyNKp5dKBi2SuvsfqfM4StaQ41m hJuMN7paH1GpWgz6jifLovi7jQLMEb4cbGv/e2+jA+s0RUIKiLl6kuGbUvEKQVJLL2XQ c3vE73x/WPOA+UgG1xdCzm9BdVQxwYcgfvPgwjJ7+mNPAeJ2eirmWmYUrd2Y+DSMMdWu 7YZZPWE3rdgUdY+a9NZkjdAN7/4x7dQcqVdkEg7+ZxWO0T2cxuU80ebFJYjK9grUKF4g Aqwg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:references:in-reply-to:message-id :date:to:from:dkim-signature:delivered-to; bh=Yg8YzLQdANrRO0lO9LYVcR2OBMSU3Y9OlhkfX9ZJxSQ=; fh=YOA8vD9MJZuwZ71F/05pj6KdCjf6jQRmzLS+CATXUQk=; b=qmZfcqbHH+Y0dyvrcuXroU1I5BfSrcGZiD+0dM3v0LbB3HGgaapcc1A6j4iPclGagU UF8X2svsQFwkoRIeR1X/zw0q+Qe/DcQvkkcE7k83mR6wmoeWGEdQZCsZ8ZraayHyuauS MtmG5D5HoPt63CNIufPzX+Ylu5vg/4n8hcBwf0+fvTsFjOVmoH/xvkKErUbYJUQnjoIZ +32AtCMpKKQo77zNcCbkZpF09ldc2gfx6s7V9mavxRjuPt1d/p0OkJl5ztTXG6/tJSa0 9VuG0qbxDOqV4GbBquH31X8gjoC1OuRBW+PYzECzJciEW1XIQ0pXU6/kzh2tclo22wUY +L1g==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b="FjTAfp/L"; 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 a640c23a62f3a-a80bb3424e8si97474266b.766.2024.08.10.10.16.45; Sat, 10 Aug 2024 10:16:45 -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; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b="FjTAfp/L"; 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 748B868D9BD; Sat, 10 Aug 2024 20:16:41 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail1.khirnov.net (quelana.khirnov.net [94.230.150.81]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id DADA068D6FD for ; Sat, 10 Aug 2024 20:16:34 +0300 (EEST) Authentication-Results: mail1.khirnov.net; dkim=pass (2048-bit key; unprotected) header.d=khirnov.net header.i=@khirnov.net header.a=rsa-sha256 header.s=mail header.b=FjTAfp/L; dkim-atps=neutral Received: from localhost (mail1.khirnov.net [IPv6:::1]) by mail1.khirnov.net (Postfix) with ESMTP id 4C5914DF5 for ; Sat, 10 Aug 2024 19:16:34 +0200 (CEST) Received: from mail1.khirnov.net ([IPv6:::1]) by localhost (mail1.khirnov.net [IPv6:::1]) (amavis, port 10024) with ESMTP id 8FTCKPqPeeCc for ; Sat, 10 Aug 2024 19:16:33 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=khirnov.net; s=mail; t=1723310193; bh=2xFpKmtxnWRTyJNDw9MH4FnSDxSp/7CsKjyAXKSYH5g=; h=From:To:Subject:Date:In-Reply-To:References:From; b=FjTAfp/LDDt1qK/oQecyFkUOvRZgzF1GV0hC2PcQ307Wun4RorTSQ4JL9iQExvjQX Apquj0ae48nbVcQwJlAwh4gvvpP2d2TreLK706XQJ2o7cO4Y2v2jhsYKaqVXBf+RjG u1GRv8gApJRwuIVHBTD9NZP1suRaruCA+Y4fOcLs3Z7dTA5732hff1cKmTPIYEg5Ic QH864qWjVPlkcEhv7Gdva9PY+VCk4qohMNZEycPBf6Qtlx85XNnct5lSLPYI8eOZVj ZAgQHQCoyRAI8cFTr/nZkFOuwy3fC2/NT2hq2fFAauS6Fksz/KU1iwU6a5rkPCXHMk pyjxFiyyRuYXw== Received: from libav.khirnov.net (libav.khirnov.net [IPv6:2a00:c500:561:201::7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256 client-signature RSA-PSS (2048 bits) client-digest SHA256) (Client CN "libav.khirnov.net", Issuer "smtp.khirnov.net SMTP CA" (verified OK)) by mail1.khirnov.net (Postfix) with ESMTPS id ACE0D4DBA for ; Sat, 10 Aug 2024 19:16:32 +0200 (CEST) Received: from libav.khirnov.net (libav.khirnov.net [IPv6:::1]) by libav.khirnov.net (Postfix) with ESMTP id 5DA493A1AA4 for ; Sat, 10 Aug 2024 19:16:26 +0200 (CEST) From: Anton Khirnov To: ffmpeg-devel@ffmpeg.org Date: Sat, 10 Aug 2024 19:16:19 +0200 Message-ID: <20240810171621.26757-4-anton@khirnov.net> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240810171621.26757-1-anton@khirnov.net> References: <20240810171621.26757-1-anton@khirnov.net> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 4/6] fftools/cmdutils: split stream specifier parsing and matching 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: +nDphp3T916b This approach has the major advantage that only parsing can fail (due to a malformed specifier or memory allocation failure). Since parsing is done generically, while matching is per-option, this will allow to remove substantial amounts of error checking code in following commits. The new code also explicitly allows stream specifiers to be followed by additional characters, which should allow cleaner handling of optional maps, i.e. -map ?, which is currently implemented in a hacky way that breaks when the stream specifier itself contains the '?' character (this can happen when matching metadata). It will also allow further extending the syntax, which will be useful in following commits. This introduces some minor behaviour changes: * Matching metadata tags now requires the ':' character in keys or values to be escaped. Previously it could not be present in keys, and would be used verbatim in values. The change is required in order to know where the value terminates. * Multiple stream types in a single specifier are now rejected - such a specifier makes no sense. * Non-existent stream group ID or index is now ignored with a warning rather than causing a failure. This is consistent with program handling and is required to make matching fail-free. --- fftools/cmdutils.c | 454 +++++++++++++++++++++++++++------------------ fftools/cmdutils.h | 56 ++++++ 2 files changed, 329 insertions(+), 181 deletions(-) diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index ffdcc85494..b3f501bd56 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -980,220 +980,312 @@ FILE *get_preset_file(char *filename, size_t filename_size, return f; } -/** - * Matches a stream specifier (but ignores requested index). - * - * @param indexptr set to point to the requested stream index if there is one - * - * @return <0 on error - * 0 if st is NOT a matching stream - * >0 if st is a matching stream - */ -static int match_stream_specifier(const AVFormatContext *s, const AVStream *st, - const char *spec, const char **indexptr, - const AVStreamGroup **g, const AVProgram **p) + +void stream_specifier_uninit(StreamSpecifier *ss) { - int match = 1; /* Stores if the specifier matches so far. */ + av_freep(&ss->meta_key); + av_freep(&ss->meta_val); + av_freep(&ss->remainder); + + memset(ss, 0, sizeof(*ss)); +} + +int stream_specifier_parse(StreamSpecifier *ss, const char *spec, + int allow_remainder, void *logctx) +{ + char *endptr; + int ret; + + memset(ss, 0, sizeof(*ss)); + + ss->idx = -1; + ss->media_type = AVMEDIA_TYPE_UNKNOWN; + ss->stream_list = STREAM_LIST_ALL; + + av_log(logctx, AV_LOG_TRACE, "Parsing stream specifier: %s\n", spec); + while (*spec) { if (*spec <= '9' && *spec >= '0') { /* opt:index */ - if (indexptr) - *indexptr = spec; - return match; + ss->idx = strtol(spec, &endptr, 0); + + av_assert0(endptr > spec); + spec = endptr; + + av_log(logctx, AV_LOG_TRACE, + "Parsed index: %d; remainder: %s\n", ss->idx, spec); + + // this terminates the specifier + break; } else if (*spec == 'v' || *spec == 'a' || *spec == 's' || *spec == 'd' || *spec == 't' || *spec == 'V') { /* opt:[vasdtV] */ - enum AVMediaType type; - int nopic = 0; + if (ss->media_type != AVMEDIA_TYPE_UNKNOWN) { + av_log(logctx, AV_LOG_ERROR, "Stream type specified multiple times\n"); + ret = AVERROR(EINVAL); + goto fail; + } switch (*spec++) { - case 'v': type = AVMEDIA_TYPE_VIDEO; break; - case 'a': type = AVMEDIA_TYPE_AUDIO; break; - case 's': type = AVMEDIA_TYPE_SUBTITLE; break; - case 'd': type = AVMEDIA_TYPE_DATA; break; - case 't': type = AVMEDIA_TYPE_ATTACHMENT; break; - case 'V': type = AVMEDIA_TYPE_VIDEO; nopic = 1; break; + case 'v': ss->media_type = AVMEDIA_TYPE_VIDEO; break; + case 'a': ss->media_type = AVMEDIA_TYPE_AUDIO; break; + case 's': ss->media_type = AVMEDIA_TYPE_SUBTITLE; break; + case 'd': ss->media_type = AVMEDIA_TYPE_DATA; break; + case 't': ss->media_type = AVMEDIA_TYPE_ATTACHMENT; break; + case 'V': ss->media_type = AVMEDIA_TYPE_VIDEO; + ss->no_apic = 1; break; default: av_assert0(0); } - if (*spec && *spec++ != ':') /* If we are not at the end, then another specifier must follow. */ - return AVERROR(EINVAL); - if (type != st->codecpar->codec_type) - match = 0; - if (nopic && (st->disposition & AV_DISPOSITION_ATTACHED_PIC)) - match = 0; + av_log(logctx, AV_LOG_TRACE, "Parsed media type: %s; remainder: %s\n", + av_get_media_type_string(ss->media_type), spec); } else if (*spec == 'g' && *(spec + 1) == ':') { - int64_t group_idx = -1, group_id = -1; - int found = 0; - char *endptr; + if (ss->stream_list != STREAM_LIST_ALL) + goto multiple_stream_lists; + spec += 2; if (*spec == '#' || (*spec == 'i' && *(spec + 1) == ':')) { - spec += 1 + (*spec == 'i'); - group_id = strtol(spec, &endptr, 0); - if (spec == endptr || (*endptr && *endptr++ != ':')) - return AVERROR(EINVAL); - spec = endptr; - } else { - group_idx = strtol(spec, &endptr, 0); - /* Disallow empty id and make sure that if we are not at the end, then another specifier must follow. */ - if (spec == endptr || (*endptr && *endptr++ != ':')) - return AVERROR(EINVAL); - spec = endptr; - } - if (match) { - if (group_id > 0) { - for (unsigned i = 0; i < s->nb_stream_groups; i++) { - if (group_id == s->stream_groups[i]->id) { - group_idx = i; - break; - } - } - } - if (group_idx < 0 || group_idx >= s->nb_stream_groups) - return AVERROR(EINVAL); - for (unsigned j = 0; j < s->stream_groups[group_idx]->nb_streams; j++) { - if (st->index == s->stream_groups[group_idx]->streams[j]->index) { - found = 1; - if (g) - *g = s->stream_groups[group_idx]; - break; - } - } - } - if (!found) - match = 0; - } else if (*spec == 'p' && *(spec + 1) == ':') { - int prog_id; - int found = 0; - char *endptr; - spec += 2; - prog_id = strtol(spec, &endptr, 0); - /* Disallow empty id and make sure that if we are not at the end, then another specifier must follow. */ - if (spec == endptr || (*endptr && *endptr++ != ':')) - return AVERROR(EINVAL); - spec = endptr; - if (match) { - for (unsigned i = 0; i < s->nb_programs; i++) { - if (s->programs[i]->id != prog_id) - continue; + ss->stream_list = STREAM_LIST_GROUP_ID; - for (unsigned j = 0; j < s->programs[i]->nb_stream_indexes; j++) { - if (st->index == s->programs[i]->stream_index[j]) { - found = 1; - if (p) - *p = s->programs[i]; - i = s->nb_programs; - break; - } - } - } + spec += 1 + (*spec == 'i'); + } else + ss->stream_list = STREAM_LIST_GROUP_IDX; + + ss->list_id = strtol(spec, &endptr, 0); + if (spec == endptr) { + av_log(logctx, AV_LOG_ERROR, "Expected stream group idx/ID, got: %s\n", spec); + ret = AVERROR(EINVAL); + goto fail; } - if (!found) - match = 0; + spec = endptr; + + av_log(logctx, AV_LOG_TRACE, "Parsed stream group %s: %"PRId64"; remainder: %s\n", + ss->stream_list == STREAM_LIST_GROUP_ID ? "ID" : "index", ss->list_id, spec); + } else if (*spec == 'p' && *(spec + 1) == ':') { + if (ss->stream_list != STREAM_LIST_ALL) + goto multiple_stream_lists; + + ss->stream_list = STREAM_LIST_PROGRAM; + + spec += 2; + ss->list_id = strtol(spec, &endptr, 0); + if (spec == endptr) { + av_log(logctx, AV_LOG_ERROR, "Expected program ID, got: %s\n", spec); + ret = AVERROR(EINVAL); + goto fail; + } + spec = endptr; + + av_log(logctx, AV_LOG_TRACE, + "Parsed program ID: %"PRId64"; remainder: %s\n", ss->list_id, spec); } else if (*spec == '#' || (*spec == 'i' && *(spec + 1) == ':')) { - int stream_id; - char *endptr; + if (ss->stream_list != STREAM_LIST_ALL) + goto multiple_stream_lists; + + ss->stream_list = STREAM_LIST_STREAM_ID; + spec += 1 + (*spec == 'i'); - stream_id = strtol(spec, &endptr, 0); - if (spec == endptr || *endptr) /* Disallow empty id and make sure we are at the end. */ - return AVERROR(EINVAL); - return match && (stream_id == st->id); + ss->list_id = strtol(spec, &endptr, 0); + if (spec == endptr) { + av_log(logctx, AV_LOG_ERROR, "Expected stream ID, got: %s\n", spec); + ret = AVERROR(EINVAL); + goto fail; + } + spec = endptr; + + av_log(logctx, AV_LOG_TRACE, + "Parsed stream ID: %"PRId64"; remainder: %s\n", ss->list_id, spec); + + // this terminates the specifier + break; } else if (*spec == 'm' && *(spec + 1) == ':') { - const AVDictionaryEntry *tag; - char *key, *val; - int ret; + av_assert0(!ss->meta_key && !ss->meta_val); - if (match) { - spec += 2; - val = strchr(spec, ':'); - - key = val ? av_strndup(spec, val - spec) : av_strdup(spec); - if (!key) - return AVERROR(ENOMEM); - - tag = av_dict_get(st->metadata, key, NULL, 0); - if (tag) { - if (!val || !strcmp(tag->value, val + 1)) - ret = 1; - else - ret = 0; - } else - ret = 0; - - av_freep(&key); + spec += 2; + ss->meta_key = av_get_token(&spec, ":"); + if (!ss->meta_key) { + ret = AVERROR(ENOMEM); + goto fail; } - return match && ret; - } else if (*spec == 'u' && *(spec + 1) == '\0') { - const AVCodecParameters *par = st->codecpar; - int val; - switch (par->codec_type) { - case AVMEDIA_TYPE_AUDIO: - val = par->sample_rate && par->ch_layout.nb_channels; - if (par->format == AV_SAMPLE_FMT_NONE) - return 0; - break; - case AVMEDIA_TYPE_VIDEO: - val = par->width && par->height; - if (par->format == AV_PIX_FMT_NONE) - return 0; - break; - case AVMEDIA_TYPE_UNKNOWN: - val = 0; - break; - default: - val = 1; - break; + if (*spec == ':') { + spec++; + ss->meta_val = av_get_token(&spec, ":"); + if (!ss->meta_val) { + ret = AVERROR(ENOMEM); + goto fail; + } } - return match && (par->codec_id != AV_CODEC_ID_NONE && val != 0); - } else { - return AVERROR(EINVAL); + + av_log(logctx, AV_LOG_TRACE, + "Parsed metadata: %s:%s; remainder: %s", ss->meta_key, + ss->meta_val ? ss->meta_val : "", spec); + + // this terminates the specifier + break; + } else if (*spec == 'u' && (*(spec + 1) == '\0' || *(spec + 1) == ':')) { + ss->usable_only = 1; + spec++; + av_log(logctx, AV_LOG_ERROR, "Parsed 'usable only'\n"); + + // this terminates the specifier + break; + } else + break; + + if (*spec == ':') + spec++; + } + + if (*spec) { + if (!allow_remainder) { + av_log(logctx, AV_LOG_ERROR, + "Trailing garbage at the end of a stream specifier: %s\n", + spec); + ret = AVERROR(EINVAL); + goto fail; + } + + if (*spec == ':') + spec++; + + ss->remainder = av_strdup(spec); + if (!ss->remainder) { + ret = AVERROR(EINVAL); + goto fail; } } - return match; + return 0; + +multiple_stream_lists: + av_log(logctx, AV_LOG_ERROR, + "Cannot combine multiple program/group designators in a " + "single stream specifier"); + ret = AVERROR(EINVAL); + +fail: + stream_specifier_uninit(ss); + return ret; +} + +unsigned stream_specifier_match(const StreamSpecifier *ss, + const AVFormatContext *s, const AVStream *st, + void *logctx) +{ + const AVStreamGroup *g = NULL; + const AVProgram *p = NULL; + int start_stream = 0, nb_streams; + int nb_matched = 0; + + switch (ss->stream_list) { + case STREAM_LIST_STREAM_ID: + // stream with given ID makes no sense and should be impossible to request + av_assert0(ss->idx < 0); + // return early if we know for sure the stream does not match + if (st->id != ss->list_id) + return 0; + start_stream = st->index; + nb_streams = st->index + 1; + break; + case STREAM_LIST_ALL: + start_stream = ss->idx >= 0 ? 0 : st->index; + nb_streams = st->index + 1; + break; + case STREAM_LIST_PROGRAM: + for (unsigned i = 0; i < s->nb_programs; i++) { + if (s->programs[i]->id == ss->list_id) { + p = s->programs[i]; + break; + } + } + if (!p) { + av_log(logctx, AV_LOG_WARNING, "No program with ID %"PRId64" exists," + " stream specifier can never match\n", ss->list_id); + return 0; + } + nb_streams = p->nb_stream_indexes; + break; + case STREAM_LIST_GROUP_ID: + for (unsigned i = 0; i < s->nb_stream_groups; i++) { + if (ss->list_id == s->stream_groups[i]->id) { + g = s->stream_groups[i]; + break; + } + } + // fall-through + case STREAM_LIST_GROUP_IDX: + if (ss->stream_list == STREAM_LIST_GROUP_IDX && + ss->list_id >= 0 && ss->list_id < s->nb_stream_groups) + g = s->stream_groups[ss->list_id]; + + if (!g) { + av_log(logctx, AV_LOG_WARNING, "No stream group with group %s %" + PRId64" exists, stream specifier can never match\n", + ss->stream_list == STREAM_LIST_GROUP_ID ? "ID" : "index", + ss->list_id); + return 0; + } + nb_streams = g->nb_streams; + break; + default: av_assert0(0); + } + + for (int i = start_stream; i < nb_streams; i++) { + const AVStream *candidate = s->streams[g ? g->streams[i]->index : + p ? p->stream_index[i] : i]; + + if (ss->media_type != AVMEDIA_TYPE_UNKNOWN && + (ss->media_type != candidate->codecpar->codec_type || + (ss->no_apic && (candidate->disposition & AV_DISPOSITION_ATTACHED_PIC)))) + continue; + + if (ss->meta_key) { + const AVDictionaryEntry *tag = av_dict_get(candidate->metadata, + ss->meta_key, NULL, 0); + + if (!tag) + continue; + if (ss->meta_val && strcmp(tag->value, ss->meta_val)) + continue; + } + + if (ss->usable_only) { + const AVCodecParameters *par = candidate->codecpar; + + switch (par->codec_type) { + case AVMEDIA_TYPE_AUDIO: + if (!par->sample_rate || !par->ch_layout.nb_channels || + par->format == AV_SAMPLE_FMT_NONE) + continue; + break; + case AVMEDIA_TYPE_VIDEO: + if (!par->width || !par->height || par->format == AV_PIX_FMT_NONE) + continue; + break; + case AVMEDIA_TYPE_UNKNOWN: + continue; + } + } + + if (st == candidate) + return ss->idx < 0 || ss->idx == nb_matched; + + nb_matched++; + } + + return 0; } int check_stream_specifier(AVFormatContext *s, AVStream *st, const char *spec) { - int ret, index; - char *endptr; - const char *indexptr = NULL; - const AVStreamGroup *g = NULL; - const AVProgram *p = NULL; - int nb_streams; + StreamSpecifier ss; + int ret; - ret = match_stream_specifier(s, st, spec, &indexptr, &g, &p); + ret = stream_specifier_parse(&ss, spec, 0, NULL); if (ret < 0) - goto error; - - if (!indexptr) return ret; - index = strtol(indexptr, &endptr, 0); - if (*endptr) { /* We can't have anything after the requested index. */ - ret = AVERROR(EINVAL); - goto error; - } - - /* This is not really needed but saves us a loop for simple stream index specifiers. */ - if (spec == indexptr) - return (index == st->index); - - /* If we requested a matching stream index, we have to ensure st is that. */ - nb_streams = g ? g->nb_streams : (p ? p->nb_stream_indexes : s->nb_streams); - for (int i = 0; i < nb_streams && index >= 0; i++) { - unsigned idx = g ? g->streams[i]->index : (p ? p->stream_index[i] : i); - const AVStream *candidate = s->streams[idx]; - ret = match_stream_specifier(s, candidate, spec, NULL, NULL, NULL); - if (ret < 0) - goto error; - if (ret > 0 && index-- == 0 && st == candidate) - return 1; - } - return 0; - -error: - if (ret == AVERROR(EINVAL)) - av_log(s, AV_LOG_ERROR, "Invalid stream specifier: %s.\n", spec); + ret = stream_specifier_match(&ss, s, st, NULL); + stream_specifier_uninit(&ss); return ret; } diff --git a/fftools/cmdutils.h b/fftools/cmdutils.h index abc8d26607..f7005aabf9 100644 --- a/fftools/cmdutils.h +++ b/fftools/cmdutils.h @@ -102,6 +102,62 @@ enum OptionType { int parse_number(const char *context, const char *numstr, enum OptionType type, double min, double max, double *dst); +enum StreamList { + STREAM_LIST_ALL, + STREAM_LIST_STREAM_ID, + STREAM_LIST_PROGRAM, + STREAM_LIST_GROUP_ID, + STREAM_LIST_GROUP_IDX, +}; + +typedef struct StreamSpecifier { + // trailing stream index - pick idx-th stream that matches + // all the other constraints; -1 when not present + int idx; + + // which stream list to consider + enum StreamList stream_list; + + // STREAM_LIST_STREAM_ID: stream ID + // STREAM_LIST_GROUP_IDX: group index + // STREAM_LIST_GROUP_ID: group ID + // STREAM_LIST_PROGRAM: program ID + int64_t list_id; + + // when not AVMEDIA_TYPE_UNKNOWN, consider only streams of this type + enum AVMediaType media_type; + uint8_t no_apic; + + uint8_t usable_only; + + char *meta_key; + char *meta_val; + + char *remainder; +} StreamSpecifier; + +/** + * Parse a stream specifier string into a form suitable for matching. + * + * @param ss Parsed specifier will be stored here; must be uninitialized + * with stream_specifier_uninit() when no longer needed. + * @param spec String containing the stream specifier to be parsed. + * @param allow_remainder When 1, the part of spec that is left after parsing + * the stream specifier is stored into ss->remainder. + * When 0, any remainder will cause parsing to fail. + */ +int stream_specifier_parse(StreamSpecifier *ss, const char *spec, + int allow_remainder, void *logctx); + +/** + * @return 1 if st matches the parsed specifier, 0 if it does not + */ +unsigned stream_specifier_match(const StreamSpecifier *ss, + const AVFormatContext *s, const AVStream *st, + void *logctx); + +void stream_specifier_uninit(StreamSpecifier *ss); + typedef struct SpecifierOpt { char *specifier; /**< stream/chapter/program/... specifier */ union { From patchwork Sat Aug 10 17:16:20 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Khirnov X-Patchwork-Id: 50970 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:612c:1ff2:b0:489:2eb3:e4c4 with SMTP id ks18csp530199vqb; Sat, 10 Aug 2024 10:26:13 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCUfHtvw85V/Nz0B3+Wb9Euca4KQMR+uufMdsoicotPsUtpPZZ3xWoDMk9+B30qRyE67Oo6c/gzMexj/7/IVwvWb3GOxI+Y3GCzsaA== X-Google-Smtp-Source: AGHT+IHkc39DIHs4/GBxxAinkodS/zKCpGWfm7eu0SEZ3zKwxwp62GPjkUOz9gRLkIPWqT64l7Nm X-Received: by 2002:a2e:a23b:0:b0:2ef:18b7:440a with SMTP id 38308e7fff4ca-2f1a6c67ca2mr34272171fa.22.1723310772889; Sat, 10 Aug 2024 10:26:12 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1723310772; cv=none; d=google.com; s=arc-20160816; b=Nh7ENFaJvkRQNFB1KCABfhPMBwIX7KZp+o3a18q4Ph/MVfwar4LJWaNMnsAQPGF44q gBE8l25HqIiLLlNaYIm7ROM6Ne3sNOCnwW/Vgsi1XtymIAOoZMTBAj+SCB9UoL3yenCg i+hj8J/3+drXxMakXbhYrdrbmjXczvEFaSWmzbTNDEkO2QTK4Q0llM2u+ceAsBxLxC7R GFsdU71hIzeX9F/YQxk57/xusEcnR0hbVMEkHz+dubWFM2SfdBuxF4k3/FJPam4HIuAU QnYC8AQ3r/vPQHT/hk9IhEEFtS9OT8ZoKcttfNk/ZDLfDMRLqa2tuRm2o//mzcvpUIHG ljzw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:references:in-reply-to:message-id :date:to:from:dkim-signature:delivered-to; bh=p2b+kXJ08nN6TlEDbyQ72X4HATszGy6KV4elYxmsV5c=; fh=YOA8vD9MJZuwZ71F/05pj6KdCjf6jQRmzLS+CATXUQk=; b=GzkuG97lbxCi2ZnHoGVIYCypZEjZmStLzSbAUq9evQw8Bg8THFcgInBEYBj+KLRFjz dxgcaWUZTfNoC+eHUU/uTMi5LPyt64GisLgYugliCu0UGLRIPaBITNIKgh7t4u70dvp9 hTtzrNHNU2zVEZ3YrbVh0wXo04iaFCv22EdDfn9FL7u1n+vsdZXSolekdcSzqeuSrjt6 um95MA/bqoNoK6lTxr1z/hG2701Iwqn5vKkD/NgjytXVNP1erhxRofsKja+kNBCcmczE EYi20YoH0otYIT0M5POuiX78QzLI0jB546I95NgUAazhaQYMMdBWcm/LQ92/bzUAi7Rb nNkg==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b=nxW4bOtm; 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 38308e7fff4ca-2f292073ed6si5457591fa.610.2024.08.10.10.26.12; Sat, 10 Aug 2024 10:26:12 -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; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b=nxW4bOtm; 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 ED62568DA86; Sat, 10 Aug 2024 20:16:45 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail1.khirnov.net (quelana.khirnov.net [94.230.150.81]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id E6CDF68D93B for ; Sat, 10 Aug 2024 20:16:35 +0300 (EEST) Authentication-Results: mail1.khirnov.net; dkim=pass (2048-bit key; unprotected) header.d=khirnov.net header.i=@khirnov.net header.a=rsa-sha256 header.s=mail header.b=nxW4bOtm; dkim-atps=neutral Received: from localhost (mail1.khirnov.net [IPv6:::1]) by mail1.khirnov.net (Postfix) with ESMTP id 83D524DF6 for ; Sat, 10 Aug 2024 19:16:35 +0200 (CEST) Received: from mail1.khirnov.net ([IPv6:::1]) by localhost (mail1.khirnov.net [IPv6:::1]) (amavis, port 10024) with ESMTP id IHiJIxnDL2GC for ; Sat, 10 Aug 2024 19:16:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=khirnov.net; s=mail; t=1723310193; bh=AAoE9vXAbbKt3WBUWAfs8b9Tm5vrsFN0qshNd1CIWdU=; h=From:To:Subject:Date:In-Reply-To:References:From; b=nxW4bOtmx0bU59ia9uoCCF3El2SZHDNYskrFhDBPp9Np8HXrHV9UteX5kGzm5gWrt rNMHP2iwT+xN/+itar1J8Clmp9JJ+sAcOBPHMMQcCs/HOwsJjNps/f2ccYeBACFqhO LPWhfMj8elLebAzTDa0w9MLnMvJNIJVuvwCV+Y8Ky6zbfRSxiRvM5W/sLYoRSgdeXi +4mtBb+vBNDHpu7etxy5iCbymBzqYfrvXoCCGX2QBT7CrmKVWcOIhqQ/tCuq/VxJ2l GuhscPzpjE7Q9pk4IcQ7yueKuLk57oIY6l7W15wbhzfZUnGXrz0R7Mcm/EevV6tuY9 LErZpNCyedwGw== Received: from libav.khirnov.net (libav.khirnov.net [IPv6:2a00:c500:561:201::7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256 client-signature RSA-PSS (2048 bits) client-digest SHA256) (Client CN "libav.khirnov.net", Issuer "smtp.khirnov.net SMTP CA" (verified OK)) by mail1.khirnov.net (Postfix) with ESMTPS id E863C4DBC for ; Sat, 10 Aug 2024 19:16:32 +0200 (CEST) Received: from libav.khirnov.net (libav.khirnov.net [IPv6:::1]) by libav.khirnov.net (Postfix) with ESMTP id 6957C3A1E29 for ; Sat, 10 Aug 2024 19:16:26 +0200 (CEST) From: Anton Khirnov To: ffmpeg-devel@ffmpeg.org Date: Sat, 10 Aug 2024 19:16:20 +0200 Message-ID: <20240810171621.26757-5-anton@khirnov.net> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240810171621.26757-1-anton@khirnov.net> References: <20240810171621.26757-1-anton@khirnov.net> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 5/6] fftools/ffmpeg: use new stream specifier API in opt_match_per_stream*() 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: YTM46G5Atmdz Removes a lot of error checking code, as matching cannot fail. --- fftools/cmdutils.c | 8 ++ fftools/cmdutils.h | 6 +- fftools/ffmpeg.h | 16 +-- fftools/ffmpeg_demux.c | 87 ++++------------- fftools/ffmpeg_mux_init.c | 198 ++++++++++---------------------------- fftools/ffmpeg_opt.c | 32 +++--- 6 files changed, 105 insertions(+), 242 deletions(-) diff --git a/fftools/cmdutils.c b/fftools/cmdutils.c index b3f501bd56..f725c31c12 100644 --- a/fftools/cmdutils.c +++ b/fftools/cmdutils.c @@ -288,6 +288,14 @@ static int write_option(void *optctx, const OptionDef *po, const char *opt, goto finish; } sol->opt[sol->nb_opt - 1].specifier = str; + + if (po->flags & OPT_FLAG_PERSTREAM) { + ret = stream_specifier_parse(&sol->opt[sol->nb_opt - 1].stream_spec, + str, 0, NULL); + if (ret < 0) + goto finish; + } + dst = &sol->opt[sol->nb_opt - 1].u; } diff --git a/fftools/cmdutils.h b/fftools/cmdutils.h index f7005aabf9..9609c6c739 100644 --- a/fftools/cmdutils.h +++ b/fftools/cmdutils.h @@ -159,7 +159,11 @@ unsigned stream_specifier_match(const StreamSpecifier *ss, void stream_specifier_uninit(StreamSpecifier *ss); typedef struct SpecifierOpt { - char *specifier; /**< stream/chapter/program/... specifier */ + // original specifier or empty string + char *specifier; + // parsed specifier for OPT_FLAG_PERSTREAM options + StreamSpecifier stream_spec; + union { uint8_t *str; int i; diff --git a/fftools/ffmpeg.h b/fftools/ffmpeg.h index 674ae340f2..3c5d933e17 100644 --- a/fftools/ffmpeg.h +++ b/fftools/ffmpeg.h @@ -859,14 +859,14 @@ void update_benchmark(const char *fmt, ...); const char *opt_match_per_type_str(const SpecifierOptList *sol, char mediatype); -int opt_match_per_stream_str(void *logctx, const SpecifierOptList *sol, - AVFormatContext *fc, AVStream *st, const char **out); -int opt_match_per_stream_int(void *logctx, const SpecifierOptList *sol, - AVFormatContext *fc, AVStream *st, int *out); -int opt_match_per_stream_int64(void *logctx, const SpecifierOptList *sol, - AVFormatContext *fc, AVStream *st, int64_t *out); -int opt_match_per_stream_dbl(void *logctx, const SpecifierOptList *sol, - AVFormatContext *fc, AVStream *st, double *out); +void opt_match_per_stream_str(void *logctx, const SpecifierOptList *sol, + AVFormatContext *fc, AVStream *st, const char **out); +void opt_match_per_stream_int(void *logctx, const SpecifierOptList *sol, + AVFormatContext *fc, AVStream *st, int *out); +void opt_match_per_stream_int64(void *logctx, const SpecifierOptList *sol, + AVFormatContext *fc, AVStream *st, int64_t *out); +void opt_match_per_stream_dbl(void *logctx, const SpecifierOptList *sol, + AVFormatContext *fc, AVStream *st, double *out); int muxer_thread(void *arg); int encoder_thread(void *arg); diff --git a/fftools/ffmpeg_demux.c b/fftools/ffmpeg_demux.c index e9a4b5c22a..039ee0c785 100644 --- a/fftools/ffmpeg_demux.c +++ b/fftools/ffmpeg_demux.c @@ -1084,12 +1084,8 @@ static int choose_decoder(const OptionsContext *o, void *logctx, { const char *codec_name = NULL; - int ret; - - ret = opt_match_per_stream_str(logctx, &o->codec_names, s, st, &codec_name); - if (ret < 0) - return ret; + opt_match_per_stream_str(logctx, &o->codec_names, s, st, &codec_name); if (codec_name) { int ret = find_codec(NULL, codec_name, st->codecpar->codec_type, 0, pcodec); if (ret < 0) @@ -1153,18 +1149,11 @@ static int add_display_matrix_to_stream(const OptionsContext *o, double rotation = DBL_MAX; int hflip = -1, vflip = -1; int hflip_set = 0, vflip_set = 0, rotation_set = 0; - int ret; int32_t *buf; - ret = opt_match_per_stream_dbl(ist, &o->display_rotations, ctx, st, &rotation); - if (ret < 0) - return ret; - ret = opt_match_per_stream_int(ist, &o->display_hflips, ctx, st, &hflip); - if (ret < 0) - return ret; - ret = opt_match_per_stream_int(ist, &o->display_vflips, ctx, st, &vflip); - if (ret < 0) - return ret; + opt_match_per_stream_dbl(ist, &o->display_rotations, ctx, st, &rotation); + opt_match_per_stream_int(ist, &o->display_hflips, ctx, st, &hflip); + opt_match_per_stream_int(ist, &o->display_vflips, ctx, st, &vflip); rotation_set = rotation != DBL_MAX; hflip_set = hflip != -1; @@ -1262,19 +1251,13 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona ds->dec_opts.time_base = st->time_base; ds->ts_scale = 1.0; - ret = opt_match_per_stream_dbl(ist, &o->ts_scale, ic, st, &ds->ts_scale); - if (ret < 0) - return ret; + opt_match_per_stream_dbl(ist, &o->ts_scale, ic, st, &ds->ts_scale); ds->autorotate = 1; - ret = opt_match_per_stream_int(ist, &o->autorotate, ic, st, &ds->autorotate); - if (ret < 0) - return ret; + opt_match_per_stream_int(ist, &o->autorotate, ic, st, &ds->autorotate); ds->apply_cropping = CROP_ALL; - ret = opt_match_per_stream_str(ist, &o->apply_cropping, ic, st, &apply_cropping); - if (ret < 0) - return ret; + opt_match_per_stream_str(ist, &o->apply_cropping, ic, st, &apply_cropping); if (apply_cropping) { const AVOption opts[] = { { "apply_cropping", NULL, 0, AV_OPT_TYPE_INT, @@ -1300,9 +1283,7 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona } } - ret = opt_match_per_stream_str(ist, &o->codec_tags, ic, st, &codec_tag); - if (ret < 0) - return ret; + opt_match_per_stream_str(ist, &o->codec_tags, ic, st, &codec_tag); if (codec_tag) { uint32_t tag = strtol(codec_tag, &next, 0); if (*next) { @@ -1319,15 +1300,9 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona if (ret < 0) return ret; - ret = opt_match_per_stream_str(ist, &o->hwaccels, ic, st, &hwaccel); - if (ret < 0) - return ret; - - ret = opt_match_per_stream_str(ist, &o->hwaccel_output_formats, ic, st, + opt_match_per_stream_str(ist, &o->hwaccels, ic, st, &hwaccel); + opt_match_per_stream_str(ist, &o->hwaccel_output_formats, ic, st, &hwaccel_output_format); - if (ret < 0) - return ret; - if (!hwaccel_output_format && hwaccel && !strcmp(hwaccel, "cuvid")) { av_log(ist, AV_LOG_WARNING, "WARNING: defaulting hwaccel_output_format to cuda for compatibility " @@ -1385,9 +1360,7 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona } } - ret = opt_match_per_stream_str(ist, &o->hwaccel_devices, ic, st, &hwaccel_device); - if (ret < 0) - return ret; + opt_match_per_stream_str(ist, &o->hwaccel_devices, ic, st, &hwaccel_device); if (hwaccel_device) { ds->dec_opts.hwaccel_device = av_strdup(hwaccel_device); if (!ds->dec_opts.hwaccel_device) @@ -1408,9 +1381,7 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona } ds->reinit_filters = -1; - ret = opt_match_per_stream_int(ist, &o->reinit_filters, ic, st, &ds->reinit_filters); - if (ret < 0) - return ret; + opt_match_per_stream_int(ist, &o->reinit_filters, ic, st, &ds->reinit_filters); ist->user_set_discard = AVDISCARD_NONE; @@ -1420,9 +1391,7 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona (o->data_disable && ist->st->codecpar->codec_type == AVMEDIA_TYPE_DATA)) ist->user_set_discard = AVDISCARD_ALL; - ret = opt_match_per_stream_str(ist, &o->discard, ic, st, &discard_str); - if (ret < 0) - return ret; + opt_match_per_stream_str(ist, &o->discard, ic, st, &discard_str); if (discard_str) { ret = av_opt_set(ist->st, "discard", discard_str, 0); if (ret < 0) { @@ -1444,9 +1413,7 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona switch (par->codec_type) { case AVMEDIA_TYPE_VIDEO: - ret = opt_match_per_stream_str(ist, &o->frame_rates, ic, st, &framerate); - if (ret < 0) - return ret; + opt_match_per_stream_str(ist, &o->frame_rates, ic, st, &framerate); if (framerate) { ret = av_parse_video_rate(&ist->framerate, framerate); if (ret < 0) { @@ -1458,18 +1425,14 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona #if FFMPEG_OPT_TOP ist->top_field_first = -1; - ret = opt_match_per_stream_int(ist, &o->top_field_first, ic, st, &ist->top_field_first); - if (ret < 0) - return ret; + opt_match_per_stream_int(ist, &o->top_field_first, ic, st, &ist->top_field_first); #endif break; case AVMEDIA_TYPE_AUDIO: { const char *ch_layout_str = NULL; - ret = opt_match_per_stream_str(ist, &o->audio_ch_layouts, ic, st, &ch_layout_str); - if (ret < 0) - return ret; + opt_match_per_stream_str(ist, &o->audio_ch_layouts, ic, st, &ch_layout_str); if (ch_layout_str) { AVChannelLayout ch_layout; ret = av_channel_layout_from_string(&ch_layout, ch_layout_str); @@ -1489,10 +1452,7 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona } } else { int guess_layout_max = INT_MAX; - ret = opt_match_per_stream_int(ist, &o->guess_layout_max, ic, st, &guess_layout_max); - if (ret < 0) - return ret; - + opt_match_per_stream_int(ist, &o->guess_layout_max, ic, st, &guess_layout_max); guess_input_channel_layout(ist, par, guess_layout_max); } break; @@ -1501,13 +1461,8 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona case AVMEDIA_TYPE_SUBTITLE: { const char *canvas_size = NULL; - ret = opt_match_per_stream_int(ist, &o->fix_sub_duration, ic, st, &ist->fix_sub_duration); - if (ret < 0) - return ret; - - ret = opt_match_per_stream_str(ist, &o->canvas_sizes, ic, st, &canvas_size); - if (ret < 0) - return ret; + opt_match_per_stream_int(ist, &o->fix_sub_duration, ic, st, &ist->fix_sub_duration); + opt_match_per_stream_str(ist, &o->canvas_sizes, ic, st, &canvas_size); if (canvas_size) { ret = av_parse_video_size(&par->width, &par->height, canvas_size); @@ -1537,9 +1492,7 @@ static int ist_add(const OptionsContext *o, Demuxer *d, AVStream *st, AVDictiona if (ist->st->sample_aspect_ratio.num) ist->par->sample_aspect_ratio = ist->st->sample_aspect_ratio; - ret = opt_match_per_stream_str(ist, &o->bitstream_filters, ic, st, &bsfs); - if (ret < 0) - return ret; + opt_match_per_stream_str(ist, &o->bitstream_filters, ic, st, &bsfs); if (bsfs) { ret = av_bsf_list_parse_str(bsfs, &ds->bsf); if (ret < 0) { diff --git a/fftools/ffmpeg_mux_init.c b/fftools/ffmpeg_mux_init.c index 2b3a4ddcf9..e84fa9719f 100644 --- a/fftools/ffmpeg_mux_init.c +++ b/fftools/ffmpeg_mux_init.c @@ -71,13 +71,10 @@ static int choose_encoder(const OptionsContext *o, AVFormatContext *s, { enum AVMediaType type = ost->type; const char *codec_name = NULL; - int ret; *enc = NULL; - ret = opt_match_per_stream_str(ost, &o->codec_names, s, ost->st, &codec_name); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->codec_names, s, ost->st, &codec_name); if (type != AVMEDIA_TYPE_VIDEO && type != AVMEDIA_TYPE_AUDIO && @@ -419,17 +416,12 @@ static int ost_get_filters(const OptionsContext *o, AVFormatContext *oc, OutputStream *ost, char **dst) { const char *filters = NULL; - int ret; #if FFMPEG_OPT_FILTER_SCRIPT const char *filters_script = NULL; - ret = opt_match_per_stream_str(ost, &o->filter_scripts, oc, ost->st, &filters_script); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->filter_scripts, oc, ost->st, &filters_script); #endif - ret = opt_match_per_stream_str(ost, &o->filters, oc, ost->st, &filters); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->filters, oc, ost->st, &filters); if (!ost->enc) { if ( @@ -599,17 +591,13 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, st = ost->st; - ret = opt_match_per_stream_str(ost, &o->frame_rates, oc, st, &frame_rate); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->frame_rates, oc, st, &frame_rate); if (frame_rate && av_parse_video_rate(&ost->frame_rate, frame_rate) < 0) { av_log(ost, AV_LOG_FATAL, "Invalid framerate value: %s\n", frame_rate); return AVERROR(EINVAL); } - ret = opt_match_per_stream_str(ost, &o->max_frame_rates, oc, st, &max_frame_rate); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->max_frame_rates, oc, st, &max_frame_rate); if (max_frame_rate && av_parse_video_rate(&ost->max_frame_rate, max_frame_rate) < 0) { av_log(ost, AV_LOG_FATAL, "Invalid maximum framerate value: %s\n", max_frame_rate); return AVERROR(EINVAL); @@ -620,9 +608,7 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, return AVERROR(EINVAL); } - ret = opt_match_per_stream_str(ost, &o->frame_aspect_ratios, oc, st, &frame_aspect_ratio); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->frame_aspect_ratios, oc, st, &frame_aspect_ratio); if (frame_aspect_ratio) { AVRational q; if (av_parse_ratio(&q, frame_aspect_ratio, 255, 0, NULL) < 0 || @@ -643,9 +629,7 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, int do_pass = 0; int i; - ret = opt_match_per_stream_str(ost, &o->frame_sizes, oc, st, &frame_size); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->frame_sizes, oc, st, &frame_size); if (frame_size) { ret = av_parse_video_size(&video_enc->width, &video_enc->height, frame_size); if (ret < 0) { @@ -654,9 +638,7 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, } } - ret = opt_match_per_stream_str(ost, &o->frame_pix_fmts, oc, st, &frame_pix_fmt); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->frame_pix_fmts, oc, st, &frame_pix_fmt); if (frame_pix_fmt && *frame_pix_fmt == '+') { *keep_pix_fmt = 1; if (!*++frame_pix_fmt) @@ -668,9 +650,7 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, return AVERROR(EINVAL); } - ret = opt_match_per_stream_str(ost, &o->intra_matrices, oc, st, &intra_matrix); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->intra_matrices, oc, st, &intra_matrix); if (intra_matrix) { if (!(video_enc->intra_matrix = av_mallocz(sizeof(*video_enc->intra_matrix) * 64))) return AVERROR(ENOMEM); @@ -679,9 +659,7 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, if (ret < 0) return ret; } - ret = opt_match_per_stream_str(ost, &o->chroma_intra_matrices, oc, st, &chroma_intra_matrix); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->chroma_intra_matrices, oc, st, &chroma_intra_matrix); if (chroma_intra_matrix) { uint16_t *p = av_mallocz(sizeof(*video_enc->chroma_intra_matrix) * 64); if (!p) @@ -691,9 +669,7 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, if (ret < 0) return ret; } - ret = opt_match_per_stream_str(ost, &o->inter_matrices, oc, st, &inter_matrix); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->inter_matrices, oc, st, &inter_matrix); if (inter_matrix) { if (!(video_enc->inter_matrix = av_mallocz(sizeof(*video_enc->inter_matrix) * 64))) return AVERROR(ENOMEM); @@ -702,9 +678,7 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, return ret; } - ret = opt_match_per_stream_str(ost, &o->rc_overrides, oc, st, &p); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->rc_overrides, oc, st, &p); for (i = 0; p; i++) { int start, end, q; int e = sscanf(p, "%d,%d,%d", &start, &end, &q); @@ -735,9 +709,7 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, video_enc->rc_override_count = i; /* two pass mode */ - ret = opt_match_per_stream_int(ost, &o->pass, oc, st, &do_pass); - if (ret < 0) - return ret; + opt_match_per_stream_int(ost, &o->pass, oc, st, &do_pass); if (do_pass) { if (do_pass & 1) video_enc->flags |= AV_CODEC_FLAG_PASS1; @@ -745,9 +717,7 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, video_enc->flags |= AV_CODEC_FLAG_PASS2; } - ret = opt_match_per_stream_str(ost, &o->passlogfiles, oc, st, &ost->logfile_prefix); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->passlogfiles, oc, st, &ost->logfile_prefix); if (ost->logfile_prefix && !(ost->logfile_prefix = av_strdup(ost->logfile_prefix))) return AVERROR(ENOMEM); @@ -794,15 +764,11 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, } } - ret = opt_match_per_stream_int(ost, &o->force_fps, oc, st, &ost->force_fps); - if (ret < 0) - return ret; + opt_match_per_stream_int(ost, &o->force_fps, oc, st, &ost->force_fps); #if FFMPEG_OPT_TOP ost->top_field_first = -1; - ret = opt_match_per_stream_int(ost, &o->top_field_first, oc, st, &ost->top_field_first); - if (ret < 0) - return ret; + opt_match_per_stream_int(ost, &o->top_field_first, oc, st, &ost->top_field_first); if (ost->top_field_first >= 0) av_log(ost, AV_LOG_WARNING, "-top is deprecated, use the setfield filter instead\n"); #endif @@ -812,9 +778,7 @@ static int new_stream_video(Muxer *mux, const OptionsContext *o, #else *vsync_method = VSYNC_AUTO; #endif - ret = opt_match_per_stream_str(ost, &o->fps_mode, oc, st, &fps_mode); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->fps_mode, oc, st, &fps_mode); if (fps_mode) { ret = parse_and_set_vsync(fps_mode, vsync_method, ost->file->index, ost->index, 0); if (ret < 0) @@ -872,40 +836,28 @@ static int new_stream_audio(Muxer *mux, const OptionsContext *o, int channels = 0; const char *layout = NULL; const char *sample_fmt = NULL; - int ret; - ret = opt_match_per_stream_int(ost, &o->audio_channels, oc, st, &channels); - if (ret < 0) - return ret; + opt_match_per_stream_int(ost, &o->audio_channels, oc, st, &channels); if (channels) { audio_enc->ch_layout.order = AV_CHANNEL_ORDER_UNSPEC; audio_enc->ch_layout.nb_channels = channels; } - ret = opt_match_per_stream_str(ost, &o->audio_ch_layouts, oc, st, &layout); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->audio_ch_layouts, oc, st, &layout); if (layout && av_channel_layout_from_string(&audio_enc->ch_layout, layout) < 0) { av_log(ost, AV_LOG_FATAL, "Unknown channel layout: %s\n", layout); return AVERROR(EINVAL); } - ret = opt_match_per_stream_str(ost, &o->sample_fmts, oc, st, &sample_fmt); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->sample_fmts, oc, st, &sample_fmt); if (sample_fmt && (audio_enc->sample_fmt = av_get_sample_fmt(sample_fmt)) == AV_SAMPLE_FMT_NONE) { av_log(ost, AV_LOG_FATAL, "Invalid sample format '%s'\n", sample_fmt); return AVERROR(EINVAL); } - ret = opt_match_per_stream_int(ost, &o->audio_sample_rate, oc, st, &audio_enc->sample_rate); - if (ret < 0) - return ret; - - ret = opt_match_per_stream_str(ost, &o->apad, oc, st, &ms->apad); - if (ret < 0) - return ret; + opt_match_per_stream_int(ost, &o->audio_sample_rate, oc, st, &audio_enc->sample_rate); + opt_match_per_stream_str(ost, &o->apad, oc, st, &ms->apad); } return 0; @@ -928,11 +880,8 @@ static int new_stream_subtitle(Muxer *mux, const OptionsContext *o, int input_props = 0, output_props = 0; const char *frame_size = NULL; - int ret; - ret = opt_match_per_stream_str(ost, &o->frame_sizes, mux->fc, st, &frame_size); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->frame_sizes, mux->fc, st, &frame_size); if (frame_size) { int ret = av_parse_video_size(&subtitle_enc->width, &subtitle_enc->height, frame_size); if (ret < 0) { @@ -1211,13 +1160,8 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, if (ret < 0) goto fail; - ret = opt_match_per_stream_str(ost, &o->presets, oc, st, &preset); - if (ret < 0) - goto fail; - - ret = opt_match_per_stream_int(ost, &o->autoscale, oc, st, &autoscale); - if (ret < 0) - goto fail; + opt_match_per_stream_str(ost, &o->presets, oc, st, &preset); + opt_match_per_stream_int(ost, &o->autoscale, oc, st, &autoscale); if (preset && (!(ret = get_preset_file_2(preset, enc->codec->name, &s)))) { AVBPrint bprint; av_bprint_init(&bprint, 0, AV_BPRINT_SIZE_UNLIMITED); @@ -1248,14 +1192,12 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, goto fail; } - ret = opt_match_per_stream_str(ost, &o->enc_stats_pre, oc, st, &enc_stats_pre); - if (ret < 0) - goto fail; + opt_match_per_stream_str(ost, &o->enc_stats_pre, oc, st, &enc_stats_pre); if (enc_stats_pre && (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { const char *format = "{fidx} {sidx} {n} {t}"; - ret = opt_match_per_stream_str(ost, &o->enc_stats_pre_fmt, oc, st, &format); + opt_match_per_stream_str(ost, &o->enc_stats_pre_fmt, oc, st, &format); if (ret < 0) goto fail; @@ -1264,41 +1206,31 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, goto fail; } - ret = opt_match_per_stream_str(ost, &o->enc_stats_post, oc, st, &enc_stats_post); - if (ret < 0) - goto fail; + opt_match_per_stream_str(ost, &o->enc_stats_post, oc, st, &enc_stats_post); if (enc_stats_post && (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { const char *format = "{fidx} {sidx} {n} {t}"; - ret = opt_match_per_stream_str(ost, &o->enc_stats_post_fmt, oc, st, &format); - if (ret < 0) - goto fail; + opt_match_per_stream_str(ost, &o->enc_stats_post_fmt, oc, st, &format); ret = enc_stats_init(ost, &ost->enc_stats_post, 0, enc_stats_post, format); if (ret < 0) goto fail; } - ret = opt_match_per_stream_str(ost, &o->mux_stats, oc, st, &mux_stats); - if (ret < 0) - goto fail; + opt_match_per_stream_str(ost, &o->mux_stats, oc, st, &mux_stats); if (mux_stats && (type == AVMEDIA_TYPE_VIDEO || type == AVMEDIA_TYPE_AUDIO)) { const char *format = "{fidx} {sidx} {n} {t}"; - ret = opt_match_per_stream_str(ost, &o->mux_stats_fmt, oc, st, &format); - if (ret < 0) - goto fail; + opt_match_per_stream_str(ost, &o->mux_stats_fmt, oc, st, &format); ret = enc_stats_init(ost, &ms->stats, 0, mux_stats, format); if (ret < 0) goto fail; } - ret = opt_match_per_stream_str(ost, &o->enc_time_bases, oc, st, &enc_time_base); - if (ret < 0) - goto fail; + opt_match_per_stream_str(ost, &o->enc_time_bases, oc, st, &enc_time_base); if (enc_time_base && type == AVMEDIA_TYPE_SUBTITLE) av_log(ost, AV_LOG_WARNING, "-enc_time_base not supported for subtitles, ignoring\n"); @@ -1361,9 +1293,7 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, ost->bitexact = !!(ost->enc_ctx->flags & AV_CODEC_FLAG_BITEXACT); } - ret = opt_match_per_stream_str(ost, &o->time_bases, oc, st, &time_base); - if (ret < 0) - goto fail; + opt_match_per_stream_str(ost, &o->time_bases, oc, st, &time_base); if (time_base) { AVRational q; if (av_parse_ratio(&q, time_base, INT_MAX, 0, NULL) < 0 || @@ -1376,9 +1306,7 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, } ms->max_frames = INT64_MAX; - ret = opt_match_per_stream_int64(ost, &o->max_frames, oc, st, &ms->max_frames); - if (ret < 0) - return ret; + opt_match_per_stream_int64(ost, &o->max_frames, oc, st, &ms->max_frames); for (int i = 0; i < o->max_frames.nb_opt; i++) { char *p = o->max_frames.opt[i].specifier; if (!*p && type != AVMEDIA_TYPE_VIDEO) { @@ -1388,13 +1316,8 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, } ms->copy_prior_start = -1; - ret = opt_match_per_stream_int(ost, &o->copy_prior_start, oc, st, &ms->copy_prior_start); - if (ret < 0) - goto fail; - - ret = opt_match_per_stream_str(ost, &o->bitstream_filters, oc, st, &bsfs); - if (ret < 0) - goto fail; + opt_match_per_stream_int(ost, &o->copy_prior_start, oc, st, &ms->copy_prior_start); + opt_match_per_stream_str(ost, &o->bitstream_filters, oc, st, &bsfs); if (bsfs && *bsfs) { ret = av_bsf_list_parse_str(bsfs, &ms->bsf_ctx); if (ret < 0) { @@ -1403,9 +1326,7 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, } } - ret = opt_match_per_stream_str(ost, &o->codec_tags, oc, st, &codec_tag); - if (ret < 0) - goto fail; + opt_match_per_stream_str(ost, &o->codec_tags, oc, st, &codec_tag); if (codec_tag) { uint32_t tag = strtol(codec_tag, &next, 0); if (*next) { @@ -1419,9 +1340,7 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, ost->enc_ctx->codec_tag = tag; } - ret = opt_match_per_stream_dbl(ost, &o->qscale, oc, st, &qscale); - if (ret < 0) - return ret; + opt_match_per_stream_dbl(ost, &o->qscale, oc, st, &qscale); if (ost->enc_ctx && qscale >= 0) { ost->enc_ctx->flags |= AV_CODEC_FLAG_QSCALE; ost->enc_ctx->global_quality = FF_QP2LAMBDA * qscale; @@ -1431,38 +1350,26 @@ static int ost_add(Muxer *mux, const OptionsContext *o, enum AVMediaType type, int max_muxing_queue_size = 128; int muxing_queue_data_threshold = 50 * 1024 * 1024; - ret = opt_match_per_stream_int(ost, &o->max_muxing_queue_size, oc, st, - &max_muxing_queue_size); - if (ret < 0) - goto fail; - - ret = opt_match_per_stream_int(ost, &o->muxing_queue_data_threshold, - oc, st, &muxing_queue_data_threshold); - if (ret < 0) - goto fail; + opt_match_per_stream_int(ost, &o->max_muxing_queue_size, oc, st, + &max_muxing_queue_size); + opt_match_per_stream_int(ost, &o->muxing_queue_data_threshold, + oc, st, &muxing_queue_data_threshold); sch_mux_stream_buffering(mux->sch, mux->sch_idx, ms->sch_idx, max_muxing_queue_size, muxing_queue_data_threshold); } - ret = opt_match_per_stream_int(ost, &o->bits_per_raw_sample, oc, st, + opt_match_per_stream_int(ost, &o->bits_per_raw_sample, oc, st, &ost->bits_per_raw_sample); - if (ret < 0) - goto fail; - ret = opt_match_per_stream_int(ost, &o->fix_sub_duration_heartbeat, - oc, st, &ost->fix_sub_duration_heartbeat); - if (ret < 0) - goto fail; + opt_match_per_stream_int(ost, &o->fix_sub_duration_heartbeat, + oc, st, &ost->fix_sub_duration_heartbeat); if (oc->oformat->flags & AVFMT_GLOBALHEADER && ost->enc_ctx) ost->enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER; - ret = opt_match_per_stream_int(ost, &o->copy_initial_nonkeyframes, - oc, st, &ms->copy_initial_nonkeyframes); - if (ret < 0) - goto fail; - + opt_match_per_stream_int(ost, &o->copy_initial_nonkeyframes, + oc, st, &ms->copy_initial_nonkeyframes); switch (type) { case AVMEDIA_TYPE_VIDEO: ret = new_stream_video (mux, o, ost, &keep_pix_fmt, &vsync_method); break; case AVMEDIA_TYPE_AUDIO: ret = new_stream_audio (mux, o, ost); break; @@ -3036,9 +2943,7 @@ static int set_dispositions(Muxer *mux, const OptionsContext *o) nb_streams[ost->type + 1]++; - ret = opt_match_per_stream_str(ost, &o->disposition, ctx, ost->st, &dispositions[i]); - if (ret < 0) - goto finish; + opt_match_per_stream_str(ost, &o->disposition, ctx, ost->st, &dispositions[i]); have_manual |= !!dispositions[i]; @@ -3183,12 +3088,9 @@ static int process_forced_keyframes(Muxer *mux, const OptionsContext *o) for (int i = 0; i < mux->of.nb_streams; i++) { OutputStream *ost = mux->of.streams[i]; const char *forced_keyframes = NULL; - int ret; - ret = opt_match_per_stream_str(ost, &o->forced_key_frames, - mux->fc, ost->st, &forced_keyframes); - if (ret < 0) - return ret; + opt_match_per_stream_str(ost, &o->forced_key_frames, + mux->fc, ost->st, &forced_keyframes); if (!(ost->type == AVMEDIA_TYPE_VIDEO && ost->enc_ctx && forced_keyframes)) diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index c32d3ddc55..f6df6ae952 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -101,6 +101,8 @@ static void uninit_options(OptionsContext *o) SpecifierOptList *so = dst; for (int i = 0; i < so->nb_opt; i++) { av_freep(&so->opt[i].specifier); + if (po->flags & OPT_FLAG_PERSTREAM) + stream_specifier_uninit(&so->opt[i].stream_spec); if (po->type == OPT_TYPE_STRING) av_freep(&so->opt[i].u.str); } @@ -164,22 +166,21 @@ const char *opt_match_per_type_str(const SpecifierOptList *sol, return NULL; } -static int opt_match_per_stream(void *logctx, enum OptionType type, - const SpecifierOptList *sol, - AVFormatContext *fc, AVStream *st) +static unsigned opt_match_per_stream(void *logctx, enum OptionType type, + const SpecifierOptList *sol, + AVFormatContext *fc, AVStream *st) { int matches = 0, match_idx = -1; av_assert0((type == sol->type) || !sol->nb_opt); for (int i = 0; i < sol->nb_opt; i++) { - const char *spec = sol->opt[i].specifier; - int ret = check_stream_specifier(fc, st, spec); - if (ret > 0) { + const StreamSpecifier *ss = &sol->opt[i].stream_spec; + + if (stream_specifier_match(ss, fc, st, logctx)) { match_idx = i; matches++; - } else if (ret < 0) - return ret; + } } if (matches > 1 && sol->opt_canon) { @@ -216,17 +217,12 @@ static int opt_match_per_stream(void *logctx, enum OptionType type, } #define OPT_MATCH_PER_STREAM(name, type, opt_type, m) \ -int opt_match_per_stream_ ## name(void *logctx, const SpecifierOptList *sol, \ - AVFormatContext *fc, AVStream *st, type *out) \ +void opt_match_per_stream_ ## name(void *logctx, const SpecifierOptList *sol, \ + AVFormatContext *fc, AVStream *st, type *out) \ { \ - int ret = opt_match_per_stream(logctx, opt_type, sol, fc, st); \ - \ - if (ret <= 0) \ - return ret; \ - \ - *out = sol->opt[ret - 1].u.m; \ - \ - return 0; \ + unsigned ret = opt_match_per_stream(logctx, opt_type, sol, fc, st); \ + if (ret > 0) \ + *out = sol->opt[ret - 1].u.m; \ } OPT_MATCH_PER_STREAM(str, const char *, OPT_TYPE_STRING, str); From patchwork Sat Aug 10 17:16:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anton Khirnov X-Patchwork-Id: 50966 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:612c:1ff2:b0:489:2eb3:e4c4 with SMTP id ks18csp527104vqb; Sat, 10 Aug 2024 10:17:07 -0700 (PDT) X-Forwarded-Encrypted: i=2; AJvYcCWovxfNjoWK89iVJU8xojyoHM4es/0AYk0ah09NGGZpHBo9iNmO/QjI9ol9/MgWbE1sMtsjgpE7pDP2mlR2OZknJ/crvWp+9hgv6g== X-Google-Smtp-Source: AGHT+IGLmzWx5ODqzWfKj/nZ5VNkhRFf3bRTMgJ7Wk18qMNR5JaxhXsAJZEMgdcz35spgUlLJO6m X-Received: by 2002:a05:6512:10d0:b0:52e:9b15:1c60 with SMTP id 2adb3069b0e04-530eea2f9a4mr3273211e87.48.1723310227235; Sat, 10 Aug 2024 10:17:07 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1723310227; cv=none; d=google.com; s=arc-20160816; b=s2i4yUxW6T9C05ZqhQePTi/rIzrxk7a6P6eJTZ595wUBpEVyle8XIkZRO4VanjsoCa HvlzB0N8Y39kNHV/ikee7GCE95F7F0fyUAkhyW4Q2F3dFUE3HfRVKkIhZBWDyOI4ALNg I62Xyyo+Fy4W2d58v/notFjUKtW2S5l/FUSjADV6GB2wvgFP70K/fVzB+KiCe9pbl/Ib lkkmYE8ZvGijraIBTbbSO6kQpfcsOmZmMxaGCzpLJzkfX4TlAx89xkYxifxlm7hDz8uH EQNDnBpXVEPH08uIPaWoRVAQH237UkIx3PPJj7mQ9PJ1MLym75cMaaaTz1RupfnW1f9H VhiQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:mime-version:references:in-reply-to:message-id :date:to:from:dkim-signature:delivered-to; bh=x+gNt7Y2TdEwEa3YR5tz2/yMzUTYRRV0e2V2bevBno0=; fh=YOA8vD9MJZuwZ71F/05pj6KdCjf6jQRmzLS+CATXUQk=; b=ZuvZYOb2VFu43kiOiO2v4nqFHoytGrJtXdRlnavdZYQjzzvCKhGHLz2djkd6fhOWB4 n2Me2vY525vjDTqj915AXpItIJxa9QEqO56+ksaWAo2pZDkxmSg1lwHQ9U4pzL/ZD4tF N9PKXQyOsxgKIz1Cao3tBT4ZZsCVP9YIuhobB3Kf4S+3oV3wo0K3LZmYrNqZd2vUEAql 9jY9WCBns/LwjAaSOocPWEPQ/I/WOx0LOHd2lOjyRXpSP1xJAFiG1EvqhQ5jJan7g8Dz U3FDJ0aXsGPnD5HsQJp4/dk/rzvVGiKm9VAEn1bvb1tHxoddgDJq2cmkmgIU9Fvmekht 6xog==; dara=google.com ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b=GgIFnkuU; 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 a640c23a62f3a-a80bb34257bsi108350566b.798.2024.08.10.10.17.06; Sat, 10 Aug 2024 10:17:07 -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; dkim=neutral (body hash did not verify) header.i=@khirnov.net header.s=mail header.b=GgIFnkuU; 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 B221B68DAC2; Sat, 10 Aug 2024 20:16:43 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail1.khirnov.net (quelana.khirnov.net [94.230.150.81]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 5288A68D93B for ; Sat, 10 Aug 2024 20:16:35 +0300 (EEST) Authentication-Results: mail1.khirnov.net; dkim=pass (2048-bit key; unprotected) header.d=khirnov.net header.i=@khirnov.net header.a=rsa-sha256 header.s=mail header.b=GgIFnkuU; dkim-atps=neutral Received: from localhost (mail1.khirnov.net [IPv6:::1]) by mail1.khirnov.net (Postfix) with ESMTP id CB670EE6 for ; Sat, 10 Aug 2024 19:16:34 +0200 (CEST) Received: from mail1.khirnov.net ([IPv6:::1]) by localhost (mail1.khirnov.net [IPv6:::1]) (amavis, port 10024) with ESMTP id drjh8gzgHM33 for ; Sat, 10 Aug 2024 19:16:34 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=khirnov.net; s=mail; t=1723310193; bh=TCsqoCtvwq1erUiJWG8srBWEA1jR5A5SZU0AT25RKms=; h=From:To:Subject:Date:In-Reply-To:References:From; b=GgIFnkuUZDJ2lhFG5Hkfcq9vLP/WfWg9XFRG9IsHoGmeeYsqP2pHVWy1yrjYwTCY6 128yg2NT99EK31lcMCBTqSPlH7m070edAaQu4Sn+9j/IuTKXcG8ZriZpVmuGMwo6Lg fL31b5+DXMpOP0ETFgAHpfTN0IDaXfMAIYMTA5F3zrTdxHwKp3M2TRGfTklnW5G5e3 YvwCCzFbKd024KpMXGsa7Q+itUS+b1QX9mfsMkXtjQ2yJScNCes81kBYjunSvhUpOz LyuzWrvBSaEqej9d9RSgEbMhvMmA/siauPgCZSa7D7G88/HmAOC4MibDFr7h/5Pdc+ +sRq5rYwFloew== Received: from libav.khirnov.net (libav.khirnov.net [IPv6:2a00:c500:561:201::7]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256 client-signature RSA-PSS (2048 bits) client-digest SHA256) (Client CN "libav.khirnov.net", Issuer "smtp.khirnov.net SMTP CA" (verified OK)) by mail1.khirnov.net (Postfix) with ESMTPS id 51F2B4DDB for ; Sat, 10 Aug 2024 19:16:33 +0200 (CEST) Received: from libav.khirnov.net (libav.khirnov.net [IPv6:::1]) by libav.khirnov.net (Postfix) with ESMTP id 7673A3A27B8 for ; Sat, 10 Aug 2024 19:16:26 +0200 (CEST) From: Anton Khirnov To: ffmpeg-devel@ffmpeg.org Date: Sat, 10 Aug 2024 19:16:21 +0200 Message-ID: <20240810171621.26757-6-anton@khirnov.net> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240810171621.26757-1-anton@khirnov.net> References: <20240810171621.26757-1-anton@khirnov.net> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 6/6] fftools/ffmpeg: switch -map parsing to new stream specifier API 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 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: a8sP+UoTaIcK Makes optional map handling less hacky, fixes combining optional maps with metadata matching on tags/values containing the '?' character/ Forward errors from stream specifier parsing, previously the code would ignore them. --- fftools/ffmpeg_opt.c | 54 ++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 17 deletions(-) diff --git a/fftools/ffmpeg_opt.c b/fftools/ffmpeg_opt.c index f6df6ae952..3cbf0795ac 100644 --- a/fftools/ffmpeg_opt.c +++ b/fftools/ffmpeg_opt.c @@ -427,22 +427,20 @@ static int opt_map(void *optctx, const char *opt, const char *arg) { OptionsContext *o = optctx; StreamMap *m = NULL; + StreamSpecifier ss; int i, negative = 0, file_idx, disabled = 0; - int ret; - char *map, *p; - char *allow_unused; + int ret, allow_unused = 0; + + memset(&ss, 0, sizeof(ss)); if (*arg == '-') { negative = 1; arg++; } - map = av_strdup(arg); - if (!map) - return AVERROR(ENOMEM); - if (map[0] == '[') { + if (arg[0] == '[') { /* this mapping refers to lavfi output */ - const char *c = map + 1; + const char *c = arg + 1; ret = GROW_ARRAY(o->stream_maps, o->nb_stream_maps); if (ret < 0) @@ -451,33 +449,55 @@ static int opt_map(void *optctx, const char *opt, const char *arg) m = &o->stream_maps[o->nb_stream_maps - 1]; m->linklabel = av_get_token(&c, "]"); if (!m->linklabel) { - av_log(NULL, AV_LOG_ERROR, "Invalid output link label: %s.\n", map); + av_log(NULL, AV_LOG_ERROR, "Invalid output link label: %s.\n", arg); ret = AVERROR(EINVAL); goto fail; } } else { - if (allow_unused = strchr(map, '?')) - *allow_unused = 0; - file_idx = strtol(map, &p, 0); + char *endptr; + + file_idx = strtol(arg, &endptr, 0); if (file_idx >= nb_input_files || file_idx < 0) { av_log(NULL, AV_LOG_FATAL, "Invalid input file index: %d.\n", file_idx); ret = AVERROR(EINVAL); goto fail; } + arg = endptr; + + ret = stream_specifier_parse(&ss, *arg == ':' ? arg + 1 : arg, 1, NULL); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "Invalid stream specifier: %s\n", arg); + goto fail; + } + + if (ss.remainder) { + if (!strcmp(ss.remainder, "?")) + allow_unused = 1; + else { + av_log(NULL, AV_LOG_ERROR, "Trailing garbage after stream specifier: %s\n", + ss.remainder); + ret = AVERROR(EINVAL); + goto fail; + } + } + if (negative) /* disable some already defined maps */ for (i = 0; i < o->nb_stream_maps; i++) { m = &o->stream_maps[i]; if (file_idx == m->file_index && - check_stream_specifier(input_files[m->file_index]->ctx, + stream_specifier_match(&ss, + input_files[m->file_index]->ctx, input_files[m->file_index]->ctx->streams[m->stream_index], - *p == ':' ? p + 1 : p) > 0) + NULL)) m->disabled = 1; } else for (i = 0; i < input_files[file_idx]->nb_streams; i++) { - if (check_stream_specifier(input_files[file_idx]->ctx, input_files[file_idx]->ctx->streams[i], - *p == ':' ? p + 1 : p) <= 0) + if (!stream_specifier_match(&ss, + input_files[file_idx]->ctx, + input_files[file_idx]->ctx->streams[i], + NULL)) continue; if (input_files[file_idx]->streams[i]->user_set_discard == AVDISCARD_ALL) { disabled = 1; @@ -511,7 +531,7 @@ static int opt_map(void *optctx, const char *opt, const char *arg) } ret = 0; fail: - av_freep(&map); + stream_specifier_uninit(&ss); return ret; }