Properly handle memory layout of input buffer for test camera
Bug: 301023410
Test: manually with OpenCamera
Test: atest virtual_camera_tests
Test: atest VirtualCameraTest
Change-Id: I32c0074f7d6cf2f098525e48686dafb0970d4298
diff --git a/services/camera/virtualcamera/VirtualCameraRenderThread.cc b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
index 25cc270..50b5709 100644
--- a/services/camera/virtualcamera/VirtualCameraRenderThread.cc
+++ b/services/camera/virtualcamera/VirtualCameraRenderThread.cc
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include "hardware/gralloc.h"
#define LOG_TAG "VirtualCameraRenderThread"
#include "VirtualCameraRenderThread.h"
@@ -392,6 +393,11 @@
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()) {
diff --git a/services/camera/virtualcamera/util/TestPatternHelper.cc b/services/camera/virtualcamera/util/TestPatternHelper.cc
index a00a1b8..274996a 100644
--- a/services/camera/virtualcamera/util/TestPatternHelper.cc
+++ b/services/camera/virtualcamera/util/TestPatternHelper.cc
@@ -15,6 +15,7 @@
*/
// #define LOG_NDEBUG 0
+
#define LOG_TAG "TestPatternHelper"
#include "TestPatternHelper.h"
@@ -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 {
@@ -31,6 +35,10 @@
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++) {
@@ -40,72 +48,103 @@
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;
-
- AHardwareBuffer_Desc hwBufferDesc;
- AHardwareBuffer_describe(buffer.get(), &hwBufferDesc);
-
- const int width = hwBufferDesc.width;
- const int height = hwBufferDesc.height;
-
- 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);
+void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber) {
+ if (surface == nullptr) {
+ ALOGE("%s: null surface, skipping render", __func__);
return;
}
- renderTestPatternYcbCr420(
- reinterpret_cast<uint8_t*>(planes_info.planes[0].data), width, height,
- frameNumber);
+ 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;
+ }
- AHardwareBuffer_unlock(buffer.get(), nullptr);
-}
+ if (buffer == nullptr) {
+ ALOGE("%s: ANativeWindowBuffer is null after dequeing", __func__);
+ return;
+ }
-void renderTestPatternYCbCr420(sp<Surface> surface, int frameNumber) {
- ANativeWindow_Buffer buffer;
- surface->lock(&buffer, nullptr);
+ 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;
+ }
+ }
- ALOGV("buffer: %dx%d stride %d, pixfmt %d", buffer.width, buffer.height,
- buffer.stride, buffer.format);
+ sp<GraphicBuffer> gBuffer = GraphicBuffer::from(buffer);
+ android_ycbcr ycbr;
- renderTestPatternYcbCr420(reinterpret_cast<uint8_t*>(buffer.bits),
- buffer.width, buffer.height, frameNumber);
+ 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;
+ }
- surface->unlockAndPost();
+ renderTestPatternYcbCr420(ycbr, gBuffer->getWidth(), gBuffer->getHeight(),
+ frameNumber);
+
+ ret = gBuffer->unlock();
+ if (ret != NO_ERROR) {
+ ALOGE("%s: Failed to unlock buffer, ret %d", __func__, ret);
+ return;
+ }
+
+ 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
diff --git a/services/camera/virtualcamera/util/TestPatternHelper.h b/services/camera/virtualcamera/util/TestPatternHelper.h
index aca1cdd..f842b29 100644
--- a/services/camera/virtualcamera/util/TestPatternHelper.h
+++ b/services/camera/virtualcamera/util/TestPatternHelper.h
@@ -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);