[libcamera-devel] [PATCH 3/3] simple-cam: Provide event-loop backed by libevent

Umang Jain email at uajain.com
Tue Nov 24 06:58:17 CET 2020


Hi Kieran

On 11/24/20 3:05 AM, Kieran Bingham wrote:
> Hi Umang,
>
> On 19/11/2020 12:34, Umang Jain wrote:
>> libcamera moved its EventDispatcher and Timer API to its internal API,
>> since providing an event loop to applications should not be the job of
>> libcamera. Application utility like cam, were ported to use libevent,
>> hence inspired from that, un-break simple-cam by using the similar
>> implementation to replace the EventDispatcher and Timer functionality
>> by libevent.
>>
> Hrm, "simple" cam is starting to look not so simple ...
> but that's not your fault.
>
> The only thing holding me back on this patch is the idea that perhaps it
> might be 'simpler' (in the context of the actual libcamera parts) if the
> event handling was separated.
>
> But handling the events is somewhat now critical anyway.
>
> I guess the only way to separate the event handling, and still have it
> all as one file would be to put the event handling in it's own class
> (and the implementation inline in the class definition) to keep that all
> separate.
>
> I suspect that might get bulky too though.
I hear you. I actually started out this, with a "EventLoop" class that 
wraps the libevent specifics, similar to cam. But indeed, it turned out 
to be bulkier hence, I went the minimalistic libevent implementation way 
(without a wrapper) and still I don't like it 100%.
>
> I'm tempted to say we just merge this and fix the compilation faults,
> and consider later if we have better ways to re-simplify it again.
>
> Thoughts on a post-card anyone?
>
> Otherwise, I'll likely merge to get things working again tomorrow.
> --
> Kieran.
>
>
>
>
>> Signed-off-by: Umang Jain <email at uajain.com>
>> ---
>>   meson.build    |   1 +
>>   simple-cam.cpp | 100 +++++++++++++++++++++++++++++++++++++++++++------
>>   2 files changed, 90 insertions(+), 11 deletions(-)
>>
>> diff --git a/meson.build b/meson.build
>> index c312f2c..0eca0a1 100644
>> --- a/meson.build
>> +++ b/meson.build
>> @@ -14,6 +14,7 @@ src_files = files([
>>   # libcamera install path camera.pc file ($prefix/lib/pkgconfig/camera.pc)
>>   libcamera_deps = [
>>         dependency('camera', required : true),
>> +      dependency('libevent_pthreads', required : true),
>>   ]
>>   
>>   cpp_arguments = [ '-Wno-unused-parameter', ]
>> diff --git a/simple-cam.cpp b/simple-cam.cpp
>> index 65e0f14..7fbb890 100644
>> --- a/simple-cam.cpp
>> +++ b/simple-cam.cpp
>> @@ -8,12 +8,57 @@
>>   #include <iomanip>
>>   #include <iostream>
>>   #include <memory>
>> +#include <mutex>
>> +
>> +#include <event2/event.h>
>> +#include <event2/thread.h>
>>   
>>   #include <libcamera/libcamera.h>
>>   
>> +#define TIMEOUT_SEC 3
>> +
>>   using namespace libcamera;
>>   std::shared_ptr<Camera> camera;
>>   
>> +/*
>> + * --------------------------------------------------------------------
>> + * Helper functions to interact and control the event loop.
>> + */
>> +bool capture = true;
>> +std::mutex lock;
>> +std::list<std::function<void()>> calls_;
>> +struct event_base *event;
>> +
>> +void interrupt()
>> +{
>> +	event_base_loopbreak(event);
>> +}
>> +
>> +void callLater(const std::function<void()> &func)
>> +{
>> +	{
>> +		std::unique_lock<std::mutex> locker(lock);
>> +		calls_.push_back(func);
>> +	}
>> +
>> +	interrupt();
>> +}
>> +
>> +void dispatchCalls()
>> +{
>> +	std::unique_lock<std::mutex> locker(lock);
>> +
>> +	for (auto iter = calls_.begin(); iter != calls_.end(); ) {
>> +		std::function<void()> call = std::move(*iter);
>> +
>> +		iter = calls_.erase(iter);
>> +
>> +		locker.unlock();
>> +		call();
>> +		locker.lock();
>> +	}
>> +}
>> +
>>   /*
>>    * --------------------------------------------------------------------
>>    * Handle RequestComplete
>> @@ -21,13 +66,26 @@ std::shared_ptr<Camera> camera;
>>    * For each Camera::requestCompleted Signal emitted from the Camera the
>>    * connected Slot is invoked.
>>    *
>> + * The Slot is invoked in the CameraManager's thread, hence one should avoid
>> + * any heavy processing here. The processing of the request shall be re-directed
>> + * to the application's thread instead, so as not to block the CameraManager's
>> + * thread for large amount of time.
>> + *
>>    * The Slot receives the Request as a parameter.
>>    */
>> +
>> +static void processRequest(Request *request);
>> +
>>   static void requestComplete(Request *request)
>>   {
>>   	if (request->status() == Request::RequestCancelled)
>>   		return;
>>   
>> +	callLater(std::bind(&processRequest, request));
>> +}
>> +
>> +static void processRequest(Request *request)
>> +{
>>   	const Request::BufferMap &buffers = request->buffers();
>>   
>>   	for (auto bufferPair : buffers) {
>> @@ -60,6 +118,15 @@ static void requestComplete(Request *request)
>>   	camera->queueRequest(request);
>>   }
>>   
>> +void timeoutTriggered(int fd, short event, void *arg)
>> +{
>> +	capture = false;
>> +	interrupt();
>> +
>> +	std::cout << "Capture ran for " << TIMEOUT_SEC << " seconds, Exiting."
>> +		  << std::endl;
>> +}
>> +
>>   int main()
>>   {
>>   	/*
>> @@ -321,18 +388,25 @@ int main()
>>   	 * In order to dispatch events received from the video devices, such
>>   	 * as buffer completions, an event loop has to be run.
>>   	 *
>> -	 * Libcamera provides its own default event dispatcher realized by
>> -	 * polling a set of file descriptors, but applications can integrate
>> -	 * their own even loop with the Libcamera EventDispatcher.
>> -	 *
>> -	 * Here, as an example, run the poll-based EventDispatcher for 3
>> -	 * seconds.
>> +	 * simple-cam uses a base event loop provided by libevent, which is run
>> +	 * for TIMEOUT_SEC seconds after which, the event loop is interrupted
>> +	 * via interrupt() and begins the cleanup. The interval for which the
>> +	 * event loop is being run, completed requests are processed via
>> +	 * requestComplete() handler.
>>   	 */
>> -	EventDispatcher *dispatcher = cm->eventDispatcher();
>> -	Timer timer;
>> -	timer.start(3000);
>> -	while (timer.isRunning())
>> -		dispatcher->processEvents();
>> +	struct event *ev;
>> +	struct timeval tv;
>> +
>> +	tv.tv_sec = TIMEOUT_SEC;
>> +	tv.tv_usec = 0;
>> +	evthread_use_pthreads();
>> +	event = event_base_new();
>> +	ev = event_new(event, -1, EV_TIMEOUT, &timeoutTriggered, NULL);
>> +	event_add(ev, &tv);
>> +	while (capture) {
>> +		dispatchCalls();
>> +		event_base_loop(event, EVLOOP_NO_EXIT_ON_EMPTY);
>> +	}
>>   
>>   	/*
>>   	 * --------------------------------------------------------------------
>> @@ -341,6 +415,10 @@ int main()
>>   	 * Stop the Camera, release resources and stop the CameraManager.
>>   	 * Libcamera has now released all resources it owned.
>>   	 */
>> +	event_free(ev);
>> +	event_base_free(event);
>> +	libevent_global_shutdown();
>> +
>>   	camera->stop();
>>   	allocator->free(stream);
>>   	delete allocator;
>>



More information about the libcamera-devel mailing list