From patchwork Tue Jun 8 14:44:12 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: David Plowman X-Patchwork-Id: 12520 Return-Path: X-Original-To: parsemail@patchwork.libcamera.org Delivered-To: parsemail@patchwork.libcamera.org Received: from lancelot.ideasonboard.com (lancelot.ideasonboard.com [92.243.16.209]) by patchwork.libcamera.org (Postfix) with ESMTPS id A3C25C320B for ; Tue, 8 Jun 2021 14:44:21 +0000 (UTC) Received: from lancelot.ideasonboard.com (localhost [IPv6:::1]) by lancelot.ideasonboard.com (Postfix) with ESMTP id 1E8F068935; Tue, 8 Jun 2021 16:44:20 +0200 (CEST) Authentication-Results: lancelot.ideasonboard.com; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=raspberrypi.com header.i=@raspberrypi.com header.b="dgyP4oDG"; dkim-atps=neutral Received: from mail-wr1-x429.google.com (mail-wr1-x429.google.com [IPv6:2a00:1450:4864:20::429]) by lancelot.ideasonboard.com (Postfix) with ESMTPS id 6E16D602A7 for ; Tue, 8 Jun 2021 16:44:18 +0200 (CEST) Received: by mail-wr1-x429.google.com with SMTP id o3so3490591wri.8 for ; Tue, 08 Jun 2021 07:44:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=raspberrypi.com; s=google; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=D9/f6O7zN/BBzUimu5fdwYXvAs0jlUvjQorkBozOPyo=; b=dgyP4oDGqGNsErz1owPaE+XLUZoU89dt3l1JUpwsjiLUhwQT1rnPzQKBTVeG+4iQAO EfwQnJtr5f3geUimo1wHPal3/yz5yfir2HhQYhMY0d0GT1e6ZIyZaoJTaM8lMBpmW99X KEH5p13aQvjYmbeISRKhzdSsWEVCyh75upoF1SaQUQLzpcoetoL67wyDdCQ98OT7q9H4 yaKis7x2Onag9E22TtpYVmJbRHMpxrxug5yWNt6uivzmEEE1pnS87ms5z6K6yIAxba4z oaytpS9C4bYixQikrfihNY/8jes4NUPkTW+NHKECs3uVeQlrNANt20tu53229oYf8ka/ ri4A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=D9/f6O7zN/BBzUimu5fdwYXvAs0jlUvjQorkBozOPyo=; b=Hm+3n+yd0FK2O22xiJYTzCuWwjx+0lpzzuBzh/x2MJOEQ6OkGtWOxuplKzDkUDbHHP SMf6bLwK1NEgMd/tTLl59LDILEEOaHQqOzAAE4joC0tghUz1MPHePytHQ8eb1Ws8mXoH MQ7LFzZUsKJWIAbn3hUS0ebyfhvEAaEyMTSAbIR/uQNwZrZOwgg5Z0NhmskTCf7j299v FyeO3cHJwdnApXx9yHhnvh8kfpk93cTbMNqK6o4/WPxwXvPku//9NI4+u3SAptUCTIh9 SNMNKRzPBE6Omk/ZedHsuc3MbQSkJAAO7KERMRJoeYI0dvS2UbWXyA5Q554fqmkAnRz5 Qv8A== X-Gm-Message-State: AOAM533TMMdizaMsWvkHZVrnXu1kHitnabm7dbcit+GtGLOmoVro3qCM Si/BYgadYl7voiATU5OXXqoLcknnaq7hZb70 X-Google-Smtp-Source: ABdhPJzcrnxppY9bLf8JEM1Sbgd2f6yGgZ6AYj9xEBNbpnP/hyZz0S4FCPRn7LUAR6cFwlQUi7Uzwg== X-Received: by 2002:adf:a195:: with SMTP id u21mr22724502wru.367.1623163457952; Tue, 08 Jun 2021 07:44:17 -0700 (PDT) Received: from pi4-davidp.pitowers.org ([2a00:1098:3142:14:1ce1:9965:4328:89c4]) by smtp.gmail.com with ESMTPSA id i21sm3053173wmq.12.2021.06.08.07.44.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 08 Jun 2021 07:44:17 -0700 (PDT) From: David Plowman To: libcamera-devel@lists.libcamera.org Date: Tue, 8 Jun 2021 15:44:12 +0100 Message-Id: <20210608144413.1529-3-david.plowman@raspberrypi.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210608144413.1529-1-david.plowman@raspberrypi.com> References: <20210608144413.1529-1-david.plowman@raspberrypi.com> MIME-Version: 1.0 Subject: [libcamera-devel] [RFC PATCH 2/3] libcamera: Support passing ColorSpaces to V4L2 drivers X-BeenThere: libcamera-devel@lists.libcamera.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libcamera-devel-bounces@lists.libcamera.org Sender: "libcamera-devel" The ColorSpace class is added to the StreamConfiguration, and is now passed to V4L2 devices where it is handled appropriately. Note how this means that the colour space is configured per-stream (though platforms may restrict this). --- include/libcamera/internal/v4l2_videodevice.h | 2 + include/libcamera/stream.h | 3 + src/libcamera/v4l2_videodevice.cpp | 112 ++++++++++++++++++ 3 files changed, 117 insertions(+) diff --git a/include/libcamera/internal/v4l2_videodevice.h b/include/libcamera/internal/v4l2_videodevice.h index 7938343b..5122d422 100644 --- a/include/libcamera/internal/v4l2_videodevice.h +++ b/include/libcamera/internal/v4l2_videodevice.h @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -162,6 +163,7 @@ public: V4L2PixelFormat fourcc; Size size; + ColorSpace colorSpace; std::array planes; unsigned int planesCount = 0; diff --git a/include/libcamera/stream.h b/include/libcamera/stream.h index bb47c390..f79c6987 100644 --- a/include/libcamera/stream.h +++ b/include/libcamera/stream.h @@ -13,6 +13,7 @@ #include #include +#include #include #include @@ -47,6 +48,8 @@ struct StreamConfiguration { unsigned int bufferCount; + ColorSpace colorSpace; + Stream *stream() const { return stream_; } void setStream(Stream *stream) { stream_ = stream; } const StreamFormats &formats() const { return formats_; } diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 12c09dc7..d606f81d 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -730,6 +730,114 @@ int V4L2VideoDevice::getFormat(V4L2DeviceFormat *format) return getFormatSingleplane(format); } +static const std::vector> colorSpaceToV4l2 = { + { ColorSpace::RAW, V4L2_COLORSPACE_RAW }, + { ColorSpace::JFIF, V4L2_COLORSPACE_JPEG }, + { ColorSpace::SMPTE170M, V4L2_COLORSPACE_SMPTE170M }, + { ColorSpace::REC709, V4L2_COLORSPACE_REC709 }, + { ColorSpace::REC2020, V4L2_COLORSPACE_BT2020 }, +}; + +static const std::map encodingToV4l2 = { + { ColorSpace::Encoding::REC601, V4L2_YCBCR_ENC_601 }, + { ColorSpace::Encoding::REC709, V4L2_YCBCR_ENC_709 }, + { ColorSpace::Encoding::REC2020, V4L2_YCBCR_ENC_BT2020 }, +}; + +static const std::map transferFunctionToV4l2 = { + { ColorSpace::TransferFunction::IDENTITY, V4L2_XFER_FUNC_NONE }, + { ColorSpace::TransferFunction::SRGB, V4L2_XFER_FUNC_SRGB }, + { ColorSpace::TransferFunction::REC709, V4L2_XFER_FUNC_709 }, +}; + +static const std::map rangeToV4l2 = { + { ColorSpace::Range::FULL, V4L2_QUANTIZATION_FULL_RANGE }, + { ColorSpace::Range::LIMITED, V4L2_QUANTIZATION_LIM_RANGE }, +}; + +template +static void setColorSpace(const ColorSpace &colorSpace, T &v4l2PixFormat) +{ + if (!colorSpace.isFullyDefined()) + LOG(V4L2, Warning) << "Setting non-fully defined colour space" + << colorSpace.toString(); + + v4l2PixFormat.colorspace = V4L2_COLORSPACE_DEFAULT; + v4l2PixFormat.ycbcr_enc = V4L2_YCBCR_ENC_DEFAULT; + v4l2PixFormat.xfer_func = V4L2_XFER_FUNC_DEFAULT; + v4l2PixFormat.quantization = V4L2_QUANTIZATION_DEFAULT; + + auto it_color = std::find_if(colorSpaceToV4l2.begin(), colorSpaceToV4l2.end(), + [&colorSpace](const std::pair &item) { return colorSpace == item.first; }); + if (it_color != colorSpaceToV4l2.end()) + v4l2PixFormat.colorspace = it_color->second; + + auto it_encoding = encodingToV4l2.find(colorSpace.encoding); + if (it_encoding != encodingToV4l2.end()) + v4l2PixFormat.ycbcr_enc = it_encoding->second; + + auto it_transfer = transferFunctionToV4l2.find(colorSpace.transferFunction); + if (it_transfer != transferFunctionToV4l2.end()) + v4l2PixFormat.xfer_func = it_transfer->second; + + auto it_range = rangeToV4l2.find(colorSpace.range); + if (it_range != rangeToV4l2.end()) + v4l2PixFormat.quantization = it_range->second; +} + +static const std::map v4l2ToColorSpace = { + { V4L2_COLORSPACE_RAW, ColorSpace::RAW }, + { V4L2_COLORSPACE_JPEG, ColorSpace::JFIF }, + { V4L2_COLORSPACE_SRGB, ColorSpace::JFIF }, + { V4L2_COLORSPACE_SMPTE170M, ColorSpace::SMPTE170M }, + { V4L2_COLORSPACE_REC709, ColorSpace::REC709 }, + { V4L2_COLORSPACE_BT2020, ColorSpace::REC2020 }, +}; + +static const std::map v4l2ToEncoding = { + { V4L2_YCBCR_ENC_601, ColorSpace::Encoding::REC601 }, + { V4L2_YCBCR_ENC_709, ColorSpace::Encoding::REC709 }, + { V4L2_YCBCR_ENC_BT2020, ColorSpace::Encoding::REC2020 }, +}; + +static const std::map v4l2ToTransferFunction = { + { V4L2_XFER_FUNC_NONE, ColorSpace::TransferFunction::IDENTITY }, + { V4L2_XFER_FUNC_SRGB, ColorSpace::TransferFunction::SRGB }, + { V4L2_XFER_FUNC_709, ColorSpace::TransferFunction::REC709 }, +}; + +static const std::map v4l2ToRange = { + { V4L2_QUANTIZATION_FULL_RANGE, ColorSpace::Range::FULL }, + { V4L2_QUANTIZATION_LIM_RANGE, ColorSpace::Range::LIMITED }, +}; + +template +ColorSpace getColorSpace(const T &v4l2PixFormat) +{ + ColorSpace colorSpace; + + auto it_color = v4l2ToColorSpace.find(v4l2PixFormat.colorspace); + if (it_color != v4l2ToColorSpace.end()) + colorSpace = it_color->second; + + auto it_encoding = v4l2ToEncoding.find(v4l2PixFormat.ycbcr_enc); + if (it_encoding != v4l2ToEncoding.end()) + colorSpace.encoding = it_encoding->second; + + auto it_transfer = v4l2ToTransferFunction.find(v4l2PixFormat.xfer_func); + if (it_transfer != v4l2ToTransferFunction.end()) + colorSpace.transferFunction = it_transfer->second; + + auto it_range = v4l2ToRange.find(v4l2PixFormat.quantization); + if (it_range != v4l2ToRange.end()) + colorSpace.range = it_range->second; + + if (!colorSpace.isFullyDefined()) + LOG(V4L2, Warning) << "Returning non-fully defined colour space" + << colorSpace.toString(); + return colorSpace; +} + /** * \brief Try an image format on the V4L2 video device * \param[inout] format The image format to test applicability to the video device @@ -839,6 +947,7 @@ int V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format) format->size.width = pix->width; format->size.height = pix->height; format->fourcc = V4L2PixelFormat(pix->pixelformat); + format->colorSpace = getColorSpace(*pix); format->planesCount = pix->num_planes; for (unsigned int i = 0; i < format->planesCount; ++i) { @@ -861,6 +970,7 @@ int V4L2VideoDevice::trySetFormatMultiplane(V4L2DeviceFormat *format, bool set) pix->pixelformat = format->fourcc; pix->num_planes = format->planesCount; pix->field = V4L2_FIELD_NONE; + setColorSpace(format->colorSpace, *pix); ASSERT(pix->num_planes <= std::size(pix->plane_fmt)); @@ -909,6 +1019,7 @@ int V4L2VideoDevice::getFormatSingleplane(V4L2DeviceFormat *format) format->size.width = pix->width; format->size.height = pix->height; format->fourcc = V4L2PixelFormat(pix->pixelformat); + format->colorSpace = getColorSpace(*pix); format->planesCount = 1; format->planes[0].bpl = pix->bytesperline; format->planes[0].size = pix->sizeimage; @@ -928,6 +1039,7 @@ int V4L2VideoDevice::trySetFormatSingleplane(V4L2DeviceFormat *format, bool set) pix->pixelformat = format->fourcc; pix->bytesperline = format->planes[0].bpl; pix->field = V4L2_FIELD_NONE; + setColorSpace(format->colorSpace, *pix); ret = ioctl(set ? VIDIOC_S_FMT : VIDIOC_TRY_FMT, &v4l2Format); if (ret) { LOG(V4L2, Error)