Wait for next available frame
... for the maximal possible time not
violating the min fps.
Bug: 301023410
Test: atest VirtualCameraTest
Test: atest CtsVirtualDevicesCameraCtsTestCases
Change-Id: Iebc3eae22e54fc153a2daa19e7ea331d89b16a8e
diff --git a/services/camera/virtualcamera/VirtualCameraDevice.cc b/services/camera/virtualcamera/VirtualCameraDevice.cc
index fe9e0ed..ba4ea6b 100644
--- a/services/camera/virtualcamera/VirtualCameraDevice.cc
+++ b/services/camera/virtualcamera/VirtualCameraDevice.cc
@@ -73,13 +73,14 @@
constexpr int32_t kMaxJpegSize = 3 * 1024 * 1024 /*3MiB*/;
-constexpr int32_t kMinFps = 15;
-
constexpr std::chrono::nanoseconds kMaxFrameDuration =
- std::chrono::duration_cast<std::chrono::nanoseconds>(1e9ns / kMinFps);
+ std::chrono::duration_cast<std::chrono::nanoseconds>(
+ 1e9ns / VirtualCameraDevice::kMinFps);
constexpr uint8_t kPipelineMaxDepth = 2;
+constexpr int k30Fps = 30;
+
constexpr MetadataBuilder::ControlRegion kDefaultEmptyControlRegion{};
const std::array<Resolution, 5> kStandardJpegThumbnailSizes{
@@ -130,16 +131,20 @@
std::set<FpsRange> availableRanges;
for (const SupportedStreamConfiguration& config : configs) {
- availableRanges.insert({.minFps = kMinFps, .maxFps = config.maxFps});
+ availableRanges.insert(
+ {.minFps = VirtualCameraDevice::kMinFps, .maxFps = config.maxFps});
availableRanges.insert({.minFps = config.maxFps, .maxFps = config.maxFps});
}
if (std::any_of(configs.begin(), configs.end(),
[](const SupportedStreamConfiguration& config) {
- return config.maxFps >= 30;
+ return config.maxFps >= k30Fps;
})) {
- availableRanges.insert({.minFps = kMinFps, .maxFps = 30});
- availableRanges.insert({.minFps = 30, .maxFps = 30});
+ // Extend the set of available ranges with (minFps <= 15, 30) & (30, 30) as
+ // required by CDD.
+ availableRanges.insert(
+ {.minFps = VirtualCameraDevice::kMinFps, .maxFps = k30Fps});
+ availableRanges.insert({.minFps = k30Fps, .maxFps = k30Fps});
}
return std::vector<FpsRange>(availableRanges.begin(), availableRanges.end());
diff --git a/services/camera/virtualcamera/VirtualCameraDevice.h b/services/camera/virtualcamera/VirtualCameraDevice.h
index cba0674..8a7875f 100644
--- a/services/camera/virtualcamera/VirtualCameraDevice.h
+++ b/services/camera/virtualcamera/VirtualCameraDevice.h
@@ -126,6 +126,10 @@
// Default JPEG orientation.
static constexpr uint8_t kDefaultJpegOrientation = 0;
+ // TODO(b/342674104) CDD requires <= 15.
+ // Change this to lower value after confirming it doesn't cause any issue (timeouts).
+ static constexpr int kMinFps = 15;
+
// Default Make and Model for Exif
static constexpr char kDefaultMakeAndModel[] = "Android Virtual Camera";
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.cc b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
index a5ee922..08b2698 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.cc
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
@@ -277,6 +277,16 @@
return app1Data;
}
+std::chrono::nanoseconds getMaxFrameDuration(
+ const RequestSettings& requestSettings) {
+ if (requestSettings.fpsRange.has_value()) {
+ return std::chrono::nanoseconds(static_cast<uint64_t>(
+ 1e9 / std::max(1, requestSettings.fpsRange->minFps)));
+ }
+ return std::chrono::nanoseconds(
+ static_cast<uint64_t>(1e9 / VirtualCameraDevice::kMinFps));
+}
+
} // namespace
CaptureRequestBuffer::CaptureRequestBuffer(int streamId, int bufferId,
@@ -419,7 +429,7 @@
std::chrono::nanoseconds timestamp =
std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch());
- std::chrono::nanoseconds lastAcquisitionTimestamp(
+ const std::chrono::nanoseconds lastAcquisitionTimestamp(
mLastAcquisitionTimestampNanoseconds.exchange(timestamp.count(),
std::memory_order_relaxed));
@@ -449,6 +459,29 @@
}
}
+ // Calculate the maximal amount of time we can afford to wait for next frame.
+ const std::chrono::nanoseconds maxFrameDuration =
+ getMaxFrameDuration(request.getRequestSettings());
+ const std::chrono::nanoseconds elapsedDuration =
+ timestamp - lastAcquisitionTimestamp;
+ if (elapsedDuration < maxFrameDuration) {
+ // We can afford to wait for next frame.
+ // Note that if there's already new frame in the input Surface, the call
+ // below returns immediatelly.
+ bool gotNewFrame = mEglSurfaceTexture->waitForNextFrame(maxFrameDuration -
+ elapsedDuration);
+ timestamp = std::chrono::duration_cast<std::chrono::nanoseconds>(
+ std::chrono::steady_clock::now().time_since_epoch());
+ if (!gotNewFrame) {
+ ALOGV(
+ "%s: No new frame received on input surface after waiting for "
+ "%" PRIu64 "ns, repeating last frame.",
+ __func__,
+ static_cast<uint64_t>((timestamp - lastAcquisitionTimestamp).count()));
+ }
+ mLastAcquisitionTimestampNanoseconds.store(timestamp.count(),
+ std::memory_order_relaxed);
+ }
// Acquire new (most recent) image from the Surface.
mEglSurfaceTexture->updateTexture();
diff --git a/services/camera/virtualcamera/util/EglSurfaceTexture.cc b/services/camera/virtualcamera/util/EglSurfaceTexture.cc
index 9f26e19..7de5020 100644
--- a/services/camera/virtualcamera/util/EglSurfaceTexture.cc
+++ b/services/camera/virtualcamera/util/EglSurfaceTexture.cc
@@ -15,6 +15,7 @@
*/
// #define LOG_NDEBUG 0
+#include "utils/Timers.h"
#define LOG_TAG "EglSurfaceTexture"
#include <cstdint>
@@ -63,6 +64,11 @@
return mGlConsumer->getCurrentBuffer();
}
+bool EglSurfaceTexture::waitForNextFrame(const std::chrono::nanoseconds timeout) {
+ return mSurface->waitForNextFrame(mGlConsumer->getFrameNumber(),
+ static_cast<nsecs_t>(timeout.count()));
+}
+
GLuint EglSurfaceTexture::updateTexture() {
mGlConsumer->updateTexImage();
return mTextureId;
diff --git a/services/camera/virtualcamera/util/EglSurfaceTexture.h b/services/camera/virtualcamera/util/EglSurfaceTexture.h
index faad7c4..b9c5126 100644
--- a/services/camera/virtualcamera/util/EglSurfaceTexture.h
+++ b/services/camera/virtualcamera/util/EglSurfaceTexture.h
@@ -17,6 +17,7 @@
#ifndef ANDROID_COMPANION_VIRTUALCAMERA_EGLSURFACETEXTURE_H
#define ANDROID_COMPANION_VIRTUALCAMERA_EGLSURFACETEXTURE_H
+#include <chrono>
#include <cstdint>
#include "GLES/gl.h"
@@ -51,6 +52,12 @@
// Get height of surface / texture.
uint32_t getHeight() const;
+ // Wait for next frame to be available in the surface
+ // until timeout.
+ //
+ // Returns false on timeout, true if new frame was received before timeout.
+ bool waitForNextFrame(std::chrono::nanoseconds timeout);
+
// Update the texture with the most recent submitted buffer.
// Most be called on thread with EGL context.
//