Camera: Heic: Update codec quality per shot

Update codec quality setting at the time of:
- Input pending frame creation if there is no inflight encoding.
- Any pending input frame is done processing.

Note that since there is no way for camera framework to synchronize
quality setting per-frame when encoder runs in surface mode, the update
of quality is on best-effort basis.

Test: vendor testing, and camera CTS
Bug: 140506016
Change-Id: I58ddc9302b1507428ccf42f4c77c6624f1c600ba
diff --git a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
index bf4d1c8..9cdad70 100644
--- a/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
+++ b/services/camera/libcameraservice/api2/HeicCompositeStream.cpp
@@ -67,6 +67,7 @@
         mDequeuedOutputBufferCnt(0),
         mLockedAppSegmentBufferCnt(0),
         mCodecOutputCounter(0),
+        mQuality(-1),
         mGridTimestampUs(0) {
 }
 
@@ -525,6 +526,12 @@
         mPendingInputFrames[it->first].orientation = it->second.first;
         mPendingInputFrames[it->first].quality = it->second.second;
         mSettingsByTimestamp.erase(it);
+
+        // Set encoder quality if no inflight encoding
+        if (mPendingInputFrames.size() == 1) {
+            int32_t newQuality = mPendingInputFrames.begin()->second.quality;
+            updateCodecQualityLocked(newQuality);
+        }
     }
 
     while (!mInputAppSegmentBuffers.empty()) {
@@ -851,17 +858,6 @@
                 strerror(-res), res);
         return res;
     }
-    // Set encoder quality
-    {
-        sp<AMessage> qualityParams = new AMessage;
-        qualityParams->setInt32(PARAMETER_KEY_VIDEO_BITRATE, inputFrame.quality);
-        res = mCodec->setParameters(qualityParams);
-        if (res != OK) {
-            ALOGE("%s: Failed to set codec quality: %s (%d)",
-                    __FUNCTION__, strerror(-res), res);
-            return res;
-        }
-    }
 
     ssize_t trackId = inputFrame.muxer->addTrack(inputFrame.format);
     if (trackId < 0) {
@@ -1148,16 +1144,30 @@
 
 void HeicCompositeStream::releaseInputFramesLocked() {
     auto it = mPendingInputFrames.begin();
+    bool inputFrameDone = false;
     while (it != mPendingInputFrames.end()) {
         auto& inputFrame = it->second;
         if (inputFrame.error ||
             (inputFrame.appSegmentWritten && inputFrame.pendingOutputTiles == 0)) {
             releaseInputFrameLocked(&inputFrame);
             it = mPendingInputFrames.erase(it);
+            inputFrameDone = true;
         } else {
             it++;
         }
     }
+
+    // Update codec quality based on first upcoming input frame.
+    // Note that when encoding is in surface mode, currently there is  no
+    // way for camera service to synchronize quality setting on a per-frame
+    // basis: we don't get notification when codec is ready to consume a new
+    // input frame. So we update codec quality on a best-effort basis.
+    if (inputFrameDone) {
+        auto firstPendingFrame = mPendingInputFrames.begin();
+        if (firstPendingFrame != mPendingInputFrames.end()) {
+            updateCodecQualityLocked(firstPendingFrame->second.quality);
+        }
+    }
 }
 
 status_t HeicCompositeStream::initializeCodec(uint32_t width, uint32_t height,
@@ -1546,6 +1556,20 @@
     return maxAppsSegment * (2 + 0xFFFF) + sizeof(struct CameraBlob);
 }
 
+void HeicCompositeStream::updateCodecQualityLocked(int32_t quality) {
+    if (quality != mQuality) {
+        sp<AMessage> qualityParams = new AMessage;
+        qualityParams->setInt32(PARAMETER_KEY_VIDEO_BITRATE, quality);
+        status_t res = mCodec->setParameters(qualityParams);
+        if (res != OK) {
+            ALOGE("%s: Failed to set codec quality: %s (%d)",
+                    __FUNCTION__, strerror(-res), res);
+        } else {
+            mQuality = quality;
+        }
+    }
+}
+
 bool HeicCompositeStream::threadLoop() {
     int64_t currentTs = INT64_MAX;
     bool newInputAvailable = false;