From patchwork Wed Jun 29 10:12:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Niklas Haas X-Patchwork-Id: 36523 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp206898pzh; Wed, 29 Jun 2022 03:13:19 -0700 (PDT) X-Google-Smtp-Source: AGRyM1ux/Mj242CgbsfDqSZ5DzI2MDbQTdZz1g4wZxl/N6okKkPsyWzIJmeUJzT98tahGy804VBn X-Received: by 2002:a05:6402:540c:b0:434:d965:f8a with SMTP id ev12-20020a056402540c00b00434d9650f8amr3251211edb.30.1656497599451; Wed, 29 Jun 2022 03:13:19 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656497599; cv=none; d=google.com; s=arc-20160816; b=BckI048bU/for/ljOKqF3cF6OXk718Vqzn+89+qzi0eEnO0NMvLtBppE1zfHNDhKZU bA9T9K9qBaKj1mZkzYojG0J8ZoCBirfDOtBQ7+2AMt9gdDSAKqf8vrIQtBhRyY4jtonr pKHDOQufpdrDzgQ0eKGEeyt/qbVc8yrpb428VBpoRFZLH+V0e26GRjiErFOE0VXS83e1 knvPggU3XK5Uo8pSkuV/NEy55QFfg/jUMxOeEqDoglLJ/kGCkbd3BTY0VWz/CzvaIQ1z aq71fmGxKaoU/DHX3mwjj1SJUGgQwD7dVbgW7SxPzNNbMfeKalruSP+9bd6aFz4AEtla 5bBg== 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=wBCAm88RcZVDV3Zq1UWacdOuhPt+Bq1XCE8skFfG0BQ=; b=1IHSg/Ben67HRwgrgS0APCvGLa6BjYZzEpXEd81aXvnYROpkCMVdhzI73FTbG3hCaR 65gsioYKPdCrlT9AqvtUq/ZYtK+urnznAOFEo6GIwX2pVrLOu26GATOqOb6oM/H3K4Gg RdFAyPkKsoEVSW+gfnLJt5L679Mi1k0qaMLdShO4Ay59oaZRqkRLKmI8f+kRKXq81eTC 0O5nVSQxvgCo3sH2ba5mWYSQBgizwCx+8UIkxNcpDbjRyLn0UW1QUY64P8TefjOJnl0j szwFxr5X5R+bz3cjYeAnPdDrbUnPhlA70iEIYlyTC+ngd34YliduwroGKYaSa3jHMZAn ondg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@haasn.xyz header.s=mail header.b=gjrSuuFu; 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 mp14-20020a1709071b0e00b00722a6137b13si546492ejc.239.2022.06.29.03.13.17; Wed, 29 Jun 2022 03:13:19 -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=gjrSuuFu; 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 A63DD68B6EC; Wed, 29 Jun 2022 13:13:04 +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 257A668B6A0 for ; Wed, 29 Jun 2022 13:12:58 +0300 (EEST) Received: from haasn.dev (unknown [10.30.0.2]) by haasn.dev (Postfix) with ESMTP id D44F44A7E7; Wed, 29 Jun 2022 12:12:57 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=haasn.xyz; s=mail; t=1656497577; bh=PXe5zVSU2cg7H6jwh5ea7b6MKeUgDxVKucjdtwrxYOI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=gjrSuuFu9xzt2I9tQxOyEe9lu8502LwnwKDp5rKxS2dOn3ETmL7A2p6ygAI0W2Ird dEVRoOGwNoLhYRWkRrmrt4BPV9QqAnDFF8SnIdf5Qa99KyumaT2nxjkTVHPkLgOF8u hvpsEc6vu2TO+SkhwYAddd5zyIJpm+R7vTREkK78= From: Niklas Haas To: ffmpeg-devel@ffmpeg.org Date: Wed, 29 Jun 2022 12:12:46 +0200 Message-Id: <20220629101251.13236-2-ffmpeg@haasn.xyz> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220629101251.13236-1-ffmpeg@haasn.xyz> References: <20220629101251.13236-1-ffmpeg@haasn.xyz> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 1/6] fflcms2: move to libavcodec 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: fp0T1iZQWn8v From: Niklas Haas We will need this helper inside libavcodec in the future, so move it there, leaving behind an #include to the raw source file in its old location in libvfilter. This approach is inspired by the handling of vulkan.c, and avoids us needing to expose any of it publicly (or semi-publicly) in e.g. libavutil, thus avoiding any ABI headaches. It's debatable whether the actual code belongs in libavcodec or libavfilter, but I decided to put it into libavcodec because it conceptually deals with encoding and decoding ICC profiles, and will be used to decode embedded ICC profiles in image files. Signed-off-by: Niklas Haas --- libavcodec/Makefile | 1 + libavcodec/fflcms2.c | 311 ++++++++++++++++++++++++++++++++++++++++++ libavcodec/fflcms2.h | 87 ++++++++++++ libavfilter/fflcms2.c | 294 +-------------------------------------- libavfilter/fflcms2.h | 65 +-------- 5 files changed, 401 insertions(+), 357 deletions(-) create mode 100644 libavcodec/fflcms2.c create mode 100644 libavcodec/fflcms2.h diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 050934101c..5c4f62c631 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -1235,6 +1235,7 @@ SKIPHEADERS-$(CONFIG_AMF) += amfenc.h SKIPHEADERS-$(CONFIG_D3D11VA) += d3d11va.h dxva2_internal.h SKIPHEADERS-$(CONFIG_DXVA2) += dxva2.h dxva2_internal.h SKIPHEADERS-$(CONFIG_JNI) += ffjni.h +SKIPHEADERS-$(CONFIG_LCMS2) += fflcms2.h SKIPHEADERS-$(CONFIG_LIBJXL) += libjxl.h SKIPHEADERS-$(CONFIG_LIBVPX) += libvpx.h SKIPHEADERS-$(CONFIG_LIBWEBP_ENCODER) += libwebpenc_common.h diff --git a/libavcodec/fflcms2.c b/libavcodec/fflcms2.c new file mode 100644 index 0000000000..fd370fb310 --- /dev/null +++ b/libavcodec/fflcms2.c @@ -0,0 +1,311 @@ +/* + * 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 + */ + +#include "libavutil/color_utils.h" +#include "libavutil/csp.h" + +#include "fflcms2.h" + +static void log_cb(cmsContext ctx, cmsUInt32Number error, const char *str) +{ + FFIccContext *s = cmsGetContextUserData(ctx); + av_log(s->avctx, AV_LOG_ERROR, "lcms2: [%"PRIu32"] %s\n", error, str); +} + +int ff_icc_context_init(FFIccContext *s, void *avctx) +{ + memset(s, 0, sizeof(*s)); + s->avctx = avctx; + s->ctx = cmsCreateContext(NULL, s); + if (!s->ctx) + return AVERROR(ENOMEM); + + cmsSetLogErrorHandlerTHR(s->ctx, log_cb); + return 0; +} + +void ff_icc_context_uninit(FFIccContext *s) +{ + for (int i = 0; i < FF_ARRAY_ELEMS(s->curves); i++) + cmsFreeToneCurve(s->curves[i]); + cmsDeleteContext(s->ctx); + memset(s, 0, sizeof(*s)); +} + +static int get_curve(FFIccContext *s, enum AVColorTransferCharacteristic trc, + cmsToneCurve **out_curve) +{ + if (trc >= AVCOL_TRC_NB) + return AVERROR_INVALIDDATA; + + if (s->curves[trc]) + goto done; + + switch (trc) { + case AVCOL_TRC_LINEAR: + s->curves[trc] = cmsBuildGamma(s->ctx, 1.0); + break; + case AVCOL_TRC_GAMMA22: + s->curves[trc] = cmsBuildGamma(s->ctx, 2.2); + break; + case AVCOL_TRC_GAMMA28: + s->curves[trc] = cmsBuildGamma(s->ctx, 2.8); + break; + case AVCOL_TRC_BT709: + case AVCOL_TRC_SMPTE170M: + case AVCOL_TRC_BT2020_10: + case AVCOL_TRC_BT2020_12: + s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) { + /* γ = */ 1/0.45, + /* a = */ 1/1.099296826809442, + /* b = */ 1 - 1/1.099296826809442, + /* c = */ 1/4.5, + /* d = */ 4.5 * 0.018053968510807, + }); + break; + case AVCOL_TRC_SMPTE240M: + s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) { + /* γ = */ 1/0.45, + /* a = */ 1/1.1115, + /* b = */ 1 - 1/1.1115, + /* c = */ 1/4.0, + /* d = */ 4.0 * 0.0228, + }); + break; + case AVCOL_TRC_LOG: + s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 8, (double[5]) { + /* a = */ 1.0, + /* b = */ 10.0, + /* c = */ 2.0, + /* d = */ -1.0, + /* e = */ 0.0 + }); + break; + case AVCOL_TRC_LOG_SQRT: + s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 8, (double[5]) { + /* a = */ 1.0, + /* b = */ 10.0, + /* c = */ 2.5, + /* d = */ -1.0, + /* e = */ 0.0 + }); + break; + case AVCOL_TRC_IEC61966_2_1: + s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) { + /* γ = */ 2.4, + /* a = */ 1/1.055, + /* b = */ 1 - 1/1.055, + /* c = */ 1/12.92, + /* d = */ 12.92 * 0.0031308, + }); + break; + case AVCOL_TRC_SMPTE428: + s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 2, (double[3]) { + /* γ = */ 2.6, + /* a = */ pow(52.37/48.0, 1/2.6), + /* b = */ 0.0 + }); + break; + + /* Can't be represented using the existing parametric tone curves. + * FIXME: use cmsBuildTabulatedToneCurveFloat instead */ + case AVCOL_TRC_IEC61966_2_4: + case AVCOL_TRC_BT1361_ECG: + case AVCOL_TRC_SMPTE2084: + case AVCOL_TRC_ARIB_STD_B67: + return AVERROR_PATCHWELCOME; + + default: + return AVERROR_INVALIDDATA; + } + + if (!s->curves[trc]) + return AVERROR(ENOMEM); + +done: + *out_curve = s->curves[trc]; + return 0; +} + +int ff_icc_profile_generate(FFIccContext *s, + enum AVColorPrimaries color_prim, + enum AVColorTransferCharacteristic color_trc, + cmsHPROFILE *out_profile) +{ + cmsToneCurve *tonecurve; + const AVColorPrimariesDesc *prim; + int ret; + + if (!(prim = av_csp_primaries_desc_from_id(color_prim))) + return AVERROR_INVALIDDATA; + if ((ret = get_curve(s, color_trc, &tonecurve)) < 0) + return ret; + + *out_profile = cmsCreateRGBProfileTHR(s->ctx, + &(cmsCIExyY) { av_q2d(prim->wp.x), av_q2d(prim->wp.y), 1.0 }, + &(cmsCIExyYTRIPLE) { + .Red = { av_q2d(prim->prim.r.x), av_q2d(prim->prim.r.y), 1.0 }, + .Green = { av_q2d(prim->prim.g.x), av_q2d(prim->prim.g.y), 1.0 }, + .Blue = { av_q2d(prim->prim.b.x), av_q2d(prim->prim.b.y), 1.0 }, + }, + (cmsToneCurve *[3]) { tonecurve, tonecurve, tonecurve } + ); + + return *out_profile == NULL ? AVERROR(ENOMEM) : 0; +} + +int ff_icc_profile_attach(FFIccContext *s, cmsHPROFILE profile, AVFrame *frame) +{ + cmsUInt32Number size; + AVBufferRef *buf; + + if (!cmsSaveProfileToMem(profile, NULL, &size)) + return AVERROR_EXTERNAL; + + buf = av_buffer_alloc(size); + if (!buf) + return AVERROR(ENOMEM); + + if (!cmsSaveProfileToMem(profile, buf->data, &size) || size != buf->size) { + av_buffer_unref(&buf); + return AVERROR_EXTERNAL; + } + + if (!av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_ICC_PROFILE, buf)) { + av_buffer_unref(&buf); + return AVERROR(ENOMEM); + } + + return 0; +} + +static av_always_inline void XYZ_xy(cmsCIEXYZ XYZ, AVCIExy *xy) +{ + double k = 1.0 / (XYZ.X + XYZ.Y + XYZ.Z); + xy->x = av_d2q(k * XYZ.X, 100000); + xy->y = av_d2q(k * XYZ.Y, 100000); +} + +int ff_icc_profile_read_primaries(FFIccContext *s, cmsHPROFILE profile, + AVColorPrimariesDesc *out_primaries) +{ + static const uint8_t testprimaries[4][3] = { + { 0xFF, 0, 0 }, /* red */ + { 0, 0xFF, 0 }, /* green */ + { 0, 0, 0xFF }, /* blue */ + { 0xFF, 0xFF, 0xFF }, /* white */ + }; + + AVWhitepointCoefficients *wp = &out_primaries->wp; + AVPrimaryCoefficients *prim = &out_primaries->prim; + cmsFloat64Number prev_adapt; + cmsHPROFILE xyz; + cmsHTRANSFORM tf; + cmsCIEXYZ dst[4]; + + xyz = cmsCreateXYZProfileTHR(s->ctx); + if (!xyz) + return AVERROR(ENOMEM); + + /* We need to use an unadapted observer to get the raw values */ + prev_adapt = cmsSetAdaptationStateTHR(s->ctx, 0.0); + tf = cmsCreateTransformTHR(s->ctx, profile, TYPE_RGB_8, xyz, TYPE_XYZ_DBL, + INTENT_ABSOLUTE_COLORIMETRIC, + /* Note: These flags mostly don't do anything + * anyway, but specify them regardless */ + cmsFLAGS_NOCACHE | + cmsFLAGS_NOOPTIMIZE | + cmsFLAGS_LOWRESPRECALC | + cmsFLAGS_GRIDPOINTS(2)); + cmsSetAdaptationStateTHR(s->ctx, prev_adapt); + cmsCloseProfile(xyz); + if (!tf) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid ICC profile (e.g. CMYK)\n"); + return AVERROR_INVALIDDATA; + } + + cmsDoTransform(tf, testprimaries, dst, 4); + cmsDeleteTransform(tf); + XYZ_xy(dst[0], &prim->r); + XYZ_xy(dst[1], &prim->g); + XYZ_xy(dst[2], &prim->b); + XYZ_xy(dst[3], wp); + return 0; +} + +int ff_icc_profile_detect_transfer(FFIccContext *s, cmsHPROFILE profile, + enum AVColorTransferCharacteristic *out_trc) +{ + /* 8-bit linear grayscale ramp */ + static const uint8_t testramp[16][3] = { + { 1, 1, 1}, /* avoid exact zero due to log100 etc. */ + { 17, 17, 17}, + { 34, 34, 34}, + { 51, 51, 51}, + { 68, 68, 68}, + { 85, 85, 85}, + { 02, 02, 02}, + {119, 119, 119}, + {136, 136, 136}, + {153, 153, 153}, + {170, 170, 170}, + {187, 187, 187}, + {204, 204, 204}, + {221, 221, 221}, + {238, 238, 238}, + {255, 255, 255}, + }; + + double dst[FF_ARRAY_ELEMS(testramp)]; + + for (enum AVColorTransferCharacteristic trc = 0; trc < AVCOL_TRC_NB; trc++) { + cmsToneCurve *tonecurve; + cmsHPROFILE ref; + cmsHTRANSFORM tf; + double delta = 0.0; + if (get_curve(s, trc, &tonecurve) < 0) + continue; + + ref = cmsCreateGrayProfileTHR(s->ctx, cmsD50_xyY(), tonecurve); + if (!ref) + return AVERROR(ENOMEM); + + tf = cmsCreateTransformTHR(s->ctx, profile, TYPE_RGB_8, ref, TYPE_GRAY_DBL, + INTENT_RELATIVE_COLORIMETRIC, + cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE); + cmsCloseProfile(ref); + if (!tf) { + av_log(s->avctx, AV_LOG_ERROR, "Invalid ICC profile (e.g. CMYK)\n"); + return AVERROR_INVALIDDATA; + } + + cmsDoTransform(tf, testramp, dst, FF_ARRAY_ELEMS(dst)); + cmsDeleteTransform(tf); + + for (int i = 0; i < FF_ARRAY_ELEMS(dst); i++) + delta += fabs(testramp[i][0] / 255.0 - dst[i]); + if (delta < 0.01) { + *out_trc = trc; + return 0; + } + } + + *out_trc = AVCOL_TRC_UNSPECIFIED; + return 0; +} diff --git a/libavcodec/fflcms2.h b/libavcodec/fflcms2.h new file mode 100644 index 0000000000..af63c9a13c --- /dev/null +++ b/libavcodec/fflcms2.h @@ -0,0 +1,87 @@ +/* + * 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 + * Various functions for dealing with ICC profiles + */ + +#ifndef AVCODEC_FFLCMS2_H +#define AVCODEC_FFLCMS2_H + +#include "libavutil/csp.h" +#include "libavutil/frame.h" +#include "libavutil/pixfmt.h" + +#include + +typedef struct FFIccContext { + void *avctx; + cmsContext ctx; + cmsToneCurve *curves[AVCOL_TRC_NB]; /* tone curve cache */ +} FFIccContext; + +/** + * Initializes an FFIccContext. This must be done prior to using it. + * + * Returns 0 on success, or a negative error code. + */ +int ff_icc_context_init(FFIccContext *s, void *avctx); +void ff_icc_context_uninit(FFIccContext *s); + +/** + * Generate an ICC profile for a given combination of color primaries and + * transfer function. Both values must be set to valid entries (not + * "undefined") for this function to work. + * + * Returns 0 on success, or a negative error code. + */ +int ff_icc_profile_generate(FFIccContext *s, + enum AVColorPrimaries color_prim, + enum AVColorTransferCharacteristic color_trc, + cmsHPROFILE *out_profile); + +/** + * Attach an ICC profile to a frame. Helper wrapper around cmsSaveProfileToMem + * and av_frame_new_side_data_from_buf. + * + * Returns 0 on success, or a negative error code. + */ +int ff_icc_profile_attach(FFIccContext *s, cmsHPROFILE profile, AVFrame *frame); + +/** + * Read the color primaries and white point coefficients encoded by an ICC + * profile, and return the raw values in `out_primaries`. + * + * Returns 0 on success, or a negative error code. + */ +int ff_icc_profile_read_primaries(FFIccContext *s, cmsHPROFILE profile, + AVColorPrimariesDesc *out_primaries); + +/** + * Attempt detecting the transfer characteristic that best approximates the + * transfer function encoded by an ICC profile. Sets `out_trc` to + * AVCOL_TRC_UNSPECIFIED if no clear match can be identified. + * + * Returns 0 on success (including no match), or a negative error code. + */ +int ff_icc_profile_detect_transfer(FFIccContext *s, cmsHPROFILE profile, + enum AVColorTransferCharacteristic *out_trc); + +#endif /* AVCODEC_FFLCMS2_H */ diff --git a/libavfilter/fflcms2.c b/libavfilter/fflcms2.c index fd370fb310..822462de87 100644 --- a/libavfilter/fflcms2.c +++ b/libavfilter/fflcms2.c @@ -1,5 +1,4 @@ /* - * Copyright (c) 2022 Niklas Haas * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -17,295 +16,4 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "libavutil/color_utils.h" -#include "libavutil/csp.h" - -#include "fflcms2.h" - -static void log_cb(cmsContext ctx, cmsUInt32Number error, const char *str) -{ - FFIccContext *s = cmsGetContextUserData(ctx); - av_log(s->avctx, AV_LOG_ERROR, "lcms2: [%"PRIu32"] %s\n", error, str); -} - -int ff_icc_context_init(FFIccContext *s, void *avctx) -{ - memset(s, 0, sizeof(*s)); - s->avctx = avctx; - s->ctx = cmsCreateContext(NULL, s); - if (!s->ctx) - return AVERROR(ENOMEM); - - cmsSetLogErrorHandlerTHR(s->ctx, log_cb); - return 0; -} - -void ff_icc_context_uninit(FFIccContext *s) -{ - for (int i = 0; i < FF_ARRAY_ELEMS(s->curves); i++) - cmsFreeToneCurve(s->curves[i]); - cmsDeleteContext(s->ctx); - memset(s, 0, sizeof(*s)); -} - -static int get_curve(FFIccContext *s, enum AVColorTransferCharacteristic trc, - cmsToneCurve **out_curve) -{ - if (trc >= AVCOL_TRC_NB) - return AVERROR_INVALIDDATA; - - if (s->curves[trc]) - goto done; - - switch (trc) { - case AVCOL_TRC_LINEAR: - s->curves[trc] = cmsBuildGamma(s->ctx, 1.0); - break; - case AVCOL_TRC_GAMMA22: - s->curves[trc] = cmsBuildGamma(s->ctx, 2.2); - break; - case AVCOL_TRC_GAMMA28: - s->curves[trc] = cmsBuildGamma(s->ctx, 2.8); - break; - case AVCOL_TRC_BT709: - case AVCOL_TRC_SMPTE170M: - case AVCOL_TRC_BT2020_10: - case AVCOL_TRC_BT2020_12: - s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) { - /* γ = */ 1/0.45, - /* a = */ 1/1.099296826809442, - /* b = */ 1 - 1/1.099296826809442, - /* c = */ 1/4.5, - /* d = */ 4.5 * 0.018053968510807, - }); - break; - case AVCOL_TRC_SMPTE240M: - s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) { - /* γ = */ 1/0.45, - /* a = */ 1/1.1115, - /* b = */ 1 - 1/1.1115, - /* c = */ 1/4.0, - /* d = */ 4.0 * 0.0228, - }); - break; - case AVCOL_TRC_LOG: - s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 8, (double[5]) { - /* a = */ 1.0, - /* b = */ 10.0, - /* c = */ 2.0, - /* d = */ -1.0, - /* e = */ 0.0 - }); - break; - case AVCOL_TRC_LOG_SQRT: - s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 8, (double[5]) { - /* a = */ 1.0, - /* b = */ 10.0, - /* c = */ 2.5, - /* d = */ -1.0, - /* e = */ 0.0 - }); - break; - case AVCOL_TRC_IEC61966_2_1: - s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 4, (double[5]) { - /* γ = */ 2.4, - /* a = */ 1/1.055, - /* b = */ 1 - 1/1.055, - /* c = */ 1/12.92, - /* d = */ 12.92 * 0.0031308, - }); - break; - case AVCOL_TRC_SMPTE428: - s->curves[trc] = cmsBuildParametricToneCurve(s->ctx, 2, (double[3]) { - /* γ = */ 2.6, - /* a = */ pow(52.37/48.0, 1/2.6), - /* b = */ 0.0 - }); - break; - - /* Can't be represented using the existing parametric tone curves. - * FIXME: use cmsBuildTabulatedToneCurveFloat instead */ - case AVCOL_TRC_IEC61966_2_4: - case AVCOL_TRC_BT1361_ECG: - case AVCOL_TRC_SMPTE2084: - case AVCOL_TRC_ARIB_STD_B67: - return AVERROR_PATCHWELCOME; - - default: - return AVERROR_INVALIDDATA; - } - - if (!s->curves[trc]) - return AVERROR(ENOMEM); - -done: - *out_curve = s->curves[trc]; - return 0; -} - -int ff_icc_profile_generate(FFIccContext *s, - enum AVColorPrimaries color_prim, - enum AVColorTransferCharacteristic color_trc, - cmsHPROFILE *out_profile) -{ - cmsToneCurve *tonecurve; - const AVColorPrimariesDesc *prim; - int ret; - - if (!(prim = av_csp_primaries_desc_from_id(color_prim))) - return AVERROR_INVALIDDATA; - if ((ret = get_curve(s, color_trc, &tonecurve)) < 0) - return ret; - - *out_profile = cmsCreateRGBProfileTHR(s->ctx, - &(cmsCIExyY) { av_q2d(prim->wp.x), av_q2d(prim->wp.y), 1.0 }, - &(cmsCIExyYTRIPLE) { - .Red = { av_q2d(prim->prim.r.x), av_q2d(prim->prim.r.y), 1.0 }, - .Green = { av_q2d(prim->prim.g.x), av_q2d(prim->prim.g.y), 1.0 }, - .Blue = { av_q2d(prim->prim.b.x), av_q2d(prim->prim.b.y), 1.0 }, - }, - (cmsToneCurve *[3]) { tonecurve, tonecurve, tonecurve } - ); - - return *out_profile == NULL ? AVERROR(ENOMEM) : 0; -} - -int ff_icc_profile_attach(FFIccContext *s, cmsHPROFILE profile, AVFrame *frame) -{ - cmsUInt32Number size; - AVBufferRef *buf; - - if (!cmsSaveProfileToMem(profile, NULL, &size)) - return AVERROR_EXTERNAL; - - buf = av_buffer_alloc(size); - if (!buf) - return AVERROR(ENOMEM); - - if (!cmsSaveProfileToMem(profile, buf->data, &size) || size != buf->size) { - av_buffer_unref(&buf); - return AVERROR_EXTERNAL; - } - - if (!av_frame_new_side_data_from_buf(frame, AV_FRAME_DATA_ICC_PROFILE, buf)) { - av_buffer_unref(&buf); - return AVERROR(ENOMEM); - } - - return 0; -} - -static av_always_inline void XYZ_xy(cmsCIEXYZ XYZ, AVCIExy *xy) -{ - double k = 1.0 / (XYZ.X + XYZ.Y + XYZ.Z); - xy->x = av_d2q(k * XYZ.X, 100000); - xy->y = av_d2q(k * XYZ.Y, 100000); -} - -int ff_icc_profile_read_primaries(FFIccContext *s, cmsHPROFILE profile, - AVColorPrimariesDesc *out_primaries) -{ - static const uint8_t testprimaries[4][3] = { - { 0xFF, 0, 0 }, /* red */ - { 0, 0xFF, 0 }, /* green */ - { 0, 0, 0xFF }, /* blue */ - { 0xFF, 0xFF, 0xFF }, /* white */ - }; - - AVWhitepointCoefficients *wp = &out_primaries->wp; - AVPrimaryCoefficients *prim = &out_primaries->prim; - cmsFloat64Number prev_adapt; - cmsHPROFILE xyz; - cmsHTRANSFORM tf; - cmsCIEXYZ dst[4]; - - xyz = cmsCreateXYZProfileTHR(s->ctx); - if (!xyz) - return AVERROR(ENOMEM); - - /* We need to use an unadapted observer to get the raw values */ - prev_adapt = cmsSetAdaptationStateTHR(s->ctx, 0.0); - tf = cmsCreateTransformTHR(s->ctx, profile, TYPE_RGB_8, xyz, TYPE_XYZ_DBL, - INTENT_ABSOLUTE_COLORIMETRIC, - /* Note: These flags mostly don't do anything - * anyway, but specify them regardless */ - cmsFLAGS_NOCACHE | - cmsFLAGS_NOOPTIMIZE | - cmsFLAGS_LOWRESPRECALC | - cmsFLAGS_GRIDPOINTS(2)); - cmsSetAdaptationStateTHR(s->ctx, prev_adapt); - cmsCloseProfile(xyz); - if (!tf) { - av_log(s->avctx, AV_LOG_ERROR, "Invalid ICC profile (e.g. CMYK)\n"); - return AVERROR_INVALIDDATA; - } - - cmsDoTransform(tf, testprimaries, dst, 4); - cmsDeleteTransform(tf); - XYZ_xy(dst[0], &prim->r); - XYZ_xy(dst[1], &prim->g); - XYZ_xy(dst[2], &prim->b); - XYZ_xy(dst[3], wp); - return 0; -} - -int ff_icc_profile_detect_transfer(FFIccContext *s, cmsHPROFILE profile, - enum AVColorTransferCharacteristic *out_trc) -{ - /* 8-bit linear grayscale ramp */ - static const uint8_t testramp[16][3] = { - { 1, 1, 1}, /* avoid exact zero due to log100 etc. */ - { 17, 17, 17}, - { 34, 34, 34}, - { 51, 51, 51}, - { 68, 68, 68}, - { 85, 85, 85}, - { 02, 02, 02}, - {119, 119, 119}, - {136, 136, 136}, - {153, 153, 153}, - {170, 170, 170}, - {187, 187, 187}, - {204, 204, 204}, - {221, 221, 221}, - {238, 238, 238}, - {255, 255, 255}, - }; - - double dst[FF_ARRAY_ELEMS(testramp)]; - - for (enum AVColorTransferCharacteristic trc = 0; trc < AVCOL_TRC_NB; trc++) { - cmsToneCurve *tonecurve; - cmsHPROFILE ref; - cmsHTRANSFORM tf; - double delta = 0.0; - if (get_curve(s, trc, &tonecurve) < 0) - continue; - - ref = cmsCreateGrayProfileTHR(s->ctx, cmsD50_xyY(), tonecurve); - if (!ref) - return AVERROR(ENOMEM); - - tf = cmsCreateTransformTHR(s->ctx, profile, TYPE_RGB_8, ref, TYPE_GRAY_DBL, - INTENT_RELATIVE_COLORIMETRIC, - cmsFLAGS_NOCACHE | cmsFLAGS_NOOPTIMIZE); - cmsCloseProfile(ref); - if (!tf) { - av_log(s->avctx, AV_LOG_ERROR, "Invalid ICC profile (e.g. CMYK)\n"); - return AVERROR_INVALIDDATA; - } - - cmsDoTransform(tf, testramp, dst, FF_ARRAY_ELEMS(dst)); - cmsDeleteTransform(tf); - - for (int i = 0; i < FF_ARRAY_ELEMS(dst); i++) - delta += fabs(testramp[i][0] / 255.0 - dst[i]); - if (delta < 0.01) { - *out_trc = trc; - return 0; - } - } - - *out_trc = AVCOL_TRC_UNSPECIFIED; - return 0; -} +#include "libavcodec/fflcms2.c" diff --git a/libavfilter/fflcms2.h b/libavfilter/fflcms2.h index 0d238c679f..1ac29e357b 100644 --- a/libavfilter/fflcms2.h +++ b/libavfilter/fflcms2.h @@ -1,5 +1,4 @@ /* - * Copyright (c) 2022 Niklas Haas * This file is part of FFmpeg. * * FFmpeg is free software; you can redistribute it and/or @@ -17,71 +16,9 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -/** - * @file - * Various functions for dealing with ICC profiles - */ - #ifndef AVFILTER_FFLCMS2_H #define AVFILTER_FFLCMS2_H -#include "libavutil/csp.h" -#include "libavutil/frame.h" -#include "libavutil/pixfmt.h" - -#include - -typedef struct FFIccContext { - void *avctx; - cmsContext ctx; - cmsToneCurve *curves[AVCOL_TRC_NB]; /* tone curve cache */ -} FFIccContext; - -/** - * Initializes an FFIccContext. This must be done prior to using it. - * - * Returns 0 on success, or a negative error code. - */ -int ff_icc_context_init(FFIccContext *s, void *avctx); -void ff_icc_context_uninit(FFIccContext *s); - -/** - * Generate an ICC profile for a given combination of color primaries and - * transfer function. Both values must be set to valid entries (not - * "undefined") for this function to work. - * - * Returns 0 on success, or a negative error code. - */ -int ff_icc_profile_generate(FFIccContext *s, - enum AVColorPrimaries color_prim, - enum AVColorTransferCharacteristic color_trc, - cmsHPROFILE *out_profile); - -/** - * Attach an ICC profile to a frame. Helper wrapper around cmsSaveProfileToMem - * and av_frame_new_side_data_from_buf. - * - * Returns 0 on success, or a negative error code. - */ -int ff_icc_profile_attach(FFIccContext *s, cmsHPROFILE profile, AVFrame *frame); - -/** - * Read the color primaries and white point coefficients encoded by an ICC - * profile, and return the raw values in `out_primaries`. - * - * Returns 0 on success, or a negative error code. - */ -int ff_icc_profile_read_primaries(FFIccContext *s, cmsHPROFILE profile, - AVColorPrimariesDesc *out_primaries); - -/** - * Attempt detecting the transfer characteristic that best approximates the - * transfer function encoded by an ICC profile. Sets `out_trc` to - * AVCOL_TRC_UNSPECIFIED if no clear match can be identified. - * - * Returns 0 on success (including no match), or a negative error code. - */ -int ff_icc_profile_detect_transfer(FFIccContext *s, cmsHPROFILE profile, - enum AVColorTransferCharacteristic *out_trc); +#include "libavcodec/fflcms2.h" #endif /* AVFILTER_FFLCMS2_H */ From patchwork Wed Jun 29 10:12:47 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niklas Haas X-Patchwork-Id: 36524 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp206984pzh; Wed, 29 Jun 2022 03:13:28 -0700 (PDT) X-Google-Smtp-Source: AGRyM1uMBi3fZ5MO5TqkIvTCSYeMau1lHRIKyTJu9Nf9gIRWRjRbdtdH1aEHK67++4+MuZ3b59sT X-Received: by 2002:a17:907:7781:b0:6fe:4398:47b3 with SMTP id ky1-20020a170907778100b006fe439847b3mr2513300ejc.513.1656497608471; Wed, 29 Jun 2022 03:13:28 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656497608; cv=none; d=google.com; s=arc-20160816; b=h8sXMqf3w9GXA0KtLeyGe8QtJAP6Sj32cG4lNfBQwo88k9u0lCkV4EyC7YAEMRgO4i xe2wRaxWoDz3MXM0Fax6V3xrQ8S5mc29OPvH8i1xrAmu23oZSodXwwJiVGuSijy/y+d4 jVDRXlccGw1QkKuZ8dhYXlFXVRQRGSo8FnaHyhmEm2t+gd+9q0pmVwvHtEHXN4gwJlEi 4PrSYco6Y0jU+K85bs/W9JfR0R4b5wMDVT4VH3wknknPsUaRX0b5+loj0O501UN+dV2K f315NugCww1FirAcgjXuzhxPChr5NlkPlozuqf1yQ23CA0XeHUPjrtHf7kIylb/+2pRa OJmg== 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=xq3ufitgGDztMIkLAWAAcwP0wO7qfoTt22DuTXJig+k=; b=aCInlGDUsw/ySjpzqwooIMcHCZVBdECi20V1ECyVBDYEuLrhbovLyERb2A83XJvh4f POyYiGoXh0zKGhnyUoU9ejdUVxHdgwcPkNsuJRdDaCA6xUkjFEKZFvTxMtIJabJq1sbS 9nupekbiE2x1ewFpBSpmus9Ir99iXlYwsKpohGpWfuRnvm/+GIqYpfDc4qmmW2h9hcTL 71pE9kG2Ol/lUPZok+jEI9/0bWcoSEjmOuqrgkcbtI427fdedSrPXhd5qsztOwmClciW +ce8MDbGptV6g/4BSf+D3c9Oc6WlsfG4xKS2QCLh/5pbKe3ZvqqdypgMgN93ocPGrQsp lh5Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@haasn.xyz header.s=mail header.b=I3f93cOP; 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 a12-20020a056402168c00b00435728cd12csi2439243edv.301.2022.06.29.03.13.28; Wed, 29 Jun 2022 03:13:28 -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=I3f93cOP; 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 C8AA568B701; Wed, 29 Jun 2022 13:13:05 +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 5AF5568B6A0 for ; Wed, 29 Jun 2022 13:12:58 +0300 (EEST) Received: from haasn.dev (unknown [10.30.0.2]) by haasn.dev (Postfix) with ESMTP id 1662D4A7E8; Wed, 29 Jun 2022 12:12:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=haasn.xyz; s=mail; t=1656497578; bh=EveejBhGv5Lypv4WhxZ5suOEH7ueRJkdrpN31C5uOp8=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=I3f93cOPU/s7GBV2sA5ml8RhBce05Pfie6wSS6N3O3iiuGORUzzHHZa6IHCQyH/9A U0/weGnwSuL6QdyacJNw9/W6OQf0pbudMet5ccVTCzwHc5t98PT2tAP1nJrPEzIpTD btVOY1fdSnxWQEjS4DDxLbdZYstWRMBUUbmyoUKI= From: Niklas Haas To: ffmpeg-devel@ffmpeg.org Date: Wed, 29 Jun 2022 12:12:47 +0200 Message-Id: <20220629101251.13236-3-ffmpeg@haasn.xyz> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220629101251.13236-1-ffmpeg@haasn.xyz> References: <20220629101251.13236-1-ffmpeg@haasn.xyz> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 2/6] avcodec/codec_internal: add cap for ICC profile support 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: AcaGO53ipSnf From: Niklas Haas Codecs that can read/write ICC profiles deserve a special capability so the common logic in encode.c/decode.c can decide whether or not there needs to be any special handling for ICC profiles. The motivation here is to be able to use it to decide whether or not an ICC profile needs to be generated in the encode path, but it might as well get added to decoders as well for purely informative reasons. It's not entirely clear to me whether the "thp" and "smvjpeg" variants of "mjpeg" should have this capability set or not, given that the code technically supports it but I somehow doubt these files may contain them. In either case, this cap is purely informative for decoders so it doesn't matter too much either way. It's also not entirely clear whether the "amv" encoder should signal ICC profile support, but again erring on the side of caution, we probably *shouldn't* be generating (and encoding!) ICC profiles for this type of media file. Signed-off-by: Niklas Haas --- libavcodec/codec_internal.h | 4 ++++ libavcodec/libjxldec.c | 3 ++- libavcodec/libjxlenc.c | 3 ++- libavcodec/mjpegdec.c | 3 ++- libavcodec/mjpegenc.c | 3 ++- libavcodec/pngdec.c | 5 +++-- libavcodec/pngenc.c | 4 ++-- libavcodec/tiff.c | 3 ++- libavcodec/webp.c | 2 +- 9 files changed, 20 insertions(+), 10 deletions(-) diff --git a/libavcodec/codec_internal.h b/libavcodec/codec_internal.h index 5df286ce52..b4e3813353 100644 --- a/libavcodec/codec_internal.h +++ b/libavcodec/codec_internal.h @@ -73,6 +73,10 @@ * internal logic derive them from AVCodecInternal.last_pkt_props. */ #define FF_CODEC_CAP_SETS_FRAME_PROPS (1 << 8) +/** + * Codec supports embedded ICC profiles (AV_FRAME_DATA_ICC_PROFILE). + */ +#define FF_CODEC_CAP_ICC_PROFILES (1 << 9) /** * FFCodec.codec_tags termination value diff --git a/libavcodec/libjxldec.c b/libavcodec/libjxldec.c index d516d3b0ac..7c78627037 100644 --- a/libavcodec/libjxldec.c +++ b/libavcodec/libjxldec.c @@ -455,6 +455,7 @@ const FFCodec ff_libjxl_decoder = { FF_CODEC_DECODE_CB(libjxl_decode_frame), .close = libjxl_decode_close, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_OTHER_THREADS, - .caps_internal = FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP, + .caps_internal = FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP | + FF_CODEC_CAP_ICC_PROFILES, .p.wrapper_name = "libjxl", }; diff --git a/libavcodec/libjxlenc.c b/libavcodec/libjxlenc.c index 6a948cc3ae..3a533db8a1 100644 --- a/libavcodec/libjxlenc.c +++ b/libavcodec/libjxlenc.c @@ -463,7 +463,8 @@ const FFCodec ff_libjxl_encoder = { FF_CODEC_ENCODE_CB(libjxl_encode_frame), .close = libjxl_encode_close, .p.capabilities = AV_CODEC_CAP_OTHER_THREADS, - .caps_internal = FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP, + .caps_internal = FF_CODEC_CAP_AUTO_THREADS | FF_CODEC_CAP_INIT_CLEANUP | + FF_CODEC_CAP_ICC_PROFILES, .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGB24, AV_PIX_FMT_RGBA, AV_PIX_FMT_RGB48, AV_PIX_FMT_RGBA64, diff --git a/libavcodec/mjpegdec.c b/libavcodec/mjpegdec.c index 32874a5a19..8e5878e9ee 100644 --- a/libavcodec/mjpegdec.c +++ b/libavcodec/mjpegdec.c @@ -3027,7 +3027,8 @@ const FFCodec ff_mjpeg_decoder = { .p.priv_class = &mjpegdec_class, .p.profiles = NULL_IF_CONFIG_SMALL(ff_mjpeg_profiles), .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP | - FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | FF_CODEC_CAP_SETS_PKT_DTS, + FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | FF_CODEC_CAP_SETS_PKT_DTS | + FF_CODEC_CAP_ICC_PROFILES, .hw_configs = (const AVCodecHWConfigInternal *const []) { #if CONFIG_MJPEG_NVDEC_HWACCEL HWACCEL_NVDEC(mjpeg), diff --git a/libavcodec/mjpegenc.c b/libavcodec/mjpegenc.c index 27217441a3..8787528b3e 100644 --- a/libavcodec/mjpegenc.c +++ b/libavcodec/mjpegenc.c @@ -652,7 +652,8 @@ const FFCodec ff_mjpeg_encoder = { FF_CODEC_ENCODE_CB(ff_mpv_encode_picture), .close = mjpeg_encode_close, .p.capabilities = AV_CODEC_CAP_SLICE_THREADS | AV_CODEC_CAP_FRAME_THREADS, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP | + FF_CODEC_CAP_ICC_PROFILES, .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUV422P, AV_PIX_FMT_YUV444P, diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c index 6b44af59f2..5cd8a176fd 100644 --- a/libavcodec/pngdec.c +++ b/libavcodec/pngdec.c @@ -1727,7 +1727,7 @@ const FFCodec ff_apng_decoder = { .update_thread_context = ONLY_IF_THREADS_ENABLED(update_thread_context), .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS /*| AV_CODEC_CAP_DRAW_HORIZ_BAND*/, .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP | - FF_CODEC_CAP_ALLOCATE_PROGRESS, + FF_CODEC_CAP_ALLOCATE_PROGRESS | FF_CODEC_CAP_ICC_PROFILES, }; #endif @@ -1744,6 +1744,7 @@ const FFCodec ff_png_decoder = { .update_thread_context = ONLY_IF_THREADS_ENABLED(update_thread_context), .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS /*| AV_CODEC_CAP_DRAW_HORIZ_BAND*/, .caps_internal = FF_CODEC_CAP_SKIP_FRAME_FILL_PARAM | FF_CODEC_CAP_INIT_THREADSAFE | - FF_CODEC_CAP_ALLOCATE_PROGRESS | FF_CODEC_CAP_INIT_CLEANUP, + FF_CODEC_CAP_ALLOCATE_PROGRESS | FF_CODEC_CAP_INIT_CLEANUP | + FF_CODEC_CAP_ICC_PROFILES, }; #endif diff --git a/libavcodec/pngenc.c b/libavcodec/pngenc.c index d79b4e3895..b23ff84787 100644 --- a/libavcodec/pngenc.c +++ b/libavcodec/pngenc.c @@ -1210,7 +1210,7 @@ const FFCodec ff_png_encoder = { AV_PIX_FMT_MONOBLACK, AV_PIX_FMT_NONE }, .p.priv_class = &pngenc_class, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_ICC_PROFILES, }; const FFCodec ff_apng_encoder = { @@ -1232,5 +1232,5 @@ const FFCodec ff_apng_encoder = { AV_PIX_FMT_NONE }, .p.priv_class = &pngenc_class, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_ICC_PROFILES, }; diff --git a/libavcodec/tiff.c b/libavcodec/tiff.c index e4a5d3c537..c369c12f71 100644 --- a/libavcodec/tiff.c +++ b/libavcodec/tiff.c @@ -2187,6 +2187,7 @@ const FFCodec ff_tiff_decoder = { .close = tiff_end, FF_CODEC_DECODE_CB(decode_frame), .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_INIT_CLEANUP | + FF_CODEC_CAP_ICC_PROFILES, .p.priv_class = &tiff_decoder_class, }; diff --git a/libavcodec/webp.c b/libavcodec/webp.c index 1b5e943a6e..2b1242615d 100644 --- a/libavcodec/webp.c +++ b/libavcodec/webp.c @@ -1565,5 +1565,5 @@ const FFCodec ff_webp_decoder = { FF_CODEC_DECODE_CB(webp_decode_frame), .close = webp_decode_close, .p.capabilities = AV_CODEC_CAP_DR1 | AV_CODEC_CAP_FRAME_THREADS, - .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, + .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE | FF_CODEC_CAP_ICC_PROFILES, }; From patchwork Wed Jun 29 10:12:48 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niklas Haas X-Patchwork-Id: 36525 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp207070pzh; Wed, 29 Jun 2022 03:13:39 -0700 (PDT) X-Google-Smtp-Source: AGRyM1v+0gl05wNBFMNK9rDet014POZ0XKKNGWLGQ1eFdV+AgcMLUw4OM6QdcScc9Syv2Va6rBnI X-Received: by 2002:a17:907:7603:b0:72a:4271:5cef with SMTP id jx3-20020a170907760300b0072a42715cefmr626563ejc.454.1656497619214; Wed, 29 Jun 2022 03:13:39 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656497619; cv=none; d=google.com; s=arc-20160816; b=W1XRY/BBkq6NpMQ4y+hY8a6cuiQYYbN9y0PYwLBJ2NU8aSdfTpo2XD0oseGO0Q6/DV mxi/Fm7TFxJWU45sR2xjoCCdRV8UWdUbaaMEhLeZ1tm7Y9A+Esjt/pGI955oSAke+NLS CpiWTgsAR2Q4YD9WyPajY45VdNy9/H713k2fAzOseeCXVJOpfVJkVL2IhRmg8IRhLy/A PQOQEy6ZStZW5nz0QJJ3kgx5/ZMpMyAWCgafItmHXfzILoTi80uV8g3kplB67lcWOGAC t2cz/npMRr7hLXEyLHkGzQzqPDKRx25NoRX+zcVmKUBOX1J+BS1L+yOsNsH83I6nQZdR 0AyA== 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=rNH8eupX5ug+TgOcqDUBvvZG25xnJu+sAMppxPpOo9M=; b=hnziGGk5j1KZPL+xwTag8nJ2s84yVylnIiuUg7EIS7kMmc5v2xnTMew6W6nBK2GLLy QferRFV2xYE5vQN1LUBGBuBkn5kfV7kZ6+p/u0i9CVXFI/xpczAf24HJSQdA+wMEOBv5 SXwME0t1lrEzw4sxSdeJMYJnWApGLNa3ofXumjzxpqkuuBho2R/wdBw6S3niD6Jqs9/3 aPFPWqPnO9ltD8WDfuQEIw+JEaYxyB1J3rTLkbTaf3QcFaS34TqmjQHDGE9tHH87M+Pm J+60pHENNXybdKOOjBg1PdGG8gGqcoFlbnTTasU/kKmeivx+1zOLMiXd35g8oZpYcr4l Fm1g== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@haasn.xyz header.s=mail header.b="P+Ws/+1l"; 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 gt43-20020a1709072dab00b007269f2170e3si11122768ejc.754.2022.06.29.03.13.38; Wed, 29 Jun 2022 03:13: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="P+Ws/+1l"; 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 D519868B6B0; Wed, 29 Jun 2022 13:13:06 +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 9202C68B6A0 for ; Wed, 29 Jun 2022 13:12:58 +0300 (EEST) Received: from haasn.dev (unknown [10.30.0.2]) by haasn.dev (Postfix) with ESMTP id 4D89B4A7EB; Wed, 29 Jun 2022 12:12:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=haasn.xyz; s=mail; t=1656497578; bh=piXmwqSCDE0/pOS0s5u5bHu09a4hNKR8T94bx4HEGgU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=P+Ws/+1l69BUqz1AUJgDuakAntpKaHJ8tzUKo4WzBxqGqS/oC7XIp5HWPtheh7lqQ WljwO8J1xAOsl71lxWG9A5zGFOIEvp1k/3OABtYujzmSc3k+VWEKfmBZreBPSSZ/0J RHsHaQaYvRqf2yv1FWdQhCABFyfofWdLwS9qyJPc= From: Niklas Haas To: ffmpeg-devel@ffmpeg.org Date: Wed, 29 Jun 2022 12:12:48 +0200 Message-Id: <20220629101251.13236-4-ffmpeg@haasn.xyz> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220629101251.13236-1-ffmpeg@haasn.xyz> References: <20220629101251.13236-1-ffmpeg@haasn.xyz> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 3/6] avcodec: add API for automatic handling of 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: nUzmSy6raN83 From: Niklas Haas This functionally already exists, but as pointed out in #9672 and #9673, requiring users to manually include filters is clumsy, error-prone and hard to use together with tools like ffplay. To streamline ICC profile support, add a new AVCodecContext option to globally enable reading and writing ICC profiles, automatically, for all appropriate media types. Note that this commit only includes the new API. The implementation is split off to separate commits for readability. Signed-off-by: Niklas Haas --- doc/APIchanges | 4 ++++ libavcodec/avcodec.h | 10 ++++++++++ libavcodec/version.h | 2 +- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/doc/APIchanges b/doc/APIchanges index 20b944933a..b8acf86352 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -14,6 +14,10 @@ libavutil: 2021-04-27 API changes, most recent first: +2022-06-28 - xxxxxxxxx - lavc 59.35.100 - avcodec.h + Add the icc_profiles option to AVCodecContext, to enable automatic reading + and writing of embedded ICC profiles in image files. + 2022-06-12 - xxxxxxxxxx - lavf 59.25.100 - avio.h Add avio_vprintf(), similar to avio_printf() but allow to use it from within a function taking a variable argument list as input. diff --git a/libavcodec/avcodec.h b/libavcodec/avcodec.h index 4dae23d06e..8ee9be22d0 100644 --- a/libavcodec/avcodec.h +++ b/libavcodec/avcodec.h @@ -979,6 +979,16 @@ typedef struct AVCodecContext { */ enum AVChromaLocation chroma_sample_location; + /** + * Whether to decode/encode ICC profiles. If set, libavcodec will + * generate/parse ICC profiles as appropriate for the type of file. No + * effect on codecs which cannot contain embedded ICC profiles, or when + * compiled without support for lcms2. + * - encoding: Set by user + * - decoding: Set by user + */ + int icc_profiles; + /** * Number of slices. * Indicates number of picture subdivisions. Used for parallelized diff --git a/libavcodec/version.h b/libavcodec/version.h index 0ef6c991f3..1008fead27 100644 --- a/libavcodec/version.h +++ b/libavcodec/version.h @@ -29,7 +29,7 @@ #include "version_major.h" -#define LIBAVCODEC_VERSION_MINOR 34 +#define LIBAVCODEC_VERSION_MINOR 35 #define LIBAVCODEC_VERSION_MICRO 100 #define LIBAVCODEC_VERSION_INT AV_VERSION_INT(LIBAVCODEC_VERSION_MAJOR, \ From patchwork Wed Jun 29 10:12:49 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niklas Haas X-Patchwork-Id: 36526 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp207126pzh; Wed, 29 Jun 2022 03:13:49 -0700 (PDT) X-Google-Smtp-Source: AGRyM1u9NE1NioDhWZZIJ88Fvl9jyZL+Ex2o4eVDB5livuw5nzP1mxcrljL0zQbLPboyYfGDcFUd X-Received: by 2002:a17:906:9755:b0:722:f1e3:ec1a with SMTP id o21-20020a170906975500b00722f1e3ec1amr2539994ejy.709.1656497629225; Wed, 29 Jun 2022 03:13:49 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656497629; cv=none; d=google.com; s=arc-20160816; b=AwFFqzhpctMgAeSZxA0KrwsQQ8yBpmG1iq3P42sbOvapk4mCx/ea91liH2xxszlw5+ AHB15UrVVUDvCHuy8C2WHMauJ6bjbkze6nAm41crhVyTPvuh9bwuVYFc/I8/kwOrmKZl r9t+vqhF9CUW3aVzEVaOoBGV3FMolHx4j/M/7KqE1hS/jbMKWUCfrPH2ifI39Z9jaBDR P+2vywut1g27qxgJuYOtuxEFV1sAKjNq+AWRL3U+bVU8M5Q/rXUsWU2kqDxK/NtSaMf9 supzoSzoYF6zJ72YYalVrv4K6LKkfLYNlpDYDb92VPV9YUn2XfhQ//5RNZoH49T2opHZ gUBg== 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=oVPFwVeKb43qQm2IVyet+guFknVNYC07G2HstuB3VwM=; b=oHLYu9OVrshByljHCRbokNVpR7qUlmpc9JLqo6lU9kRfO4/fMPdcT8TrPW7CHsZKLu QdwxrGhSKoqTlogYr4xhNU7E6aVLuK/9VV0dAc1eceRIFKXXJSaFPTANoHdsITlBF8CC 95u7gXqFkCRIbTo/6KO3LP8iZyT9iHpQ3h8I3hShA5pTK6vYf8ZLQs2yfiZFMVbo0gjF yeNyGEtu0AFwi9CfdB94Wx97tMEA6Vn7PfA8eysITSzGRIw7Akm7ktycho/mkCpLFByc MmcPgcRIr3LhRcxGmc0w80JcUlYQmATTjTbb0YK/ddLNWGpKXKULxhNuoY4+H77L3m4l woGw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@haasn.xyz header.s=mail header.b=OSUPYeAZ; 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 js3-20020a17090797c300b007263057e377si18060214ejc.863.2022.06.29.03.13.48; Wed, 29 Jun 2022 03:13:49 -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=OSUPYeAZ; 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 C663A68B737; Wed, 29 Jun 2022 13:13:07 +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 BDAD568B6D1 for ; Wed, 29 Jun 2022 13:12:58 +0300 (EEST) Received: from haasn.dev (unknown [10.30.0.2]) by haasn.dev (Postfix) with ESMTP id 8789A4A7ED; Wed, 29 Jun 2022 12:12:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=haasn.xyz; s=mail; t=1656497578; bh=DVZDjOhTpdpj1f1R+avj3own3DR0iL7Twdn/JypCtSU=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=OSUPYeAZYTDUo0eB98Gw72nLrMEl+PmySijVeTwn2cSvdNZ6wanZwexXEt5U4phgj mocdISPI3+fHjREeVxQdxnpMsnf1bc8qlrmnNDj68dOmgoh13zX1C7jNmR8DjIsm3T nTsjUMKn14NI2ThitBCpdQ0ILKbnlB0JsO86NDO4= From: Niklas Haas To: ffmpeg-devel@ffmpeg.org Date: Wed, 29 Jun 2022 12:12:49 +0200 Message-Id: <20220629101251.13236-5-ffmpeg@haasn.xyz> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220629101251.13236-1-ffmpeg@haasn.xyz> References: <20220629101251.13236-1-ffmpeg@haasn.xyz> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 4/6] avcodec: add common fflcms2 boilerplate 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: coE17fVdkT8N From: Niklas Haas Handling this in general code makes more sense than handling it in individual codec files, because it would be a lot of unnecessary code duplication for the plenty of formats that support exporting ICC profiles (jpg, png, tiff, webp, jxl, ...). encode.c and decode.c will be in charge of initializing this state as needed, so we merely need to make sure to uninit it afterwards from the common destructor path. Signed-off-by: Niklas Haas --- configure | 2 +- libavcodec/Makefile | 1 + libavcodec/avcodec.c | 4 ++++ libavcodec/decode.c | 4 ++++ libavcodec/internal.h | 8 ++++++++ 5 files changed, 18 insertions(+), 1 deletion(-) diff --git a/configure b/configure index 0de9b2abcb..d3f151e246 100755 --- a/configure +++ b/configure @@ -3807,7 +3807,7 @@ swresample_suggest="libm libsoxr stdatomic" swscale_deps="avutil" swscale_suggest="libm stdatomic" -avcodec_extralibs="pthreads_extralibs iconv_extralibs dxva2_extralibs" +avcodec_extralibs="pthreads_extralibs iconv_extralibs dxva2_extralibs lcms2_extralibs" avfilter_extralibs="pthreads_extralibs" avutil_extralibs="d3d11va_extralibs nanosleep_extralibs pthreads_extralibs vaapi_drm_extralibs vaapi_x11_extralibs vdpau_x11_extralibs" diff --git a/libavcodec/Makefile b/libavcodec/Makefile index 5c4f62c631..d2f969ba52 100644 --- a/libavcodec/Makefile +++ b/libavcodec/Makefile @@ -114,6 +114,7 @@ OBJS-$(CONFIG_INTRAX8) += intrax8.o intrax8dsp.o msmpeg4data.o OBJS-$(CONFIG_IVIDSP) += ivi_dsp.o OBJS-$(CONFIG_JNI) += ffjni.o jni.o OBJS-$(CONFIG_JPEGTABLES) += jpegtables.o +OBJS-$(CONFIG_LCMS2) += fflcms2.o OBJS-$(CONFIG_LLAUDDSP) += lossless_audiodsp.o OBJS-$(CONFIG_LLVIDDSP) += lossless_videodsp.o OBJS-$(CONFIG_LLVIDENCDSP) += lossless_videoencdsp.o diff --git a/libavcodec/avcodec.c b/libavcodec/avcodec.c index 5f6e71a39e..3524214546 100644 --- a/libavcodec/avcodec.c +++ b/libavcodec/avcodec.c @@ -481,6 +481,10 @@ av_cold int avcodec_close(AVCodecContext *avctx) av_channel_layout_uninit(&avci->initial_ch_layout); +#if CONFIG_LCMS2 + ff_icc_context_uninit(&avci->icc); +#endif + av_freep(&avctx->internal); } diff --git a/libavcodec/decode.c b/libavcodec/decode.c index 1893caa6a6..d1414c599b 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -49,6 +49,10 @@ #include "internal.h" #include "thread.h" +#if CONFIG_LCMS2 +# include "fflcms2.h" +#endif + static int apply_param_change(AVCodecContext *avctx, const AVPacket *avpkt) { int ret; diff --git a/libavcodec/internal.h b/libavcodec/internal.h index 17e1de8127..e284816705 100644 --- a/libavcodec/internal.h +++ b/libavcodec/internal.h @@ -33,6 +33,10 @@ #include "avcodec.h" #include "config.h" +#if CONFIG_LCMS2 +# include "fflcms2.h" +#endif + #define FF_SANE_NB_CHANNELS 512U #if HAVE_SIMD_ALIGN_64 @@ -148,6 +152,10 @@ typedef struct AVCodecInternal { uint64_t initial_channel_layout; #endif AVChannelLayout initial_ch_layout; + +#if CONFIG_LCMS2 + FFIccContext icc; /* used to read and write embedded ICC profiles */ +#endif } AVCodecInternal; /** From patchwork Wed Jun 29 10:12:50 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niklas Haas X-Patchwork-Id: 36527 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp207173pzh; Wed, 29 Jun 2022 03:13:58 -0700 (PDT) X-Google-Smtp-Source: AGRyM1s3rTlYr5BP9ehCbl+kZPVATPngTmdcAGEACtTbabDyhqRqezXoROGVdFJzhBcYr34rwZdk X-Received: by 2002:a05:6402:2743:b0:437:d467:4a57 with SMTP id z3-20020a056402274300b00437d4674a57mr3176157edd.235.1656497638755; Wed, 29 Jun 2022 03:13:58 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656497638; cv=none; d=google.com; s=arc-20160816; b=C1MaHhW3GdtNOxT6y4YnakRHApnMW9oMQ6dPzz2YTJGEpiTu1AqispHaGGGZt5F76g e+SHyhDN7PPMbGoBmbUjU3cc0sqvlicjMDJBYXduL2PUi+jZIj/LG4h+YOsAczOnkxfm OMS770RluCMx68rL4+xxL6S2TgZ0Hvu5cERXIod/Yq5AIfs3erJv4mGApNkIsw2n1UXh gYVLttdeouE9jBsIKdGVW8BENpJwtceJogewKdHeJbafA0DIM4UFz0cTksfxAke6ciuI pg+sNa5iMkKgWkhnIXJGFD5Zq8t5SLaYoYY1aXhvitBtBy1teNisnuelSFt4jTzNr1nZ ZCtQ== 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=HwnaEoCA+STvEPZyk8cS5dkjn3uqZj3/97u77FdcnLQ=; b=jCUWOzCULx26zgSEzZ1OB1U6xz2N/lLoXrkW52dQ6CTAkrwzcw5avkAO/279hm/bET ujRNM9qUmShcFeMTHBmYxE8vj9dtVUGRHwPLQDkLdhGCXpuXQZVWpgWaRy00gkTaAF5a 4IkDBx/QR8PWlBjdNocjuYT/wOIorYf6AdCJLBtRly1awAVXYYgoJsb/U4zCkvhxTu7U PWpm7aEY0NNmxMus4ZmtsvJQb3amhcXInNtLh8QgCK8eUySpTfDVwjG4BvBMxfFDtnHm q5u24g/bl+cQELerWM9ZomjfcQduUHxeyA2IcEu4LZtcLjG9JsZWeS/TIeSQS0BWpgef tGVw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@haasn.xyz header.s=mail header.b=aAERpOMi; 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 hc39-20020a17090716a700b00711da366315si1097690ejc.717.2022.06.29.03.13.58; Wed, 29 Jun 2022 03:13:58 -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=aAERpOMi; 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 A876A68B74E; Wed, 29 Jun 2022 13:13:08 +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 5CFF668B6EA for ; Wed, 29 Jun 2022 13:13:01 +0300 (EEST) Received: from haasn.dev (unknown [10.30.0.2]) by haasn.dev (Postfix) with ESMTP id BEAB24A7F3; Wed, 29 Jun 2022 12:12:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=haasn.xyz; s=mail; t=1656497578; bh=+xt5UOJPIdZ7NhtmtaQcLpwIIkd+qYvKhYL3k3PsuME=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=aAERpOMi5OBqdBqEMZwrjQb5bkqaM5MR+FfNiGthkDhdJqktQExigZ8P9ZiHDDU7g qodabIVLFobhTN9oBIhy1/5yOJnp1ibsuE2v3T4jKJUNm3GdeWTc/2UWw8s+niOPNn XF7yuDnnOAz3PTebzLlLe/N3On8d0hcPHw7mHMrg= From: Niklas Haas To: ffmpeg-devel@ffmpeg.org Date: Wed, 29 Jun 2022 12:12:50 +0200 Message-Id: <20220629101251.13236-6-ffmpeg@haasn.xyz> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220629101251.13236-1-ffmpeg@haasn.xyz> References: <20220629101251.13236-1-ffmpeg@haasn.xyz> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 5/6] avcodec/decode: parse 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: 3p3h2QtjZ/t5 From: Niklas Haas Implementation for the decode side of the ICC profile API, roughly matching the behavior of the existing vf_iccdetect filter. Closes: #9673 Signed-off-by: Niklas Haas --- libavcodec/decode.c | 61 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 56 insertions(+), 5 deletions(-) diff --git a/libavcodec/decode.c b/libavcodec/decode.c index d1414c599b..e81a7fb4d5 100644 --- a/libavcodec/decode.c +++ b/libavcodec/decode.c @@ -49,10 +49,6 @@ #include "internal.h" #include "thread.h" -#if CONFIG_LCMS2 -# include "fflcms2.h" -#endif - static int apply_param_change(AVCodecContext *avctx, const AVPacket *avpkt) { int ret; @@ -508,6 +504,54 @@ FF_ENABLE_DEPRECATION_WARNINGS return ret < 0 ? ret : 0; } +#if CONFIG_LCMS2 +static int detect_colorspace(AVCodecContext *avctx, AVFrame *frame) +{ + AVCodecInternal *avci = avctx->internal; + enum AVColorTransferCharacteristic trc; + AVColorPrimariesDesc coeffs; + enum AVColorPrimaries prim; + cmsHPROFILE profile; + AVFrameSideData *sd; + int ret; + if (!avctx->icc_profiles) + return 0; + + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_ICC_PROFILE); + if (!sd || !sd->size) + return 0; + + if (!avci->icc.avctx) { + ret = ff_icc_context_init(&avci->icc, avctx); + if (ret < 0) + return ret; + } + + profile = cmsOpenProfileFromMemTHR(avci->icc.ctx, sd->data, sd->size); + if (!profile) + return AVERROR_INVALIDDATA; + + ret = ff_icc_profile_read_primaries(&avci->icc, profile, &coeffs); + if (!ret) + ret = ff_icc_profile_detect_transfer(&avci->icc, profile, &trc); + cmsCloseProfile(profile); + if (ret < 0) + return ret; + + prim = av_csp_primaries_id_from_desc(&coeffs); + if (prim != AVCOL_PRI_UNSPECIFIED) + frame->color_primaries = prim; + if (trc != AVCOL_TRC_UNSPECIFIED) + frame->color_trc = trc; + return 0; +} +#else /* !CONFIG_LCMS2 */ +static int detect_colorspace(av_unused AVCodecContext *c, av_unused AVFrame *f) +{ + return 0; +} +#endif + static int decode_simple_receive_frame(AVCodecContext *avctx, AVFrame *frame) { int ret; @@ -528,7 +572,7 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) { AVCodecInternal *avci = avctx->internal; const FFCodec *const codec = ffcodec(avctx->codec); - int ret; + int ret, ok; av_assert0(!frame->buf[0]); @@ -542,6 +586,13 @@ static int decode_receive_frame_internal(AVCodecContext *avctx, AVFrame *frame) if (ret == AVERROR_EOF) avci->draining_done = 1; + /* preserve ret */ + ok = detect_colorspace(avctx, frame); + if (ok < 0) { + av_frame_unref(frame); + return ok; + } + if (!(codec->caps_internal & FF_CODEC_CAP_SETS_FRAME_PROPS) && IS_EMPTY(avci->last_pkt_props)) { // May fail if the FIFO is empty. From patchwork Wed Jun 29 10:12:51 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Niklas Haas X-Patchwork-Id: 36528 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:8b27:b0:88:1bbf:7fd2 with SMTP id l39csp207264pzh; Wed, 29 Jun 2022 03:14:09 -0700 (PDT) X-Google-Smtp-Source: AGRyM1sEfoxgzPaJyMBx8hVv9PI2+jixHuG+RDKLUnxyvneG/KK4e4IG5M8DFpGJZvi+wP9Gq53W X-Received: by 2002:a05:6402:270a:b0:437:63ea:f2b5 with SMTP id y10-20020a056402270a00b0043763eaf2b5mr3238611edd.33.1656497649496; Wed, 29 Jun 2022 03:14:09 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1656497649; cv=none; d=google.com; s=arc-20160816; b=TQ4VvrV5KMu7QznI4arQGKMWMZYXRa1aT20+2ErRhQkFJV1i84xsOCMB9vHM/zfMNw sc+iX8JYano3vTN3d60mFfNzHZTzqbxmXvSjsyXfNW+v1T6yEh+dvp2xXylCjMhvjEaI jze4Vvt9uD34fUf2AXR16CvMfmndpQ0LvrqYTfixzO4v0/mNx1eYQZ2Jbm2qkVZzzm9X sMDdpW2Dx5au0WP+k5+3cTGZFcXmd7GKHcV0Zrztz/zE9ci/HYRQU83OKJI2O72q+2uB SeKzxUXwMEOKijUmOTFXuiOyRODWUlOvuym5TuOI1bMC6gbJQCjiVYBXJwG3Swfj9qqC u9wQ== 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=ddBHRuRtmdFFmv1Fu3fMWPrwxazyECo1XW7Iu4oahkY=; b=sEbIWQWVyKxvbFhtw9IzyK7DL/MJQSWzKU/avwZTGE+EM9Wa85l2xSuOmzEyopDiD2 KHNn9FZnfPYETg/Q2KdjWdJift0WJ2BTDTcZk3RLYLcKf5iouBDf8KnhJQl6vRkDLFVE X0ejixthvTig+7FrGRaXY762E5dncJOqUt/4Y1TrLciqdWTb6RSZw56znfD5n9ZqJRRo 1IJDP+tuYS8dkCvWAzYI2A8yzfPcJPl3rARxJj3Hf9Cn0iy+Tizft/boyRuJS4e+smQc FgqqMjdCraWUoVepi9NTDuWuznmCAukS16WdoR6aQ29Yarwdu7V2mV2unlW69gU9RW9y +o8Q== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@haasn.xyz header.s=mail header.b=pzzu9nf4; 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 f20-20020a056402355400b0043774af6f0asi3895907edd.371.2022.06.29.03.14.07; Wed, 29 Jun 2022 03:14:09 -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=pzzu9nf4; 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 A575A68B73C; Wed, 29 Jun 2022 13:13:09 +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 689E668B704 for ; Wed, 29 Jun 2022 13:13:01 +0300 (EEST) Received: from haasn.dev (unknown [10.30.0.2]) by haasn.dev (Postfix) with ESMTP id 0449C4A7F4; Wed, 29 Jun 2022 12:12:58 +0200 (CEST) DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=haasn.xyz; s=mail; t=1656497579; bh=2eXqwu4pHKH+Mjil0Bi++fsFEfcpHZO7ScOluSPGUQ4=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=pzzu9nf4PRBD/E/RHLVLQpKEH3Kk50Rw10GuP8/FaKESjht6wtS3nWt+duOmsEWC/ 4F/Ep/2daov8Q6Lh7lP+A2SnTkfMsE0s7jp10dAcDl+TNTFvRch1TmWa7ahoHkRomV F8DSsj3VycajW0kxNL8fSrFKadFXXpPEVdH9rYqQ= From: Niklas Haas To: ffmpeg-devel@ffmpeg.org Date: Wed, 29 Jun 2022 12:12:51 +0200 Message-Id: <20220629101251.13236-7-ffmpeg@haasn.xyz> X-Mailer: git-send-email 2.36.1 In-Reply-To: <20220629101251.13236-1-ffmpeg@haasn.xyz> References: <20220629101251.13236-1-ffmpeg@haasn.xyz> MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH v2 6/6] avcodec/encode:: generate 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: KUZXKd4aeZMN From: Niklas Haas Only if requested, and only if the codec signals support for ICC profiles. Implementation roughly matches the functionality of the existing vf_iccgen filter, albeit with some reduced flexibility and no caching. Ideally, we'd also only do this on the first frame (e.g. mjpeg, apng), but there's no meaningful way for us to distinguish between this case and e.g. somebody using the image2 muxer, in which case we'd want to attach ICC profiles to every frame in the stream. Closes: #9672 Signed-off-by: Niklas Haas --- libavcodec/encode.c | 50 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/libavcodec/encode.c b/libavcodec/encode.c index b68bf1e184..d1fb6f3a75 100644 --- a/libavcodec/encode.c +++ b/libavcodec/encode.c @@ -308,6 +308,50 @@ static int encode_receive_packet_internal(AVCodecContext *avctx, AVPacket *avpkt return ret; } +#if CONFIG_LCMS2 +static int encode_generate_icc_profile(AVCodecContext *avctx, AVFrame *frame) +{ + enum AVColorTransferCharacteristic trc = frame->color_trc; + enum AVColorPrimaries prim = frame->color_primaries; + const FFCodec *const codec = ffcodec(avctx->codec); + AVCodecInternal *avci = avctx->internal; + cmsHPROFILE profile; + int ret; + + if (!avctx->icc_profiles || !(codec->caps_internal & FF_CODEC_CAP_ICC_PROFILES)) + return 0; /* don't generate ICC profiles if disabled or unsupported */ + + if (trc == AVCOL_TRC_UNSPECIFIED) + trc = avctx->color_trc; + if (prim == AVCOL_PRI_UNSPECIFIED) + prim = avctx->color_primaries; + if (trc == AVCOL_TRC_UNSPECIFIED || prim == AVCOL_PRI_UNSPECIFIED) + return 0; /* can't generate ICC profile with missing csp tags */ + + if (av_frame_get_side_data(frame, AV_FRAME_DATA_ICC_PROFILE)) + return 0; /* don't overwrite existing ICC profile */ + + if (!avci->icc.avctx) { + ret = ff_icc_context_init(&avci->icc, avctx); + if (ret < 0) + return ret; + } + + ret = ff_icc_profile_generate(&avci->icc, prim, trc, &profile); + if (ret < 0) + return ret; + + ret = ff_icc_profile_attach(&avci->icc, profile, frame); + cmsCloseProfile(profile); + return ret; +} +#else /* !CONFIG_LCMS2 */ +static int encode_generate_icc_profile(av_unused AVCodecContext *c, av_unused AVFrame *f) +{ + return 0; +} +#endif + static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src) { AVCodecInternal *avci = avctx->internal; @@ -352,6 +396,12 @@ static int encode_send_frame_internal(AVCodecContext *avctx, const AVFrame *src) return ret; } + if (avctx->codec->type == AVMEDIA_TYPE_VIDEO) { + ret = encode_generate_icc_profile(avctx, dst); + if (ret < 0) + return ret; + } + return 0; }