@@ -135,8 +135,14 @@ VirtualCameraData::VirtualCameraData(PipelineHandler *pipe,
moveToThread(this);
}
-void VirtualCameraData::processRequest(Request *request)
+void VirtualCameraData::processRequest(Request *request,
+ uint64_t targetTimestamp)
{
+ const auto targetTime = std::chrono::steady_clock::time_point{
+ std::chrono::nanoseconds(targetTimestamp)
+ };
+ std::this_thread::sleep_until(targetTime);
+
for (const auto &[stream, buffer] : request->buffers()) {
bool found = false;
/* map buffer and fill test patterns */
@@ -146,7 +152,7 @@ void VirtualCameraData::processRequest(Request *request)
fmd.status = FrameMetadata::Status::FrameSuccess;
fmd.sequence = streamConfig.seq++;
- fmd.timestamp = currentTimestamp();
+ fmd.timestamp = targetTimestamp;
std::span<const FrameBuffer::Plane> planes = buffer->planes();
for (const auto [i, p] : utils::enumerate(planes))
@@ -348,11 +354,34 @@ 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());
+
+ /* By default target the highest frame rate allowed by the config. */
+ data->minFrameDuration_ =
+ cameraFrameDurationLimits->second.min().get<int64_t>();
+
+ if (controls) {
+ if (auto clientFrameDurationLimits =
+ controls->get(controls::FrameDurationLimits)) {
+ /*
+ * Target the highest frame rate allowed by the client,
+ * clamped to the values allowed by the config.
+ */
+ data->minFrameDuration_ = std::clamp(
+ clientFrameDurationLimits->front(),
+ data->minFrameDuration_,
+ cameraFrameDurationLimits->second.max().get<int64_t>());
+ }
+ }
+
+ data->targetTimestamp_ = 0;
+
for (auto &s : data->streamConfigs_)
s.seq = 0;
@@ -379,15 +408,26 @@ void PipelineHandlerVirtual::stopDevice(Camera *camera)
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);
+ /*
+ * TODO: Skip frames if we can't keep up with the maximum frame
+ * duration.
+ */
+ data->targetTimestamp_ =
+ std::max(currentTimestamp(),
+ data->targetTimestamp_ + data->minFrameDuration_ * 1000);
+
+ request->_d()->metadata().set(controls::SensorTimestamp,
+ data->targetTimestamp_);
+ request->_d()->metadata().set(controls::FrameDuration,
+ data->minFrameDuration_);
+
data->invokeMethod(&VirtualCameraData::processRequest,
- ConnectionTypeQueued, request);
+ ConnectionTypeQueued, request,
+ data->targetTimestamp_);
return 0;
}
@@ -60,12 +60,15 @@ public:
~VirtualCameraData() = default;
- void processRequest(Request *request);
+ void processRequest(Request *request, uint64_t targetTimestamp);
Configuration config_;
std::vector<StreamConfig> streamConfigs_;
Signal<FrameBuffer *> bufferCompleted;
+
+ int64_t minFrameDuration_;
+ uint64_t targetTimestamp_;
};
} /* namespace libcamera */