External camera: add device config file

Also remove sizes cannot be cropped from maximal
size.

Bug: 72261897
Change-Id: Icb50cfa58a12e80be3cacc49569fac90be03c8e5
diff --git a/camera/device/3.4/default/ExternalCameraDevice.cpp b/camera/device/3.4/default/ExternalCameraDevice.cpp
index e6e0ae3..569acfd 100644
--- a/camera/device/3.4/default/ExternalCameraDevice.cpp
+++ b/camera/device/3.4/default/ExternalCameraDevice.cpp
@@ -15,9 +15,10 @@
  */
 
 #define LOG_TAG "ExtCamDev@3.4"
-#define LOG_NDEBUG 0
+//#define LOG_NDEBUG 0
 #include <log/log.h>
 
+#include <algorithm>
 #include <array>
 #include <linux/videodev2.h>
 #include "android-base/macros.h"
@@ -25,7 +26,6 @@
 #include "../../3.2/default/include/convert.h"
 #include "ExternalCameraDevice_3_4.h"
 
-
 namespace android {
 namespace hardware {
 namespace camera {
@@ -42,16 +42,12 @@
     V4L2_PIX_FMT_MJPEG
 }}; // double braces required in C++11
 
-// TODO: b/72261897
-//       Define max size/fps this Android device can advertise (and streaming at reasonable speed)
-//       Also make sure that can be done without editing source code
-
-// TODO: b/72261675: make it dynamic since this affects memory usage
-const int kMaxJpegSize = {5 * 1024 * 1024};  // 5MB
 } // anonymous namespace
 
 ExternalCameraDevice::ExternalCameraDevice(const std::string& cameraId) :
-        mCameraId(cameraId) {
+        mCameraId(cameraId),
+        mCfg(ExternalCameraDeviceConfig::loadFromCfg()) {
+
     status_t ret = initCameraCharacteristics();
     if (ret != OK) {
         ALOGE("%s: init camera characteristics failed: errorno %d", __FUNCTION__, ret);
@@ -133,7 +129,8 @@
     }
 
     session = new ExternalCameraDeviceSession(
-            callback, mSupportedFormats, mCameraCharacteristics, std::move(fd));
+            callback, mCfg, mSupportedFormats, mCroppingType,
+            mCameraCharacteristics, std::move(fd));
     if (session == nullptr) {
         ALOGE("%s: camera device session allocation failed", __FUNCTION__);
         mLock.unlock();
@@ -283,7 +280,7 @@
     UPDATE(ANDROID_JPEG_AVAILABLE_THUMBNAIL_SIZES, jpegAvailableThumbnailSizes,
            ARRAY_SIZE(jpegAvailableThumbnailSizes));
 
-    const int32_t jpegMaxSize = kMaxJpegSize;
+    const int32_t jpegMaxSize = mCfg.maxJpegBufSize;
     UPDATE(ANDROID_JPEG_MAX_SIZE, &jpegMaxSize, 1);
 
     const uint8_t jpegQuality = 90;
@@ -692,7 +689,7 @@
 #undef UPDATE
 
 void ExternalCameraDevice::getFrameRateList(
-        int fd, SupportedV4L2Format* format) {
+        int fd, float fpsUpperBound, SupportedV4L2Format* format) {
     format->frameRates.clear();
 
     v4l2_frmivalenum frameInterval {
@@ -709,7 +706,10 @@
             if (frameInterval.discrete.numerator != 0) {
                 float framerate = frameInterval.discrete.denominator /
                         static_cast<float>(frameInterval.discrete.numerator);
-                ALOGV("index:%d, format:%c%c%c%c, w %d, h %d, framerate %f",
+                if (framerate > fpsUpperBound) {
+                    continue;
+                }
+                ALOGI("index:%d, format:%c%c%c%c, w %d, h %d, framerate %f",
                     frameInterval.index,
                     frameInterval.pixel_format & 0xFF,
                     (frameInterval.pixel_format >> 8) & 0xFF,
@@ -732,6 +732,63 @@
     }
 }
 
+CroppingType ExternalCameraDevice::initCroppingType(
+        /*inout*/std::vector<SupportedV4L2Format>* pSortedFmts) {
+    std::vector<SupportedV4L2Format>& sortedFmts = *pSortedFmts;
+    const auto& maxSize = sortedFmts[sortedFmts.size() - 1];
+    float maxSizeAr = ASPECT_RATIO(maxSize);
+    float minAr = kMaxAspectRatio;
+    float maxAr = kMinAspectRatio;
+    for (const auto& fmt : sortedFmts) {
+        float ar = ASPECT_RATIO(fmt);
+        if (ar < minAr) {
+            minAr = ar;
+        }
+        if (ar > maxAr) {
+            maxAr = ar;
+        }
+    }
+
+    CroppingType ct = VERTICAL;
+    if (isAspectRatioClose(maxSizeAr, maxAr)) {
+        // Ex: 16:9 sensor, cropping horizontally to get to 4:3
+        ct = HORIZONTAL;
+    } else if (isAspectRatioClose(maxSizeAr, minAr)) {
+        // Ex: 4:3 sensor, cropping vertically to get to 16:9
+        ct = VERTICAL;
+    } else {
+        ALOGI("%s: camera maxSizeAr %f is not close to minAr %f or maxAr %f",
+                __FUNCTION__, maxSizeAr, minAr, maxAr);
+        if ((maxSizeAr - minAr) < (maxAr - maxSizeAr)) {
+            ct = VERTICAL;
+        } else {
+            ct = HORIZONTAL;
+        }
+
+        // Remove formats that has aspect ratio not croppable from largest size
+        std::vector<SupportedV4L2Format> out;
+        for (const auto& fmt : sortedFmts) {
+            float ar = ASPECT_RATIO(fmt);
+            if (isAspectRatioClose(ar, maxSizeAr)) {
+                out.push_back(fmt);
+            } else if (ct == HORIZONTAL && ar < maxSizeAr) {
+                out.push_back(fmt);
+            } else if (ct == VERTICAL && ar > maxSizeAr) {
+                out.push_back(fmt);
+            } else {
+                ALOGD("%s: size (%d,%d) is removed due to unable to crop %s from (%d,%d)",
+                    __FUNCTION__, fmt.width, fmt.height,
+                    ct == VERTICAL ? "vertically" : "horizontally",
+                    maxSize.width, maxSize.height);
+            }
+        }
+        sortedFmts = out;
+    }
+    ALOGI("%s: camera croppingType is %s", __FUNCTION__,
+            ct == VERTICAL ? "VERTICAL" : "HORIZONTAL");
+    return ct;
+}
+
 void ExternalCameraDevice::initSupportedFormatsLocked(int fd) {
     struct v4l2_fmtdesc fmtdesc {
         .index = 0,
@@ -755,7 +812,7 @@
                 for (; TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frameSize)) == 0;
                         ++frameSize.index) {
                     if (frameSize.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
-                        ALOGD("index:%d, format:%c%c%c%c, w %d, h %d", frameSize.index,
+                        ALOGV("index:%d, format:%c%c%c%c, w %d, h %d", frameSize.index,
                             fmtdesc.pixelformat & 0xFF,
                             (fmtdesc.pixelformat >> 8) & 0xFF,
                             (fmtdesc.pixelformat >> 16) & 0xFF,
@@ -771,7 +828,20 @@
                             .height = frameSize.discrete.height,
                             .fourcc = fmtdesc.pixelformat
                         };
-                        getFrameRateList(fd, &format);
+
+                        float fpsUpperBound = -1.0;
+                        for (const auto& limit : mCfg.fpsLimits) {
+                            if (format.width <= limit.size.width &&
+                                    format.height <= limit.size.height) {
+                                fpsUpperBound = limit.fpsUpperBound;
+                                break;
+                            }
+                        }
+                        if (fpsUpperBound < 0.f) {
+                            continue;
+                        }
+
+                        getFrameRateList(fd, fpsUpperBound, &format);
                         if (!format.frameRates.empty()) {
                             mSupportedFormats.push_back(format);
                         }
@@ -781,6 +851,16 @@
         }
         fmtdesc.index++;
     }
+
+    std::sort(mSupportedFormats.begin(), mSupportedFormats.end(),
+            [](const SupportedV4L2Format& a, const SupportedV4L2Format& b) -> bool {
+                if (a.width == b.width) {
+                    return a.height < b.height;
+                }
+                return a.width < b.width;
+            });
+
+    mCroppingType = initCroppingType(&mSupportedFormats);
 }
 
 }  // namespace implementation