[libcamera-devel] Request and Pipeline::Stop() behavior
Naushir Patuck
naush at raspberrypi.com
Wed Feb 26 16:13:50 CET 2020
Hi Kieran,
On Wed, 26 Feb 2020 at 14:24, Kieran Bingham
<kieran.bingham at ideasonboard.com> wrote:
>
> Hi Naush,
>
> On 26/02/2020 09:03, Naushir Patuck wrote:
> > Hi,
> >
> > Could you help with my understanding on how Requests are handled in
> > vendor pipeline handlers please?
> >
> > Given that multiple requests can be queued, I assume the pipeline
> > handlers must maintain a queue of Request types. Can we use
> > CameraData::queuedRequests_ for this, or is that a libcamera internal
> > queue and we must duplicate?
> >
> > On a PipelineHandler::stop(), I assume we must go through the Request
> > queue and call PipelineHandler::completeRequest() on each Request.
> > However, this will fail if the Request has any outstanding buffers
> > pending for which we need to call PipelineHandler::completeBuffer().
>
> There's another way to look at it. The framework is already in place to
> report cancelled buffers up to complete the request (which gets reported
> as cancelled).
>
> The RkISP1 handles this issue by simply calling streamOff() on all of
> the applicable devices :
>
>
> void PipelineHandlerRkISP1::stop(Camera *camera)
> {
> RkISP1CameraData *data = cameraData(camera);
> int ret;
>
> ret = video_->streamOff();
> if (ret)
> LOG(RkISP1, Warning)
> << "Failed to stop camera " << camera->name();
>
> ret = stat_->streamOff();
> if (ret)
> LOG(RkISP1, Warning)
> << "Failed to stop statistics " << camera->name();
>
> ret = param_->streamOff();
> if (ret)
> LOG(RkISP1, Warning)
> << "Failed to stop parameters " << camera->name();
>
> data->timeline_.reset();
>
> freeBuffers(camera);
>
> activeCamera_ = nullptr;
> }
>
Initially, this is what I was doing as well, but seems it was not enough.
>
> This has the effect that all buffers queued will be marked as
> 'cancelled' and returned as part of V4L2VideoDevice through the
> bufferReady signal:
>
>
> int V4L2VideoDevice::streamOff()
> {
> int ret;
>
> ret = ioctl(VIDIOC_STREAMOFF, &bufferType_);
> if (ret < 0) {
> LOG(V4L2, Error)
> << "Failed to stop streaming: " << strerror(-ret);
> return ret;
> }
>
> /* Send back all queued buffers. */
> for (auto it : queuedBuffers_) {
> FrameBuffer *buffer = it.second;
>
> buffer->metadata_.status = FrameMetadata::FrameCancelled;
> bufferReady.emit(buffer);
> }
>
> queuedBuffers_.clear();
> fdEvent_->setEnabled(false);
>
> return 0;
> }
>
Yes, but I thought that mechanism was only to mark the buffers
FrameMetadata::FrameCancelled, and causes Request::complete() to set
the appropriate stats
void Request::complete()
{
ASSERT(!hasPendingBuffers());
status_ = cancelled_ ? RequestCancelled : RequestComplete;
}
>
> And once all buffers have completed (with their status as cancelled) the
> Request will (I think) automatically be cancelled too.
>
I did not see where this might be happening. From my brief
investigation, CameraData::queuedRequests_ maintains an internal list
of pending requests. The only place this list gets cleared is in
PipelineHandler::completeRequest. So I was under the impression that
my pipeline handler should also maintain a list of Requests queued,
and call PipelineHandler::completeReques() on all those elements
during a Stop(). By the way, I noticed the UVC pipeline handler does
not do this, so I could be completely wrong about this!
void PipelineHandler::completeRequest(Camera *camera, Request *request)
{
request->complete();
CameraData *data = cameraData(camera);
while (!data->queuedRequests_.empty()) {
Request *req = data->queuedRequests_.front();
if (req->status() == Request::RequestPending)
break;
ASSERT(!req->hasPendingBuffers());
data->queuedRequests_.pop_front();
camera->requestComplete(req);
}
}
If I called the above function on all the queued requests, the assert
would fire as the buffers in the request are not deleted via
Request::completeBuffer().
>
> > This in-turn means that each pipeline handler must have a mechanism to
> > go through all Requests and call PipelineHandler::completeBuffer() on
> > all streams. If we call PipelineHandler::completeBuffer() on a stream
> > that has already previously called PipelineHandler::completeBuffer()
> > for an in-flight Request, we hit an assert. So the pipeline handlers
> > must maintain state of what streams/buffers in the Request is
> > completed to clean up correctly in these situations.
> >
> > Now this is not difficult to maintain state in the pipeline handers,
> > but seems like it would be duplicated code for all vendors. Given
> > that there is already a notion of RequestCancelled (in
> > Request::status_), would it make state to have a
> > PipelineHandler::cancelRequest() method that explicitly does a
> > completeBuffer() on all outstanding streams in the Request and markes
> > status_ as RequestCancelled. This would avoid any code duplication in
> > the various vendor pipeline handlers.
>
> If we do indeed need anything generic to handle this in the other
> direction - then I would agree we should offer a cancel() operation on
> the Request which would perform things in the reverse direction, and
> would thus mark all buffers as cancelled (and ideally wait/ensure that
> they are released from hardware) before returning.
>
> But it's the 'release from hardware' part that makes me think
> maintaining the current method of stopping the hardware is appropriate
> in this case.
>
> If in the future we discover we need to stop a single request without
> stopping the pipeline - that might cause a rethink here...
>
>
> Do you have a use case where just stopping the V4L2VideoDevice is not
> enough to cancel all of the queued Requests?
>
See above :)
>
> > Apologies if I have misunderstood and this is not how any of this is
> > meant to work, please do let me know :)
>
> No apologies required :-) I suspect that as we are lacking a "Pipeline
> Implementers Guidebook" - all questions are valid :-)
>
> --
> Regards
> --
> Kieran
Thanks,
Naush
More information about the libcamera-devel
mailing list