Camera: Add HEIC support

- Derive HEIC capabilities from camera HAL and media framework.
- Add HeicCompositeStream to encode camera buffers to HEIC buffers.
- Add ExifUtils to overwrite JPEG APP segments and send to media codec.
- Add NDK enums and corresponding format support.

Test: Camera CTS
Bug: 79465976
Change-Id: I0a885e76335f3eba4be0fd42241edb0b7349f284
diff --git a/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp
new file mode 100644
index 0000000..ed9be6e
--- /dev/null
+++ b/services/camera/libcameraservice/api2/HeicEncoderInfoManager.cpp
@@ -0,0 +1,294 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "HeicEncoderInfoManager"
+//#define LOG_NDEBUG 0
+
+#include <cstdint>
+#include <regex>
+
+#include <cutils/properties.h>
+#include <log/log_main.h>
+#include <system/graphics.h>
+
+#include <media/stagefright/MediaCodecList.h>
+#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/foundation/ABuffer.h>
+
+#include "HeicEncoderInfoManager.h"
+
+namespace android {
+namespace camera3 {
+
+HeicEncoderInfoManager::HeicEncoderInfoManager() :
+        mIsInited(false),
+        mMinSizeHeic(0, 0),
+        mMaxSizeHeic(INT32_MAX, INT32_MAX),
+        mHasHEVC(false),
+        mHasHEIC(false),
+        mDisableGrid(false) {
+    if (initialize() == OK) {
+        mIsInited = true;
+    }
+}
+
+HeicEncoderInfoManager::~HeicEncoderInfoManager() {
+}
+
+bool HeicEncoderInfoManager::isSizeSupported(int32_t width, int32_t height, bool* useHeic,
+        bool* useGrid, int64_t* stall) const {
+    if (useHeic == nullptr || useGrid == nullptr) {
+        ALOGE("%s: invalid parameters: useHeic %p, useGrid %p",
+                __FUNCTION__, useHeic, useGrid);
+        return false;
+    }
+    if (!mIsInited) return false;
+
+    bool chooseHeic = false, enableGrid = true;
+    if (mHasHEIC && width >= mMinSizeHeic.first &&
+            height >= mMinSizeHeic.second && width <= mMaxSizeHeic.first &&
+            height <= mMaxSizeHeic.second) {
+        chooseHeic = true;
+        enableGrid = false;
+    } else if (mHasHEVC) {
+        bool fullSizeSupportedByHevc = (width >= mMinSizeHevc.first &&
+                height >= mMinSizeHevc.second &&
+                width <= mMaxSizeHevc.first &&
+                height <= mMaxSizeHevc.second);
+        if (fullSizeSupportedByHevc && (mDisableGrid ||
+                (width <= 1920 && height <= 1080))) {
+            enableGrid = false;
+        }
+    } else {
+        // No encoder available for the requested size.
+        return false;
+    }
+
+    if (stall != nullptr) {
+        // Find preferred encoder which advertise
+        // "measured-frame-rate-WIDTHxHEIGHT-range" key.
+        const FrameRateMaps& maps =
+                (chooseHeic && mHeicFrameRateMaps.size() > 0) ?
+                mHeicFrameRateMaps : mHevcFrameRateMaps;
+        const auto& closestSize = findClosestSize(maps, width, height);
+        if (closestSize == maps.end()) {
+            // The "measured-frame-rate-WIDTHxHEIGHT-range" key is optional.
+            // Hardcode to some default value (3.33ms * tile count) based on resolution.
+            *stall = 3333333LL * width * height / (kGridWidth * kGridHeight);
+            return true;
+        }
+
+        // Derive stall durations based on average fps of the closest size.
+        constexpr int64_t NSEC_PER_SEC = 1000000000LL;
+        int32_t avgFps = (closestSize->second.first + closestSize->second.second)/2;
+        float ratio = 1.0f * width * height /
+                (closestSize->first.first * closestSize->first.second);
+        *stall = ratio * NSEC_PER_SEC / avgFps;
+    }
+
+    *useHeic = chooseHeic;
+    *useGrid = enableGrid;
+    return true;
+}
+
+status_t HeicEncoderInfoManager::initialize() {
+    mDisableGrid = property_get_bool("camera.heic.disable_grid", false);
+    sp<IMediaCodecList> codecsList = MediaCodecList::getInstance();
+    if (codecsList == nullptr) {
+        // No media codec available.
+        return OK;
+    }
+
+    sp<AMessage> heicDetails = getCodecDetails(codecsList, MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC);
+    sp<AMessage> hevcDetails = getCodecDetails(codecsList, MEDIA_MIMETYPE_VIDEO_HEVC);
+
+    if (hevcDetails == nullptr) {
+        if (heicDetails != nullptr) {
+            ALOGE("%s: Device must support HEVC codec if HEIC codec is available!",
+                    __FUNCTION__);
+            return BAD_VALUE;
+        }
+        return OK;
+    }
+
+    // Check CQ mode for HEVC codec
+    {
+        AString bitrateModes;
+        auto hasItem = hevcDetails->findString("feature-bitrate-modes", &bitrateModes);
+        if (!hasItem) {
+            ALOGE("%s: Failed to query bitrate modes for HEVC codec", __FUNCTION__);
+            return BAD_VALUE;
+        }
+        ALOGV("%s: HEVC codec's feature-bitrate-modes value is %d, %s",
+                __FUNCTION__, hasItem, bitrateModes.c_str());
+        std::regex pattern("(^|,)CQ($|,)", std::regex_constants::icase);
+        if (!std::regex_search(bitrateModes.c_str(), pattern)) {
+            return OK;
+        }
+    }
+
+    // HEIC size range
+    if (heicDetails != nullptr) {
+        auto res = getCodecSizeRange(MEDIA_MIMETYPE_IMAGE_ANDROID_HEIC,
+                heicDetails, &mMinSizeHeic, &mMaxSizeHeic, &mHeicFrameRateMaps);
+        if (res != OK) {
+            ALOGE("%s: Failed to get HEIC codec size range: %s (%d)", __FUNCTION__,
+                    strerror(-res), res);
+            return BAD_VALUE;
+        }
+        mHasHEIC = true;
+    }
+
+    // HEVC size range
+    {
+        auto res = getCodecSizeRange(MEDIA_MIMETYPE_VIDEO_HEVC,
+                hevcDetails, &mMinSizeHevc, &mMaxSizeHevc, &mHevcFrameRateMaps);
+        if (res != OK) {
+            ALOGE("%s: Failed to get HEVC codec size range: %s (%d)", __FUNCTION__,
+                    strerror(-res), res);
+            return BAD_VALUE;
+        }
+
+        mHasHEVC = true;
+    }
+
+    return OK;
+}
+
+status_t HeicEncoderInfoManager::getFrameRateMaps(sp<AMessage> details, FrameRateMaps* maps) {
+    if (details == nullptr || maps == nullptr) {
+        ALOGE("%s: Invalid input: details: %p, maps: %p", __FUNCTION__, details.get(), maps);
+        return BAD_VALUE;
+    }
+
+    for (size_t i = 0; i < details->countEntries(); i++) {
+        AMessage::Type type;
+        const char* entryName = details->getEntryNameAt(i, &type);
+        if (type != AMessage::kTypeString) continue;
+        std::regex frameRateNamePattern("measured-frame-rate-([0-9]+)[*x]([0-9]+)-range",
+                std::regex_constants::icase);
+        std::cmatch sizeMatch;
+        if (std::regex_match(entryName, sizeMatch, frameRateNamePattern) &&
+                sizeMatch.size() == 3) {
+            AMessage::ItemData item = details->getEntryAt(i);
+            AString fpsRangeStr;
+            if (item.find(&fpsRangeStr)) {
+                ALOGV("%s: %s", entryName, fpsRangeStr.c_str());
+                std::regex frameRatePattern("([0-9]+)-([0-9]+)");
+                std::cmatch fpsMatch;
+                if (std::regex_match(fpsRangeStr.c_str(), fpsMatch, frameRatePattern) &&
+                        fpsMatch.size() == 3) {
+                    maps->emplace(
+                            std::make_pair(stoi(sizeMatch[1]), stoi(sizeMatch[2])),
+                            std::make_pair(stoi(fpsMatch[1]), stoi(fpsMatch[2])));
+                } else {
+                    return BAD_VALUE;
+                }
+            }
+        }
+    }
+    return OK;
+}
+
+status_t HeicEncoderInfoManager::getCodecSizeRange(
+        const char* codecName,
+        sp<AMessage> details,
+        std::pair<int32_t, int32_t>* minSize,
+        std::pair<int32_t, int32_t>* maxSize,
+        FrameRateMaps* frameRateMaps) {
+    if (codecName == nullptr || minSize == nullptr || maxSize == nullptr ||
+            details == nullptr || frameRateMaps == nullptr) {
+        return BAD_VALUE;
+    }
+
+    AString sizeRange;
+    auto hasItem = details->findString("size-range", &sizeRange);
+    if (!hasItem) {
+        ALOGE("%s: Failed to query size range for codec %s", __FUNCTION__, codecName);
+        return BAD_VALUE;
+    }
+    ALOGV("%s: %s codec's size range is %s", __FUNCTION__, codecName, sizeRange.c_str());
+    std::regex pattern("([0-9]+)[*x]([0-9]+)-([0-9]+)[*x]([0-9]+)");
+    std::cmatch match;
+    if (std::regex_match(sizeRange.c_str(), match, pattern)) {
+        if (match.size() == 5) {
+            minSize->first = stoi(match[1]);
+            minSize->second = stoi(match[2]);
+            maxSize->first = stoi(match[3]);
+            maxSize->second = stoi(match[4]);
+            if (minSize->first > maxSize->first ||
+                    minSize->second > maxSize->second) {
+                ALOGE("%s: Invalid %s code size range: %s",
+                        __FUNCTION__, codecName, sizeRange.c_str());
+                return BAD_VALUE;
+            }
+        } else {
+            return BAD_VALUE;
+        }
+    }
+
+    auto res = getFrameRateMaps(details, frameRateMaps);
+    if (res != OK) {
+        return res;
+    }
+
+    return OK;
+}
+
+HeicEncoderInfoManager::FrameRateMaps::const_iterator HeicEncoderInfoManager::findClosestSize(
+        const FrameRateMaps& maps, int32_t width, int32_t height) const {
+    int32_t minDiff = INT32_MAX;
+    FrameRateMaps::const_iterator closestIter = maps.begin();
+    for (auto iter = maps.begin(); iter != maps.end(); iter++) {
+        // Use area difference between the sizes to approximate size
+        // difference.
+        int32_t diff = abs(iter->first.first * iter->first.second - width * height);
+        if (diff < minDiff) {
+            closestIter = iter;
+            minDiff = diff;
+        }
+    }
+    return closestIter;
+}
+
+sp<AMessage> HeicEncoderInfoManager::getCodecDetails(
+        sp<IMediaCodecList> codecsList, const char* name) {
+    ssize_t idx = codecsList->findCodecByType(name, true /*encoder*/);
+    if (idx < 0) {
+        return nullptr;
+    }
+
+    const sp<MediaCodecInfo> info = codecsList->getCodecInfo(idx);
+    if (info == nullptr) {
+        ALOGE("%s: Failed to get codec info for %s", __FUNCTION__, name);
+        return nullptr;
+    }
+    const sp<MediaCodecInfo::Capabilities> caps =
+            info->getCapabilitiesFor(name);
+    if (caps == nullptr) {
+        ALOGE("%s: Failed to get capabilities for codec %s", __FUNCTION__, name);
+        return nullptr;
+    }
+    const sp<AMessage> details = caps->getDetails();
+    if (details == nullptr) {
+        ALOGE("%s: Failed to get details for codec %s", __FUNCTION__, name);
+        return nullptr;
+    }
+
+    return details;
+}
+} //namespace camera3
+} // namespace android