Loading services/camera/virtualcamera/VirtualCameraDevice.cc +12 −7 Original line number Diff line number Diff line Loading @@ -73,13 +73,14 @@ const char* kDevicePathPrefix = "device@1.1/virtual/"; 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{ Loading Loading @@ -130,16 +131,20 @@ std::vector<FpsRange> fpsRangesForInputConfig( 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()); Loading services/camera/virtualcamera/VirtualCameraDevice.h +4 −0 Original line number Diff line number Diff line Loading @@ -126,6 +126,10 @@ class VirtualCameraDevice // 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"; Loading services/camera/virtualcamera/VirtualCameraRenderThread.cc +34 −1 Original line number Diff line number Diff line Loading @@ -277,6 +277,16 @@ std::vector<uint8_t> createExif( 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, Loading Loading @@ -419,7 +429,7 @@ void VirtualCameraRenderThread::processCaptureRequest( 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)); Loading Loading @@ -449,6 +459,29 @@ void VirtualCameraRenderThread::processCaptureRequest( } } // 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(); Loading services/camera/virtualcamera/util/EglSurfaceTexture.cc +6 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ // #define LOG_NDEBUG 0 #include "utils/Timers.h" #define LOG_TAG "EglSurfaceTexture" #include <cstdint> Loading Loading @@ -63,6 +64,11 @@ sp<GraphicBuffer> EglSurfaceTexture::getCurrentBuffer() { 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; Loading services/camera/virtualcamera/util/EglSurfaceTexture.h +7 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #ifndef ANDROID_COMPANION_VIRTUALCAMERA_EGLSURFACETEXTURE_H #define ANDROID_COMPANION_VIRTUALCAMERA_EGLSURFACETEXTURE_H #include <chrono> #include <cstdint> #include "GLES/gl.h" Loading Loading @@ -51,6 +52,12 @@ class EglSurfaceTexture { // 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. // Loading Loading
services/camera/virtualcamera/VirtualCameraDevice.cc +12 −7 Original line number Diff line number Diff line Loading @@ -73,13 +73,14 @@ const char* kDevicePathPrefix = "device@1.1/virtual/"; 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{ Loading Loading @@ -130,16 +131,20 @@ std::vector<FpsRange> fpsRangesForInputConfig( 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()); Loading
services/camera/virtualcamera/VirtualCameraDevice.h +4 −0 Original line number Diff line number Diff line Loading @@ -126,6 +126,10 @@ class VirtualCameraDevice // 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"; Loading
services/camera/virtualcamera/VirtualCameraRenderThread.cc +34 −1 Original line number Diff line number Diff line Loading @@ -277,6 +277,16 @@ std::vector<uint8_t> createExif( 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, Loading Loading @@ -419,7 +429,7 @@ void VirtualCameraRenderThread::processCaptureRequest( 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)); Loading Loading @@ -449,6 +459,29 @@ void VirtualCameraRenderThread::processCaptureRequest( } } // 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(); Loading
services/camera/virtualcamera/util/EglSurfaceTexture.cc +6 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ // #define LOG_NDEBUG 0 #include "utils/Timers.h" #define LOG_TAG "EglSurfaceTexture" #include <cstdint> Loading Loading @@ -63,6 +64,11 @@ sp<GraphicBuffer> EglSurfaceTexture::getCurrentBuffer() { 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; Loading
services/camera/virtualcamera/util/EglSurfaceTexture.h +7 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ #ifndef ANDROID_COMPANION_VIRTUALCAMERA_EGLSURFACETEXTURE_H #define ANDROID_COMPANION_VIRTUALCAMERA_EGLSURFACETEXTURE_H #include <chrono> #include <cstdint> #include "GLES/gl.h" Loading Loading @@ -51,6 +52,12 @@ class EglSurfaceTexture { // 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. // Loading