From patchwork Mon Apr 11 15:36:53 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niklas Haas X-Patchwork-Id: 35258 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:671c:b0:7c:62c8:b2d1 with SMTP id q28csp1111783pzh; Mon, 11 Apr 2022 08:37:39 -0700 (PDT) X-Google-Smtp-Source: ABdhPJxEQwPj7nSPp8B5oUTATLwMIiO/aIYyVXw9nwum32ZGVI1/j46HnuD0mepq5g24GVuYYGbF X-Received: by 2002:a17:906:60c2:b0:6e7:681e:b4b7 with SMTP id f2-20020a17090660c200b006e7681eb4b7mr30306287ejk.130.1649691459739; Mon, 11 Apr 2022 08:37:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1649691459; cv=none; d=google.com; s=arc-20160816; b=VHaRMjVteX1KkmtuB7L7PW6OhvHm/sq67dxdHHGin/nvWgYNFvkSBag34jiQsp0eXt kfDcgMiEtw1/MS+sOkWmHU1CgV8nJeCJ3BJcIjasQXnfXGsqa8VvbeAVgdyoc3Tqf2FL g51JVjzK7t0DbMO1Y4n0oVQeltNpeoDf6TOxuajxyF1I+xlG3gL3ZvJ3u+JAVvpI7zXM /1cO189TuOMOLAqFbbOlDa5KbJWOQHhXxnV3As/qZJrt5oryJYHCSuZANfhGNBraFNFV GdWPR21Vj1hkEbdblv3s57HTUnmF17rjy59xju3t37PjkOPCZmA9i2Mc9R1IJy9AD1Dr 34iw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:cc: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=BjRgtpNvSuDzQqHzYtUInrIRO0AHGmvxugXTCIW82bE=; b=o8nZfv8lEm4JXbv8g+nw5Z81Qx91wwbU3/9j/ihw8NiaS84wU6s46yoWdrC22gaYAs F/m81ohIdKNp4j0I+k2H6yfxF6MSc0eSIi9YJMFqSjbC2RkzGcrZQQqJnlFljuX6hVcf ryjD4BM7D+FfJEZvmqc/XfASgS1PyyZxsdHZP240VTwgD6CUCwZcYkGdxu/g6mPYqN3b +MgetvdJzg0+NyUsX1UGytprFLeLnCeDxt2o4O9ZmQMKxUzPbBs6Hi+u9O7HPqEUVVP1 HRb0erigDR9egWOzm1pQCkAzqy4YbHTz0emflr9bnJXkDNj5Ux7t8CqX3e3JD5KbBRH8 G+mg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@haasn.xyz header.s=mail header.b=Q+j7GhAa; 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 l24-20020a1709062a9800b006df76385d13si6873459eje.435.2022.04.11.08.37.39; Mon, 11 Apr 2022 08:37:39 -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=@haasn.xyz header.s=mail header.b=Q+j7GhAa; 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 2934C68B2E2; Mon, 11 Apr 2022 18:37:11 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from haasn.dev (haasn.dev [78.46.187.166]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B43DE68B221 for ; Mon, 11 Apr 2022 18:37:02 +0300 (EEST) Received: from haasn.dev (unknown [10.30.0.2]) by haasn.dev (Postfix) with ESMTP id 780C049DF0; Mon, 11 Apr 2022 17:37:02 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=haasn.xyz; s=mail; t=1649691422; bh=2bxE5glkuMRcxbEEw6cbJn/WcqR8LijIczZgdWiB/u4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=Q+j7GhAa3pY7RviEL2n2uaQA4Wm4p3G/u7oTWZGcf6Sd7n3pMDzSbJvLpRuaRJiLu 5o4GgdV7+0FTgev4sherVEht+CSTwKMwkxXbo9VdfREC4djSzwpfKTvBX3C0KHiLld kG0+nwdCveIvKP2rGXz9InrJxyR6EsgEOUskmFhE= From: Niklas Haas To: ffmpeg-devel@ffmpeg.org Date: Mon, 11 Apr 2022 17:36:53 +0200 Message-Id: <20220411153654.116722-4-ffmpeg@haasn.xyz> X-Mailer: git-send-email 2.35.1 In-Reply-To: <20220411153654.116722-1-ffmpeg@haasn.xyz> References: <20220411153654.116722-1-ffmpeg@haasn.xyz> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH 4/5] lavfi: add vf_iccgen for generating ICC profiles X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Niklas Haas Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: TpCCZF3JXdyy From: Niklas Haas This filter is designed to specifically cover the task of generating ICC profiles (and attaching them to output frames) on demand. Other tasks, such as ICC profile loading/stripping, or ICC profile application, are better left to separate filters (or included into e.g. vf_setparams). Signed-off-by: Niklas Haas --- configure | 1 + doc/filters.texi | 21 +++++ libavfilter/Makefile | 1 + libavfilter/allfilters.c | 1 + libavfilter/vf_iccgen.c | 179 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 203 insertions(+) create mode 100644 libavfilter/vf_iccgen.c diff --git a/configure b/configure index 1a9c3dcd3c..d64ccf7181 100755 --- a/configure +++ b/configure @@ -3659,6 +3659,7 @@ gblur_vulkan_filter_deps="vulkan spirv_compiler" hflip_vulkan_filter_deps="vulkan spirv_compiler" histeq_filter_deps="gpl" hqdn3d_filter_deps="gpl" +iccgen_filter_deps="lcms2" interlace_filter_deps="gpl" kerndeint_filter_deps="gpl" ladspa_filter_deps="ladspa libdl" diff --git a/doc/filters.texi b/doc/filters.texi index 4e9b0e0111..9673858355 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -14304,6 +14304,27 @@ By default value is 0. The @code{hysteresis} filter also supports the @ref{framesync} options. +@section iccgen + +Generate ICC profiles and attach them to frames. + +This filter accepts the following options: + +@table @option +@item color_primaries +@item color_trc +Configure the colorspace that the ICC profile will be generated for. The +default value of @code{auto} infers the value from the input frame's metadata, +defaulting to BT.709/sRGB as appropriate. + +See the @ref{setparams} filter for a list of possible values, but note that +@code{unknown} are not valid values for this filter. + +@item force +If true, an ICC profile will be generated even if it would overwrite an +already existing ICC profile. Disabled by default. +@end table + @section identity Obtain the identity score between two input videos. diff --git a/libavfilter/Makefile b/libavfilter/Makefile index c4c946a988..8ffc53d751 100644 --- a/libavfilter/Makefile +++ b/libavfilter/Makefile @@ -320,6 +320,7 @@ OBJS-$(CONFIG_HWMAP_FILTER) += vf_hwmap.o OBJS-$(CONFIG_HWUPLOAD_CUDA_FILTER) += vf_hwupload_cuda.o OBJS-$(CONFIG_HWUPLOAD_FILTER) += vf_hwupload.o OBJS-$(CONFIG_HYSTERESIS_FILTER) += vf_hysteresis.o framesync.o +OBJS-$(CONFIG_ICCGEN_FILTER) += vf_iccgen.o fflcms2.o colorspace.o OBJS-$(CONFIG_IDENTITY_FILTER) += vf_identity.o OBJS-$(CONFIG_IDET_FILTER) += vf_idet.o OBJS-$(CONFIG_IL_FILTER) += vf_il.o diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c index 9fbaaacf47..d43f4b45b1 100644 --- a/libavfilter/allfilters.c +++ b/libavfilter/allfilters.c @@ -303,6 +303,7 @@ extern const AVFilter ff_vf_hwmap; extern const AVFilter ff_vf_hwupload; extern const AVFilter ff_vf_hwupload_cuda; extern const AVFilter ff_vf_hysteresis; +extern const AVFilter ff_vf_iccgen; extern const AVFilter ff_vf_identity; extern const AVFilter ff_vf_idet; extern const AVFilter ff_vf_il; diff --git a/libavfilter/vf_iccgen.c b/libavfilter/vf_iccgen.c new file mode 100644 index 0000000000..afc924e291 --- /dev/null +++ b/libavfilter/vf_iccgen.c @@ -0,0 +1,179 @@ +/* + * Copyright (c) 2022 Niklas Haas + * This file is part of FFmpeg. + * + * FFmpeg is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * FFmpeg is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with FFmpeg; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/** + * @file + * filter for generating ICC profiles + */ + +#include + +#include "libavutil/opt.h" +#include "libavutil/pixdesc.h" + +#include "avfilter.h" +#include "fflcms2.h" +#include "internal.h" + +typedef struct IccGenContext { + const AVClass *class; + FFIccContext icc; + /* options */ + int color_prim; + int color_trc; + int force; + /* (cached) generated ICC profile */ + cmsHPROFILE profile; + int profile_prim; + int profile_trc; +} IccGenContext; + +#define OFFSET(x) offsetof(IccGenContext, x) +#define VF AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM + +static const AVOption iccgen_options[] = { + {"color_primaries", "select color primaries", OFFSET(color_prim), AV_OPT_TYPE_INT, {.i64=0}, 0, AVCOL_PRI_NB-1, VF, "color_primaries"}, + {"auto", "infer based on frame", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, "color_primaries"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT709}, 0, 0, VF, "color_primaries"}, + {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470M}, 0, 0, VF, "color_primaries"}, + {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT470BG}, 0, 0, VF, "color_primaries"}, + {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE170M}, 0, 0, VF, "color_primaries"}, + {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE240M}, 0, 0, VF, "color_primaries"}, + {"film", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_FILM}, 0, 0, VF, "color_primaries"}, + {"bt2020", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_BT2020}, 0, 0, VF, "color_primaries"}, + {"smpte428", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE428}, 0, 0, VF, "color_primaries"}, + {"smpte431", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE431}, 0, 0, VF, "color_primaries"}, + {"smpte432", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_SMPTE432}, 0, 0, VF, "color_primaries"}, + {"jedec-p22", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_JEDEC_P22}, 0, 0, VF, "color_primaries"}, + {"ebu3213", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_PRI_EBU3213}, 0, 0, VF, "color_primaries"}, + {"color_trc", "select color transfer", OFFSET(color_trc), AV_OPT_TYPE_INT, {.i64=0}, 0, AVCOL_TRC_NB-1, VF, "color_trc"}, + {"auto", "infer based on frame", 0, AV_OPT_TYPE_CONST, {.i64=0}, 0, 0, VF, "color_trc"}, + {"bt709", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT709}, 0, 0, VF, "color_trc"}, + {"bt470m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA22}, 0, 0, VF, "color_trc"}, + {"bt470bg", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_GAMMA28}, 0, 0, VF, "color_trc"}, + {"smpte170m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE170M}, 0, 0, VF, "color_trc"}, + {"smpte240m", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE240M}, 0, 0, VF, "color_trc"}, + {"linear", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_LINEAR}, 0, 0, VF, "color_trc"}, + {"iec61966-2-4", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_4}, 0, 0, VF, "color_trc"}, + {"bt1361e", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT1361_ECG}, 0, 0, VF, "color_trc"}, + {"iec61966-2-1", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_IEC61966_2_1}, 0, 0, VF, "color_trc"}, + {"bt2020-10", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_10}, 0, 0, VF, "color_trc"}, + {"bt2020-12", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_BT2020_12}, 0, 0, VF, "color_trc"}, + {"smpte2084", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_SMPTE2084}, 0, 0, VF, "color_trc"}, + {"arib-std-b67", NULL, 0, AV_OPT_TYPE_CONST, {.i64=AVCOL_TRC_ARIB_STD_B67}, 0, 0, VF, "color_trc"}, + { "force", "overwrite existing ICC profile", OFFSET(force), AV_OPT_TYPE_BOOL, {.i64=0}, 0, 1, VF }, + { NULL } +}; + +AVFILTER_DEFINE_CLASS(iccgen); + +static av_cold void iccgen_uninit(AVFilterContext *avctx) +{ + IccGenContext *s = avctx->priv; + cmsCloseProfile(s->profile); + ff_icc_context_uninit(&s->icc); +} + +static av_cold int iccgen_init(AVFilterContext *avctx) +{ + IccGenContext *s = avctx->priv; + return ff_icc_context_init(&s->icc, avctx); +} + +static int iccgen_filter_frame(AVFilterLink *inlink, AVFrame *frame) +{ + AVFilterContext *avctx = inlink->dst; + IccGenContext *s = avctx->priv; + enum AVColorTransferCharacteristic trc; + enum AVColorPrimaries prim; + int ret; + + if (av_frame_get_side_data(frame, AV_FRAME_DATA_ICC_PROFILE)) { + if (s->force) { + av_frame_remove_side_data(frame, AV_FRAME_DATA_ICC_PROFILE); + } else { + return ff_filter_frame(inlink->dst->outputs[0], frame); + } + } + + trc = s->color_trc ? s->color_trc : frame->color_trc; + if (!trc || trc == AVCOL_TRC_UNSPECIFIED) { + const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format); + if (!desc) + return AVERROR_INVALIDDATA; + + if ((desc->flags & AV_PIX_FMT_FLAG_RGB) || frame->color_range == AVCOL_RANGE_JPEG) { + /* Default to sRGB for RGB or full-range content */ + trc = AVCOL_TRC_IEC61966_2_1; + } else { + /* Default to an ITU-R transfer depending on the bit-depth */ + trc = desc->comp[0].depth >= 12 ? AVCOL_TRC_BT2020_12 + : desc->comp[0].depth >= 10 ? AVCOL_TRC_BT2020_10 + : AVCOL_TRC_BT709; + } + } + + prim = s->color_prim ? s->color_prim : frame->color_primaries; + if (!prim || prim == AVCOL_PRI_UNSPECIFIED) { + /* Simply always default to sRGB/BT.709 primaries to avoid surprises */ + prim = AVCOL_PRI_BT709; + } + + if (s->profile && prim != s->profile_prim && trc != s->profile_trc) { + cmsCloseProfile(s->profile); + s->profile = NULL; + } + + if (!s->profile) { + if ((ret = ff_icc_profile_generate(&s->icc, prim, trc, &s->profile)) < 0) + return ret; + } + + if ((ret = ff_icc_profile_attach(&s->icc, s->profile, frame)) < 0) + return ret; + + return ff_filter_frame(inlink->dst->outputs[0], frame); +} + +static const AVFilterPad iccgen_inputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + .filter_frame = iccgen_filter_frame, + }, +}; + +static const AVFilterPad iccgen_outputs[] = { + { + .name = "default", + .type = AVMEDIA_TYPE_VIDEO, + }, +}; + +const AVFilter ff_vf_iccgen = { + .name = "iccgen", + .description = NULL_IF_CONFIG_SMALL("Generate and attach ICC profiles."), + .priv_size = sizeof(IccGenContext), + .priv_class = &iccgen_class, + .flags = AVFILTER_FLAG_METADATA_ONLY, + .init = &iccgen_init, + .uninit = &iccgen_uninit, + FILTER_INPUTS(iccgen_inputs), + FILTER_OUTPUTS(iccgen_outputs), +};