You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
244 lines
9.3 KiB
244 lines
9.3 KiB
// Copyright 2020 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
//#define LOG_NDEBUG 0
|
|
#define LOG_TAG "VideoFramePool"
|
|
|
|
#include <v4l2_codec2/components/VideoFramePool.h>
|
|
|
|
#include <stdint.h>
|
|
#include <memory>
|
|
|
|
#include <android/hardware/graphics/common/1.0/types.h>
|
|
#include <base/bind.h>
|
|
#include <base/memory/ptr_util.h>
|
|
#include <base/time/time.h>
|
|
#include <log/log.h>
|
|
|
|
#include <v4l2_codec2/common/VideoTypes.h>
|
|
#include <v4l2_codec2/plugin_store/C2VdaBqBlockPool.h>
|
|
#include <v4l2_codec2/plugin_store/C2VdaPooledBlockPool.h>
|
|
#include <v4l2_codec2/plugin_store/V4L2AllocatorId.h>
|
|
|
|
using android::hardware::graphics::common::V1_0::BufferUsage;
|
|
|
|
namespace android {
|
|
|
|
// static
|
|
std::optional<uint32_t> VideoFramePool::getBufferIdFromGraphicBlock(C2BlockPool& blockPool,
|
|
const C2Block2D& block) {
|
|
ALOGV("%s() blockPool.getAllocatorId() = %u", __func__, blockPool.getAllocatorId());
|
|
|
|
if (blockPool.getAllocatorId() == android::V4L2AllocatorId::V4L2_BUFFERPOOL) {
|
|
return C2VdaPooledBlockPool::getBufferIdFromGraphicBlock(block);
|
|
} else if (blockPool.getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
|
|
C2VdaBqBlockPool* bqPool = static_cast<C2VdaBqBlockPool*>(&blockPool);
|
|
return bqPool->getBufferIdFromGraphicBlock(block);
|
|
}
|
|
|
|
ALOGE("%s(): unknown allocator ID: %u", __func__, blockPool.getAllocatorId());
|
|
return std::nullopt;
|
|
}
|
|
|
|
// static
|
|
c2_status_t VideoFramePool::requestNewBufferSet(C2BlockPool& blockPool, int32_t bufferCount,
|
|
const ui::Size& size, uint32_t format,
|
|
C2MemoryUsage usage) {
|
|
ALOGV("%s() blockPool.getAllocatorId() = %u", __func__, blockPool.getAllocatorId());
|
|
|
|
if (blockPool.getAllocatorId() == android::V4L2AllocatorId::V4L2_BUFFERPOOL) {
|
|
C2VdaPooledBlockPool* bpPool = static_cast<C2VdaPooledBlockPool*>(&blockPool);
|
|
return bpPool->requestNewBufferSet(bufferCount);
|
|
} else if (blockPool.getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
|
|
C2VdaBqBlockPool* bqPool = static_cast<C2VdaBqBlockPool*>(&blockPool);
|
|
return bqPool->requestNewBufferSet(bufferCount, size.width, size.height, format, usage);
|
|
}
|
|
|
|
ALOGE("%s(): unknown allocator ID: %u", __func__, blockPool.getAllocatorId());
|
|
return C2_BAD_VALUE;
|
|
}
|
|
|
|
// static
|
|
bool VideoFramePool::setNotifyBlockAvailableCb(C2BlockPool& blockPool, ::base::OnceClosure cb) {
|
|
ALOGV("%s() blockPool.getAllocatorId() = %u", __func__, blockPool.getAllocatorId());
|
|
|
|
if (blockPool.getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
|
|
C2VdaBqBlockPool* bqPool = static_cast<C2VdaBqBlockPool*>(&blockPool);
|
|
return bqPool->setNotifyBlockAvailableCb(std::move(cb));
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// static
|
|
std::unique_ptr<VideoFramePool> VideoFramePool::Create(
|
|
std::shared_ptr<C2BlockPool> blockPool, const size_t numBuffers, const ui::Size& size,
|
|
HalPixelFormat pixelFormat, bool isSecure,
|
|
scoped_refptr<::base::SequencedTaskRunner> taskRunner) {
|
|
ALOG_ASSERT(blockPool != nullptr);
|
|
|
|
uint64_t usage = static_cast<uint64_t>(BufferUsage::VIDEO_DECODER);
|
|
if (isSecure) {
|
|
usage |= C2MemoryUsage::READ_PROTECTED;
|
|
} else if (blockPool->getAllocatorId() == android::V4L2AllocatorId::V4L2_BUFFERPOOL) {
|
|
// CPU access to buffers is only required in byte buffer mode.
|
|
usage |= C2MemoryUsage::CPU_READ;
|
|
}
|
|
const C2MemoryUsage memoryUsage(usage);
|
|
|
|
if (requestNewBufferSet(*blockPool, numBuffers, size, static_cast<uint32_t>(pixelFormat),
|
|
memoryUsage) != C2_OK) {
|
|
return nullptr;
|
|
}
|
|
|
|
std::unique_ptr<VideoFramePool> pool = ::base::WrapUnique(new VideoFramePool(
|
|
std::move(blockPool), size, pixelFormat, memoryUsage, std::move(taskRunner)));
|
|
if (!pool->initialize()) return nullptr;
|
|
return pool;
|
|
}
|
|
|
|
VideoFramePool::VideoFramePool(std::shared_ptr<C2BlockPool> blockPool, const ui::Size& size,
|
|
HalPixelFormat pixelFormat, C2MemoryUsage memoryUsage,
|
|
scoped_refptr<::base::SequencedTaskRunner> taskRunner)
|
|
: mBlockPool(std::move(blockPool)),
|
|
mSize(size),
|
|
mPixelFormat(pixelFormat),
|
|
mMemoryUsage(memoryUsage),
|
|
mClientTaskRunner(std::move(taskRunner)) {
|
|
ALOGV("%s(size=%dx%d)", __func__, size.width, size.height);
|
|
ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
|
|
DCHECK(mBlockPool);
|
|
DCHECK(mClientTaskRunner);
|
|
}
|
|
|
|
bool VideoFramePool::initialize() {
|
|
if (!mFetchThread.Start()) {
|
|
ALOGE("Fetch thread failed to start.");
|
|
return false;
|
|
}
|
|
mFetchTaskRunner = mFetchThread.task_runner();
|
|
|
|
mClientWeakThis = mClientWeakThisFactory.GetWeakPtr();
|
|
mFetchWeakThis = mFetchWeakThisFactory.GetWeakPtr();
|
|
|
|
return true;
|
|
}
|
|
|
|
VideoFramePool::~VideoFramePool() {
|
|
ALOGV("%s()", __func__);
|
|
ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
|
|
|
|
mClientWeakThisFactory.InvalidateWeakPtrs();
|
|
|
|
if (mFetchThread.IsRunning()) {
|
|
mFetchTaskRunner->PostTask(FROM_HERE,
|
|
::base::BindOnce(&VideoFramePool::destroyTask, mFetchWeakThis));
|
|
mFetchThread.Stop();
|
|
}
|
|
}
|
|
|
|
void VideoFramePool::destroyTask() {
|
|
ALOGV("%s()", __func__);
|
|
ALOG_ASSERT(mFetchTaskRunner->RunsTasksInCurrentSequence());
|
|
|
|
mFetchWeakThisFactory.InvalidateWeakPtrs();
|
|
}
|
|
|
|
bool VideoFramePool::getVideoFrame(GetVideoFrameCB cb) {
|
|
ALOGV("%s()", __func__);
|
|
ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
|
|
|
|
if (mOutputCb) {
|
|
return false;
|
|
}
|
|
|
|
mOutputCb = std::move(cb);
|
|
mFetchTaskRunner->PostTask(
|
|
FROM_HERE, ::base::BindOnce(&VideoFramePool::getVideoFrameTask, mFetchWeakThis));
|
|
return true;
|
|
}
|
|
|
|
// static
|
|
void VideoFramePool::getVideoFrameTaskThunk(
|
|
scoped_refptr<::base::SequencedTaskRunner> taskRunner,
|
|
std::optional<::base::WeakPtr<VideoFramePool>> weakPool) {
|
|
ALOGV("%s()", __func__);
|
|
ALOG_ASSERT(weakPool);
|
|
|
|
taskRunner->PostTask(FROM_HERE,
|
|
::base::BindOnce(&VideoFramePool::getVideoFrameTask, *weakPool));
|
|
}
|
|
|
|
void VideoFramePool::getVideoFrameTask() {
|
|
ALOGV("%s()", __func__);
|
|
ALOG_ASSERT(mFetchTaskRunner->RunsTasksInCurrentSequence());
|
|
|
|
// Variables used to exponential backoff retry when buffer fetching times out.
|
|
constexpr size_t kFetchRetryDelayInit = 64; // Initial delay: 64us
|
|
constexpr size_t kFetchRetryDelayMax = 16384; // Max delay: 16ms (1 frame at 60fps)
|
|
static size_t sNumRetries = 0;
|
|
static size_t sDelay = kFetchRetryDelayInit;
|
|
|
|
std::shared_ptr<C2GraphicBlock> block;
|
|
c2_status_t err = mBlockPool->fetchGraphicBlock(
|
|
mSize.width, mSize.height, static_cast<uint32_t>(mPixelFormat), mMemoryUsage, &block);
|
|
if (err == C2_TIMED_OUT || err == C2_BLOCKING) {
|
|
if (setNotifyBlockAvailableCb(*mBlockPool,
|
|
::base::BindOnce(&VideoFramePool::getVideoFrameTaskThunk,
|
|
mFetchTaskRunner, mFetchWeakThis))) {
|
|
ALOGV("%s(): fetchGraphicBlock() timeout, waiting for block available.", __func__);
|
|
} else {
|
|
ALOGV("%s(): fetchGraphicBlock() timeout, waiting %zuus (%zu retry)", __func__, sDelay,
|
|
sNumRetries + 1);
|
|
mFetchTaskRunner->PostDelayedTask(
|
|
FROM_HERE, ::base::BindOnce(&VideoFramePool::getVideoFrameTask, mFetchWeakThis),
|
|
::base::TimeDelta::FromMicroseconds(sDelay));
|
|
|
|
sDelay = std::min(sDelay * 2, kFetchRetryDelayMax); // Exponential backoff
|
|
sNumRetries++;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// Reset to the default value.
|
|
sNumRetries = 0;
|
|
sDelay = kFetchRetryDelayInit;
|
|
|
|
std::optional<FrameWithBlockId> frameWithBlockId;
|
|
if (err == C2_OK) {
|
|
ALOG_ASSERT(block != nullptr);
|
|
std::optional<uint32_t> bufferId = getBufferIdFromGraphicBlock(*mBlockPool, *block);
|
|
std::unique_ptr<VideoFrame> frame = VideoFrame::Create(std::move(block));
|
|
// Only pass the frame + id pair if both have successfully been obtained.
|
|
// Otherwise exit the loop so a nullopt is passed to the client.
|
|
if (bufferId && frame) {
|
|
frameWithBlockId = std::make_pair(std::move(frame), *bufferId);
|
|
} else {
|
|
ALOGE("%s(): Failed to generate VideoFrame or get the buffer id.", __func__);
|
|
}
|
|
} else {
|
|
ALOGE("%s(): Failed to fetch block, err=%d", __func__, err);
|
|
}
|
|
|
|
mClientTaskRunner->PostTask(
|
|
FROM_HERE, ::base::BindOnce(&VideoFramePool::onVideoFrameReady, mClientWeakThis,
|
|
std::move(frameWithBlockId)));
|
|
}
|
|
|
|
void VideoFramePool::onVideoFrameReady(std::optional<FrameWithBlockId> frameWithBlockId) {
|
|
ALOGV("%s()", __func__);
|
|
ALOG_ASSERT(mClientTaskRunner->RunsTasksInCurrentSequence());
|
|
|
|
if (!frameWithBlockId) {
|
|
ALOGE("Failed to get GraphicBlock, abandoning all pending requests.");
|
|
mClientWeakThisFactory.InvalidateWeakPtrs();
|
|
mClientWeakThis = mClientWeakThisFactory.GetWeakPtr();
|
|
}
|
|
|
|
ALOG_ASSERT(mOutputCb);
|
|
std::move(mOutputCb).Run(std::move(frameWithBlockId));
|
|
}
|
|
|
|
} // namespace android
|