Loading camera/device/3.4/default/CameraDeviceSession.cpp +0 −1 Original line number Diff line number Diff line Loading @@ -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, ¶mBuffer); } Loading camera/device/3.4/default/ExternalCameraDevice.cpp +121 −73 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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 { Loading @@ -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, Loading @@ -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, Loading Loading @@ -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); } } } Loading @@ -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 Loading camera/device/3.4/default/ExternalCameraDeviceSession.cpp +131 −56 Original line number Diff line number Diff line Loading @@ -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"); Loading @@ -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; { Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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); Loading Loading @@ -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(); Loading @@ -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) ? Loading Loading @@ -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; } } Loading camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h +7 −1 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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) Loading camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h +9 −2 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; Loading Loading
camera/device/3.4/default/CameraDeviceSession.cpp +0 −1 Original line number Diff line number Diff line Loading @@ -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, ¶mBuffer); } Loading
camera/device/3.4/default/ExternalCameraDevice.cpp +121 −73 Original line number Diff line number Diff line Loading @@ -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); Loading Loading @@ -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 { Loading @@ -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, Loading @@ -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, Loading Loading @@ -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); } } } Loading @@ -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 Loading
camera/device/3.4/default/ExternalCameraDeviceSession.cpp +131 −56 Original line number Diff line number Diff line Loading @@ -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"); Loading @@ -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; { Loading Loading @@ -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; Loading Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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); Loading Loading @@ -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(); Loading @@ -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) ? Loading Loading @@ -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; } } Loading
camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDeviceSession.h +7 −1 Original line number Diff line number Diff line Loading @@ -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 Loading Loading @@ -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); Loading Loading @@ -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) Loading
camera/device/3.4/default/include/ext_device_v3_4_impl/ExternalCameraDevice_3_4.h +9 −2 Original line number Diff line number Diff line Loading @@ -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); Loading @@ -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; Loading