Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 92f90635 authored by Yin-Chia Yeh's avatar Yin-Chia Yeh Committed by Android (Google) Code Review
Browse files

Merge "Camera: add external camera fps control"

parents a8f16445 3aa9ae97
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -107,7 +107,6 @@ Return<void> CameraDeviceSession::configureStreams_3_4(

    const camera_metadata_t *paramBuffer = nullptr;
    if (0 < requestedConfiguration.sessionParams.size()) {
        ::android::hardware::camera::common::V1_0::helper::CameraMetadata sessionParams;
        V3_2::implementation::convertFromHidl(requestedConfiguration.sessionParams, &paramBuffer);
    }

+121 −73
Original line number Diff line number Diff line
@@ -638,12 +638,6 @@ status_t ExternalCameraDevice::initOutputCharsKeys(int fd,
        }
    }

    // The document in aeAvailableTargetFpsRanges section says the minFps should
    // not be larger than 15.
    // We cannot support fixed 30fps but Android requires (min, max) and
    // (max, max) ranges.
    // TODO: populate more, right now this does not support 30,30 if the device
    //       has higher than 30 fps modes
    std::vector<int32_t> fpsRanges;
    // Variable range
    fpsRanges.push_back(minFps);
@@ -693,7 +687,7 @@ status_t ExternalCameraDevice::initOutputCharsKeys(int fd,
#undef UPDATE

void ExternalCameraDevice::getFrameRateList(
        int fd, float fpsUpperBound, SupportedV4L2Format* format) {
        int fd, double fpsUpperBound, SupportedV4L2Format* format) {
    format->frameRates.clear();

    v4l2_frmivalenum frameInterval {
@@ -715,7 +709,7 @@ void ExternalCameraDevice::getFrameRateList(
                if (framerate > fpsUpperBound) {
                    continue;
                }
                ALOGI("index:%d, format:%c%c%c%c, w %d, h %d, framerate %f",
                ALOGV("index:%d, format:%c%c%c%c, w %d, h %d, framerate %f",
                    frameInterval.index,
                    frameInterval.pixel_format & 0xFF,
                    (frameInterval.pixel_format >> 8) & 0xFF,
@@ -738,71 +732,68 @@ void ExternalCameraDevice::getFrameRateList(
    }
}

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;
void ExternalCameraDevice::trimSupportedFormats(
        CroppingType cropType,
        /*inout*/std::vector<SupportedV4L2Format>* pFmts) {
    std::vector<SupportedV4L2Format>& sortedFmts = *pFmts;
    if (cropType == VERTICAL) {
        std::sort(sortedFmts.begin(), sortedFmts.end(),
                [](const SupportedV4L2Format& a, const SupportedV4L2Format& b) -> bool {
                    if (a.width == b.width) {
                        return a.height < b.height;
                    }
        if (ar > maxAr) {
            maxAr = ar;
                    return a.width < b.width;
                });
    } else {
        std::sort(sortedFmts.begin(), sortedFmts.end(),
                [](const SupportedV4L2Format& a, const SupportedV4L2Format& b) -> bool {
                    if (a.height == b.height) {
                        return a.width < b.width;
                    }
                    return a.height < b.height;
                });
    }

    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;
    if (sortedFmts.size() == 0) {
        ALOGE("%s: input format list is empty!", __FUNCTION__);
        return;
    }

    const auto& maxSize = sortedFmts[sortedFmts.size() - 1];
    float maxSizeAr = ASPECT_RATIO(maxSize);

    // 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) {
        } else if (cropType == HORIZONTAL && ar < maxSizeAr) {
            out.push_back(fmt);
            } else if (ct == VERTICAL && ar > maxSizeAr) {
        } else if (cropType == VERTICAL && ar > maxSizeAr) {
            out.push_back(fmt);
        } else {
                ALOGD("%s: size (%d,%d) is removed due to unable to crop %s from (%d,%d)",
            ALOGV("%s: size (%d,%d) is removed due to unable to crop %s from (%d,%d)",
                __FUNCTION__, fmt.width, fmt.height,
                    ct == VERTICAL ? "vertically" : "horizontally",
                cropType == 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) {
std::vector<SupportedV4L2Format>
ExternalCameraDevice::getCandidateSupportedFormatsLocked(
        int fd, CroppingType cropType,
        const std::vector<ExternalCameraConfig::FpsLimitation>& fpsLimits) {
    std::vector<SupportedV4L2Format> outFmts;
    struct v4l2_fmtdesc fmtdesc {
        .index = 0,
        .type = V4L2_BUF_TYPE_VIDEO_CAPTURE};
    int ret = 0;
    while (ret == 0) {
        ret = TEMP_FAILURE_RETRY(ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc));
        ALOGD("index:%d,ret:%d, format:%c%c%c%c", fmtdesc.index, ret,
        ALOGV("index:%d,ret:%d, format:%c%c%c%c", fmtdesc.index, ret,
                fmtdesc.pixelformat & 0xFF,
                (fmtdesc.pixelformat >> 8) & 0xFF,
                (fmtdesc.pixelformat >> 16) & 0xFF,
@@ -835,21 +826,28 @@ void ExternalCameraDevice::initSupportedFormatsLocked(int fd) {
                            .fourcc = fmtdesc.pixelformat
                        };

                        float fpsUpperBound = -1.0;
                        for (const auto& limit : mCfg.fpsLimits) {
                            if (format.width <= limit.size.width &&
                                    format.height <= limit.size.height) {
                        double fpsUpperBound = -1.0;
                        for (const auto& limit : fpsLimits) {
                            if (cropType == VERTICAL) {
                                if (format.width <= limit.size.width) {
                                    fpsUpperBound = limit.fpsUpperBound;
                                    break;
                                }
                            } else { // HORIZONTAL
                                if (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);
                            outFmts.push_back(format);
                        }
                    }
                }
@@ -857,16 +855,66 @@ void ExternalCameraDevice::initSupportedFormatsLocked(int fd) {
        }
        fmtdesc.index++;
    }
    trimSupportedFormats(cropType, &outFmts);
    return outFmts;
}

    std::sort(mSupportedFormats.begin(), mSupportedFormats.end(),
            [](const SupportedV4L2Format& a, const SupportedV4L2Format& b) -> bool {
                if (a.width == b.width) {
                    return a.height < b.height;
void ExternalCameraDevice::initSupportedFormatsLocked(int fd) {

    std::vector<SupportedV4L2Format> horizontalFmts =
            getCandidateSupportedFormatsLocked(fd, HORIZONTAL, mCfg.fpsLimits);
    std::vector<SupportedV4L2Format> verticalFmts =
            getCandidateSupportedFormatsLocked(fd, VERTICAL, mCfg.fpsLimits);

    size_t horiSize = horizontalFmts.size();
    size_t vertSize = verticalFmts.size();

    if (horiSize == 0 && vertSize == 0) {
        ALOGE("%s: cannot find suitable cropping type!", __FUNCTION__);
        return;
    }
                return a.width < b.width;
            });

    mCroppingType = initCroppingType(&mSupportedFormats);
    if (horiSize == 0) {
        mSupportedFormats = verticalFmts;
        mCroppingType = VERTICAL;
        return;
    } else if (vertSize == 0) {
        mSupportedFormats = horizontalFmts;
        mCroppingType = HORIZONTAL;
        return;
    }

    const auto& maxHoriSize = horizontalFmts[horizontalFmts.size() - 1];
    const auto& maxVertSize = verticalFmts[verticalFmts.size() - 1];

    // Try to keep largest possible output size
    // When they are the same or ambiguous, pick the one support more sizes
    if (maxHoriSize.width == maxVertSize.width &&
            maxHoriSize.height == maxVertSize.height) {
        if (horiSize > vertSize) {
            mSupportedFormats = horizontalFmts;
            mCroppingType = HORIZONTAL;
        } else {
            mSupportedFormats = verticalFmts;
            mCroppingType = VERTICAL;
        }
    } else if (maxHoriSize.width >= maxVertSize.width &&
            maxHoriSize.height >= maxVertSize.height) {
        mSupportedFormats = horizontalFmts;
        mCroppingType = HORIZONTAL;
    } else if (maxHoriSize.width <= maxVertSize.width &&
            maxHoriSize.height <= maxVertSize.height) {
        mSupportedFormats = verticalFmts;
        mCroppingType = VERTICAL;
    } else {
        if (horiSize > vertSize) {
            mSupportedFormats = horizontalFmts;
            mCroppingType = HORIZONTAL;
        } else {
            mSupportedFormats = verticalFmts;
            mCroppingType = VERTICAL;
        }
    }
}

}  // namespace implementation
+131 −56
Original line number Diff line number Diff line
@@ -160,7 +160,6 @@ void ExternalCameraDeviceSession::dumpState(const native_handle_t* handle) {
    SupportedV4L2Format streamingFmt;
    std::unordered_set<uint32_t>  inflightFrames;
    {
        Mutex::Autolock _l(mLock);
        bool sessionLocked = tryLock(mLock);
        if (!sessionLocked) {
            dprintf(fd, "!! ExternalCameraDeviceSession mLock may be deadlocked !!\n");
@@ -180,12 +179,13 @@ void ExternalCameraDeviceSession::dumpState(const native_handle_t* handle) {
            streaming ? "streaming" : "not streaming");
    if (streaming) {
        // TODO: dump fps later
        dprintf(fd, "Current V4L2 format %c%c%c%c %dx%d\n",
        dprintf(fd, "Current V4L2 format %c%c%c%c %dx%d @ %ffps\n",
                streamingFmt.fourcc & 0xFF,
                (streamingFmt.fourcc >> 8) & 0xFF,
                (streamingFmt.fourcc >> 16) & 0xFF,
                (streamingFmt.fourcc >> 24) & 0xFF,
                streamingFmt.width, streamingFmt.height);
                streamingFmt.width, streamingFmt.height,
                mV4l2StreamingFps);

        size_t numDequeuedV4l2Buffers = 0;
        {
@@ -291,7 +291,6 @@ Return<void> ExternalCameraDeviceSession::configureStreams_3_4(
        config_v32.streams[i] = requestedConfiguration.streams[i].v3_2;
    }

    // Ignore requestedConfiguration.sessionParams. External camera does not support it
    Status status = configureStreams(config_v32, &outStreams_v33);

    V3_4::HalStreamConfiguration outStreams;
@@ -451,6 +450,23 @@ void ExternalCameraDeviceSession::cleanupInflightFences(
    }
}

int ExternalCameraDeviceSession::waitForV4L2BufferReturnLocked(std::unique_lock<std::mutex>& lk) {
    std::chrono::seconds timeout = std::chrono::seconds(kBufferWaitTimeoutSec);
    mLock.unlock();
    auto st = mV4L2BufferReturned.wait_for(lk, timeout);
    // Here we introduce a order where mV4l2BufferLock is acquired before mLock, while
    // the normal lock acquisition order is reversed. This is fine because in most of
    // cases we are protected by mInterfaceLock. The only thread that can cause deadlock
    // is the OutputThread, where we do need to make sure we don't acquire mLock then
    // mV4l2BufferLock
    mLock.lock();
    if (st == std::cv_status::timeout) {
        ALOGE("%s: wait for V4L2 buffer return timeout!", __FUNCTION__);
        return -1;
    }
    return 0;
}

Status ExternalCameraDeviceSession::processOneCaptureRequest(const CaptureRequest& request)  {
    Status status = initStatus();
    if (status != Status::OK) {
@@ -510,15 +526,59 @@ Status ExternalCameraDeviceSession::processOneCaptureRequest(const CaptureReques
        return Status::ILLEGAL_ARGUMENT;
    }

    camera_metadata_entry fpsRange = mLatestReqSetting.find(ANDROID_CONTROL_AE_TARGET_FPS_RANGE);
    if (fpsRange.count == 2) {
        double requestFpsMax = fpsRange.data.i32[1];
        double closestFps = 0.0;
        double fpsError = 1000.0;
        bool fpsSupported = false;
        for (const auto& fr : mV4l2StreamingFmt.frameRates) {
            double f = fr.getDouble();
            if (std::fabs(requestFpsMax - f) < 1.0) {
                fpsSupported = true;
                break;
            }
            if (std::fabs(requestFpsMax - f) < fpsError) {
                fpsError = std::fabs(requestFpsMax - f);
                closestFps = f;
            }
        }
        if (!fpsSupported) {
            /* This can happen in a few scenarios:
             * 1. The application is sending a FPS range not supported by the configured outputs.
             * 2. The application is sending a valid FPS range for all cofigured outputs, but
             *    the selected V4L2 size can only run at slower speed. This should be very rare
             *    though: for this to happen a sensor needs to support at least 3 different aspect
             *    ratio outputs, and when (at least) two outputs are both not the main aspect ratio
             *    of the webcam, a third size that's larger might be picked and runs into this
             *    issue.
             */
            ALOGW("%s: cannot reach fps %d! Will do %f instead",
                    __FUNCTION__, fpsRange.data.i32[1], closestFps);
            requestFpsMax = closestFps;
        }

        if (requestFpsMax != mV4l2StreamingFps) {
            {
                std::unique_lock<std::mutex> lk(mV4l2BufferLock);
                while (mNumDequeuedV4l2Buffers != 0) {
                    // Wait until pipeline is idle before reconfigure stream
                    int waitRet = waitForV4L2BufferReturnLocked(lk);
                    if (waitRet != 0) {
                        ALOGE("%s: wait for pipeline idle failed!", __FUNCTION__);
                        return Status::INTERNAL_ERROR;
                    }
                }
            }
            configureV4l2StreamLocked(mV4l2StreamingFmt, requestFpsMax);
        }
    }

    status = importRequest(request, allBufPtrs, allFences);
    if (status != Status::OK) {
        return status;
    }

    // TODO: program fps range per capture request here
    //       or limit the set of availableFpsRange


    nsecs_t shutterTs = 0;
    sp<V4L2Frame> frameIn = dequeueV4l2FrameLocked(&shutterTs);
    if ( frameIn == nullptr) {
@@ -1979,7 +2039,46 @@ int ExternalCameraDeviceSession::v4l2StreamOffLocked() {
    return OK;
}

int ExternalCameraDeviceSession::configureV4l2StreamLocked(const SupportedV4L2Format& v4l2Fmt) {
int ExternalCameraDeviceSession::setV4l2FpsLocked(double fps) {
    // VIDIOC_G_PARM/VIDIOC_S_PARM: set fps
    v4l2_streamparm streamparm = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
    // The following line checks that the driver knows about framerate get/set.
    int ret = TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_G_PARM, &streamparm));
    if (ret != 0) {
        if (errno == -EINVAL) {
            ALOGW("%s: device does not support VIDIOC_G_PARM", __FUNCTION__);
        }
        return -errno;
    }
    // Now check if the device is able to accept a capture framerate set.
    if (!(streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME)) {
        ALOGW("%s: device does not support V4L2_CAP_TIMEPERFRAME", __FUNCTION__);
        return -EINVAL;
    }

    // fps is float, approximate by a fraction.
    const int kFrameRatePrecision = 10000;
    streamparm.parm.capture.timeperframe.numerator = kFrameRatePrecision;
    streamparm.parm.capture.timeperframe.denominator =
        (fps * kFrameRatePrecision);

    if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_S_PARM, &streamparm)) < 0) {
        ALOGE("%s: failed to set framerate to %f: %s", __FUNCTION__, fps, strerror(errno));
        return -1;
    }

    double retFps = streamparm.parm.capture.timeperframe.denominator /
            static_cast<double>(streamparm.parm.capture.timeperframe.numerator);
    if (std::fabs(fps - retFps) > 1.0) {
        ALOGE("%s: expect fps %f, got %f instead", __FUNCTION__, fps, retFps);
        return -1;
    }
    mV4l2StreamingFps = fps;
    return 0;
}

int ExternalCameraDeviceSession::configureV4l2StreamLocked(
        const SupportedV4L2Format& v4l2Fmt, double requestFps) {
    int ret = v4l2StreamOffLocked();
    if (ret != OK) {
        ALOGE("%s: stop v4l2 streaming failed: ret %d", __FUNCTION__, ret);
@@ -2016,9 +2115,12 @@ int ExternalCameraDeviceSession::configureV4l2StreamLocked(const SupportedV4L2Fo
    uint32_t bufferSize = fmt.fmt.pix.sizeimage;
    ALOGI("%s: V4L2 buffer size is %d", __FUNCTION__, bufferSize);

    float maxFps = -1.f;
    float fps = 1000.f;
    const float kDefaultFps = 30.f;
    const double kDefaultFps = 30.0;
    double fps = 1000.0;
    if (requestFps != 0.0) {
        fps = requestFps;
    } else {
        double maxFps = -1.0;
        // Try to pick the slowest fps that is at least 30
        for (const auto& fr : v4l2Fmt.frameRates) {
            double f = fr.getDouble();
@@ -2029,33 +2131,15 @@ int ExternalCameraDeviceSession::configureV4l2StreamLocked(const SupportedV4L2Fo
                fps = f;
            }
        }
    if (fps == 1000.f) {
        if (fps == 1000.0) {
            fps = maxFps;
        }

    // VIDIOC_G_PARM/VIDIOC_S_PARM: set fps
    v4l2_streamparm streamparm = { .type = V4L2_BUF_TYPE_VIDEO_CAPTURE };
    // The following line checks that the driver knows about framerate get/set.
    if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_G_PARM, &streamparm)) >= 0) {
        // Now check if the device is able to accept a capture framerate set.
        if (streamparm.parm.capture.capability & V4L2_CAP_TIMEPERFRAME) {
            // |frame_rate| is float, approximate by a fraction.
            const int kFrameRatePrecision = 10000;
            streamparm.parm.capture.timeperframe.numerator = kFrameRatePrecision;
            streamparm.parm.capture.timeperframe.denominator =
                (fps * kFrameRatePrecision);

            if (TEMP_FAILURE_RETRY(ioctl(mV4l2Fd.get(), VIDIOC_S_PARM, &streamparm)) < 0) {
                ALOGE("%s: failed to set framerate to %f", __FUNCTION__, fps);
                return UNKNOWN_ERROR;
            }
        }
    }
    float retFps = streamparm.parm.capture.timeperframe.denominator /
                streamparm.parm.capture.timeperframe.numerator;
    if (std::fabs(fps - retFps) > std::numeric_limits<float>::epsilon()) {
        ALOGE("%s: expect fps %f, got %f instead", __FUNCTION__, fps, retFps);
        return BAD_VALUE;

    int fpsRet = setV4l2FpsLocked(fps);
    if (fpsRet != 0 && fpsRet != -EINVAL) {
        ALOGE("%s: set fps failed: %s", __FUNCTION__, strerror(fpsRet));
        return fpsRet;
    }

    uint32_t v4lBufferCount = (fps >= kDefaultFps) ?
@@ -2136,17 +2220,8 @@ sp<V4L2Frame> ExternalCameraDeviceSession::dequeueV4l2FrameLocked(/*out*/nsecs_t
    {
        std::unique_lock<std::mutex> lk(mV4l2BufferLock);
        if (mNumDequeuedV4l2Buffers == mV4L2BufferCount) {
            std::chrono::seconds timeout = std::chrono::seconds(kBufferWaitTimeoutSec);
            mLock.unlock();
            auto st = mV4L2BufferReturned.wait_for(lk, timeout);
            // Here we introduce a case where mV4l2BufferLock is acquired before mLock, while
            // the normal lock acquisition order is reversed, but this is fine because in most of
            // cases we are protected by mInterfaceLock. The only thread that can compete these
            // locks are the OutputThread, where we do need to make sure we don't acquire mLock then
            // mV4l2BufferLock
            mLock.lock();
            if (st == std::cv_status::timeout) {
                ALOGE("%s: wait for V4L2 buffer return timeout!", __FUNCTION__);
            int waitRet = waitForV4L2BufferReturnLocked(lk);
            if (waitRet != 0) {
                return ret;
            }
        }
+7 −1
Original line number Diff line number Diff line
@@ -177,8 +177,11 @@ protected:
    status_t initDefaultRequests();
    status_t fillCaptureResult(common::V1_0::helper::CameraMetadata& md, nsecs_t timestamp);
    Status configureStreams(const V3_2::StreamConfiguration&, V3_3::HalStreamConfiguration* out);
    int configureV4l2StreamLocked(const SupportedV4L2Format& fmt);
    // fps = 0.0 means default, which is
    // slowest fps that is at least 30, or fastest fps if 30 is not supported
    int configureV4l2StreamLocked(const SupportedV4L2Format& fmt, double fps = 0.0);
    int v4l2StreamOffLocked();
    int setV4l2FpsLocked(double fps);

    // TODO: change to unique_ptr for better tracking
    sp<V4L2Frame> dequeueV4l2FrameLocked(/*out*/nsecs_t* shutterTs); // Called with mLock hold
@@ -212,6 +215,8 @@ protected:

    ssize_t getJpegBufferSize(uint32_t width, uint32_t height) const;

    int waitForV4L2BufferReturnLocked(std::unique_lock<std::mutex>& lk);

    class OutputThread : public android::Thread {
    public:
        OutputThread(wp<ExternalCameraDeviceSession> parent, CroppingType);
@@ -307,6 +312,7 @@ protected:

    bool mV4l2Streaming = false;
    SupportedV4L2Format mV4l2StreamingFmt;
    double mV4l2StreamingFps = 0.0;
    size_t mV4L2BufferCount = 0;

    static const int kBufferWaitTimeoutSec = 3; // TODO: handle long exposure (or not allowing)
+9 −2
Original line number Diff line number Diff line
@@ -78,7 +78,6 @@ struct ExternalCameraDevice : public ICameraDevice {
    /* End of Methods from ::android::hardware::camera::device::V3_2::ICameraDevice */

protected:
    void getFrameRateList(int fd, float fpsUpperBound, SupportedV4L2Format* format);
    // Init supported w/h/format/fps in mSupportedFormats. Caller still owns fd
    void initSupportedFormatsLocked(int fd);

@@ -92,7 +91,15 @@ protected:
    status_t initOutputCharsKeys(int fd,
            ::android::hardware::camera::common::V1_0::helper::CameraMetadata*);

    static CroppingType initCroppingType(/*inout*/std::vector<SupportedV4L2Format>*);
    static void getFrameRateList(int fd, double fpsUpperBound, SupportedV4L2Format* format);

    // Get candidate supported formats list of input cropping type.
    static std::vector<SupportedV4L2Format> getCandidateSupportedFormatsLocked(
            int fd, CroppingType cropType,
            const std::vector<ExternalCameraConfig::FpsLimitation>& fpsLimits);
    // Trim supported format list by the cropping type. Also sort output formats by width/height
    static void trimSupportedFormats(CroppingType cropType,
            /*inout*/std::vector<SupportedV4L2Format>* pFmts);

    Mutex mLock;
    bool mInitFailed = false;