[libcamera-devel] [PATCH 19/20] libcamera: pipeline: simple: Support usage of multiple streams
Kieran Bingham
kieran.bingham at ideasonboard.com
Tue Mar 2 12:06:13 CET 2021
On 02/03/2021 10:39, Laurent Pinchart wrote:
> Hi again,
>
> On Tue, Mar 02, 2021 at 12:34:10PM +0200, Laurent Pinchart wrote:
>> On Tue, Mar 02, 2021 at 04:09:33PM +0900, paul.elder at ideasonboard.com wrote:
>>> On Mon, Feb 01, 2021 at 12:47:01AM +0200, Laurent Pinchart wrote:
>>>> To extend the multi-stream support to runtime operation of the pipeline,
>>>> expand the converter queue to store multiple output buffers, and update
>>>> the request queuing and buffer completion handlers accordingly.
>>>>
>>>> Signed-off-by: Laurent Pinchart <laurent.pinchart at ideasonboard.com>
>>>> ---
>>>> src/libcamera/pipeline/simple/simple.cpp | 93 ++++++++++++++----------
>>>> 1 file changed, 54 insertions(+), 39 deletions(-)
>>>>
>>>> diff --git a/src/libcamera/pipeline/simple/simple.cpp b/src/libcamera/pipeline/simple/simple.cpp
>>>> index 58e5f0d23139..55a5528611c8 100644
>>>> --- a/src/libcamera/pipeline/simple/simple.cpp
>>>> +++ b/src/libcamera/pipeline/simple/simple.cpp
>>>> @@ -173,7 +173,7 @@ public:
>>>>
>>>> std::vector<std::unique_ptr<FrameBuffer>> converterBuffers_;
>>>> bool useConverter_;
>>>> - std::queue<FrameBuffer *> converterQueue_;
>>>> + std::queue<std::map<unsigned int, FrameBuffer *>> converterQueue_;
>>>> };
>>>>
>>>> class SimpleCameraConfiguration : public CameraConfiguration
>>>> @@ -762,10 +762,12 @@ int SimplePipelineHandler::exportFrameBuffers(Camera *camera, Stream *stream,
>>>> * Export buffers on the converter or capture video node, depending on
>>>> * whether the converter is used or not.
>>>> */
>>>> - if (data->useConverter_)
>>>> - return converter_->exportBuffers(0, count, buffers);
>>>> - else
>>>> + if (data->useConverter_) {
>>>> + unsigned int index = stream - &data->streams_.front();
>>>> + return converter_->exportBuffers(index, count, buffers);
>>>> + } else {
>>>> return data->video_->exportBuffers(count, buffers);
>>>> + }
>>>> }
>>>>
>>>> int SimplePipelineHandler::start(Camera *camera, [[maybe_unused]] ControlList *controls)
>>>> @@ -830,25 +832,30 @@ void SimplePipelineHandler::stop(Camera *camera)
>>>> int SimplePipelineHandler::queueRequestDevice(Camera *camera, Request *request)
>>>> {
>>>> SimpleCameraData *data = cameraData(camera);
>>>> - Stream *stream = &data->streams_[0];
>>>> + int ret;
>>>>
>>>> - FrameBuffer *buffer = request->findBuffer(stream);
>>>> - if (!buffer) {
>>>> - LOG(SimplePipeline, Error)
>>>> - << "Attempt to queue request with invalid stream";
>>>> - return -ENOENT;
We used to validate the stream here...
>>>> - }
>>>> + std::map<unsigned int, FrameBuffer *> buffers;
>>>>
>>>> - /*
>>>> - * If conversion is needed, push the buffer to the converter queue, it
>>>> - * will be handed to the converter in the capture completion handler.
>>>> - */
>>>> - if (data->useConverter_) {
>>>> - data->converterQueue_.push(buffer);
>>>> - return 0;
>>>> + for (auto &[stream, buffer] : request->buffers()) {
>>>> + /*
>>>> + * If conversion is needed, push the buffer to the converter
>>>> + * queue, it will be handed to the converter in the capture
>>>> + * completion handler.
>>>> + */
>>>> + if (data->useConverter_) {
>>>> + unsigned int index = stream - &data->streams_.front();
Should we have a helper to convert from stream to index, which includes
a check to validate that it is within streams_.size() ?
That would add some safety to here, and the usage above in
exportFrameBuffers().
>>>> + buffers.emplace(index, buffer);
>>>> + } else {
>>>> + ret = data->video_->queueBuffer(buffer);
>>>> + if (ret < 0)
>>>> + return ret;
>>>> + }
>>>> }
>>>>
>>>> - return data->video_->queueBuffer(buffer);
>>>> + if (data->useConverter_)
>>>> + data->converterQueue_.push(std::move(buffers));
>>>> +
>>>> + return 0;
>>>> }
>>>>
>>>> /* -----------------------------------------------------------------------------
>>>> @@ -1020,24 +1027,34 @@ void SimplePipelineHandler::bufferReady(FrameBuffer *buffer)
>>>> * point converting an erroneous buffer.
>>>> */
>>>> if (buffer->metadata().status != FrameMetadata::FrameSuccess) {
>>>> - if (data->useConverter_) {
>>>> - /* Requeue the buffer for capture. */
>>>> - data->video_->queueBuffer(buffer);
>>>> + if (!data->useConverter_) {
>>>> + /* No conversion, just complete the request. */
>>>> + Request *request = buffer->request();
>>>> + completeBuffer(request, buffer);
>>>> + completeRequest(request);
>>>> + return;
>>>> + }
>>>> +
>>>> + /*
>>>> + * The converter is in use. Requeue the internal buffer for
>>>> + * capture, and complete the request with all the user-facing
>>>> + * buffers.
>>>> + */
>>>> + data->video_->queueBuffer(buffer);
Does this incorrectly requeue buffers if we're stopping? (I.e. if we
were 'FrameCancelled' or such?
>>>>
>>>> - /*
>>>> - * Get the next user-facing buffer to complete the
>>>> - * request.
>>>> - */
>>>> - if (data->converterQueue_.empty())
>>>> - return;
>>>> + if (data->converterQueue_.empty())
>>>> + return;
>>>>
>>>> - buffer = data->converterQueue_.front();
>>>> - data->converterQueue_.pop();
>>>> + Request *request = nullptr;
>>>> + for (auto &item : data->converterQueue_.front()) {
>>>> + FrameBuffer *outputBuffer = item.second;
>>>> + request = outputBuffer->request();
>>>> + completeBuffer(request, outputBuffer);
>>>> }
>>>> + data->converterQueue_.pop();
>>>>
>>>> - Request *request = buffer->request();
>>>> - completeBuffer(request, buffer);
>>>> - completeRequest(request);
>>>> + if (request)
>>>
>>> This check doesn't seem necessary, as we return early if the
>>> converterQueue_ is empty, so the loop will always run once. Is it just
>>> to appease coverity?
>>
>> You're right, I'll drop that.
>
> Actually, we're not looping over data->converterQueue_, but over
> converterQueue_.front(). While it should never be empty, I think it will
> be difficult for compilers (and coverity) to know that, and a check here
> can also act as a bit of defensive programming. I think I'd prefer
> keeping the check.
I presume at this point no buffer has been passed to any convertor, so
there can't be anything happening in parallel at this point.
I don't think there can be so:
Reviewed-by: Kieran Bingham <kieran.bingham at ideasonboard.com>
>>> Reviewed-by: Paul Elder <paul.elder at ideasonboard.com>
>>>
>>>> + completeRequest(request);
>>>> return;
>>>> }
>>>>
>>>> @@ -1052,10 +1069,8 @@ void SimplePipelineHandler::bufferReady(FrameBuffer *buffer)
>>>> return;
>>>> }
>>>>
>>>> - FrameBuffer *output = data->converterQueue_.front();
>>>> + converter_->queueBuffers(buffer, data->converterQueue_.front());
>>>> data->converterQueue_.pop();
>>>> -
>>>> - converter_->queueBuffers(buffer, { { 0, output } });
>>>> return;
>>>> }
>>>>
>>>> @@ -1078,10 +1093,10 @@ void SimplePipelineHandler::converterOutputDone(FrameBuffer *buffer)
>>>> {
>>>> ASSERT(activeCamera_);
>>>>
>>>> - /* Complete the request. */
>>>> + /* Complete the buffer and the request. */
>>>> Request *request = buffer->request();
>>>> - completeBuffer(request, buffer);
>>>> - completeRequest(request);
>>>> + if (completeBuffer(request, buffer))
>>>> + completeRequest(request);>>>> }
>>>>
>>>> REGISTER_PIPELINE_HANDLER(SimplePipelineHandler)
>
--
Regards
--
Kieran
More information about the libcamera-devel
mailing list