@@ -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)
@@ -349,18 +361,36 @@ void PipelineHandlerVirtual::stopDevice(Camera *camera)
/* Process pending `bufferCompleted` signals. */
thread()->dispatchMessages(Message::Type::InvokeMessage, this);
data->bufferCompleted.disconnect(this);
+ 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();
+ const uint64_t now = currentTimestamp();
+ uint64_t minFrameDurationNsec;
+ uint64_t targetTimestamp;
+
+ auto frameDurationLimits = camera->controls().find(&controls::FrameDurationLimits);
+ if (frameDurationLimits != camera->controls().end()) {
+ minFrameDurationNsec =
+ frameDurationLimits->second.min().get<int64_t>() * 1000;
+ } else {
+ /* Default to 30 FPS */
+ minFrameDurationNsec = 33333 * 1000;
+ }
+
+ targetTimestamp = data->lastTimestamp + minFrameDurationNsec;
+ if (targetTimestamp < now)
+ targetTimestamp = now;
+ data->lastTimestamp = targetTimestamp;
+
+ request->_d()->metadata().set(controls::SensorTimestamp,
+ targetTimestamp);
- request->_d()->metadata().set(controls::SensorTimestamp, timestamp);
data->invokeMethod(&VirtualCameraData::processRequest,
ConnectionTypeQueued, request);
@@ -66,6 +66,7 @@ public:
std::vector<StreamConfig> streamConfigs_;
Signal<FrameBuffer *> bufferCompleted;
+ uint64_t lastTimestamp = 0;
};
} /* namespace libcamera */
Throttle requests to the minimal frame duration in order to honor the configured framerates. Client-requested values are ignored for now. Signed-off-by: Robert Mader <robert.mader@collabora.com> --- 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 | 42 ++++++++++++++++++---- src/libcamera/pipeline/virtual/virtual.h | 1 + 2 files changed, 37 insertions(+), 6 deletions(-)