blob: c0ba09d0ea3af638afc715254035462d82f34ef9 [file]
/*
* Copyright (C) 2024 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.
*/
// #define LOG_NDEBUG 0
#define LOG_TAG "VirtualCameraTestInstance"
#include "VirtualCameraTestInstance.h"
#include <atomic>
#include <chrono>
#include <memory>
#include <mutex>
#include <ratio>
#include <thread>
#include "GLES/gl.h"
#include "android/binder_auto_utils.h"
#include "android/native_window.h"
#include "log/log.h"
#include "util/EglDisplayContext.h"
#include "util/EglProgram.h"
namespace android {
namespace companion {
namespace virtualcamera {
using ::aidl::android::companion::virtualcamera::Format;
using ::aidl::android::companion::virtualcamera::ICaptureResultConsumer;
using ::aidl::android::companion::virtualcamera::VirtualCameraMetadata;
using ::aidl::android::view::Surface;
using ::ndk::ScopedAStatus;
namespace {
std::shared_ptr<ANativeWindow> nativeWindowFromSurface(const Surface& surface) {
ANativeWindow* nativeWindow = surface.get();
if (nativeWindow != nullptr) {
ANativeWindow_acquire(nativeWindow);
}
return std::shared_ptr<ANativeWindow>(nativeWindow, ANativeWindow_release);
}
std::chrono::nanoseconds getCurrentTimestamp() {
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::steady_clock::now().time_since_epoch());
}
} // namespace
TestPatternRenderer::TestPatternRenderer(
std::shared_ptr<ANativeWindow> nativeWindow, int fps)
: mFps(fps), mNativeWindow(nativeWindow) {
}
void TestPatternRenderer::start() {
std::lock_guard<std::mutex> lock(mLock);
if (mRunning.exchange(true, std::memory_order_relaxed)) {
ALOGW("Render thread already started.");
return;
}
mThread =
std::thread(&TestPatternRenderer::renderThreadLoop, this, mNativeWindow);
}
void TestPatternRenderer::stop() {
std::lock_guard<std::mutex> lock(mLock);
if (!mRunning.exchange(false, std::memory_order_relaxed)) {
ALOGW("Render thread already stopped.");
return;
}
mThread.detach();
mRunning = false;
}
void TestPatternRenderer::renderThreadLoop(
std::shared_ptr<ANativeWindow> nativeWindow) {
// Prevent destruction of this instance until the thread terminates.
std::shared_ptr<TestPatternRenderer> thiz = shared_from_this();
ALOGV("Starting test client render loop");
EglDisplayContext eglDisplayContext(nativeWindow);
EglTestPatternProgram testPatternProgram;
const std::chrono::nanoseconds frameDuration(
static_cast<uint64_t>(1e9 / mFps));
while (mRunning) {
std::chrono::nanoseconds startTs = getCurrentTimestamp();
// Render the test pattern and update timestamp.
testPatternProgram.draw(startTs);
eglDisplayContext.swapBuffers();
std::chrono::nanoseconds endTs = getCurrentTimestamp();
std::chrono::nanoseconds elapsed = endTs - startTs;
if (elapsed < frameDuration) {
std::this_thread::sleep_for(frameDuration - elapsed);
}
}
ALOGV("Terminating test client render loop");
}
VirtualCameraTestInstance::VirtualCameraTestInstance(const int fps)
: mFps(fps) {
}
ScopedAStatus VirtualCameraTestInstance::onOpenCamera() {
return ScopedAStatus::ok();
}
::ndk::ScopedAStatus VirtualCameraTestInstance::onConfigureSession(
const VirtualCameraMetadata& sessionParameters,
const std::shared_ptr<ICaptureResultConsumer>& captureResultConsumer) {
(void)sessionParameters;
(void)captureResultConsumer;
ALOGV("%s: Not used for VirtualCameraTestInstance.", __func__);
return ScopedAStatus::ok();
}
ScopedAStatus VirtualCameraTestInstance::onStreamConfigured(
const int32_t streamId, const Surface& surface, const int32_t width,
const int32_t height, const Format imageFormat) {
ALOGV("%s: streamId %d, %dx%d imgFmt=%s", __func__, streamId, width, height,
toString(imageFormat).c_str());
auto renderer = std::make_shared<TestPatternRenderer>(
nativeWindowFromSurface(surface), mFps);
std::lock_guard<std::mutex> lock(mLock);
if (mInputRenderers.try_emplace(streamId, renderer).second) {
renderer->start();
} else {
ALOGE(
"%s: Input stream with id %d is already active, ignoring "
"onStreamConfigured call",
__func__, streamId);
}
return ScopedAStatus::ok();
}
ScopedAStatus VirtualCameraTestInstance::onProcessCaptureRequest(
const int32_t /*streamId*/, const int32_t /*frameId*/,
const std::optional<VirtualCameraMetadata>& /*in_captureRequestSettings*/) {
return ScopedAStatus::ok();
}
ScopedAStatus VirtualCameraTestInstance::onStreamClosed(const int32_t streamId) {
ALOGV("%s: streamId %d", __func__, streamId);
std::shared_ptr<TestPatternRenderer> renderer;
{
std::lock_guard<std::mutex> lock(mLock);
auto it = mInputRenderers.find(streamId);
if (it != mInputRenderers.end()) {
renderer = std::move(it->second);
mInputRenderers.erase(it);
}
}
if (renderer != nullptr) {
renderer->stop();
}
return ScopedAStatus::ok();
}
} // namespace virtualcamera
} // namespace companion
} // namespace android