Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6402fef6 authored by Jan Sebechlebsky's avatar Jan Sebechlebsky
Browse files

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
parent 4d98da0f
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
 * limitations under the License.
 */

#include "hardware/gralloc.h"
#define LOG_TAG "VirtualCameraRenderThread"
#include "VirtualCameraRenderThread.h"

@@ -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()) {
+82 −43
Original line number Diff line number Diff line
@@ -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 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++) {
@@ -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
+0 −8
Original line number Diff line number Diff line
@@ -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);