@@ -130,6 +130,9 @@ VirtualCameraData::VirtualCameraData(PipelineHandler *pipe,
void VirtualCameraData::processRequest(Request *request)
{
+ int64_t sensorTimestamp = *request->metadata().get(controls::SensorTimestamp);
+ std::vector<FrameBuffer *> buffers;
+
for (const auto &[stream, buffer] : request->buffers()) {
bool found = false;
/* map buffer and fill test patterns */
@@ -139,7 +142,7 @@ void VirtualCameraData::processRequest(Request *request)
fmd.status = FrameMetadata::Status::FrameSuccess;
fmd.sequence = streamConfig.seq++;
- fmd.timestamp = currentTimestamp();
+ fmd.timestamp = sensorTimestamp;
std::span<const FrameBuffer::Plane> planes = buffer->planes();
for (const auto [i, p] : utils::enumerate(planes))
@@ -151,12 +154,21 @@ void VirtualCameraData::processRequest(Request *request)
stream->configuration().size, buffer))
fmd.status = FrameMetadata::Status::FrameError;
- bufferCompleted.emit(buffer);
+ buffers.push_back(buffer);
break;
}
}
ASSERT(found);
}
+
+ const uint64_t now = currentTimestamp();
+ if (now < static_cast<uint64_t>(sensorTimestamp)) {
+ std::chrono::nanoseconds duration(sensorTimestamp - now);
+ std::this_thread::sleep_for(duration);
+ }
+
+ for (auto buffer : buffers)
+ bufferCompleted.emit(buffer);
}
VirtualCameraConfiguration::VirtualCameraConfiguration(VirtualCameraData *data)
@@ -323,11 +335,24 @@ int PipelineHandlerVirtual::exportFrameBuffers([[maybe_unused]] Camera *camera,
return dmaBufAllocator_.exportBuffers(config.bufferCount, planeSizes, buffers);
}
-int PipelineHandlerVirtual::start([[maybe_unused]] Camera *camera,
- [[maybe_unused]] const ControlList *controls)
+int PipelineHandlerVirtual::start(Camera *camera, const ControlList *controls)
{
VirtualCameraData *data = cameraData(camera);
+ auto cameraFrameDurationLimits =
+ camera->controls().find(&controls::FrameDurationLimits);
+ ASSERT(cameraFrameDurationLimits != camera->controls().end());
+ data->minFrameDuration_ =
+ cameraFrameDurationLimits->second.min().get<int64_t>();
+
+ if (controls) {
+ if (auto clientFrameDurationLimits =
+ controls->get(controls::FrameDurationLimits)) {
+ data->minFrameDuration_ = std::max(data->minFrameDuration_,
+ clientFrameDurationLimits->front());
+ }
+ }
+
for (auto &s : data->streamConfigs_)
s.seq = 0;
@@ -349,18 +374,30 @@ void PipelineHandlerVirtual::stopDevice(Camera *camera)
/* Process pending `bufferCompleted` signals. */
thread()->dispatchMessages(Message::Type::InvokeMessage, this);
data->bufferCompleted.disconnect(this);
+ data->minFrameDuration_ = 0;
+ data->lastTimestamp_ = 0;
while (!data->queuedRequests_.empty())
cancelRequest(data->queuedRequests_.front());
}
-int PipelineHandlerVirtual::queueRequestDevice([[maybe_unused]] Camera *camera,
- Request *request)
+int PipelineHandlerVirtual::queueRequestDevice(Camera *camera, Request *request)
{
VirtualCameraData *data = cameraData(camera);
- const auto timestamp = currentTimestamp();
- request->_d()->metadata().set(controls::SensorTimestamp, timestamp);
+ const uint64_t now = currentTimestamp();
+ uint64_t targetTimestamp =
+ data->lastTimestamp_ + data->minFrameDuration_ * 1000;
+
+ if (targetTimestamp < now)
+ targetTimestamp = now;
+ data->lastTimestamp_ = targetTimestamp;
+
+ request->_d()->metadata().set(controls::SensorTimestamp,
+ targetTimestamp);
+ request->_d()->metadata().set(controls::FrameDuration,
+ data->minFrameDuration_);
+
data->invokeMethod(&VirtualCameraData::processRequest,
ConnectionTypeQueued, request);
@@ -66,6 +66,9 @@ public:
std::vector<StreamConfig> streamConfigs_;
Signal<FrameBuffer *> bufferCompleted;
+
+ int64_t minFrameDuration_ = 0;
+ uint64_t lastTimestamp_ = 0;
};
} /* namespace libcamera */
Throttle requests to the minimal frame duration in order to honor the configured framerates. Signed-off-by: Robert Mader <robert.mader@collabora.com> --- Changes in v4: - Calculate minFrameDuration_ once in start() using FDLs from camera and, if present, from client provided controls. The later is not yet tested. - Assert that the camera has FDLs set by the config parser. - Set FrameDuration in request metadata. Changes in v3: - Removed a leftover debug change Changes in v2: - Remove debug log in order to avoid CI issues. - Simplify code in queueRequestDevice(), hopefully making it easier to follow. Changes in v1 (from RFC version): - Use std::this_thread::sleep_for() instead usleep() (Barnabás) - Sleep only once per request, after generating all frames - Remove control handling in start() (defer that for later) - Reset state in stop() instead of start() --- src/libcamera/pipeline/virtual/virtual.cpp | 53 ++++++++++++++++++---- src/libcamera/pipeline/virtual/virtual.h | 3 ++ 2 files changed, 48 insertions(+), 8 deletions(-)