{"id":9432,"url":"https://patchwork.libcamera.org/api/1.1/patches/9432/?format=json","web_url":"https://patchwork.libcamera.org/patch/9432/","project":{"id":1,"url":"https://patchwork.libcamera.org/api/1.1/projects/1/?format=json","name":"libcamera","link_name":"libcamera","list_id":"libcamera_core","list_email":"libcamera-devel@lists.libcamera.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20200829115429.30010-7-david.plowman@raspberrypi.com>","date":"2020-08-29T11:54:27","name":"[libcamera-devel,v5,6/8] libcamera: raspberrypi: Set camera flips correctly from user transform","commit_ref":null,"pull_url":null,"state":"accepted","archived":false,"hash":"1c90f7755992e8abd5f4fa1b13ca7a770d774998","submitter":{"id":42,"url":"https://patchwork.libcamera.org/api/1.1/people/42/?format=json","name":"David Plowman","email":"david.plowman@raspberrypi.com"},"delegate":null,"mbox":"https://patchwork.libcamera.org/patch/9432/mbox/","series":[{"id":1252,"url":"https://patchwork.libcamera.org/api/1.1/series/1252/?format=json","web_url":"https://patchwork.libcamera.org/project/libcamera/list/?series=1252","date":"2020-08-29T11:54:21","name":"2D transforms","version":5,"mbox":"https://patchwork.libcamera.org/series/1252/mbox/"}],"comments":"https://patchwork.libcamera.org/api/patches/9432/comments/","check":"pending","checks":"https://patchwork.libcamera.org/api/patches/9432/checks/","tags":{},"headers":{"Return-Path":"<libcamera-devel-bounces@lists.libcamera.org>","X-Original-To":"parsemail@patchwork.libcamera.org","Delivered-To":"parsemail@patchwork.libcamera.org","Received":["from lancelot.ideasonboard.com (lancelot.ideasonboard.com\n\t[92.243.16.209])\n\tby patchwork.libcamera.org (Postfix) with ESMTPS id 2CE7CBF019\n\tfor <parsemail@patchwork.libcamera.org>;\n\tSat, 29 Aug 2020 11:54:43 +0000 (UTC)","from lancelot.ideasonboard.com (localhost [IPv6:::1])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTP id EDFC76293F;\n\tSat, 29 Aug 2020 13:54:42 +0200 (CEST)","from mail-wr1-x42b.google.com (mail-wr1-x42b.google.com\n\t[IPv6:2a00:1450:4864:20::42b])\n\tby lancelot.ideasonboard.com (Postfix) with ESMTPS id BD93C62920\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 29 Aug 2020 13:54:39 +0200 (CEST)","by mail-wr1-x42b.google.com with SMTP id b18so1552013wrs.7\n\tfor <libcamera-devel@lists.libcamera.org>;\n\tSat, 29 Aug 2020 04:54:39 -0700 (PDT)","from pi4-davidp.lan (plowpeople3.plus.com. [80.229.223.72])\n\tby smtp.gmail.com with ESMTPSA id\n\tv16sm3071523wmj.14.2020.08.29.04.54.38\n\t(version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n\tSat, 29 Aug 2020 04:54:38 -0700 (PDT)"],"Authentication-Results":"lancelot.ideasonboard.com;\n\tdkim=fail reason=\"signature verification failed\" (2048-bit key;\n\tunprotected) header.d=raspberrypi.com header.i=@raspberrypi.com\n\theader.b=\"l4pIK0dQ\"; dkim-atps=neutral","DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=raspberrypi.com; s=google;\n\th=from:to:cc:subject:date:message-id:in-reply-to:references\n\t:mime-version:content-transfer-encoding;\n\tbh=zDr2YMtYWCg4u86sRPc68xnTPi105UzxUzv42mZx7uw=;\n\tb=l4pIK0dQ5OOYP6lifyWPIpOQCAr3S1cVqBxOvzhArCLB43ROPF1upMRR1uo0g8BY5N\n\teQisYiExZpB0YlbNIezjxIDn6CK+4ZclNktb0YSSq9XsFooNjUDFTqlFBKEN9gkFVqTY\n\tcndBcfs0eJB3MVXGvhDgnxhbSpQ7vwUNsmeyJ1qPf6Zg34wnv2mj3esGxvoWPNIf04Yy\n\tB6GfkCK2m0c49o9VLGUNB5SAnZD0KdaNh0tuHdfKsHRyJrTG74sgwtJzgluQobGa41MW\n\tiM0SjO2Lt2Q9RgcMJGuL61+xAxFmRBpN9b2qxu2zNUptA2Fr1mxKlWOlN4JxDieB4Sz5\n\tx39A==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n\td=1e100.net; s=20161025;\n\th=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to\n\t:references:mime-version:content-transfer-encoding;\n\tbh=zDr2YMtYWCg4u86sRPc68xnTPi105UzxUzv42mZx7uw=;\n\tb=cp0cJyJDpo9iXXTGKwMkGqwFcf4Z+6MG10g4gUTjSrXEicXA7dvNevXvsoFiqF7ySl\n\tquncB3bpDyJFqwRzuJuFHa3JFfOPrt2cZxu7CTztFtJN9UsN+7wvuHzHluZLSnBvQvut\n\twC8/TFGezBxFsAKYWqW9xVImcFVL2sRRssmubRn90Ki6R1TItZyA+ZRNirugYixZ6cKF\n\tyTWpGpRfp8hYywklzM6X09Y4qSkml3rxmcK6PtlTFkZP2OEAs0yMy6voixwQMIXqj0DD\n\tKEDvuYsXGSf05GwwScrxLM8R23D/snOjNoqk5gQeuJozfCEBZlgSvswgRqMzlk8jRgBr\n\ta7kg==","X-Gm-Message-State":"AOAM530OrenagWmg7XfEIxdvOtb5Pk92HSV3hymzMibtBmVS8VrM0q1c\n\tSdhwfido32GTFu5TE4n7Q6rrPUgZ1zfk/g==","X-Google-Smtp-Source":"ABdhPJweY8ywZbVOribAyDsMtQj5rjI/4HeHkM5MREYB3GHV1SaQ26jaEHOc8ugrLicVItcpo1EuVA==","X-Received":"by 2002:adf:ef0a:: with SMTP id\n\te10mr1414775wro.362.1598702079089; \n\tSat, 29 Aug 2020 04:54:39 -0700 (PDT)","From":"David Plowman <david.plowman@raspberrypi.com>","To":"libcamera-devel@lists.libcamera.org","Date":"Sat, 29 Aug 2020 12:54:27 +0100","Message-Id":"<20200829115429.30010-7-david.plowman@raspberrypi.com>","X-Mailer":"git-send-email 2.20.1","In-Reply-To":"<20200829115429.30010-1-david.plowman@raspberrypi.com>","References":"<20200829115429.30010-1-david.plowman@raspberrypi.com>","MIME-Version":"1.0","Subject":"[libcamera-devel] [PATCH v5 6/8] libcamera: raspberrypi: Set camera\n\tflips correctly from user transform","X-BeenThere":"libcamera-devel@lists.libcamera.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"<libcamera-devel.lists.libcamera.org>","List-Unsubscribe":"<https://lists.libcamera.org/options/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=unsubscribe>","List-Archive":"<https://lists.libcamera.org/pipermail/libcamera-devel/>","List-Post":"<mailto:libcamera-devel@lists.libcamera.org>","List-Help":"<mailto:libcamera-devel-request@lists.libcamera.org?subject=help>","List-Subscribe":"<https://lists.libcamera.org/listinfo/libcamera-devel>,\n\t<mailto:libcamera-devel-request@lists.libcamera.org?subject=subscribe>","Content-Type":"text/plain; charset=\"us-ascii\"","Content-Transfer-Encoding":"7bit","Errors-To":"libcamera-devel-bounces@lists.libcamera.org","Sender":"\"libcamera-devel\" <libcamera-devel-bounces@lists.libcamera.org>"},"content":"The Raspberry Pi pipeline handler allows all transforms except those\ninvolving a transpose. The user transform is combined with any\ninherent rotation of the camera, and the camera's H and V flip bits\nare set accordingly.\n\nNote that the validate() method has to work out what the final Bayer\norder of any raw streams will be, before configure() actually applies\nthe transform to the sensor.\n\nSigned-off-by: David Plowman <david.plowman@raspberrypi.com>\n---\n .../pipeline/raspberrypi/raspberrypi.cpp      | 82 ++++++++++++++++---\n 1 file changed, 71 insertions(+), 11 deletions(-)","diff":"diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\nindex c554532..333aa94 100644\n--- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n+++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp\n@@ -294,7 +294,7 @@ public:\n \tvoid frameStarted(uint32_t sequence);\n \n \tint loadIPA();\n-\tint configureIPA();\n+\tint configureIPA(CameraConfiguration *config);\n \n \tvoid queueFrameAction(unsigned int frame, const IPAOperationData &action);\n \n@@ -400,8 +400,34 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()\n \tif (config_.empty())\n \t\treturn Invalid;\n \n-\tif (transform != Transform::Identity) {\n-\t\ttransform = Transform::Identity;\n+\t/*\n+\t * What if the platform has a non-90 degree rotation? We can't even\n+\t * \"adjust\" the configuration and carry on. Alternatively, raising an\n+\t * error means the platform can never run. Let's just print a warning\n+\t * and continue regardless; the rotation is effectively set to zero.\n+\t */\n+\tint32_t rotation = data_->sensor_->properties().get(properties::Rotation);\n+\tbool success;\n+\tTransform combined = transform * transformFromRotation(rotation, &success);\n+\tif (!success)\n+\t\tLOG(RPI, Warning) << \"Invalid rotation of \" << rotation\n+\t\t\t\t  << \" degrees - ignoring\";\n+\n+\t/*\n+\t * We combine the platform and user transform, but must \"adjust away\"\n+\t * any combined result that includes a transform, as we can't do those.\n+\t * In this case, flipping only the transpose bit is helpful to\n+\t * applications - they either get the transform they requested, or have\n+\t * to do a simple transpose themselves (they don't have to worry about\n+\t * the other possible cases).\n+\t */\n+\tif (!!(combined & Transform::Transpose)) {\n+\t\t/*\n+\t\t * Flipping the transpose bit in \"transform\" flips it in\n+\t\t * combined result too (as it's the last thing that happens).\n+\t\t */\n+\t\ttransform ^= Transform::Transpose;\n+\t\tcombined ^= Transform::Transpose;\n \t\tstatus = Adjusted;\n \t}\n \n@@ -414,13 +440,42 @@ CameraConfiguration::Status RPiCameraConfiguration::validate()\n \t\t\t * Calculate the best sensor mode we can use based on\n \t\t\t * the user request.\n \t\t\t */\n-\t\t\tV4L2VideoDevice::Formats fmts = data_->unicam_[Unicam::Image].dev()->formats();\n+\t\t\tV4L2VideoDevice *dev = data_->unicam_[Unicam::Image].dev();\n+\t\t\tV4L2VideoDevice::Formats fmts = dev->formats();\n \t\t\tV4L2DeviceFormat sensorFormat = findBestMode(fmts, cfg.size);\n-\t\t\tint ret = data_->unicam_[Unicam::Image].dev()->tryFormat(&sensorFormat);\n+\t\t\tint ret = dev->tryFormat(&sensorFormat);\n \t\t\tif (ret)\n \t\t\t\treturn Invalid;\n \n-\t\t\tPixelFormat sensorPixFormat = sensorFormat.fourcc.toPixelFormat();\n+\t\t\t/*\n+\t\t\t * Some sensors change their Bayer order when they are\n+\t\t\t * h-flipped or v-flipped, according to the transform.\n+\t\t\t * If the controls own up to \"modifying the layout\" we\n+\t\t\t * will assume that's what is going on and advertise\n+\t\t\t * the transformed Bayer order in the stream.\n+\t\t\t */\n+\t\t\tV4L2PixelFormat fourcc = sensorFormat.fourcc;\n+\n+\t\t\t/*\n+\t\t\t * The camera may already be transformed, so we must\n+\t\t\t * only transform the fourcc if the new transform is\n+\t\t\t * different.\n+\t\t\t */\n+\t\t\tControlList ctrls = dev->getControls({ V4L2_CID_HFLIP, V4L2_CID_VFLIP });\n+\n+\t\t\tconst struct v4l2_query_ext_ctrl *hflipCtrl = dev->queryControl(V4L2_CID_HFLIP);\n+\t\t\tif (hflipCtrl &&\n+\t\t\t    (hflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT) &&\n+\t\t\t    ctrls.get(V4L2_CID_HFLIP).get<int32_t>() != !!(combined & Transform::HFlip))\n+\t\t\t\tfourcc = fourcc.transform(Transform::HFlip);\n+\n+\t\t\tconst struct v4l2_query_ext_ctrl *vflipCtrl = dev->queryControl(V4L2_CID_VFLIP);\n+\t\t\tif (vflipCtrl &&\n+\t\t\t    (vflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT) &&\n+\t\t\t    ctrls.get(V4L2_CID_VFLIP).get<int32_t>() != !!(combined & Transform::VFlip))\n+\t\t\t\tfourcc = fourcc.transform(Transform::VFlip);\n+\n+\t\t\tPixelFormat sensorPixFormat = fourcc.toPixelFormat();\n \t\t\tif (cfg.size != sensorFormat.size ||\n \t\t\t    cfg.pixelFormat != sensorPixFormat) {\n \t\t\t\tcfg.size = sensorFormat.size;\n@@ -756,7 +811,7 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config)\n \tcrop.y = (sensorFormat.size.height - crop.height) >> 1;\n \tdata->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop);\n \n-\tret = data->configureIPA();\n+\tret = data->configureIPA(config);\n \tif (ret)\n \t\tLOG(RPI, Error) << \"Failed to configure the IPA: \" << ret;\n \n@@ -1112,7 +1167,7 @@ int RPiCameraData::loadIPA()\n \treturn ipa_->init(settings);\n }\n \n-int RPiCameraData::configureIPA()\n+int RPiCameraData::configureIPA(CameraConfiguration *config)\n {\n \tstd::map<unsigned int, IPAStream> streamConfig;\n \tstd::map<unsigned int, const ControlInfoMap &> entityControls;\n@@ -1170,11 +1225,16 @@ int RPiCameraData::configureIPA()\n \t\t\tsensorMetadata_ = result.data[2];\n \t\t}\n \n-\t\t/* Configure the H/V flip controls based on the sensor rotation. */\n+\t\t/* \n+\t\t * Configure the H/V flip controls based on the sensor rotation\n+\t\t * and user transform.\n+\t\t */\n \t\tControlList ctrls(unicam_[Unicam::Image].dev()->controls());\n \t\tint32_t rotation = sensor_->properties().get(properties::Rotation);\n-\t\tctrls.set(V4L2_CID_HFLIP, static_cast<int32_t>(!!rotation));\n-\t\tctrls.set(V4L2_CID_VFLIP, static_cast<int32_t>(!!rotation));\n+\t\t/* The rotation angle was already checked in validate(). */\n+\t\tTransform combined = config->transform * transformFromRotation(rotation);\n+\t\tctrls.set(V4L2_CID_HFLIP, static_cast<int32_t>(!!(combined & Transform::HFlip)));\n+\t\tctrls.set(V4L2_CID_VFLIP, static_cast<int32_t>(!!(combined & Transform::VFlip)));\n \t\tunicam_[Unicam::Image].dev()->setControls(&ctrls);\n \t}\n \n","prefixes":["libcamera-devel","v5","6/8"]}