[libcamera-devel] cam: Add --monitor option

Message ID 20200713121122.20000-1-email@uajain.com
State Superseded
Headers show
Series
  • [libcamera-devel] cam: Add --monitor option
Related show

Commit Message

Umang Jain July 13, 2020, 12:11 p.m. UTC
Add --monitor to monitor new hotplug and unplug camera events from
the CameraManager.

Signed-off-by: Umang Jain <email@uajain.com>
---
 src/cam/main.cpp | 24 ++++++++++++++++++++++++
 src/cam/main.h   |  1 +
 2 files changed, 25 insertions(+)

Comments

Niklas Söderlund July 13, 2020, 2:05 p.m. UTC | #1
Hi Umang,

Thanks for your work.

On 2020-07-13 12:11:28 +0000, Umang Jain wrote:
> Add --monitor to monitor new hotplug and unplug camera events from
> the CameraManager.
> 
> Signed-off-by: Umang Jain <email@uajain.com>

First off, for this patch.

Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>

When testing this patch to discover how far hotplug support have come I 
noticed some issues. I'm not aware if you know about them or not so I 
will describe them here.

1. Error message printed from cam

   When I run cam --monitor and physically unplug and then replug a USB 
   UVC camera I get the following ERROR:

   $ cam --monitor

   * unplug camera *
   Camera Removed: Logitech Webcam C930e
   [465:45:06.515424906] [593092] ERROR V4L2 v4l2_videodevice.cpp:1117 /dev/video0[cap]: Unable to request 0 buffers: No such device

   * plug camera *
   Camera Added: Logitech Webcam C930e

   Would it be possible to not print the error when unplugging? I 
   understand the IOCTL can never succeed as the vidoe device is gone 
   but maybe we could avoid logging the error.

2. Qcam hits a delayed assert with hot-plug

   Once more the USB UVC camera is used.

   $ qcam
   [465:59:29.856991110] [599647]  INFO IPAManager ipa_manager.cpp:136 libcamera is not installed. Adding '/home/neg/work/libcamera/libcamera/build/normal/src/ipa' to the IPA search path
   [465:59:29.858010955] [599647]  INFO Camera camera_manager.cpp:283 libcamera v0.0.0+1602-7a13375c
   [465:59:29.870436670] [599649]  INFO IPAProxy ipa_proxy.cpp:122 libcamera is not installed. Loading IPA configuration from '/home/neg/work/libcamera/libcamera/src/ipa/vimc/data'
   [465:59:36.255358189] [599647]  INFO Camera camera.cpp:770 configuring streams: (0) 1920x1080-MJPEG
   Using software format conversion from MJPEG
   seq: 000000 bytesused: 151768 timestamp: 1677576692011000 fps: 0.00
   seq: 000001 bytesused: 157570 timestamp: 1677576715997000 fps: 41.69
   seq: 000002 bytesused: 158088 timestamp: 1677576751996000 fps: 27.78
   seq: 000003 bytesused: 158089 timestamp: 1677576783994000 fps: 31.25
   ....
   seq: 000080 bytesused: 167393 timestamp: 1677579347940000 fps: 31.25
   seq: 000081 bytesused: 167030 timestamp: 1677579383939000 fps: 27.78
   seq: 000082 bytesused: 167221 timestamp: 1677579415938000 fps: 31.25
   seq: 000083 bytesused: 166575 timestamp: 1677579447937000 fps: 31.25
   seq: 000084 bytesused: 165611 timestamp: 1677579483936000 fps: 27.78
   seq: 000085 bytesused: 166295 timestamp: 1677579515936000 fps: 31.25

   * unplug camera *
   Removing camera: Logitech Webcam C930e
   Failed to stop capture

   * wait a while *

   * close qcam *
   [465:59:47.587993893] [599647] ERROR V4L2 v4l2_videodevice.cpp:1117 /dev/video0[cap]: Unable to request 0 buffers: No such device
   [465:59:47.588042533] [599649] FATAL default event_dispatcher_poll.cpp:258 assertion "iter != notifiers_.end()" failed
   Backtrace:
   /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera19EventDispatcherPoll16processNotifiersERKSt6vectorI6pollfdSaIS2_EE+0x150) [0x7ffb28d9ae52]
   /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera19EventDispatcherPoll13processEventsEv+0x2b9) [0x7ffb28d9a75f]
   /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera6Thread4execEv+0x8a) [0x7ffb28de4396]
   /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private3runEv+0x104) [0x7ffb28d4c0d4]
   /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera6Thread11startThreadEv+0xe5) [0x7ffb28de42ff]
   /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZSt13__invoke_implIvMN9libcamera6ThreadEFvvEPS1_JEET_St21__invoke_memfun_derefOT0_OT1_DpOT2_+0x67) [0x7ffb28de8c2b]
   /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZSt8__invokeIMN9libcamera6ThreadEFvvEJPS1_EENSt15__invoke_resultIT_JDpT0_EE4typeEOS6_DpOS7_+0x37) [0x7ffb28de8b6d]
   /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZNSt6thread8_InvokerISt5tupleIJMN9libcamera6ThreadEFvvEPS3_EEE9_M_invokeIJLm0ELm1EEEEvSt12_Index_tupleIJXspT_EEE+0x43) [0x7ffb28de8add]
   /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZNSt6thread8_InvokerISt5tupleIJMN9libcamera6ThreadEFvvEPS3_EEEclEv+0x18) [0x7ffb28de8a96]
   /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZNSt6thread11_State_implINS_8_InvokerISt5tupleIJMN9libcamera6ThreadEFvvEPS4_EEEEE6_M_runEv+0x1c) [0x7ffb28de8a7a]
   /usr/lib/libstdc++.so.6(+0xcfb74) [0x7ffb27582b74]
   /usr/lib/libpthread.so.0(+0x9422) [0x7ffb26f7d422]
   /usr/lib/libc.so.6(clone+0x43) [0x7ffb273cfbf3]

> ---
>  src/cam/main.cpp | 24 ++++++++++++++++++++++++
>  src/cam/main.h   |  1 +
>  2 files changed, 25 insertions(+)
> 
> diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> index 2512fe9..3488315 100644
> --- a/src/cam/main.cpp
> +++ b/src/cam/main.cpp
> @@ -36,6 +36,8 @@ public:
>  	void quit();
>  
>  private:
> +	void cameraAdded(std::shared_ptr<Camera> cam);
> +	void cameraRemoved(std::shared_ptr<Camera> cam);
>  	int parseOptions(int argc, char *argv[]);
>  	int prepareConfig();
>  	int listControls();
> @@ -115,6 +117,10 @@ int CamApp::init(int argc, char **argv)
>  		ret = prepareConfig();
>  		if (ret)
>  			return ret;
> +	} else if (options_.isSet(OptMonitor)) {
> +		cm_->cameraAdded.connect(this, &CamApp::cameraAdded);
> +		cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved);
> +		std::cout << "Monitoring new hotplug and unplug events..." << std::endl;
>  	}
>  
>  	loop_ = new EventLoop(cm_->eventDispatcher());
> @@ -179,6 +185,8 @@ int CamApp::parseOptions(int argc, char *argv[])
>  			 "list-controls");
>  	parser.addOption(OptListProperties, OptionNone, "List cameras properties",
>  			 "list-properties");
> +	parser.addOption(OptMonitor, OptionNone, "Monitor for hotplug and unplug camera events",
> +			 "monitor");
>  
>  	options_ = parser.parse(argc, argv);
>  	if (!options_.valid())
> @@ -293,6 +301,16 @@ int CamApp::infoConfiguration()
>  	return 0;
>  }
>  
> +void CamApp::cameraAdded(std::shared_ptr<Camera> cam)
> +{
> +	std::cout << "Camera Added: " << cam->name() << std::endl;
> +}
> +
> +void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)
> +{
> +	std::cout << "Camera Removed: " << cam->name() << std::endl;
> +}
> +
>  int CamApp::run()
>  {
>  	int ret;
> @@ -330,6 +348,12 @@ int CamApp::run()
>  		return capture.run(loop_, options_);
>  	}
>  
> +	if (options_.isSet(OptMonitor)) {
> +		ret = loop_->exec();
> +		if (ret)
> +			std::cout << "Failed to run monitor loop" << std::endl;
> +	}
> +
>  	return 0;
>  }
>  
> diff --git a/src/cam/main.h b/src/cam/main.h
> index 4a130d8..f61d288 100644
> --- a/src/cam/main.h
> +++ b/src/cam/main.h
> @@ -15,6 +15,7 @@ enum {
>  	OptInfo = 'I',
>  	OptList = 'l',
>  	OptListProperties = 'p',
> +	OptMonitor = 'm',
>  	OptStream = 's',
>  	OptListControls = 256,
>  };
> -- 
> 2.26.2
> 
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Kieran Bingham July 13, 2020, 2:14 p.m. UTC | #2
Hi Umang,

On 13/07/2020 13:11, Umang Jain wrote:
> Add --monitor to monitor new hotplug and unplug camera events from
> the CameraManager.
> 
> Signed-off-by: Umang Jain <email@uajain.com>
> ---
>  src/cam/main.cpp | 24 ++++++++++++++++++++++++
>  src/cam/main.h   |  1 +
>  2 files changed, 25 insertions(+)
> 
> diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> index 2512fe9..3488315 100644
> --- a/src/cam/main.cpp
> +++ b/src/cam/main.cpp
> @@ -36,6 +36,8 @@ public:
>  	void quit();
>  
>  private:
> +	void cameraAdded(std::shared_ptr<Camera> cam);
> +	void cameraRemoved(std::shared_ptr<Camera> cam);
>  	int parseOptions(int argc, char *argv[]);
>  	int prepareConfig();
>  	int listControls();
> @@ -115,6 +117,10 @@ int CamApp::init(int argc, char **argv)
>  		ret = prepareConfig();
>  		if (ret)
>  			return ret;
> +	} else if (options_.isSet(OptMonitor)) {
> +		cm_->cameraAdded.connect(this, &CamApp::cameraAdded);
> +		cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved);

Should we add these event notifiers unconditionally?

I think if we are running any kind of event loop, or capture - if
something gets unplugged it would be nice to know about it.

What happens if we pull out a UVC camera while streaming with cam?

Still, I think any graceful handling of hotplug in cam is separate to
adding a monitor, which is useful on it's own, so:

Reviewed-by: Kieran Bingham <kieran.bingham@ideasonboard.com>


> +		std::cout << "Monitoring new hotplug and unplug events..." << std::endl;
>  	}
>  
>  	loop_ = new EventLoop(cm_->eventDispatcher());
> @@ -179,6 +185,8 @@ int CamApp::parseOptions(int argc, char *argv[])
>  			 "list-controls");
>  	parser.addOption(OptListProperties, OptionNone, "List cameras properties",
>  			 "list-properties");
> +	parser.addOption(OptMonitor, OptionNone, "Monitor for hotplug and unplug camera events",
> +			 "monitor");
>  
>  	options_ = parser.parse(argc, argv);
>  	if (!options_.valid())
> @@ -293,6 +301,16 @@ int CamApp::infoConfiguration()
>  	return 0;
>  }
>  
> +void CamApp::cameraAdded(std::shared_ptr<Camera> cam)
> +{
> +	std::cout << "Camera Added: " << cam->name() << std::endl;
> +}
> +
> +void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)
> +{
> +	std::cout << "Camera Removed: " << cam->name() << std::endl;
> +}
> +
>  int CamApp::run()
>  {
>  	int ret;
> @@ -330,6 +348,12 @@ int CamApp::run()
>  		return capture.run(loop_, options_);
>  	}
>  
> +	if (options_.isSet(OptMonitor)) {
> +		ret = loop_->exec();
> +		if (ret)
> +			std::cout << "Failed to run monitor loop" << std::endl;
> +	}
> +
>  	return 0;
>  }
>  
> diff --git a/src/cam/main.h b/src/cam/main.h
> index 4a130d8..f61d288 100644
> --- a/src/cam/main.h
> +++ b/src/cam/main.h
> @@ -15,6 +15,7 @@ enum {
>  	OptInfo = 'I',
>  	OptList = 'l',
>  	OptListProperties = 'p',
> +	OptMonitor = 'm',
>  	OptStream = 's',
>  	OptListControls = 256,
>  };
>
Umang Jain July 13, 2020, 3:53 p.m. UTC | #3
Hi Niklas,

Thanks for the review.

On 7/13/20 7:35 PM, Niklas Söderlund wrote:
> Hi Umang,
>
> Thanks for your work.
>
> On 2020-07-13 12:11:28 +0000, Umang Jain wrote:
>> Add --monitor to monitor new hotplug and unplug camera events from
>> the CameraManager.
>>
>> Signed-off-by: Umang Jain <email@uajain.com>
> First off, for this patch.
>
> Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
>
> When testing this patch to discover how far hotplug support have come I
> noticed some issues. I'm not aware if you know about them or not so I
> will describe them here.
>
> 1. Error message printed from cam
>
>     When I run cam --monitor and physically unplug and then replug a USB
>     UVC camera I get the following ERROR:
>
>     $ cam --monitor
>
>     * unplug camera *
>     Camera Removed: Logitech Webcam C930e
>     [465:45:06.515424906] [593092] ERROR V4L2 v4l2_videodevice.cpp:1117 /dev/video0[cap]: Unable to request 0 buffers: No such device
>
>     * plug camera *
>     Camera Added: Logitech Webcam C930e
>
>     Would it be possible to not print the error when unplugging? I
>     understand the IOCTL can never succeed as the vidoe device is gone
>     but maybe we could avoid logging the error.
hmm, I am not sure if we can suppress logging that error without much 
invasive changes.
On looking around, I noticed there couple of places where 
V4L2VideoDevice::requestBuffers()
is called, with request buffer count = 0. This particular one, is being 
called from releaseBuffers.
>
> 2. Qcam hits a delayed assert with hot-plug
>
>     Once more the USB UVC camera is used.
>
>     $ qcam
>     [465:59:29.856991110] [599647]  INFO IPAManager ipa_manager.cpp:136 libcamera is not installed. Adding '/home/neg/work/libcamera/libcamera/build/normal/src/ipa' to the IPA search path
>     [465:59:29.858010955] [599647]  INFO Camera camera_manager.cpp:283 libcamera v0.0.0+1602-7a13375c
>     [465:59:29.870436670] [599649]  INFO IPAProxy ipa_proxy.cpp:122 libcamera is not installed. Loading IPA configuration from '/home/neg/work/libcamera/libcamera/src/ipa/vimc/data'
>     [465:59:36.255358189] [599647]  INFO Camera camera.cpp:770 configuring streams: (0) 1920x1080-MJPEG
>     Using software format conversion from MJPEG
>     seq: 000000 bytesused: 151768 timestamp: 1677576692011000 fps: 0.00
>     seq: 000001 bytesused: 157570 timestamp: 1677576715997000 fps: 41.69
>     seq: 000002 bytesused: 158088 timestamp: 1677576751996000 fps: 27.78
>     seq: 000003 bytesused: 158089 timestamp: 1677576783994000 fps: 31.25
>     ....
>     seq: 000080 bytesused: 167393 timestamp: 1677579347940000 fps: 31.25
>     seq: 000081 bytesused: 167030 timestamp: 1677579383939000 fps: 27.78
>     seq: 000082 bytesused: 167221 timestamp: 1677579415938000 fps: 31.25
>     seq: 000083 bytesused: 166575 timestamp: 1677579447937000 fps: 31.25
>     seq: 000084 bytesused: 165611 timestamp: 1677579483936000 fps: 27.78
>     seq: 000085 bytesused: 166295 timestamp: 1677579515936000 fps: 31.25
>
>     * unplug camera *
>     Removing camera: Logitech Webcam C930e
>     Failed to stop capture
>
>     * wait a while *
>
>     * close qcam *
>     [465:59:47.587993893] [599647] ERROR V4L2 v4l2_videodevice.cpp:1117 /dev/video0[cap]: Unable to request 0 buffers: No such device
>     [465:59:47.588042533] [599649] FATAL default event_dispatcher_poll.cpp:258 assertion "iter != notifiers_.end()" failed
>     Backtrace:
>     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera19EventDispatcherPoll16processNotifiersERKSt6vectorI6pollfdSaIS2_EE+0x150) [0x7ffb28d9ae52]
>     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera19EventDispatcherPoll13processEventsEv+0x2b9) [0x7ffb28d9a75f]
>     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera6Thread4execEv+0x8a) [0x7ffb28de4396]
>     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private3runEv+0x104) [0x7ffb28d4c0d4]
>     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera6Thread11startThreadEv+0xe5) [0x7ffb28de42ff]
>     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZSt13__invoke_implIvMN9libcamera6ThreadEFvvEPS1_JEET_St21__invoke_memfun_derefOT0_OT1_DpOT2_+0x67) [0x7ffb28de8c2b]
>     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZSt8__invokeIMN9libcamera6ThreadEFvvEJPS1_EENSt15__invoke_resultIT_JDpT0_EE4typeEOS6_DpOS7_+0x37) [0x7ffb28de8b6d]
>     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZNSt6thread8_InvokerISt5tupleIJMN9libcamera6ThreadEFvvEPS3_EEE9_M_invokeIJLm0ELm1EEEEvSt12_Index_tupleIJXspT_EEE+0x43) [0x7ffb28de8add]
>     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZNSt6thread8_InvokerISt5tupleIJMN9libcamera6ThreadEFvvEPS3_EEEclEv+0x18) [0x7ffb28de8a96]
>     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZNSt6thread11_State_implINS_8_InvokerISt5tupleIJMN9libcamera6ThreadEFvvEPS4_EEEEE6_M_runEv+0x1c) [0x7ffb28de8a7a]
>     /usr/lib/libstdc++.so.6(+0xcfb74) [0x7ffb27582b74]
>     /usr/lib/libpthread.so.0(+0x9422) [0x7ffb26f7d422]
>     /usr/lib/libc.so.6(clone+0x43) [0x7ffb273cfbf3]
Yes, I am aware of QCam issue. I suppose you only had one camera(i.e. 
UVC) on the system and trying to plug/unplug that?
Or did you have multiple ?

QCam still has some corners cases with one camera attached to the system 
when I looked at it last time.
>
>> ---
>>   src/cam/main.cpp | 24 ++++++++++++++++++++++++
>>   src/cam/main.h   |  1 +
>>   2 files changed, 25 insertions(+)
>>
>> diff --git a/src/cam/main.cpp b/src/cam/main.cpp
>> index 2512fe9..3488315 100644
>> --- a/src/cam/main.cpp
>> +++ b/src/cam/main.cpp
>> @@ -36,6 +36,8 @@ public:
>>   	void quit();
>>   
>>   private:
>> +	void cameraAdded(std::shared_ptr<Camera> cam);
>> +	void cameraRemoved(std::shared_ptr<Camera> cam);
>>   	int parseOptions(int argc, char *argv[]);
>>   	int prepareConfig();
>>   	int listControls();
>> @@ -115,6 +117,10 @@ int CamApp::init(int argc, char **argv)
>>   		ret = prepareConfig();
>>   		if (ret)
>>   			return ret;
>> +	} else if (options_.isSet(OptMonitor)) {
>> +		cm_->cameraAdded.connect(this, &CamApp::cameraAdded);
>> +		cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved);
>> +		std::cout << "Monitoring new hotplug and unplug events..." << std::endl;
>>   	}
>>   
>>   	loop_ = new EventLoop(cm_->eventDispatcher());
>> @@ -179,6 +185,8 @@ int CamApp::parseOptions(int argc, char *argv[])
>>   			 "list-controls");
>>   	parser.addOption(OptListProperties, OptionNone, "List cameras properties",
>>   			 "list-properties");
>> +	parser.addOption(OptMonitor, OptionNone, "Monitor for hotplug and unplug camera events",
>> +			 "monitor");
>>   
>>   	options_ = parser.parse(argc, argv);
>>   	if (!options_.valid())
>> @@ -293,6 +301,16 @@ int CamApp::infoConfiguration()
>>   	return 0;
>>   }
>>   
>> +void CamApp::cameraAdded(std::shared_ptr<Camera> cam)
>> +{
>> +	std::cout << "Camera Added: " << cam->name() << std::endl;
>> +}
>> +
>> +void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)
>> +{
>> +	std::cout << "Camera Removed: " << cam->name() << std::endl;
>> +}
>> +
>>   int CamApp::run()
>>   {
>>   	int ret;
>> @@ -330,6 +348,12 @@ int CamApp::run()
>>   		return capture.run(loop_, options_);
>>   	}
>>   
>> +	if (options_.isSet(OptMonitor)) {
>> +		ret = loop_->exec();
>> +		if (ret)
>> +			std::cout << "Failed to run monitor loop" << std::endl;
>> +	}
>> +
>>   	return 0;
>>   }
>>   
>> diff --git a/src/cam/main.h b/src/cam/main.h
>> index 4a130d8..f61d288 100644
>> --- a/src/cam/main.h
>> +++ b/src/cam/main.h
>> @@ -15,6 +15,7 @@ enum {
>>   	OptInfo = 'I',
>>   	OptList = 'l',
>>   	OptListProperties = 'p',
>> +	OptMonitor = 'm',
>>   	OptStream = 's',
>>   	OptListControls = 256,
>>   };
>> -- 
>> 2.26.2
>>
>> _______________________________________________
>> libcamera-devel mailing list
>> libcamera-devel@lists.libcamera.org
>> https://lists.libcamera.org/listinfo/libcamera-devel
Niklas Söderlund July 13, 2020, 6:21 p.m. UTC | #4
Hi Umang,

On 2020-07-13 15:53:15 +0000, Umang Jain wrote:
> Hi Niklas,
> 
> Thanks for the review.
> 
> On 7/13/20 7:35 PM, Niklas Söderlund wrote:
> > Hi Umang,
> > 
> > Thanks for your work.
> > 
> > On 2020-07-13 12:11:28 +0000, Umang Jain wrote:
> > > Add --monitor to monitor new hotplug and unplug camera events from
> > > the CameraManager.
> > > 
> > > Signed-off-by: Umang Jain <email@uajain.com>
> > First off, for this patch.
> > 
> > Reviewed-by: Niklas Söderlund <niklas.soderlund@ragnatech.se>
> > 
> > When testing this patch to discover how far hotplug support have come I
> > noticed some issues. I'm not aware if you know about them or not so I
> > will describe them here.
> > 
> > 1. Error message printed from cam
> > 
> >     When I run cam --monitor and physically unplug and then replug a USB
> >     UVC camera I get the following ERROR:
> > 
> >     $ cam --monitor
> > 
> >     * unplug camera *
> >     Camera Removed: Logitech Webcam C930e
> >     [465:45:06.515424906] [593092] ERROR V4L2 v4l2_videodevice.cpp:1117 /dev/video0[cap]: Unable to request 0 buffers: No such device
> > 
> >     * plug camera *
> >     Camera Added: Logitech Webcam C930e
> > 
> >     Would it be possible to not print the error when unplugging? I
> >     understand the IOCTL can never succeed as the vidoe device is gone
> >     but maybe we could avoid logging the error.
> hmm, I am not sure if we can suppress logging that error without much
> invasive changes.
> On looking around, I noticed there couple of places where
> V4L2VideoDevice::requestBuffers()
> is called, with request buffer count = 0. This particular one, is being
> called from releaseBuffers.
> > 
> > 2. Qcam hits a delayed assert with hot-plug
> > 
> >     Once more the USB UVC camera is used.
> > 
> >     $ qcam
> >     [465:59:29.856991110] [599647]  INFO IPAManager ipa_manager.cpp:136 libcamera is not installed. Adding '/home/neg/work/libcamera/libcamera/build/normal/src/ipa' to the IPA search path
> >     [465:59:29.858010955] [599647]  INFO Camera camera_manager.cpp:283 libcamera v0.0.0+1602-7a13375c
> >     [465:59:29.870436670] [599649]  INFO IPAProxy ipa_proxy.cpp:122 libcamera is not installed. Loading IPA configuration from '/home/neg/work/libcamera/libcamera/src/ipa/vimc/data'
> >     [465:59:36.255358189] [599647]  INFO Camera camera.cpp:770 configuring streams: (0) 1920x1080-MJPEG
> >     Using software format conversion from MJPEG
> >     seq: 000000 bytesused: 151768 timestamp: 1677576692011000 fps: 0.00
> >     seq: 000001 bytesused: 157570 timestamp: 1677576715997000 fps: 41.69
> >     seq: 000002 bytesused: 158088 timestamp: 1677576751996000 fps: 27.78
> >     seq: 000003 bytesused: 158089 timestamp: 1677576783994000 fps: 31.25
> >     ....
> >     seq: 000080 bytesused: 167393 timestamp: 1677579347940000 fps: 31.25
> >     seq: 000081 bytesused: 167030 timestamp: 1677579383939000 fps: 27.78
> >     seq: 000082 bytesused: 167221 timestamp: 1677579415938000 fps: 31.25
> >     seq: 000083 bytesused: 166575 timestamp: 1677579447937000 fps: 31.25
> >     seq: 000084 bytesused: 165611 timestamp: 1677579483936000 fps: 27.78
> >     seq: 000085 bytesused: 166295 timestamp: 1677579515936000 fps: 31.25
> > 
> >     * unplug camera *
> >     Removing camera: Logitech Webcam C930e
> >     Failed to stop capture
> > 
> >     * wait a while *
> > 
> >     * close qcam *
> >     [465:59:47.587993893] [599647] ERROR V4L2 v4l2_videodevice.cpp:1117 /dev/video0[cap]: Unable to request 0 buffers: No such device
> >     [465:59:47.588042533] [599649] FATAL default event_dispatcher_poll.cpp:258 assertion "iter != notifiers_.end()" failed
> >     Backtrace:
> >     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera19EventDispatcherPoll16processNotifiersERKSt6vectorI6pollfdSaIS2_EE+0x150) [0x7ffb28d9ae52]
> >     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera19EventDispatcherPoll13processEventsEv+0x2b9) [0x7ffb28d9a75f]
> >     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera6Thread4execEv+0x8a) [0x7ffb28de4396]
> >     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera13CameraManager7Private3runEv+0x104) [0x7ffb28d4c0d4]
> >     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZN9libcamera6Thread11startThreadEv+0xe5) [0x7ffb28de42ff]
> >     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZSt13__invoke_implIvMN9libcamera6ThreadEFvvEPS1_JEET_St21__invoke_memfun_derefOT0_OT1_DpOT2_+0x67) [0x7ffb28de8c2b]
> >     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZSt8__invokeIMN9libcamera6ThreadEFvvEJPS1_EENSt15__invoke_resultIT_JDpT0_EE4typeEOS6_DpOS7_+0x37) [0x7ffb28de8b6d]
> >     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZNSt6thread8_InvokerISt5tupleIJMN9libcamera6ThreadEFvvEPS3_EEE9_M_invokeIJLm0ELm1EEEEvSt12_Index_tupleIJXspT_EEE+0x43) [0x7ffb28de8add]
> >     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZNSt6thread8_InvokerISt5tupleIJMN9libcamera6ThreadEFvvEPS3_EEEclEv+0x18) [0x7ffb28de8a96]
> >     /home/neg/work/libcamera/libcamera/build/normal/src/qcam/../libcamera/libcamera.so(_ZNSt6thread11_State_implINS_8_InvokerISt5tupleIJMN9libcamera6ThreadEFvvEPS4_EEEEE6_M_runEv+0x1c) [0x7ffb28de8a7a]
> >     /usr/lib/libstdc++.so.6(+0xcfb74) [0x7ffb27582b74]
> >     /usr/lib/libpthread.so.0(+0x9422) [0x7ffb26f7d422]
> >     /usr/lib/libc.so.6(clone+0x43) [0x7ffb273cfbf3]
> Yes, I am aware of QCam issue. I suppose you only had one camera(i.e. UVC)
> on the system and trying to plug/unplug that?
> Or did you have multiple ?

I have two cameras in my system for this test a uvc and vimc pipeline.

$ cam -l
Available cameras:
1: Logitech Webcam C930e
2: VIMC Sensor B

If I remove the vimc kernel driver and thus only have one (uvc) camera 
and run the test described above I get the same result.

> 
> QCam still has some corners cases with one camera attached to the system
> when I looked at it last time.
> > 
> > > ---
> > >   src/cam/main.cpp | 24 ++++++++++++++++++++++++
> > >   src/cam/main.h   |  1 +
> > >   2 files changed, 25 insertions(+)
> > > 
> > > diff --git a/src/cam/main.cpp b/src/cam/main.cpp
> > > index 2512fe9..3488315 100644
> > > --- a/src/cam/main.cpp
> > > +++ b/src/cam/main.cpp
> > > @@ -36,6 +36,8 @@ public:
> > >   	void quit();
> > >   private:
> > > +	void cameraAdded(std::shared_ptr<Camera> cam);
> > > +	void cameraRemoved(std::shared_ptr<Camera> cam);
> > >   	int parseOptions(int argc, char *argv[]);
> > >   	int prepareConfig();
> > >   	int listControls();
> > > @@ -115,6 +117,10 @@ int CamApp::init(int argc, char **argv)
> > >   		ret = prepareConfig();
> > >   		if (ret)
> > >   			return ret;
> > > +	} else if (options_.isSet(OptMonitor)) {
> > > +		cm_->cameraAdded.connect(this, &CamApp::cameraAdded);
> > > +		cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved);
> > > +		std::cout << "Monitoring new hotplug and unplug events..." << std::endl;
> > >   	}
> > >   	loop_ = new EventLoop(cm_->eventDispatcher());
> > > @@ -179,6 +185,8 @@ int CamApp::parseOptions(int argc, char *argv[])
> > >   			 "list-controls");
> > >   	parser.addOption(OptListProperties, OptionNone, "List cameras properties",
> > >   			 "list-properties");
> > > +	parser.addOption(OptMonitor, OptionNone, "Monitor for hotplug and unplug camera events",
> > > +			 "monitor");
> > >   	options_ = parser.parse(argc, argv);
> > >   	if (!options_.valid())
> > > @@ -293,6 +301,16 @@ int CamApp::infoConfiguration()
> > >   	return 0;
> > >   }
> > > +void CamApp::cameraAdded(std::shared_ptr<Camera> cam)
> > > +{
> > > +	std::cout << "Camera Added: " << cam->name() << std::endl;
> > > +}
> > > +
> > > +void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)
> > > +{
> > > +	std::cout << "Camera Removed: " << cam->name() << std::endl;
> > > +}
> > > +
> > >   int CamApp::run()
> > >   {
> > >   	int ret;
> > > @@ -330,6 +348,12 @@ int CamApp::run()
> > >   		return capture.run(loop_, options_);
> > >   	}
> > > +	if (options_.isSet(OptMonitor)) {
> > > +		ret = loop_->exec();
> > > +		if (ret)
> > > +			std::cout << "Failed to run monitor loop" << std::endl;
> > > +	}
> > > +
> > >   	return 0;
> > >   }
> > > diff --git a/src/cam/main.h b/src/cam/main.h
> > > index 4a130d8..f61d288 100644
> > > --- a/src/cam/main.h
> > > +++ b/src/cam/main.h
> > > @@ -15,6 +15,7 @@ enum {
> > >   	OptInfo = 'I',
> > >   	OptList = 'l',
> > >   	OptListProperties = 'p',
> > > +	OptMonitor = 'm',
> > >   	OptStream = 's',
> > >   	OptListControls = 256,
> > >   };
> > > -- 
> > > 2.26.2
> > > 
> > > _______________________________________________
> > > libcamera-devel mailing list
> > > libcamera-devel@lists.libcamera.org
> > > https://lists.libcamera.org/listinfo/libcamera-devel

Patch

diff --git a/src/cam/main.cpp b/src/cam/main.cpp
index 2512fe9..3488315 100644
--- a/src/cam/main.cpp
+++ b/src/cam/main.cpp
@@ -36,6 +36,8 @@  public:
 	void quit();
 
 private:
+	void cameraAdded(std::shared_ptr<Camera> cam);
+	void cameraRemoved(std::shared_ptr<Camera> cam);
 	int parseOptions(int argc, char *argv[]);
 	int prepareConfig();
 	int listControls();
@@ -115,6 +117,10 @@  int CamApp::init(int argc, char **argv)
 		ret = prepareConfig();
 		if (ret)
 			return ret;
+	} else if (options_.isSet(OptMonitor)) {
+		cm_->cameraAdded.connect(this, &CamApp::cameraAdded);
+		cm_->cameraRemoved.connect(this, &CamApp::cameraRemoved);
+		std::cout << "Monitoring new hotplug and unplug events..." << std::endl;
 	}
 
 	loop_ = new EventLoop(cm_->eventDispatcher());
@@ -179,6 +185,8 @@  int CamApp::parseOptions(int argc, char *argv[])
 			 "list-controls");
 	parser.addOption(OptListProperties, OptionNone, "List cameras properties",
 			 "list-properties");
+	parser.addOption(OptMonitor, OptionNone, "Monitor for hotplug and unplug camera events",
+			 "monitor");
 
 	options_ = parser.parse(argc, argv);
 	if (!options_.valid())
@@ -293,6 +301,16 @@  int CamApp::infoConfiguration()
 	return 0;
 }
 
+void CamApp::cameraAdded(std::shared_ptr<Camera> cam)
+{
+	std::cout << "Camera Added: " << cam->name() << std::endl;
+}
+
+void CamApp::cameraRemoved(std::shared_ptr<Camera> cam)
+{
+	std::cout << "Camera Removed: " << cam->name() << std::endl;
+}
+
 int CamApp::run()
 {
 	int ret;
@@ -330,6 +348,12 @@  int CamApp::run()
 		return capture.run(loop_, options_);
 	}
 
+	if (options_.isSet(OptMonitor)) {
+		ret = loop_->exec();
+		if (ret)
+			std::cout << "Failed to run monitor loop" << std::endl;
+	}
+
 	return 0;
 }
 
diff --git a/src/cam/main.h b/src/cam/main.h
index 4a130d8..f61d288 100644
--- a/src/cam/main.h
+++ b/src/cam/main.h
@@ -15,6 +15,7 @@  enum {
 	OptInfo = 'I',
 	OptList = 'l',
 	OptListProperties = 'p',
+	OptMonitor = 'm',
 	OptStream = 's',
 	OptListControls = 256,
 };