/* Copyright (c) 2016, 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 #include "QCameraFOVControl.h" extern "C" { #include "mm_camera_dbg.h" } namespace qcamera { /*=========================================================================== * FUNCTION : QCameraFOVControl constructor * * DESCRIPTION: class constructor * * PARAMETERS : none * * RETURN : void * *==========================================================================*/ QCameraFOVControl::QCameraFOVControl() { 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)); mFovControlData.camcorderMode = false; mFovControlData.status3A.main.af.status = AF_INVALID; mFovControlData.status3A.aux.af.status = AF_INVALID; mFovControlResult.camMasterPreview = CAM_TYPE_MAIN; mFovControlResult.camMaster3A = CAM_TYPE_MAIN; mFovControlResult.activeCamState = (uint32_t)CAM_TYPE_MAIN; mFovControlResult.snapshotPostProcess = false; mFovControlData.spatialAlignResult.status = 0; mFovControlData.spatialAlignResult.camMasterPreview = CAM_ROLE_WIDE; mFovControlData.spatialAlignResult.camMaster3A = CAM_ROLE_WIDE; mFovControlData.spatialAlignResult.activeCamState = (uint32_t)CAM_TYPE_MAIN; mFovControlData.spatialAlignResult.shiftHorz = 0; mFovControlData.spatialAlignResult.shiftVert = 0; } /*=========================================================================== * FUNCTION : QCameraFOVControl destructor * * DESCRIPTION: class destructor * * PARAMETERS : none * * RETURN : void * *==========================================================================*/ QCameraFOVControl::~QCameraFOVControl() { } /*=========================================================================== * 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)) { 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; } 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; memcpy(&capsConsolidated, capsMainCam, sizeof(cam_capability_t)); if ((capsMainCam != NULL) && (capsAuxCam != NULL)) { // 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; } } // Choose the smaller 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))); } // 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; } return capsConsolidated; } /*=========================================================================== * 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; for (int i = 0; i < MAX_NUM_STREAMS; ++i) { if (camMainStreamInfo.type[i] == CAM_STREAM_TYPE_VIDEO) { mFovControlData.camcorderMode = true; } } 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 mFovControlData.previewSize = camMainStreamInfo.stream_sizes[i]; if (!mFovControlData.camcorderMode) { mFovControlData.camMainWidthMargin = camMainStreamInfo.margins[i].widthMargins; mFovControlData.camMainHeightMargin = camMainStreamInfo.margins[i].heightMargins; break; } } } 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; } } } // Recalculate the transition parameters if (calculateBasicFovRatio() && combineFovAdjustment()) { calculateDualCamTransitionParams(); 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); convertUserZoomToMainAndAux(userZoom); ADD_SET_PARAM_ENTRY_TO_BATCH(paramsAuxCam, CAM_INTF_PARM_ZOOM, mFovControlData.zoomAux); } if (paramsMainCam->is_valid[CAM_INTF_PARM_AF_ROI] || paramsMainCam->is_valid[CAM_INTF_PARM_AEC_ROI]) { convertDisparityForInputParams(); } // 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); ADD_SET_PARAM_ENTRY_TO_BATCH(paramsAuxCam, CAM_INTF_PARM_AF_ROI, roiAfAux); } } // 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); ADD_SET_PARAM_ENTRY_TO_BATCH(paramsAuxCam, CAM_INTF_PARM_AEC_ROI, roiAecAux); } } 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; // Temporary code to determine the master camera. // This code will be replaced once we have the logic // to determine master based on the frame number in HAL. cam_sync_type_t master = mFovControlResult.camMasterPreview; if ((master == CAM_TYPE_AUX) && metaAux) { // Translate face detection ROI IF_META_AVAILABLE(cam_face_detection_data_t, metaFD, CAM_INTF_META_FACE_DETECTION, metaAux) { cam_face_detection_data_t metaFDTranslated; metaFDTranslated = translateRoiFD(*metaFD); ADD_SET_PARAM_ENTRY_TO_BATCH(metaAux, CAM_INTF_META_FACE_DETECTION, metaFDTranslated); } metaResult = metaAux; } else if (metaMain) { metaResult = metaMain; } else { // Metadata for the master camera was dropped metaResult = NULL; return metaResult; } 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 for preview if (spatialAlignOutput->is_master_preview_valid) { uint8_t master = spatialAlignOutput->master_preview; if ((master == CAM_ROLE_WIDE) || (master == CAM_ROLE_TELE)) { mFovControlData.spatialAlignResult.camMasterPreview = master; } } // Get master camera for 3A if (spatialAlignOutput->is_master_3A_valid) { uint8_t master = spatialAlignOutput->master_3A; if ((master == CAM_ROLE_WIDE) || (master == CAM_ROLE_TELE)) { mFovControlData.spatialAlignResult.camMaster3A = master; } } // Get spatial alignment ready status if (spatialAlignOutput->is_ready_status_valid) { mFovControlData.spatialAlignResult.status = spatialAlignOutput->ready_status; } // temp code: Always set it to 1 until spatial align functionality is in place mFovControlData.spatialAlignResult.status = 1; // Get spatial alignment output shift if (spatialAlignOutput->is_output_shift_valid) { mFovControlData.spatialAlignResult.shiftHorz = spatialAlignOutput->output_shift.shift_horz; mFovControlData.spatialAlignResult.shiftVert = spatialAlignOutput->output_shift.shift_vert; } } if (mFovControlData.availableSpatialAlignSolns & CAM_SPATIAL_ALIGN_OEM) { // Get low power mode info IF_META_AVAILABLE(uint8_t, enableLPM, CAM_INTF_META_DC_LOW_POWER_ENABLE, meta) { if (*enableLPM) { mFovControlData.spatialAlignResult.activeCamState = (uint32_t)mFovControlResult.camMasterPreview; } } } // Get AF status if (metaMain) { IF_META_AVAILABLE(uint32_t, afState, CAM_INTF_META_AF_STATE, metaMain) { if (((*afState) == CAM_AF_STATE_FOCUSED_LOCKED) || ((*afState) == CAM_AF_STATE_NOT_FOCUSED_LOCKED) || ((*afState) == CAM_AF_STATE_PASSIVE_FOCUSED) || ((*afState) == CAM_AF_STATE_PASSIVE_UNFOCUSED)) { mFovControlData.status3A.main.af.status = AF_VALID; } else { mFovControlData.status3A.main.af.status = AF_INVALID; } } } if (metaAux) { IF_META_AVAILABLE(uint32_t, afState, CAM_INTF_META_AF_STATE, metaAux) { if (((*afState) == CAM_AF_STATE_FOCUSED_LOCKED) || ((*afState) == CAM_AF_STATE_NOT_FOCUSED_LOCKED) || ((*afState) == CAM_AF_STATE_PASSIVE_FOCUSED) || ((*afState) == CAM_AF_STATE_PASSIVE_UNFOCUSED)) { mFovControlData.status3A.aux.af.status = AF_VALID; } else { mFovControlData.status3A.aux.af.status = AF_INVALID; } } } mMutex.unlock(); } return metaResult; } /*=========================================================================== * FUNCTION : getFovControlResult * * DESCRIPTION: Return the result from FOV control * * PARAMETERS : None * * RETURN : FOV-control result * *==========================================================================*/ fov_control_result_t QCameraFOVControl::getFovControlResult() { cam_sync_type_t camWide = mFovControlData.camWide; cam_sync_type_t camTele = mFovControlData.camTele; float zoom = findZoomRatio(mFovControlData.zoomMain) / (float)mFovControlData.zoomRatioTable[0]; // Read AF status with mutex lock mMutex.lock(); af_status afStatusMain = mFovControlData.status3A.main.af.status; af_status afStatusAux = mFovControlData.status3A.aux.af.status; mMutex.unlock(); // Update the dual camera state based on the current zoom switch (mFovControlData.camState) { case STATE_WIDE: if (zoom >= mFovControlData.transitionParams.transitionLow) { mFovControlData.camState = STATE_TRANSITION_WIDE_TO_TELE; } break; case STATE_TRANSITION_WIDE_TO_TELE: if ((zoom < mFovControlData.transitionParams.transitionLow) && (mFovControlResult.camMasterPreview == camWide) && (mFovControlResult.camMaster3A == camWide)) { mFovControlData.camState = STATE_WIDE; } else if ((zoom >= mFovControlData.transitionParams.transitionHigh) && (mFovControlResult.camMasterPreview == camTele) && (mFovControlResult.camMaster3A == camTele) && (afStatusAux == AF_VALID)) { mFovControlData.camState = STATE_TELE; } break; case STATE_TELE: if (zoom < mFovControlData.transitionParams.transitionHigh) { mFovControlData.camState = STATE_TRANSITION_TELE_TO_WIDE; } break; case STATE_TRANSITION_TELE_TO_WIDE: if ((zoom >= mFovControlData.transitionParams.transitionHigh) && (mFovControlResult.camMasterPreview == camTele) && (mFovControlResult.camMaster3A == camTele)) { mFovControlData.camState = STATE_TELE; } else if ((zoom < mFovControlData.transitionParams.transitionLow) && (mFovControlResult.camMasterPreview == camWide) && (mFovControlResult.camMaster3A == camWide) && (afStatusMain == AF_VALID)) { mFovControlData.camState = STATE_WIDE; } break; } // Generate the result using updated dual camera state switch (mFovControlData.camState) { case STATE_WIDE: mFovControlResult.activeCamState = camWide; mFovControlResult.camMaster3A = camWide; mFovControlResult.camMasterPreview = camWide; mFovControlResult.snapshotPostProcess = false; break; case STATE_TRANSITION_WIDE_TO_TELE: if (zoom > mFovControlData.transitionParams.cutOverMainToAux) { mFovControlResult.camMasterPreview = camTele; mFovControlResult.camMaster3A = camTele; } else { mFovControlResult.camMasterPreview = camWide; mFovControlResult.camMaster3A = camWide; } mFovControlResult.activeCamState = (camWide | camTele); mFovControlResult.snapshotPostProcess = false; break; case STATE_TRANSITION_TELE_TO_WIDE: if (zoom < mFovControlData.transitionParams.cutOverAuxToMain) { mFovControlResult.camMasterPreview = camWide; mFovControlResult.camMaster3A = camWide; } else { mFovControlResult.camMasterPreview = camTele; mFovControlResult.camMaster3A = camTele; } mFovControlResult.activeCamState = (camWide | camTele); mFovControlResult.snapshotPostProcess = false; break; case STATE_TELE: mFovControlResult.camMaster3A = camTele; mFovControlResult.camMasterPreview = camTele; mFovControlResult.activeCamState = camTele; mFovControlResult.snapshotPostProcess = false; break; } if (mFovControlData.availableSpatialAlignSolns & CAM_SPATIAL_ALIGN_OEM) { // Override the FOVC result if (mFovControlData.spatialAlignResult.camMasterPreview == CAM_ROLE_WIDE) { mFovControlResult.camMasterPreview = camWide; mFovControlResult.camMaster3A = camWide; mFovControlResult.activeCamState |= camWide; } else { mFovControlResult.camMasterPreview = camTele; mFovControlResult.camMaster3A = camTele; mFovControlResult.activeCamState |= camTele; } } // Debug print for the FOV-control result LOGD("Effective zoom: %f", zoom); LOGD("ZoomMain: %d, ZoomAux: %d", mFovControlData.zoomMain, mFovControlData.zoomAux); LOGD("Master camera for preview: %s", (mFovControlResult.camMasterPreview == CAM_TYPE_MAIN ) ? "Main" : "Aux"); LOGD("Master camera for 3A : %s", (mFovControlResult.camMaster3A == CAM_TYPE_MAIN ) ? "Main" : "Aux"); LOGD("Main camera status: %s", (mFovControlResult.activeCamState & CAM_TYPE_MAIN) ? "Active" : "Standby"); LOGD("Aux camera status : %s", (mFovControlResult.activeCamState & CAM_TYPE_AUX) ? "Active" : "Standby"); LOGD("snapshot postprocess : %d", mFovControlResult.snapshotPostProcess); return mFovControlResult; } /*=========================================================================== * 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) { // TODO : Replace the hardcoded values for mFovControlConfig and mDualCamParams below // with the ones extracted from capabilities when available in eeprom. mFovControlConfig.percentMarginHysterisis = 5; mFovControlConfig.percentMarginMain = 10; mFovControlConfig.percentMarginAux = 15; mFovControlConfig.waitTimeForHandoffMs = 1000; mDualCamParams.paramsMain.sensorStreamWidth = 4208; mDualCamParams.paramsMain.sensorStreamHeight = 3120; mDualCamParams.paramsMain.pixelPitchUm = 1.12; mDualCamParams.paramsMain.focalLengthMm = 3.5; mDualCamParams.paramsAux.sensorStreamWidth = 4208; mDualCamParams.paramsAux.sensorStreamHeight = 3120; mDualCamParams.paramsAux.pixelPitchUm = 1.12; mDualCamParams.paramsAux.focalLengthMm = 7; mDualCamParams.baselineMm = 9.5; mDualCamParams.minFocusDistanceCm = 30; mDualCamParams.rollDegrees = 1.0; mDualCamParams.pitchDegrees = 1.0; mDualCamParams.yawDegrees = 1.0; mDualCamParams.positionAux = CAM_POSITION_LEFT; if ((capsMainCam->avail_spatial_align_solns & CAM_SPATIAL_ALIGN_QCOM) || (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 fovMain; float fovAux; bool rc = false; if ((mDualCamParams.paramsMain.focalLengthMm > 0.0f) && (mDualCamParams.paramsAux.focalLengthMm > 0.0f)) { fovMain = (mDualCamParams.paramsMain.sensorStreamWidth * mDualCamParams.paramsMain.pixelPitchUm) / mDualCamParams.paramsMain.focalLengthMm; fovAux = (mDualCamParams.paramsAux.sensorStreamWidth * mDualCamParams.paramsAux.pixelPitchUm) / mDualCamParams.paramsAux.focalLengthMm; if (fovAux > 0.0f) { mFovControlData.basicFovRatio = (fovMain / fovAux); rc = true; } } 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; } 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() { mFovControlData.transitionParams.cropRatio = mFovControlData.basicFovRatio; mFovControlData.transitionParams.cutOverMainToAux = mFovControlData.transitionParams.cutOverFactor + (mFovControlConfig.percentMarginHysterisis / 100.0) * mFovControlData.basicFovRatio; mFovControlData.transitionParams.cutOverAuxToMain = mFovControlData.transitionParams.cutOverFactor; mFovControlData.transitionParams.transitionHigh = mFovControlData.transitionParams.cutOverMainToAux + (mFovControlConfig.percentMarginMain / 100.0) * mFovControlData.basicFovRatio; mFovControlData.transitionParams.transitionLow = mFovControlData.transitionParams.cutOverAuxToMain - (mFovControlConfig.percentMarginAux / 100.0) * mFovControlData.basicFovRatio; } /*=========================================================================== * 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 : readjustZoomForAux * * DESCRIPTION: Calculate the zoom value for the aux camera based on the input * zoom value for the main camera * * PARAMETERS : * @zoom : Zoom value for main camera * * RETURN : Zoom value for aux camera * *==========================================================================*/ uint32_t QCameraFOVControl::readjustZoomForAux( uint32_t zoomMain) { uint32_t zoomRatioMain; uint32_t zoomRatioAux; zoomRatioMain = findZoomRatio(zoomMain); zoomRatioAux = zoomRatioMain / mFovControlData.transitionParams.cutOverFactor; return(findZoomValue(zoomRatioAux)); } /*=========================================================================== * FUNCTION : readjustZoomForMain * * DESCRIPTION: Calculate the zoom value for the main camera based on the input * zoom value for the aux camera * * PARAMETERS : * @zoom : Zoom value for aux camera * * RETURN : Zoom value for main camera * *==========================================================================*/ uint32_t QCameraFOVControl::readjustZoomForMain( uint32_t zoomAux) { uint32_t zoomRatioMain; uint32_t zoomRatioAux; zoomRatioAux = findZoomRatio(zoomAux); zoomRatioMain = zoomRatioAux * mFovControlData.transitionParams.cutOverFactor; return(findZoomValue(zoomRatioMain)); } /*=========================================================================== * FUNCTION : convertUserZoomToMainAndAux * * DESCRIPTION: Calculate the zoom value for the main and aux cameras based on * the input user zoom value * * PARAMETERS : * @zoom : User zoom value * * RETURN : none * *==========================================================================*/ void QCameraFOVControl::convertUserZoomToMainAndAux( uint32_t zoom) { mFovControlData.zoomMain = zoom; mFovControlData.zoomAux = readjustZoomForAux(mFovControlData.zoomMain); } /*=========================================================================== * FUNCTION : convertDisparityForInputParams * * DESCRIPTION: Convert the disparity for translation of input parameters * * PARAMETERS : none * * RETURN : none * *==========================================================================*/ void QCameraFOVControl::convertDisparityForInputParams() { Mutex::Autolock lock(mMutex); mFovControlData.shiftHorzAdjMain = mFovControlData.transitionParams.cropRatio / (mFovControlData.zoomMain / (float)mFovControlData.zoomRatioTable[0]) * mFovControlData.spatialAlignResult.shiftHorz; } /*=========================================================================== * FUNCTION : translateFocusAreas * * DESCRIPTION: Translate the focus areas from main to aux camera. * * PARAMETERS : * @roiAfMain : Focus area ROI for main camera * * RETURN : Translated focus area ROI for aux camera * *==========================================================================*/ cam_roi_info_t QCameraFOVControl::translateFocusAreas( cam_roi_info_t roiAfMain) { float fovRatio; float zoomMain; float zoomAux; float AuxDiffRoiLeft; float AuxDiffRoiTop; float AuxRoiLeft; float AuxRoiTop; cam_roi_info_t roiAfAux; zoomMain = findZoomRatio(mFovControlData.zoomMain); zoomAux = findZoomRatio(mFovControlData.zoomAux); fovRatio = mFovControlData.transitionParams.cropRatio; fovRatio = (zoomAux / zoomMain) * mFovControlData.transitionParams.cropRatio; for (int i = 0; i < roiAfMain.num_roi; ++i) { AuxDiffRoiLeft = fovRatio*(roiAfMain.roi[i].left - (mFovControlData.previewSize.width / 2)); AuxRoiLeft = (mFovControlData.previewSize.width / 2) + AuxDiffRoiLeft; AuxDiffRoiTop = fovRatio*(roiAfMain.roi[i].top - (mFovControlData.previewSize.height/ 2)); AuxRoiTop = (mFovControlData.previewSize.height / 2) + AuxDiffRoiTop; roiAfAux.roi[i].width *= fovRatio; roiAfAux.roi[i].height *= fovRatio; roiAfAux.roi[i].top = AuxRoiTop; if (mDualCamParams.positionAux == CAM_POSITION_LEFT) { roiAfAux.roi[i].left = AuxRoiLeft + mFovControlData.shiftHorzAdjMain; } else { roiAfAux.roi[i].left = AuxRoiLeft - mFovControlData.shiftHorzAdjMain; } } return roiAfAux; } /*=========================================================================== * FUNCTION : translateMeteringAreas * * DESCRIPTION: Translate the AEC metering areas from main to aux camera. * * PARAMETERS : * @roiAfMain : AEC ROI for main camera * * RETURN : Translated AEC ROI for aux camera * *==========================================================================*/ cam_set_aec_roi_t QCameraFOVControl::translateMeteringAreas( cam_set_aec_roi_t roiAecMain) { float fovRatio; float zoomMain; float zoomAux; float AuxDiffRoiX; float AuxDiffRoiY; float AuxRoiX; float AuxRoiY; cam_set_aec_roi_t roiAecAux; zoomMain = findZoomRatio(mFovControlData.zoomMain); zoomAux = findZoomRatio(mFovControlData.zoomAux); fovRatio = mFovControlData.transitionParams.cropRatio; fovRatio = (zoomAux / zoomMain) * mFovControlData.transitionParams.cropRatio; for (int i = 0; i < roiAecMain.num_roi; ++i) { AuxDiffRoiX = fovRatio*(roiAecMain.cam_aec_roi_position.coordinate[i].x - (mFovControlData.previewSize.width / 2)); AuxRoiX = (mFovControlData.previewSize.width / 2) + AuxDiffRoiX; AuxDiffRoiY = fovRatio*(roiAecMain.cam_aec_roi_position.coordinate[i].y - (mFovControlData.previewSize.height / 2)); AuxRoiY = (mFovControlData.previewSize.height / 2) + AuxDiffRoiY; roiAecAux.cam_aec_roi_position.coordinate[i].y = AuxRoiY; if (mDualCamParams.positionAux == CAM_POSITION_LEFT) { roiAecAux.cam_aec_roi_position.coordinate[i].x = AuxRoiX + mFovControlData.shiftHorzAdjMain; } else { roiAecAux.cam_aec_roi_position.coordinate[i].x = AuxRoiX - mFovControlData.shiftHorzAdjMain; } } return roiAecAux; } /*=========================================================================== * 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. * * RETURN : none * *==========================================================================*/ cam_face_detection_data_t QCameraFOVControl::translateRoiFD( cam_face_detection_data_t metaFD) { cam_face_detection_data_t metaFDTranslated = metaFD; for (int i = 0; i < metaFDTranslated.num_faces_detected; ++i) { if (mDualCamParams.positionAux == CAM_POSITION_LEFT) { metaFDTranslated.faces[i].face_boundary.left -= mFovControlData.spatialAlignResult.shiftHorz; } else { metaFDTranslated.faces[i].face_boundary.left += mFovControlData.spatialAlignResult.shiftHorz; } } return metaFDTranslated; } }; // namespace qcamera