| /* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| */ |
| |
| #define LOG_TAG "QCameraFOVControl" |
| |
| #include <stdlib.h> |
| #include <cutils/properties.h> |
| #include <utils/Errors.h> |
| #include "QCameraFOVControl.h" |
| #include "QCameraDualCamSettings.h" |
| |
| |
| extern "C" { |
| #include "mm_camera_dbg.h" |
| } |
| |
| namespace qcamera { |
| |
| /*=========================================================================== |
| * FUNCTION : QCameraFOVControl constructor |
| * |
| * DESCRIPTION: class constructor |
| * |
| * PARAMETERS : none |
| * |
| * RETURN : void |
| * |
| *==========================================================================*/ |
| QCameraFOVControl::QCameraFOVControl() |
| { |
| mZoomTranslator = NULL; |
| memset(&mDualCamParams, 0, sizeof(dual_cam_params_t)); |
| memset(&mFovControlConfig, 0, sizeof(fov_control_config_t)); |
| memset(&mFovControlData, 0, sizeof(fov_control_data_t)); |
| memset(&mFovControlResult, 0, sizeof(fov_control_result_t)); |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : QCameraFOVControl destructor |
| * |
| * DESCRIPTION: class destructor |
| * |
| * PARAMETERS : none |
| * |
| * RETURN : void |
| * |
| *==========================================================================*/ |
| QCameraFOVControl::~QCameraFOVControl() |
| { |
| // De-initialize zoom translator lib |
| if (mZoomTranslator && mZoomTranslator->isInitialized()) { |
| mZoomTranslator->deInit(); |
| } |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : create |
| * |
| * DESCRIPTION: This is a static method to create FOV-control object. It calls |
| * private constructor of the class and only returns a valid object |
| * if it can successfully initialize the FOV-control. |
| * |
| * PARAMETERS : |
| * @capsMain : The capabilities for the main camera |
| * @capsAux : The capabilities for the aux camera |
| * |
| * RETURN : Valid object pointer if succeeds |
| * NULL if fails |
| * |
| *==========================================================================*/ |
| QCameraFOVControl* QCameraFOVControl::create( |
| cam_capability_t *capsMainCam, |
| cam_capability_t *capsAuxCam) |
| { |
| QCameraFOVControl *pFovControl = NULL; |
| |
| if (capsMainCam && capsAuxCam) { |
| // Create FOV control object |
| pFovControl = new QCameraFOVControl(); |
| |
| if (pFovControl) { |
| bool success = false; |
| if (pFovControl->validateAndExtractParameters(capsMainCam, capsAuxCam)) { |
| // Based on focal lengths, map main and aux camera to wide and tele |
| if (pFovControl->mDualCamParams.paramsMain.focalLengthMm < |
| pFovControl->mDualCamParams.paramsAux.focalLengthMm) { |
| pFovControl->mFovControlData.camWide = CAM_TYPE_MAIN; |
| pFovControl->mFovControlData.camTele = CAM_TYPE_AUX; |
| pFovControl->mFovControlData.camState = STATE_WIDE; |
| } else { |
| pFovControl->mFovControlData.camWide = CAM_TYPE_AUX; |
| pFovControl->mFovControlData.camTele = CAM_TYPE_MAIN; |
| pFovControl->mFovControlData.camState = STATE_TELE; |
| } |
| |
| // Initialize the master info to main camera |
| pFovControl->mFovControlResult.camMasterPreview = CAM_TYPE_MAIN; |
| pFovControl->mFovControlResult.camMaster3A = CAM_TYPE_MAIN; |
| |
| // Check if LPM is enabled |
| char prop[PROPERTY_VALUE_MAX]; |
| int lpmEnable = 1; |
| property_get("persist.dualcam.lpm.enable", prop, "1"); |
| lpmEnable = atoi(prop); |
| if ((lpmEnable == 0) || (DUALCAM_LPM_ENABLE == 0)) { |
| pFovControl->mFovControlData.lpmEnabled = false; |
| } else { |
| pFovControl->mFovControlData.lpmEnabled = true; |
| } |
| |
| // Open the external zoom translation library if requested |
| if (FOVC_USE_EXTERNAL_ZOOM_TRANSLATOR) { |
| pFovControl->mZoomTranslator = |
| QCameraExtZoomTranslator::create(); |
| if (!pFovControl->mZoomTranslator) { |
| LOGE("Unable to open zoom translation lib"); |
| } |
| } |
| success = true; |
| } |
| |
| if (!success) { |
| LOGE("FOV-control: Failed to create an object"); |
| delete pFovControl; |
| pFovControl = NULL; |
| } |
| } else { |
| LOGE("FOV-control: Failed to allocate memory for FOV-control object"); |
| } |
| } |
| |
| return pFovControl; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : consolidateCapabilities |
| * |
| * DESCRIPTION : Combine the capabilities from main and aux cameras to return |
| * the consolidated capabilities. |
| * |
| * PARAMETERS : |
| * @capsMainCam: Capabilities for the main camera |
| * @capsAuxCam : Capabilities for the aux camera |
| * |
| * RETURN : Consolidated capabilities |
| * |
| *==========================================================================*/ |
| cam_capability_t QCameraFOVControl::consolidateCapabilities( |
| cam_capability_t *capsMainCam, |
| cam_capability_t *capsAuxCam) |
| { |
| cam_capability_t capsConsolidated; |
| memset(&capsConsolidated, 0, sizeof(cam_capability_t)); |
| |
| if ((capsMainCam != NULL) && |
| (capsAuxCam != NULL)) { |
| |
| memcpy(&capsConsolidated, capsMainCam, sizeof(cam_capability_t)); |
| |
| // Consolidate preview sizes |
| uint32_t previewSizesTblCntMain = capsMainCam->preview_sizes_tbl_cnt; |
| uint32_t previewSizesTblCntAux = capsAuxCam->preview_sizes_tbl_cnt; |
| uint32_t previewSizesTblCntFinal = 0; |
| |
| for (uint32_t i = 0; i < previewSizesTblCntMain; ++i) { |
| for (uint32_t j = 0; j < previewSizesTblCntAux; ++j) { |
| if ((capsMainCam->preview_sizes_tbl[i].width == |
| capsAuxCam->preview_sizes_tbl[j].width) && |
| (capsMainCam->preview_sizes_tbl[i].height == |
| capsAuxCam->preview_sizes_tbl[j].height)) { |
| if (previewSizesTblCntFinal != i) { |
| capsConsolidated.preview_sizes_tbl[previewSizesTblCntFinal].width = |
| capsAuxCam->preview_sizes_tbl[j].width; |
| capsConsolidated.preview_sizes_tbl[previewSizesTblCntFinal].height = |
| capsMainCam->preview_sizes_tbl[j].height; |
| } |
| ++previewSizesTblCntFinal; |
| break; |
| } |
| } |
| } |
| capsConsolidated.preview_sizes_tbl_cnt = previewSizesTblCntFinal; |
| |
| // Consolidate video sizes |
| uint32_t videoSizesTblCntMain = capsMainCam->video_sizes_tbl_cnt; |
| uint32_t videoSizesTblCntAux = capsAuxCam->video_sizes_tbl_cnt; |
| uint32_t videoSizesTblCntFinal = 0; |
| |
| for (uint32_t i = 0; i < videoSizesTblCntMain; ++i) { |
| for (uint32_t j = 0; j < videoSizesTblCntAux; ++j) { |
| if ((capsMainCam->video_sizes_tbl[i].width == |
| capsAuxCam->video_sizes_tbl[j].width) && |
| (capsMainCam->video_sizes_tbl[i].height == |
| capsAuxCam->video_sizes_tbl[j].height)) { |
| if (videoSizesTblCntFinal != i) { |
| capsConsolidated.video_sizes_tbl[videoSizesTblCntFinal].width = |
| capsAuxCam->video_sizes_tbl[j].width; |
| capsConsolidated.video_sizes_tbl[videoSizesTblCntFinal].height = |
| capsMainCam->video_sizes_tbl[j].height; |
| } |
| ++videoSizesTblCntFinal; |
| break; |
| } |
| } |
| } |
| capsConsolidated.video_sizes_tbl_cnt = videoSizesTblCntFinal; |
| |
| // Consolidate livesnapshot sizes |
| uint32_t livesnapshotSizesTblCntMain = capsMainCam->livesnapshot_sizes_tbl_cnt; |
| uint32_t livesnapshotSizesTblCntAux = capsAuxCam->livesnapshot_sizes_tbl_cnt; |
| uint32_t livesnapshotSizesTblCntFinal = 0; |
| |
| for (uint32_t i = 0; i < livesnapshotSizesTblCntMain; ++i) { |
| for (uint32_t j = 0; j < livesnapshotSizesTblCntAux; ++j) { |
| if ((capsMainCam->livesnapshot_sizes_tbl[i].width == |
| capsAuxCam->livesnapshot_sizes_tbl[j].width) && |
| (capsMainCam->livesnapshot_sizes_tbl[i].height == |
| capsAuxCam->livesnapshot_sizes_tbl[j].height)) { |
| if (livesnapshotSizesTblCntFinal != i) { |
| capsConsolidated.livesnapshot_sizes_tbl[livesnapshotSizesTblCntFinal].width= |
| capsAuxCam->livesnapshot_sizes_tbl[j].width; |
| capsConsolidated.livesnapshot_sizes_tbl[livesnapshotSizesTblCntFinal].height= |
| capsMainCam->livesnapshot_sizes_tbl[j].height; |
| } |
| ++livesnapshotSizesTblCntFinal; |
| break; |
| } |
| } |
| } |
| capsConsolidated.livesnapshot_sizes_tbl_cnt = livesnapshotSizesTblCntFinal; |
| |
| // Consolidate picture size |
| // Find max picture dimension for main camera |
| cam_dimension_t maxPicDimMain; |
| maxPicDimMain.width = 0; |
| maxPicDimMain.height = 0; |
| |
| for(uint32_t i = 0; i < (capsMainCam->picture_sizes_tbl_cnt - 1); ++i) { |
| if ((maxPicDimMain.width * maxPicDimMain.height) < |
| (capsMainCam->picture_sizes_tbl[i].width * |
| capsMainCam->picture_sizes_tbl[i].height)) { |
| maxPicDimMain.width = capsMainCam->picture_sizes_tbl[i].width; |
| maxPicDimMain.height = capsMainCam->picture_sizes_tbl[i].height; |
| } |
| } |
| |
| // Find max picture dimension for aux camera |
| cam_dimension_t maxPicDimAux; |
| maxPicDimAux.width = 0; |
| maxPicDimAux.height = 0; |
| |
| for(uint32_t i = 0; i < (capsAuxCam->picture_sizes_tbl_cnt - 1); ++i) { |
| if ((maxPicDimAux.width * maxPicDimAux.height) < |
| (capsAuxCam->picture_sizes_tbl[i].width * |
| capsAuxCam->picture_sizes_tbl[i].height)) { |
| maxPicDimAux.width = capsAuxCam->picture_sizes_tbl[i].width; |
| maxPicDimAux.height = capsAuxCam->picture_sizes_tbl[i].height; |
| } |
| } |
| |
| LOGH("MAIN Max picture wxh %dx%d", maxPicDimMain.width, maxPicDimMain.height); |
| LOGH("AUX Max picture wxh %dx%d", maxPicDimAux.width, maxPicDimAux.height); |
| |
| // Choose the larger of the two max picture dimensions |
| if ((maxPicDimAux.width * maxPicDimAux.height) > |
| (maxPicDimMain.width * maxPicDimMain.height)) { |
| capsConsolidated.picture_sizes_tbl_cnt = capsAuxCam->picture_sizes_tbl_cnt; |
| memcpy(capsConsolidated.picture_sizes_tbl, capsAuxCam->picture_sizes_tbl, |
| (capsAuxCam->picture_sizes_tbl_cnt * sizeof(cam_dimension_t))); |
| } |
| LOGH("Consolidated Max picture wxh %dx%d", capsConsolidated.picture_sizes_tbl[0].width, |
| capsConsolidated.picture_sizes_tbl[0].height); |
| |
| // Consolidate supported preview formats |
| uint32_t supportedPreviewFmtCntMain = capsMainCam->supported_preview_fmt_cnt; |
| uint32_t supportedPreviewFmtCntAux = capsAuxCam->supported_preview_fmt_cnt; |
| uint32_t supportedPreviewFmtCntFinal = 0; |
| for (uint32_t i = 0; i < supportedPreviewFmtCntMain; ++i) { |
| for (uint32_t j = 0; j < supportedPreviewFmtCntAux; ++j) { |
| if (capsMainCam->supported_preview_fmts[i] == |
| capsAuxCam->supported_preview_fmts[j]) { |
| if (supportedPreviewFmtCntFinal != i) { |
| capsConsolidated.supported_preview_fmts[supportedPreviewFmtCntFinal] = |
| capsAuxCam->supported_preview_fmts[j]; |
| } |
| ++supportedPreviewFmtCntFinal; |
| break; |
| } |
| } |
| } |
| capsConsolidated.supported_preview_fmt_cnt = supportedPreviewFmtCntFinal; |
| |
| // Consolidate supported picture formats |
| uint32_t supportedPictureFmtCntMain = capsMainCam->supported_picture_fmt_cnt; |
| uint32_t supportedPictureFmtCntAux = capsAuxCam->supported_picture_fmt_cnt; |
| uint32_t supportedPictureFmtCntFinal = 0; |
| for (uint32_t i = 0; i < supportedPictureFmtCntMain; ++i) { |
| for (uint32_t j = 0; j < supportedPictureFmtCntAux; ++j) { |
| if (capsMainCam->supported_picture_fmts[i] == |
| capsAuxCam->supported_picture_fmts[j]) { |
| if (supportedPictureFmtCntFinal != i) { |
| capsConsolidated.supported_picture_fmts[supportedPictureFmtCntFinal] = |
| capsAuxCam->supported_picture_fmts[j]; |
| } |
| ++supportedPictureFmtCntFinal; |
| break; |
| } |
| } |
| } |
| capsConsolidated.supported_picture_fmt_cnt = supportedPictureFmtCntFinal; |
| |
| if (mZoomTranslator) { |
| // Copy the opaque calibration data pointer and size |
| mFovControlData.zoomTransInitData.calibData = |
| capsConsolidated.related_cam_calibration.dc_otp_params; |
| mFovControlData.zoomTransInitData.calibDataSize = |
| capsConsolidated.related_cam_calibration.dc_otp_size; |
| } |
| } |
| return capsConsolidated; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : resetVars |
| * |
| * DESCRIPTION : Reset the variables used in FOV-control. |
| * |
| * PARAMETERS : None |
| * |
| * RETURN : None |
| * |
| *==========================================================================*/ |
| void QCameraFOVControl::resetVars() |
| { |
| // Copy the FOV-control settings for camera/camcorder from QCameraFOVControlSettings.h |
| if (mFovControlData.camcorderMode) { |
| mFovControlConfig.snapshotPPConfig.enablePostProcess = |
| FOVC_CAMCORDER_SNAPSHOT_PP_ENABLE; |
| } else { |
| mFovControlConfig.snapshotPPConfig.enablePostProcess = FOVC_CAM_SNAPSHOT_PP_ENABLE; |
| mFovControlConfig.snapshotPPConfig.zoomMin = FOVC_CAM_SNAPSHOT_PP_ZOOM_MIN; |
| mFovControlConfig.snapshotPPConfig.zoomMax = FOVC_CAM_SNAPSHOT_PP_ZOOM_MAX; |
| mFovControlConfig.snapshotPPConfig.luxMin = FOVC_CAM_SNAPSHOT_PP_LUX_MIN; |
| } |
| mFovControlConfig.auxSwitchBrightnessMin = FOVC_AUXCAM_SWITCH_LUX_MIN; |
| mFovControlConfig.auxSwitchFocusDistCmMin = FOVC_AUXCAM_SWITCH_FOCUS_DIST_CM_MIN; |
| |
| mFovControlData.fallbackEnabled = FOVC_MAIN_CAM_FALLBACK_MECHANISM; |
| |
| mFovControlConfig.zoomStableCountThreshold = FOVC_ZOOM_STABLE_COUNT_THRESHOLD; |
| mFovControlConfig.focusDistStableCountThreshold = FOVC_FOCUS_DIST_STABLE_COUNT_THRESHOLD; |
| mFovControlConfig.brightnessStableCountThreshold = FOVC_BRIGHTNESS_STABLE_COUNT_THRESHOLD; |
| |
| // Reset variables |
| mFovControlData.zoomStableCount = 0; |
| mFovControlData.brightnessStableCount = 0; |
| mFovControlData.focusDistStableCount = 0; |
| mFovControlData.zoomDirection = ZOOM_STABLE; |
| mFovControlData.fallbackToWide = false; |
| |
| mFovControlData.status3A.main.af.status = AF_INVALID; |
| mFovControlData.status3A.aux.af.status = AF_INVALID; |
| |
| mFovControlData.afStatusMain = CAM_AF_STATE_INACTIVE; |
| mFovControlData.afStatusAux = CAM_AF_STATE_INACTIVE; |
| |
| mFovControlData.wideCamStreaming = false; |
| mFovControlData.teleCamStreaming = false; |
| |
| mFovControlData.spatialAlignResult.readyStatus = 0; |
| mFovControlData.spatialAlignResult.activeCameras = 0; |
| mFovControlData.spatialAlignResult.camMasterHint = 0; |
| mFovControlData.spatialAlignResult.shiftWide.shiftHorz = 0; |
| mFovControlData.spatialAlignResult.shiftWide.shiftVert = 0; |
| mFovControlData.spatialAlignResult.shiftTele.shiftHorz = 0; |
| mFovControlData.spatialAlignResult.shiftTele.shiftVert = 0; |
| |
| // WA for now until the QTI solution is in place writing the spatial alignment ready status |
| mFovControlData.spatialAlignResult.readyStatus = 1; |
| } |
| |
| /*=========================================================================== |
| * FUNCTION : updateConfigSettings |
| * |
| * DESCRIPTION : Update the config settings such as margins and preview size |
| * and recalculate the transition parameters. |
| * |
| * PARAMETERS : |
| * @capsMainCam: Capabilities for the main camera |
| * @capsAuxCam : Capabilities for the aux camera |
| * |
| * RETURN : |
| * NO_ERROR : Success |
| * INVALID_OPERATION : Failure |
| * |
| *==========================================================================*/ |
| int32_t QCameraFOVControl::updateConfigSettings( |
| parm_buffer_t* paramsMainCam, |
| parm_buffer_t* paramsAuxCam) |
| { |
| int32_t rc = INVALID_OPERATION; |
| |
| if (paramsMainCam && |
| paramsAuxCam && |
| paramsMainCam->is_valid[CAM_INTF_META_STREAM_INFO] && |
| paramsAuxCam->is_valid[CAM_INTF_META_STREAM_INFO]) { |
| |
| cam_stream_size_info_t camMainStreamInfo; |
| READ_PARAM_ENTRY(paramsMainCam, CAM_INTF_META_STREAM_INFO, camMainStreamInfo); |
| mFovControlData.camcorderMode = false; |
| |
| // Identify if in camera or camcorder mode |
| for (int i = 0; i < MAX_NUM_STREAMS; ++i) { |
| if (camMainStreamInfo.type[i] == CAM_STREAM_TYPE_VIDEO) { |
| mFovControlData.camcorderMode = true; |
| } |
| } |
| |
| // Get the margins for the main camera. If video stream is present, the margins correspond |
| // to video stream. Otherwise, margins are copied from preview stream. |
| for (int i = 0; i < MAX_NUM_STREAMS; ++i) { |
| if (camMainStreamInfo.type[i] == CAM_STREAM_TYPE_VIDEO) { |
| mFovControlData.camMainWidthMargin = camMainStreamInfo.margins[i].widthMargins; |
| mFovControlData.camMainHeightMargin = camMainStreamInfo.margins[i].heightMargins; |
| } |
| if (camMainStreamInfo.type[i] == CAM_STREAM_TYPE_PREVIEW) { |
| // Update the preview dimension and ISP output size |
| mFovControlData.previewSize = camMainStreamInfo.stream_sizes[i]; |
| mFovControlData.ispOutSize = camMainStreamInfo.stream_sz_plus_margin[i]; |
| if (!mFovControlData.camcorderMode) { |
| mFovControlData.camMainWidthMargin = |
| camMainStreamInfo.margins[i].widthMargins; |
| mFovControlData.camMainHeightMargin = |
| camMainStreamInfo.margins[i].heightMargins; |
| break; |
| } |
| } |
| } |
| |
| // Get the margins for the aux camera. If video stream is present, the margins correspond |
| // to the video stream. Otherwise, margins are copied from preview stream. |
| cam_stream_size_info_t camAuxStreamInfo; |
| READ_PARAM_ENTRY(paramsAuxCam, CAM_INTF_META_STREAM_INFO, camAuxStreamInfo); |
| for (int i = 0; i < MAX_NUM_STREAMS; ++i) { |
| if (camAuxStreamInfo.type[i] == CAM_STREAM_TYPE_VIDEO) { |
| mFovControlData.camAuxWidthMargin = camAuxStreamInfo.margins[i].widthMargins; |
| mFovControlData.camAuxHeightMargin = camAuxStreamInfo.margins[i].heightMargins; |
| } |
| if (camAuxStreamInfo.type[i] == CAM_STREAM_TYPE_PREVIEW) { |
| // Update the preview dimension |
| mFovControlData.previewSize = camAuxStreamInfo.stream_sizes[i]; |
| if (!mFovControlData.camcorderMode) { |
| mFovControlData.camAuxWidthMargin = camAuxStreamInfo.margins[i].widthMargins; |
| mFovControlData.camAuxHeightMargin = camAuxStreamInfo.margins[i].heightMargins; |
| break; |
| } |
| } |
| } |
| |
| #if 0 // Update to 07.01.01.253.071 |
| // Get the sensor out dimensions |
| cam_dimension_t sensorDimMain = {0,0}; |
| cam_dimension_t sensorDimAux = {0,0}; |
| if (paramsMainCam->is_valid[CAM_INTF_PARM_RAW_DIMENSION]) { |
| READ_PARAM_ENTRY(paramsMainCam, CAM_INTF_PARM_RAW_DIMENSION, sensorDimMain); |
| } |
| if (paramsAuxCam->is_valid[CAM_INTF_PARM_RAW_DIMENSION]) { |
| READ_PARAM_ENTRY(paramsAuxCam, CAM_INTF_PARM_RAW_DIMENSION, sensorDimAux); |
| } |
| #endif // Update to 07.01.01.253.071 |
| |
| // Reset the internal variables |
| resetVars(); |
| |
| // Recalculate the transition parameters |
| if (calculateBasicFovRatio() && combineFovAdjustment()) { |
| |
| calculateDualCamTransitionParams(); |
| |
| // Set initial camera state |
| float zoom = findZoomRatio(mFovControlData.zoomWide) / |
| (float)mFovControlData.zoomRatioTable[0]; |
| if (zoom > mFovControlData.transitionParams.cutOverWideToTele) { |
| mFovControlResult.camMasterPreview = mFovControlData.camTele; |
| mFovControlResult.camMaster3A = mFovControlData.camTele; |
| mFovControlResult.activeCameras = (uint32_t)mFovControlData.camTele; |
| mFovControlData.camState = STATE_TELE; |
| LOGD("start camera state: TELE"); |
| } else { |
| mFovControlResult.camMasterPreview = mFovControlData.camWide; |
| mFovControlResult.camMaster3A = mFovControlData.camWide; |
| mFovControlResult.activeCameras = (uint32_t)mFovControlData.camWide; |
| mFovControlData.camState = STATE_WIDE; |
| LOGD("start camera state: WIDE"); |
| } |
| mFovControlResult.snapshotPostProcess = false; |
| |
| // Deinit zoom translation lib if needed |
| if (mZoomTranslator && mZoomTranslator->isInitialized()) { |
| if (mZoomTranslator->deInit() != NO_ERROR) { |
| ALOGW("deinit failed for zoom translation lib"); |
| } |
| } |
| |
| #if 0 // Update to 07.01.01.253.071 |
| // Initialize the zoom translation lib |
| if (mZoomTranslator) { |
| // Set the initialization data |
| mFovControlData.zoomTransInitData.previewDimension.width = |
| mFovControlData.previewSize.width; |
| mFovControlData.zoomTransInitData.previewDimension.height = |
| mFovControlData.previewSize.height; |
| mFovControlData.zoomTransInitData.ispOutDimension.width = |
| mFovControlData.ispOutSize.width; |
| mFovControlData.zoomTransInitData.ispOutDimension.height = |
| mFovControlData.ispOutSize.height; |
| mFovControlData.zoomTransInitData.sensorOutDimensionMain.width = |
| sensorDimMain.width; |
| mFovControlData.zoomTransInitData.sensorOutDimensionMain.height = |
| sensorDimMain.height; |
| mFovControlData.zoomTransInitData.sensorOutDimensionAux.width = |
| sensorDimAux.width; |
| mFovControlData.zoomTransInitData.sensorOutDimensionAux.height = |
| sensorDimAux.height; |
| mFovControlData.zoomTransInitData.zoomRatioTable = |
| mFovControlData.zoomRatioTable; |
| mFovControlData.zoomTransInitData.zoomRatioTableCount = |
| mFovControlData.zoomRatioTableCount; |
| mFovControlData.zoomTransInitData.mode = mFovControlData.camcorderMode ? |
| MODE_CAMCORDER : MODE_CAMERA; |
| |
| if(mZoomTranslator->init(mFovControlData.zoomTransInitData) != NO_ERROR) { |
| LOGE("init failed for zoom translation lib"); |
| |
| // deinitialize the zoom translator and set to NULL |
| mZoomTranslator->deInit(); |
| mZoomTranslator = NULL; |
| } |
| } |
| #endif // Update to 07.01.01.253.071 |
| |
| // FOV-control config is complete for the current use case |
| mFovControlData.configCompleted = true; |
| rc = NO_ERROR; |
| } |
| } |
| |
| return rc; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : translateInputParams |
| * |
| * DESCRIPTION: Translate a subset of input parameters from main camera. As main |
| * and aux cameras have different properties/params, this translation |
| * is needed before the input parameters are sent to the aux camera. |
| * |
| * PARAMETERS : |
| * @paramsMainCam : Input parameters for main camera |
| * @paramsAuxCam : Input parameters for aux camera |
| * |
| * RETURN : |
| * NO_ERROR : Success |
| * INVALID_OPERATION : Failure |
| * |
| *==========================================================================*/ |
| int32_t QCameraFOVControl::translateInputParams( |
| parm_buffer_t* paramsMainCam, |
| parm_buffer_t* paramsAuxCam) |
| { |
| int32_t rc = INVALID_OPERATION; |
| if (paramsMainCam && paramsAuxCam) { |
| // First copy all the parameters from main to aux and then translate the subset |
| memcpy(paramsAuxCam, paramsMainCam, sizeof(parm_buffer_t)); |
| |
| // Translate zoom |
| if (paramsMainCam->is_valid[CAM_INTF_PARM_ZOOM]) { |
| uint32_t userZoom = 0; |
| READ_PARAM_ENTRY(paramsMainCam, CAM_INTF_PARM_ZOOM, userZoom); |
| convertUserZoomToWideAndTele(userZoom); |
| |
| // Update zoom values in the param buffers |
| uint32_t zoomMain = isMainCamFovWider() ? |
| mFovControlData.zoomWide : mFovControlData.zoomTele; |
| ADD_SET_PARAM_ENTRY_TO_BATCH(paramsMainCam, CAM_INTF_PARM_ZOOM, zoomMain); |
| |
| uint32_t zoomAux = isMainCamFovWider() ? |
| mFovControlData.zoomTele : mFovControlData.zoomWide; |
| ADD_SET_PARAM_ENTRY_TO_BATCH(paramsAuxCam, CAM_INTF_PARM_ZOOM, zoomAux); |
| |
| // Write the user zoom in main and aux param buffers |
| // The user zoom will always correspond to the wider camera |
| paramsMainCam->is_valid[CAM_INTF_PARM_DC_USERZOOM] = 1; |
| paramsAuxCam->is_valid[CAM_INTF_PARM_DC_USERZOOM] = 1; |
| |
| ADD_SET_PARAM_ENTRY_TO_BATCH(paramsMainCam, CAM_INTF_PARM_DC_USERZOOM, |
| mFovControlData.zoomWide); |
| ADD_SET_PARAM_ENTRY_TO_BATCH(paramsAuxCam, CAM_INTF_PARM_DC_USERZOOM, |
| mFovControlData.zoomWide); |
| |
| // Generate FOV-control result |
| generateFovControlResult(); |
| } |
| |
| // Translate focus areas |
| if (paramsMainCam->is_valid[CAM_INTF_PARM_AF_ROI]) { |
| cam_roi_info_t roiAfMain; |
| cam_roi_info_t roiAfAux; |
| READ_PARAM_ENTRY(paramsMainCam, CAM_INTF_PARM_AF_ROI, roiAfMain); |
| if (roiAfMain.num_roi > 0) { |
| roiAfAux = translateFocusAreas(roiAfMain, CAM_TYPE_AUX); |
| roiAfMain = translateFocusAreas(roiAfMain, CAM_TYPE_MAIN); |
| ADD_SET_PARAM_ENTRY_TO_BATCH(paramsAuxCam, CAM_INTF_PARM_AF_ROI, roiAfAux); |
| ADD_SET_PARAM_ENTRY_TO_BATCH(paramsMainCam, CAM_INTF_PARM_AF_ROI, roiAfMain); |
| } |
| } |
| |
| // Translate metering areas |
| if (paramsMainCam->is_valid[CAM_INTF_PARM_AEC_ROI]) { |
| cam_set_aec_roi_t roiAecMain; |
| cam_set_aec_roi_t roiAecAux; |
| READ_PARAM_ENTRY(paramsMainCam, CAM_INTF_PARM_AEC_ROI, roiAecMain); |
| if (roiAecMain.aec_roi_enable == CAM_AEC_ROI_ON) { |
| roiAecAux = translateMeteringAreas(roiAecMain, CAM_TYPE_AUX); |
| roiAecMain = translateMeteringAreas(roiAecMain, CAM_TYPE_MAIN); |
| ADD_SET_PARAM_ENTRY_TO_BATCH(paramsAuxCam, CAM_INTF_PARM_AEC_ROI, roiAecAux); |
| ADD_SET_PARAM_ENTRY_TO_BATCH(paramsMainCam, CAM_INTF_PARM_AEC_ROI, roiAecMain); |
| } |
| } |
| rc = NO_ERROR; |
| } |
| return rc; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : processResultMetadata |
| * |
| * DESCRIPTION: Process the metadata from main and aux cameras to generate the |
| * result metadata. The result metadata should be the metadata |
| * coming from the master camera. If aux camera is master, the |
| * subset of the metadata needs to be translated to main as that's |
| * the only camera seen by the application. |
| * |
| * PARAMETERS : |
| * @metaMain : metadata for main camera |
| * @metaAux : metadata for aux camera |
| * |
| * RETURN : |
| * Result metadata for the logical camera. After successfully processing main |
| * and aux metadata, the result metadata points to either main or aux metadata |
| * based on which one was the master. In case of failure, it returns NULL. |
| *==========================================================================*/ |
| metadata_buffer_t* QCameraFOVControl::processResultMetadata( |
| metadata_buffer_t* metaMain, |
| metadata_buffer_t* metaAux) |
| { |
| metadata_buffer_t* metaResult = NULL; |
| |
| if (metaMain || metaAux) { |
| metadata_buffer_t *meta = metaMain ? metaMain : metaAux; |
| cam_sync_type_t masterCam = mFovControlResult.camMasterPreview; |
| |
| mMutex.lock(); |
| // Book-keep the needed metadata from main camera and aux camera |
| IF_META_AVAILABLE(cam_sac_output_info_t, spatialAlignOutput, |
| CAM_INTF_META_DC_SAC_OUTPUT_INFO, meta) { |
| |
| // Get master camera hint |
| if (spatialAlignOutput->is_master_hint_valid) { |
| uint8_t master = spatialAlignOutput->master_hint; |
| if (master == CAM_ROLE_WIDE) { |
| mFovControlData.spatialAlignResult.camMasterHint = mFovControlData.camWide; |
| } else if (master == CAM_ROLE_TELE) { |
| mFovControlData.spatialAlignResult.camMasterHint = mFovControlData.camTele; |
| } |
| } |
| |
| // Get master camera used for the preview in the frame corresponding to this metadata |
| if (spatialAlignOutput->is_master_preview_valid) { |
| uint8_t master = spatialAlignOutput->master_preview; |
| if (master == CAM_ROLE_WIDE) { |
| masterCam = mFovControlData.camWide; |
| mFovControlData.spatialAlignResult.camMasterPreview = masterCam; |
| } else if (master == CAM_ROLE_TELE) { |
| masterCam = mFovControlData.camTele; |
| mFovControlData.spatialAlignResult.camMasterPreview = masterCam; |
| } |
| } |
| |
| // Get master camera used for 3A in the frame corresponding to this metadata |
| if (spatialAlignOutput->is_master_3A_valid) { |
| uint8_t master = spatialAlignOutput->master_3A; |
| if (master == CAM_ROLE_WIDE) { |
| mFovControlData.spatialAlignResult.camMaster3A = mFovControlData.camWide; |
| } else if (master == CAM_ROLE_TELE) { |
| mFovControlData.spatialAlignResult.camMaster3A = mFovControlData.camTele; |
| } |
| } |
| |
| // Get spatial alignment ready status |
| if (spatialAlignOutput->is_ready_status_valid) { |
| mFovControlData.spatialAlignResult.readyStatus = spatialAlignOutput->ready_status; |
| } |
| } |
| |
| metadata_buffer_t *metaWide = isMainCamFovWider() ? metaMain : metaAux; |
| metadata_buffer_t *metaTele = isMainCamFovWider() ? metaAux : metaMain; |
| |
| // Get spatial alignment output info for wide camera |
| if (metaWide) { |
| IF_META_AVAILABLE(cam_sac_output_info_t, spatialAlignOutput, |
| CAM_INTF_META_DC_SAC_OUTPUT_INFO, metaWide) { |
| // Get spatial alignment output shift for wide camera |
| |
| if (spatialAlignOutput->is_output_shift_valid) { |
| // Calculate the spatial alignment shift for the current stream dimensions based |
| // on the reference resolution used for the output shift. |
| float horzShiftFactor = (float)mFovControlData.previewSize.width / |
| spatialAlignOutput->reference_res_for_output_shift.width; |
| float vertShiftFactor = (float)mFovControlData.previewSize.height / |
| spatialAlignOutput->reference_res_for_output_shift.height; |
| |
| mFovControlData.spatialAlignResult.shiftWide.shiftHorz = |
| spatialAlignOutput->output_shift.shift_horz * horzShiftFactor; |
| mFovControlData.spatialAlignResult.shiftWide.shiftVert = |
| spatialAlignOutput->output_shift.shift_vert * vertShiftFactor; |
| |
| LOGD("SAC output shift for Wide: x:%d, y:%d", |
| mFovControlData.spatialAlignResult.shiftWide.shiftHorz, |
| mFovControlData.spatialAlignResult.shiftWide.shiftVert); |
| } |
| |
| // Get the AF roi shift for wide camera |
| if (spatialAlignOutput->is_focus_roi_shift_valid) { |
| // Calculate the spatial alignment shift for the current stream dimensions based |
| // on the reference resolution used for the output shift. |
| float horzShiftFactor = (float)mFovControlData.previewSize.width / |
| spatialAlignOutput->reference_res_for_focus_roi_shift.width; |
| float vertShiftFactor = (float)mFovControlData.previewSize.height / |
| spatialAlignOutput->reference_res_for_focus_roi_shift.height; |
| |
| mFovControlData.spatialAlignResult.shiftAfRoiWide.shiftHorz = |
| spatialAlignOutput->focus_roi_shift.shift_horz * horzShiftFactor; |
| mFovControlData.spatialAlignResult.shiftAfRoiWide.shiftVert = |
| spatialAlignOutput->focus_roi_shift.shift_vert * vertShiftFactor; |
| |
| LOGD("SAC AF ROI shift for Wide: x:%d, y:%d", |
| mFovControlData.spatialAlignResult.shiftAfRoiWide.shiftHorz, |
| mFovControlData.spatialAlignResult.shiftAfRoiWide.shiftVert); |
| } |
| } |
| } |
| |
| // Get spatial alignment output info for tele camera |
| if (metaTele) { |
| IF_META_AVAILABLE(cam_sac_output_info_t, spatialAlignOutput, |
| CAM_INTF_META_DC_SAC_OUTPUT_INFO, metaTele) { |
| |
| // Get spatial alignment output shift for tele camera |
| if (spatialAlignOutput->is_output_shift_valid) { |
| // Calculate the spatial alignment shift for the current stream dimensions based |
| // on the reference resolution used for the output shift. |
| float horzShiftFactor = (float)mFovControlData.previewSize.width / |
| spatialAlignOutput->reference_res_for_output_shift.width; |
| float vertShiftFactor = (float)mFovControlData.previewSize.height / |
| spatialAlignOutput->reference_res_for_output_shift.height; |
| |
| mFovControlData.spatialAlignResult.shiftTele.shiftHorz = |
| spatialAlignOutput->output_shift.shift_horz * horzShiftFactor; |
| mFovControlData.spatialAlignResult.shiftTele.shiftVert = |
| spatialAlignOutput->output_shift.shift_vert * vertShiftFactor; |
| |
| LOGD("SAC output shift for Tele: x:%d, y:%d", |
| mFovControlData.spatialAlignResult.shiftTele.shiftHorz, |
| mFovControlData.spatialAlignResult.shiftTele.shiftVert); |
| } |
| |
| // Get the AF roi shift for tele camera |
| if (spatialAlignOutput->is_focus_roi_shift_valid) { |
| // Calculate the spatial alignment shift for the current stream dimensions based |
| // on the reference resolution used for the output shift. |
| float horzShiftFactor = (float)mFovControlData.previewSize.width / |
| spatialAlignOutput->reference_res_for_focus_roi_shift.width; |
| float vertShiftFactor = (float)mFovControlData.previewSize.height / |
| spatialAlignOutput->reference_res_for_focus_roi_shift.height; |
| |
| mFovControlData.spatialAlignResult.shiftAfRoiTele.shiftHorz = |
| spatialAlignOutput->focus_roi_shift.shift_horz * horzShiftFactor; |
| mFovControlData.spatialAlignResult.shiftAfRoiTele.shiftVert = |
| spatialAlignOutput->focus_roi_shift.shift_vert * vertShiftFactor; |
| |
| LOGD("SAC AF ROI shift for Tele: x:%d, y:%d", |
| mFovControlData.spatialAlignResult.shiftAfRoiTele.shiftHorz, |
| mFovControlData.spatialAlignResult.shiftAfRoiTele.shiftVert); |
| } |
| } |
| } |
| |
| // Update the camera streaming status |
| if (metaWide) { |
| mFovControlData.wideCamStreaming = true; |
| IF_META_AVAILABLE(uint8_t, enableLPM, CAM_INTF_META_DC_LOW_POWER_ENABLE, metaWide) { |
| if (*enableLPM) { |
| // If LPM enabled is 1, this is probably the last metadata returned |
| // before going into LPM state |
| mFovControlData.wideCamStreaming = false; |
| |
| // Update active cameras requested by spatial alignment |
| mFovControlData.spatialAlignResult.activeCameras &= ~mFovControlData.camWide; |
| } else { |
| mFovControlData.spatialAlignResult.activeCameras |= mFovControlData.camWide; |
| } |
| } |
| } |
| |
| if (metaTele) { |
| mFovControlData.teleCamStreaming = true; |
| IF_META_AVAILABLE(uint8_t, enableLPM, CAM_INTF_META_DC_LOW_POWER_ENABLE, metaTele) { |
| if (*enableLPM) { |
| // If LPM enabled is 1, this is probably the last metadata returned |
| // before going into LPM state |
| mFovControlData.teleCamStreaming = false; |
| |
| // Update active cameras requested by spatial alignment |
| mFovControlData.spatialAlignResult.activeCameras &= ~mFovControlData.camTele; |
| } else { |
| mFovControlData.spatialAlignResult.activeCameras |= mFovControlData.camTele; |
| } |
| } |
| } |
| |
| // Get AF status |
| if (metaMain) { |
| IF_META_AVAILABLE(uint32_t, afState, CAM_INTF_META_AF_STATE, metaMain) { |
| if ((*afState) != CAM_AF_STATE_INACTIVE) { |
| mFovControlData.status3A.main.af.status = AF_VALID; |
| } else { |
| mFovControlData.status3A.main.af.status = AF_INVALID; |
| } |
| mFovControlData.afStatusMain = *afState; |
| LOGD("AF state: Main cam: %d", mFovControlData.afStatusMain); |
| } |
| |
| IF_META_AVAILABLE(float, luxIndex, CAM_INTF_META_AEC_LUX_INDEX, metaMain) { |
| mFovControlData.status3A.main.ae.luxIndex = *luxIndex; |
| LOGD("Lux Index: Main cam: %f", mFovControlData.status3A.main.ae.luxIndex); |
| } |
| |
| IF_META_AVAILABLE(int32_t, objDist, CAM_INTF_META_AF_OBJ_DIST_CM, metaMain) { |
| mFovControlData.status3A.main.af.focusDistCm = (*objDist < 0) ? 0 : *objDist; |
| LOGD("Obj Dist: Main cam: %d", mFovControlData.status3A.main.af.focusDistCm); |
| } |
| } |
| if (metaAux) { |
| IF_META_AVAILABLE(uint32_t, afState, CAM_INTF_META_AF_STATE, metaAux) { |
| if ((*afState) != CAM_AF_STATE_INACTIVE) { |
| mFovControlData.status3A.aux.af.status = AF_VALID; |
| } else { |
| mFovControlData.status3A.aux.af.status = AF_INVALID; |
| } |
| mFovControlData.afStatusAux = *afState; |
| LOGD("AF state: Aux cam: %d", mFovControlData.afStatusAux); |
| } |
| |
| IF_META_AVAILABLE(float, luxIndex, CAM_INTF_META_AEC_LUX_INDEX, metaAux) { |
| mFovControlData.status3A.aux.ae.luxIndex = *luxIndex; |
| LOGD("Lux Index: Aux cam: %f", mFovControlData.status3A.aux.ae.luxIndex); |
| } |
| |
| IF_META_AVAILABLE(int32_t, objDist, CAM_INTF_META_AF_OBJ_DIST_CM, metaAux) { |
| mFovControlData.status3A.aux.af.focusDistCm = (*objDist < 0) ? 0 : *objDist; |
| LOGD("Obj Dist: Aux cam: %d", mFovControlData.status3A.aux.af.focusDistCm); |
| } |
| } |
| |
| if ((masterCam == CAM_TYPE_AUX) && metaAux) { |
| // Translate face detection ROI from aux camera |
| IF_META_AVAILABLE(cam_face_detection_data_t, metaFD, |
| CAM_INTF_META_FACE_DETECTION, metaAux) { |
| cam_face_detection_data_t metaFDTranslated; |
| metaFDTranslated = translateRoiFD(*metaFD, CAM_TYPE_AUX); |
| ADD_SET_PARAM_ENTRY_TO_BATCH(metaAux, CAM_INTF_META_FACE_DETECTION, |
| metaFDTranslated); |
| } |
| metaResult = metaAux; |
| } |
| else if ((masterCam == CAM_TYPE_MAIN) && metaMain) { |
| // Translate face detection ROI from main camera |
| IF_META_AVAILABLE(cam_face_detection_data_t, metaFD, |
| CAM_INTF_META_FACE_DETECTION, metaMain) { |
| cam_face_detection_data_t metaFDTranslated; |
| metaFDTranslated = translateRoiFD(*metaFD, CAM_TYPE_MAIN); |
| ADD_SET_PARAM_ENTRY_TO_BATCH(metaMain, CAM_INTF_META_FACE_DETECTION, |
| metaFDTranslated); |
| } |
| metaResult = metaMain; |
| } else { |
| // Metadata for the master camera was dropped |
| metaResult = NULL; |
| } |
| |
| // If snapshot postprocess is enabled, consolidate the AF status to be sent to the app |
| // when in the transition state. |
| // Only return focused if both are focused. |
| if ((mFovControlResult.snapshotPostProcess == true) && |
| (mFovControlData.camState == STATE_TRANSITION) && |
| metaResult) { |
| if (((mFovControlData.afStatusMain == CAM_AF_STATE_FOCUSED_LOCKED) || |
| (mFovControlData.afStatusMain == CAM_AF_STATE_NOT_FOCUSED_LOCKED)) && |
| ((mFovControlData.afStatusAux == CAM_AF_STATE_FOCUSED_LOCKED) || |
| (mFovControlData.afStatusAux == CAM_AF_STATE_NOT_FOCUSED_LOCKED))) { |
| // If both indicate focused, return focused. |
| // If either one indicates 'not focused', return 'not focused'. |
| if ((mFovControlData.afStatusMain == CAM_AF_STATE_FOCUSED_LOCKED) && |
| (mFovControlData.afStatusAux == CAM_AF_STATE_FOCUSED_LOCKED)) { |
| ADD_SET_PARAM_ENTRY_TO_BATCH(metaResult, CAM_INTF_META_AF_STATE, |
| CAM_AF_STATE_FOCUSED_LOCKED); |
| } else { |
| ADD_SET_PARAM_ENTRY_TO_BATCH(metaResult, CAM_INTF_META_AF_STATE, |
| CAM_AF_STATE_NOT_FOCUSED_LOCKED); |
| } |
| } else { |
| // If either one indicates passive state or active scan, return that state |
| if ((mFovControlData.afStatusMain != CAM_AF_STATE_FOCUSED_LOCKED) && |
| (mFovControlData.afStatusMain != CAM_AF_STATE_NOT_FOCUSED_LOCKED)) { |
| ADD_SET_PARAM_ENTRY_TO_BATCH(metaResult, CAM_INTF_META_AF_STATE, |
| mFovControlData.afStatusMain); |
| } else { |
| ADD_SET_PARAM_ENTRY_TO_BATCH(metaResult, CAM_INTF_META_AF_STATE, |
| mFovControlData.afStatusAux); |
| } |
| } |
| IF_META_AVAILABLE(uint32_t, afState, CAM_INTF_META_AF_STATE, metaResult) { |
| LOGD("Result AF state: %d", *afState); |
| } |
| } |
| |
| mMutex.unlock(); |
| |
| // Generate FOV-control result only if the result meta is valid |
| if (metaResult) { |
| generateFovControlResult(); |
| } |
| } |
| return metaResult; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : generateFovControlResult |
| * |
| * DESCRIPTION: Generate FOV control result |
| * |
| * PARAMETERS : None |
| * |
| * RETURN : None |
| * |
| *==========================================================================*/ |
| void QCameraFOVControl::generateFovControlResult() |
| { |
| Mutex::Autolock lock(mMutex); |
| |
| float zoom = findZoomRatio(mFovControlData.zoomWide) / (float)mFovControlData.zoomRatioTable[0]; |
| uint32_t zoomWide = mFovControlData.zoomWide; |
| uint32_t zoomWidePrev = mFovControlData.zoomWidePrev; |
| |
| if (mFovControlData.configCompleted == false) { |
| // Return as invalid result if the FOV-control configuration is not yet complete |
| mFovControlResult.isValid = false; |
| return; |
| } |
| |
| // Update previous zoom value |
| mFovControlData.zoomWidePrev = mFovControlData.zoomWide; |
| |
| uint32_t currentBrightness = 0; |
| uint32_t currentFocusDist = 0; |
| |
| if (mFovControlResult.camMasterPreview == CAM_TYPE_MAIN) { |
| currentBrightness = mFovControlData.status3A.main.ae.luxIndex; |
| currentFocusDist = mFovControlData.status3A.main.af.focusDistCm; |
| } else if (mFovControlResult.camMasterPreview == CAM_TYPE_AUX) { |
| currentBrightness = mFovControlData.status3A.aux.ae.luxIndex; |
| currentFocusDist = mFovControlData.status3A.aux.af.focusDistCm; |
| } |
| |
| float transitionLow = mFovControlData.transitionParams.transitionLow; |
| float transitionHigh = mFovControlData.transitionParams.transitionHigh; |
| float cutOverWideToTele = mFovControlData.transitionParams.cutOverWideToTele; |
| float cutOverTeleToWide = mFovControlData.transitionParams.cutOverTeleToWide; |
| |
| cam_sync_type_t camWide = mFovControlData.camWide; |
| cam_sync_type_t camTele = mFovControlData.camTele; |
| |
| uint16_t thresholdBrightness = mFovControlConfig.auxSwitchBrightnessMin; |
| uint16_t thresholdFocusDist = mFovControlConfig.auxSwitchFocusDistCmMin; |
| |
| if (zoomWide == zoomWidePrev) { |
| mFovControlData.zoomDirection = ZOOM_STABLE; |
| ++mFovControlData.zoomStableCount; |
| } else if (zoomWide > zoomWidePrev) { |
| mFovControlData.zoomDirection = ZOOM_IN; |
| mFovControlData.zoomStableCount = 0; |
| } else { |
| mFovControlData.zoomDirection = ZOOM_OUT; |
| mFovControlData.zoomStableCount = 0; |
| } |
| |
| // Update snapshot post-process flags |
| if (mFovControlConfig.snapshotPPConfig.enablePostProcess && |
| (zoom >= mFovControlConfig.snapshotPPConfig.zoomMin) && |
| (zoom <= mFovControlConfig.snapshotPPConfig.zoomMax)) { |
| mFovControlResult.snapshotPostProcessZoomRange = true; |
| } else { |
| mFovControlResult.snapshotPostProcessZoomRange = false; |
| } |
| |
| if (mFovControlResult.snapshotPostProcessZoomRange && |
| (currentBrightness >= mFovControlConfig.snapshotPPConfig.luxMin) && |
| (currentFocusDist >= mFovControlConfig.snapshotPPConfig.focusDistanceMin)) { |
| mFovControlResult.snapshotPostProcess = true; |
| } else { |
| mFovControlResult.snapshotPostProcess = false; |
| } |
| |
| switch (mFovControlData.camState) { |
| case STATE_WIDE: |
| // If the scene continues to be bright, update stable count; reset otherwise |
| if (currentBrightness >= thresholdBrightness) { |
| ++mFovControlData.brightnessStableCount; |
| } else { |
| mFovControlData.brightnessStableCount = 0; |
| } |
| |
| // If the scene continues to be non-macro, update stable count; reset otherwise |
| if (currentFocusDist >= thresholdFocusDist) { |
| ++mFovControlData.focusDistStableCount; |
| } else { |
| mFovControlData.focusDistStableCount = 0; |
| } |
| |
| // Reset fallback to main flag if zoom is less than cutover point |
| if (zoom <= cutOverTeleToWide) { |
| mFovControlData.fallbackToWide = false; |
| } |
| |
| // Check if the scene is good for aux (bright and far focused) |
| if ((currentBrightness >= thresholdBrightness) && |
| (currentFocusDist >= thresholdFocusDist)) { |
| // Lower constraint if zooming in or if snapshot postprocessing is true |
| if (mFovControlResult.snapshotPostProcess || |
| (((zoom >= transitionLow) || |
| (sacRequestedDualZone())) && |
| (mFovControlData.zoomDirection == ZOOM_IN) && |
| (mFovControlData.fallbackToWide == false))) { |
| mFovControlData.camState = STATE_TRANSITION; |
| mFovControlResult.activeCameras = (camWide | camTele); |
| } |
| // Higher constraint if not zooming in |
| else if ((zoom > cutOverWideToTele) && |
| (mFovControlData.brightnessStableCount >= |
| mFovControlConfig.brightnessStableCountThreshold) && |
| (mFovControlData.focusDistStableCount >= |
| mFovControlConfig.focusDistStableCountThreshold)) { |
| // Enter the transition state |
| mFovControlData.camState = STATE_TRANSITION; |
| mFovControlResult.activeCameras = (camWide | camTele); |
| |
| // Reset fallback to wide flag |
| mFovControlData.fallbackToWide = false; |
| |
| // Reset zoom stable count |
| mFovControlData.zoomStableCount = 0; |
| } |
| } |
| break; |
| |
| case STATE_TRANSITION: |
| // Reset brightness stable count |
| mFovControlData.brightnessStableCount = 0; |
| // Reset focus distance stable count |
| mFovControlData.focusDistStableCount = 0; |
| |
| // Set the master info |
| // Switch to wide |
| if ((mFovControlResult.camMasterPreview == camTele) && |
| canSwitchMasterTo(CAM_TYPE_WIDE)) { |
| mFovControlResult.camMasterPreview = camWide; |
| mFovControlResult.camMaster3A = camWide; |
| } |
| // switch to tele |
| else if ((mFovControlResult.camMasterPreview == camWide) && |
| canSwitchMasterTo(CAM_TYPE_TELE)) { |
| mFovControlResult.camMasterPreview = camTele; |
| mFovControlResult.camMaster3A = camTele; |
| } |
| |
| // Change the transition state if necessary. |
| // If fallback to wide is initiated, try to move to wide state |
| if (mFovControlData.fallbackEnabled && mFovControlData.fallbackToWide) { |
| if (mFovControlResult.camMasterPreview == camWide) { |
| mFovControlData.camState = STATE_WIDE; |
| mFovControlResult.activeCameras = camWide; |
| } |
| } |
| // If snapshot post processing is required, do not change the state. |
| else if (mFovControlResult.snapshotPostProcess == false) { |
| if ((zoom < transitionLow) && |
| !sacRequestedDualZone() && |
| (mFovControlResult.camMasterPreview == camWide)) { |
| mFovControlData.camState = STATE_WIDE; |
| mFovControlResult.activeCameras = camWide; |
| } else if ((zoom > transitionHigh) && |
| !sacRequestedDualZone() && |
| (mFovControlResult.camMasterPreview == camTele)) { |
| mFovControlData.camState = STATE_TELE; |
| mFovControlResult.activeCameras = camTele; |
| } else if (mFovControlData.zoomStableCount >= |
| mFovControlConfig.zoomStableCountThreshold) { |
| // If the zoom is stable put the non-master camera to LPM for power optimization |
| if (mFovControlResult.camMasterPreview == camWide) { |
| mFovControlData.camState = STATE_WIDE; |
| mFovControlResult.activeCameras = camWide; |
| } else { |
| mFovControlData.camState = STATE_TELE; |
| mFovControlResult.activeCameras = camTele; |
| } |
| } |
| } |
| break; |
| |
| case STATE_TELE: |
| // If the scene continues to be dark, update stable count; reset otherwise |
| if (currentBrightness < thresholdBrightness) { |
| ++mFovControlData.brightnessStableCount; |
| } else { |
| mFovControlData.brightnessStableCount = 0; |
| } |
| |
| // If the scene continues to be macro, update stable count; reset otherwise |
| if (currentFocusDist < thresholdFocusDist) { |
| ++mFovControlData.focusDistStableCount; |
| } else { |
| mFovControlData.focusDistStableCount = 0; |
| } |
| |
| // Lower constraint if zooming out or if the snapshot postprocessing is true |
| if (mFovControlResult.snapshotPostProcess || |
| (((zoom <= transitionHigh) || sacRequestedDualZone()) && |
| (mFovControlData.zoomDirection == ZOOM_OUT))) { |
| mFovControlData.camState = STATE_TRANSITION; |
| mFovControlResult.activeCameras = (camWide | camTele); |
| } |
| // Higher constraint if not zooming out. Only if fallback is enabled |
| else if (((currentBrightness < thresholdBrightness) || |
| (currentFocusDist < thresholdFocusDist)) && |
| mFovControlData.fallbackEnabled) { |
| // Enter transition state if brightness or focus distance is below threshold |
| if ((mFovControlData.brightnessStableCount >= |
| mFovControlConfig.brightnessStableCountThreshold) || |
| (mFovControlData.focusDistStableCount >= |
| mFovControlConfig.focusDistStableCountThreshold)) { |
| mFovControlData.camState = STATE_TRANSITION; |
| mFovControlResult.activeCameras = (camWide | camTele); |
| |
| // Reset zoom stable count and set fallback flag to true |
| mFovControlData.zoomStableCount = 0; |
| mFovControlData.fallbackToWide = true; |
| LOGD("Low light/Macro scene - fall back to Wide from Tele"); |
| } |
| } |
| break; |
| } |
| |
| // Update snapshot postprocess result based on fall back to wide decision |
| if (mFovControlData.fallbackEnabled && mFovControlData.fallbackToWide) { |
| mFovControlResult.snapshotPostProcess = false; |
| } |
| |
| mFovControlResult.isValid = true; |
| // Debug print for the FOV-control result |
| LOGD("Effective zoom: %f", zoom); |
| LOGD("zoom direction: %d", (uint32_t)mFovControlData.zoomDirection); |
| LOGD("zoomWide: %d, zoomTele: %d", zoomWide, mFovControlData.zoomTele); |
| LOGD("Snapshot postprocess: %d", mFovControlResult.snapshotPostProcess); |
| LOGD("Master camera : %s", (mFovControlResult.camMasterPreview == CAM_TYPE_MAIN) ? |
| "CAM_TYPE_MAIN" : "CAM_TYPE_AUX"); |
| LOGD("Master camera for preview: %s", |
| (mFovControlResult.camMasterPreview == camWide ) ? "Wide" : "Tele"); |
| LOGD("Master camera for 3A : %s", |
| (mFovControlResult.camMaster3A == camWide ) ? "Wide" : "Tele"); |
| LOGD("Wide camera status : %s", |
| (mFovControlResult.activeCameras & camWide) ? "Active" : "LPM"); |
| LOGD("Tele camera status : %s", |
| (mFovControlResult.activeCameras & camTele) ? "Active" : "LPM"); |
| LOGD("transition state: %s", ((mFovControlData.camState == STATE_WIDE) ? "STATE_WIDE" : |
| ((mFovControlData.camState == STATE_TELE) ? "STATE_TELE" : "STATE_TRANSITION" ))); |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : getFovControlResult |
| * |
| * DESCRIPTION: Return FOV-control result |
| * |
| * PARAMETERS : None |
| * |
| * RETURN : FOV-control result |
| * |
| *==========================================================================*/ |
| fov_control_result_t QCameraFOVControl::getFovControlResult() |
| { |
| Mutex::Autolock lock(mMutex); |
| fov_control_result_t fovControlResult = mFovControlResult; |
| return fovControlResult; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : isMainCamFovWider |
| * |
| * DESCRIPTION : Check if the main camera FOV is wider than aux |
| * |
| * PARAMETERS : None |
| * |
| * RETURN : |
| * true : If main cam FOV is wider than tele |
| * false : If main cam FOV is narrower than tele |
| * |
| *==========================================================================*/ |
| inline bool QCameraFOVControl::isMainCamFovWider() |
| { |
| if (mDualCamParams.paramsMain.focalLengthMm < |
| mDualCamParams.paramsAux.focalLengthMm) { |
| return true; |
| } else { |
| return false; |
| } |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : sacRequestedDualZone |
| * |
| * DESCRIPTION : Check if Spatial alignment block requested both the cameras to be active. |
| * The request is valid only when LPM is enabled. |
| * |
| * PARAMETERS : None |
| * |
| * RETURN : |
| * true : If dual zone is requested with LPM enabled |
| * false : If LPM is disabled or if dual zone is not requested with LPM enabled |
| * |
| *==========================================================================*/ |
| inline bool QCameraFOVControl::sacRequestedDualZone() |
| { |
| bool ret = false; |
| cam_sync_type_t camWide = mFovControlData.camWide; |
| cam_sync_type_t camTele = mFovControlData.camTele; |
| |
| // Return true if Spatial alignment block requested both the cameras to be active |
| // in the case of lpm enabled |
| if ((mFovControlData.spatialAlignResult.activeCameras == (camWide | camTele)) && |
| (mFovControlData.lpmEnabled)) { |
| ret = true; |
| } |
| return ret; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : canSwitchMasterTo |
| * |
| * DESCRIPTION : Check if the master can be switched to the camera- cam. |
| * |
| * PARAMETERS : |
| * @cam : cam type |
| * |
| * RETURN : |
| * true : If master can be switched |
| * false : If master cannot be switched |
| * |
| *==========================================================================*/ |
| bool QCameraFOVControl::canSwitchMasterTo( |
| cam_type cam) |
| { |
| bool ret = false; |
| float zoom = findZoomRatio(mFovControlData.zoomWide) / (float)mFovControlData.zoomRatioTable[0]; |
| float cutOverWideToTele = mFovControlData.transitionParams.cutOverWideToTele; |
| float cutOverTeleToWide = mFovControlData.transitionParams.cutOverTeleToWide; |
| af_status afStatusAux = mFovControlData.status3A.aux.af.status; |
| |
| char prop[PROPERTY_VALUE_MAX]; |
| int override = 0; |
| property_get("persist.camera.fovc.override", prop, "0"); |
| override = atoi(prop); |
| if(override) { |
| afStatusAux = AF_VALID; |
| } |
| |
| if (cam == CAM_TYPE_WIDE) { |
| if (mFovControlData.availableSpatialAlignSolns & CAM_SPATIAL_ALIGN_OEM) { |
| // In case of OEM Spatial alignment solution, check the spatial alignment ready |
| if (mFovControlData.wideCamStreaming && isSpatialAlignmentReady()) { |
| ret = true; |
| } |
| } else { |
| // In case of QTI Spatial alignment solution and no spatial alignment solution, |
| // check the fallback flag or if the zoom level has crossed the threhold. |
| if ((mFovControlData.fallbackEnabled && mFovControlData.fallbackToWide) || |
| (zoom < cutOverTeleToWide)) { |
| if (mFovControlData.wideCamStreaming) { |
| ret = true; |
| } |
| } |
| } |
| } else if (cam == CAM_TYPE_TELE) { |
| if (mFovControlData.fallbackEnabled && mFovControlData.fallbackToWide) { |
| // If fallback to wide is initiated, don't switch the master to tele |
| ret = false; |
| } else if (mFovControlData.availableSpatialAlignSolns & CAM_SPATIAL_ALIGN_OEM) { |
| // In case of OEM Spatial alignment solution, check the spatial alignment ready and |
| // af status |
| if (mFovControlData.teleCamStreaming && |
| isSpatialAlignmentReady() && |
| (afStatusAux == AF_VALID)) { |
| ret = true; |
| } |
| } else if (mFovControlData.availableSpatialAlignSolns & CAM_SPATIAL_ALIGN_QTI) { |
| // In case of QTI Spatial alignment solution check the spatial alignment ready flag, |
| // af status and if the zoom level has crossed the threhold. |
| if ((zoom > cutOverWideToTele) && |
| isSpatialAlignmentReady() && |
| (afStatusAux == AF_VALID)) { |
| ret = true; |
| } |
| } else { |
| // In case of no spatial alignment solution check af status and |
| // if the zoom level has crossed the threhold. |
| if ((zoom > cutOverWideToTele) && |
| (afStatusAux == AF_VALID)) { |
| ret = true; |
| } |
| } |
| } else { |
| LOGE("Request to switch to invalid cam type"); |
| } |
| return ret; |
| } |
| |
| /*=========================================================================== |
| * FUNCTION : isSpatialAlignmentReady |
| * |
| * DESCRIPTION : Check if the spatial alignment is ready. |
| * For QTI solution, check ready_status flag |
| * For OEM solution, check camMasterHint |
| * If the spatial alignment solution is not needed, return true |
| * |
| * PARAMETERS : None |
| * |
| * RETURN : |
| * true : If spatial alignment is ready |
| * false : If spatial alignment is not yet ready |
| * |
| *==========================================================================*/ |
| bool QCameraFOVControl::isSpatialAlignmentReady() |
| { |
| bool ret = true; |
| cam_sync_type_t camWide = mFovControlData.camWide; |
| cam_sync_type_t camTele = mFovControlData.camTele; |
| |
| if (mFovControlData.availableSpatialAlignSolns & CAM_SPATIAL_ALIGN_OEM) { |
| uint8_t currentMaster = (uint8_t)mFovControlResult.camMasterPreview; |
| uint8_t camMasterHint = mFovControlData.spatialAlignResult.camMasterHint; |
| |
| if (((currentMaster == camWide) && (camMasterHint == camTele)) || |
| ((currentMaster == camTele) && (camMasterHint == camWide))){ |
| ret = true; |
| } else { |
| ret = false; |
| } |
| } else if (mFovControlData.availableSpatialAlignSolns & CAM_SPATIAL_ALIGN_QTI) { |
| if (mFovControlData.spatialAlignResult.readyStatus) { |
| ret = true; |
| } else { |
| ret = false; |
| } |
| } |
| |
| char prop[PROPERTY_VALUE_MAX]; |
| int override = 0; |
| property_get("persist.camera.fovc.override", prop, "0"); |
| override = atoi(prop); |
| if(override) { |
| ret = true; |
| } |
| |
| return ret; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : validateAndExtractParameters |
| * |
| * DESCRIPTION : Validates a subset of parameters from capabilities and |
| * saves those parameters for decision making. |
| * |
| * PARAMETERS : |
| * @capsMain : The capabilities for the main camera |
| * @capsAux : The capabilities for the aux camera |
| * |
| * RETURN : |
| * true : Success |
| * false : Failure |
| * |
| *==========================================================================*/ |
| bool QCameraFOVControl::validateAndExtractParameters( |
| cam_capability_t *capsMainCam, |
| cam_capability_t *capsAuxCam) |
| { |
| bool rc = false; |
| if (capsMainCam && capsAuxCam) { |
| |
| mFovControlConfig.percentMarginHysterisis = 5; |
| mFovControlConfig.percentMarginMain = 25; |
| mFovControlConfig.percentMarginAux = 15; |
| mFovControlConfig.waitTimeForHandoffMs = 1000; |
| |
| mDualCamParams.paramsMain.sensorStreamWidth = |
| capsMainCam->related_cam_calibration.main_cam_specific_calibration.\ |
| native_sensor_resolution_width; |
| mDualCamParams.paramsMain.sensorStreamHeight = |
| capsMainCam->related_cam_calibration.main_cam_specific_calibration.\ |
| native_sensor_resolution_height; |
| |
| mDualCamParams.paramsAux.sensorStreamWidth = |
| capsMainCam->related_cam_calibration.aux_cam_specific_calibration.\ |
| native_sensor_resolution_width; |
| mDualCamParams.paramsAux.sensorStreamHeight = |
| capsMainCam->related_cam_calibration.aux_cam_specific_calibration.\ |
| native_sensor_resolution_height; |
| |
| mDualCamParams.paramsMain.focalLengthMm = capsMainCam->focal_length; |
| mDualCamParams.paramsAux.focalLengthMm = capsAuxCam->focal_length; |
| |
| mDualCamParams.paramsMain.pixelPitchUm = capsMainCam->pixel_pitch_um; |
| mDualCamParams.paramsAux.pixelPitchUm = capsAuxCam->pixel_pitch_um; |
| |
| if ((capsMainCam->min_focus_distance > 0) && |
| (capsAuxCam->min_focus_distance > 0)) { |
| // Convert the min focus distance from diopters to cm |
| // and choose the max of both sensors. |
| uint32_t minFocusDistCmMain = (100.0f / capsMainCam->min_focus_distance); |
| uint32_t minFocusDistCmAux = (100.0f / capsAuxCam->min_focus_distance); |
| mDualCamParams.minFocusDistanceCm = (minFocusDistCmMain > minFocusDistCmAux) ? |
| minFocusDistCmMain : minFocusDistCmAux; |
| } |
| |
| if (capsMainCam->related_cam_calibration.relative_position_flag == 0) { |
| mDualCamParams.positionAux = CAM_POSITION_RIGHT; |
| } else { |
| mDualCamParams.positionAux = CAM_POSITION_LEFT; |
| } |
| |
| if ((capsMainCam->avail_spatial_align_solns & CAM_SPATIAL_ALIGN_QTI) || |
| (capsMainCam->avail_spatial_align_solns & CAM_SPATIAL_ALIGN_OEM)) { |
| mFovControlData.availableSpatialAlignSolns = |
| capsMainCam->avail_spatial_align_solns; |
| } else { |
| LOGW("Spatial alignment not supported"); |
| } |
| |
| if (capsMainCam->zoom_supported > 0) { |
| mFovControlData.zoomRatioTable = capsMainCam->zoom_ratio_tbl; |
| mFovControlData.zoomRatioTableCount = capsMainCam->zoom_ratio_tbl_cnt; |
| } else { |
| LOGE("zoom feature not supported"); |
| return false; |
| } |
| rc = true; |
| } |
| |
| return rc; |
| } |
| |
| /*=========================================================================== |
| * FUNCTION : calculateBasicFovRatio |
| * |
| * DESCRIPTION: Calculate the FOV ratio between main and aux cameras |
| * |
| * PARAMETERS : None |
| * |
| * RETURN : |
| * true : Success |
| * false : Failure |
| * |
| *==========================================================================*/ |
| bool QCameraFOVControl::calculateBasicFovRatio() |
| { |
| float fovWide = 0.0f; |
| float fovTele = 0.0f; |
| bool rc = false; |
| |
| if ((mDualCamParams.paramsMain.focalLengthMm > 0.0f) && |
| (mDualCamParams.paramsAux.focalLengthMm > 0.0f)) { |
| if (mDualCamParams.paramsMain.focalLengthMm < |
| mDualCamParams.paramsAux.focalLengthMm) { |
| fovWide = (mDualCamParams.paramsMain.sensorStreamWidth * |
| mDualCamParams.paramsMain.pixelPitchUm) / |
| mDualCamParams.paramsMain.focalLengthMm; |
| |
| fovTele = (mDualCamParams.paramsAux.sensorStreamWidth * |
| mDualCamParams.paramsAux.pixelPitchUm) / |
| mDualCamParams.paramsAux.focalLengthMm; |
| } else { |
| fovWide = (mDualCamParams.paramsAux.sensorStreamWidth * |
| mDualCamParams.paramsAux.pixelPitchUm) / |
| mDualCamParams.paramsAux.focalLengthMm; |
| |
| fovTele = (mDualCamParams.paramsMain.sensorStreamWidth * |
| mDualCamParams.paramsMain.pixelPitchUm) / |
| mDualCamParams.paramsMain.focalLengthMm; |
| } |
| if (fovTele > 0.0f) { |
| mFovControlData.basicFovRatio = (fovWide / fovTele); |
| rc = true; |
| } |
| } |
| |
| LOGD("Main cam focalLengthMm : %f", mDualCamParams.paramsMain.focalLengthMm); |
| LOGD("Aux cam focalLengthMm : %f", mDualCamParams.paramsAux.focalLengthMm); |
| LOGD("Main cam sensorStreamWidth : %u", mDualCamParams.paramsMain.sensorStreamWidth); |
| LOGD("Main cam sensorStreamHeight: %u", mDualCamParams.paramsMain.sensorStreamHeight); |
| LOGD("Main cam pixelPitchUm : %f", mDualCamParams.paramsMain.pixelPitchUm); |
| LOGD("Main cam focalLengthMm : %f", mDualCamParams.paramsMain.focalLengthMm); |
| LOGD("Aux cam sensorStreamWidth : %u", mDualCamParams.paramsAux.sensorStreamWidth); |
| LOGD("Aux cam sensorStreamHeight : %u", mDualCamParams.paramsAux.sensorStreamHeight); |
| LOGD("Aux cam pixelPitchUm : %f", mDualCamParams.paramsAux.pixelPitchUm); |
| LOGD("Aux cam focalLengthMm : %f", mDualCamParams.paramsAux.focalLengthMm); |
| LOGD("fov wide : %f", fovWide); |
| LOGD("fov tele : %f", fovTele); |
| LOGD("BasicFovRatio : %f", mFovControlData.basicFovRatio); |
| |
| return rc; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : combineFovAdjustment |
| * |
| * DESCRIPTION: Calculate the final FOV adjustment by combining basic FOV ratio |
| * with the margin info |
| * |
| * PARAMETERS : None |
| * |
| * RETURN : |
| * true : Success |
| * false : Failure |
| * |
| *==========================================================================*/ |
| bool QCameraFOVControl::combineFovAdjustment() |
| { |
| float ratioMarginWidth; |
| float ratioMarginHeight; |
| float adjustedRatio; |
| bool rc = false; |
| |
| ratioMarginWidth = (1.0 + (mFovControlData.camMainWidthMargin)) / |
| (1.0 + (mFovControlData.camAuxWidthMargin)); |
| ratioMarginHeight = (1.0 + (mFovControlData.camMainHeightMargin)) / |
| (1.0 + (mFovControlData.camAuxHeightMargin)); |
| |
| adjustedRatio = (ratioMarginHeight < ratioMarginWidth) ? ratioMarginHeight : ratioMarginWidth; |
| |
| if (adjustedRatio > 0.0f) { |
| mFovControlData.transitionParams.cutOverFactor = |
| (mFovControlData.basicFovRatio / adjustedRatio); |
| rc = true; |
| } |
| |
| LOGD("Main cam margin for width : %f", mFovControlData.camMainWidthMargin); |
| LOGD("Main cam margin for height : %f", mFovControlData.camMainHeightMargin); |
| LOGD("Aux cam margin for width : %f", mFovControlData.camAuxWidthMargin); |
| LOGD("Aux cam margin for height : %f", mFovControlData.camAuxHeightMargin); |
| LOGD("Width margin ratio : %f", ratioMarginWidth); |
| LOGD("Height margin ratio : %f", ratioMarginHeight); |
| |
| return rc; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : calculateDualCamTransitionParams |
| * |
| * DESCRIPTION: Calculate the transition parameters needed to switch the camera |
| * between main and aux |
| * |
| * PARAMETERS : |
| * @fovAdjustBasic : basic FOV ratio |
| * @zoomTranslationFactor: translation factor for main, aux zoom |
| * |
| * RETURN : none |
| * |
| *==========================================================================*/ |
| void QCameraFOVControl::calculateDualCamTransitionParams() |
| { |
| float percentMarginWide; |
| float percentMarginTele; |
| |
| if (isMainCamFovWider()) { |
| percentMarginWide = mFovControlConfig.percentMarginMain; |
| percentMarginTele = mFovControlConfig.percentMarginAux; |
| } else { |
| percentMarginWide = mFovControlConfig.percentMarginAux; |
| percentMarginTele = mFovControlConfig.percentMarginMain; |
| } |
| |
| mFovControlData.transitionParams.cropRatio = mFovControlData.basicFovRatio; |
| |
| mFovControlData.transitionParams.cutOverWideToTele = |
| mFovControlData.transitionParams.cutOverFactor + |
| (mFovControlConfig.percentMarginHysterisis / 100.0) * mFovControlData.basicFovRatio; |
| |
| mFovControlData.transitionParams.cutOverTeleToWide = |
| mFovControlData.transitionParams.cutOverFactor; |
| |
| mFovControlData.transitionParams.transitionHigh = |
| mFovControlData.transitionParams.cutOverWideToTele + |
| (percentMarginWide / 100.0) * mFovControlData.basicFovRatio; |
| |
| mFovControlData.transitionParams.transitionLow = |
| mFovControlData.transitionParams.cutOverTeleToWide - |
| (percentMarginTele / 100.0) * mFovControlData.basicFovRatio; |
| |
| if (mFovControlConfig.snapshotPPConfig.enablePostProcess) { |
| // Expand the transition zone if necessary to account for |
| // the snapshot post-process settings |
| if (mFovControlConfig.snapshotPPConfig.zoomMax > |
| mFovControlData.transitionParams.transitionHigh) { |
| mFovControlData.transitionParams.transitionHigh = |
| mFovControlConfig.snapshotPPConfig.zoomMax; |
| } |
| if (mFovControlConfig.snapshotPPConfig.zoomMin < |
| mFovControlData.transitionParams.transitionLow) { |
| mFovControlData.transitionParams.transitionLow = |
| mFovControlConfig.snapshotPPConfig.zoomMin; |
| } |
| |
| // Set aux switch brightness threshold as the lower of aux switch and |
| // snapshot post-process thresholds |
| if (mFovControlConfig.snapshotPPConfig.luxMin < mFovControlConfig.auxSwitchBrightnessMin) { |
| mFovControlConfig.auxSwitchBrightnessMin = mFovControlConfig.snapshotPPConfig.luxMin; |
| } |
| } |
| |
| LOGD("transition param: TransitionLow %f", mFovControlData.transitionParams.transitionLow); |
| LOGD("transition param: TeleToWide %f", mFovControlData.transitionParams.cutOverTeleToWide); |
| LOGD("transition param: WideToTele %f", mFovControlData.transitionParams.cutOverWideToTele); |
| LOGD("transition param: TransitionHigh %f", mFovControlData.transitionParams.transitionHigh); |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : findZoomValue |
| * |
| * DESCRIPTION: For the input zoom ratio, find the zoom value. |
| * Zoom table contains zoom ratios where the indices |
| * in the zoom table indicate the corresponding zoom values. |
| * PARAMETERS : |
| * @zoomRatio : Zoom ratio |
| * |
| * RETURN : Zoom value |
| * |
| *==========================================================================*/ |
| uint32_t QCameraFOVControl::findZoomValue( |
| uint32_t zoomRatio) |
| { |
| uint32_t zoom = 0; |
| for (uint32_t i = 0; i < mFovControlData.zoomRatioTableCount; ++i) { |
| if (zoomRatio <= mFovControlData.zoomRatioTable[i]) { |
| zoom = i; |
| break; |
| } |
| } |
| return zoom; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : findZoomRatio |
| * |
| * DESCRIPTION: For the input zoom value, find the zoom ratio. |
| * Zoom table contains zoom ratios where the indices |
| * in the zoom table indicate the corresponding zoom values. |
| * PARAMETERS : |
| * @zoom : zoom value |
| * |
| * RETURN : zoom ratio |
| * |
| *==========================================================================*/ |
| uint32_t QCameraFOVControl::findZoomRatio( |
| uint32_t zoom) |
| { |
| return mFovControlData.zoomRatioTable[zoom]; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : readjustZoomForTele |
| * |
| * DESCRIPTION: Calculate the zoom value for the tele camera based on zoom value |
| * for the wide camera |
| * |
| * PARAMETERS : |
| * @zoomWide : Zoom value for wide camera |
| * |
| * RETURN : Zoom value for tele camera |
| * |
| *==========================================================================*/ |
| uint32_t QCameraFOVControl::readjustZoomForTele( |
| uint32_t zoomWide) |
| { |
| uint32_t zoomRatioWide; |
| uint32_t zoomRatioTele; |
| |
| zoomRatioWide = findZoomRatio(zoomWide); |
| zoomRatioTele = zoomRatioWide / mFovControlData.transitionParams.cutOverFactor; |
| |
| return(findZoomValue(zoomRatioTele)); |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : readjustZoomForWide |
| * |
| * DESCRIPTION: Calculate the zoom value for the wide camera based on zoom value |
| * for the tele camera |
| * |
| * PARAMETERS : |
| * @zoomTele : Zoom value for tele camera |
| * |
| * RETURN : Zoom value for wide camera |
| * |
| *==========================================================================*/ |
| uint32_t QCameraFOVControl::readjustZoomForWide( |
| uint32_t zoomTele) |
| { |
| uint32_t zoomRatioWide; |
| uint32_t zoomRatioTele; |
| |
| zoomRatioTele = findZoomRatio(zoomTele); |
| zoomRatioWide = zoomRatioTele * mFovControlData.transitionParams.cutOverFactor; |
| |
| return(findZoomValue(zoomRatioWide)); |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : convertUserZoomToWideAndTele |
| * |
| * DESCRIPTION: Calculate the zoom value for the wide and tele cameras |
| * based on the input user zoom value |
| * |
| * PARAMETERS : |
| * @zoom : User zoom value |
| * |
| * RETURN : none |
| * |
| *==========================================================================*/ |
| void QCameraFOVControl::convertUserZoomToWideAndTele( |
| uint32_t zoom) |
| { |
| Mutex::Autolock lock(mMutex); |
| |
| // If the zoom translation library is present and initialized, |
| // use it to get wide and tele zoom values |
| if (mZoomTranslator && mZoomTranslator->isInitialized()) { |
| uint32_t zoomWide = 0; |
| uint32_t zoomTele = 0; |
| if (mZoomTranslator->getZoomValues(zoom, &zoomWide, &zoomTele) != NO_ERROR) { |
| LOGE("getZoomValues failed from zoom translation lib"); |
| // Use zoom translation logic from FOV-control |
| mFovControlData.zoomWide = zoom; |
| mFovControlData.zoomTele = readjustZoomForTele(mFovControlData.zoomWide); |
| } else { |
| // Use the zoom values provided by zoom translation lib |
| mFovControlData.zoomWide = zoomWide; |
| mFovControlData.zoomTele = zoomTele; |
| } |
| } else { |
| mFovControlData.zoomWide = zoom; |
| mFovControlData.zoomTele = readjustZoomForTele(mFovControlData.zoomWide); |
| } |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : translateFocusAreas |
| * |
| * DESCRIPTION: Translate the focus areas from main to aux camera. |
| * |
| * PARAMETERS : |
| * @roiAfMain : Focus area ROI for main camera |
| * @cam : Cam type |
| * |
| * RETURN : Translated focus area ROI for aux camera |
| * |
| *==========================================================================*/ |
| cam_roi_info_t QCameraFOVControl::translateFocusAreas( |
| cam_roi_info_t roiAfMain, |
| cam_sync_type_t cam) |
| { |
| float fovRatio; |
| float zoomWide; |
| float zoomTele; |
| float AuxDiffRoiLeft; |
| float AuxDiffRoiTop; |
| float AuxRoiLeft; |
| float AuxRoiTop; |
| cam_roi_info_t roiAfTrans = roiAfMain; |
| int32_t shiftHorzAdjusted; |
| int32_t shiftVertAdjusted; |
| float zoom = findZoomRatio(mFovControlData.zoomWide) / (float)mFovControlData.zoomRatioTable[0]; |
| |
| zoomWide = findZoomRatio(mFovControlData.zoomWide); |
| zoomTele = findZoomRatio(mFovControlData.zoomTele); |
| |
| if (cam == mFovControlData.camWide) { |
| fovRatio = 1.0f; |
| } else { |
| fovRatio = (zoomTele / zoomWide) * mFovControlData.transitionParams.cropRatio; |
| } |
| |
| // Acquire the mutex in order to read the spatial alignment result which is written |
| // by another thread |
| mMutex.lock(); |
| if (cam == mFovControlData.camWide) { |
| shiftHorzAdjusted = mFovControlData.spatialAlignResult.shiftAfRoiWide.shiftHorz; |
| shiftVertAdjusted = mFovControlData.spatialAlignResult.shiftAfRoiWide.shiftVert; |
| } else { |
| shiftHorzAdjusted = (mFovControlData.transitionParams.cropRatio / zoom) * |
| mFovControlData.spatialAlignResult.shiftAfRoiTele.shiftHorz; |
| shiftVertAdjusted = (mFovControlData.transitionParams.cropRatio / zoom) * |
| mFovControlData.spatialAlignResult.shiftAfRoiTele.shiftVert; |
| } |
| mMutex.unlock(); |
| |
| for (int i = 0; i < roiAfMain.num_roi; ++i) { |
| roiAfTrans.roi[i].width = roiAfMain.roi[i].width * fovRatio; |
| roiAfTrans.roi[i].height = roiAfMain.roi[i].height * fovRatio; |
| |
| AuxDiffRoiLeft = (roiAfTrans.roi[i].width - roiAfMain.roi[i].width) / 2.0f; |
| AuxRoiLeft = roiAfMain.roi[i].left - AuxDiffRoiLeft; |
| AuxDiffRoiTop = (roiAfTrans.roi[i].height - roiAfMain.roi[i].height) / 2.0f; |
| AuxRoiTop = roiAfMain.roi[i].top - AuxDiffRoiTop; |
| |
| roiAfTrans.roi[i].left = AuxRoiLeft - shiftHorzAdjusted; |
| roiAfTrans.roi[i].top = AuxRoiTop - shiftVertAdjusted; |
| |
| // Check the ROI bounds and correct if necessory |
| // If ROI is out of bounds, revert to default ROI |
| if ((roiAfTrans.roi[i].left >= mFovControlData.previewSize.width) || |
| (roiAfTrans.roi[i].top >= mFovControlData.previewSize.height) || |
| (roiAfTrans.roi[i].width >= mFovControlData.previewSize.width) || |
| (roiAfTrans.roi[i].height >= mFovControlData.previewSize.height)) { |
| // TODO : use default ROI when available from AF. This part of the code |
| // is still being worked upon. WA - set it to main cam ROI |
| roiAfTrans = roiAfMain; |
| LOGW("AF ROI translation failed, reverting to the default ROI"); |
| } else { |
| if (roiAfTrans.roi[i].left < 0) { |
| roiAfTrans.roi[i].left = 0; |
| LOGW("AF ROI translation failed"); |
| } |
| if (roiAfTrans.roi[i].top < 0) { |
| roiAfTrans.roi[i].top = 0; |
| LOGW("AF ROI translation failed"); |
| } |
| if ((roiAfTrans.roi[i].left + roiAfTrans.roi[i].width) >= |
| mFovControlData.previewSize.width) { |
| roiAfTrans.roi[i].width = |
| mFovControlData.previewSize.width - roiAfTrans.roi[i].left; |
| LOGW("AF ROI translation failed"); |
| } |
| if ((roiAfTrans.roi[i].top + roiAfTrans.roi[i].height) >= |
| mFovControlData.previewSize.height) { |
| roiAfTrans.roi[i].height = |
| mFovControlData.previewSize.height - roiAfTrans.roi[i].top; |
| LOGW("AF ROI translation failed"); |
| } |
| } |
| |
| LOGD("Translated AF ROI-%d %s: L:%d, T:%d, W:%d, H:%d", i, |
| (cam == CAM_TYPE_MAIN) ? "main cam" : "aux cam", roiAfTrans.roi[i].left, |
| roiAfTrans.roi[i].top, roiAfTrans.roi[i].width, roiAfTrans.roi[i].height); |
| } |
| return roiAfTrans; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : translateMeteringAreas |
| * |
| * DESCRIPTION: Translate the AEC metering areas from main to aux camera. |
| * |
| * PARAMETERS : |
| * @roiAfMain : AEC ROI for main camera |
| * @cam : Cam type |
| * |
| * RETURN : Translated AEC ROI for aux camera |
| * |
| *==========================================================================*/ |
| cam_set_aec_roi_t QCameraFOVControl::translateMeteringAreas( |
| cam_set_aec_roi_t roiAecMain, |
| cam_sync_type_t cam) |
| { |
| float fovRatio; |
| float zoomWide; |
| float zoomTele; |
| float AuxDiffRoiX; |
| float AuxDiffRoiY; |
| float AuxRoiX; |
| float AuxRoiY; |
| cam_set_aec_roi_t roiAecTrans = roiAecMain; |
| int32_t shiftHorzAdjusted; |
| int32_t shiftVertAdjusted; |
| float zoom = findZoomRatio(mFovControlData.zoomWide) / (float)mFovControlData.zoomRatioTable[0]; |
| |
| zoomWide = findZoomRatio(mFovControlData.zoomWide); |
| zoomTele = findZoomRatio(mFovControlData.zoomTele); |
| |
| if (cam == mFovControlData.camWide) { |
| fovRatio = 1.0f; |
| } else { |
| fovRatio = (zoomTele / zoomWide) * mFovControlData.transitionParams.cropRatio; |
| } |
| |
| // Acquire the mutex in order to read the spatial alignment result which is written |
| // by another thread |
| mMutex.lock(); |
| if (cam == mFovControlData.camWide) { |
| shiftHorzAdjusted = mFovControlData.spatialAlignResult.shiftAfRoiWide.shiftHorz; |
| shiftVertAdjusted = mFovControlData.spatialAlignResult.shiftAfRoiWide.shiftVert; |
| } else { |
| shiftHorzAdjusted = (mFovControlData.transitionParams.cropRatio / zoom) * |
| mFovControlData.spatialAlignResult.shiftAfRoiTele.shiftHorz; |
| shiftVertAdjusted = (mFovControlData.transitionParams.cropRatio / zoom) * |
| mFovControlData.spatialAlignResult.shiftAfRoiTele.shiftVert; |
| } |
| mMutex.unlock(); |
| |
| for (int i = 0; i < roiAecMain.num_roi; ++i) { |
| AuxDiffRoiX = fovRatio * ((float)roiAecMain.cam_aec_roi_position.coordinate[i].x - |
| (mFovControlData.previewSize.width / 2)); |
| AuxRoiX = (mFovControlData.previewSize.width / 2) + AuxDiffRoiX; |
| |
| AuxDiffRoiY = fovRatio * ((float)roiAecMain.cam_aec_roi_position.coordinate[i].y - |
| (mFovControlData.previewSize.height / 2)); |
| AuxRoiY = (mFovControlData.previewSize.height / 2) + AuxDiffRoiY; |
| |
| roiAecTrans.cam_aec_roi_position.coordinate[i].x = AuxRoiX - shiftHorzAdjusted; |
| roiAecTrans.cam_aec_roi_position.coordinate[i].y = AuxRoiY - shiftVertAdjusted; |
| |
| // Check the ROI bounds and correct if necessory |
| if ((AuxRoiX < 0) || |
| (AuxRoiY < 0)) { |
| roiAecTrans.cam_aec_roi_position.coordinate[i].x = 0; |
| roiAecTrans.cam_aec_roi_position.coordinate[i].y = 0; |
| LOGW("AEC ROI translation failed"); |
| } else if ((AuxRoiX >= mFovControlData.previewSize.width) || |
| (AuxRoiY >= mFovControlData.previewSize.height)) { |
| // Clamp the Aux AEC ROI co-ordinates to max possible value |
| if (AuxRoiX >= mFovControlData.previewSize.width) { |
| roiAecTrans.cam_aec_roi_position.coordinate[i].x = |
| mFovControlData.previewSize.width - 1; |
| } |
| if (AuxRoiY >= mFovControlData.previewSize.height) { |
| roiAecTrans.cam_aec_roi_position.coordinate[i].y = |
| mFovControlData.previewSize.height - 1; |
| } |
| LOGW("AEC ROI translation failed"); |
| } |
| |
| LOGD("Translated AEC ROI-%d %s: x:%d, y:%d", i, |
| (cam == CAM_TYPE_MAIN) ? "main cam" : "aux cam", |
| roiAecTrans.cam_aec_roi_position.coordinate[i].x, |
| roiAecTrans.cam_aec_roi_position.coordinate[i].y); |
| } |
| return roiAecTrans; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : translateRoiFD |
| * |
| * DESCRIPTION: Translate face detection ROI from aux metadata to main |
| * |
| * PARAMETERS : |
| * @faceDetectionInfo : face detection data from aux metadata. This face |
| * detection data is overwritten with the translated |
| * FD ROI. |
| * @cam : Cam type |
| * |
| * RETURN : none |
| * |
| *==========================================================================*/ |
| cam_face_detection_data_t QCameraFOVControl::translateRoiFD( |
| cam_face_detection_data_t metaFD, |
| cam_sync_type_t cam) |
| { |
| cam_face_detection_data_t metaFDTranslated = metaFD; |
| int32_t shiftHorz = 0; |
| int32_t shiftVert = 0; |
| |
| if (cam == mFovControlData.camWide) { |
| shiftHorz = mFovControlData.spatialAlignResult.shiftWide.shiftHorz; |
| shiftVert = mFovControlData.spatialAlignResult.shiftWide.shiftVert; |
| } else { |
| shiftHorz = mFovControlData.spatialAlignResult.shiftTele.shiftHorz; |
| shiftVert = mFovControlData.spatialAlignResult.shiftTele.shiftVert; |
| } |
| |
| for (int i = 0; i < metaFDTranslated.num_faces_detected; ++i) { |
| metaFDTranslated.faces[i].face_boundary.left += shiftHorz; |
| metaFDTranslated.faces[i].face_boundary.top += shiftVert; |
| } |
| |
| // If ROI is out of bounds, remove that FD ROI from the list |
| for (int i = 0; i < metaFDTranslated.num_faces_detected; ++i) { |
| if ((metaFDTranslated.faces[i].face_boundary.left < 0) || |
| (metaFDTranslated.faces[i].face_boundary.left >= mFovControlData.previewSize.width) || |
| (metaFDTranslated.faces[i].face_boundary.top < 0) || |
| (metaFDTranslated.faces[i].face_boundary.top >= mFovControlData.previewSize.height) || |
| ((metaFDTranslated.faces[i].face_boundary.left + |
| metaFDTranslated.faces[i].face_boundary.width) >= |
| mFovControlData.previewSize.width) || |
| ((metaFDTranslated.faces[i].face_boundary.top + |
| metaFDTranslated.faces[i].face_boundary.height) >= |
| mFovControlData.previewSize.height)) { |
| // Invalid FD ROI detected |
| LOGD("Failed translating FD ROI %s: L:%d, T:%d, W:%d, H:%d", |
| (cam == CAM_TYPE_MAIN) ? "main cam" : "aux cam", |
| metaFDTranslated.faces[i].face_boundary.left, |
| metaFDTranslated.faces[i].face_boundary.top, |
| metaFDTranslated.faces[i].face_boundary.width, |
| metaFDTranslated.faces[i].face_boundary.height); |
| |
| // Remove it by copying the last FD ROI at this index |
| if (i < (metaFDTranslated.num_faces_detected - 1)) { |
| metaFDTranslated.faces[i] = |
| metaFDTranslated.faces[metaFDTranslated.num_faces_detected - 1]; |
| // Decrement the current index to process the newly copied FD ROI. |
| --i; |
| } |
| --metaFDTranslated.num_faces_detected; |
| } |
| else { |
| LOGD("Translated FD ROI-%d %s: L:%d, T:%d, W:%d, H:%d", i, |
| (cam == CAM_TYPE_MAIN) ? "main cam" : "aux cam", |
| metaFDTranslated.faces[i].face_boundary.left, |
| metaFDTranslated.faces[i].face_boundary.top, |
| metaFDTranslated.faces[i].face_boundary.width, |
| metaFDTranslated.faces[i].face_boundary.height); |
| } |
| } |
| return metaFDTranslated; |
| } |
| |
| |
| /*=========================================================================== |
| * FUNCTION : getFrameMargins |
| * |
| * DESCRIPTION : Return frame margin data for the requested camera |
| * |
| * PARAMETERS : |
| * @masterCamera : Master camera id |
| * |
| * RETURN : Frame margins |
| * |
| *==========================================================================*/ |
| cam_frame_margins_t QCameraFOVControl::getFrameMargins( |
| int8_t masterCamera) |
| { |
| cam_frame_margins_t frameMargins; |
| memset(&frameMargins, 0, sizeof(cam_frame_margins_t)); |
| |
| if (masterCamera == CAM_TYPE_MAIN) { |
| frameMargins.widthMargins = mFovControlData.camMainWidthMargin; |
| frameMargins.heightMargins = mFovControlData.camMainHeightMargin; |
| } else if (masterCamera == CAM_TYPE_AUX) { |
| frameMargins.widthMargins = mFovControlData.camAuxWidthMargin; |
| frameMargins.heightMargins = mFovControlData.camAuxHeightMargin; |
| } |
| |
| return frameMargins; |
| } |
| }; // namespace qcamera |