[RFC PATCH v1 20/23] libcamera: pipeline: rpi: Queue metadata until completion
Barnabás Pőcze
barnabas.pocze at ideasonboard.com
Fri Jun 6 18:41:53 CEST 2025
The rpi pipeline drops a certain number of initial frames. It uses
`ControlList::clear()` to remove the metadata items of a request that
was populated while processing a frame that is ultimately dropped.
Since the decision to drop a frame only occurs at the very end of the
processing, this stop-gap measure is introduced to delay metadata
completion until the request is actually completed.
Obsoleted by https://patchwork.libcamera.org/cover/23474/.
Signed-off-by: Barnabás Pőcze <barnabas.pocze at ideasonboard.com>
---
.../pipeline/rpi/common/pipeline_base.cpp | 29 +++++++++++--------
.../pipeline/rpi/common/pipeline_base.h | 4 +--
src/libcamera/pipeline/rpi/pisp/pisp.cpp | 8 ++---
src/libcamera/pipeline/rpi/vc4/vc4.cpp | 8 ++---
4 files changed, 27 insertions(+), 22 deletions(-)
diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
index 98507a152..d432cfb51 100644
--- a/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.cpp
@@ -780,7 +780,7 @@ int PipelineHandlerBase::queueRequestDevice(Camera *camera, Request *request)
}
/* Push the request to the back of the queue. */
- data->requestQueue_.push(request);
+ data->requestQueue_.push({ request, {} });
data->handleState();
return 0;
@@ -1232,8 +1232,8 @@ void CameraData::metadataReady(const ControlList &metadata)
/* Add to the Request metadata buffer what the IPA has provided. */
/* Last thing to do is to fill up the request metadata. */
- Request *request = requestQueue_.front();
- request->metadata().merge(metadata);
+ ControlList &requestMetadata = requestQueue_.front().second;
+ requestMetadata.merge(metadata);
/*
* Inform the sensor of the latest colour gains if it has the
@@ -1392,7 +1392,7 @@ void CameraData::clearIncompleteRequests()
* back to the application.
*/
while (!requestQueue_.empty()) {
- Request *request = requestQueue_.front();
+ auto &[request, metadata] = requestQueue_.front();
for (auto &b : request->buffers()) {
FrameBuffer *buffer = b.second;
@@ -1406,6 +1406,9 @@ void CameraData::clearIncompleteRequests()
}
}
+ // TODO: need this when cancelled?
+ request->metadata().merge(metadata);
+
pipe()->completeRequest(request);
requestQueue_.pop();
}
@@ -1418,7 +1421,7 @@ void CameraData::handleStreamBuffer(FrameBuffer *buffer, RPi::Stream *stream)
* that we actually have one to action, otherwise we just return
* buffer back to the stream.
*/
- Request *request = requestQueue_.empty() ? nullptr : requestQueue_.front();
+ Request *request = requestQueue_.empty() ? nullptr : requestQueue_.front().first;
if (!dropFrameCount_ && request && request->findBuffer(stream) == buffer) {
/*
* Tag the buffer as completed, returning it to the
@@ -1471,7 +1474,7 @@ void CameraData::checkRequestCompleted()
* change the state to IDLE when ready.
*/
if (!dropFrameCount_) {
- Request *request = requestQueue_.front();
+ auto &[request, metadata] = requestQueue_.front();
if (request->hasPendingBuffers())
return;
@@ -1482,6 +1485,8 @@ void CameraData::checkRequestCompleted()
LOG(RPI, Debug) << "Completing request sequence: "
<< request->sequence();
+ request->metadata().merge(metadata);
+
pipe()->completeRequest(request);
requestQueue_.pop();
requestCompleted = true;
@@ -1504,10 +1509,10 @@ void CameraData::checkRequestCompleted()
}
}
-void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request *request)
+void CameraData::fillRequestMetadata(const ControlList &bufferControls, ControlList &metadata)
{
- request->metadata().set(controls::SensorTimestamp,
- bufferControls.get(controls::SensorTimestamp).value_or(0));
+ metadata.set(controls::SensorTimestamp,
+ bufferControls.get(controls::SensorTimestamp).value_or(0));
if (cropParams_.size()) {
std::vector<Rectangle> crops;
@@ -1515,10 +1520,10 @@ void CameraData::fillRequestMetadata(const ControlList &bufferControls, Request
for (auto const &[k, v] : cropParams_)
crops.push_back(scaleIspCrop(v.ispCrop));
- request->metadata().set(controls::ScalerCrop, crops[0]);
+ metadata.set(controls::ScalerCrop, crops[0]);
if (crops.size() > 1) {
- request->metadata().set(controls::rpi::ScalerCrops,
- Span<const Rectangle>(crops.data(), crops.size()));
+ metadata.set(controls::rpi::ScalerCrops,
+ Span<const Rectangle>(crops.data(), crops.size()));
}
}
}
diff --git a/src/libcamera/pipeline/rpi/common/pipeline_base.h b/src/libcamera/pipeline/rpi/common/pipeline_base.h
index aae0c2f35..d3a1bd216 100644
--- a/src/libcamera/pipeline/rpi/common/pipeline_base.h
+++ b/src/libcamera/pipeline/rpi/common/pipeline_base.h
@@ -129,7 +129,7 @@ public:
return state_ != State::Stopped && state_ != State::Error;
}
- std::queue<Request *> requestQueue_;
+ std::queue<std::pair<Request *, ControlList>> requestQueue_;
/* For handling digital zoom. */
IPACameraSensorInfo sensorInfo_;
@@ -179,7 +179,7 @@ public:
protected:
void fillRequestMetadata(const ControlList &bufferControls,
- Request *request);
+ ControlList &metadata);
virtual void tryRunPipeline() = 0;
diff --git a/src/libcamera/pipeline/rpi/pisp/pisp.cpp b/src/libcamera/pipeline/rpi/pisp/pisp.cpp
index 91e7f4c94..8436cb4fa 100644
--- a/src/libcamera/pipeline/rpi/pisp/pisp.cpp
+++ b/src/libcamera/pipeline/rpi/pisp/pisp.cpp
@@ -2318,7 +2318,7 @@ void PiSPCameraData::tryRunPipeline()
CfeJob &job = cfeJobQueue_.front();
/* Take the first request from the queue and action the IPA. */
- Request *request = requestQueue_.front();
+ auto &[request, metadata] = requestQueue_.front();
/* See if a new ScalerCrop value needs to be applied. */
applyScalerCrop(request->controls());
@@ -2328,8 +2328,8 @@ void PiSPCameraData::tryRunPipeline()
* related controls. We clear it first because the request metadata
* may have been populated if we have dropped the previous frame.
*/
- request->metadata().clear();
- fillRequestMetadata(job.sensorControls, request);
+ metadata.clear();
+ fillRequestMetadata(job.sensorControls, metadata);
/* Set our state to say the pipeline is active. */
state_ = State::Busy;
@@ -2347,7 +2347,7 @@ void PiSPCameraData::tryRunPipeline()
params.buffers.bayer = RPi::MaskBayerData | bayerId;
params.buffers.stats = RPi::MaskStats | statsId;
params.buffers.embedded = 0;
- params.ipaContext = requestQueue_.front()->sequence();
+ params.ipaContext = requestQueue_.front().first->sequence();
params.delayContext = job.delayContext;
params.sensorControls = std::move(job.sensorControls);
params.requestControls = request->controls();
diff --git a/src/libcamera/pipeline/rpi/vc4/vc4.cpp b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
index fe910bdf2..a311f43a4 100644
--- a/src/libcamera/pipeline/rpi/vc4/vc4.cpp
+++ b/src/libcamera/pipeline/rpi/vc4/vc4.cpp
@@ -836,7 +836,7 @@ void Vc4CameraData::ispOutputDequeue(FrameBuffer *buffer)
if (stream == &isp_[Isp::Stats]) {
ipa::RPi::ProcessParams params;
params.buffers.stats = index | RPi::MaskStats;
- params.ipaContext = requestQueue_.front()->sequence();
+ params.ipaContext = requestQueue_.front().first->sequence();
ipa_->processStats(params);
} else {
/* Any other ISP output can be handed back to the application now. */
@@ -935,7 +935,7 @@ void Vc4CameraData::tryRunPipeline()
return;
/* Take the first request from the queue and action the IPA. */
- Request *request = requestQueue_.front();
+ auto &[request, metadata] = requestQueue_.front();
/* See if a new ScalerCrop value needs to be applied. */
applyScalerCrop(request->controls());
@@ -945,8 +945,8 @@ void Vc4CameraData::tryRunPipeline()
* related controls. We clear it first because the request metadata
* may have been populated if we have dropped the previous frame.
*/
- request->metadata().clear();
- fillRequestMetadata(bayerFrame.controls, request);
+ metadata.clear();
+ fillRequestMetadata(bayerFrame.controls, metadata);
/* Set our state to say the pipeline is active. */
state_ = State::Busy;
--
2.49.0
More information about the libcamera-devel
mailing list