Reconfigure the input surface when needed.
When the set of output streams changes so that
different input configuration is picked, reconfigure
client to use new Surface with preferred input configuration.
Bug: 343628528
Test: atest virtual_camera_tests
Test: atest CtsVirtualDevicesCameraTestCases
Test: atest CtsVirtualDevicesCameraCtsTestCases
Change-Id: I8ec56adf0a230981be779a9b51d8505f1c83647c
diff --git a/services/camera/virtualcamera/VirtualCameraSession.cc b/services/camera/virtualcamera/VirtualCameraSession.cc
index 7f0adc3..e1815c7 100644
--- a/services/camera/virtualcamera/VirtualCameraSession.cc
+++ b/services/camera/virtualcamera/VirtualCameraSession.cc
@@ -212,6 +212,27 @@
return Resolution(inputConfig.width, inputConfig.height);
}
+std::optional<Resolution> resolutionFromSurface(const sp<Surface> surface) {
+ Resolution res{0, 0};
+ if (surface == nullptr) {
+ ALOGE("%s: Cannot get resolution from null surface", __func__);
+ return std::nullopt;
+ }
+
+ int status = surface->query(NATIVE_WINDOW_WIDTH, &res.width);
+ if (status != NO_ERROR) {
+ ALOGE("%s: Failed to get width from surface", __func__);
+ return std::nullopt;
+ }
+
+ status = surface->query(NATIVE_WINDOW_HEIGHT, &res.height);
+ if (status != NO_ERROR) {
+ ALOGE("%s: Failed to get height from surface", __func__);
+ return std::nullopt;
+ }
+ return res;
+}
+
std::optional<SupportedStreamConfiguration> pickInputConfigurationForStreams(
const std::vector<Stream>& requestedStreams,
const std::vector<SupportedStreamConfiguration>& supportedInputConfigs) {
@@ -292,13 +313,13 @@
ndk::ScopedAStatus VirtualCameraSession::close() {
ALOGV("%s", __func__);
-
- if (mVirtualCameraClientCallback != nullptr) {
- mVirtualCameraClientCallback->onStreamClosed(/*streamId=*/0);
- }
-
{
std::lock_guard<std::mutex> lock(mLock);
+
+ if (mVirtualCameraClientCallback != nullptr) {
+ mVirtualCameraClientCallback->onStreamClosed(mCurrentInputStreamId);
+ }
+
if (mRenderThread != nullptr) {
mRenderThread->stop();
mRenderThread = nullptr;
@@ -339,6 +360,7 @@
}
sp<Surface> inputSurface = nullptr;
+ int inputStreamId = -1;
std::optional<SupportedStreamConfiguration> inputConfig;
{
std::lock_guard<std::mutex> lock(mLock);
@@ -358,13 +380,49 @@
__func__, in_requestedConfiguration.toString().c_str());
return cameraStatus(Status::ILLEGAL_ARGUMENT);
}
- if (mRenderThread == nullptr) {
- mRenderThread = std::make_unique<VirtualCameraRenderThread>(
- mSessionContext, resolutionFromInputConfig(*inputConfig),
- virtualCamera->getMaxInputResolution(), mCameraDeviceCallback);
- mRenderThread->start();
- inputSurface = mRenderThread->getInputSurface();
+
+ if (mRenderThread != nullptr) {
+ // If there's already a render thread, it means this is not a first
+ // configuration call. If the surface has the same resolution and pixel
+ // format as the picked config, we don't need to do anything, the current
+ // render thread is capable of serving new set of configuration. However
+ // if it differens, we need to discard the current surface and
+ // reinitialize the render thread.
+
+ std::optional<Resolution> currentInputResolution =
+ resolutionFromSurface(mRenderThread->getInputSurface());
+ if (currentInputResolution.has_value() &&
+ *currentInputResolution == resolutionFromInputConfig(*inputConfig)) {
+ ALOGI(
+ "%s: Newly configured set of streams matches existing client "
+ "surface (%dx%d)",
+ __func__, currentInputResolution->width,
+ currentInputResolution->height);
+ return ndk::ScopedAStatus::ok();
+ }
+
+ if (mVirtualCameraClientCallback != nullptr) {
+ mVirtualCameraClientCallback->onStreamClosed(mCurrentInputStreamId);
+ }
+
+ ALOGV(
+ "%s: Newly requested output streams are not suitable for "
+ "pre-existing surface (%dx%d), creating new surface (%dx%d)",
+ __func__, currentInputResolution->width,
+ currentInputResolution->height, inputConfig->width,
+ inputConfig->height);
+
+ mRenderThread->flush();
+ mRenderThread->stop();
}
+
+ mRenderThread = std::make_unique<VirtualCameraRenderThread>(
+ mSessionContext, resolutionFromInputConfig(*inputConfig),
+ virtualCamera->getMaxInputResolution(), mCameraDeviceCallback);
+ mRenderThread->start();
+ inputSurface = mRenderThread->getInputSurface();
+ inputStreamId = mCurrentInputStreamId =
+ virtualCamera->allocateInputStreamId();
}
if (mVirtualCameraClientCallback != nullptr && inputSurface != nullptr) {
@@ -372,7 +430,7 @@
// support for multiple input streams is implemented. For now we always
// create single texture.
mVirtualCameraClientCallback->onStreamConfigured(
- /*streamId=*/0, aidl::android::view::Surface(inputSurface.get()),
+ inputStreamId, aidl::android::view::Surface(inputSurface.get()),
inputConfig->width, inputConfig->height, inputConfig->pixelFormat);
}
@@ -519,6 +577,7 @@
std::shared_ptr<ICameraDeviceCallback> cameraCallback = nullptr;
RequestSettings requestSettings;
+ int currentInputStreamId;
{
std::lock_guard<std::mutex> lock(mLock);
@@ -537,6 +596,7 @@
requestSettings = createSettingsFromMetadata(mCurrentRequestMetadata);
cameraCallback = mCameraDeviceCallback;
+ currentInputStreamId = mCurrentInputStreamId;
}
if (cameraCallback == nullptr) {
@@ -574,7 +634,7 @@
if (mVirtualCameraClientCallback != nullptr) {
auto status = mVirtualCameraClientCallback->onProcessCaptureRequest(
- /*streamId=*/0, request.frameNumber);
+ currentInputStreamId, request.frameNumber);
if (!status.isOk()) {
ALOGE(
"Failed to invoke onProcessCaptureRequest client callback for frame "