diff options
author | Jan Sebechlebsky <jsebechlebsky@google.com> | 2023-11-14 10:06:12 +0100 |
---|---|---|
committer | Jan Sebechlebsky <jsebechlebsky@google.com> | 2023-11-16 12:23:56 +0100 |
commit | fe55cce399018e2b35543ac32043dc88d9d546bb (patch) | |
tree | 9a588e65441032b06fd3b17c60ba4f6853e03eda | |
parent | e953a8be564ad10343d9a32eb5fbd3cdb1a9e8f3 (diff) | |
download | camera-fe55cce399018e2b35543ac32043dc88d9d546bb.tar.gz |
Implement flush operation for camera session.
Bug: 301023410
Test: atest virtual_camera_tests
Change-Id: Ic10d6dfe15c2fefdf3dd980dab57c521f967e4f0
-rw-r--r-- | devices/VirtualCamera/VirtualCameraRenderThread.cc | 71 | ||||
-rw-r--r-- | devices/VirtualCamera/VirtualCameraRenderThread.h | 13 | ||||
-rw-r--r-- | devices/VirtualCamera/VirtualCameraSession.cc | 2 | ||||
-rw-r--r-- | devices/VirtualCamera/VirtualCameraSession.h | 18 | ||||
-rw-r--r-- | devices/VirtualCamera/tests/Android.bp | 1 | ||||
-rw-r--r-- | devices/VirtualCamera/tests/VirtualCameraRenderThreadTest.cc | 146 |
6 files changed, 228 insertions, 23 deletions
diff --git a/devices/VirtualCamera/VirtualCameraRenderThread.cc b/devices/VirtualCamera/VirtualCameraRenderThread.cc index 49e210d..582e47f 100644 --- a/devices/VirtualCamera/VirtualCameraRenderThread.cc +++ b/devices/VirtualCamera/VirtualCameraRenderThread.cc @@ -31,6 +31,7 @@ #include "aidl/android/hardware/camera/device/CaptureResult.h" #include "aidl/android/hardware/camera/device/ErrorCode.h" #include "aidl/android/hardware/camera/device/ICameraDeviceCallback.h" +#include "aidl/android/hardware/camera/device/NotifyMsg.h" #include "aidl/android/hardware/camera/device/ShutterMsg.h" #include "aidl/android/hardware/camera/device/StreamBuffer.h" #include "android-base/thread_annotations.h" @@ -88,11 +89,18 @@ NotifyMsg createShutterNotifyMsg(int frameNumber, return msg; } -NotifyMsg createErrorNotifyMsg(int frameNumber, int streamId, ErrorCode error) { +NotifyMsg createBufferErrorNotifyMsg(int frameNumber, int streamId) { NotifyMsg msg; msg.set<NotifyMsg::Tag::error>(ErrorMsg{.frameNumber = frameNumber, .errorStreamId = streamId, - .errorCode = error}); + .errorCode = ErrorCode::ERROR_BUFFER}); + return msg; +} + +NotifyMsg createRequestErrorNotifyMsg(int frameNumber) { + NotifyMsg msg; + msg.set<NotifyMsg::Tag::error>(ErrorMsg{ + .frameNumber = frameNumber, .errorCode = ErrorCode::ERROR_REQUEST}); return msg; } @@ -154,6 +162,14 @@ void VirtualCameraRenderThread::enqueueTask( mCondVar.notify_one(); } +void VirtualCameraRenderThread::flush() { + std::lock_guard<std::mutex> lock(mLock); + for (auto task = std::move(mQueue.front()); !mQueue.empty(); + mQueue.pop_front()) { + flushCaptureRequest(*task); + } +} + void VirtualCameraRenderThread::start() { mThread = std::thread(&VirtualCameraRenderThread::threadLoop, this); } @@ -264,9 +280,8 @@ void VirtualCameraRenderThread::processCaptureRequest( createShutterNotifyMsg(request.getFrameNumber(), timestamp)}; for (const StreamBuffer& resBuffer : captureResult.outputBuffers) { if (resBuffer.status != BufferStatus::OK) { - notifyMsg.push_back(createErrorNotifyMsg(request.getFrameNumber(), - resBuffer.streamId, - ErrorCode::ERROR_BUFFER)); + notifyMsg.push_back(createBufferErrorNotifyMsg(request.getFrameNumber(), + resBuffer.streamId)); } } @@ -291,6 +306,52 @@ void VirtualCameraRenderThread::processCaptureRequest( ALOGD("%s: Successfully called processCaptureResult", __func__); } +void VirtualCameraRenderThread::flushCaptureRequest( + const ProcessCaptureRequestTask& request) { + const std::chrono::nanoseconds timestamp = + std::chrono::duration_cast<std::chrono::nanoseconds>( + std::chrono::steady_clock::now().time_since_epoch()); + + CaptureResult captureResult; + captureResult.fmqResultSize = 0; + captureResult.frameNumber = request.getFrameNumber(); + captureResult.inputBuffer.streamId = -1; + captureResult.result = createCaptureResultMetadata(timestamp); + + const std::vector<CaptureRequestBuffer>& buffers = request.getBuffers(); + captureResult.outputBuffers.resize(buffers.size()); + + for (int i = 0; i < buffers.size(); ++i) { + const CaptureRequestBuffer& reqBuffer = buffers[i]; + StreamBuffer& resBuffer = captureResult.outputBuffers[i]; + resBuffer.streamId = reqBuffer.getStreamId(); + resBuffer.bufferId = reqBuffer.getBufferId(); + resBuffer.status = BufferStatus::ERROR; + sp<Fence> fence = reqBuffer.getFence(); + if (fence != nullptr && fence->isValid()) { + resBuffer.releaseFence.fds.emplace_back(fence->dup()); + } + } + + auto status = mCameraDeviceCallback->notify( + {createRequestErrorNotifyMsg(request.getFrameNumber())}); + if (!status.isOk()) { + ALOGE("%s: notify call failed: %s", __func__, + status.getDescription().c_str()); + return; + } + + std::vector<::aidl::android::hardware::camera::device::CaptureResult> + captureResults(1); + captureResults[0] = std::move(captureResult); + + status = mCameraDeviceCallback->processCaptureResult(captureResults); + if (!status.isOk()) { + ALOGE("%s: processCaptureResult call failed: %s", __func__, + status.getDescription().c_str()); + } +} + ndk::ScopedAStatus VirtualCameraRenderThread::renderIntoBlobStreamBuffer( const int streamId, const int bufferId, const size_t bufferSize, sp<Fence> fence) { diff --git a/devices/VirtualCamera/VirtualCameraRenderThread.h b/devices/VirtualCamera/VirtualCameraRenderThread.h index 647b8e2..30de7c2 100644 --- a/devices/VirtualCamera/VirtualCameraRenderThread.h +++ b/devices/VirtualCamera/VirtualCameraRenderThread.h @@ -23,9 +23,7 @@ #include <thread> #include "VirtualCameraSessionContext.h" -#include "aidl/android/hardware/camera/device/CaptureRequest.h" #include "aidl/android/hardware/camera/device/ICameraDeviceCallback.h" -#include "android/hardware_buffer.h" #include "util/EglDisplayContext.h" #include "util/EglProgram.h" #include "util/EglSurfaceTexture.h" @@ -98,13 +96,17 @@ class VirtualCameraRenderThread { void stop(); // Equeue capture task for processing on render thread. - void enqueueTask(std::unique_ptr<ProcessCaptureRequestTask> task); + void enqueueTask(std::unique_ptr<ProcessCaptureRequestTask> task) + EXCLUDES(mLock); + + // Flush all in-flight requests. + void flush() EXCLUDES(mLock); // Returns input surface corresponding to "virtual camera sensor". sp<Surface> getInputSurface(); private: - std::unique_ptr<ProcessCaptureRequestTask> dequeueTask(); + std::unique_ptr<ProcessCaptureRequestTask> dequeueTask() EXCLUDES(mLock); // Rendering thread entry point. void threadLoop(); @@ -112,6 +114,9 @@ class VirtualCameraRenderThread { // Process single capture request task (always called on render thread). void processCaptureRequest(const ProcessCaptureRequestTask& captureRequestTask); + // Flush single capture request task returning the error status immediately. + void flushCaptureRequest(const ProcessCaptureRequestTask& captureRequestTask); + // TODO(b/301023410) - Refactor the actual rendering logic off this class for // easier testability. diff --git a/devices/VirtualCamera/VirtualCameraSession.cc b/devices/VirtualCamera/VirtualCameraSession.cc index eb9bac1..70991a5 100644 --- a/devices/VirtualCamera/VirtualCameraSession.cc +++ b/devices/VirtualCamera/VirtualCameraSession.cc @@ -276,6 +276,8 @@ ndk::ScopedAStatus VirtualCameraSession::constructDefaultRequestSettings( ndk::ScopedAStatus VirtualCameraSession::flush() { ALOGV("%s", __func__); + std::lock_guard<std::mutex> lock(mLock); + mRenderThread->flush(); return ndk::ScopedAStatus::ok(); } diff --git a/devices/VirtualCamera/VirtualCameraSession.h b/devices/VirtualCamera/VirtualCameraSession.h index 61b36d5..440720e 100644 --- a/devices/VirtualCamera/VirtualCameraSession.h +++ b/devices/VirtualCamera/VirtualCameraSession.h @@ -17,25 +17,14 @@ #ifndef ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERASESSION_H #define ANDROID_COMPANION_VIRTUALCAMERA_VIRTUALCAMERASESSION_H -#include <map> #include <memory> #include <set> #include "VirtualCameraRenderThread.h" #include "VirtualCameraSessionContext.h" -#include "VirtualCameraStream.h" #include "aidl/android/companion/virtualcamera/IVirtualCameraCallback.h" -#include "aidl/android/hardware/camera/common/Status.h" #include "aidl/android/hardware/camera/device/BnCameraDeviceSession.h" -#include "aidl/android/hardware/camera/device/BufferRequest.h" #include "aidl/android/hardware/camera/device/ICameraDeviceCallback.h" -#include "aidl/android/hardware/camera/device/Stream.h" -#include "aidl/android/hardware/camera/device/StreamBuffer.h" -#include "android-base/unique_fd.h" -#include "android/hardware_buffer.h" -#include "util/EglDisplayContext.h" -#include "util/EglProgram.h" -#include "util/EglSurfaceTexture.h" #include "utils/Mutex.h" namespace android { @@ -71,14 +60,14 @@ class VirtualCameraSession const ::aidl::android::hardware::camera::device::StreamConfiguration& in_requestedConfiguration, std::vector<::aidl::android::hardware::camera::device::HalStream>* - _aidl_return) override; + _aidl_return) override EXCLUDES(mLock); ndk::ScopedAStatus constructDefaultRequestSettings( ::aidl::android::hardware::camera::device::RequestTemplate in_type, ::aidl::android::hardware::camera::device::CameraMetadata* _aidl_return) override; - ndk::ScopedAStatus flush() override; + ndk::ScopedAStatus flush() override EXCLUDES(mLock); ndk::ScopedAStatus getCaptureRequestMetadataQueue( ::aidl::android::hardware::common::fmq::MQDescriptor< @@ -122,7 +111,8 @@ class VirtualCameraSession private: ndk::ScopedAStatus processCaptureRequest( - const ::aidl::android::hardware::camera::device::CaptureRequest& request); + const ::aidl::android::hardware::camera::device::CaptureRequest& request) + EXCLUDES(mLock); const std::string mCameraId; diff --git a/devices/VirtualCamera/tests/Android.bp b/devices/VirtualCamera/tests/Android.bp index 246b5fb..f590454 100644 --- a/devices/VirtualCamera/tests/Android.bp +++ b/devices/VirtualCamera/tests/Android.bp @@ -19,6 +19,7 @@ cc_test { ], srcs: ["EglUtilTest.cc", "VirtualCameraProviderTest.cc", + "VirtualCameraRenderThreadTest.cc", "VirtualCameraServiceTest.cc", "VirtualCameraSessionTest.cc"], test_suites: ["device-tests"], diff --git a/devices/VirtualCamera/tests/VirtualCameraRenderThreadTest.cc b/devices/VirtualCamera/tests/VirtualCameraRenderThreadTest.cc new file mode 100644 index 0000000..2e40d16 --- /dev/null +++ b/devices/VirtualCamera/tests/VirtualCameraRenderThreadTest.cc @@ -0,0 +1,146 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <sys/cdefs.h> + +#include <memory> + +#include "VirtualCameraRenderThread.h" +#include "VirtualCameraSessionContext.h" +#include "aidl/android/hardware/camera/common/CameraDeviceStatus.h" +#include "aidl/android/hardware/camera/common/TorchModeStatus.h" +#include "aidl/android/hardware/camera/device/BnCameraDeviceCallback.h" +#include "aidl/android/hardware/camera/device/BufferRequest.h" +#include "aidl/android/hardware/camera/device/BufferRequestStatus.h" +#include "aidl/android/hardware/camera/device/BufferStatus.h" +#include "aidl/android/hardware/camera/device/CaptureResult.h" +#include "aidl/android/hardware/camera/device/NotifyMsg.h" +#include "aidl/android/hardware/camera/device/StreamBuffer.h" +#include "aidl/android/hardware/camera/device/StreamBufferRet.h" +#include "android/binder_auto_utils.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" + +namespace android { +namespace companion { +namespace virtualcamera { +namespace { + +using ::aidl::android::hardware::camera::common::CameraDeviceStatus; +using ::aidl::android::hardware::camera::common::TorchModeStatus; +using ::aidl::android::hardware::camera::device::BnCameraDeviceCallback; +using ::aidl::android::hardware::camera::device::BufferRequest; +using ::aidl::android::hardware::camera::device::BufferRequestStatus; +using ::aidl::android::hardware::camera::device::BufferStatus; +using ::aidl::android::hardware::camera::device::CaptureResult; +using ::aidl::android::hardware::camera::device::ErrorMsg; +using ::aidl::android::hardware::camera::device::NotifyMsg; +using ::aidl::android::hardware::camera::device::StreamBuffer; +using ::aidl::android::hardware::camera::device::StreamBufferRet; +using ::testing::AllOf; +using ::testing::ElementsAre; +using ::testing::Eq; +using ::testing::Field; +using ::testing::Matcher; +using ::testing::Property; +using ::testing::Return; +using ::testing::SizeIs; + +constexpr int kInputWidth = 640; +constexpr int kInputHeight = 480; + +Matcher<StreamBuffer> IsStreamBufferWithStatus(const int streamId, + const int bufferId, + const BufferStatus status) { + return AllOf(Field(&StreamBuffer::streamId, Eq(streamId)), + Field(&StreamBuffer::bufferId, Eq(bufferId)), + Field(&StreamBuffer::status, Eq(status))); +} + +Matcher<NotifyMsg> IsRequestErrorNotifyMsg(const int frameId) { + return AllOf(Property(&NotifyMsg::getTag, Eq(NotifyMsg::error)), + Property(&NotifyMsg::get<NotifyMsg::error>, + Field(&ErrorMsg::frameNumber, Eq(frameId)))); +} + +class MockCameraDeviceCallback : public BnCameraDeviceCallback { + public: + MOCK_METHOD(ndk::ScopedAStatus, notify, (const std::vector<NotifyMsg>&), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, processCaptureResult, + (const std::vector<CaptureResult>&), (override)); + MOCK_METHOD(ndk::ScopedAStatus, requestStreamBuffers, + (const std::vector<BufferRequest>&, std::vector<StreamBufferRet>*, + BufferRequestStatus*), + (override)); + MOCK_METHOD(ndk::ScopedAStatus, returnStreamBuffers, + (const std::vector<StreamBuffer>&), (override)); +}; + +class VirtualCameraRenderThreadTest : public ::testing::Test { + public: + void SetUp() override { + mSessionContext = std::make_unique<VirtualCameraSessionContext>(); + mMockCameraDeviceCallback = + ndk::SharedRefBase::make<MockCameraDeviceCallback>(); + mRenderThread = std::make_unique<VirtualCameraRenderThread>( + *mSessionContext, kInputWidth, kInputHeight, mMockCameraDeviceCallback); + } + + protected: + std::unique_ptr<VirtualCameraSessionContext> mSessionContext; + std::unique_ptr<VirtualCameraRenderThread> mRenderThread; + std::shared_ptr<MockCameraDeviceCallback> mMockCameraDeviceCallback; +}; + +TEST_F(VirtualCameraRenderThreadTest, FlushReturnsErrorForInFlightRequests) { + const int frameNumber = 42; + const int firstStreamId = 1; + const int firstStreamBufferId = 1234; + const int secondStreamId = 7; + const int secondStreamBufferId = 4321; + + // Notify should be called with the error set to corresponding frame. + EXPECT_CALL(*mMockCameraDeviceCallback, + notify(ElementsAre(IsRequestErrorNotifyMsg(frameNumber)))) + .WillOnce(Return(ndk::ScopedAStatus::ok())); + + // Process capture result should be called with all buffers in error state. + EXPECT_CALL( + *mMockCameraDeviceCallback, + processCaptureResult(ElementsAre(AllOf( + Field(&CaptureResult::frameNumber, frameNumber), + Field(&CaptureResult::outputBuffers, + testing::UnorderedElementsAre( + IsStreamBufferWithStatus(firstStreamId, firstStreamBufferId, + BufferStatus::ERROR), + IsStreamBufferWithStatus(secondStreamId, secondStreamBufferId, + BufferStatus::ERROR))))))) + .WillOnce([]() { return ndk::ScopedAStatus::ok(); }); + + mRenderThread->enqueueTask(std::make_unique<ProcessCaptureRequestTask>( + frameNumber, + std::vector<CaptureRequestBuffer>{ + CaptureRequestBuffer(firstStreamId, firstStreamBufferId), + CaptureRequestBuffer(secondStreamId, secondStreamBufferId)})); + + mRenderThread->flush(); +} + +} // namespace +} // namespace virtualcamera +} // namespace companion +} // namespace android
\ No newline at end of file |