[libcamera-devel] v4l2: Support setting frame rate in the V4L2 Adaptation layer
diff mbox series

Message ID 20220215114222.115790-1-galof.nejc@gmail.com
State Superseded
Headers show
Series
  • [libcamera-devel] v4l2: Support setting frame rate in the V4L2 Adaptation layer
Related show

Commit Message

Nejc Galof Feb. 15, 2022, 11:42 a.m. UTC
The V4L2 adaptation layer can already support streaming with components
such as OpenCV, however it is not accepting, or handling any requests to
configure the frame rate.

In V4L2 the frame rate is set by configuring the timeperframe component
of the v4l2_streamparm structure through the VIDIOC_S_PARM ioctl.

Extend the V4L2 compatibility layer to accept the VIDIOC_S_PARM ioctls
and provide an interface for setting controls on the V4L2Camera class to
set the requested rate when starting the camera.

Signed-off-by: Nejc Galof <galof.nejc@gmail.com>
Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
 src/v4l2/v4l2_camera.cpp       | 12 +++++++++---
 src/v4l2/v4l2_camera.h         |  7 +++++++
 src/v4l2/v4l2_camera_proxy.cpp | 25 +++++++++++++++++++++++++
 src/v4l2/v4l2_camera_proxy.h   |  1 +
 4 files changed, 42 insertions(+), 3 deletions(-)

Comments

Laurent Pinchart Feb. 25, 2022, 8:47 a.m. UTC | #1
Hi Nejc,

Thank you for the patch.

On Tue, Feb 15, 2022 at 12:42:22PM +0100, Nejc Galof wrote:
> The V4L2 adaptation layer can already support streaming with components
> such as OpenCV, however it is not accepting, or handling any requests to
> configure the frame rate.
> 
> In V4L2 the frame rate is set by configuring the timeperframe component
> of the v4l2_streamparm structure through the VIDIOC_S_PARM ioctl.
> 
> Extend the V4L2 compatibility layer to accept the VIDIOC_S_PARM ioctls
> and provide an interface for setting controls on the V4L2Camera class to
> set the requested rate when starting the camera.
> 
> Signed-off-by: Nejc Galof <galof.nejc@gmail.com>
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> ---
>  src/v4l2/v4l2_camera.cpp       | 12 +++++++++---
>  src/v4l2/v4l2_camera.h         |  7 +++++++
>  src/v4l2/v4l2_camera_proxy.cpp | 25 +++++++++++++++++++++++++
>  src/v4l2/v4l2_camera_proxy.h   |  1 +
>  4 files changed, 42 insertions(+), 3 deletions(-)
> 
> diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
> index e922b9e6..e4eb3a2b 100644
> --- a/src/v4l2/v4l2_camera.cpp
> +++ b/src/v4l2/v4l2_camera.cpp
> @@ -12,13 +12,15 @@
>  
>  #include <libcamera/base/log.h>
>  
> +#include <libcamera/control_ids.h>
> +
>  using namespace libcamera;
>  
>  LOG_DECLARE_CATEGORY(V4L2Compat)
>  
>  V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)
> -	: camera_(camera), isRunning_(false), bufferAllocator_(nullptr),
> -	  efd_(-1), bufferAvailableCount_(0)
> +	: camera_(camera), controls_(controls::controls), isRunning_(false),
> +	  bufferAllocator_(nullptr), efd_(-1), bufferAvailableCount_(0)
>  {
>  	camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);
>  }
> @@ -203,10 +205,12 @@ int V4L2Camera::streamOn()
>  	if (isRunning_)
>  		return 0;
>  
> -	int ret = camera_->start();
> +	int ret = camera_->start(&controls_);
>  	if (ret < 0)
>  		return ret == -EACCES ? -EBUSY : ret;
>  
> +	controls_.clear();
> +
>  	isRunning_ = true;
>  
>  	for (Request *req : pendingRequests_) {
> @@ -266,6 +270,8 @@ int V4L2Camera::qbuf(unsigned int index)
>  		return 0;
>  	}
>  
> +	request->controls().merge(std::move(controls_));
> +
>  	ret = camera_->queueRequest(request);
>  	if (ret < 0) {
>  		LOG(V4L2Compat, Error) << "Can't queue request";
> diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
> index 03e74118..9e6c895a 100644
> --- a/src/v4l2/v4l2_camera.h
> +++ b/src/v4l2/v4l2_camera.h
> @@ -10,11 +10,14 @@
>  #include <deque>
>  #include <utility>
>  
> +#include <linux/videodev2.h>

I don't think you need to include this here.

> +
>  #include <libcamera/base/mutex.h>
>  #include <libcamera/base/semaphore.h>
>  #include <libcamera/base/shared_fd.h>
>  
>  #include <libcamera/camera.h>
> +#include <libcamera/controls.h>
>  #include <libcamera/framebuffer.h>
>  #include <libcamera/framebuffer_allocator.h>
>  
> @@ -49,6 +52,8 @@ public:
>  				  const libcamera::Size &size,
>  				  libcamera::StreamConfiguration *streamConfigOut);
>  
> +	libcamera::ControlList &controls() { return controls_; }

I may have gone for a setControls() function, but this works too.

> +
>  	int allocBuffers(unsigned int count);
>  	void freeBuffers();
>  	int getBufferFd(unsigned int index);
> @@ -69,6 +74,8 @@ private:
>  	std::shared_ptr<libcamera::Camera> camera_;
>  	std::unique_ptr<libcamera::CameraConfiguration> config_;
>  
> +	libcamera::ControlList controls_;
> +
>  	bool isRunning_;
>  
>  	libcamera::Mutex bufferLock_;
> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
> index e114d09f..f005b21c 100644
> --- a/src/v4l2/v4l2_camera_proxy.cpp
> +++ b/src/v4l2/v4l2_camera_proxy.cpp
> @@ -18,6 +18,8 @@
>  #include <unistd.h>
>  
>  #include <libcamera/camera.h>
> +#include <libcamera/controls.h>
> +#include <libcamera/control_ids.h>
>  #include <libcamera/formats.h>
>  
>  #include <libcamera/base/log.h>
> @@ -33,6 +35,7 @@
>  #define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
>  
>  using namespace libcamera;
> +using namespace std::literals::chrono_literals;
>  
>  LOG_DECLARE_CATEGORY(V4L2Compat)
>  
> @@ -755,6 +758,24 @@ int V4L2CameraProxy::vidioc_streamoff(V4L2CameraFile *file, int *arg)
>  	return ret;
>  }
>  
> +int V4L2CameraProxy::vidioc_s_param(V4L2CameraFile *file, struct v4l2_streamparm *arg)
> +{
> +	LOG(V4L2Compat, Debug)
> +		<< "[" << file->description() << "] " << __func__ << "()";
> +
> +	if (!validateBufferType(arg->type))
> +		return -EINVAL;
> +
> +	struct v4l2_fract *timeperframe = &arg->parm.capture.timeperframe;
> +	utils::Duration frameDuration = 1s * (static_cast<double>(timeperframe->numerator) /
> +					      static_cast<double>(timeperframe->denominator));

Does the following work ?

	utils::Duration frameDuration = 1.0s * timeperframe->numerator
				      / timeperframe->denominator;

> +
> +	int64_t uDuration = frameDuration.get<std::micro>();
> +	vcam_->controls().set(controls::FrameDurationLimits, { uDuration, uDuration });

Looks like we should move the FrameDurationLimits control to using the
Duration type. Not a candidate for this patch of course.

> +
> +	return 0;
> +}

Getting the frame rate back from the camera to implement vidioc_g_param
is a more difficult task, but would it make sense to cache the frame
rate in the V4L2CameraProxy and implement vidioc_g_param based on that
already ? The question of what frame rate to expose by default will be
an interesting one, but I think you can start with a hardcoded default
value of 1/30. This could be done in this patch if easy, or in a
separate patch.

> +
>  const std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
>  	VIDIOC_QUERYCAP,
>  	VIDIOC_ENUM_FRAMESIZES,
> @@ -775,6 +796,7 @@ const std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
>  	VIDIOC_EXPBUF,
>  	VIDIOC_STREAMON,
>  	VIDIOC_STREAMOFF,
> +	VIDIOC_S_PARM,
>  };
>  
>  int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *arg)
> @@ -852,6 +874,9 @@ int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *ar
>  	case VIDIOC_STREAMOFF:
>  		ret = vidioc_streamoff(file, static_cast<int *>(arg));
>  		break;
> +	case VIDIOC_S_PARM:
> +		ret = vidioc_s_param(file, static_cast<struct v4l2_streamparm *>(arg));

vidioc_s_parm, to match the ioctl name.

All in all it looks fairly good. With the two small changes requested
above,

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Will you send a v2 ?

> +		break;
>  	default:
>  		ret = -ENOTTY;
>  		break;
> diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h
> index 76ca2d8a..30a3f492 100644
> --- a/src/v4l2/v4l2_camera_proxy.h
> +++ b/src/v4l2/v4l2_camera_proxy.h
> @@ -65,6 +65,7 @@ private:
>  	int vidioc_expbuf(V4L2CameraFile *file, struct v4l2_exportbuffer *arg);
>  	int vidioc_streamon(V4L2CameraFile *file, int *arg);
>  	int vidioc_streamoff(V4L2CameraFile *file, int *arg);
> +	int vidioc_s_param(V4L2CameraFile *file, struct v4l2_streamparm *arg);
>  
>  	bool hasOwnership(V4L2CameraFile *file);
>  	int acquire(V4L2CameraFile *file);
Nejc Galof Feb. 25, 2022, 2:19 p.m. UTC | #2
Hi Laurent.

I something messed up with patches (I think), and it is added like a new
patch. Can you review what I do wrong, for the next update in the feature?
Some notes about review:
- GetParams I recommended to add in the new patch.
- setControls is not named like that, because there is no getControls (yet).
- All other recommendations are added and fixed.

Regards,
Nejc Galof

V V pet., 25. feb. 2022 ob 09:47 je oseba Laurent Pinchart <
laurent.pinchart@ideasonboard.com> napisala:

> Hi Nejc,
>
> Thank you for the patch.
>
> On Tue, Feb 15, 2022 at 12:42:22PM +0100, Nejc Galof wrote:
> > The V4L2 adaptation layer can already support streaming with components
> > such as OpenCV, however it is not accepting, or handling any requests to
> > configure the frame rate.
> >
> > In V4L2 the frame rate is set by configuring the timeperframe component
> > of the v4l2_streamparm structure through the VIDIOC_S_PARM ioctl.
> >
> > Extend the V4L2 compatibility layer to accept the VIDIOC_S_PARM ioctls
> > and provide an interface for setting controls on the V4L2Camera class to
> > set the requested rate when starting the camera.
> >
> > Signed-off-by: Nejc Galof <galof.nejc@gmail.com>
> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > ---
> >  src/v4l2/v4l2_camera.cpp       | 12 +++++++++---
> >  src/v4l2/v4l2_camera.h         |  7 +++++++
> >  src/v4l2/v4l2_camera_proxy.cpp | 25 +++++++++++++++++++++++++
> >  src/v4l2/v4l2_camera_proxy.h   |  1 +
> >  4 files changed, 42 insertions(+), 3 deletions(-)
> >
> > diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
> > index e922b9e6..e4eb3a2b 100644
> > --- a/src/v4l2/v4l2_camera.cpp
> > +++ b/src/v4l2/v4l2_camera.cpp
> > @@ -12,13 +12,15 @@
> >
> >  #include <libcamera/base/log.h>
> >
> > +#include <libcamera/control_ids.h>
> > +
> >  using namespace libcamera;
> >
> >  LOG_DECLARE_CATEGORY(V4L2Compat)
> >
> >  V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)
> > -     : camera_(camera), isRunning_(false), bufferAllocator_(nullptr),
> > -       efd_(-1), bufferAvailableCount_(0)
> > +     : camera_(camera), controls_(controls::controls),
> isRunning_(false),
> > +       bufferAllocator_(nullptr), efd_(-1), bufferAvailableCount_(0)
> >  {
> >       camera_->requestCompleted.connect(this,
> &V4L2Camera::requestComplete);
> >  }
> > @@ -203,10 +205,12 @@ int V4L2Camera::streamOn()
> >       if (isRunning_)
> >               return 0;
> >
> > -     int ret = camera_->start();
> > +     int ret = camera_->start(&controls_);
> >       if (ret < 0)
> >               return ret == -EACCES ? -EBUSY : ret;
> >
> > +     controls_.clear();
> > +
> >       isRunning_ = true;
> >
> >       for (Request *req : pendingRequests_) {
> > @@ -266,6 +270,8 @@ int V4L2Camera::qbuf(unsigned int index)
> >               return 0;
> >       }
> >
> > +     request->controls().merge(std::move(controls_));
> > +
> >       ret = camera_->queueRequest(request);
> >       if (ret < 0) {
> >               LOG(V4L2Compat, Error) << "Can't queue request";
> > diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
> > index 03e74118..9e6c895a 100644
> > --- a/src/v4l2/v4l2_camera.h
> > +++ b/src/v4l2/v4l2_camera.h
> > @@ -10,11 +10,14 @@
> >  #include <deque>
> >  #include <utility>
> >
> > +#include <linux/videodev2.h>
>
> I don't think you need to include this here.
>
> > +
> >  #include <libcamera/base/mutex.h>
> >  #include <libcamera/base/semaphore.h>
> >  #include <libcamera/base/shared_fd.h>
> >
> >  #include <libcamera/camera.h>
> > +#include <libcamera/controls.h>
> >  #include <libcamera/framebuffer.h>
> >  #include <libcamera/framebuffer_allocator.h>
> >
> > @@ -49,6 +52,8 @@ public:
> >                                 const libcamera::Size &size,
> >                                 libcamera::StreamConfiguration
> *streamConfigOut);
> >
> > +     libcamera::ControlList &controls() { return controls_; }
>
> I may have gone for a setControls() function, but this works too.
>
> > +
> >       int allocBuffers(unsigned int count);
> >       void freeBuffers();
> >       int getBufferFd(unsigned int index);
> > @@ -69,6 +74,8 @@ private:
> >       std::shared_ptr<libcamera::Camera> camera_;
> >       std::unique_ptr<libcamera::CameraConfiguration> config_;
> >
> > +     libcamera::ControlList controls_;
> > +
> >       bool isRunning_;
> >
> >       libcamera::Mutex bufferLock_;
> > diff --git a/src/v4l2/v4l2_camera_proxy.cpp
> b/src/v4l2/v4l2_camera_proxy.cpp
> > index e114d09f..f005b21c 100644
> > --- a/src/v4l2/v4l2_camera_proxy.cpp
> > +++ b/src/v4l2/v4l2_camera_proxy.cpp
> > @@ -18,6 +18,8 @@
> >  #include <unistd.h>
> >
> >  #include <libcamera/camera.h>
> > +#include <libcamera/controls.h>
> > +#include <libcamera/control_ids.h>
> >  #include <libcamera/formats.h>
> >
> >  #include <libcamera/base/log.h>
> > @@ -33,6 +35,7 @@
> >  #define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
> >
> >  using namespace libcamera;
> > +using namespace std::literals::chrono_literals;
> >
> >  LOG_DECLARE_CATEGORY(V4L2Compat)
> >
> > @@ -755,6 +758,24 @@ int
> V4L2CameraProxy::vidioc_streamoff(V4L2CameraFile *file, int *arg)
> >       return ret;
> >  }
> >
> > +int V4L2CameraProxy::vidioc_s_param(V4L2CameraFile *file, struct
> v4l2_streamparm *arg)
> > +{
> > +     LOG(V4L2Compat, Debug)
> > +             << "[" << file->description() << "] " << __func__ << "()";
> > +
> > +     if (!validateBufferType(arg->type))
> > +             return -EINVAL;
> > +
> > +     struct v4l2_fract *timeperframe = &arg->parm.capture.timeperframe;
> > +     utils::Duration frameDuration = 1s *
> (static_cast<double>(timeperframe->numerator) /
> > +
>  static_cast<double>(timeperframe->denominator));
>
> Does the following work ?
>
>         utils::Duration frameDuration = 1.0s * timeperframe->numerator
>                                       / timeperframe->denominator;
>
> > +
> > +     int64_t uDuration = frameDuration.get<std::micro>();
> > +     vcam_->controls().set(controls::FrameDurationLimits, { uDuration,
> uDuration });
>
> Looks like we should move the FrameDurationLimits control to using the
> Duration type. Not a candidate for this patch of course.
>
> > +
> > +     return 0;
> > +}
>
> Getting the frame rate back from the camera to implement vidioc_g_param
> is a more difficult task, but would it make sense to cache the frame
> rate in the V4L2CameraProxy and implement vidioc_g_param based on that
> already ? The question of what frame rate to expose by default will be
> an interesting one, but I think you can start with a hardcoded default
> value of 1/30. This could be done in this patch if easy, or in a
> separate patch.
>
> > +
> >  const std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
> >       VIDIOC_QUERYCAP,
> >       VIDIOC_ENUM_FRAMESIZES,
> > @@ -775,6 +796,7 @@ const std::set<unsigned long>
> V4L2CameraProxy::supportedIoctls_ = {
> >       VIDIOC_EXPBUF,
> >       VIDIOC_STREAMON,
> >       VIDIOC_STREAMOFF,
> > +     VIDIOC_S_PARM,
> >  };
> >
> >  int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request,
> void *arg)
> > @@ -852,6 +874,9 @@ int V4L2CameraProxy::ioctl(V4L2CameraFile *file,
> unsigned long request, void *ar
> >       case VIDIOC_STREAMOFF:
> >               ret = vidioc_streamoff(file, static_cast<int *>(arg));
> >               break;
> > +     case VIDIOC_S_PARM:
> > +             ret = vidioc_s_param(file, static_cast<struct
> v4l2_streamparm *>(arg));
>
> vidioc_s_parm, to match the ioctl name.
>
> All in all it looks fairly good. With the two small changes requested
> above,
>
> Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
>
> Will you send a v2 ?
>
> > +             break;
> >       default:
> >               ret = -ENOTTY;
> >               break;
> > diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h
> > index 76ca2d8a..30a3f492 100644
> > --- a/src/v4l2/v4l2_camera_proxy.h
> > +++ b/src/v4l2/v4l2_camera_proxy.h
> > @@ -65,6 +65,7 @@ private:
> >       int vidioc_expbuf(V4L2CameraFile *file, struct v4l2_exportbuffer
> *arg);
> >       int vidioc_streamon(V4L2CameraFile *file, int *arg);
> >       int vidioc_streamoff(V4L2CameraFile *file, int *arg);
> > +     int vidioc_s_param(V4L2CameraFile *file, struct v4l2_streamparm
> *arg);
> >
> >       bool hasOwnership(V4L2CameraFile *file);
> >       int acquire(V4L2CameraFile *file);
>
> --
> Regards,
>
> Laurent Pinchart
>
Laurent Pinchart Feb. 25, 2022, 2:25 p.m. UTC | #3
Hi Nejc,

On Fri, Feb 25, 2022 at 03:19:18PM +0100, Nejc Galof wrote:
> Hi Laurent.
> 
> I something messed up with patches (I think), and it is added like a new
> patch.

It should indeed have been a single patch. If the first patch had been
merged already, a separate patch to fix issues would have been the right
thing to do, but as that's not the case, the fixes should have been
squashed. "git commit --amend" and "git rebase -i" are you friends there.

> Can you review what I do wrong, for the next update in the feature?
> Some notes about review:
> - GetParams I recommended to add in the new patch.
> - setControls is not named like that, because there is no getControls (yet).
> - All other recommendations are added and fixed.

Sure, I'll review v2.

> V V pet., 25. feb. 2022 ob 09:47 je oseba Laurent Pinchart napisala:
> > On Tue, Feb 15, 2022 at 12:42:22PM +0100, Nejc Galof wrote:
> > > The V4L2 adaptation layer can already support streaming with components
> > > such as OpenCV, however it is not accepting, or handling any requests to
> > > configure the frame rate.
> > >
> > > In V4L2 the frame rate is set by configuring the timeperframe component
> > > of the v4l2_streamparm structure through the VIDIOC_S_PARM ioctl.
> > >
> > > Extend the V4L2 compatibility layer to accept the VIDIOC_S_PARM ioctls
> > > and provide an interface for setting controls on the V4L2Camera class to
> > > set the requested rate when starting the camera.
> > >
> > > Signed-off-by: Nejc Galof <galof.nejc@gmail.com>
> > > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > > ---
> > >  src/v4l2/v4l2_camera.cpp       | 12 +++++++++---
> > >  src/v4l2/v4l2_camera.h         |  7 +++++++
> > >  src/v4l2/v4l2_camera_proxy.cpp | 25 +++++++++++++++++++++++++
> > >  src/v4l2/v4l2_camera_proxy.h   |  1 +
> > >  4 files changed, 42 insertions(+), 3 deletions(-)
> > >
> > > diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
> > > index e922b9e6..e4eb3a2b 100644
> > > --- a/src/v4l2/v4l2_camera.cpp
> > > +++ b/src/v4l2/v4l2_camera.cpp
> > > @@ -12,13 +12,15 @@
> > >
> > >  #include <libcamera/base/log.h>
> > >
> > > +#include <libcamera/control_ids.h>
> > > +
> > >  using namespace libcamera;
> > >
> > >  LOG_DECLARE_CATEGORY(V4L2Compat)
> > >
> > >  V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)
> > > -     : camera_(camera), isRunning_(false), bufferAllocator_(nullptr),
> > > -       efd_(-1), bufferAvailableCount_(0)
> > > +     : camera_(camera), controls_(controls::controls), isRunning_(false),
> > > +       bufferAllocator_(nullptr), efd_(-1), bufferAvailableCount_(0)
> > >  {
> > >       camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);
> > >  }
> > > @@ -203,10 +205,12 @@ int V4L2Camera::streamOn()
> > >       if (isRunning_)
> > >               return 0;
> > >
> > > -     int ret = camera_->start();
> > > +     int ret = camera_->start(&controls_);
> > >       if (ret < 0)
> > >               return ret == -EACCES ? -EBUSY : ret;
> > >
> > > +     controls_.clear();
> > > +
> > >       isRunning_ = true;
> > >
> > >       for (Request *req : pendingRequests_) {
> > > @@ -266,6 +270,8 @@ int V4L2Camera::qbuf(unsigned int index)
> > >               return 0;
> > >       }
> > >
> > > +     request->controls().merge(std::move(controls_));
> > > +
> > >       ret = camera_->queueRequest(request);
> > >       if (ret < 0) {
> > >               LOG(V4L2Compat, Error) << "Can't queue request";
> > > diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
> > > index 03e74118..9e6c895a 100644
> > > --- a/src/v4l2/v4l2_camera.h
> > > +++ b/src/v4l2/v4l2_camera.h
> > > @@ -10,11 +10,14 @@
> > >  #include <deque>
> > >  #include <utility>
> > >
> > > +#include <linux/videodev2.h>
> >
> > I don't think you need to include this here.
> >
> > > +
> > >  #include <libcamera/base/mutex.h>
> > >  #include <libcamera/base/semaphore.h>
> > >  #include <libcamera/base/shared_fd.h>
> > >
> > >  #include <libcamera/camera.h>
> > > +#include <libcamera/controls.h>
> > >  #include <libcamera/framebuffer.h>
> > >  #include <libcamera/framebuffer_allocator.h>
> > >
> > > @@ -49,6 +52,8 @@ public:
> > >                                 const libcamera::Size &size,
> > >                                 libcamera::StreamConfiguration *streamConfigOut);
> > >
> > > +     libcamera::ControlList &controls() { return controls_; }
> >
> > I may have gone for a setControls() function, but this works too.
> >
> > > +
> > >       int allocBuffers(unsigned int count);
> > >       void freeBuffers();
> > >       int getBufferFd(unsigned int index);
> > > @@ -69,6 +74,8 @@ private:
> > >       std::shared_ptr<libcamera::Camera> camera_;
> > >       std::unique_ptr<libcamera::CameraConfiguration> config_;
> > >
> > > +     libcamera::ControlList controls_;
> > > +
> > >       bool isRunning_;
> > >
> > >       libcamera::Mutex bufferLock_;
> > > diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
> > > index e114d09f..f005b21c 100644
> > > --- a/src/v4l2/v4l2_camera_proxy.cpp
> > > +++ b/src/v4l2/v4l2_camera_proxy.cpp
> > > @@ -18,6 +18,8 @@
> > >  #include <unistd.h>
> > >
> > >  #include <libcamera/camera.h>
> > > +#include <libcamera/controls.h>
> > > +#include <libcamera/control_ids.h>
> > >  #include <libcamera/formats.h>
> > >
> > >  #include <libcamera/base/log.h>
> > > @@ -33,6 +35,7 @@
> > >  #define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
> > >
> > >  using namespace libcamera;
> > > +using namespace std::literals::chrono_literals;
> > >
> > >  LOG_DECLARE_CATEGORY(V4L2Compat)
> > >
> > > @@ -755,6 +758,24 @@ int V4L2CameraProxy::vidioc_streamoff(V4L2CameraFile *file, int *arg)
> > >       return ret;
> > >  }
> > >
> > > +int V4L2CameraProxy::vidioc_s_param(V4L2CameraFile *file, struct v4l2_streamparm *arg)
> > > +{
> > > +     LOG(V4L2Compat, Debug)
> > > +             << "[" << file->description() << "] " << __func__ << "()";
> > > +
> > > +     if (!validateBufferType(arg->type))
> > > +             return -EINVAL;
> > > +
> > > +     struct v4l2_fract *timeperframe = &arg->parm.capture.timeperframe;
> > > +     utils::Duration frameDuration = 1s * (static_cast<double>(timeperframe->numerator) /
> > > +                                           static_cast<double>(timeperframe->denominator));
> >
> > Does the following work ?
> >
> >         utils::Duration frameDuration = 1.0s * timeperframe->numerator
> >                                       / timeperframe->denominator;
> >
> > > +
> > > +     int64_t uDuration = frameDuration.get<std::micro>();
> > > +     vcam_->controls().set(controls::FrameDurationLimits, { uDuration, uDuration });
> >
> > Looks like we should move the FrameDurationLimits control to using the
> > Duration type. Not a candidate for this patch of course.
> >
> > > +
> > > +     return 0;
> > > +}
> >
> > Getting the frame rate back from the camera to implement vidioc_g_param
> > is a more difficult task, but would it make sense to cache the frame
> > rate in the V4L2CameraProxy and implement vidioc_g_param based on that
> > already ? The question of what frame rate to expose by default will be
> > an interesting one, but I think you can start with a hardcoded default
> > value of 1/30. This could be done in this patch if easy, or in a
> > separate patch.
> >
> > > +
> > >  const std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
> > >       VIDIOC_QUERYCAP,
> > >       VIDIOC_ENUM_FRAMESIZES,
> > > @@ -775,6 +796,7 @@ const std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
> > >       VIDIOC_EXPBUF,
> > >       VIDIOC_STREAMON,
> > >       VIDIOC_STREAMOFF,
> > > +     VIDIOC_S_PARM,
> > >  };
> > >
> > >  int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *arg)
> > > @@ -852,6 +874,9 @@ int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *ar
> > >       case VIDIOC_STREAMOFF:
> > >               ret = vidioc_streamoff(file, static_cast<int *>(arg));
> > >               break;
> > > +     case VIDIOC_S_PARM:
> > > +             ret = vidioc_s_param(file, static_cast<struct v4l2_streamparm *>(arg));
> >
> > vidioc_s_parm, to match the ioctl name.
> >
> > All in all it looks fairly good. With the two small changes requested
> > above,
> >
> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
> >
> > Will you send a v2 ?
> >
> > > +             break;
> > >       default:
> > >               ret = -ENOTTY;
> > >               break;
> > > diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h
> > > index 76ca2d8a..30a3f492 100644
> > > --- a/src/v4l2/v4l2_camera_proxy.h
> > > +++ b/src/v4l2/v4l2_camera_proxy.h
> > > @@ -65,6 +65,7 @@ private:
> > >       int vidioc_expbuf(V4L2CameraFile *file, struct v4l2_exportbuffer
> > *arg);
> > >       int vidioc_streamon(V4L2CameraFile *file, int *arg);
> > >       int vidioc_streamoff(V4L2CameraFile *file, int *arg);
> > > +     int vidioc_s_param(V4L2CameraFile *file, struct v4l2_streamparm
> > *arg);
> > >
> > >       bool hasOwnership(V4L2CameraFile *file);
> > >       int acquire(V4L2CameraFile *file);

Patch
diff mbox series

diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
index e922b9e6..e4eb3a2b 100644
--- a/src/v4l2/v4l2_camera.cpp
+++ b/src/v4l2/v4l2_camera.cpp
@@ -12,13 +12,15 @@ 
 
 #include <libcamera/base/log.h>
 
+#include <libcamera/control_ids.h>
+
 using namespace libcamera;
 
 LOG_DECLARE_CATEGORY(V4L2Compat)
 
 V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)
-	: camera_(camera), isRunning_(false), bufferAllocator_(nullptr),
-	  efd_(-1), bufferAvailableCount_(0)
+	: camera_(camera), controls_(controls::controls), isRunning_(false),
+	  bufferAllocator_(nullptr), efd_(-1), bufferAvailableCount_(0)
 {
 	camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);
 }
@@ -203,10 +205,12 @@  int V4L2Camera::streamOn()
 	if (isRunning_)
 		return 0;
 
-	int ret = camera_->start();
+	int ret = camera_->start(&controls_);
 	if (ret < 0)
 		return ret == -EACCES ? -EBUSY : ret;
 
+	controls_.clear();
+
 	isRunning_ = true;
 
 	for (Request *req : pendingRequests_) {
@@ -266,6 +270,8 @@  int V4L2Camera::qbuf(unsigned int index)
 		return 0;
 	}
 
+	request->controls().merge(std::move(controls_));
+
 	ret = camera_->queueRequest(request);
 	if (ret < 0) {
 		LOG(V4L2Compat, Error) << "Can't queue request";
diff --git a/src/v4l2/v4l2_camera.h b/src/v4l2/v4l2_camera.h
index 03e74118..9e6c895a 100644
--- a/src/v4l2/v4l2_camera.h
+++ b/src/v4l2/v4l2_camera.h
@@ -10,11 +10,14 @@ 
 #include <deque>
 #include <utility>
 
+#include <linux/videodev2.h>
+
 #include <libcamera/base/mutex.h>
 #include <libcamera/base/semaphore.h>
 #include <libcamera/base/shared_fd.h>
 
 #include <libcamera/camera.h>
+#include <libcamera/controls.h>
 #include <libcamera/framebuffer.h>
 #include <libcamera/framebuffer_allocator.h>
 
@@ -49,6 +52,8 @@  public:
 				  const libcamera::Size &size,
 				  libcamera::StreamConfiguration *streamConfigOut);
 
+	libcamera::ControlList &controls() { return controls_; }
+
 	int allocBuffers(unsigned int count);
 	void freeBuffers();
 	int getBufferFd(unsigned int index);
@@ -69,6 +74,8 @@  private:
 	std::shared_ptr<libcamera::Camera> camera_;
 	std::unique_ptr<libcamera::CameraConfiguration> config_;
 
+	libcamera::ControlList controls_;
+
 	bool isRunning_;
 
 	libcamera::Mutex bufferLock_;
diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
index e114d09f..f005b21c 100644
--- a/src/v4l2/v4l2_camera_proxy.cpp
+++ b/src/v4l2/v4l2_camera_proxy.cpp
@@ -18,6 +18,8 @@ 
 #include <unistd.h>
 
 #include <libcamera/camera.h>
+#include <libcamera/controls.h>
+#include <libcamera/control_ids.h>
 #include <libcamera/formats.h>
 
 #include <libcamera/base/log.h>
@@ -33,6 +35,7 @@ 
 #define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
 
 using namespace libcamera;
+using namespace std::literals::chrono_literals;
 
 LOG_DECLARE_CATEGORY(V4L2Compat)
 
@@ -755,6 +758,24 @@  int V4L2CameraProxy::vidioc_streamoff(V4L2CameraFile *file, int *arg)
 	return ret;
 }
 
+int V4L2CameraProxy::vidioc_s_param(V4L2CameraFile *file, struct v4l2_streamparm *arg)
+{
+	LOG(V4L2Compat, Debug)
+		<< "[" << file->description() << "] " << __func__ << "()";
+
+	if (!validateBufferType(arg->type))
+		return -EINVAL;
+
+	struct v4l2_fract *timeperframe = &arg->parm.capture.timeperframe;
+	utils::Duration frameDuration = 1s * (static_cast<double>(timeperframe->numerator) /
+					      static_cast<double>(timeperframe->denominator));
+
+	int64_t uDuration = frameDuration.get<std::micro>();
+	vcam_->controls().set(controls::FrameDurationLimits, { uDuration, uDuration });
+
+	return 0;
+}
+
 const std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
 	VIDIOC_QUERYCAP,
 	VIDIOC_ENUM_FRAMESIZES,
@@ -775,6 +796,7 @@  const std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
 	VIDIOC_EXPBUF,
 	VIDIOC_STREAMON,
 	VIDIOC_STREAMOFF,
+	VIDIOC_S_PARM,
 };
 
 int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *arg)
@@ -852,6 +874,9 @@  int V4L2CameraProxy::ioctl(V4L2CameraFile *file, unsigned long request, void *ar
 	case VIDIOC_STREAMOFF:
 		ret = vidioc_streamoff(file, static_cast<int *>(arg));
 		break;
+	case VIDIOC_S_PARM:
+		ret = vidioc_s_param(file, static_cast<struct v4l2_streamparm *>(arg));
+		break;
 	default:
 		ret = -ENOTTY;
 		break;
diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h
index 76ca2d8a..30a3f492 100644
--- a/src/v4l2/v4l2_camera_proxy.h
+++ b/src/v4l2/v4l2_camera_proxy.h
@@ -65,6 +65,7 @@  private:
 	int vidioc_expbuf(V4L2CameraFile *file, struct v4l2_exportbuffer *arg);
 	int vidioc_streamon(V4L2CameraFile *file, int *arg);
 	int vidioc_streamoff(V4L2CameraFile *file, int *arg);
+	int vidioc_s_param(V4L2CameraFile *file, struct v4l2_streamparm *arg);
 
 	bool hasOwnership(V4L2CameraFile *file);
 	int acquire(V4L2CameraFile *file);