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

Commit 18ac32cc authored by Jan Sebechlebsky's avatar Jan Sebechlebsky
Browse files

Trigger texture update on each submission of input buffer.

When input queue is full (which will happen if there are
no capture requests coming from the camera client), it blocks
until there's buffer freed up.
This is not desired (for example, it causes video decoder
attached to the virtual camera surface to drop frames), and
causes that the subsequent capture request received by camera
won't get the most recent image from the virtual camera owner.

This cl fixes this behavior, by consuming the buffer from
the input queue immediatelly after it's submitted.

Bug: 344572859
Test: atest VirtualCameraCaptureTest
Change-Id: Ic7556921630d56f73bbd861d35c5218351217ee6
parent 328d19bf
Loading
Loading
Loading
Loading
+70 −8
Original line number Original line Diff line number Diff line
@@ -24,6 +24,7 @@
#include <memory>
#include <memory>
#include <mutex>
#include <mutex>
#include <thread>
#include <thread>
#include <utility>
#include <vector>
#include <vector>


#include "Exif.h"
#include "Exif.h"
@@ -78,6 +79,15 @@ using ::android::hardware::camera::common::helper::ExifUtils;


namespace {
namespace {


// helper type for the visitor
template <class... Ts>
struct overloaded : Ts... {
  using Ts::operator()...;
};
// explicit deduction guide (not needed as of C++20)
template <class... Ts>
overloaded(Ts...) -> overloaded<Ts...>;

using namespace std::chrono_literals;
using namespace std::chrono_literals;


static constexpr std::chrono::milliseconds kAcquireFenceTimeout = 500ms;
static constexpr std::chrono::milliseconds kAcquireFenceTimeout = 500ms;
@@ -89,6 +99,8 @@ static constexpr uint8_t kPipelineDepth = 2;


static constexpr size_t kJpegThumbnailBufferSize = 32 * 1024;  // 32 KiB
static constexpr size_t kJpegThumbnailBufferSize = 32 * 1024;  // 32 KiB


static constexpr UpdateTextureTask kUpdateTextureTask;

CameraMetadata createCaptureResultMetadata(
CameraMetadata createCaptureResultMetadata(
    const std::chrono::nanoseconds timestamp,
    const std::chrono::nanoseconds timestamp,
    const RequestSettings& requestSettings,
    const RequestSettings& requestSettings,
@@ -287,6 +299,21 @@ std::chrono::nanoseconds getMaxFrameDuration(
      static_cast<uint64_t>(1e9 / VirtualCameraDevice::kMinFps));
      static_cast<uint64_t>(1e9 / VirtualCameraDevice::kMinFps));
}
}


class FrameAvailableListenerProxy : public ConsumerBase::FrameAvailableListener {
 public:
  FrameAvailableListenerProxy(std::function<void()> callback)
      : mOnFrameAvailableCallback(callback) {
  }

  virtual void onFrameAvailable(const BufferItem&) override {
    ALOGV("%s: onFrameAvailable", __func__);
    mOnFrameAvailableCallback();
  }

 private:
  std::function<void()> mOnFrameAvailableCallback;
};

}  // namespace
}  // namespace


CaptureRequestBuffer::CaptureRequestBuffer(int streamId, int bufferId,
CaptureRequestBuffer::CaptureRequestBuffer(int streamId, int bufferId,
@@ -345,9 +372,25 @@ const RequestSettings& ProcessCaptureRequestTask::getRequestSettings() const {
  return mRequestSettings;
  return mRequestSettings;
}
}


void VirtualCameraRenderThread::requestTextureUpdate() {
  std::lock_guard<std::mutex> lock(mLock);
  // If queue is not empty, we don't need to set the mTextureUpdateRequested
  // flag, since the texture will be updated during ProcessCaptureRequestTask
  // processing anyway.
  if (mQueue.empty()) {
    mTextureUpdateRequested = true;
    mCondVar.notify_one();
  }
}

void VirtualCameraRenderThread::enqueueTask(
void VirtualCameraRenderThread::enqueueTask(
    std::unique_ptr<ProcessCaptureRequestTask> task) {
    std::unique_ptr<ProcessCaptureRequestTask> task) {
  std::lock_guard<std::mutex> lock(mLock);
  std::lock_guard<std::mutex> lock(mLock);
  // When enqueving process capture request task, clear the
  // mTextureUpdateRequested flag. If this flag is set, the texture was not yet
  // updated and it will be updated when processing ProcessCaptureRequestTask
  // anyway.
  mTextureUpdateRequested = false;
  mQueue.emplace_back(std::move(task));
  mQueue.emplace_back(std::move(task));
  mCondVar.notify_one();
  mCondVar.notify_one();
}
}
@@ -377,8 +420,7 @@ sp<Surface> VirtualCameraRenderThread::getInputSurface() {
  return mInputSurfaceFuture.get();
  return mInputSurfaceFuture.get();
}
}


std::unique_ptr<ProcessCaptureRequestTask>
RenderThreadTask VirtualCameraRenderThread::dequeueTask() {
VirtualCameraRenderThread::dequeueTask() {
  std::unique_lock<std::mutex> lock(mLock);
  std::unique_lock<std::mutex> lock(mLock);
  // Clang's thread safety analysis doesn't perform alias analysis,
  // Clang's thread safety analysis doesn't perform alias analysis,
  // so it doesn't support moveable std::unique_lock.
  // so it doesn't support moveable std::unique_lock.
@@ -389,12 +431,20 @@ VirtualCameraRenderThread::dequeueTask() {
  ScopedLockAssertion lockAssertion(mLock);
  ScopedLockAssertion lockAssertion(mLock);


  mCondVar.wait(lock, [this]() REQUIRES(mLock) {
  mCondVar.wait(lock, [this]() REQUIRES(mLock) {
    return mPendingExit || !mQueue.empty();
    return mPendingExit || mTextureUpdateRequested || !mQueue.empty();
  });
  });
  if (mPendingExit) {
  if (mPendingExit) {
    return nullptr;
    // Render thread task with null task signals render thread to terminate.
    return RenderThreadTask(nullptr);
  }
  }
  std::unique_ptr<ProcessCaptureRequestTask> task = std::move(mQueue.front());
  if (mTextureUpdateRequested) {
    // If mTextureUpdateRequested, it's guaranteed the queue is empty, return
    // kUpdateTextureTask to signal we want render thread to update the texture
    // (consume buffer from the queue).
    mTextureUpdateRequested = false;
    return RenderThreadTask(kUpdateTextureTask);
  }
  RenderThreadTask task(std::move(mQueue.front()));
  mQueue.pop_front();
  mQueue.pop_front();
  return task;
  return task;
}
}
@@ -409,11 +459,23 @@ void VirtualCameraRenderThread::threadLoop() {
      EglTextureProgram::TextureFormat::RGBA);
      EglTextureProgram::TextureFormat::RGBA);
  mEglSurfaceTexture = std::make_unique<EglSurfaceTexture>(
  mEglSurfaceTexture = std::make_unique<EglSurfaceTexture>(
      mInputSurfaceSize.width, mInputSurfaceSize.height);
      mInputSurfaceSize.width, mInputSurfaceSize.height);
  sp<FrameAvailableListenerProxy> frameAvailableListener =
      sp<FrameAvailableListenerProxy>::make(
          [this]() { requestTextureUpdate(); });
  mEglSurfaceTexture->setFrameAvailableListener(frameAvailableListener);


  mInputSurfacePromise.set_value(mEglSurfaceTexture->getSurface());
  mInputSurfacePromise.set_value(mEglSurfaceTexture->getSurface());


  while (std::unique_ptr<ProcessCaptureRequestTask> task = dequeueTask()) {
  while (RenderThreadTask task = dequeueTask()) {
    processCaptureRequest(*task);
    std::visit(
        overloaded{[this](const std::unique_ptr<ProcessCaptureRequestTask>& t) {
                     processTask(*t);
                   },
                   [this](const UpdateTextureTask&) {
                     ALOGV("Idle update of the texture");
                     mEglSurfaceTexture->updateTexture();
                   }},
        task);
  }
  }


  // Destroy EGL utilities still on the render thread.
  // Destroy EGL utilities still on the render thread.
@@ -425,7 +487,7 @@ void VirtualCameraRenderThread::threadLoop() {
  ALOGV("Render thread exiting");
  ALOGV("Render thread exiting");
}
}


void VirtualCameraRenderThread::processCaptureRequest(
void VirtualCameraRenderThread::processTask(
    const ProcessCaptureRequestTask& request) {
    const ProcessCaptureRequestTask& request) {
  std::chrono::nanoseconds timestamp =
  std::chrono::nanoseconds timestamp =
      std::chrono::duration_cast<std::chrono::nanoseconds>(
      std::chrono::duration_cast<std::chrono::nanoseconds>(
+28 −3
Original line number Original line Diff line number Diff line
@@ -23,6 +23,7 @@
#include <future>
#include <future>
#include <memory>
#include <memory>
#include <thread>
#include <thread>
#include <variant>
#include <vector>
#include <vector>


#include "VirtualCameraDevice.h"
#include "VirtualCameraDevice.h"
@@ -34,7 +35,6 @@
#include "util/EglFramebuffer.h"
#include "util/EglFramebuffer.h"
#include "util/EglProgram.h"
#include "util/EglProgram.h"
#include "util/EglSurfaceTexture.h"
#include "util/EglSurfaceTexture.h"
#include "util/MetadataUtil.h"
#include "util/Util.h"
#include "util/Util.h"


namespace android {
namespace android {
@@ -94,6 +94,24 @@ class ProcessCaptureRequestTask {
  const RequestSettings mRequestSettings;
  const RequestSettings mRequestSettings;
};
};


struct UpdateTextureTask {};

struct RenderThreadTask
    : public std::variant<std::unique_ptr<ProcessCaptureRequestTask>,
                          UpdateTextureTask> {
  // Allow implicit conversion to bool.
  //
  // Returns false, if the RenderThreadTask consist of null
  // ProcessCaptureRequestTask, which signals that the thread should terminate.
  operator bool() const {
    const bool isExitSignal =
        std::holds_alternative<std::unique_ptr<ProcessCaptureRequestTask>>(
            *this) &&
        std::get<std::unique_ptr<ProcessCaptureRequestTask>>(*this) == nullptr;
    return !isExitSignal;
  }
};

// Wraps dedicated rendering thread and rendering business with corresponding
// Wraps dedicated rendering thread and rendering business with corresponding
// input surface.
// input surface.
class VirtualCameraRenderThread {
class VirtualCameraRenderThread {
@@ -120,6 +138,12 @@ class VirtualCameraRenderThread {
  // Stop rendering thread.
  // Stop rendering thread.
  void stop();
  void stop();


  // Send request to render thread to update the texture.
  // Currently queued buffers in the input surface will be consumed and the most
  // recent buffer in the input surface will be attached to the texture), all
  // other buffers will be returned to the buffer queue.
  void requestTextureUpdate() EXCLUDES(mLock);

  // Equeue capture task for processing on render thread.
  // Equeue capture task for processing on render thread.
  void enqueueTask(std::unique_ptr<ProcessCaptureRequestTask> task)
  void enqueueTask(std::unique_ptr<ProcessCaptureRequestTask> task)
      EXCLUDES(mLock);
      EXCLUDES(mLock);
@@ -131,13 +155,13 @@ class VirtualCameraRenderThread {
  sp<Surface> getInputSurface();
  sp<Surface> getInputSurface();


 private:
 private:
  std::unique_ptr<ProcessCaptureRequestTask> dequeueTask() EXCLUDES(mLock);
  RenderThreadTask dequeueTask() EXCLUDES(mLock);


  // Rendering thread entry point.
  // Rendering thread entry point.
  void threadLoop();
  void threadLoop();


  // Process single capture request task (always called on render thread).
  // Process single capture request task (always called on render thread).
  void processCaptureRequest(const ProcessCaptureRequestTask& captureRequestTask);
  void processTask(const ProcessCaptureRequestTask& captureRequestTask);


  // Flush single capture request task returning the error status immediately.
  // Flush single capture request task returning the error status immediately.
  void flushCaptureRequest(const ProcessCaptureRequestTask& captureRequestTask);
  void flushCaptureRequest(const ProcessCaptureRequestTask& captureRequestTask);
@@ -192,6 +216,7 @@ class VirtualCameraRenderThread {
  std::mutex mLock;
  std::mutex mLock;
  std::deque<std::unique_ptr<ProcessCaptureRequestTask>> mQueue GUARDED_BY(mLock);
  std::deque<std::unique_ptr<ProcessCaptureRequestTask>> mQueue GUARDED_BY(mLock);
  std::condition_variable mCondVar;
  std::condition_variable mCondVar;
  volatile bool mTextureUpdateRequested GUARDED_BY(mLock);
  volatile bool mPendingExit GUARDED_BY(mLock);
  volatile bool mPendingExit GUARDED_BY(mLock);


  // Acquisition timestamp of last frame.
  // Acquisition timestamp of last frame.
+5 −0
Original line number Original line Diff line number Diff line
@@ -64,6 +64,11 @@ sp<GraphicBuffer> EglSurfaceTexture::getCurrentBuffer() {
  return mGlConsumer->getCurrentBuffer();
  return mGlConsumer->getCurrentBuffer();
}
}


void EglSurfaceTexture::setFrameAvailableListener(
    const wp<ConsumerBase::FrameAvailableListener>& listener) {
  mGlConsumer->setFrameAvailableListener(listener);
}

bool EglSurfaceTexture::waitForNextFrame(const std::chrono::nanoseconds timeout) {
bool EglSurfaceTexture::waitForNextFrame(const std::chrono::nanoseconds timeout) {
  return mSurface->waitForNextFrame(mGlConsumer->getFrameNumber(),
  return mSurface->waitForNextFrame(mGlConsumer->getFrameNumber(),
                                    static_cast<nsecs_t>(timeout.count()));
                                    static_cast<nsecs_t>(timeout.count()));
+4 −0
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@
#include <cstdint>
#include <cstdint>


#include "GLES/gl.h"
#include "GLES/gl.h"
#include "gui/ConsumerBase.h"
#include "gui/Surface.h"
#include "gui/Surface.h"
#include "utils/RefBase.h"
#include "utils/RefBase.h"


@@ -58,6 +59,9 @@ class EglSurfaceTexture {
  // Returns false on timeout, true if new frame was received before timeout.
  // Returns false on timeout, true if new frame was received before timeout.
  bool waitForNextFrame(std::chrono::nanoseconds timeout);
  bool waitForNextFrame(std::chrono::nanoseconds timeout);


  void setFrameAvailableListener(
      const wp<ConsumerBase::FrameAvailableListener>& listener);

  // Update the texture with the most recent submitted buffer.
  // Update the texture with the most recent submitted buffer.
  // Most be called on thread with EGL context.
  // Most be called on thread with EGL context.
  //
  //