summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJán Sebechlebský <jsebechlebsky@google.com>2023-11-16 14:37:23 +0000
committerAndroid (Google) Code Review <android-gerrit@google.com>2023-11-16 14:37:23 +0000
commit44a431b4807b2a2e253a60a921edb416bda4010b (patch)
tree92339fc9c5a7af21e77e88161dfd090a4ce78d25
parent207e26a358917495fcddbfa57d005e0030a6140c (diff)
parentfe55cce399018e2b35543ac32043dc88d9d546bb (diff)
downloadcamera-44a431b4807b2a2e253a60a921edb416bda4010b.tar.gz
Merge "Implement flush operation for camera session." into main
-rw-r--r--devices/VirtualCamera/VirtualCameraRenderThread.cc71
-rw-r--r--devices/VirtualCamera/VirtualCameraRenderThread.h13
-rw-r--r--devices/VirtualCamera/VirtualCameraSession.cc2
-rw-r--r--devices/VirtualCamera/VirtualCameraSession.h18
-rw-r--r--devices/VirtualCamera/tests/Android.bp1
-rw-r--r--devices/VirtualCamera/tests/VirtualCameraRenderThreadTest.cc146
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