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