diff options
Diffstat (limited to 'src/libcamera/v4l2_videodevice.cpp')
-rw-r--r-- | src/libcamera/v4l2_videodevice.cpp | 114 |
1 files changed, 94 insertions, 20 deletions
diff --git a/src/libcamera/v4l2_videodevice.cpp b/src/libcamera/v4l2_videodevice.cpp index 63911339..a72ef64d 100644 --- a/src/libcamera/v4l2_videodevice.cpp +++ b/src/libcamera/v4l2_videodevice.cpp @@ -633,13 +633,9 @@ int V4L2VideoDevice::open() << "Opened device " << caps_.bus_info() << ": " << caps_.driver() << ": " << caps_.card(); - ret = getFormat(&format_); - if (ret) { - LOG(V4L2, Error) << "Failed to get format"; + ret = initFormats(); + if (ret) return ret; - } - - formatInfo_ = &PixelFormatInfo::info(format_.fourcc); return 0; } @@ -726,7 +722,24 @@ int V4L2VideoDevice::open(SharedFD handle, enum v4l2_buf_type type) << "Opened device " << caps_.bus_info() << ": " << caps_.driver() << ": " << caps_.card(); - ret = getFormat(&format_); + ret = initFormats(); + if (ret) + return ret; + + return 0; +} + +int V4L2VideoDevice::initFormats() +{ + const std::vector<V4L2PixelFormat> &deviceFormats = enumPixelformats(0); + if (deviceFormats.empty()) { + LOG(V4L2, Error) << "Failed to initialize device formats"; + return -EINVAL; + } + + pixelFormats_ = { deviceFormats.begin(), deviceFormats.end() }; + + int ret = getFormat(&format_); if (ret) { LOG(V4L2, Error) << "Failed to get format"; return ret; @@ -901,6 +914,13 @@ int V4L2VideoDevice::trySetFormatMeta(V4L2DeviceFormat *format, bool set) return 0; } +template<typename T> +std::optional<ColorSpace> V4L2VideoDevice::toColorSpace(const T &v4l2Format) +{ + V4L2PixelFormat fourcc{ v4l2Format.pixelformat }; + return V4L2Device::toColorSpace(v4l2Format, PixelFormatInfo::info(fourcc).colourEncoding); +} + int V4L2VideoDevice::getFormatMultiplane(V4L2DeviceFormat *format) { struct v4l2_format v4l2Format = {}; @@ -940,7 +960,12 @@ int V4L2VideoDevice::trySetFormatMultiplane(V4L2DeviceFormat *format, bool set) pix->pixelformat = format->fourcc; pix->num_planes = format->planesCount; pix->field = V4L2_FIELD_NONE; - fromColorSpace(format->colorSpace, *pix); + if (format->colorSpace) { + fromColorSpace(format->colorSpace, *pix); + + if (caps_.isVideoCapture()) + pix->flags |= V4L2_PIX_FMT_FLAG_SET_CSC; + } ASSERT(pix->num_planes <= std::size(pix->plane_fmt)); @@ -1010,7 +1035,12 @@ int V4L2VideoDevice::trySetFormatSingleplane(V4L2DeviceFormat *format, bool set) pix->pixelformat = format->fourcc; pix->bytesperline = format->planes[0].bpl; pix->field = V4L2_FIELD_NONE; - fromColorSpace(format->colorSpace, *pix); + if (format->colorSpace) { + fromColorSpace(format->colorSpace, *pix); + + if (caps_.isVideoCapture()) + pix->flags |= V4L2_PIX_FMT_FLAG_SET_CSC; + } ret = ioctl(set ? VIDIOC_S_FMT : VIDIOC_TRY_FMT, &v4l2Format); if (ret) { @@ -1441,7 +1471,7 @@ UniqueFD V4L2VideoDevice::exportDmabufFd(unsigned int index, expbuf.type = bufferType_; expbuf.index = index; expbuf.plane = plane; - expbuf.flags = O_RDWR; + expbuf.flags = O_CLOEXEC | O_RDWR; ret = ioctl(VIDIOC_EXPBUF, &expbuf); if (ret < 0) { @@ -1503,6 +1533,9 @@ int V4L2VideoDevice::importBuffers(unsigned int count) */ int V4L2VideoDevice::releaseBuffers() { + if (!cache_) + return 0; + LOG(V4L2, Debug) << "Releasing buffers"; delete cache_; @@ -1593,6 +1626,11 @@ int V4L2VideoDevice::queueBuffer(FrameBuffer *buffer) if (V4L2_TYPE_IS_OUTPUT(buf.type)) { const FrameMetadata &metadata = buffer->metadata(); + for (const auto &plane : metadata.planes()) { + if (!plane.bytesused) + LOG(V4L2, Warning) << "byteused == 0 is deprecated"; + } + if (numV4l2Planes != planes.size()) { /* * If we have a multi-planar buffer with a V4L2 @@ -1761,12 +1799,14 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer() watchdog_.start(std::chrono::duration_cast<std::chrono::milliseconds>(watchdogDuration_)); } - buffer->metadata_.status = buf.flags & V4L2_BUF_FLAG_ERROR - ? FrameMetadata::FrameError - : FrameMetadata::FrameSuccess; - buffer->metadata_.sequence = buf.sequence; - buffer->metadata_.timestamp = buf.timestamp.tv_sec * 1000000000ULL - + buf.timestamp.tv_usec * 1000ULL; + FrameMetadata &metadata = buffer->_d()->metadata(); + + metadata.status = buf.flags & V4L2_BUF_FLAG_ERROR + ? FrameMetadata::FrameError + : FrameMetadata::FrameSuccess; + metadata.sequence = buf.sequence; + metadata.timestamp = buf.timestamp.tv_sec * 1000000000ULL + + buf.timestamp.tv_usec * 1000ULL; if (V4L2_TYPE_IS_OUTPUT(buf.type)) return buffer; @@ -1777,15 +1817,14 @@ FrameBuffer *V4L2VideoDevice::dequeueBuffer() */ if (!firstFrame_) { if (buf.sequence) - LOG(V4L2, Warning) + LOG(V4L2, Info) << "Zero sequence expected for first frame (got " << buf.sequence << ")"; firstFrame_ = buf.sequence; } - buffer->metadata_.sequence -= firstFrame_.value(); + metadata.sequence -= firstFrame_.value(); unsigned int numV4l2Planes = multiPlanar ? buf.length : 1; - FrameMetadata &metadata = buffer->metadata_; if (numV4l2Planes != buffer->planes().size()) { /* @@ -1911,9 +1950,10 @@ int V4L2VideoDevice::streamOff() /* Send back all queued buffers. */ for (auto it : queuedBuffers_) { FrameBuffer *buffer = it.second; + FrameMetadata &metadata = buffer->_d()->metadata(); cache_->put(it.first); - buffer->metadata_.status = FrameMetadata::FrameCancelled; + metadata.status = FrameMetadata::FrameCancelled; bufferReady.emit(buffer); } @@ -1990,6 +2030,40 @@ V4L2VideoDevice::fromEntityName(const MediaDevice *media, } /** + * \brief Convert \a PixelFormat to a V4L2PixelFormat supported by the device + * \param[in] pixelFormat The PixelFormat to convert + * + * Convert \a pixelformat to a V4L2 FourCC that is known to be supported by + * the video device. + * + * A V4L2VideoDevice may support different V4L2 pixel formats that map the same + * PixelFormat. This is the case of the contiguous and non-contiguous variants + * of multiplanar formats, and with the V4L2 MJPEG and JPEG pixel formats. + * Converting a PixelFormat to a V4L2PixelFormat may thus have multiple answers. + * + * This function converts the \a pixelFormat using the list of V4L2 pixel + * formats that the V4L2VideoDevice supports. This guarantees that the returned + * V4L2PixelFormat will be valid for the device. If multiple matches are still + * possible, contiguous variants are preferred. If the \a pixelFormat is not + * supported by the device, the function returns an invalid V4L2PixelFormat. + * + * \return The V4L2PixelFormat corresponding to \a pixelFormat if supported by + * the device, or an invalid V4L2PixelFormat otherwise + */ +V4L2PixelFormat V4L2VideoDevice::toV4L2PixelFormat(const PixelFormat &pixelFormat) const +{ + const std::vector<V4L2PixelFormat> &v4l2PixelFormats = + V4L2PixelFormat::fromPixelFormat(pixelFormat); + + for (const V4L2PixelFormat &v4l2Format : v4l2PixelFormats) { + if (pixelFormats_.count(v4l2Format)) + return v4l2Format; + } + + return {}; +} + +/** * \class V4L2M2MDevice * \brief Memory-to-Memory video device * |