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

Commit 4f6e352d authored by Ján Sebechlebský's avatar Ján Sebechlebský Committed by Android (Google) Code Review
Browse files

Merge "Properly handle memory layout of input buffer for test camera" into main

parents ada78eb7 6402fef6
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);