diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/libcamera/pipeline/raspberrypi/raspberrypi.cpp | 155 |
1 files changed, 143 insertions, 12 deletions
diff --git a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp index 36e38894..60f25864 100644 --- a/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp +++ b/src/libcamera/pipeline/raspberrypi/raspberrypi.cpp @@ -23,6 +23,7 @@ #include <linux/videodev2.h> +#include "libcamera/internal/bayer_format.h" #include "libcamera/internal/camera_sensor.h" #include "libcamera/internal/device_enumerator.h" #include "libcamera/internal/ipa_manager.h" @@ -133,6 +134,7 @@ class RPiCameraData : public CameraData public: RPiCameraData(PipelineHandler *pipe) : CameraData(pipe), sensor_(nullptr), state_(State::Stopped), + supportsFlips_(false), flipsAlterBayerOrder_(false), dropFrameCount_(0), ispOutputCount_(0) { } @@ -140,7 +142,7 @@ public: void frameStarted(uint32_t sequence); int loadIPA(); - int configureIPA(); + int configureIPA(const CameraConfiguration *config); void queueFrameAction(unsigned int frame, const IPAOperationData &action); @@ -182,6 +184,15 @@ public: std::queue<FrameBuffer *> embeddedQueue_; std::deque<Request *> requestQueue_; + /* + * Manage horizontal and vertical flips supported (or not) by the + * sensor. Also store the "native" Bayer order (that is, with no + * transforms applied). + */ + bool supportsFlips_; + bool flipsAlterBayerOrder_; + BayerFormat::Order nativeBayerOrder_; + unsigned int dropFrameCount_; private: @@ -201,6 +212,9 @@ public: Status validate() override; + /* Cache the combinedTransform_ that will be applied to the sensor */ + Transform combinedTransform_; + private: const RPiCameraData *data_; }; @@ -250,11 +264,61 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() if (config_.empty()) return Invalid; - if (transform != Transform::Identity) { - transform = Transform::Identity; + /* + * What if the platform has a non-90 degree rotation? We can't even + * "adjust" the configuration and carry on. Alternatively, raising an + * error means the platform can never run. Let's just print a warning + * and continue regardless; the rotation is effectively set to zero. + */ + int32_t rotation = data_->sensor_->properties().get(properties::Rotation); + bool success; + Transform rotationTransform = transformFromRotation(rotation, &success); + if (!success) + LOG(RPI, Warning) << "Invalid rotation of " << rotation + << " degrees - ignoring"; + Transform combined = transform * rotationTransform; + + /* + * We combine the platform and user transform, but must "adjust away" + * any combined result that includes a transform, as we can't do those. + * In this case, flipping only the transpose bit is helpful to + * applications - they either get the transform they requested, or have + * to do a simple transpose themselves (they don't have to worry about + * the other possible cases). + */ + if (!!(combined & Transform::Transpose)) { + /* + * Flipping the transpose bit in "transform" flips it in the + * combined result too (as it's the last thing that happens), + * which is of course clearing it. + */ + transform ^= Transform::Transpose; + combined &= ~Transform::Transpose; + status = Adjusted; + } + + /* + * We also check if the sensor doesn't do h/vflips at all, in which + * case we clear them, and the application will have to do everything. + */ + if (!data_->supportsFlips_ && !!combined) { + /* + * If the sensor can do no transforms, then combined must be + * changed to the identity. The only user transform that gives + * rise to this the inverse of the rotation. (Recall that + * combined = transform * rotationTransform.) + */ + transform = -rotationTransform; + combined = Transform::Identity; status = Adjusted; } + /* + * Store the final combined transform that configure() will need to + * apply to the sensor to save us working it out again. + */ + combinedTransform_ = combined; + unsigned int rawCount = 0, outCount = 0, count = 0, maxIndex = 0; std::pair<int, Size> outSize[2]; Size maxSize; @@ -270,7 +334,23 @@ CameraConfiguration::Status RPiCameraConfiguration::validate() if (ret) return Invalid; - PixelFormat sensorPixFormat = sensorFormat.fourcc.toPixelFormat(); + /* + * Some sensors change their Bayer order when they are + * h-flipped or v-flipped, according to the transform. + * If this one does, we must advertise the transformed + * Bayer order in the raw stream. Note how we must + * fetch the "native" (i.e. untransformed) Bayer order, + * because the sensor may currently be flipped! + */ + V4L2PixelFormat fourcc = sensorFormat.fourcc; + if (data_->flipsAlterBayerOrder_) { + BayerFormat bayer(fourcc); + bayer.order = data_->nativeBayerOrder_; + bayer = bayer.transform(combined); + fourcc = bayer.toV4L2PixelFormat(); + } + + PixelFormat sensorPixFormat = fourcc.toPixelFormat(); if (cfg.size != sensorFormat.size || cfg.pixelFormat != sensorPixFormat) { cfg.size = sensorFormat.size; @@ -613,7 +693,7 @@ int PipelineHandlerRPi::configure(Camera *camera, CameraConfiguration *config) crop.y = (sensorFormat.size.height - crop.height) >> 1; data->isp_[Isp::Input].dev()->setSelection(V4L2_SEL_TGT_CROP, &crop); - ret = data->configureIPA(); + ret = data->configureIPA(config); if (ret) LOG(RPI, Error) << "Failed to configure the IPA: " << ret; @@ -847,6 +927,47 @@ bool PipelineHandlerRPi::match(DeviceEnumerator *enumerator) data->properties_ = data->sensor_->properties(); /* + * We cache three things about the sensor in relation to transforms + * (meaning horizontal and vertical flips). + * + * Firstly, does it support them? + * Secondly, if you use them does it affect the Bayer ordering? + * Thirdly, what is the "native" Bayer order, when no transforms are + * applied? + * + * As part of answering the final question, we reset the camera to + * no transform at all. + */ + + V4L2VideoDevice *dev = data->unicam_[Unicam::Image].dev(); + const struct v4l2_query_ext_ctrl *hflipCtrl = dev->controlInfo(V4L2_CID_HFLIP); + if (hflipCtrl) { + /* We assume it will support vflips too... */ + data->supportsFlips_ = true; + data->flipsAlterBayerOrder_ = hflipCtrl->flags & V4L2_CTRL_FLAG_MODIFY_LAYOUT; + + ControlList ctrls(dev->controls()); + ctrls.set(V4L2_CID_HFLIP, 0); + ctrls.set(V4L2_CID_VFLIP, 0); + dev->setControls(&ctrls); + } + + /* Look for a valid Bayer format. */ + BayerFormat bayerFormat; + for (const auto &iter : dev->formats()) { + V4L2PixelFormat v4l2Format = iter.first; + bayerFormat = BayerFormat(v4l2Format); + if (bayerFormat.isValid()) + break; + } + + if (!bayerFormat.isValid()) { + LOG(RPI, Error) << "No Bayer format found"; + return false; + } + data->nativeBayerOrder_ = bayerFormat.order; + + /* * List the available streams an application may request. At present, we * do not advertise Unicam Embedded and ISP Statistics streams, as there * is no mechanism for the application to request non-image buffer formats. @@ -985,8 +1106,12 @@ int RPiCameraData::loadIPA() return ipa_->init(settings); } -int RPiCameraData::configureIPA() +int RPiCameraData::configureIPA(const CameraConfiguration *config) { + /* We know config must be an RPiCameraConfiguration. */ + const RPiCameraConfiguration *rpiConfig = + static_cast<const RPiCameraConfiguration *>(config); + std::map<unsigned int, IPAStream> streamConfig; std::map<unsigned int, const ControlInfoMap &> entityControls; IPAOperationData ipaConfig = {}; @@ -1045,12 +1170,18 @@ int RPiCameraData::configureIPA() sensorMetadata_ = result.data[resultIdx++]; } - /* Configure the H/V flip controls based on the sensor rotation. */ - ControlList ctrls(unicam_[Unicam::Image].dev()->controls()); - int32_t rotation = sensor_->properties().get(properties::Rotation); - ctrls.set(V4L2_CID_HFLIP, static_cast<int32_t>(!!rotation)); - ctrls.set(V4L2_CID_VFLIP, static_cast<int32_t>(!!rotation)); - unicam_[Unicam::Image].dev()->setControls(&ctrls); + /* + * Configure the H/V flip controls based on the combination of + * the sensor and user transform. + */ + if (supportsFlips_) { + ControlList ctrls(unicam_[Unicam::Image].dev()->controls()); + ctrls.set(V4L2_CID_HFLIP, + static_cast<int32_t>(!!(rpiConfig->combinedTransform_ & Transform::HFlip))); + ctrls.set(V4L2_CID_VFLIP, + static_cast<int32_t>(!!(rpiConfig->combinedTransform_ & Transform::VFlip))); + unicam_[Unicam::Image].dev()->setControls(&ctrls); + } } if (result.operation & RPI_IPA_CONFIG_SENSOR) { |