summaryrefslogtreecommitdiff
path: root/src/gstreamer/gstlibcamerasrc.cpp
AgeCommit message (Collapse)Author
2022-03-29gstreamer: Fix typo in commentLaurent Pinchart
Possibly the most trivial patch. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2022-01-19gstreamer: gstlibcamerasrc: Fix include orderingKieran Bingham
The libcamera coding style has libcamera headers after system headers, and before any other library headers. Move the libcamera headers above the Gst headers accordingly. Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Umang Jain <umang.jain@ideasonboard.com>
2021-09-28gstreamer: Fix spelling of the work manager used in a util functionVedant Paranjape
Fix all name in all instances of the function gst_libcamera_get_camera_mananger to gst_libcamera_get_camera_manager. Spelling of manager was incorrect. This patch has no functional changes. Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2021-08-26gstreamer: Fix usage of default size for fixationNicolas Dufresne
Pipeline managers sets a default value to StreamConfiguration::size. The original fixation code was attempting to use it, but as it was truncating the caps to its first structure it would never actually find a best match. In this patch, instead of truncating, we weight various matches using the product of the width and height delta. We also split delta from ranges apart and prefer fixed size over them as ranges are not reliable. This patch also removes the related todo, as it seems that libcamera core won't go further then providing this default value and won't be sorting the format and size lists. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
2021-08-26libcamerasrc: Fix deadlock on EOSNicolas Dufresne
It's not allowed in GStreamer to push events while holding the object lock. This reduce the scope into which we hold the object lock. In fact we don't need to protect against gst_task_resume() concurrency when we stop the task as resume only do something if the task is paused. This fixes a deadlock when running multiple instances of libcamerasrc and closing one of the streaming window. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2021-08-26gstreamer: Fix concurrent access issues to CameraManagerNicolas Dufresne
It's not allowed to have multiple instances of CameraManager. This requirement is not easy for GStreamer were the device monitor and the camerasrc, or two camerasrc instances don't usually have any interaction between each other. Fix this by implementing a minimalist singleton around CameraManager constructor and start()/stop() operations. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Tested-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2021-08-05gstreamer: Update format specifier in Request Pad templateVedant Paranjape
Change format specifier %s to %u in name template field of request pad template. Pad names are as follows, src_0, src_1, etc. So, instead of using string format specifier, use unsigned integer format specifier. Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
2021-07-28gstreamer: Store group_id in GstLibcameraSrcStateVedant Paranjape
This patch adds group_id in GstLibcameraSrcState, since group_id is something which should be same for all the pads, it can be reused later. Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
2021-06-25gstreamer: Added virtual functions needed to support request padsVedant Paranjape
This patch adds support for using request pads in libcamerasrc Gst Element. It allows a user to request multiple streams if the platform supports multistream output using libcamera. This was tested on Raspberry Pi 4B+ with a camera connected to CSI port. It can be tested by running the following command gst-launch-1.0 libcamerasrc camera-name="<camera-name-here>" name=src src.src ! queue ! videoconvert ! autovideosink src.src_0 ! queue ! videoconvert ! autovideosink Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
2021-06-07gstreamer: Add error checking in gst_libcamera_src_task_enter()Vedant Paranjape
The return value from generateConfiguration() was not checked. Only assert was added as a guard which checked if the size of the generated camera config was equal to size of roles passed to it. If the roles variable has an invalid/unsupported role, it will return a nullptr and then trying to access a member on a nullptr for size comparison will result in a segmentation fault. So, if the function returns a nullptr, simply push an error message on GstBus and gracefully exit. Signed-off-by: Vedant Paranjape <vedantparanjape160201@gmail.com> Reviewed-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
2021-03-15gst: Use the streams of CameraConfiguration when allocating buffersDafna Hirschfeld
Currently, when allocating buffers, the streams of the Camera object are used. Instead the streams of the CameraConfiguration object should be used. This is because the Camera object holds all available streams while the CameraConfiguration holds only the streams associated with the current configuration. Signed-off-by: Dafna Hirschfeld <dafna.hirschfeld@collabora.com> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2021-03-12libcamera: gst: Fix double-free when acquire_buffer failsMarian Cichy
If gst_buffer_pool_acquire_buffer in gst_libcamera_task_run fails, the unique_ptr to the request-object gets reset and hence, its destructor is called. However, the wrap-object points to the same object and is still alive at this moment. When the task_run-function is finished, the destructor of the wrap-object is called, which in return calls the destructor of the request-object again. Instead of taking care of both, the request and the wrap-object, we can move the request to the wrap which will then effectively take care of the request object automatically. Signed-off-by: Marian Cichy <m.cichy@pengutronix.de> Suggested-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
2020-11-07gstreamer: libcamerasrc: Delete configuration before stopping camera managerLaurent Pinchart
When closing the libcamerasrc, the reference to camera is released and the camera manager is stopped. However, the camera configuration still exists at that point, and holds a reference to the camera. This leads to a warning from the device enumerator complaining that the media devices are still in use: [1:53:48.792327560] [408] ERROR DeviceEnumerator device_enumerator.cpp:165 Removing media device /dev/media1 while still in use [1:53:48.792354022] [408] ERROR DeviceEnumerator device_enumerator.cpp:165 Removing media device /dev/media0 while still in use A crash follows when the libcamerasrc is finalized, as deleting the camera configuration will then release the last reference to the camera, which attempts to delete the camera object with deleteLater() without an event dispatcher. Fix it by deleting the camera configuration before stopping the camera manager. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Nicolas Dufresne <nicolas.dufresne@collabora.com>
2020-10-20gstreamer: Omit extra semicolonsHirokazu Honda
Macros used in gstreamer (e.g. G_DEFINE_TYPE) are functions. The end semicolons with the macros are unnecessary. Signed-off-by: Hirokazu Honda <hiroh@chromium.org> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-10-12libcamera, android, cam, gstreamer, qcam, v4l2: Reuse RequestPaul Elder
Allow reuse of the Request object by implementing reuse(). This means the applications now have the responsibility of freeing the Request objects, so make all libcamera users (cam, qcam, v4l2-compat, gstreamer, android) do so. Signed-off-by: Paul Elder <paul.elder@ideasonboard.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> Reviewed-by: Jacopo Mondi <jacopo@jmondi.org>
2020-08-25meson: Remove -Wno-unused-parameterLaurent Pinchart
We build libcamera with -Wno-unused-parameter and this doesn't cause much issue internally. However, it prevents catching unused parameters in inline functions defined in public headers. This can lead to compilation warnings for applications compiled without -Wno-unused-parameter. To catch those issues, remove -Wno-unused-parameter and fix all the related warnings with [[maybe_unused]]. Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
2020-08-05libcamera: camera: Rename name() to id()Niklas Söderlund
Rename Camera::name() to camera::id() to better describe what it represents, a unique and stable ID for the camera. While at it improve the documentation for the camera ID to describe it needs to be stable for a camera between resets of the system. Signed-off-by: Niklas Söderlund <niklas.soderlund@ragnatech.se> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Prevent src task deadlock on exhausted buffer poolJakub Adam
Allow GstLibcameraPool to notify the source when a new buffer has become available in a previously exhausted buffer pool. This can be used to resume a src task that got paused because it couldn't acquire a buffer. Without this change the src task will never resume from pause once the pool gets exhausted. To trigger the deadlock (it doesn't happen every time), run: gst-launch-1.0 libcamerasrc ! queue ! glimagesink Signed-off-by: Jakub Adam <jakub.adam@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: utils: Factor-out the task resume helperJakub Adam
Task resume will be added in the core GStreamer API in the future and we will need to call this in another location in the following patches. Signed-off-by: Jakub Adam <jakub.adam@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Add a TODO commentNicolas Dufresne
This is to guide upcoming contributors toward what is left to do to get toward a production ready element. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Implement timestamp supportNicolas Dufresne
This is an experimental patch adding timestamp support to the libcamerasrc element. This patch currently assume that the driver timestamp are relative to the system monotonic clock. Without a reference clock source, the timestamp are otherwise unusable, and without timestamp only minor use case can be achieved. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Implement initial streamingNicolas Dufresne
With this patch, the element is now able to push buffers to the next element in the graph. The buffers are currently missing any metadata like timestamp, sequence number. This will be added in the next commit. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Allocate and release buffersNicolas Dufresne
Setup the allocation and the release of buffers in the element. We have one pooling GstAllocator that wraps the FrameBufferAllocator and tracks the lifetime of FrameBuffer objects. Then, for each pad we have a GstBufferPool object which is only used to avoid re-allocating the GstBuffer structure every time we push a buffer. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Push segment eventNicolas Dufresne
Now that we have stream-start and caps, we can now push a segment event to announce what time will our buffer correlate to. For live sources this is just an open segment in time format. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Implement minimal caps negotiationNicolas Dufresne
This is not expected to work in every possible cases, but should be sufficient as an initial implementation. What it does is that it turns the StreamFormats into caps and queries downstream caps with that as a filter. The result is the subset of caps that can be used. We then keep the first structure in that result and fixate using the default values found in StreamConfiguration as a default in case a range is available. We then validate this configuration and turn the potentially modified configuration into caps that we push downstream. Note that we trust the order in StreamFormats as being sorted best first, but this is not currently in libcamera. A todo has been added in the head of this file as a reminder to fix that in the core. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Send stream start eventNicolas Dufresne
Prior to sending caps, we need to send a stream-start event. This requires generating a stream and a group id. The stream id is random for live sources and the group id is shared across all pads. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Store the srcpad in a vectorNicolas Dufresne
This will allow implementing generic algorithm even if we cannot request pads yet. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Add a task for the streaming threadNicolas Dufresne
Use a GstTask as our internal streaming thread. Unlike GstBaseSrc, we will be running a streaming thread at the element level rather than per pad. This is needed to combine buffer request for multiple pads. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Implement selection and acquisitionNicolas Dufresne
This adds code to select and acquire a camera. With this, it is now possible to run a pipeline like: gst-launch-1.0 libcamerasrc ! fakesink Though no buffer will be streamed yet. In this function, we implement the change_state() virtual method to trigger actions on specific state transitions. Note that we also return GST_STATE_CHANGE_NO_PREROLL in GST_STATE_CHANGE_READY_TO_PAUSED and GST_STATE_CHANGE_PLAYING_TO_PAUSED transitions as this is required for all live sources. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Add a debug categoryNicolas Dufresne
This will allow selecting libcamerasrc traces with the following environment: GST_DEBUG=libcamerasrc:7 Or all libcamera GStreamer element traces using GST_DEBUG="libcamera*:7" Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Add camera-name propertyNicolas Dufresne
This property will be used to select by name the camera to use. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: libcamerasrc: Allocate and add static padNicolas Dufresne
This pad will always be present and will allow simple pipeline to be used to stream from the camera. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07gst: Add pads to the sourceNicolas Dufresne
This simply adds the boiler plate for pads on the source element. The design is that we have one pad, called "src", that will always be present, and then more pads can be requested prior in READY or less state. Initially pads have one property "stream-role" that let you decide which role this pad will have. Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
2020-03-07Add GStreamer plugin and element skeletonNicolas Dufresne
This implements the GStreamer plugin interface and adds libcamerasrc element feature to it. This is just enough to allow plugin introspection. gst-inspect-1.0 build/src/gstreamer/libgstlibcamera.so Plugin Details: Name libcamera Description libcamera capture plugin Filename build/src/gstreamer/libgstlibcamera.so Version 0.0.0+1042-6c9f16d3-dirty License LGPL Source module libcamera Binary package libcamera Origin URL https://libcamera.org libcamerasrc: libcamera Source 1 features: GST_PLUGIN_PATH=$(pwd)/build/src/gstreamer gst-inspect-1.0 libcamerasrc Factory Details: Rank primary (256) Long-name libcamera Source Klass Source/Video Description Linux Camera source using libcamera Author Nicolas Dufresne <nicolas.dufresne@collabora.com Plugin Details: Name libcamera Description libcamera capture plugin Filename /home/nicolas/Sources/libcamera/build/src/gstreamer/libgstlibcamera.so Version 0.0.0+1042-6c9f16d3-dirty License LGPL Source module libcamera Binary package libcamera Origin URL https://libcamera.org GObject +----GInitiallyUnowned +----GstObject +----GstElement +----GstLibcameraSrc Pad Templates: none Element has no clocking capabilities. Element has no URI handling capabilities. Pads: none Element Properties: name : The name of the object flags: accès en lecture, accès en écriture, 0x2000 String. Default: "libcamerasrc0" parent : The parent of the object flags: accès en lecture, accès en écriture, 0x2000 Object of type "GstObject" Signed-off-by: Nicolas Dufresne <nicolas.dufresne@collabora.com> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com> [Silence -Wunused-function warning for older GLib versions] Signed-off-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
SOR: case MEDIA_ENT_F_PROC_VIDEO_ISP: break; default: LOG(CameraSensor, Error) << "Invalid sensor function " << utils::hex(entity_->function()); return -EINVAL; } /* Create and open the subdev. */ subdev_ = std::make_unique<V4L2Subdevice>(entity_); int ret = subdev_->open(); if (ret < 0) return ret; /* Enumerate, sort and cache media bus codes and sizes. */ formats_ = subdev_->formats(pad_); if (formats_.empty()) { LOG(CameraSensor, Error) << "No image format found"; return -EINVAL; } mbusCodes_ = utils::map_keys(formats_); std::sort(mbusCodes_.begin(), mbusCodes_.end()); for (const auto &format : formats_) { const std::vector<SizeRange> &ranges = format.second; std::transform(ranges.begin(), ranges.end(), std::back_inserter(sizes_), [](const SizeRange &range) { return range.max; }); } std::sort(sizes_.begin(), sizes_.end()); /* Remove duplicates. */ auto last = std::unique(sizes_.begin(), sizes_.end()); sizes_.erase(last, sizes_.end()); /* * VIMC is a bit special, as it does not yet support all the mandatory * requirements regular sensors have to respect. * * Do not validate the driver if it's VIMC and initialize the sensor * properties with static information. * * \todo Remove the special case once the VIMC driver has been * updated in all test platforms. */ if (entity_->device()->driver() == "vimc") { initVimcDefaultProperties(); return initProperties(); } /* Get the color filter array pattern (only for RAW sensors). */ for (unsigned int mbusCode : mbusCodes_) { const BayerFormat &bayerFormat = BayerFormat::fromMbusCode(mbusCode); if (bayerFormat.isValid()) { bayerFormat_ = &bayerFormat; break; } } ret = validateSensorDriver(); if (ret) return ret; ret = initProperties(); if (ret) return ret; return 0; } int CameraSensor::validateSensorDriver() { int err = 0; /* * Optional controls are used to register optional sensor properties. If * not present, some values will be defaulted. */ static constexpr uint32_t optionalControls[] = { V4L2_CID_CAMERA_SENSOR_ROTATION, }; const ControlIdMap &controls = subdev_->controls().idmap(); for (uint32_t ctrl : optionalControls) { if (!controls.count(ctrl)) LOG(CameraSensor, Debug) << "Optional V4L2 control " << utils::hex(ctrl) << " not supported"; } /* * Recommended controls are similar to optional controls, but will * become mandatory in the near future. Be loud if they're missing. */ static constexpr uint32_t recommendedControls[] = { V4L2_CID_CAMERA_ORIENTATION, }; for (uint32_t ctrl : recommendedControls) { if (!controls.count(ctrl)) { LOG(CameraSensor, Warning) << "Recommended V4L2 control " << utils::hex(ctrl) << " not supported"; err = -EINVAL; } } /* * Make sure the required selection targets are supported. * * Failures in reading any of the targets are not deemed to be fatal, * but some properties and features, like constructing a * IPACameraSensorInfo for the IPA module, won't be supported. * * \todo Make support for selection targets mandatory as soon as all * test platforms have been updated. */ Rectangle rect; int ret = subdev_->getSelection(pad_, V4L2_SEL_TGT_CROP_BOUNDS, &rect); if (ret) { /* * Default the pixel array size to the largest size supported * by the sensor. The sizes_ vector is sorted in ascending * order, the largest size is thus the last element. */ pixelArraySize_ = sizes_.back(); LOG(CameraSensor, Warning) << "The PixelArraySize property has been defaulted to " << pixelArraySize_.toString(); err = -EINVAL; } else { pixelArraySize_ = rect.size(); } ret = subdev_->getSelection(pad_, V4L2_SEL_TGT_CROP_DEFAULT, &activeArea_); if (ret) { activeArea_ = Rectangle(pixelArraySize_); LOG(CameraSensor, Warning) << "The PixelArrayActiveAreas property has been defaulted to " << activeArea_.toString(); err = -EINVAL; } ret = subdev_->getSelection(pad_, V4L2_SEL_TGT_CROP, &rect); if (ret) { LOG(CameraSensor, Warning) << "Failed to retrieve the sensor crop rectangle"; err = -EINVAL; } if (err) { LOG(CameraSensor, Warning) << "The sensor kernel driver needs to be fixed"; LOG(CameraSensor, Warning) << "See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information"; } if (!bayerFormat_) return 0; /* * For raw sensors, make sure the sensor driver supports the controls * required by the CameraSensor class. */ static constexpr uint32_t mandatoryControls[] = { V4L2_CID_EXPOSURE, V4L2_CID_HBLANK, V4L2_CID_PIXEL_RATE, V4L2_CID_VBLANK, }; err = 0; for (uint32_t ctrl : mandatoryControls) { if (!controls.count(ctrl)) { LOG(CameraSensor, Error) << "Mandatory V4L2 control " << utils::hex(ctrl) << " not available"; err = -EINVAL; } } if (err) { LOG(CameraSensor, Error) << "The sensor kernel driver needs to be fixed"; LOG(CameraSensor, Error) << "See Documentation/sensor_driver_requirements.rst in the libcamera sources for more information"; return err; } return 0; } /* * \brief Initialize properties that cannot be intialized by the * regular initProperties() function for VIMC */ void CameraSensor::initVimcDefaultProperties() { /* Use the largest supported size. */ pixelArraySize_ = sizes_.back(); activeArea_ = Rectangle(pixelArraySize_); } void CameraSensor::initStaticProperties() { const CameraSensorProperties *props = CameraSensorProperties::get(model_); if (!props) return; /* Register the properties retrieved from the sensor database. */ properties_.set(properties::UnitCellSize, props->unitCellSize); initTestPatternModes(props->testPatternModes); } void CameraSensor::initTestPatternModes( const std::map<int32_t, int32_t> &testPatternModes) { const auto &v4l2TestPattern = controls().find(V4L2_CID_TEST_PATTERN); if (v4l2TestPattern == controls().end()) { LOG(CameraSensor, Debug) << "No static test pattern map for \'" << model() << "\'"; return; } for (const ControlValue &value : v4l2TestPattern->second.values()) { const int32_t index = value.get<int32_t>(); const auto it = testPatternModes.find(index); if (it == testPatternModes.end()) { LOG(CameraSensor, Debug) << "Test pattern mode " << index << " ignored"; continue; } testPatternModes_.push_back(it->second); } } int CameraSensor::initProperties() { /* * Extract the camera sensor model name from the media entity name. * * There is no standardized naming scheme for sensor entities in the * Linux kernel at the moment. * * - The most common rule, used by I2C sensors, associates the model * name with the I2C bus number and address (e.g. 'imx219 0-0010'). * * - When the sensor exposes multiple subdevs, the model name is * usually followed by a function name, as in the smiapp driver (e.g. * 'jt8ew9 pixel_array 0-0010'). * * - The vimc driver names its sensors 'Sensor A' and 'Sensor B'. * * Other schemes probably exist. As a best effort heuristic, use the * part of the entity name before the first space if the name contains * an I2C address, and use the full entity name otherwise. */ std::string entityName = entity_->name(); std::regex i2cRegex{ " [0-9]+-[0-9a-f]{4}" }; std::smatch match; if (std::regex_search(entityName, match, i2cRegex)) model_ = entityName.substr(0, entityName.find(' ')); else model_ = entityName; properties_.set(properties::Model, utils::toAscii(model_)); /* Generate a unique ID for the sensor. */ int ret = generateId(); if (ret) return ret; /* Initialize the static properties from the sensor database. */ initStaticProperties(); /* Retrieve and register properties from the kernel interface. */ const ControlInfoMap &controls = subdev_->controls(); int32_t propertyValue; const auto &orientation = controls.find(V4L2_CID_CAMERA_ORIENTATION); if (orientation != controls.end()) { int32_t v4l2Orientation = orientation->second.def().get<int32_t>(); switch (v4l2Orientation) { default: LOG(CameraSensor, Warning) << "Unsupported camera location " << v4l2Orientation << ", setting to External"; /* Fall-through */ case V4L2_CAMERA_ORIENTATION_EXTERNAL: propertyValue = properties::CameraLocationExternal; break; case V4L2_CAMERA_ORIENTATION_FRONT: propertyValue = properties::CameraLocationFront; break; case V4L2_CAMERA_ORIENTATION_BACK: propertyValue = properties::CameraLocationBack; break; } properties_.set(properties::Location, propertyValue); } else { LOG(CameraSensor, Warning) << "Failed to retrieve the camera location"; } const auto &rotationControl = controls.find(V4L2_CID_CAMERA_SENSOR_ROTATION); if (rotationControl != controls.end()) { propertyValue = rotationControl->second.def().get<int32_t>(); properties_.set(properties::Rotation, propertyValue); } properties_.set(properties::PixelArraySize, pixelArraySize_); properties_.set(properties::PixelArrayActiveAreas, { activeArea_ }); /* Color filter array pattern, register only for RAW sensors. */ if (bayerFormat_) { int32_t cfa; switch (bayerFormat_->order) { case BayerFormat::BGGR: cfa = properties::draft::BGGR; break; case BayerFormat::GBRG: cfa = properties::draft::GBRG; break; case BayerFormat::GRBG: cfa = properties::draft::GRBG; break; case BayerFormat::RGGB: cfa = properties::draft::RGGB; break; case BayerFormat::MONO: cfa = properties::draft::MONO; break; } properties_.set(properties::draft::ColorFilterArrangement, cfa); } return 0; } /** * \fn CameraSensor::model() * \brief Retrieve the sensor model name * * The sensor model name is a free-formed string that uniquely identifies the * sensor model. * * \return The sensor model name */ /** * \fn CameraSensor::id() * \brief Retrieve the sensor ID * * The sensor ID is a free-form string that uniquely identifies the sensor in * the system. The ID satisfies the requirements to be used as a camera ID. * * \return The sensor ID */ /** * \fn CameraSensor::entity() * \brief Retrieve the sensor media entity * \return The sensor media entity */ /** * \fn CameraSensor::mbusCodes() * \brief Retrieve the media bus codes supported by the camera sensor * \return The supported media bus codes sorted in increasing order */ /** * \fn CameraSensor::sizes() * \brief Retrieve the frame sizes supported by the camera sensor * * The reported sizes span all media bus codes supported by the camera sensor. * Not all sizes may be supported by all media bus codes. * * \return The supported frame sizes sorted in increasing order */ /** * \brief Retrieve the camera sensor resolution * * The camera sensor resolution is the active pixel area size, clamped to the * maximum frame size the sensor can produce if it is smaller than the active * pixel area. * * \todo Consider if it desirable to distinguish between the maximum resolution * the sensor can produce (also including upscaled ones) and the actual pixel * array size by splitting this method in two. * * \return The camera sensor resolution in pixels */ Size CameraSensor::resolution() const { return std::min(sizes_.back(), activeArea_.size()); } /** * \fn CameraSensor::testPatternModes() * \brief Retrieve all the supported test pattern modes of the camera sensor * The test pattern mode values correspond to the controls::TestPattern control. * * \return The list of test pattern modes */ /** * \brief Retrieve the best sensor format for a desired output * \param[in] mbusCodes The list of acceptable media bus codes * \param[in] size The desired size * * Media bus codes are selected from \a mbusCodes, which lists all acceptable * codes in decreasing order of preference. Media bus codes supported by the * sensor but not listed in \a mbusCodes are ignored. If none of the desired * codes is supported, it returns an error. * * \a size indicates the desired size at the output of the sensor. This method * selects the best media bus code and size supported by the sensor according * to the following criteria. * * - The desired \a size shall fit in the sensor output size to avoid the need * to up-scale. * - The sensor output size shall match the desired aspect ratio to avoid the * need to crop the field of view. * - The sensor output size shall be as small as possible to lower the required * bandwidth. * - The desired \a size shall be supported by one of the media bus code listed * in \a mbusCodes. * * When multiple media bus codes can produce the same size, the code at the * lowest position in \a mbusCodes is selected. * * The use of this method is optional, as the above criteria may not match the * needs of all pipeline handlers. Pipeline handlers may implement custom * sensor format selection when needed. * * The returned sensor output format is guaranteed to be acceptable by the * setFormat() method without any modification. * * \return The best sensor output format matching the desired media bus codes * and size on success, or an empty format otherwise. */ V4L2SubdeviceFormat CameraSensor::getFormat(const std::vector<unsigned int> &mbusCodes, const Size &size) const { unsigned int desiredArea = size.width * size.height; unsigned int bestArea = UINT_MAX; float desiredRatio = static_cast<float>(size.width) / size.height; float bestRatio = FLT_MAX; const Size *bestSize = nullptr; uint32_t bestCode = 0; for (unsigned int code : mbusCodes) { const auto formats = formats_.find(code); if (formats == formats_.end()) continue; for (const SizeRange &range : formats->second) { const Size &sz = range.max; if (sz.width < size.width || sz.height < size.height) continue; float ratio = static_cast<float>(sz.width) / sz.height; float ratioDiff = fabsf(ratio - desiredRatio); unsigned int area = sz.width * sz.height; unsigned int areaDiff = area - desiredArea; if (ratioDiff > bestRatio) continue; if (ratioDiff < bestRatio || areaDiff < bestArea) { bestRatio = ratioDiff; bestArea = areaDiff; bestSize = &sz; bestCode = code; } } } if (!bestSize) { LOG(CameraSensor, Debug) << "No supported format or size found"; return {}; } V4L2SubdeviceFormat format{ .mbus_code = bestCode, .size = *bestSize, }; return format; } /** * \brief Set the sensor output format * \param[in] format The desired sensor output format * * The ranges of any controls associated with the sensor are also updated. * * \return 0 on success or a negative error code otherwise */ int CameraSensor::setFormat(V4L2SubdeviceFormat *format) { int ret = subdev_->setFormat(pad_, format); if (ret) return ret; updateControlInfo(); return 0; } /** * \brief Retrieve the supported V4L2 controls and their information * * Control information is updated automatically to reflect the current sensor * configuration when the setFormat() function is called, without invalidating * any iterator on the ControlInfoMap. A manual update can also be forced by * calling the updateControlInfo() function for pipeline handlers that change * the sensor configuration wihtout using setFormat(). * * \return A map of the V4L2 controls supported by the sensor */ const ControlInfoMap &CameraSensor::controls() const { return subdev_->controls(); } /** * \brief Read V4L2 controls from the sensor * \param[in] ids The list of controls to read, specified by their ID * * This method reads the value of all controls contained in \a ids, and returns * their values as a ControlList. The control identifiers are defined by the * V4L2 specification (V4L2_CID_*). * * If any control in \a ids is not supported by the device, is disabled (i.e. * has the V4L2_CTRL_FLAG_DISABLED flag set), or if any other error occurs * during validation of the requested controls, no control is read and this * method returns an empty control list. * * \sa V4L2Device::getControls() * * \return The control values in a ControlList on success, or an empty list on * error */ ControlList CameraSensor::getControls(const std::vector<uint32_t> &ids) { return subdev_->getControls(ids); } /** * \brief Write V4L2 controls to the sensor * \param[in] ctrls The list of controls to write * * This method writes the value of all controls contained in \a ctrls, and * stores the values actually applied to the device in the corresponding \a * ctrls entry. The control identifiers are defined by the V4L2 specification * (V4L2_CID_*). * * If any control in \a ctrls is not supported by the device, is disabled (i.e. * has the V4L2_CTRL_FLAG_DISABLED flag set), is read-only, or if any other * error occurs during validation of the requested controls, no control is * written and this method returns -EINVAL. * * If an error occurs while writing the controls, the index of the first * control that couldn't be written is returned. All controls below that index * are written and their values are updated in \a ctrls, while all other * controls are not written and their values are not changed. * * \sa V4L2Device::setControls() * * \return 0 on success or an error code otherwise * \retval -EINVAL One of the control is not supported or not accessible * \retval i The index of the control that failed */ int CameraSensor::setControls(ControlList *ctrls) { return subdev_->setControls(ctrls); } /** * \fn CameraSensor::device() * \brief Retrieve the camera sensor device * \todo Remove this function by integrating DelayedControl with CameraSensor * \return The camera sensor device */ /** * \fn CameraSensor::properties() * \brief Retrieve the camera sensor properties * \return The list of camera sensor properties */ /** * \brief Assemble and return the camera sensor info * \param[out] info The camera sensor info * * This function fills \a info with information that describes the camera sensor * and its current configuration. The information combines static data (such as * the the sensor model or active pixel array size) and data specific to the * current sensor configuration (such as the line length and pixel rate). * * Sensor information is only available for raw sensors. When called for a YUV * sensor, this function returns -EINVAL. * * Pipeline handlers that do not change the sensor format using the setFormat() * method may need to call updateControlInfo() beforehand, to ensure all the * control ranges are up to date. * * \return 0 on success, a negative error code otherwise */ int CameraSensor::sensorInfo(IPACameraSensorInfo *info) const { if (!bayerFormat_) return -EINVAL; info->model = model(); /* * The active area size is a static property, while the crop * rectangle needs to be re-read as it depends on the sensor * configuration. */ info->activeAreaSize = { activeArea_.width, activeArea_.height }; /* * \todo Support for retreiving the crop rectangle is scheduled to * become mandatory. For the time being use the default value if it has * been initialized at sensor driver validation time. */ int ret = subdev_->getSelection(pad_, V4L2_SEL_TGT_CROP, &info->analogCrop); if (ret) { info->analogCrop = activeArea_; LOG(CameraSensor, Warning) << "The analogue crop rectangle has been defaulted to the active area size"; } /* * IPACameraSensorInfo::analogCrop::x and IPACameraSensorInfo::analogCrop::y * are defined relatively to the active pixel area, while V4L2's * TGT_CROP target is defined in respect to the full pixel array. * * Compensate it by subtracting the active area offset. */ info->analogCrop.x -= activeArea_.x; info->analogCrop.y -= activeArea_.y; /* The bit depth and image size depend on the currently applied format. */ V4L2SubdeviceFormat format{}; ret = subdev_->getFormat(pad_, &format); if (ret) return ret; info->bitsPerPixel = format.bitsPerPixel(); info->outputSize = format.size; /* * Retrieve the pixel rate, line length and minimum/maximum frame * duration through V4L2 controls. Support for the V4L2_CID_PIXEL_RATE, * V4L2_CID_HBLANK and V4L2_CID_VBLANK controls is mandatory. */ ControlList ctrls = subdev_->getControls({ V4L2_CID_PIXEL_RATE, V4L2_CID_HBLANK, V4L2_CID_VBLANK }); if (ctrls.empty()) { LOG(CameraSensor, Error) << "Failed to retrieve camera info controls"; return -EINVAL; } int32_t hblank = ctrls.get(V4L2_CID_HBLANK).get<int32_t>(); info->lineLength = info->outputSize.width + hblank; info->pixelRate = ctrls.get(V4L2_CID_PIXEL_RATE).get<int64_t>(); const ControlInfo vblank = ctrls.infoMap()->at(V4L2_CID_VBLANK); info->minFrameLength = info->outputSize.height + vblank.min().get<int32_t>(); info->maxFrameLength = info->outputSize.height + vblank.max().get<int32_t>(); return 0; } /** * \fn void CameraSensor::updateControlInfo() * \brief Update the sensor's ControlInfoMap in case they have changed * \sa V4L2Device::updateControlInfo() */ void CameraSensor::updateControlInfo() { subdev_->updateControlInfo(); } std::string CameraSensor::logPrefix() const { return "'" + entity_->name() + "'"; } int CameraSensor::generateId() { const std::string devPath = subdev_->devicePath(); /* Try to get ID from firmware description. */ id_ = sysfs::firmwareNodePath(devPath); if (!id_.empty()) return 0; /* * Virtual sensors not described in firmware * * Verify it's a platform device and construct ID from the deive path * and model of sensor. */ if (devPath.find("/sys/devices/platform/", 0) == 0) { id_ = devPath.substr(strlen("/sys/devices/")) + " " + model(); return 0; } LOG(CameraSensor, Error) << "Can't generate sensor ID"; return -EINVAL; } } /* namespace libcamera */