From patchwork Mon Jan 16 20:01:39 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Leo Izen X-Patchwork-Id: 40055 Delivered-To: ffmpegpatchwork2@gmail.com Received: by 2002:a05:6a20:1d43:b0:b8:6755:f97e with SMTP id cs3csp1286295pzb; Mon, 16 Jan 2023 12:01:59 -0800 (PST) X-Google-Smtp-Source: AMrXdXsMaClXaGfLW514yi7ezXUz2USi4T7W2LAoiZshATj22rLmiw2PC11SyItxOQ2zqqN4yYJ5 X-Received: by 2002:a17:906:7101:b0:870:4f04:7ad0 with SMTP id x1-20020a170906710100b008704f047ad0mr213474ejj.45.1673899318970; Mon, 16 Jan 2023 12:01:58 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1673899318; cv=none; d=google.com; s=arc-20160816; b=uTDZAqEokRKc8Xuk2iBHdYmAkDCdSjR0mSfFmHAxY3f8v0pDoHi4c88D1iVyhRdiDE vUczAWcqXMxMIaaLGZFCttmUWtpOJAYQvKvLnMCZtlJiCcz2wVwFoL22mDi9+9YSc7PX BOmHzbqxUA1EWGMesbj9dwNF1NjVlzJzQ5oI5PWvSbDKm0GlJJmRxl0ReixBtLY1kqVr fDoNA1G5J/9IshUEyixVwaCCnrjWP02GgmifFDrqOY7EoJMXF35ygWUkM0ldluuWzS6h pnFJfT9k5YrRIurNfhjh77tBUtXp1rbWc10SABeelOJtaLD9Fq75qZAGnuRupYsX3bkc vtWw== 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:message-id:date:to:from :dkim-signature:delivered-to; bh=Ui8Lev7oN1SXx9bsIlZw0lAeq/V8ZvqSFipCVcs0aMI=; b=DomGO17Q4OyG3QczLXict9IqvcVnoqP/5IqMG38ppEDeZLQ0kVFDZClcxJvXKfWqvi S39FCiNrhzONfOKyt7eM1hG6orp3v3FhIyi/mR9tMz/zf/Eela7yc1UYVvIASl8QKFu/ 5EZSvlOLppmnmLIPs66ipma3y9EC5vnfgeN6XiOn53/7t+o94+9qSz04u/2uwqvVT4WQ lq0bDEsI0QUZYbarE2y2950C9HldZhT+AiuAAhj5oVD5Y8yykXJM6Hgu4gBKs0obBfgr 9qbWmwljO/E8IKk7kWWWlfO5RB9MFiqqhDyRXN1Y9Q/BWZrTNVtiS+DkxnxOFFN8koy7 ShsQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20210112 header.b=fr8lWJ4L; 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; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id be5-20020a1709070a4500b0084cbf5552a1si30211468ejc.876.2023.01.16.12.01.55; Mon, 16 Jan 2023 12:01:58 -0800 (PST) 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=@gmail.com header.s=20210112 header.b=fr8lWJ4L; 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; dmarc=fail (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 9728C68BDBA; Mon, 16 Jan 2023 22:01:51 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-io1-f50.google.com (mail-io1-f50.google.com [209.85.166.50]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 0BF0668BC8C for ; Mon, 16 Jan 2023 22:01:45 +0200 (EET) Received: by mail-io1-f50.google.com with SMTP id j1so5736155iob.6 for ; Mon, 16 Jan 2023 12:01:44 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=94/eSCHllPgKDIcMj4QFBBkDwxbkvHiiCFmimFa7/Rk=; b=fr8lWJ4LT/q++VuwCi+m0m/O4kf5rJsF95lfUz8iz3SR9LGmglSoGPi7RydjTA2ykL M19GOp8WlEJMukI+VNGGoH9JFVYWCJan3Rkx3s++uFNbGrpuQj6NL+9ItSCpzndETNuN CDON8J6gYTLgsO4VS+yY/GPk3HpgB8TNgbsqattnaYqVITHKwrPWTQbzZKWCpTiwy1N2 TAIkEV9I5TQO2NyI7fEXDrH27MLbBoEMv9eX+/1VfIkL5q4zftv7t39Th/WgAf2ZHuaZ Jpe5Poccd71asxAHfXR2A+FGnN4KTGlAni8T1hmRta/L9Xr/yGk5BAKCmfF16o8QYvyv x0mA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=94/eSCHllPgKDIcMj4QFBBkDwxbkvHiiCFmimFa7/Rk=; b=5h/fd7rmC1p4q0EIA8kOCiKwHd9pTOy4tFn7zzhLDoIusvtq0OE2KcW3ND+mTEWmlG gRXDnWMvryEO5jTDxl7MILQGw19tvxCNrSkD+oLC/szmBLK7p/8X2she4w5ohddyfny0 AR/ElP2O/NR7I10XEiH+0185NTZKuOe9F5FYy1sgeS2dNnsVqZnu3euK8f/+2cLhB6d/ NxEDmR4KsAqAIONgqomITgXameDRyYTu/4hrLAW0BSP1qFIwmeJMQ4vw0QKRg0fi1jkn IUtBKGHL884GKfn1laCgmcOVCsM2na+Dt+CpNC5gqeUILfQClkNLR97PNEtNJVEY0kyT GCag== X-Gm-Message-State: AFqh2kqTMJm8ISKsj4QNLNnl/dKuX4X7ZCR59b0TC5pVbLQTyqUUSyEL my4htDkovbHvQW8AS/nXFSsvhrSc9fg= X-Received: by 2002:a5e:db45:0:b0:6bc:d70f:8b38 with SMTP id r5-20020a5edb45000000b006bcd70f8b38mr14594928iop.18.1673899303437; Mon, 16 Jan 2023 12:01:43 -0800 (PST) Received: from gauss.local (c-68-41-54-207.hsd1.mi.comcast.net. [68.41.54.207]) by smtp.gmail.com with ESMTPSA id j5-20020a5d93c5000000b006bba42f7822sm9766604ioo.52.2023.01.16.12.01.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Jan 2023 12:01:42 -0800 (PST) From: Leo Izen To: ffmpeg-devel@ffmpeg.org Date: Mon, 16 Jan 2023 15:01:39 -0500 Message-Id: <20230116200139.232178-1-leo.izen@gmail.com> X-Mailer: git-send-email 2.39.0 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH] avcodec/png: support sRGB and cICP chunks 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: Leo Izen Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" X-TUID: wvXi9/Y3nwA+ This commit adds decode-side support for sRGB chunks and cICP chunks. The sRGB chunk is part of the PNG specification, 2nd edition, and FFmpeg already supports writing this chunk on the encode-side, but this chunk is ignored upon decode. This commit causes the PNG decoder to tag the output video stream as sRGB using the enum values if the sRGB chunk is present, and ignore the gAMA chunk (per the specification). The cICP chunk allows the PNG color space to be tagged with any of the enum values in H.273, not just sRGB, and it is defined in the working draft of the PNG specification (3rd edition). libavutil/pixfmt.h uses exact the same values as H.273 so these are translated as-is. This commit also adds support for writing the cICP chunk for video frames tagged as anything that isn't sRGB. It still writes cHRM and gAMA as a fallback. PNG specification, 2nd edition: https://www.w3.org/TR/2003/REC-PNG-20031110/ PNG specification working draft, 3rd edition: https://www.w3.org/TR/png-3/ Signed-off-by: Leo Izen --- libavcodec/pngdec.c | 80 +++++++++++++++++++++++++++++++++++++++++++++ libavcodec/pngenc.c | 8 +++++ 2 files changed, 88 insertions(+) diff --git a/libavcodec/pngdec.c b/libavcodec/pngdec.c index f1cad26c52..358a4d69e6 100644 --- a/libavcodec/pngdec.c +++ b/libavcodec/pngdec.c @@ -30,6 +30,7 @@ #include "libavutil/intreadwrite.h" #include "libavutil/stereo3d.h" #include "libavutil/mastering_display_metadata.h" +#include "libavutil/pixfmt.h" #include "avcodec.h" #include "bytestream.h" @@ -91,6 +92,9 @@ typedef struct PNGDecContext { int has_trns; uint8_t transparent_color_be[6]; + int have_cicp; + int have_srgb; + uint32_t palette[256]; uint8_t *crow_buf; uint8_t *last_row; @@ -876,6 +880,58 @@ static int decode_trns_chunk(AVCodecContext *avctx, PNGDecContext *s, return 0; } +static int decode_cicp_chunk(AVCodecContext *avctx, PNGDecContext *s, + GetByteContext *gb, AVFrame *f) +{ + int primaries, trc, colorspace, range; + + if (!(s->hdr_state & PNG_IHDR)) { + av_log(avctx, AV_LOG_ERROR, "cICP before IHDR\n"); + return AVERROR_INVALIDDATA; + } + + if (s->pic_state & PNG_PLTE) { + av_log(avctx, AV_LOG_ERROR, "cICP after PLTE\n"); + return AVERROR_INVALIDDATA; + } + + if (s->pic_state & PNG_IDAT) { + av_log(avctx, AV_LOG_ERROR, "cICP after IDAT\n"); + return AVERROR_INVALIDDATA; + } + + /* these enum values match H.273 */ + primaries = bytestream2_get_byte(gb); + if (primaries >= AVCOL_PRI_NB) + av_log(avctx, AV_LOG_WARNING, "unrecognized cICP primaries\n"); + else + avctx->color_primaries = f->color_primaries = primaries; + + trc = bytestream2_get_byte(gb); + if (trc >= AVCOL_TRC_NB) + av_log(avctx, AV_LOG_WARNING, "unrecognized cICP transfer\n"); + else + avctx->color_trc = f->color_trc = trc; + + colorspace = bytestream2_get_byte(gb); + if (colorspace != 0) + av_log(avctx, AV_LOG_WARNING, "only RGB pngs supported\n"); + else + avctx->colorspace = f->colorspace = colorspace; + + range = bytestream2_get_byte(gb); + if (range != 0 && range != 1) { + av_log(avctx, AV_LOG_ERROR, "invalid cICP range\n"); + return AVERROR_INVALIDDATA; + } + avctx->color_range = f->color_range = range ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + + /* clear gAMA-chunk derived value */ + av_dict_set(&s->frame_metadata, "gamma", NULL, 0); + s->have_cicp = 1; + return 0; +} + static int decode_iccp_chunk(PNGDecContext *s, GetByteContext *gb, AVFrame *f) { int ret, cnt = 0; @@ -1311,6 +1367,25 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, } break; } + case MKTAG('s', 'R', 'G', 'B'): + bytestream2_get_byte(&gb_chunk); /* read rendering intent */ + + /* cICP overrides sRGB */ + if (s->have_cicp) + break; + s->have_srgb = 1; + + avctx->colorspace = p->colorspace = AVCOL_SPC_RGB; + avctx->color_primaries = p->color_primaries = AVCOL_PRI_BT709; + avctx->color_trc = p->color_trc = AVCOL_TRC_IEC61966_2_1; + + /* clear gAMA-chunk derived value */ + av_dict_set(&s->frame_metadata, "gamma", NULL, 0); + break; + case MKTAG('c', 'I', 'C', 'P'): + if ((ret = decode_cicp_chunk(avctx, s, &gb_chunk, p)) < 0) + goto fail; + break; case MKTAG('i', 'C', 'C', 'P'): { if ((ret = decode_iccp_chunk(s, &gb_chunk, p)) < 0) goto fail; @@ -1335,6 +1410,10 @@ static int decode_frame_common(AVCodecContext *avctx, PNGDecContext *s, char *gamma_str; int num = bytestream2_get_be32(&gb_chunk); + /* sRGB and cICP override gAMA */ + if (s->have_srgb || s->have_cicp) + break; + av_bprint_init(&bp, 0, AV_BPRINT_SIZE_UNLIMITED); av_bprintf(&bp, "%i/%i", num, 100000); ret = av_bprint_finalize(&bp, &gamma_str); @@ -1548,6 +1627,7 @@ static int decode_frame_png(AVCodecContext *avctx, AVFrame *p, s->y = s->has_trns = 0; s->hdr_state = 0; s->pic_state = 0; + s->have_srgb = s->have_cicp = 0; /* Reset z_stream */ ret = inflateReset(&s->zstream.zstream); diff --git a/libavcodec/pngenc.c b/libavcodec/pngenc.c index ca1a186ca8..e52104526d 100644 --- a/libavcodec/pngenc.c +++ b/libavcodec/pngenc.c @@ -34,6 +34,7 @@ #include "libavutil/opt.h" #include "libavutil/color_utils.h" #include "libavutil/stereo3d.h" +#include "libavutil/pixfmt.h" #include @@ -438,6 +439,13 @@ static int encode_headers(AVCodecContext *avctx, const AVFrame *pict) pict->color_trc == AVCOL_TRC_IEC61966_2_1) { s->buf[0] = 1; /* rendering intent, relative colorimetric by default */ png_write_chunk(&s->bytestream, MKTAG('s', 'R', 'G', 'B'), s->buf, 1); + } else if (pict->color_primaries != AVCOL_PRI_UNSPECIFIED || + pict->color_trc != AVCOL_TRC_UNSPECIFIED) { + s->buf[0] = pict->color_primaries; + s->buf[1] = pict->color_trc; + s->buf[2] = 0; /* only matrix=0, i.e. RGB supported */ + s->buf[3] = pict->color_range == AVCOL_RANGE_MPEG ? 0 : 1; + png_write_chunk(&s->bytestream, MKTAG('c', 'I', 'C', 'P'), s->buf, 4); } if (png_get_chrm(pict->color_primaries, s->buf))