aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2018-08-13 13:01:07 -0700
committerXin Li <delphij@google.com>2018-08-13 13:01:07 -0700
commit0f533897fc8962dc33527aeb8bd527c53e395d33 (patch)
tree68f51b308590ec57e6ce7d323ab0bd8efdb2403b
parentbaf051157883a64feda3d83043c555d0db592393 (diff)
parentc8933181f5c09efb5527a1da7cda2fa7b4337f17 (diff)
downloadv4l2_codec2-0f533897fc8962dc33527aeb8bd527c53e395d33.tar.gz
Merge stage-dr1-aosp-master into stage-aosp-master
Bug: 112535855 Change-Id: I63787a77e44d5f49e31aa9c8b67d25329b821574
-rw-r--r--Android.bp32
-rw-r--r--Android.mk4
-rw-r--r--C2ArcVideoAcceleratorFactory.cpp87
-rw-r--r--C2VDAAdaptorProxy.cpp2
-rw-r--r--C2VDAComponent.cpp369
-rw-r--r--include/C2ArcVideoAcceleratorFactory.h37
-rw-r--r--include/C2VDAComponent.h63
-rw-r--r--tests/Android.mk1
-rw-r--r--tests/C2VDACompIntf_test.cpp3
9 files changed, 307 insertions, 291 deletions
diff --git a/Android.bp b/Android.bp
deleted file mode 100644
index db4d39f..0000000
--- a/Android.bp
+++ /dev/null
@@ -1,32 +0,0 @@
-cc_library_shared {
- name: "libv4l2_codec2_arcva_factory",
- vendor_available: true,
- product_variables: {
- arc: {
- srcs: ["C2ArcVideoAcceleratorFactory.cpp"],
-
- shared_libs: [
- "libarcbridge",
- "libarcbridgeservice",
- "libarcvideobridge",
- "libbinder",
- "libchrome",
- "liblog",
- "libmojo",
- "libutils",
- ],
-
- // -Wno-unused-parameter is needed for libchrome/base codes
- cflags: [
- "-Wall",
- "-Werror",
- "-Wno-unused-parameter",
- "-std=c++14",
- ],
- },
- },
- clang: true,
- export_include_dirs: [
- "include",
- ],
-}
diff --git a/Android.mk b/Android.mk
index bff4409..b2304fb 100644
--- a/Android.mk
+++ b/Android.mk
@@ -29,6 +29,8 @@ LOCAL_SHARED_LIBRARIES := libbinder \
liblog \
libmedia \
libstagefright \
+ libstagefright_bufferqueue_helper \
+ libstagefright_ccodec_ext \
libstagefright_codec2 \
libstagefright_codec2_vndk \
libstagefright_simple_c2component \
@@ -55,7 +57,7 @@ LOCAL_SRC_FILES := $(filter-out C2VDAAdaptor.cpp, $(LOCAL_SRC_FILES))
LOCAL_SHARED_LIBRARIES += libarcbridge \
libarcbridgeservice \
libmojo \
- libv4l2_codec2_arcva_factory \
+ libcodec2_arcva_factory \
endif # ifneq (,$(findstring cheets_,$(TARGET_PRODUCT)))
diff --git a/C2ArcVideoAcceleratorFactory.cpp b/C2ArcVideoAcceleratorFactory.cpp
deleted file mode 100644
index 07997d1..0000000
--- a/C2ArcVideoAcceleratorFactory.cpp
+++ /dev/null
@@ -1,87 +0,0 @@
-// Copyright 2018 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 "C2ArcVideoAcceleratorFactory"
-
-#include <C2ArcVideoAcceleratorFactory.h>
-
-#include <base/bind.h>
-#include <binder/IServiceManager.h>
-#include <mojo/edk/embedder/embedder.h>
-#include <mojo/public/cpp/bindings/interface_request.h>
-#include <mojo/public/cpp/system/handle.h>
-#include <utils/Log.h>
-
-namespace android {
-
-ANDROID_SINGLETON_STATIC_INSTANCE(C2ArcVideoAcceleratorFactory)
-
-bool C2ArcVideoAcceleratorFactory::createVideoDecodeAccelerator(
- ::arc::mojom::VideoDecodeAcceleratorRequest request) {
- if (!mRemoteFactory) {
- ALOGE("Factory is not ready");
- return false;
- }
- mRemoteFactory->CreateDecodeAccelerator(std::move(request));
- return true;
-}
-
-bool C2ArcVideoAcceleratorFactory::createVideoEncodeAccelerator(
- ::arc::mojom::VideoEncodeAcceleratorRequest request) {
- if (!mRemoteFactory) {
- ALOGE("Factory is not ready");
- return false;
- }
- mRemoteFactory->CreateEncodeAccelerator(std::move(request));
- return true;
-}
-
-bool C2ArcVideoAcceleratorFactory::createVideoProtectedBufferAllocator(
- ::arc::mojom::VideoProtectedBufferAllocatorRequest request) {
- if (!mRemoteFactory) {
- ALOGE("Factory is not ready");
- return false;
- }
- mRemoteFactory->CreateProtectedBufferAllocator(std::move(request));
- return true;
-}
-
-int32_t C2ArcVideoAcceleratorFactory::hostVersion() const {
- return mHostVersion;
-}
-
-C2ArcVideoAcceleratorFactory::C2ArcVideoAcceleratorFactory() : mHostVersion(0) {
- sp<IBinder> binder =
- defaultServiceManager()->getService(String16("android.os.IArcVideoBridge"));
- if (binder == nullptr) {
- ALOGE("Failed to find IArcVideoBridge service");
- return;
- }
- mArcVideoBridge = interface_cast<IArcVideoBridge>(binder);
- mHostVersion = mArcVideoBridge->hostVersion();
- if (mHostVersion < 4) {
- ALOGW("HostVersion(%d) is outdated", mHostVersion);
- return;
- }
-
- ALOGV("HostVersion: %d", mHostVersion);
-
- ::arc::MojoBootstrapResult bootstrapResult =
- mArcVideoBridge->bootstrapVideoAcceleratorFactory();
- if (!bootstrapResult.is_valid()) {
- ALOGE("bootstrapVideoAcceleratorFactory returns invalid result");
- return;
- }
- mojo::edk::ScopedPlatformHandle handle(
- mojo::edk::PlatformHandle(bootstrapResult.releaseFd().release()));
- ALOGV("SetParentPipeHandle(fd=%d)", handle.get().handle);
- mojo::edk::SetParentPipeHandle(std::move(handle));
- mojo::ScopedMessagePipeHandle server_pipe =
- mojo::edk::CreateChildMessagePipe(bootstrapResult.releaseToken());
- mRemoteFactory.Bind(mojo::InterfacePtrInfo<::arc::mojom::VideoAcceleratorFactory>(
- std::move(server_pipe), 7u));
-}
-
-} // namespace android
diff --git a/C2VDAAdaptorProxy.cpp b/C2VDAAdaptorProxy.cpp
index 2c44e6b..4c493d1 100644
--- a/C2VDAAdaptorProxy.cpp
+++ b/C2VDAAdaptorProxy.cpp
@@ -64,7 +64,7 @@ bool C2VDAAdaptorProxy::establishChannel() {
}
void C2VDAAdaptorProxy::establishChannelOnMojoThread(std::shared_ptr<::arc::Future<bool>> future) {
- C2ArcVideoAcceleratorFactory& factory = ::android::C2ArcVideoAcceleratorFactory::getInstance();
+ auto& factory = ::android::GetC2ArcVideoAcceleratorFactory();
if (!factory.createVideoDecodeAccelerator(mojo::MakeRequest(&mVDAPtr))) {
future->set(false);
diff --git a/C2VDAComponent.cpp b/C2VDAComponent.cpp
index 2b3abde..5eec1ef 100644
--- a/C2VDAComponent.cpp
+++ b/C2VDAComponent.cpp
@@ -12,11 +12,14 @@
#endif
#define __C2_GENERATE_GLOBAL_VARS__
+#include <C2VDAAllocatorStore.h>
+#include <C2VdaBqBlockPool.h>
#include <C2VDAComponent.h>
#include <C2VDASupport.h> // to getParamReflector from vda store
#include <videodev2.h>
+#include <C2AllocatorGralloc.h>
#include <C2ComponentFactory.h>
#include <C2PlatformSupport.h>
@@ -52,6 +55,16 @@ const C2String kH264DecoderName = "c2.vda.avc.decoder";
const C2String kVP8DecoderName = "c2.vda.vp8.decoder";
const C2String kVP9DecoderName = "c2.vda.vp9.decoder";
+const uint32_t kDpbOutputBufferExtraCount = 3; // Use the same number as ACodec.
+const int kDequeueRetryDelayUs = 10000; // Wait time of dequeue buffer retry in microseconds.
+
+// Hack(b/79239042): Max size of mMockBufferQueueInClient.
+// This value is empirically picked from previous CTS try-run. If this value is too big, it may
+// cause VDA deadlock when it requires more buffers to decode and dequeue a new one. On the other
+// hand, too small value may produce wrong display picture because recycling goes faster than
+// rendering.
+const size_t kMockMaxBuffersInClient = 5;
+
} // namespace
C2VDAComponent::IntfImpl::IntfImpl(C2String name, const std::shared_ptr<C2ReflectorHelper>& helper)
@@ -134,7 +147,7 @@ C2VDAComponent::IntfImpl::IntfImpl(C2String name, const std::shared_ptr<C2Reflec
.build());
C2Allocator::id_t inputAllocators[] = {C2PlatformAllocatorStore::ION};
- C2Allocator::id_t outputAllocators[] = {C2PlatformAllocatorStore::GRALLOC};
+ C2Allocator::id_t outputAllocators[] = {C2VDAAllocatorStore::V4L2_BUFFERQUEUE};
addParameter(
DefineParam(mInputAllocatorIds, C2_PARAMKEY_INPUT_ALLOCATORS)
@@ -170,28 +183,6 @@ C2VDAComponent::IntfImpl::IntfImpl(C2String name, const std::shared_ptr<C2Reflec
CHECK_NE(mComponentState, ComponentState::UNINITIALIZED); \
} while (0)
-class C2VDAGraphicBuffer : public C2Buffer {
-public:
- C2VDAGraphicBuffer(const std::shared_ptr<C2GraphicBlock>& block, const media::Rect& visibleRect,
- const base::Closure& releaseCB);
- ~C2VDAGraphicBuffer() override;
-
-private:
- base::Closure mReleaseCB;
-};
-
-C2VDAGraphicBuffer::C2VDAGraphicBuffer(const std::shared_ptr<C2GraphicBlock>& block,
- const media::Rect& visibleRect,
- const base::Closure& releaseCB)
- : C2Buffer({block->share(C2Rect(visibleRect.width(), visibleRect.height()), C2Fence())}),
- mReleaseCB(releaseCB) {}
-
-C2VDAGraphicBuffer::~C2VDAGraphicBuffer() {
- if (!mReleaseCB.is_null()) {
- mReleaseCB.Run();
- }
-}
-
C2VDAComponent::VideoFormat::VideoFormat(HalPixelFormat pixelFormat, uint32_t minNumBuffers,
media::Size codedSize, media::Rect visibleRect)
: mPixelFormat(pixelFormat),
@@ -199,15 +190,27 @@ C2VDAComponent::VideoFormat::VideoFormat(HalPixelFormat pixelFormat, uint32_t mi
mCodedSize(codedSize),
mVisibleRect(visibleRect) {}
+static uint32_t getSlotFromGraphicBlockHandle(const C2Handle* const handle) {
+ uint32_t width, height, format, stride, igbp_slot, generation;
+ uint64_t usage, igbp_id;
+ _UnwrapNativeCodec2GrallocMetadata(
+ handle, &width, &height, &format, &usage, &stride, &generation, &igbp_id, &igbp_slot);
+ ALOGV("Unwrap Metadata: igbp[%" PRIu64 ", %u] (%u*%u, fmt %#x, usage %" PRIx64 ", stride %u)",
+ igbp_id, igbp_slot, width, height, format, usage, stride);
+ return igbp_slot;
+}
+
C2VDAComponent::C2VDAComponent(C2String name, c2_node_id_t id,
const std::shared_ptr<C2ReflectorHelper>& helper)
: mIntfImpl(std::make_shared<IntfImpl>(name, helper)),
mIntf(std::make_shared<SimpleInterface<IntfImpl>>(name.c_str(), id, mIntfImpl)),
mThread("C2VDAComponentThread"),
+ mDequeueThread("C2VDAComponentDequeueThread"),
mVDAInitResult(VideoDecodeAcceleratorAdaptor::Result::ILLEGAL_STATE),
mComponentState(ComponentState::UNINITIALIZED),
- mDrainWithEOS(false),
+ mPendingOutputEOS(false),
mLastOutputTimestamp(-1),
+ mSurfaceMode(true),
mCodecProfile(media::VIDEO_CODEC_PROFILE_UNKNOWN),
mState(State::UNLOADED),
mWeakThisFactory(this) {
@@ -229,7 +232,7 @@ C2VDAComponent::~C2VDAComponent() {
if (mThread.IsRunning()) {
mTaskRunner->PostTask(FROM_HERE,
- base::Bind(&C2VDAComponent::onDestroy, base::Unretained(this)));
+ ::base::Bind(&C2VDAComponent::onDestroy, ::base::Unretained(this)));
mThread.Stop();
}
}
@@ -241,9 +244,10 @@ void C2VDAComponent::onDestroy() {
mVDAAdaptor->destroy();
mVDAAdaptor.reset(nullptr);
}
+ stopDequeueThread();
}
-void C2VDAComponent::onStart(media::VideoCodecProfile profile, base::WaitableEvent* done) {
+void C2VDAComponent::onStart(media::VideoCodecProfile profile, ::base::WaitableEvent* done) {
DCHECK(mTaskRunner->BelongsToCurrentThread());
ALOGV("onStart");
CHECK_EQ(mComponentState, ComponentState::UNINITIALIZED);
@@ -269,8 +273,6 @@ void C2VDAComponent::onQueueWork(std::unique_ptr<C2Work> work) {
ALOGV("onQueueWork: flags=0x%x, index=%llu, timestamp=%llu", work->input.flags,
work->input.ordinal.frameIndex.peekull(), work->input.ordinal.timestamp.peekull());
EXPECT_RUNNING_OR_RETURN_ON_ERROR();
- // It is illegal for client to put new works while component is still flushing.
- CHECK_NE(mComponentState, ComponentState::FLUSHING);
uint32_t drainMode = NO_DRAIN;
if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
@@ -280,7 +282,7 @@ void C2VDAComponent::onQueueWork(std::unique_ptr<C2Work> work) {
// TODO(johnylin): set a maximum size of mQueue and check if mQueue is already full.
mTaskRunner->PostTask(FROM_HERE,
- base::Bind(&C2VDAComponent::onDequeueWork, base::Unretained(this)));
+ ::base::Bind(&C2VDAComponent::onDequeueWork, ::base::Unretained(this)));
}
void C2VDAComponent::onDequeueWork() {
@@ -290,8 +292,9 @@ void C2VDAComponent::onDequeueWork() {
if (mQueue.empty()) {
return;
}
- if (mComponentState == ComponentState::DRAINING) {
- ALOGV("Temporarily stop dequeueing works since component is draining.");
+ if (mComponentState == ComponentState::DRAINING ||
+ mComponentState == ComponentState::FLUSHING) {
+ ALOGV("Temporarily stop dequeueing works since component is draining/flushing.");
return;
}
if (mComponentState != ComponentState::STARTED) {
@@ -304,10 +307,15 @@ void C2VDAComponent::onDequeueWork() {
auto drainMode = mQueue.front().mDrainMode;
mQueue.pop();
- CHECK_EQ(work->input.buffers.size(), 1u);
- C2ConstLinearBlock linearBlock = work->input.buffers.front()->data().linearBlocks().front();
- // linearBlock.size() == 0 means this is a dummy work. No decode needed.
- if (linearBlock.size() > 0) {
+ CHECK_LE(work->input.buffers.size(), 1u);
+ if (work->input.buffers.empty()) {
+ // Client may queue an EOS work with no input buffer, otherwise every work must have one
+ // input buffer.
+ CHECK(drainMode != NO_DRAIN);
+ } else {
+ // If input.buffers is not empty, the buffer should have meaningful content inside.
+ C2ConstLinearBlock linearBlock = work->input.buffers.front()->data().linearBlocks().front();
+ CHECK_GT(linearBlock.size(), 0u);
// Send input buffer to VDA for decode.
// Use frameIndex as bitstreamId.
int32_t bitstreamId = frameIndexToBitstreamId(work->input.ordinal.frameIndex);
@@ -322,15 +330,15 @@ void C2VDAComponent::onDequeueWork() {
if (drainMode != NO_DRAIN) {
mVDAAdaptor->flush();
mComponentState = ComponentState::DRAINING;
- mDrainWithEOS = drainMode == DRAIN_COMPONENT_WITH_EOS;
+ mPendingOutputEOS = drainMode == DRAIN_COMPONENT_WITH_EOS;
}
// Put work to mPendingWorks.
mPendingWorks.emplace_back(std::move(work));
if (!mQueue.empty()) {
- mTaskRunner->PostTask(FROM_HERE,
- base::Bind(&C2VDAComponent::onDequeueWork, base::Unretained(this)));
+ mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onDequeueWork,
+ ::base::Unretained(this)));
}
}
@@ -351,16 +359,9 @@ void C2VDAComponent::onInputBufferDone(int32_t bitstreamId) {
reportFinishedWorkIfAny();
}
-// This is used as callback while output buffer is released by client.
-// TODO(johnylin): consider to use C2Buffer::registerOnDestroyNotify instead
-void C2VDAComponent::returnOutputBuffer(int32_t pictureBufferId) {
- mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onOutputBufferReturned,
- base::Unretained(this), pictureBufferId));
-}
-
-void C2VDAComponent::onOutputBufferReturned(int32_t pictureBufferId) {
+void C2VDAComponent::onOutputBufferReturned(uint32_t slotId) {
DCHECK(mTaskRunner->BelongsToCurrentThread());
- ALOGV("onOutputBufferReturned: picture id=%d", pictureBufferId);
+ ALOGV("onOutputBufferReturned: slot id=%u", slotId);
if (mComponentState == ComponentState::UNINITIALIZED) {
// Output buffer is returned from client after component is stopped. Just let the buffer be
// released.
@@ -369,7 +370,7 @@ void C2VDAComponent::onOutputBufferReturned(int32_t pictureBufferId) {
// TODO(johnylin): when buffer is returned, we should confirm that output format is not changed
// yet. If changed, just let the buffer be released.
- GraphicBlockInfo* info = getGraphicBlockById(pictureBufferId);
+ GraphicBlockInfo* info = getGraphicBlockBySlot(slotId);
if (!info) {
reportError(C2_CORRUPTED);
return;
@@ -402,15 +403,28 @@ void C2VDAComponent::onOutputBufferDone(int32_t pictureBufferId, int32_t bitstre
CHECK_EQ(info->mState, GraphicBlockInfo::State::OWNED_BY_ACCELERATOR);
// Output buffer will be passed to client soon along with mListener->onWorkDone_nb().
info->mState = GraphicBlockInfo::State::OWNED_BY_CLIENT;
+ if (mSurfaceMode) {
+ mBuffersInClient++;
+ } else { // byte-buffer mode
+ // Hack(b/79239042)
+ mMockBufferQueueInClient.push_back(info->mSlotId);
+ if (mMockBufferQueueInClient.size() > kMockMaxBuffersInClient) {
+ mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onOutputBufferReturned,
+ ::base::Unretained(this),
+ mMockBufferQueueInClient.front()));
+ mMockBufferQueueInClient.pop_front();
+ }
+ }
// Attach output buffer to the work corresponded to bitstreamId.
- work->worklets.front()->output.buffers.emplace_back(std::make_shared<C2VDAGraphicBuffer>(
- info->mGraphicBlock, mOutputFormat.mVisibleRect,
- base::Bind(&C2VDAComponent::returnOutputBuffer, mWeakThisFactory.GetWeakPtr(),
- pictureBufferId)));
+ auto block = info->mGraphicBlock;
+ work->worklets.front()->output.buffers.emplace_back(C2Buffer::CreateGraphicBuffer(
+ block->share(C2Rect(mOutputFormat.mVisibleRect.width(),
+ mOutputFormat.mVisibleRect.height()),
+ C2Fence())));
// TODO: this does not work for timestamps as they can wrap around
- int64_t currentTimestamp = base::checked_cast<int64_t>(work->input.ordinal.timestamp.peek());
+ int64_t currentTimestamp = ::base::checked_cast<int64_t>(work->input.ordinal.timestamp.peek());
CHECK_GE(currentTimestamp, mLastOutputTimestamp);
mLastOutputTimestamp = currentTimestamp;
@@ -434,7 +448,7 @@ void C2VDAComponent::onDrain(uint32_t drainMode) {
if (mComponentState == ComponentState::STARTED) {
mVDAAdaptor->flush();
mComponentState = ComponentState::DRAINING;
- mDrainWithEOS = drainMode == DRAIN_COMPONENT_WITH_EOS;
+ mPendingOutputEOS = drainMode == DRAIN_COMPONENT_WITH_EOS;
} else {
ALOGV("Neglect drain. Component in state: %d", mComponentState);
}
@@ -452,13 +466,15 @@ void C2VDAComponent::onDrainDone() {
} else if (mComponentState == ComponentState::STOPPING) {
// The client signals stop right before VDA notifies drain done. Let stop process goes.
return;
- } else {
+ } else if (mComponentState != ComponentState::FLUSHING) {
+ // It is reasonable to get onDrainDone in FLUSHING, which means flush is already signaled
+ // and component should still expect onFlushDone callback from VDA.
ALOGE("Unexpected state while onDrainDone(). State=%d", mComponentState);
reportError(C2_BAD_STATE);
return;
}
- if (mDrainWithEOS) {
+ if (mPendingOutputEOS) {
// Return EOS work.
reportEOSWork();
}
@@ -470,27 +486,28 @@ void C2VDAComponent::onDrainDone() {
// Work dequeueing was stopped while component draining. Restart it.
mTaskRunner->PostTask(FROM_HERE,
- base::Bind(&C2VDAComponent::onDequeueWork, base::Unretained(this)));
+ ::base::Bind(&C2VDAComponent::onDequeueWork, ::base::Unretained(this)));
}
void C2VDAComponent::onFlush() {
DCHECK(mTaskRunner->BelongsToCurrentThread());
ALOGV("onFlush");
- if (mComponentState == ComponentState::FLUSHING) {
- return; // Ignore other flush request when component is flushing.
+ if (mComponentState == ComponentState::FLUSHING ||
+ mComponentState == ComponentState::STOPPING) {
+ return; // Ignore other flush request when component is flushing or stopping.
}
- EXPECT_STATE_OR_RETURN_ON_ERROR(STARTED);
+ EXPECT_RUNNING_OR_RETURN_ON_ERROR();
mVDAAdaptor->reset();
- // Pop all works in mQueue and put into mPendingWorks.
+ // Pop all works in mQueue and put into mAbandonedWorks.
while (!mQueue.empty()) {
- mPendingWorks.emplace_back(std::move(mQueue.front().mWork));
+ mAbandonedWorks.emplace_back(std::move(mQueue.front().mWork));
mQueue.pop();
}
mComponentState = ComponentState::FLUSHING;
}
-void C2VDAComponent::onStop(base::WaitableEvent* done) {
+void C2VDAComponent::onStop(::base::WaitableEvent* done) {
DCHECK(mTaskRunner->BelongsToCurrentThread());
ALOGV("onStop");
EXPECT_RUNNING_OR_RETURN_ON_ERROR();
@@ -501,9 +518,9 @@ void C2VDAComponent::onStop(base::WaitableEvent* done) {
mVDAAdaptor->reset();
}
- // Pop all works in mQueue and put into mPendingWorks.
+ // Pop all works in mQueue and put into mAbandonedWorks.
while (!mQueue.empty()) {
- mPendingWorks.emplace_back(std::move(mQueue.front().mWork));
+ mAbandonedWorks.emplace_back(std::move(mQueue.front().mWork));
mQueue.pop();
}
@@ -531,15 +548,16 @@ void C2VDAComponent::onFlushDone() {
// Reset the timestamp record.
mLastOutputTimestamp = -1;
mComponentState = ComponentState::STARTED;
+
+ // Work dequeueing was stopped while component flushing. Restart it.
+ mTaskRunner->PostTask(FROM_HERE,
+ ::base::Bind(&C2VDAComponent::onDequeueWork, ::base::Unretained(this)));
}
void C2VDAComponent::onStopDone() {
ALOGV("onStopDone");
CHECK(mStopDoneEvent);
- // Release the graphic block allocator object.
- mOutputBlockPool.reset();
-
// TODO(johnylin): At this moment, there may be C2Buffer still owned by client, do we need to
// do something for them?
reportAbandonedWorks();
@@ -552,6 +570,9 @@ void C2VDAComponent::onStopDone() {
mGraphicBlocks.clear();
+ mMockBufferQueueInClient.clear(); // Hack(b/79239042)
+ stopDequeueThread();
+
mStopDoneEvent->Signal();
mStopDoneEvent = nullptr;
mComponentState = ComponentState::UNINITIALIZED;
@@ -606,6 +627,19 @@ C2VDAComponent::GraphicBlockInfo* C2VDAComponent::getGraphicBlockById(int32_t bl
return &mGraphicBlocks[blockId];
}
+C2VDAComponent::GraphicBlockInfo* C2VDAComponent::getGraphicBlockBySlot(uint32_t slotId) {
+ auto blockIter = std::find_if(mGraphicBlocks.begin(), mGraphicBlocks.end(),
+ [slotId](const GraphicBlockInfo& gb) {
+ return gb.mSlotId == slotId;
+ });
+
+ if (blockIter == mGraphicBlocks.end()) {
+ ALOGE("getGraphicBlockBySlot failed: slot=%u", slotId);
+ return nullptr;
+ }
+ return &(*blockIter);
+}
+
void C2VDAComponent::onOutputFormatChanged(std::unique_ptr<VideoFormat> format) {
DCHECK(mTaskRunner->BelongsToCurrentThread());
ALOGV("onOutputFormatChanged");
@@ -666,30 +700,55 @@ c2_status_t C2VDAComponent::allocateBuffersFromBlockAllocator(const media::Size&
uint32_t pixelFormat) {
ALOGV("allocateBuffersFromBlockAllocator(%s, 0x%x)", size.ToString().c_str(), pixelFormat);
+ mMockBufferQueueInClient.clear(); // Hack(b/79239042)
+ stopDequeueThread();
+
size_t bufferCount = mOutputFormat.mMinNumBuffers + kDpbOutputBufferExtraCount;
// Allocate the output buffers.
mVDAAdaptor->assignPictureBuffers(bufferCount);
// Get block pool ID configured from the client.
+ std::shared_ptr<C2BlockPool> blockPool;
auto poolId = mIntfImpl->getBlockPoolId();
ALOGI("Using C2BlockPool ID = %" PRIu64 " for allocating output buffers", poolId);
- c2_status_t err;
- if (!mOutputBlockPool || mOutputBlockPool->getLocalId() != poolId) {
- err = GetCodec2BlockPool(poolId, shared_from_this(), &mOutputBlockPool);
- if (err != C2_OK) {
- ALOGE("Graphic block allocator is invalid");
- reportError(err);
- return err;
- }
+ auto err = GetCodec2BlockPool(poolId, shared_from_this(), &blockPool);
+ if (err != C2_OK) {
+ ALOGE("Graphic block allocator is invalid");
+ reportError(err);
+ return err;
}
mGraphicBlocks.clear();
+
+ if (blockPool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
+ // Set requested buffer count to C2VdaBqBlockPool.
+ std::shared_ptr<C2VdaBqBlockPool> bqPool =
+ std::static_pointer_cast<C2VdaBqBlockPool>(blockPool);
+ if (bqPool) {
+ err = bqPool->requestNewBufferSet(static_cast<int32_t>(bufferCount));
+ if (err == C2_NO_INIT) {
+ ALOGD("No surface in block pool, output is byte-buffer mode...");
+ mSurfaceMode = false;
+ } else if (err != C2_OK) {
+ ALOGE("failed to set buffer count magic to block pool: %d", err);
+ reportError(err);
+ return err;
+ }
+ } else {
+ ALOGE("static_pointer_cast C2VdaBqBlockPool failed...");
+ reportError(C2_CORRUPTED);
+ return C2_CORRUPTED;
+ }
+ } else { // CCodec falls back to use C2BasicGraphicBlockPool
+ ALOGD("CCodec falls back to use C2BasicGraphicBlockPool...");
+ mSurfaceMode = false;
+ }
+
for (size_t i = 0; i < bufferCount; ++i) {
std::shared_ptr<C2GraphicBlock> block;
C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, 0};
- err = mOutputBlockPool->fetchGraphicBlock(size.width(), size.height(), pixelFormat, usage,
- &block);
+ err = blockPool->fetchGraphicBlock(size.width(), size.height(), pixelFormat, usage, &block);
if (err != C2_OK) {
mGraphicBlocks.clear();
ALOGE("failed to allocate buffer: %d", err);
@@ -699,6 +758,11 @@ c2_status_t C2VDAComponent::allocateBuffersFromBlockAllocator(const media::Size&
appendOutputBuffer(std::move(block));
}
mOutputFormat.mMinNumBuffers = bufferCount;
+
+ if (mSurfaceMode && !startDequeueThread(size, pixelFormat, std::move(blockPool))) {
+ reportError(C2_CORRUPTED);
+ return C2_CORRUPTED;
+ }
return C2_OK;
}
@@ -752,7 +816,7 @@ void C2VDAComponent::appendOutputBuffer(std::shared_ptr<C2GraphicBlock> block) {
#endif
ALOGV("HAL pixel format: 0x%x", static_cast<uint32_t>(info.mPixelFormat));
- base::ScopedFD passedHandle(dup(info.mGraphicBlock->handle()->data[0]));
+ ::base::ScopedFD passedHandle(dup(info.mGraphicBlock->handle()->data[0]));
if (!passedHandle.is_valid()) {
ALOGE("Failed to dup(%d), errno=%d", info.mGraphicBlock->handle()->data[0], errno);
reportError(C2_CORRUPTED);
@@ -766,6 +830,12 @@ void C2VDAComponent::appendOutputBuffer(std::shared_ptr<C2GraphicBlock> block) {
info.mHandle = std::move(passedHandle);
info.mPlanes = std::move(passedPlanes);
+ if (mSurfaceMode) {
+ info.mSlotId = getSlotFromGraphicBlockHandle(info.mGraphicBlock->handle());
+ } else { // byte-buffer mode
+ info.mSlotId = static_cast<uint32_t>(info.mBlockId);
+ }
+
mGraphicBlocks.push_back(std::move(info));
}
@@ -808,8 +878,8 @@ c2_status_t C2VDAComponent::queue_nb(std::list<std::unique_ptr<C2Work>>* const i
}
while (!items->empty()) {
mTaskRunner->PostTask(FROM_HERE,
- base::Bind(&C2VDAComponent::onQueueWork, base::Unretained(this),
- base::Passed(&items->front())));
+ ::base::Bind(&C2VDAComponent::onQueueWork, ::base::Unretained(this),
+ ::base::Passed(&items->front())));
items->pop_front();
}
return C2_OK;
@@ -828,7 +898,8 @@ c2_status_t C2VDAComponent::flush_sm(flush_mode_t mode,
if (mState.load() != State::RUNNING) {
return C2_BAD_STATE;
}
- mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onFlush, base::Unretained(this)));
+ mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onFlush,
+ ::base::Unretained(this)));
// Instead of |flushedWork|, abandoned works will be returned via onWorkDone_nb() callback.
return C2_OK;
}
@@ -840,8 +911,9 @@ c2_status_t C2VDAComponent::drain_nb(drain_mode_t mode) {
if (mState.load() != State::RUNNING) {
return C2_BAD_STATE;
}
- mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onDrain, base::Unretained(this),
- static_cast<uint32_t>(mode)));
+ mTaskRunner->PostTask(FROM_HERE,
+ ::base::Bind(&C2VDAComponent::onDrain, ::base::Unretained(this),
+ static_cast<uint32_t>(mode)));
return C2_OK;
}
@@ -856,10 +928,11 @@ c2_status_t C2VDAComponent::start() {
mCodecProfile = mIntfImpl->getCodecProfile();
ALOGI("get parameter: mCodecProfile = %d", static_cast<int>(mCodecProfile));
- base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
- mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onStart, base::Unretained(this),
- mCodecProfile, &done));
+ ::base::WaitableEvent done(::base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ ::base::WaitableEvent::InitialState::NOT_SIGNALED);
+ mTaskRunner->PostTask(FROM_HERE,
+ ::base::Bind(&C2VDAComponent::onStart, ::base::Unretained(this),
+ mCodecProfile, &done));
done.Wait();
if (mVDAInitResult != VideoDecodeAcceleratorAdaptor::Result::SUCCESS) {
ALOGE("Failed to start component due to VDA error: %d", static_cast<int>(mVDAInitResult));
@@ -878,10 +951,10 @@ c2_status_t C2VDAComponent::stop() {
return C2_OK; // Component is already in stopped state.
}
- base::WaitableEvent done(base::WaitableEvent::ResetPolicy::AUTOMATIC,
- base::WaitableEvent::InitialState::NOT_SIGNALED);
+ ::base::WaitableEvent done(::base::WaitableEvent::ResetPolicy::AUTOMATIC,
+ ::base::WaitableEvent::InitialState::NOT_SIGNALED);
mTaskRunner->PostTask(FROM_HERE,
- base::Bind(&C2VDAComponent::onStop, base::Unretained(this), &done));
+ ::base::Bind(&C2VDAComponent::onStop, ::base::Unretained(this), &done));
done.Wait();
mState.store(State::LOADED);
return C2_OK;
@@ -910,8 +983,9 @@ void C2VDAComponent::providePictureBuffers(uint32_t minNumBuffers, const media::
// Set mRequestedVisibleRect to default.
mRequestedVisibleRect = media::Rect();
- mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onOutputFormatChanged,
- base::Unretained(this), base::Passed(&format)));
+ mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onOutputFormatChanged,
+ ::base::Unretained(this),
+ ::base::Passed(&format)));
}
void C2VDAComponent::dismissPictureBuffer(int32_t pictureBufferId) {
@@ -926,28 +1000,28 @@ void C2VDAComponent::pictureReady(int32_t pictureBufferId, int32_t bitstreamId,
if (mRequestedVisibleRect != cropRect) {
mRequestedVisibleRect = cropRect;
- mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onVisibleRectChanged,
- base::Unretained(this), cropRect));
+ mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onVisibleRectChanged,
+ ::base::Unretained(this), cropRect));
}
- mTaskRunner->PostTask(FROM_HERE,
- base::Bind(&C2VDAComponent::onOutputBufferDone, base::Unretained(this),
- pictureBufferId, bitstreamId));
+ mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onOutputBufferDone,
+ ::base::Unretained(this),
+ pictureBufferId, bitstreamId));
}
void C2VDAComponent::notifyEndOfBitstreamBuffer(int32_t bitstreamId) {
- mTaskRunner->PostTask(FROM_HERE, base::Bind(&C2VDAComponent::onInputBufferDone,
- base::Unretained(this), bitstreamId));
+ mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onInputBufferDone,
+ ::base::Unretained(this), bitstreamId));
}
void C2VDAComponent::notifyFlushDone() {
mTaskRunner->PostTask(FROM_HERE,
- base::Bind(&C2VDAComponent::onDrainDone, base::Unretained(this)));
+ ::base::Bind(&C2VDAComponent::onDrainDone, ::base::Unretained(this)));
}
void C2VDAComponent::notifyResetDone() {
mTaskRunner->PostTask(FROM_HERE,
- base::Bind(&C2VDAComponent::onResetDone, base::Unretained(this)));
+ ::base::Bind(&C2VDAComponent::onResetDone, ::base::Unretained(this)));
}
void C2VDAComponent::notifyError(VideoDecodeAcceleratorAdaptor::Result error) {
@@ -1003,16 +1077,17 @@ void C2VDAComponent::reportFinishedWorkIfAny() {
}
bool C2VDAComponent::isWorkDone(const C2Work* work) const {
+ if (work->input.buffers.empty()) {
+ // This is EOS work with no input buffer and should be processed by reportEOSWork().
+ return false;
+ }
if (work->input.buffers.front()) {
// Input buffer is still owned by VDA.
- // This condition could also recognize dummy EOS work since it won't get
- // onInputBufferDone(), input buffer won't be reset until reportEOSWork().
return false;
}
- if (mComponentState == ComponentState::DRAINING && mDrainWithEOS &&
- mPendingWorks.size() == 1u) {
- // If component is in DRAINING state and mDrainWithEOS is true. The last returned work
- // should be marked EOS flag and returned by reportEOSWork() instead.
+ if (mPendingOutputEOS && mPendingWorks.size() == 1u) {
+ // If mPendingOutputEOS is true, the last returned work should be marked EOS flag and
+ // returned by reportEOSWork() instead.
return false;
}
if (mLastOutputTimestamp < 0) {
@@ -1034,9 +1109,13 @@ void C2VDAComponent::reportEOSWork() {
return;
}
+ mPendingOutputEOS = false;
+
std::unique_ptr<C2Work> eosWork(std::move(mPendingWorks.front()));
mPendingWorks.pop_front();
- eosWork->input.buffers.front().reset();
+ if (!eosWork->input.buffers.empty()) {
+ eosWork->input.buffers.front().reset();
+ }
eosWork->result = C2_OK;
eosWork->workletsProcessed = static_cast<uint32_t>(eosWork->worklets.size());
eosWork->worklets.front()->output.flags = C2FrameData::FLAG_END_OF_STREAM;
@@ -1056,10 +1135,26 @@ void C2VDAComponent::reportAbandonedWorks() {
// TODO: correlate the definition of flushed work result to framework.
work->result = C2_NOT_FOUND;
- // When the work is abandoned, the input.buffers.front() shall reset by component.
- work->input.buffers.front().reset();
+ // When the work is abandoned, buffer in input.buffers shall reset by component.
+ if (!work->input.buffers.empty()) {
+ work->input.buffers.front().reset();
+ }
+ abandonedWorks.emplace_back(std::move(work));
+ }
+
+ for (auto& work : mAbandonedWorks) {
+ // TODO: correlate the definition of flushed work result to framework.
+ work->result = C2_NOT_FOUND;
+ // When the work is abandoned, buffer in input.buffers shall reset by component.
+ if (!work->input.buffers.empty()) {
+ work->input.buffers.front().reset();
+ }
abandonedWorks.emplace_back(std::move(work));
}
+ mAbandonedWorks.clear();
+
+ // Pending EOS work will be abandoned here due to component flush if any.
+ mPendingOutputEOS = false;
if (!abandonedWorks.empty()) {
mListener->onWorkDone_nb(shared_from_this(), std::move(abandonedWorks));
@@ -1070,6 +1165,58 @@ void C2VDAComponent::reportError(c2_status_t error) {
mListener->onError_nb(shared_from_this(), static_cast<uint32_t>(error));
}
+bool C2VDAComponent::startDequeueThread(const media::Size& size, uint32_t pixelFormat,
+ std::shared_ptr<C2BlockPool> blockPool) {
+ CHECK(!mDequeueThread.IsRunning());
+ if (!mDequeueThread.Start()) {
+ ALOGE("failed to start dequeue thread!!");
+ return false;
+ }
+ mDequeueLoopStop.store(false);
+ mBuffersInClient.store(0u);
+ mDequeueThread.task_runner()->PostTask(
+ FROM_HERE, ::base::Bind(&C2VDAComponent::dequeueThreadLoop, ::base::Unretained(this),
+ size, pixelFormat, std::move(blockPool)));
+ return true;
+}
+
+void C2VDAComponent::stopDequeueThread() {
+ if (mDequeueThread.IsRunning()) {
+ mDequeueLoopStop.store(true);
+ mDequeueThread.Stop();
+ }
+}
+
+void C2VDAComponent::dequeueThreadLoop(const media::Size& size, uint32_t pixelFormat,
+ std::shared_ptr<C2BlockPool> blockPool) {
+ ALOGV("dequeueThreadLoop starts");
+ DCHECK(mDequeueThread.task_runner()->BelongsToCurrentThread());
+
+ while (!mDequeueLoopStop.load()) {
+ if (mBuffersInClient.load() == 0) {
+ ::usleep(kDequeueRetryDelayUs); // wait for retry
+ continue;
+ }
+ std::shared_ptr<C2GraphicBlock> block;
+ C2MemoryUsage usage = {C2MemoryUsage::CPU_READ, 0};
+ auto err = blockPool->fetchGraphicBlock(size.width(), size.height(), pixelFormat, usage,
+ &block);
+ if (err == C2_TIMED_OUT) {
+ continue; // wait for retry
+ }
+ if (err == C2_OK) {
+ auto slot = getSlotFromGraphicBlockHandle(block->handle());
+ mTaskRunner->PostTask(FROM_HERE, ::base::Bind(&C2VDAComponent::onOutputBufferReturned,
+ ::base::Unretained(this), slot));
+ mBuffersInClient--;
+ } else {
+ ALOGE("dequeueThreadLoop got error: %d", err);
+ break;
+ }
+ }
+ ALOGV("dequeueThreadLoop terminates");
+}
+
class C2VDAComponentFactory : public C2ComponentFactory {
public:
C2VDAComponentFactory(C2String decoderName)
diff --git a/include/C2ArcVideoAcceleratorFactory.h b/include/C2ArcVideoAcceleratorFactory.h
deleted file mode 100644
index 9cdaf37..0000000
--- a/include/C2ArcVideoAcceleratorFactory.h
+++ /dev/null
@@ -1,37 +0,0 @@
-// Copyright 2018 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.
-
-#ifndef ANDROID_C2_ARC_VIDEO_ACCELERATOR_FACTORY_H
-#define ANDROID_C2_ARC_VIDEO_ACCELERATOR_FACTORY_H
-
-#include <media/arcvideobridge/IArcVideoBridge.h>
-#include <utils/Singleton.h>
-
-#include <components/arc/common/video.mojom.h>
-#include <components/arc/common/video_decode_accelerator.mojom.h>
-#include <components/arc/common/video_encode_accelerator.mojom.h>
-
-namespace android {
-// Helper class to create message pipe to the ArcVideoAccelerator.
-// This class should only be used in the Mojo thread.
-class C2ArcVideoAcceleratorFactory : public Singleton<C2ArcVideoAcceleratorFactory> {
-public:
- bool createVideoDecodeAccelerator(::arc::mojom::VideoDecodeAcceleratorRequest request);
- bool createVideoEncodeAccelerator(::arc::mojom::VideoEncodeAcceleratorRequest request);
- bool createVideoProtectedBufferAllocator(
- ::arc::mojom::VideoProtectedBufferAllocatorRequest request);
- int32_t hostVersion() const;
-
-private:
- C2ArcVideoAcceleratorFactory();
-
- uint32_t mHostVersion;
- sp<IArcVideoBridge> mArcVideoBridge;
- ::arc::mojom::VideoAcceleratorFactoryPtr mRemoteFactory;
-
- friend class Singleton<C2ArcVideoAcceleratorFactory>;
-};
-} // namespace android
-
-#endif // ANDROID_C2_ARC_VIDEO_ACCELERATOR_FACTORY_H
diff --git a/include/C2VDAComponent.h b/include/C2VDAComponent.h
index 6df3fe7..fcca0c9 100644
--- a/include/C2VDAComponent.h
+++ b/include/C2VDAComponent.h
@@ -132,10 +132,6 @@ private:
ERROR,
};
- enum {
- kDpbOutputBufferExtraCount = 3, // Use the same number as ACodec.
- };
-
// This constant is used to tell apart from drain_mode_t enumerations in C2Component.h, which
// means no drain request.
// Note: this value must be different than all enumerations in drain_mode_t.
@@ -156,13 +152,14 @@ private:
};
int32_t mBlockId = -1;
+ uint32_t mSlotId = 0;
State mState = State::OWNED_BY_COMPONENT;
// Graphic block buffer allocated from allocator. This should be reused.
std::shared_ptr<C2GraphicBlock> mGraphicBlock;
// HAL pixel format used while importing to VDA.
HalPixelFormat mPixelFormat;
// The handle dupped from graphic block for importing to VDA.
- base::ScopedFD mHandle;
+ ::base::ScopedFD mHandle;
// VideoFramePlane information for importing to VDA.
std::vector<VideoFramePlane> mPlanes;
};
@@ -178,12 +175,9 @@ private:
media::Rect visibleRect);
};
- // Used as the release callback for C2VDAGraphicBuffer to get back the output buffer.
- void returnOutputBuffer(int32_t pictureBufferId);
-
// These tasks should be run on the component thread |mThread|.
void onDestroy();
- void onStart(media::VideoCodecProfile profile, base::WaitableEvent* done);
+ void onStart(media::VideoCodecProfile profile, ::base::WaitableEvent* done);
void onQueueWork(std::unique_ptr<C2Work> work);
void onDequeueWork();
void onInputBufferDone(int32_t bitstreamId);
@@ -191,13 +185,13 @@ private:
void onDrain(uint32_t drainMode);
void onDrainDone();
void onFlush();
- void onStop(base::WaitableEvent* done);
+ void onStop(::base::WaitableEvent* done);
void onResetDone();
void onFlushDone();
void onStopDone();
void onOutputFormatChanged(std::unique_ptr<VideoFormat> format);
void onVisibleRectChanged(const media::Rect& cropRect);
- void onOutputBufferReturned(int32_t pictureBufferId);
+ void onOutputBufferReturned(uint32_t slotId);
// Send input buffer to accelerator with specified bitstream id.
void sendInputBufferToAccelerator(const C2ConstLinearBlock& input, int32_t bitstreamId);
@@ -207,6 +201,8 @@ private:
void setOutputFormatCrop(const media::Rect& cropRect);
// Helper function to get the specified GraphicBlockInfo object by its id.
GraphicBlockInfo* getGraphicBlockById(int32_t blockId);
+ // Helper function to get the specified GraphicBlockInfo object by its slot index.
+ GraphicBlockInfo* getGraphicBlockBySlot(uint32_t slotId);
// Helper function to get the specified work in mPendingWorks by bitstream id.
C2Work* getPendingWorkByBitstreamId(int32_t bitstreamId);
// Try to apply the output format change.
@@ -220,13 +216,22 @@ private:
void reportFinishedWorkIfAny();
// Make onWorkDone call to listener for reporting EOS work in mPendingWorks.
void reportEOSWork();
- // Abandon all works in mPendingWorks.
+ // Abandon all works in mPendingWorks and mAbandonedWorks.
void reportAbandonedWorks();
// Make onError call to listener for reporting errors.
void reportError(c2_status_t error);
// Helper function to determine if the work is finished.
bool isWorkDone(const C2Work* work) const;
+ // Start dequeue thread, return true on success.
+ bool startDequeueThread(const media::Size& size, uint32_t pixelFormat,
+ std::shared_ptr<C2BlockPool> blockPool);
+ // Stop dequeue thread.
+ void stopDequeueThread();
+ // The rountine task running on dequeue thread.
+ void dequeueThreadLoop(const media::Size& size, uint32_t pixelFormat,
+ std::shared_ptr<C2BlockPool> blockPool);
+
// The pointer of component interface implementation.
std::shared_ptr<IntfImpl> mIntfImpl;
// The pointer of component interface.
@@ -235,9 +240,16 @@ private:
std::shared_ptr<Listener> mListener;
// The main component thread.
- base::Thread mThread;
+ ::base::Thread mThread;
// The task runner on component thread.
- scoped_refptr<base::SingleThreadTaskRunner> mTaskRunner;
+ scoped_refptr<::base::SingleThreadTaskRunner> mTaskRunner;
+
+ // The dequeue buffer loop thread.
+ ::base::Thread mDequeueThread;
+ // The stop signal for dequeue loop which should be atomic (toggled by main thread).
+ std::atomic<bool> mDequeueLoopStop;
+ // The count of buffers owned by client which should be atomic.
+ std::atomic<uint32_t> mBuffersInClient;
// The following members should be utilized on component thread |mThread|.
@@ -247,12 +259,13 @@ private:
std::unique_ptr<VideoDecodeAcceleratorAdaptor> mVDAAdaptor;
// The done event pointer of stop procedure. It should be restored in onStop() and signaled in
// onStopDone().
- base::WaitableEvent* mStopDoneEvent;
+ ::base::WaitableEvent* mStopDoneEvent;
// The state machine on component thread.
ComponentState mComponentState;
- // The indicator of drain mode (true for draining with EOS). This should be always set along
- // with component going to DRAINING state, and only regarded under DRAINING state.
- bool mDrainWithEOS;
+ // The indicator of draining with EOS. This should be always set along with component going to
+ // DRAINING state, and will be unset either after reportEOSWork() (EOS is outputted), or
+ // reportAbandonedWorks() (drain is cancelled and works are abandoned).
+ bool mPendingOutputEOS;
// The vector of storing allocated output graphic block information.
std::vector<GraphicBlockInfo> mGraphicBlocks;
// The work queue. Works are queued along with drain mode from component API queue_nb and
@@ -261,6 +274,9 @@ private:
// Store all pending works. The dequeued works are placed here until they are finished and then
// sent out by onWorkDone call to listener.
std::deque<std::unique_ptr<C2Work>> mPendingWorks;
+ // Store all abandoned works. When component gets flushed/stopped, remaining works in queue are
+ // dumped here and sent out by onWorkDone call to listener after flush/stop is finished.
+ std::vector<std::unique_ptr<C2Work>> mAbandonedWorks;
// Store the visible rect provided from VDA. If this is changed, component should issue a
// visible size change event.
media::Rect mRequestedVisibleRect;
@@ -272,8 +288,13 @@ private:
// Record the timestamp of the last output buffer. This is used to determine if the work is
// finished.
int64_t mLastOutputTimestamp;
- // The pointer of output block pool.
- std::shared_ptr<C2BlockPool> mOutputBlockPool;
+ // Hack(b/79239042): We do not have a solution to recycle buffers in byte-buffer mode now. This
+ // is a fake buffer queue to record buffers outputted to client, and regard buffer is returned
+ // when it is popped by a new push of the queue (size: kMockMaxBuffersInClient).
+ // TODO: provide proper solution and get rid of this hack.
+ std::list<uint32_t> mMockBufferQueueInClient;
+ // The indicator of whether output has surface.
+ bool mSurfaceMode;
// The following members should be utilized on parent thread.
@@ -285,7 +306,7 @@ private:
std::mutex mStartStopLock;
// The WeakPtrFactory for getting weak pointer of this.
- base::WeakPtrFactory<C2VDAComponent> mWeakThisFactory;
+ ::base::WeakPtrFactory<C2VDAComponent> mWeakThisFactory;
DISALLOW_COPY_AND_ASSIGN(C2VDAComponent);
};
diff --git a/tests/Android.mk b/tests/Android.mk
index 4bafb4a..3b384b4 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -21,6 +21,7 @@ LOCAL_SHARED_LIBRARIES := \
libv4l2_codec2_vda \
LOCAL_C_INCLUDES += \
+ $(TOP)/device/google/cheets2/codec2/vdastore/include \
$(TOP)/external/v4l2_codec2/include \
$(TOP)/external/v4l2_codec2/vda \
$(TOP)/hardware/google/av/codec2/include \
diff --git a/tests/C2VDACompIntf_test.cpp b/tests/C2VDACompIntf_test.cpp
index 6ed0753..f08ee50 100644
--- a/tests/C2VDACompIntf_test.cpp
+++ b/tests/C2VDACompIntf_test.cpp
@@ -5,6 +5,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "C2VDACompIntf_test"
+#include <C2VDAAllocatorStore.h>
#include <C2VDAComponent.h>
#include <C2PlatformSupport.h>
@@ -30,7 +31,7 @@ const char* MEDIA_MIMETYPE_VIDEO_RAW = "video/raw";
const char* MEDIA_MIMETYPE_VIDEO_AVC = "video/avc";
const C2Allocator::id_t kInputAllocators[] = {C2PlatformAllocatorStore::ION};
-const C2Allocator::id_t kOutputAllocators[] = {C2PlatformAllocatorStore::GRALLOC};
+const C2Allocator::id_t kOutputAllocators[] = {C2VDAAllocatorStore::V4L2_BUFFERQUEUE};
const C2BlockPool::local_id_t kDefaultOutputBlockPool = C2BlockPool::BASIC_GRAPHIC;
class C2VDACompIntfTest : public ::testing::Test {