codec2: handle CCodec sends empty buffer at EOS
After ag/4278466, CCodec changes to send EOS work with no input buffer rather
than a dummy buffer. Handle this case for C2VDAComponent.
Bug: 73261432
Test: CtsMediaTestCases
android.media.cts.MediaCodecTest
android.media.cts.DecoderTest#testDecodeWithEOSOnLastBuffer
android.media.cts.MediaPlayerTest#testLocalVideo_MP4_H264_480x360_1000kbps_25fps_AAC_Stereo_128kbps_44110Hz
Change-Id: Ib43f39804fc63520c4320f0a4a76822c8b15e8f0
diff --git a/C2VDAComponent.cpp b/C2VDAComponent.cpp
index a6698cf..5eec1ef 100644
--- a/C2VDAComponent.cpp
+++ b/C2VDAComponent.cpp
@@ -307,10 +307,15 @@
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);
@@ -1072,10 +1077,12 @@
}
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 (mPendingOutputEOS && mPendingWorks.size() == 1u) {
@@ -1106,7 +1113,9 @@
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;
@@ -1126,16 +1135,20 @@
// 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, 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));
}
mAbandonedWorks.clear();