Loading services/camera/virtualcamera/VirtualCameraRenderThread.cc +6 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ * limitations under the License. */ #include "hardware/gralloc.h" #define LOG_TAG "VirtualCameraRenderThread" #include "VirtualCameraRenderThread.h" Loading Loading @@ -392,6 +393,11 @@ void VirtualCameraRenderThread::threadLoop() { EglTextureProgram::TextureFormat::RGBA); mEglSurfaceTexture = std::make_unique<EglSurfaceTexture>( mInputSurfaceSize.width, mInputSurfaceSize.height); sp<Surface> inputSurface = mEglSurfaceTexture->getSurface(); if (mTestMode) { inputSurface->connect(NATIVE_WINDOW_API_CPU, false, nullptr); } mInputSurfacePromise.set_value(mEglSurfaceTexture->getSurface()); while (std::unique_ptr<ProcessCaptureRequestTask> task = dequeueTask()) { Loading services/camera/virtualcamera/util/TestPatternHelper.cc +82 −43 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ // #define LOG_NDEBUG 0 #define LOG_TAG "TestPatternHelper" #include "TestPatternHelper.h" Loading @@ -23,6 +24,9 @@ #include <cstdint> #include "log/log.h" #include "nativebase/nativebase.h" #include "system/graphics.h" #include "ui/GraphicBuffer.h" #include "utils/Errors.h" namespace android { Loading @@ -31,6 +35,10 @@ namespace virtualcamera { namespace { using namespace std::chrono_literals; static constexpr std::chrono::milliseconds kAcquireFenceTimeout = 500ms; uint8_t julia(const std::complex<float> n, const std::complex<float> c) { std::complex<float> z = n; for (int i = 0; i < 64; i++) { Loading @@ -40,72 +48,103 @@ uint8_t julia(const std::complex<float> n, const std::complex<float> c) { return 0xff; } uint8_t pixelToFractal(const int x, const int y, const std::complex<float> c) { std::complex<float> n(float(x) / 640.0f - 0.5, float(y) / 480.0f - 0.5); uint8_t pixelToFractal(const int x, const int y, const int width, const int height, const std::complex<float> c) { std::complex<float> n(float(x) / float(width) - 0.5, float(y) / float(height) - 0.5); return julia(n * 5.f, c); } void renderTestPatternYcbCr420(uint8_t* data_ptr, const int width, void renderTestPatternYcbCr420(const android_ycbcr& ycbr, const int width, const int height, const int frameNumber) { float time = float(frameNumber) / 120.0f; const std::complex<float> c(std::sin(time), std::cos(time)); uint8_t* y_data = data_ptr; uint8_t* uv_data = static_cast<uint8_t*>(y_data + width * height); uint8_t* y = reinterpret_cast<uint8_t*>(ycbr.y); uint8_t* cb = reinterpret_cast<uint8_t*>(ycbr.cb); uint8_t* cr = reinterpret_cast<uint8_t*>(ycbr.cr); for (int i = 0; i < width; ++i) { for (int j = 0; j < height; ++j) { y_data[j * width + i] = pixelToFractal(i, j, c * 0.78f); if ((i & 1) && (j & 1)) { uv_data[((j / 2) * (width / 2) + i / 2) * 2] = static_cast<uint8_t>((float(i) / float(width)) * 255.f); uv_data[((j / 2) * (width / 2) + i / 2) * 2 + 1] = static_cast<uint8_t>((float(j) / float(height)) * 255.f); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { y[row * ycbr.ystride + col] = pixelToFractal(col, row, width, height, c * 0.78f); } } int cWidth = width / 2; int cHeight = height / 2; for (int row = 0; row < cHeight; row++) { for (int col = 0; col < cWidth; col++) { cb[row * ycbr.cstride + col * ycbr.chroma_step] = static_cast<uint8_t>((float(col) / float(cWidth)) * 255.f); cr[row * ycbr.cstride + col * ycbr.chroma_step] = static_cast<uint8_t>((float(row) / float(cHeight)) * 255.f); } } } } // namespace // This is just to see some meaningfull image in the buffer for testing, only // works with YcbCr420. void renderTestPatternYCbCr420(const std::shared_ptr<AHardwareBuffer> buffer, const int frameNumber, const int fence) { AHardwareBuffer_Planes planes_info; void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber) { if (surface == nullptr) { ALOGE("%s: null surface, skipping render", __func__); return; } AHardwareBuffer_Desc hwBufferDesc; AHardwareBuffer_describe(buffer.get(), &hwBufferDesc); ANativeWindowBuffer* buffer; int fenceFd; int ret = ANativeWindow_dequeueBuffer(surface.get(), &buffer, &fenceFd); if (ret != NO_ERROR) { ALOGE( "%s: Error while deuqueing buffer from surface, " "ANativeWindow_dequeueBuffer returned %d", __func__, ret); return; } const int width = hwBufferDesc.width; const int height = hwBufferDesc.height; if (buffer == nullptr) { ALOGE("%s: ANativeWindowBuffer is null after dequeing", __func__); return; } int result = AHardwareBuffer_lockPlanes(buffer.get(), AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, fence, nullptr, &planes_info); if (result != OK) { ALOGE("%s: Failed to lock planes: %d", __func__, result); sp<Fence> fence = sp<Fence>::make(fenceFd); if (fence->isValid()) { ret = fence->wait(kAcquireFenceTimeout.count()); if (ret != NO_ERROR) { ALOGE("%s: Timeout while waiting for the fence to clear", __func__); ANativeWindow_queueBuffer(surface.get(), buffer, fence->dup()); return; } } renderTestPatternYcbCr420( reinterpret_cast<uint8_t*>(planes_info.planes[0].data), width, height, frameNumber); sp<GraphicBuffer> gBuffer = GraphicBuffer::from(buffer); android_ycbcr ycbr; AHardwareBuffer_unlock(buffer.get(), nullptr); ret = gBuffer->lockAsyncYCbCr(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &ycbr, fence->dup()); if (ret != NO_ERROR) { ALOGE("%s: Failed to lock buffer retrieved from surface, ret %d", __func__, ret); return; } void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber) { ANativeWindow_Buffer buffer; surface->lock(&buffer, nullptr); ALOGV("buffer: %dx%d stride %d, pixfmt %d", buffer.width, buffer.height, buffer.stride, buffer.format); renderTestPatternYcbCr420(ycbr, gBuffer->getWidth(), gBuffer->getHeight(), frameNumber); renderTestPatternYcbCr420(reinterpret_cast<uint8_t*>(buffer.bits), buffer.width, buffer.height, frameNumber); ret = gBuffer->unlock(); if (ret != NO_ERROR) { ALOGE("%s: Failed to unlock buffer, ret %d", __func__, ret); return; } surface->unlockAndPost(); ret = ANativeWindow_queueBuffer(surface.get(), buffer, /*fenceFd=*/-1); if (ret != NO_ERROR) { ALOGE( "%s: Error while queing buffer to surface, ANativeWindow_queueBuffer " "returned %d", __func__, ret); return; } } } // namespace virtualcamera Loading services/camera/virtualcamera/util/TestPatternHelper.h +0 −8 Original line number Diff line number Diff line Loading @@ -17,20 +17,12 @@ #ifndef ANDROID_COMPANION_VIRTUALCAMERA_TESTPATTERNHELPER_H #define ANDROID_COMPANION_VIRTUALCAMERA_TESTPATTERNHELPER_H #include <memory> #include "android/hardware_buffer.h" #include "gui/Surface.h" namespace android { namespace companion { namespace virtualcamera { // Helper function filling hardware buffer with test pattern for debugging / // testing purposes. void renderTestPatternYCbCr420(std::shared_ptr<AHardwareBuffer> buffer, int frameNumber, int fence = -1); // Helper function for rendering test pattern into Surface. void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber); Loading Loading
services/camera/virtualcamera/VirtualCameraRenderThread.cc +6 −0 Original line number Diff line number Diff line Loading @@ -14,6 +14,7 @@ * limitations under the License. */ #include "hardware/gralloc.h" #define LOG_TAG "VirtualCameraRenderThread" #include "VirtualCameraRenderThread.h" Loading Loading @@ -392,6 +393,11 @@ void VirtualCameraRenderThread::threadLoop() { EglTextureProgram::TextureFormat::RGBA); mEglSurfaceTexture = std::make_unique<EglSurfaceTexture>( mInputSurfaceSize.width, mInputSurfaceSize.height); sp<Surface> inputSurface = mEglSurfaceTexture->getSurface(); if (mTestMode) { inputSurface->connect(NATIVE_WINDOW_API_CPU, false, nullptr); } mInputSurfacePromise.set_value(mEglSurfaceTexture->getSurface()); while (std::unique_ptr<ProcessCaptureRequestTask> task = dequeueTask()) { Loading
services/camera/virtualcamera/util/TestPatternHelper.cc +82 −43 Original line number Diff line number Diff line Loading @@ -15,6 +15,7 @@ */ // #define LOG_NDEBUG 0 #define LOG_TAG "TestPatternHelper" #include "TestPatternHelper.h" Loading @@ -23,6 +24,9 @@ #include <cstdint> #include "log/log.h" #include "nativebase/nativebase.h" #include "system/graphics.h" #include "ui/GraphicBuffer.h" #include "utils/Errors.h" namespace android { Loading @@ -31,6 +35,10 @@ namespace virtualcamera { namespace { using namespace std::chrono_literals; static constexpr std::chrono::milliseconds kAcquireFenceTimeout = 500ms; uint8_t julia(const std::complex<float> n, const std::complex<float> c) { std::complex<float> z = n; for (int i = 0; i < 64; i++) { Loading @@ -40,72 +48,103 @@ uint8_t julia(const std::complex<float> n, const std::complex<float> c) { return 0xff; } uint8_t pixelToFractal(const int x, const int y, const std::complex<float> c) { std::complex<float> n(float(x) / 640.0f - 0.5, float(y) / 480.0f - 0.5); uint8_t pixelToFractal(const int x, const int y, const int width, const int height, const std::complex<float> c) { std::complex<float> n(float(x) / float(width) - 0.5, float(y) / float(height) - 0.5); return julia(n * 5.f, c); } void renderTestPatternYcbCr420(uint8_t* data_ptr, const int width, void renderTestPatternYcbCr420(const android_ycbcr& ycbr, const int width, const int height, const int frameNumber) { float time = float(frameNumber) / 120.0f; const std::complex<float> c(std::sin(time), std::cos(time)); uint8_t* y_data = data_ptr; uint8_t* uv_data = static_cast<uint8_t*>(y_data + width * height); uint8_t* y = reinterpret_cast<uint8_t*>(ycbr.y); uint8_t* cb = reinterpret_cast<uint8_t*>(ycbr.cb); uint8_t* cr = reinterpret_cast<uint8_t*>(ycbr.cr); for (int i = 0; i < width; ++i) { for (int j = 0; j < height; ++j) { y_data[j * width + i] = pixelToFractal(i, j, c * 0.78f); if ((i & 1) && (j & 1)) { uv_data[((j / 2) * (width / 2) + i / 2) * 2] = static_cast<uint8_t>((float(i) / float(width)) * 255.f); uv_data[((j / 2) * (width / 2) + i / 2) * 2 + 1] = static_cast<uint8_t>((float(j) / float(height)) * 255.f); for (int row = 0; row < height; row++) { for (int col = 0; col < width; col++) { y[row * ycbr.ystride + col] = pixelToFractal(col, row, width, height, c * 0.78f); } } int cWidth = width / 2; int cHeight = height / 2; for (int row = 0; row < cHeight; row++) { for (int col = 0; col < cWidth; col++) { cb[row * ycbr.cstride + col * ycbr.chroma_step] = static_cast<uint8_t>((float(col) / float(cWidth)) * 255.f); cr[row * ycbr.cstride + col * ycbr.chroma_step] = static_cast<uint8_t>((float(row) / float(cHeight)) * 255.f); } } } } // namespace // This is just to see some meaningfull image in the buffer for testing, only // works with YcbCr420. void renderTestPatternYCbCr420(const std::shared_ptr<AHardwareBuffer> buffer, const int frameNumber, const int fence) { AHardwareBuffer_Planes planes_info; void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber) { if (surface == nullptr) { ALOGE("%s: null surface, skipping render", __func__); return; } AHardwareBuffer_Desc hwBufferDesc; AHardwareBuffer_describe(buffer.get(), &hwBufferDesc); ANativeWindowBuffer* buffer; int fenceFd; int ret = ANativeWindow_dequeueBuffer(surface.get(), &buffer, &fenceFd); if (ret != NO_ERROR) { ALOGE( "%s: Error while deuqueing buffer from surface, " "ANativeWindow_dequeueBuffer returned %d", __func__, ret); return; } const int width = hwBufferDesc.width; const int height = hwBufferDesc.height; if (buffer == nullptr) { ALOGE("%s: ANativeWindowBuffer is null after dequeing", __func__); return; } int result = AHardwareBuffer_lockPlanes(buffer.get(), AHARDWAREBUFFER_USAGE_CPU_READ_RARELY, fence, nullptr, &planes_info); if (result != OK) { ALOGE("%s: Failed to lock planes: %d", __func__, result); sp<Fence> fence = sp<Fence>::make(fenceFd); if (fence->isValid()) { ret = fence->wait(kAcquireFenceTimeout.count()); if (ret != NO_ERROR) { ALOGE("%s: Timeout while waiting for the fence to clear", __func__); ANativeWindow_queueBuffer(surface.get(), buffer, fence->dup()); return; } } renderTestPatternYcbCr420( reinterpret_cast<uint8_t*>(planes_info.planes[0].data), width, height, frameNumber); sp<GraphicBuffer> gBuffer = GraphicBuffer::from(buffer); android_ycbcr ycbr; AHardwareBuffer_unlock(buffer.get(), nullptr); ret = gBuffer->lockAsyncYCbCr(GraphicBuffer::USAGE_SW_WRITE_OFTEN, &ycbr, fence->dup()); if (ret != NO_ERROR) { ALOGE("%s: Failed to lock buffer retrieved from surface, ret %d", __func__, ret); return; } void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber) { ANativeWindow_Buffer buffer; surface->lock(&buffer, nullptr); ALOGV("buffer: %dx%d stride %d, pixfmt %d", buffer.width, buffer.height, buffer.stride, buffer.format); renderTestPatternYcbCr420(ycbr, gBuffer->getWidth(), gBuffer->getHeight(), frameNumber); renderTestPatternYcbCr420(reinterpret_cast<uint8_t*>(buffer.bits), buffer.width, buffer.height, frameNumber); ret = gBuffer->unlock(); if (ret != NO_ERROR) { ALOGE("%s: Failed to unlock buffer, ret %d", __func__, ret); return; } surface->unlockAndPost(); ret = ANativeWindow_queueBuffer(surface.get(), buffer, /*fenceFd=*/-1); if (ret != NO_ERROR) { ALOGE( "%s: Error while queing buffer to surface, ANativeWindow_queueBuffer " "returned %d", __func__, ret); return; } } } // namespace virtualcamera Loading
services/camera/virtualcamera/util/TestPatternHelper.h +0 −8 Original line number Diff line number Diff line Loading @@ -17,20 +17,12 @@ #ifndef ANDROID_COMPANION_VIRTUALCAMERA_TESTPATTERNHELPER_H #define ANDROID_COMPANION_VIRTUALCAMERA_TESTPATTERNHELPER_H #include <memory> #include "android/hardware_buffer.h" #include "gui/Surface.h" namespace android { namespace companion { namespace virtualcamera { // Helper function filling hardware buffer with test pattern for debugging / // testing purposes. void renderTestPatternYCbCr420(std::shared_ptr<AHardwareBuffer> buffer, int frameNumber, int fence = -1); // Helper function for rendering test pattern into Surface. void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber); Loading