aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--C2VDAComponent.cpp100
-rw-r--r--include/C2VDAComponent.h3
2 files changed, 52 insertions, 51 deletions
diff --git a/C2VDAComponent.cpp b/C2VDAComponent.cpp
index 1f30f7d..fd7b313 100644
--- a/C2VDAComponent.cpp
+++ b/C2VDAComponent.cpp
@@ -63,6 +63,7 @@ const C2String kVP9SecureDecoderName = "c2.vda.vp9.decoder.secure";
const uint32_t kDpbOutputBufferExtraCount = 3; // Use the same number as ACodec.
const int kDequeueRetryDelayUs = 10000; // Wait time of dequeue buffer retry in microseconds.
+const int32_t kAllocateBufferMaxRetries = 10; // Max retry time for fetchGraphicBlock timeout.
} // namespace
C2VDAComponent::IntfImpl::IntfImpl(C2String name, const std::shared_ptr<C2ReflectorHelper>& helper)
@@ -207,7 +208,6 @@ C2VDAComponent::C2VDAComponent(C2String name, c2_node_id_t id,
mVDAInitResult(VideoDecodeAcceleratorAdaptor::Result::ILLEGAL_STATE),
mComponentState(ComponentState::UNINITIALIZED),
mPendingOutputEOS(false),
- mLastOutputTimestamp(-1),
mCodecProfile(media::VIDEO_CODEC_PROFILE_UNKNOWN),
mState(State::UNLOADED),
mWeakThisFactory(this) {
@@ -306,9 +306,12 @@ void C2VDAComponent::onDequeueWork() {
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);
+ // Client may queue a work with no input buffer for either it's EOS or empty CSD, otherwise
+ // every work must have one input buffer.
+ CHECK(drainMode != NO_DRAIN || work->input.flags & C2FrameData::FLAG_CODEC_CONFIG);
+ // Emplace a nullptr to unify the check for work done.
+ ALOGV("Got a work with no input buffer! Emplace a nullptr inside.");
+ work->input.buffers.emplace_back(nullptr);
} else {
// If input.buffers is not empty, the buffer should have meaningful content inside.
C2ConstLinearBlock linearBlock = work->input.buffers.front()->data().linearBlocks().front();
@@ -366,8 +369,14 @@ void C2VDAComponent::onOutputBufferReturned(std::shared_ptr<C2GraphicBlock> bloc
return;
}
- // TODO(johnylin): when buffer is returned, we should confirm that output format is not changed
- // yet. If changed, just let the buffer be released.
+ if (block->width() != static_cast<uint32_t>(mOutputFormat.mCodedSize.width()) ||
+ block->height() != static_cast<uint32_t>(mOutputFormat.mCodedSize.height())) {
+ // Output buffer is returned after we changed output resolution. Just let the buffer be
+ // released.
+ ALOGV("Discard obsolete graphic block: pool id=%u", poolId);
+ return;
+ }
+
GraphicBlockInfo* info = getGraphicBlockByPoolId(poolId);
if (!info) {
reportError(C2_CORRUPTED);
@@ -405,18 +414,14 @@ void C2VDAComponent::onOutputBufferDone(int32_t pictureBufferId, int32_t bitstre
mBuffersInClient++;
// Attach output buffer to the work corresponded to bitstreamId.
- auto block = info->mGraphicBlock;
- work->worklets.front()->output.buffers.emplace_back(C2Buffer::CreateGraphicBuffer(
- block->share(C2Rect(mOutputFormat.mVisibleRect.width(),
- mOutputFormat.mVisibleRect.height()),
- C2Fence())));
+ C2ConstGraphicBlock constBlock = info->mGraphicBlock->share(
+ C2Rect(mOutputFormat.mVisibleRect.width(), mOutputFormat.mVisibleRect.height()),
+ C2Fence());
+ MarkBlockPoolDataAsShared(constBlock);
+ work->worklets.front()->output.buffers.emplace_back(
+ C2Buffer::CreateGraphicBuffer(std::move(constBlock)));
info->mGraphicBlock.reset();
- // 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());
- CHECK_GE(currentTimestamp, mLastOutputTimestamp);
- mLastOutputTimestamp = currentTimestamp;
-
reportFinishedWorkIfAny();
}
@@ -470,9 +475,6 @@ void C2VDAComponent::onDrainDone() {
// mPendingWorks must be empty after draining is finished.
CHECK(mPendingWorks.empty());
- // Last stream is finished. Reset the timestamp record.
- mLastOutputTimestamp = -1;
-
// Work dequeueing was stopped while component draining. Restart it.
mTaskRunner->PostTask(FROM_HERE,
::base::Bind(&C2VDAComponent::onDequeueWork, ::base::Unretained(this)));
@@ -534,8 +536,6 @@ void C2VDAComponent::onResetDone() {
void C2VDAComponent::onFlushDone() {
ALOGV("onFlushDone");
reportAbandonedWorks();
- // Reset the timestamp record.
- mLastOutputTimestamp = -1;
mComponentState = ComponentState::STARTED;
// Work dequeueing was stopped while component flushing. Restart it.
@@ -551,7 +551,6 @@ void C2VDAComponent::onStopDone() {
// do something for them?
reportAbandonedWorks();
mPendingOutputFormat.reset();
- mLastOutputTimestamp = -1;
if (mVDAAdaptor.get()) {
mVDAAdaptor->destroy();
mVDAAdaptor.reset(nullptr);
@@ -652,14 +651,15 @@ void C2VDAComponent::tryChangeOutputFormat() {
ALOGV("tryChangeOutputFormat");
CHECK(mPendingOutputFormat);
- // Change the output format only after all output buffers are returned
- // from clients.
- // TODO(johnylin): don't need to wait for new proposed buffer flow.
+ // At this point, all output buffers should not be owned by accelerator. The component is not
+ // able to know when a client will release all owned output buffers by now. But it is ok to
+ // leave them to client since componenet won't own those buffers anymore.
+ // TODO(johnylin): we may also set a parameter for component to keep dequeueing buffers and
+ // change format only after the component owns most buffers. This may prevent
+ // too many buffers are still on client's hand while component starts to
+ // allocate more buffers. However, it leads latency on output format change.
for (const auto& info : mGraphicBlocks) {
- if (info.mState == GraphicBlockInfo::State::OWNED_BY_CLIENT) {
- ALOGV("wait buffer: %d for output format change", info.mBlockId);
- return;
- }
+ CHECK(info.mState != GraphicBlockInfo::State::OWNED_BY_ACCELERATOR);
}
CHECK_EQ(mPendingOutputFormat->mPixelFormat, HalPixelFormat::YCbCr_420_888);
@@ -749,13 +749,23 @@ c2_status_t C2VDAComponent::allocateBuffersFromBlockAllocator(const media::Size&
std::shared_ptr<C2GraphicBlock> block;
C2MemoryUsage usage = {
mSecureMode ? C2MemoryUsage::READ_PROTECTED : C2MemoryUsage::CPU_READ, 0};
- err = blockPool->fetchGraphicBlock(size.width(), size.height(), pixelFormat, usage, &block);
- if (err != C2_OK) {
- mGraphicBlocks.clear();
- ALOGE("failed to allocate buffer: %d", err);
- reportError(err);
- return err;
+
+ int32_t retries_left = kAllocateBufferMaxRetries;
+ err = C2_NO_INIT;
+ while (err != C2_OK) {
+ err = blockPool->fetchGraphicBlock(size.width(), size.height(), pixelFormat, usage,
+ &block);
+ if (err == C2_TIMED_OUT && retries_left > 0) {
+ ALOGD("allocate buffer timeout, %d retry time(s) left...", retries_left);
+ retries_left--;
+ } else if (err != C2_OK) {
+ mGraphicBlocks.clear();
+ ALOGE("failed to allocate buffer: %d", err);
+ reportError(err);
+ return err;
+ }
}
+
uint32_t poolId;
if (useBufferQueue) {
err = C2VdaBqBlockPool::getPoolIdFromGraphicBlock(block, &poolId);
@@ -1098,11 +1108,6 @@ void C2VDAComponent::reportFinishedWorkIfAny() {
std::list<std::unique_ptr<C2Work>> finishedWorks;
// Work should be reported as done if both input and output buffer are returned by VDA.
-
- // Note that not every input buffer has matched output (ex. CSD header for H.264).
- // However, the timestamp is guaranteed to be monotonic increasing for buffers in display order.
- // That is, since VDA output is in display order, if we get a returned output with timestamp T,
- // it implies all works with timestamp <= T are done.
// EOS work will not be reported here. reportEOSWork() does it.
auto iter = mPendingWorks.begin();
while (iter != mPendingWorks.end()) {
@@ -1122,8 +1127,8 @@ 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().
+ if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+ // This is EOS work and should be processed by reportEOSWork().
return false;
}
if (work->input.buffers.front()) {
@@ -1135,13 +1140,12 @@ bool C2VDAComponent::isWorkDone(const C2Work* work) const {
// returned by reportEOSWork() instead.
return false;
}
- if (mLastOutputTimestamp < 0) {
- return false; // No output buffer is returned yet.
- }
- if (work->input.ordinal.timestamp > static_cast<uint64_t>(mLastOutputTimestamp)) {
- return false; // Output buffer is not returned by VDA yet.
+ if (!(work->input.flags & C2FrameData::FLAG_CODEC_CONFIG) &&
+ work->worklets.front()->output.buffers.empty()) {
+ // Output buffer is not returned from VDA yet.
+ return false;
}
- return true; // Output buffer is returned, or it has no related output buffer.
+ return true; // Output buffer is returned, or it has no related output buffer (CSD work).
}
void C2VDAComponent::reportEOSWork() {
diff --git a/include/C2VDAComponent.h b/include/C2VDAComponent.h
index 254b9b0..477bc1c 100644
--- a/include/C2VDAComponent.h
+++ b/include/C2VDAComponent.h
@@ -293,9 +293,6 @@ private:
// The pending output format. We need to wait until all buffers are returned back to apply the
// format change.
std::unique_ptr<VideoFormat> mPendingOutputFormat;
- // Record the timestamp of the last output buffer. This is used to determine if the work is
- // finished.
- int64_t mLastOutputTimestamp;
// The indicator of whether component is in secure mode.
bool mSecureMode;