C2VEAComponent: handle CSD info for input surface and byte-buffer modes
In current C2 framework, CSD info would be reported in different manners
whether encoder is in input surface mode and byte-buffer mode.
Byte-buffer mode:
A work with no input buffer will be queued prior to other works to get CSD
info only (CSD-holder work). When the first bitstream buffer is encoded by
VEA, the component needs to extract the CSD info and put into CSD-holder work
as a parameter.
Surface mode:
No CSD-holder work. When the first bitstream buffer is encoded by VEA, the
component needs to extract the CSD info and put into the same work
corresponding to this bitstream as a parameter.
Bug: 73059339
Test: android.media.cts.EncodeDecodeTest#testEncodeDecodeVideoFromBufferToBufferQCIF
Test: android.media.cts.EncodeDecodeTest#testEncodeDecodeVideoFromSurfaceToSurfaceQCIF
Change-Id: I13ede4c3b725e27cd0eb66b07925fdf93b5bafb4
(cherry picked from commit 0922077bcc55af276402129b23110de9abfee5f7)
diff --git a/C2VEAComponent.cpp b/C2VEAComponent.cpp
index 5c56e40..b448693 100644
--- a/C2VEAComponent.cpp
+++ b/C2VEAComponent.cpp
@@ -685,7 +685,8 @@
if (drainMode == NO_DRAIN) {
if (mCSDWorkIndex == kCSDInit) {
// WORKAROUND from CCodecBufferChannel:
- // An empty buffer is queued prior to any input buffer works to get the CSD info.
+ // For input byte-buffer mode, an empty buffer is queued prior to any input buffer
+ // works to get the CSD info.
mCSDWorkIndex = static_cast<int64_t>(index);
} else {
// Despite the WORKAROUND, client should not queue a work with no input buffer
@@ -919,34 +920,46 @@
C2ConstLinearBlock constBlock =
blockIter->second->share(blockIter->second->offset(), payloadSize, C2Fence());
- if (mCSDWorkIndex >= 0) {
+ // Get the work with corresponding timestamp of returned output buffer.
+ C2Work* work = getPendingWorkByTimestamp(timestamp);
+ if (!work) {
+ reportError(C2_CORRUPTED);
+ return;
+ }
+
+ // There are two different situations for CSD according to the input mode:
+ // 1. For input byte-buffer mode, an empty work is queued first to get CSD info.
+ // |mCSDWorkIndex| >= 0 which represents the frame index of that work. The extracted
+ // CSD info should be put into that work and report right away.
+ // 2. For input surface mode, there is no empty work for CSD info.
+ // |mCSDWorkIndex| == kCSDInit. The extracted CSD info should be put into the
+ // corresponding work.
+ std::unique_ptr<C2StreamCsdInfo::output> csd;
+ if (mCSDWorkIndex != kCSDSubmitted) {
C2ReadView view = constBlock.map().get();
- std::unique_ptr<C2StreamCsdInfo::output> csd;
extractCSDInfo(&csd, view.data(), view.capacity());
if (!csd) {
reportError(C2_CORRUPTED);
return;
}
- // Attach CSD info to the work whose index is |mCSDWorkIndex| and report.
- C2Work* csdWork = getPendingWorkByIndex(mCSDWorkIndex);
- if (!csdWork) {
- ALOGE("Cannot get CSD pending work by index: %" PRId64 "", mCSDWorkIndex);
- reportError(C2_CORRUPTED);
- return;
+ if (mCSDWorkIndex >= 0) {
+ // Case 1: Attach CSD info to the work whose index is |mCSDWorkIndex| and report.
+ C2Work* csdWork = getPendingWorkByIndex(mCSDWorkIndex);
+ if (!csdWork) {
+ ALOGE("Cannot get CSD pending work by index: %" PRId64 "", mCSDWorkIndex);
+ reportError(C2_CORRUPTED);
+ return;
+ }
+ csdWork->worklets.front()->output.configUpdate.push_back(std::move(csd));
+ reportWorkIfFinished(mCSDWorkIndex);
+ } else {
+ // Case 2: Attach CSD info to the corresponding work.
+ work->worklets.front()->output.configUpdate.push_back(std::move(csd));
}
- csdWork->worklets.front()->output.configUpdate.push_back(std::move(csd));
- reportWorkIfFinished(mCSDWorkIndex);
mCSDWorkIndex = kCSDSubmitted;
}
- // Attach output linear buffer to the work with corresponding timestamp.
- C2Work* work = getPendingWorkByTimestamp(timestamp);
- if (!work) {
- reportError(C2_CORRUPTED);
- return;
- }
-
std::shared_ptr<C2Buffer> buffer = C2Buffer::CreateLinearBuffer(std::move(constBlock));
if (keyFrame) {
buffer->setInfo(
@@ -981,17 +994,22 @@
ALOGE("Invalid timestamp: %" PRId64 "", timestamp);
return nullptr;
}
- auto workIter = std::find_if(mPendingWorks.begin(), mPendingWorks.end(),
- [timestamp](const std::unique_ptr<C2Work>& w) {
- return !(w->input.flags & C2FrameData::FLAG_END_OF_STREAM) &&
- w->input.ordinal.timestamp.peeku() ==
- static_cast<uint64_t>(timestamp);
- });
- if (workIter == mPendingWorks.end()) {
- ALOGE("Can't find pending work by timestmap: %" PRId64 "", timestamp);
- return nullptr;
+
+ for (auto& work : mPendingWorks) {
+ if (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) {
+ continue; // EOS work should be neglected.
+ }
+ if (mCSDWorkIndex >= 0 &&
+ work->input.ordinal.frameIndex.peeku() == static_cast<uint64_t>(mCSDWorkIndex)) {
+ continue; // CSD-holder work should be neglected.
+ }
+ if (work->input.ordinal.timestamp.peeku() == static_cast<uint64_t>(timestamp)) {
+ return work.get();
+ }
}
- return workIter->get();
+
+ ALOGE("Can't find pending work by timestmap: %" PRId64 "", timestamp);
+ return nullptr;
}
void C2VEAComponent::extractCSDInfo(std::unique_ptr<C2StreamCsdInfo::output>* const csd,
diff --git a/include/C2VEAComponent.h b/include/C2VEAComponent.h
index efc1665..9b2d3e4 100644
--- a/include/C2VEAComponent.h
+++ b/include/C2VEAComponent.h
@@ -205,7 +205,7 @@
// Helper function to get the specified work in |mPendingWorks| by frame index.
C2Work* getPendingWorkByIndex(uint64_t index);
// Helper function to get the specified work in |mPendingWorks| with the same |timestamp|.
- // Note that EOS work should be excluded because its timestmap is not meaningful.
+ // Note that EOS and CSD-holder work should be excluded because its timestmap is not meaningful.
C2Work* getPendingWorkByTimestamp(int64_t timestamp);
// For VEA, the codec-specific data (CSD in abbreviation, SPS and PPS for H264 encode) will be
// concatenated with the first encoded slice in one bitstream buffer. This function extracts CSD