[libcamera-devel,v2,3/3] Regard a request error in the request completion
diff mbox series

Message ID 20210329091552.157208-4-hiroh@chromium.org
State Changes Requested
Headers show
Series
  • Add RequestError to Request::Status
Related show

Commit Message

Hirokazu Honda March 29, 2021, 9:15 a.m. UTC
libcamera::Request contains an error value. Every
libcamera::Camera client should regards the error in a
request completion.

Signed-off-by: Hirokazu Honda <hiroh@chromium.org>
---
 Documentation/guides/application-developer.rst | 2 ++
 src/cam/capture.cpp                            | 5 +++++
 src/gstreamer/gstlibcamerasrc.cpp              | 6 +++++-
 src/qcam/main_window.cpp                       | 4 ++++
 src/v4l2/v4l2_camera.cpp                       | 5 +++++
 5 files changed, 21 insertions(+), 1 deletion(-)

Comments

Kieran Bingham March 29, 2021, 10:27 a.m. UTC | #1
Hi Hiro,

On 29/03/2021 10:15, Hirokazu Honda wrote:
> libcamera::Request contains an error value. Every
> libcamera::Camera client should regards the error in a
> request completion.
> 
> Signed-off-by: Hirokazu Honda <hiroh@chromium.org>
> ---
>  Documentation/guides/application-developer.rst | 2 ++
>  src/cam/capture.cpp                            | 5 +++++
>  src/gstreamer/gstlibcamerasrc.cpp              | 6 +++++-
>  src/qcam/main_window.cpp                       | 4 ++++
>  src/v4l2/v4l2_camera.cpp                       | 5 +++++
>  5 files changed, 21 insertions(+), 1 deletion(-)
> 
> diff --git a/Documentation/guides/application-developer.rst b/Documentation/guides/application-developer.rst
> index e3430f03..e8a1a7b4 100644
> --- a/Documentation/guides/application-developer.rst
> +++ b/Documentation/guides/application-developer.rst
> @@ -392,6 +392,8 @@ the `Request::Status`_ class enum documentation.
>  
>     if (request->status() == Request::RequestCancelled)
>        return;
> +   if (request->status() == Request::RequestError)
> +      // handle error.

Even if it's just documentation, we should wrap that // handle error. in
{ } as it leaves the if () statement applying to whatever would happen
next otherwise...



>  If the ``Request`` has completed successfully, applications can access the
>  completed buffers using the ``Request::buffers()`` function, which returns a map
> diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp
> index 7b55fc67..3e9edf80 100644
> --- a/src/cam/capture.cpp
> +++ b/src/cam/capture.cpp
> @@ -167,6 +167,11 @@ void Capture::requestComplete(Request *request)
>  {
>  	if (request->status() == Request::RequestCancelled)
>  		return;
> +	/* TODO: Handle an error correctly */
> +	if (request->status() == Request::RequestError) {
> +		std::cout << "Failed to capture request: " << request->cookie();
> +		return;

I don't think we can let this return early here. It would cause us to
drop errorred frames, and then run out of active requests.

If a Request status is RequestError, then we need to decide if the frame
is to be dropped or displayed regardless. (which is an application
choice, rather than a libcamera choice). That's fine as we're here in cam..

An application which wants to display only 'good' frames would likely
choose to not display this frame, but it would then need to make sure
that a new request would be queued still.

For cam, I think we *should* track errored frames, as they are important
in testing.

So this should certainly call into Capture::processRequest(), and likely
print a notice saying that this frame was an error. And still save it
accordingly...

Although, I'm not sure what RequestError means yet.
Does it mean there was an error and the Request wasn't processed
completely, or that it wasn't processed at all.

Perhaps the use case for a buffer not completely being processed is
already handled by the buffer status...



> +	}
>  
>  	/*
>  	 * Defer processing of the completed request to the event loop, to avoid
> diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
> index 87246b40..1ecb9883 100644
> --- a/src/gstreamer/gstlibcamerasrc.cpp
> +++ b/src/gstreamer/gstlibcamerasrc.cpp
> @@ -163,10 +163,14 @@ GstLibcameraSrcState::requestCompleted(Request *request)
>  
>  	g_return_if_fail(wrap->request_.get() == request);
>  
> -	if ((request->status() == Request::RequestCancelled)) {
> +	if (request->status() == Request::RequestCancelled) {
>  		GST_DEBUG_OBJECT(src_, "Request was cancelled");
>  		return;
>  	}
> +	if (request->status() == Request::RequestError) {
> +		GST_ERROR_OBJECT(src_, "Request doesn't complete successfully");

s/doesn't/didn't/

I haven't looked into how gstlibcamerasrc uses it's requests yet, but if
this leaks a request and causes the pipeline to stall that would be bad too.


> +		return;
> +	}
>  
>  	GstBuffer *buffer;
>  	for (GstPad *srcpad : srcpads_) {
> diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp
> index 39d034de..1288bcd5 100644
> --- a/src/qcam/main_window.cpp
> +++ b/src/qcam/main_window.cpp
> @@ -694,6 +694,10 @@ void MainWindow::requestComplete(Request *request)
>  {
>  	if (request->status() == Request::RequestCancelled)
>  		return;
> +	if (request->status() == Request::RequestError) {
> +		qCritical() << "Request doesn't complete successfully";

s/doesn't/didn't/

And the same concerns of course.

> +		return;
> +	}
>  
>  	/*
>  	 * We're running in the libcamera thread context, expensive operations
> diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
> index 97825c71..d6b1d755 100644
> --- a/src/v4l2/v4l2_camera.cpp
> +++ b/src/v4l2/v4l2_camera.cpp
> @@ -84,6 +84,11 @@ void V4L2Camera::requestComplete(Request *request)
>  {
>  	if (request->status() == Request::RequestCancelled)
>  		return;
> +	if (request->status() == Request::RequestError) {
> +		LOG(V4L2Compat, Error)
> +			<< "Request doesn't complete successfully";

And same ;-)

> +		return;
> +	}
>  
>  	/* We only have one stream at the moment. */
>  	bufferLock_.lock();
>
Laurent Pinchart April 3, 2021, 1:27 a.m. UTC | #2
Hi Kieran and Hiro,

On Mon, Mar 29, 2021 at 11:27:54AM +0100, Kieran Bingham wrote:
> On 29/03/2021 10:15, Hirokazu Honda wrote:
> > libcamera::Request contains an error value. Every
> > libcamera::Camera client should regards the error in a
> > request completion.
> > 
> > Signed-off-by: Hirokazu Honda <hiroh@chromium.org>
> > ---
> >  Documentation/guides/application-developer.rst | 2 ++
> >  src/cam/capture.cpp                            | 5 +++++
> >  src/gstreamer/gstlibcamerasrc.cpp              | 6 +++++-
> >  src/qcam/main_window.cpp                       | 4 ++++
> >  src/v4l2/v4l2_camera.cpp                       | 5 +++++
> >  5 files changed, 21 insertions(+), 1 deletion(-)
> > 
> > diff --git a/Documentation/guides/application-developer.rst b/Documentation/guides/application-developer.rst
> > index e3430f03..e8a1a7b4 100644
> > --- a/Documentation/guides/application-developer.rst
> > +++ b/Documentation/guides/application-developer.rst
> > @@ -392,6 +392,8 @@ the `Request::Status`_ class enum documentation.
> >  
> >     if (request->status() == Request::RequestCancelled)
> >        return;
> > +   if (request->status() == Request::RequestError)
> > +      // handle error.
> 
> Even if it's just documentation, we should wrap that // handle error. in
> { } as it leaves the if () statement applying to whatever would happen
> next otherwise...
> 
> >  If the ``Request`` has completed successfully, applications can access the
> >  completed buffers using the ``Request::buffers()`` function, which returns a map
> > diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp
> > index 7b55fc67..3e9edf80 100644
> > --- a/src/cam/capture.cpp
> > +++ b/src/cam/capture.cpp
> > @@ -167,6 +167,11 @@ void Capture::requestComplete(Request *request)
> >  {
> >  	if (request->status() == Request::RequestCancelled)
> >  		return;
> > +	/* TODO: Handle an error correctly */

We use the doxygen syntax, /* \todo ... */

> > +	if (request->status() == Request::RequestError) {
> > +		std::cout << "Failed to capture request: " << request->cookie();
> > +		return;
> 
> I don't think we can let this return early here. It would cause us to
> drop errorred frames, and then run out of active requests.
> 
> If a Request status is RequestError, then we need to decide if the frame
> is to be dropped or displayed regardless. (which is an application
> choice, rather than a libcamera choice). That's fine as we're here in cam..

I think we need to take a step back, and reconsider the design of error
reporting. We currently have a request status, which can only report
successful completion or full cancellation of the request, and we have a
buffer status that can report erroneous buffers (without any additional
information). The two don't really need to interact together. With the
introduction of RequestError, this situation changes, and the
interaction of buffer status and request status needs to be
reconsidered. I'd like to see a design proposal for that (as part of
patch 2/3), that would consider the combination of statuses that are
allowed, and what each of the possible combinations mean. For instance
(starting brainstorming here), we could consider that RequestError
signals an error at the request level that means that no buffer have
been written (all the buffer metadata would then be invalid), or we
could consider that any buffer that is erroneous would result in the
request being marked with RequestError (in which case applications would
need to inspect individual buffers). This needs to be designed by
considering use cases, including the error notification mechanism of the
Android camera HAL, as well as ease of use for applications. We can also
consider adding additional error information (possibly as part of this
series, or later on top), to differentiate, for instance, between fatal
errors that indicate that the camera has become unusable, and errors
that are specific to a request but won't affect further requests (is
this something that can ever happen ?). This can be done with additional
information (an error code maybe ?), or additional request statuses.
Finally, if we decide to add error codes to requests, we may consider
turning RequestCancel into an error code.

This is a bit of work, an RFC with a design proposal (or multiple
options to choose from) and an explanation of the design rationale and
associated use cases would help.

> An application which wants to display only 'good' frames would likely
> choose to not display this frame, but it would then need to make sure
> that a new request would be queued still.
> 
> For cam, I think we *should* track errored frames, as they are important
> in testing.
> 
> So this should certainly call into Capture::processRequest(), and likely
> print a notice saying that this frame was an error. And still save it
> accordingly...
> 
> Although, I'm not sure what RequestError means yet.
> Does it mean there was an error and the Request wasn't processed
> completely, or that it wasn't processed at all.
> 
> Perhaps the use case for a buffer not completely being processed is
> already handled by the buffer status...
> 
> > +	}
> >  
> >  	/*
> >  	 * Defer processing of the completed request to the event loop, to avoid
> > diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
> > index 87246b40..1ecb9883 100644
> > --- a/src/gstreamer/gstlibcamerasrc.cpp
> > +++ b/src/gstreamer/gstlibcamerasrc.cpp
> > @@ -163,10 +163,14 @@ GstLibcameraSrcState::requestCompleted(Request *request)
> >  
> >  	g_return_if_fail(wrap->request_.get() == request);
> >  
> > -	if ((request->status() == Request::RequestCancelled)) {
> > +	if (request->status() == Request::RequestCancelled) {
> >  		GST_DEBUG_OBJECT(src_, "Request was cancelled");
> >  		return;
> >  	}
> > +	if (request->status() == Request::RequestError) {
> > +		GST_ERROR_OBJECT(src_, "Request doesn't complete successfully");
> 
> s/doesn't/didn't/
> 
> I haven't looked into how gstlibcamerasrc uses it's requests yet, but if
> this leaks a request and causes the pipeline to stall that would be bad too.
> 
> > +		return;
> > +	}
> >  
> >  	GstBuffer *buffer;
> >  	for (GstPad *srcpad : srcpads_) {
> > diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp
> > index 39d034de..1288bcd5 100644
> > --- a/src/qcam/main_window.cpp
> > +++ b/src/qcam/main_window.cpp
> > @@ -694,6 +694,10 @@ void MainWindow::requestComplete(Request *request)
> >  {
> >  	if (request->status() == Request::RequestCancelled)
> >  		return;
> > +	if (request->status() == Request::RequestError) {
> > +		qCritical() << "Request doesn't complete successfully";
> 
> s/doesn't/didn't/
> 
> And the same concerns of course.
> 
> > +		return;
> > +	}
> >  
> >  	/*
> >  	 * We're running in the libcamera thread context, expensive operations
> > diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
> > index 97825c71..d6b1d755 100644
> > --- a/src/v4l2/v4l2_camera.cpp
> > +++ b/src/v4l2/v4l2_camera.cpp
> > @@ -84,6 +84,11 @@ void V4L2Camera::requestComplete(Request *request)
> >  {
> >  	if (request->status() == Request::RequestCancelled)
> >  		return;
> > +	if (request->status() == Request::RequestError) {
> > +		LOG(V4L2Compat, Error)
> > +			<< "Request doesn't complete successfully";
> 
> And same ;-)
> 
> > +		return;
> > +	}
> >  
> >  	/* We only have one stream at the moment. */
> >  	bufferLock_.lock();

Patch
diff mbox series

diff --git a/Documentation/guides/application-developer.rst b/Documentation/guides/application-developer.rst
index e3430f03..e8a1a7b4 100644
--- a/Documentation/guides/application-developer.rst
+++ b/Documentation/guides/application-developer.rst
@@ -392,6 +392,8 @@  the `Request::Status`_ class enum documentation.
 
    if (request->status() == Request::RequestCancelled)
       return;
+   if (request->status() == Request::RequestError)
+      // handle error.
 
 If the ``Request`` has completed successfully, applications can access the
 completed buffers using the ``Request::buffers()`` function, which returns a map
diff --git a/src/cam/capture.cpp b/src/cam/capture.cpp
index 7b55fc67..3e9edf80 100644
--- a/src/cam/capture.cpp
+++ b/src/cam/capture.cpp
@@ -167,6 +167,11 @@  void Capture::requestComplete(Request *request)
 {
 	if (request->status() == Request::RequestCancelled)
 		return;
+	/* TODO: Handle an error correctly */
+	if (request->status() == Request::RequestError) {
+		std::cout << "Failed to capture request: " << request->cookie();
+		return;
+	}
 
 	/*
 	 * Defer processing of the completed request to the event loop, to avoid
diff --git a/src/gstreamer/gstlibcamerasrc.cpp b/src/gstreamer/gstlibcamerasrc.cpp
index 87246b40..1ecb9883 100644
--- a/src/gstreamer/gstlibcamerasrc.cpp
+++ b/src/gstreamer/gstlibcamerasrc.cpp
@@ -163,10 +163,14 @@  GstLibcameraSrcState::requestCompleted(Request *request)
 
 	g_return_if_fail(wrap->request_.get() == request);
 
-	if ((request->status() == Request::RequestCancelled)) {
+	if (request->status() == Request::RequestCancelled) {
 		GST_DEBUG_OBJECT(src_, "Request was cancelled");
 		return;
 	}
+	if (request->status() == Request::RequestError) {
+		GST_ERROR_OBJECT(src_, "Request doesn't complete successfully");
+		return;
+	}
 
 	GstBuffer *buffer;
 	for (GstPad *srcpad : srcpads_) {
diff --git a/src/qcam/main_window.cpp b/src/qcam/main_window.cpp
index 39d034de..1288bcd5 100644
--- a/src/qcam/main_window.cpp
+++ b/src/qcam/main_window.cpp
@@ -694,6 +694,10 @@  void MainWindow::requestComplete(Request *request)
 {
 	if (request->status() == Request::RequestCancelled)
 		return;
+	if (request->status() == Request::RequestError) {
+		qCritical() << "Request doesn't complete successfully";
+		return;
+	}
 
 	/*
 	 * We're running in the libcamera thread context, expensive operations
diff --git a/src/v4l2/v4l2_camera.cpp b/src/v4l2/v4l2_camera.cpp
index 97825c71..d6b1d755 100644
--- a/src/v4l2/v4l2_camera.cpp
+++ b/src/v4l2/v4l2_camera.cpp
@@ -84,6 +84,11 @@  void V4L2Camera::requestComplete(Request *request)
 {
 	if (request->status() == Request::RequestCancelled)
 		return;
+	if (request->status() == Request::RequestError) {
+		LOG(V4L2Compat, Error)
+			<< "Request doesn't complete successfully";
+		return;
+	}
 
 	/* We only have one stream at the moment. */
 	bufferLock_.lock();