[libcamera-devel,v2,5/5] Documentation: guides: pipeline-handler: Document internal queue pattern
diff mbox series

Message ID 20210831223705.1928000-6-nfraprado@collabora.com
State New
Headers show
Series
  • libcamera: pipeline: Add internal request queue
Related show

Commit Message

NĂ­colas F. R. A. Prado Aug. 31, 2021, 10:37 p.m. UTC
Pipeline handlers need to implement a common pattern of adding queued
requests to an internal queue and queuing them to the video devices as
the buffer slots there become available.

Add this pattern to the vivid example in the pipeline-handler guide so
it's clear that new pipeline handlers should also implement it.

Signed-off-by: NĂ­colas F. R. A. Prado <nfraprado@collabora.com>

---

Changes in v2:
- New

 Documentation/guides/pipeline-handler.rst | 129 +++++++++++++++++-----
 1 file changed, 99 insertions(+), 30 deletions(-)

Patch
diff mbox series

diff --git a/Documentation/guides/pipeline-handler.rst b/Documentation/guides/pipeline-handler.rst
index 3ee79b98c4dc..96d4490d06fa 100644
--- a/Documentation/guides/pipeline-handler.rst
+++ b/Documentation/guides/pipeline-handler.rst
@@ -447,11 +447,27 @@  it will be used:
           int init();
           void bufferReady(FrameBuffer *buffer);
 
+          void queuePendingRequests();
+          void cancelPendingRequests();
+
+          void setAvailableBufferSlotCount(unsigned int count) { availableBufferSlotCount_ = count; }
+
           MediaDevice *media_;
           V4L2VideoDevice *video_;
           Stream stream_;
+
+          std::queue<Request *> pendingRequests_;
+
+   private:
+          unsigned int availableBufferSlotCount_;
    };
 
+And the following include needs to be added:
+
+.. code-block:: cpp
+
+   #include <queue>
+
 This example pipeline handler handles a single video device and supports a
 single stream, represented by the ``VividCameraData`` class members. More
 complex pipeline handlers might register cameras composed of several video
@@ -1218,7 +1234,8 @@  algorithms, or other devices you should also stop them.
 
 Of course we also need to handle the corresponding actions to stop streaming on
 a device, Add the following to the ``stop`` function, to stop the stream with
-the `streamOff`_ function and release all buffers.
+the `streamOff`_ function and release all buffers. Additionally we need to
+cancel pending requests, the reason will become clearer in the next section.
 
 .. _streamOff: http://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html#a61998710615bdf7aa25a046c8565ed66
 
@@ -1226,6 +1243,7 @@  the `streamOff`_ function and release all buffers.
 
    VividCameraData *data = cameraData(camera);
    data->video_->streamOff();
+   data->cancelPendingRequests();
    data->video_->releaseBuffers();
 
 Queuing requests between applications and hardware
@@ -1246,25 +1264,76 @@  directly with the `queueBuffer`_ function provided by the V4L2VideoDevice.
 .. _findBuffer: http://libcamera.org/api-html/classlibcamera_1_1Request.html#ac66050aeb9b92c64218945158559c4d4
 .. _queueBuffer: http://libcamera.org/api-html/classlibcamera_1_1V4L2VideoDevice.html#a594cd594686a8c1cf9ae8dba0b2a8a75
 
+Since the pipeline handler has a finite number of buffer slots allocated through
+importBuffers(), in order to not drop frames when more requests than that are
+queued by the application, we need to implement a queue to hold the requests and
+queue them to the devices as slots become available.
+
 Replace the stubbed contents of ``queueRequestDevice`` with the following:
 
 .. code-block:: cpp
 
    VividCameraData *data = cameraData(camera);
-   FrameBuffer *buffer = request->findBuffer(&data->stream_);
-   if (!buffer) {
+   if (!request->findBuffer(&data->stream_)) {
           LOG(VIVID, Error)
                   << "Attempt to queue request with invalid stream";
 
           return -ENOENT;
    }
 
-   int ret = data->video_->queueBuffer(buffer);
-   if (ret < 0)
-          return ret;
+   data->pendingRequests_.push(request);
+   data->queuePendingRequests();
 
    return 0;
 
+Now create the ``queuePendingRequests`` function for ``VividCameraData`` and
+inside it add:
+
+.. code-block:: cpp
+
+   while (!pendingRequests_.empty() && availableBufferSlotCount_) {
+           Request *request = pendingRequests_.front();
+           FrameBuffer *buffer = request->findBuffer(&stream_);
+
+           ret = video_->queueBuffer(buffer);
+           if (ret < 0) {
+                   LOG(UVC, Error) << "Failed to queue buffer with error "
+                                   << ret << ". Cancelling buffer.";
+                   buffer->cancel();
+                   pipe()->completeBuffer(request, buffer);
+                   pipe()->completeRequest(request);
+                   pendingRequests_.pop();
+
+                   continue;
+           }
+
+           availableBufferSlotCount_--;
+
+           pendingRequests_.pop();
+   }
+
+Create ``cancelPendingRequests`` as well with:
+
+.. code-block:: cpp
+
+   while (!pendingRequests_.empty()) {
+           Request *request = pendingRequests_.front();
+           FrameBuffer *buffer = request->findBuffer(&stream_);
+
+           buffer->cancel();
+           pipe()->completeBuffer(request, buffer);
+           pipe()->completeRequest(request);
+
+           pendingRequests_.pop();
+   }
+
+Finally we need to initialize the buffer slot count in ``start()``. Add the
+following line there:
+
+.. code-block:: cpp
+
+   data->setAvailableBufferSlotCount(count);
+
 Processing controls
 ~~~~~~~~~~~~~~~~~~~
 
@@ -1339,31 +1408,27 @@  where appropriate by setting controls on V4L2Subdevices directly. Each pipeline
 handler is responsible for understanding the correct procedure for applying
 controls to the device they support.
 
-This example pipeline handler applies controls during the `queueRequestDevice`_
-function for each request, and applies them to the capture device through the
-capture node.
-
-.. _queueRequestDevice: http://libcamera.org/api-html/classlibcamera_1_1PipelineHandler.html#a106914cca210640c9da9ee1f0419e83c
+This example pipeline handler applies controls as soon as the request is queued
+to the device from ``queuePendingRequests``, and applies them to the capture
+device through the capture node.
 
-In the ``queueRequestDevice`` function, replace the following:
+In the ``queuePendingRequests`` function, before the line ``ret =
+video_->queueBuffer(buffer);``, add the following:
 
 .. code-block:: cpp
 
-   int ret = data->video_->queueBuffer(buffer);
-   if (ret < 0)
-        return ret;
-
-With the following code:
-
-.. code-block:: cpp
-
-   int ret = processControls(data, request);
-   if (ret < 0)
-        return ret;
-
-   ret = data->video_->queueBuffer(buffer);
-   if (ret < 0)
-        return ret;
+   int ret = processControls(request);
+   if (ret < 0) {
+           LOG(UVC, Error) << "Failed to process controls with"
+                           << " error " << ret << ". Cancelling"
+                           << " buffer.";
+           buffer->cancel();
+           pipe()->completeBuffer(request, buffer);
+           pipe()->completeRequest(request);
+           pendingRequests_.pop();
+
+           continue;
+   }
 
 We also need to add the following include directive to support the control
 value translation operations:
@@ -1427,9 +1492,10 @@  VividCameradata::init() impelementation.
 The ``bufferReady`` function obtains the request from the buffer using the
 ``request`` function, and notifies the ``Camera`` that the buffer and
 request are completed. In this simpler pipeline handler, there is only one
-stream, so it completes the request immediately. You can find a more complex
-example of event handling with supporting multiple streams in the libcamera
-code-base.
+stream, so it completes the request immediately. It also updates the number of
+available buffer slots and tries to queue any pending requests. You can find a
+more complex example of event handling with support to multiple streams in the
+libcamera code-base.
 
 .. TODO: Add link
 
@@ -1441,6 +1507,9 @@  code-base.
 
           pipe_->completeBuffer(request, buffer);
           pipe_->completeRequest(request);
+
+          availableBufferSlotCount_++;
+          queuePendingRequests();
    }
 
 Testing a pipeline handler