[libcamera-devel,2/2] libcamera: pipeline: uvcvideo: Implement AeFlickerMode
diff mbox series

Message ID 20230725125641.1557350-3-kieran.bingham@ideasonboard.com
State New
Headers show
Series
  • libcamera: pipeline: AeFlickerMode support
Related show

Commit Message

Kieran Bingham July 25, 2023, 12:56 p.m. UTC
UVC devices often provide controls to support setting the power line
frequency which adapts the camera processing to avoid flicker visible to
the user.

Now that libcamera has implemented AeFlickerMode, use the new control to
implement support for UVC devices.

Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
---
 src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 59 ++++++++++++++++++--
 1 file changed, 55 insertions(+), 4 deletions(-)

Comments

David Plowman July 25, 2023, 1:10 p.m. UTC | #1
Hi Kieran

Thanks for the patch.

On Tue, 25 Jul 2023 at 13:56, Kieran Bingham
<kieran.bingham@ideasonboard.com> wrote:
>
> UVC devices often provide controls to support setting the power line
> frequency which adapts the camera processing to avoid flicker visible to
> the user.
>
> Now that libcamera has implemented AeFlickerMode, use the new control to
> implement support for UVC devices.
>
> Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> ---
>  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 59 ++++++++++++++++++--
>  1 file changed, 55 insertions(+), 4 deletions(-)
>
> diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> index 38f48a5d9269..365410284e5b 100644
> --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> @@ -89,8 +89,8 @@ public:
>         bool match(DeviceEnumerator *enumerator) override;
>
>  private:
> -       int processControl(ControlList *controls, unsigned int id,
> -                          const ControlValue &value);
> +       int processControl(Request *request, ControlList *controls,
> +                          unsigned int id, const ControlValue &value);
>         int processControls(UVCCameraData *data, Request *request);
>
>         UVCCameraData *cameraData(Camera *camera)
> @@ -260,7 +260,8 @@ void PipelineHandlerUVC::stopDevice(Camera *camera)
>         data->video_->releaseBuffers();
>  }
>
> -int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
> +int PipelineHandlerUVC::processControl(Request *request, ControlList *controls,
> +                                      unsigned int id,
>                                        const ControlValue &value)
>  {
>         uint32_t cid;
> @@ -277,6 +278,8 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
>                 cid = V4L2_CID_EXPOSURE_ABSOLUTE;
>         else if (id == controls::AnalogueGain)
>                 cid = V4L2_CID_GAIN;
> +       else if (id == controls::AeFlickerMode)
> +               cid = V4L2_CID_POWER_LINE_FREQUENCY;
>         else
>                 return -EINVAL;
>
> @@ -331,6 +334,42 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
>                 break;
>         }
>
> +       case V4L2_CID_POWER_LINE_FREQUENCY: {
> +               enum v4l2_power_line_frequency mode;
> +               unsigned int period = request->controls().get(controls::AeFlickerPeriod)
> +                                                        .value_or(0);
> +               switch (value.get<int32_t>()) {
> +               default:
> +               case controls::FlickerOff:
> +                       mode = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED;
> +                       break;
> +               case controls::FlickerAuto:
> +                       mode = V4L2_CID_POWER_LINE_FREQUENCY_AUTO;
> +                       break;
> +               case controls::FlickerManual:
> +                       switch (period) {
> +                       case 10000: /* 100Hz in microseconds */
> +                               mode = V4L2_CID_POWER_LINE_FREQUENCY_50HZ;
> +                               break;
> +                       case 8333: /* 120Hz in microseconds */

I suppose we're happy with the exact comparisons here? So if an
application calculated the frequency, and it wasn't exactly 100 or
120Hz, then that number would have to be tidied up before being passed
in. I think that seems OK.

> +                               mode = V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
> +                               break;
> +                       default:
> +                               LOG(UVC, Warning)
> +                                       << "Unsupported AeFlickerPeriod : "
> +                                       << period;
> +                               [[fallthrough]];
> +                       case 0:
> +                               mode = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED;
> +                               break;
> +                       }
> +                       break;
> +               }
> +
> +               controls->set(cid, static_cast<int32_t>(mode));
> +               break;
> +       }
> +
>         default: {
>                 int32_t ivalue = value.get<int32_t>();
>                 controls->set(cid, ivalue);
> @@ -346,7 +385,7 @@ int PipelineHandlerUVC::processControls(UVCCameraData *data, Request *request)
>         ControlList controls(data->video_->controls());
>
>         for (const auto &[id, value] : request->controls())
> -               processControl(&controls, id, value);
> +               processControl(request, &controls, id, value);
>
>         for (const auto &ctrl : controls)
>                 LOG(UVC, Debug)
> @@ -605,6 +644,9 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
>         case V4L2_CID_GAIN:
>                 id = &controls::AnalogueGain;
>                 break;
> +       case V4L2_CID_POWER_LINE_FREQUENCY:
> +               id = &controls::AeFlickerMode;
> +               break;
>         default:
>                 return;
>         }
> @@ -689,6 +731,15 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
>                 break;
>         }
>
> +       case V4L2_CID_POWER_LINE_FREQUENCY: {
> +               info = ControlInfo{
> +                       { static_cast<int>(controls::FlickerOff) },
> +                       { static_cast<int>(controls::FlickerAuto) },
> +                       { static_cast<int>(controls::FlickerOff) }

I assume this is setting the default value of "off"? There was some
discussion which resulted in the suggestion that the default should be
"auto" if available, otherwise "off". Do we know if a UVC device even
supports "auto"?

It doesn't particularly bother me, but we should probably just ensure
that folks are happy with this. So from my point of view:

Reviewed-by: David Plowman <david.plowman@raspberrypi.com>

Thanks!
David

> +               };
> +               break;
> +       }
> +
>         default:
>                 info = v4l2Info;
>                 break;
> --
> 2.34.1
>
Laurent Pinchart Nov. 4, 2024, 3:10 p.m. UTC | #2
On Tue, Jul 25, 2023 at 02:10:16PM +0100, 📷-dev wrote:
> On Tue, 25 Jul 2023 at 13:56, Kieran Bingham wrote:
> >
> > UVC devices often provide controls to support setting the power line
> > frequency which adapts the camera processing to avoid flicker visible to
> > the user.
> >
> > Now that libcamera has implemented AeFlickerMode, use the new control to
> > implement support for UVC devices.
> >
> > Signed-off-by: Kieran Bingham <kieran.bingham@ideasonboard.com>
> > ---
> >  src/libcamera/pipeline/uvcvideo/uvcvideo.cpp | 59 ++++++++++++++++++--
> >  1 file changed, 55 insertions(+), 4 deletions(-)
> >
> > diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> > index 38f48a5d9269..365410284e5b 100644
> > --- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> > +++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
> > @@ -89,8 +89,8 @@ public:
> >         bool match(DeviceEnumerator *enumerator) override;
> >
> >  private:
> > -       int processControl(ControlList *controls, unsigned int id,
> > -                          const ControlValue &value);
> > +       int processControl(Request *request, ControlList *controls,
> > +                          unsigned int id, const ControlValue &value);
> >         int processControls(UVCCameraData *data, Request *request);
> >
> >         UVCCameraData *cameraData(Camera *camera)
> > @@ -260,7 +260,8 @@ void PipelineHandlerUVC::stopDevice(Camera *camera)
> >         data->video_->releaseBuffers();
> >  }
> >
> > -int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
> > +int PipelineHandlerUVC::processControl(Request *request, ControlList *controls,
> > +                                      unsigned int id,
> >                                        const ControlValue &value)
> >  {
> >         uint32_t cid;
> > @@ -277,6 +278,8 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
> >                 cid = V4L2_CID_EXPOSURE_ABSOLUTE;
> >         else if (id == controls::AnalogueGain)
> >                 cid = V4L2_CID_GAIN;
> > +       else if (id == controls::AeFlickerMode)
> > +               cid = V4L2_CID_POWER_LINE_FREQUENCY;
> >         else
> >                 return -EINVAL;
> >
> > @@ -331,6 +334,42 @@ int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
> >                 break;
> >         }
> >
> > +       case V4L2_CID_POWER_LINE_FREQUENCY: {
> > +               enum v4l2_power_line_frequency mode;
> > +               unsigned int period = request->controls().get(controls::AeFlickerPeriod)
> > +                                                        .value_or(0);

If the application modifies AeFlickerPeriod when in manualy mode, but
without setting the AeFlickerMode in the same request, the frequency
won't be updated.

Similarly, if the application set AeFlickerMode to manual without
setting the period in the same request, you won't get the right
behaviour.

> > +               switch (value.get<int32_t>()) {
> > +               default:
> > +               case controls::FlickerOff:
> > +                       mode = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED;
> > +                       break;
> > +               case controls::FlickerAuto:
> > +                       mode = V4L2_CID_POWER_LINE_FREQUENCY_AUTO;
> > +                       break;
> > +               case controls::FlickerManual:
> > +                       switch (period) {
> > +                       case 10000: /* 100Hz in microseconds */
> > +                               mode = V4L2_CID_POWER_LINE_FREQUENCY_50HZ;
> > +                               break;
> > +                       case 8333: /* 120Hz in microseconds */
> 
> I suppose we're happy with the exact comparisons here? So if an
> application calculated the frequency, and it wasn't exactly 100 or
> 120Hz, then that number would have to be tidied up before being passed
> in. I think that seems OK.
>
> > +                               mode = V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
> > +                               break;
> > +                       default:
> > +                               LOG(UVC, Warning)
> > +                                       << "Unsupported AeFlickerPeriod : "
> > +                                       << period;
> > +                               [[fallthrough]];
> > +                       case 0:
> > +                               mode = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED;
> > +                               break;
> > +                       }
> > +                       break;
> > +               }
> > +
> > +               controls->set(cid, static_cast<int32_t>(mode));
> > +               break;
> > +       }
> > +
> >         default: {
> >                 int32_t ivalue = value.get<int32_t>();
> >                 controls->set(cid, ivalue);
> > @@ -346,7 +385,7 @@ int PipelineHandlerUVC::processControls(UVCCameraData *data, Request *request)
> >         ControlList controls(data->video_->controls());
> >
> >         for (const auto &[id, value] : request->controls())
> > -               processControl(&controls, id, value);
> > +               processControl(request, &controls, id, value);
> >
> >         for (const auto &ctrl : controls)
> >                 LOG(UVC, Debug)
> > @@ -605,6 +644,9 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
> >         case V4L2_CID_GAIN:
> >                 id = &controls::AnalogueGain;
> >                 break;
> > +       case V4L2_CID_POWER_LINE_FREQUENCY:
> > +               id = &controls::AeFlickerMode;
> > +               break;
> >         default:
> >                 return;
> >         }
> > @@ -689,6 +731,15 @@ void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
> >                 break;
> >         }
> >
> > +       case V4L2_CID_POWER_LINE_FREQUENCY: {
> > +               info = ControlInfo{
> > +                       { static_cast<int>(controls::FlickerOff) },
> > +                       { static_cast<int>(controls::FlickerAuto) },
> > +                       { static_cast<int>(controls::FlickerOff) }
> 
> I assume this is setting the default value of "off"? There was some
> discussion which resulted in the suggestion that the default should be
> "auto" if available, otherwise "off". Do we know if a UVC device even
> supports "auto"?

The V4L2 control should report the supported values, so we can default
to auto indeed. This needs to be taken into account for the max value
too. Note that some UVC devices don't support the "off" mode and only
support 50Hz and 60Hz. This should also be taken into account, both here
to report the limits, and in processControl() to adjust (or drop)
unsupported values.

> It doesn't particularly bother me, but we should probably just ensure
> that folks are happy with this. So from my point of view:
> 
> Reviewed-by: David Plowman <david.plowman@raspberrypi.com>
>
> > +               };
> > +               break;
> > +       }
> > +
> >         default:
> >                 info = v4l2Info;

Patch
diff mbox series

diff --git a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
index 38f48a5d9269..365410284e5b 100644
--- a/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
+++ b/src/libcamera/pipeline/uvcvideo/uvcvideo.cpp
@@ -89,8 +89,8 @@  public:
 	bool match(DeviceEnumerator *enumerator) override;
 
 private:
-	int processControl(ControlList *controls, unsigned int id,
-			   const ControlValue &value);
+	int processControl(Request *request, ControlList *controls,
+			   unsigned int id, const ControlValue &value);
 	int processControls(UVCCameraData *data, Request *request);
 
 	UVCCameraData *cameraData(Camera *camera)
@@ -260,7 +260,8 @@  void PipelineHandlerUVC::stopDevice(Camera *camera)
 	data->video_->releaseBuffers();
 }
 
-int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
+int PipelineHandlerUVC::processControl(Request *request, ControlList *controls,
+				       unsigned int id,
 				       const ControlValue &value)
 {
 	uint32_t cid;
@@ -277,6 +278,8 @@  int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
 		cid = V4L2_CID_EXPOSURE_ABSOLUTE;
 	else if (id == controls::AnalogueGain)
 		cid = V4L2_CID_GAIN;
+	else if (id == controls::AeFlickerMode)
+		cid = V4L2_CID_POWER_LINE_FREQUENCY;
 	else
 		return -EINVAL;
 
@@ -331,6 +334,42 @@  int PipelineHandlerUVC::processControl(ControlList *controls, unsigned int id,
 		break;
 	}
 
+	case V4L2_CID_POWER_LINE_FREQUENCY: {
+		enum v4l2_power_line_frequency mode;
+		unsigned int period = request->controls().get(controls::AeFlickerPeriod)
+							 .value_or(0);
+		switch (value.get<int32_t>()) {
+		default:
+		case controls::FlickerOff:
+			mode = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED;
+			break;
+		case controls::FlickerAuto:
+			mode = V4L2_CID_POWER_LINE_FREQUENCY_AUTO;
+			break;
+		case controls::FlickerManual:
+			switch (period) {
+			case 10000: /* 100Hz in microseconds */
+				mode = V4L2_CID_POWER_LINE_FREQUENCY_50HZ;
+				break;
+			case 8333: /* 120Hz in microseconds */
+				mode = V4L2_CID_POWER_LINE_FREQUENCY_60HZ;
+				break;
+			default:
+				LOG(UVC, Warning)
+					<< "Unsupported AeFlickerPeriod : "
+					<< period;
+				[[fallthrough]];
+			case 0:
+				mode = V4L2_CID_POWER_LINE_FREQUENCY_DISABLED;
+				break;
+			}
+			break;
+		}
+
+		controls->set(cid, static_cast<int32_t>(mode));
+		break;
+	}
+
 	default: {
 		int32_t ivalue = value.get<int32_t>();
 		controls->set(cid, ivalue);
@@ -346,7 +385,7 @@  int PipelineHandlerUVC::processControls(UVCCameraData *data, Request *request)
 	ControlList controls(data->video_->controls());
 
 	for (const auto &[id, value] : request->controls())
-		processControl(&controls, id, value);
+		processControl(request, &controls, id, value);
 
 	for (const auto &ctrl : controls)
 		LOG(UVC, Debug)
@@ -605,6 +644,9 @@  void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
 	case V4L2_CID_GAIN:
 		id = &controls::AnalogueGain;
 		break;
+	case V4L2_CID_POWER_LINE_FREQUENCY:
+		id = &controls::AeFlickerMode;
+		break;
 	default:
 		return;
 	}
@@ -689,6 +731,15 @@  void UVCCameraData::addControl(uint32_t cid, const ControlInfo &v4l2Info,
 		break;
 	}
 
+	case V4L2_CID_POWER_LINE_FREQUENCY: {
+		info = ControlInfo{
+			{ static_cast<int>(controls::FlickerOff) },
+			{ static_cast<int>(controls::FlickerAuto) },
+			{ static_cast<int>(controls::FlickerOff) }
+		};
+		break;
+	}
+
 	default:
 		info = v4l2Info;
 		break;