blob: ff4a2d80d8e1df1d2c2bf60ed8d6b4de347608e1 [file] [log] [blame]
/*
* 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::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));
std::chrono::nanoseconds lastFrameTs(0);
int frameNumber = 0;
while (mRunning) {
// Wait for appropriate amount of time to meet configured FPS.
std::chrono::nanoseconds ts = getCurrentTimestamp();
std::chrono::nanoseconds currentDuration = ts - lastFrameTs;
if (currentDuration < frameDuration) {
std::this_thread::sleep_for(frameDuration - currentDuration);
}
// Render the test pattern and update timestamp.
testPatternProgram.draw(ts);
eglDisplayContext.swapBuffers();
lastFrameTs = getCurrentTimestamp();
}
ALOGV("Terminating test client render loop");
}
VirtualCameraTestInstance::VirtualCameraTestInstance(const int fps)
: mFps(fps) {
}
ScopedAStatus VirtualCameraTestInstance::onStreamConfigured(
const int32_t streamId, const Surface& surface, const int32_t width,
const int32_t height, const Format pixelFormat) {
ALOGV("%s: streamId %d, %dx%d pixFmt=%s", __func__, streamId, width, height,
toString(pixelFormat).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 /*in_streamId*/, const int32_t /*in_frameId*/) {
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