| /** |
| * Copyright (C) 2022 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <ImsMediaCamera.h> |
| #include <ImsMediaTrace.h> |
| #include <ImsMediaVideoUtil.h> |
| #include <unistd.h> |
| #include <cinttypes> |
| #include <thread> |
| #include <camera/NdkCaptureRequest.h> |
| #include <media/NdkImage.h> |
| |
| #define UNKNOWN_TAG "UNKNOWN_TAG" |
| #define MAKE_PAIR(val) std::make_pair(val, #val) |
| template <typename T> |
| const char* GetPairStr(T key, std::vector<std::pair<T, const char*>>& store) |
| { |
| typedef typename std::vector<std::pair<T, const char*>>::iterator iterator; |
| for (iterator it = store.begin(); it != store.end(); ++it) |
| { |
| if (it->first == key) |
| { |
| return it->second; |
| } |
| } |
| IMLOGW1("[GetPairStr] %#08x : UNKNOWN_TAG", key); |
| return UNKNOWN_TAG; |
| } |
| |
| /* |
| * camera_status_t error translation |
| */ |
| using ERROR_PAIR = std::pair<camera_status_t, const char*>; |
| static std::vector<ERROR_PAIR> errorInfo{ |
| MAKE_PAIR(ACAMERA_OK), |
| MAKE_PAIR(ACAMERA_ERROR_UNKNOWN), |
| MAKE_PAIR(ACAMERA_ERROR_INVALID_PARAMETER), |
| MAKE_PAIR(ACAMERA_ERROR_CAMERA_DISCONNECTED), |
| MAKE_PAIR(ACAMERA_ERROR_NOT_ENOUGH_MEMORY), |
| MAKE_PAIR(ACAMERA_ERROR_METADATA_NOT_FOUND), |
| MAKE_PAIR(ACAMERA_ERROR_CAMERA_DEVICE), |
| MAKE_PAIR(ACAMERA_ERROR_CAMERA_SERVICE), |
| MAKE_PAIR(ACAMERA_ERROR_SESSION_CLOSED), |
| MAKE_PAIR(ACAMERA_ERROR_INVALID_OPERATION), |
| MAKE_PAIR(ACAMERA_ERROR_STREAM_CONFIGURE_FAIL), |
| MAKE_PAIR(ACAMERA_ERROR_CAMERA_IN_USE), |
| MAKE_PAIR(ACAMERA_ERROR_MAX_CAMERA_IN_USE), |
| MAKE_PAIR(ACAMERA_ERROR_CAMERA_DISABLED), |
| MAKE_PAIR(ACAMERA_ERROR_PERMISSION_DENIED), |
| }; |
| const char* GetErrorStr(camera_status_t err) |
| { |
| return GetPairStr<camera_status_t>(err, errorInfo); |
| } |
| |
| /** |
| * Range of Camera Exposure Time: |
| * Camera's capability range have a very long range which may be disturbing |
| * on camera. For this sample purpose, clamp to a range showing visible |
| * video on preview: 100000ns ~ 250000000ns |
| */ |
| static const uint64_t kMinExposureTime = static_cast<uint64_t>(1000000); |
| static const uint64_t kMaxExposureTime = static_cast<uint64_t>(250000000); |
| |
| std::map<std::string, CameraId> ImsMediaCamera::gCameraIds; |
| ImsMediaCondition ImsMediaCamera::gCondition; |
| ImsMediaCamera ImsMediaCamera::gCamera; |
| |
| ImsMediaCamera* ImsMediaCamera::getInstance() |
| { |
| return &gCamera; |
| } |
| |
| ImsMediaCamera::ImsMediaCamera() : |
| mManager(NULL), |
| mSessionOutputContainer(NULL), |
| mCaptureSession(NULL), |
| mCaptureSessionState(CaptureSessionState::kStateMax), |
| mExposureTime(0), |
| mSensitivity(0), |
| mCameraMode(kCameraModePreview), |
| mCameraFacing(ACAMERA_LENS_FACING_FRONT), |
| mCameraOrientation(0), |
| mActiveCameraId(""), |
| mCameraZoom(0), |
| mFramerate(-1) |
| { |
| IMLOGD0("[ImsMediaCamera]"); |
| } |
| |
| ImsMediaCamera::~ImsMediaCamera() |
| { |
| IMLOGD0("[~ImsMediaCamera]"); |
| } |
| |
| void ImsMediaCamera::Initialize() |
| { |
| IMLOGD0("[Initialize]"); |
| gCameraIds.clear(); |
| mManager = ACameraManager_create(); |
| |
| if (mManager == NULL) |
| { |
| IMLOGD0("[Initialize] manager is not created"); |
| return; |
| } |
| |
| EnumerateCamera(); |
| mCaptureRequest.outputNativeWindows.resize(0); |
| mCaptureRequest.sessionOutputs.resize(0); |
| mCaptureRequest.targets.resize(0); |
| } |
| |
| void ImsMediaCamera::DeInitialize() |
| { |
| IMLOGD0("[DeInitialize]"); |
| |
| for (auto& cam : gCameraIds) |
| { |
| if (cam.second.mDevice) |
| { |
| ACameraDevice_close(cam.second.mDevice); |
| } |
| } |
| |
| if (mManager) |
| { |
| camera_status_t status = |
| ACameraManager_unregisterAvailabilityCallback(mManager, GetManagerListener()); |
| |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE0("[DeInitialize] error[%s], GetErrorStr(status)"); |
| } |
| |
| ACameraManager_delete(mManager); |
| mManager = NULL; |
| } |
| |
| gCameraIds.clear(); |
| for (auto& cam : gCameraIds) |
| { |
| if (cam.second.mDevice) |
| { |
| ACameraDevice_close(cam.second.mDevice); |
| } |
| } |
| |
| if (mManager) |
| { |
| camera_status_t status = |
| ACameraManager_unregisterAvailabilityCallback(mManager, GetManagerListener()); |
| |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE0("[~ImsMediaCamera] error[%s], GetErrorStr(status)"); |
| } |
| |
| ACameraManager_delete(mManager); |
| mManager = NULL; |
| } |
| |
| gCameraIds.clear(); |
| } |
| |
| bool ImsMediaCamera::OpenCamera() |
| { |
| IMLOGD1("[OpenCamera] active camera[%s]", mActiveCameraId.c_str()); |
| |
| if (mManager == NULL) |
| { |
| return false; |
| } |
| |
| if (mActiveCameraId.compare(std::string("")) == 0) |
| { |
| IMLOGE0("[OpenCamera] no active camera"); |
| return false; |
| } |
| |
| // Create back facing camera device |
| camera_status_t status = ACameraManager_openCamera(mManager, mActiveCameraId.c_str(), |
| GetDeviceListener(), &gCameraIds[mActiveCameraId].mDevice); |
| |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE1("[OpenCamera] cannot open camera, error[%s]", GetErrorStr(status)); |
| return false; |
| } |
| |
| status = ACameraManager_registerAvailabilityCallback(mManager, GetManagerListener()); |
| |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE1("[OpenCamera] fail to register manager callback, error[%s]", GetErrorStr(status)); |
| return false; |
| } |
| |
| // Initialize camera controls(exposure time and sensitivity), pick |
| // up value of 2% * range + min as starting value (just a number, no magic) |
| ACameraMetadata* metadataObj; |
| ACameraManager_getCameraCharacteristics(mManager, mActiveCameraId.c_str(), &metadataObj); |
| ACameraMetadata_const_entry val; |
| status = ACameraMetadata_getConstEntry( |
| metadataObj, ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE, &val); |
| |
| if (status == ACAMERA_OK) |
| { |
| mExposureRange.min = val.data.i64[0]; |
| if (mExposureRange.min < kMinExposureTime) |
| { |
| mExposureRange.min = kMinExposureTime; |
| } |
| |
| mExposureRange.max = val.data.i64[1]; |
| |
| if (mExposureRange.max > kMaxExposureTime) |
| { |
| mExposureRange.max = kMaxExposureTime; |
| } |
| |
| mExposureTime = mExposureRange.value(2); |
| } |
| else |
| { |
| IMLOGW0("[OpenCamera] Unsupported ACAMERA_SENSOR_INFO_EXPOSURE_TIME_RANGE"); |
| mExposureRange.min = mExposureRange.max = 0l; |
| mExposureTime = 0l; |
| } |
| status = |
| ACameraMetadata_getConstEntry(metadataObj, ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE, &val); |
| |
| if (status == ACAMERA_OK) |
| { |
| mSensitivityRange.min = val.data.i32[0]; |
| mSensitivityRange.max = val.data.i32[1]; |
| mSensitivity = mSensitivityRange.value(2); |
| } |
| else |
| { |
| IMLOGW0("[OpenCamera] failed for ACAMERA_SENSOR_INFO_SENSITIVITY_RANGE"); |
| mSensitivityRange.min = mSensitivityRange.max = 0; |
| mSensitivity = 0; |
| } |
| |
| return true; |
| } |
| |
| void ImsMediaCamera::SetCameraConfig(int32_t cameraId, int32_t cameraZoom, int32_t framerate) |
| { |
| IMLOGD3("[SetCameraConfig] id[%d], zoom[%d], FPS[%d]", cameraId, cameraZoom, framerate); |
| uint32_t idx = 0; |
| for (std::map<std::string, CameraId>::iterator it = gCameraIds.begin(); it != gCameraIds.end(); |
| ++it) |
| { |
| if (idx == cameraId) |
| { |
| mActiveCameraId = (it->second).mId; |
| break; |
| } |
| ++idx; |
| } |
| |
| mCameraZoom = cameraZoom; |
| mFramerate = framerate; |
| } |
| |
| bool ImsMediaCamera::CreateSession(ANativeWindow* preview, ANativeWindow* recording) |
| { |
| if (preview == NULL) |
| { |
| return false; |
| } |
| |
| if (MatchCaptureSizeRequest(preview) == false) |
| { |
| IMLOGE0("[CreateSession] resolution is not matched"); |
| return false; |
| } |
| |
| mCaptureRequest.outputNativeWindows.push_back(preview); |
| |
| if (recording != NULL) |
| { |
| mCaptureRequest.outputNativeWindows.push_back(recording); |
| } |
| |
| mCaptureRequest.sessionOutputs.resize(mCaptureRequest.outputNativeWindows.size()); |
| mCaptureRequest.targets.resize(mCaptureRequest.outputNativeWindows.size()); |
| |
| // Create output from this app's ANativeWindow, and add into output container |
| recording == NULL ? mCaptureRequest.requestTemplate = TEMPLATE_PREVIEW |
| : mCaptureRequest.requestTemplate = TEMPLATE_RECORD; |
| |
| camera_status_t status = ACaptureSessionOutputContainer_create(&mSessionOutputContainer); |
| |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE1("[CreateSession] create output container, error[%s]", GetErrorStr(status)); |
| return false; |
| } |
| |
| for (int idxTarget = 0; idxTarget < mCaptureRequest.outputNativeWindows.size(); idxTarget++) |
| { |
| if (mCaptureRequest.outputNativeWindows[idxTarget] == NULL) |
| continue; |
| |
| IMLOGD0("[CreateSession] acquire window"); |
| ANativeWindow_acquire(mCaptureRequest.outputNativeWindows[idxTarget]); |
| status = ACaptureSessionOutput_create(mCaptureRequest.outputNativeWindows[idxTarget], |
| &mCaptureRequest.sessionOutputs[idxTarget]); |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE1("[CreateSession] create capture output, error[%s]", GetErrorStr(status)); |
| continue; |
| } |
| |
| ACaptureSessionOutputContainer_add( |
| mSessionOutputContainer, mCaptureRequest.sessionOutputs[idxTarget]); |
| status = ACameraOutputTarget_create(mCaptureRequest.outputNativeWindows[idxTarget], |
| &mCaptureRequest.targets[idxTarget]); |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE1("[CreateSession] create output target, error[%s]", GetErrorStr(status)); |
| continue; |
| } |
| } |
| |
| if (gCameraIds[mActiveCameraId].mAvailable == false) |
| { |
| gCondition.wait_timeout(MAX_WAIT_CAMERA); |
| } |
| |
| status = ACameraDevice_createCaptureRequest(gCameraIds[mActiveCameraId].mDevice, |
| mCaptureRequest.requestTemplate, &mCaptureRequest.request); |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE1("[CreateSession] create capture request, error[%s]", GetErrorStr(status)); |
| return false; |
| } |
| |
| for (int idxTarget = 0; idxTarget < mCaptureRequest.outputNativeWindows.size(); idxTarget++) |
| { |
| IMLOGD0("[CreateSession] add target"); |
| ACaptureRequest_addTarget(mCaptureRequest.request, mCaptureRequest.targets[idxTarget]); |
| } |
| |
| // Create a capture session for the given preview request |
| mCaptureSessionState = CaptureSessionState::kStateReady; |
| status = ACameraDevice_createCaptureSession(gCameraIds[mActiveCameraId].mDevice, |
| mSessionOutputContainer, GetSessionListener(), &mCaptureSession); |
| |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE1("[CreateSession] create capture session, error[%s]", GetErrorStr(status)); |
| return false; |
| } |
| |
| IMLOGD1("[CreateSession] create capture session[%p]", mCaptureSession); |
| |
| uint8_t afModeAuto = ACAMERA_CONTROL_AF_MODE_AUTO; |
| ACaptureRequest_setEntry_u8(mCaptureRequest.request, ACAMERA_CONTROL_AF_MODE, 1, &afModeAuto); |
| const int32_t targetFps[2] = {mFramerate, mFramerate}; |
| ACaptureRequest_setEntry_i32( |
| mCaptureRequest.request, ACAMERA_CONTROL_AE_TARGET_FPS_RANGE, 2, targetFps); |
| |
| return true; |
| } |
| |
| bool ImsMediaCamera::DeleteSession() |
| { |
| IMLOGD0("[DeleteSession]"); |
| camera_status_t status; |
| |
| if (mCaptureSession != NULL) |
| { |
| IMLOGD0("[DeleteSession] session close"); |
| gCondition.reset(); |
| ACameraCaptureSession_close(mCaptureSession); |
| gCondition.wait_timeout(MAX_WAIT_CAMERA); |
| mCaptureSession = NULL; |
| } |
| |
| for (int idxTarget = 0; idxTarget < mCaptureRequest.outputNativeWindows.size(); idxTarget++) |
| { |
| if (mCaptureRequest.outputNativeWindows[idxTarget] == NULL) |
| { |
| continue; |
| } |
| |
| status = ACaptureRequest_removeTarget( |
| mCaptureRequest.request, mCaptureRequest.targets[idxTarget]); |
| |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE1("[DeleteSession] error ACaptureRequest_removeTarget[%s]", GetErrorStr(status)); |
| } |
| |
| ACameraOutputTarget_free(mCaptureRequest.targets[idxTarget]); |
| status = ACaptureSessionOutputContainer_remove( |
| mSessionOutputContainer, mCaptureRequest.sessionOutputs[idxTarget]); |
| |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE1("[DeleteSession] error ACaptureSessionOutputContainer_remove[%s]", |
| GetErrorStr(status)); |
| } |
| |
| ACaptureSessionOutput_free(mCaptureRequest.sessionOutputs[idxTarget]); |
| ANativeWindow_release(mCaptureRequest.outputNativeWindows[idxTarget]); |
| } |
| |
| if (mCaptureRequest.request != NULL) |
| { |
| IMLOGD0("[DeleteSession] free request"); |
| ACaptureRequest_free(mCaptureRequest.request); |
| } |
| |
| mCaptureRequest.outputNativeWindows.resize(0); |
| mCaptureRequest.sessionOutputs.resize(0); |
| mCaptureRequest.targets.resize(0); |
| |
| if (mSessionOutputContainer != NULL) |
| { |
| IMLOGD0("[DeleteSession] free container"); |
| ACaptureSessionOutputContainer_free(mSessionOutputContainer); |
| } |
| |
| return true; |
| } |
| |
| bool ImsMediaCamera::StartSession(bool bRecording) |
| { |
| IMLOGD1("[StartSession] recording[%d]", bRecording); |
| |
| camera_status_t status; |
| bRecording ? mCameraMode = kCameraModeRecord : kCameraModePreview; |
| |
| gCondition.reset(); |
| status = ACameraCaptureSession_setRepeatingRequest( |
| mCaptureSession, NULL, 1, &mCaptureRequest.request, NULL); |
| |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE1("[StartSession] error[%s]", GetErrorStr(status)); |
| return false; |
| } |
| |
| gCondition.wait_timeout(MAX_WAIT_CAMERA); |
| return true; |
| } |
| |
| bool ImsMediaCamera::StopSession() |
| { |
| IMLOGD1("[StopSession] state[%d]", mCaptureSessionState); |
| |
| if (mCaptureSessionState == CaptureSessionState::kStateActive) |
| { |
| gCondition.reset(); |
| camera_status_t status = ACameraCaptureSession_stopRepeating(mCaptureSession); |
| |
| if (status != ACAMERA_OK) |
| { |
| IMLOGE1("[StopSession] stopRepeating error[%s]", GetErrorStr(status)); |
| return false; |
| } |
| } |
| |
| gCondition.wait_timeout(MAX_WAIT_CAMERA); |
| return true; |
| } |
| |
| /* |
| * Camera Manager Listener object |
| */ |
| void OnCameraAvailable(void* context, const char* id) |
| { |
| IMLOGD1("[OnCameraAvailable] id[%s]", id == NULL ? "NULL" : id); |
| |
| if (context != NULL) |
| { |
| reinterpret_cast<ImsMediaCamera*>(context)->OnCameraStatusChanged(id, true); |
| } |
| } |
| |
| void OnCameraUnavailable(void* context, const char* id) |
| { |
| IMLOGD1("[OnCameraUnavailable] id[%s]", id == NULL ? "NULL" : id); |
| |
| if (context != NULL) |
| { |
| reinterpret_cast<ImsMediaCamera*>(context)->OnCameraStatusChanged(id, false); |
| } |
| } |
| |
| void ImsMediaCamera::OnCameraStatusChanged(const char* id, bool available) |
| { |
| IMLOGD2("[OnCameraStatusChanged] id[%s], available[%d]", id == NULL ? "NULL" : id, available); |
| |
| if (id != NULL && mManager != NULL && !gCameraIds.empty()) |
| { |
| if (gCameraIds.find(std::string(id)) != gCameraIds.end()) |
| { |
| gCameraIds[std::string(id)].mAvailable = available; |
| |
| if (available) |
| { |
| gCondition.signal(); |
| } |
| } |
| } |
| } |
| |
| ACameraManager_AvailabilityCallbacks* ImsMediaCamera::GetManagerListener() |
| { |
| static ACameraManager_AvailabilityCallbacks cameraMgrListener = { |
| .context = this, |
| .onCameraAvailable = ::OnCameraAvailable, |
| .onCameraUnavailable = ::OnCameraUnavailable, |
| }; |
| return &cameraMgrListener; |
| } |
| |
| /* |
| * CameraDevice callbacks |
| */ |
| void OnDeviceStateChanges(void* context, ACameraDevice* dev) |
| { |
| IMLOGW0("[OnDeviceStateChanges]"); |
| |
| if (context != NULL) |
| { |
| reinterpret_cast<ImsMediaCamera*>(context)->OnDeviceState(dev); |
| } |
| } |
| |
| void OnDeviceErrorChanges(void* context, ACameraDevice* dev, int err) |
| { |
| IMLOGW0("[OnDeviceErrorChanges]"); |
| |
| if (context != NULL) |
| { |
| reinterpret_cast<ImsMediaCamera*>(context)->OnDeviceError(dev, err); |
| } |
| } |
| |
| ACameraDevice_stateCallbacks* ImsMediaCamera::GetDeviceListener() |
| { |
| static ACameraDevice_stateCallbacks cameraDeviceListener = { |
| .context = this, |
| .onDisconnected = ::OnDeviceStateChanges, |
| .onError = ::OnDeviceErrorChanges, |
| }; |
| return &cameraDeviceListener; |
| } |
| |
| void ImsMediaCamera::OnDeviceState(ACameraDevice* dev) |
| { |
| std::string id(ACameraDevice_getId(dev)); |
| IMLOGW1("[OnDeviceState] device %s is disconnected", id.c_str()); |
| gCameraIds[id].mAvailable = false; |
| ACameraDevice_close(gCameraIds[id].mDevice); |
| gCameraIds.erase(id); |
| } |
| |
| /* |
| * CameraDevice error state translation, used in |
| * ACameraDevice_ErrorStateCallback |
| */ |
| using DEV_ERROR_PAIR = std::pair<int, const char*>; |
| static std::vector<DEV_ERROR_PAIR> devErrors{ |
| MAKE_PAIR(ERROR_CAMERA_IN_USE), |
| MAKE_PAIR(ERROR_MAX_CAMERAS_IN_USE), |
| MAKE_PAIR(ERROR_CAMERA_DISABLED), |
| MAKE_PAIR(ERROR_CAMERA_DEVICE), |
| MAKE_PAIR(ERROR_CAMERA_SERVICE), |
| }; |
| |
| const char* GetCameraDeviceErrorStr(int err) |
| { |
| return GetPairStr<int>(err, devErrors); |
| } |
| |
| void PrintCameraDeviceError(int err) |
| { |
| IMLOGD2("[PrintCameraDeviceError] CameraDeviceError(%#x): %s", err, |
| GetCameraDeviceErrorStr(err)); |
| } |
| |
| void ImsMediaCamera::OnDeviceError(ACameraDevice* dev, int err) |
| { |
| std::string id(ACameraDevice_getId(dev)); |
| IMLOGE2("[OnDeviceError] CameraDevice %s is in error %#x", id.c_str(), err); |
| PrintCameraDeviceError(err); |
| |
| CameraId& cam = gCameraIds[id]; |
| |
| switch (err) |
| { |
| case ERROR_CAMERA_IN_USE: |
| cam.mAvailable = false; |
| cam.mOwner = false; |
| break; |
| case ERROR_CAMERA_SERVICE: |
| case ERROR_CAMERA_DEVICE: |
| case ERROR_CAMERA_DISABLED: |
| case ERROR_MAX_CAMERAS_IN_USE: |
| cam.mAvailable = false; |
| cam.mOwner = false; |
| break; |
| default: |
| IMLOGD1("[OnDeviceError] Unknown Camera Device Error: %#x", err); |
| } |
| } |
| |
| // CaptureSession state callbacks |
| void OnSessionClosed(void* context, ACameraCaptureSession* session) |
| { |
| IMLOGW1("[OnSessionClosed] session[%p] closed", session); |
| reinterpret_cast<ImsMediaCamera*>(context)->OnSessionState( |
| session, CaptureSessionState::kStateClosed); |
| } |
| |
| void OnSessionReady(void* context, ACameraCaptureSession* session) |
| { |
| IMLOGW1("[OnSessionReady] session[%p] ready", session); |
| reinterpret_cast<ImsMediaCamera*>(context)->OnSessionState( |
| session, CaptureSessionState::kStateReady); |
| } |
| |
| void OnSessionActive(void* context, ACameraCaptureSession* session) |
| { |
| IMLOGW1("[OnSessionActive] session[%p] active", session); |
| reinterpret_cast<ImsMediaCamera*>(context)->OnSessionState( |
| session, CaptureSessionState::kStateActive); |
| } |
| |
| ACameraCaptureSession_stateCallbacks* ImsMediaCamera::GetSessionListener() |
| { |
| static ACameraCaptureSession_stateCallbacks sessionListener = { |
| .context = this, |
| .onClosed = ::OnSessionClosed, |
| .onReady = ::OnSessionReady, |
| .onActive = ::OnSessionActive, |
| }; |
| return &sessionListener; |
| } |
| |
| void ImsMediaCamera::OnSessionState(ACameraCaptureSession* session, CaptureSessionState state) |
| { |
| IMLOGD0("[OnSessionState]"); |
| |
| if (mCaptureSession == NULL) |
| { |
| IMLOGW0("[OnSessionState] CaptureSession closed"); |
| return; |
| } |
| |
| if (!session || session != mCaptureSession) |
| { |
| IMLOGW1("[OnSessionState] CaptureSession is %s", (session ? "NOT our session" : "NULL")); |
| return; |
| } |
| |
| if (state >= CaptureSessionState::kStateMax) |
| { |
| IMLOGE1("[OnSessionState] Wrong state[%d]", state); |
| } |
| else |
| { |
| mCaptureSessionState = state; |
| gCondition.signal(); |
| IMLOGD1("[OnSessionState] state[%d]", state); |
| } |
| } |
| |
| void ImsMediaCamera::EnumerateCamera() |
| { |
| if (mManager == NULL) |
| { |
| return; |
| } |
| |
| ACameraIdList* cameraIds = NULL; |
| auto ret = ACameraManager_getCameraIdList(mManager, &cameraIds); |
| |
| if (ret != ACAMERA_OK) |
| { |
| return; |
| } |
| |
| for (int i = 0; i < cameraIds->numCameras; ++i) |
| { |
| const char* id = cameraIds->cameraIds[i]; |
| ACameraMetadata* metadataObj; |
| ACameraManager_getCameraCharacteristics(mManager, id, &metadataObj); |
| |
| int32_t count = 0; |
| const uint32_t* tags = NULL; |
| ACameraMetadata_getAllTags(metadataObj, &count, &tags); |
| |
| for (int tagIdx = 0; tagIdx < count; ++tagIdx) |
| { |
| if (ACAMERA_LENS_FACING == tags[tagIdx]) |
| { |
| ACameraMetadata_const_entry lensInfo; |
| ACameraMetadata_getConstEntry(metadataObj, tags[tagIdx], &lensInfo); |
| CameraId cam(id); |
| cam.mFacing = static_cast<acamera_metadata_enum_android_lens_facing_t>( |
| lensInfo.data.u8[0]); |
| cam.mOwner = false; |
| cam.mDevice = NULL; |
| gCameraIds[cam.mId] = cam; |
| IMLOGD2("[EnumerateCamera] cameraId[%s], facing[%d]", cam.mId.c_str(), cam.mFacing); |
| } |
| } |
| ACameraMetadata_free(metadataObj); |
| } |
| |
| if (gCameraIds.size() == 0) |
| { |
| IMLOGD0("[EnumerateCamera] No Camera Available on the device"); |
| } |
| |
| ACameraManager_deleteCameraIdList(cameraIds); |
| } |
| |
| bool ImsMediaCamera::GetSensorOrientation(const int cameraId, int32_t* facing, int32_t* angle) |
| { |
| if (!mManager) |
| { |
| return false; |
| } |
| |
| camera_status_t status; |
| ACameraMetadata* metadataObj; |
| std::string targetCameraId; |
| uint32_t idx = 0; |
| for (std::map<std::string, CameraId>::iterator it = gCameraIds.begin(); it != gCameraIds.end(); |
| ++it) |
| { |
| if (idx == cameraId) |
| { |
| status = ACameraManager_getCameraCharacteristics( |
| mManager, gCameraIds[(it->second).mId].mId.c_str(), &metadataObj); |
| if (status == ACAMERA_OK) |
| { |
| ACameraMetadata_const_entry face, orientation; |
| ACameraMetadata_getConstEntry(metadataObj, ACAMERA_LENS_FACING, &face); |
| mCameraFacing = static_cast<int32_t>(face.data.u8[0]); |
| ACameraMetadata_getConstEntry( |
| metadataObj, ACAMERA_SENSOR_ORIENTATION, &orientation); |
| ACameraMetadata_free(metadataObj); |
| mCameraOrientation = orientation.data.i32[0]; |
| |
| mCameraFacing == 0 ? * facing = kCameraFacingFront : * facing = kCameraFacingRear; |
| *angle = mCameraOrientation; |
| return true; |
| } |
| } |
| ++idx; |
| } |
| |
| return false; |
| } |
| |
| bool ImsMediaCamera::GetExposureRange(int64_t* min, int64_t* max, int64_t* curVal) |
| { |
| if (!mExposureRange.Supported() || !mExposureTime || !min || !max || !curVal) |
| { |
| return false; |
| } |
| *min = mExposureRange.min; |
| *max = mExposureRange.max; |
| *curVal = mExposureTime; |
| |
| return true; |
| } |
| |
| bool ImsMediaCamera::GetSensitivityRange(int64_t* min, int64_t* max, int64_t* curVal) |
| { |
| if (!mSensitivityRange.Supported() || !mSensitivity || !min || !max || !curVal) |
| { |
| return false; |
| } |
| *min = static_cast<int64_t>(mSensitivityRange.min); |
| *max = static_cast<int64_t>(mSensitivityRange.max); |
| *curVal = mSensitivity; |
| return true; |
| } |
| |
| /** |
| * A helper class to assist image size comparison, by comparing the absolute |
| * size |
| * regardless of the portrait or landscape mode. |
| */ |
| class DisplayDimension |
| { |
| public: |
| DisplayDimension(int32_t w, int32_t h) : |
| w_(w), |
| h_(h), |
| portrait_(false) |
| { |
| if (h > w) |
| { |
| // make it landscape |
| w_ = h; |
| h_ = w; |
| portrait_ = true; |
| } |
| } |
| DisplayDimension(const DisplayDimension& other) |
| { |
| w_ = other.w_; |
| h_ = other.h_; |
| portrait_ = other.portrait_; |
| } |
| |
| DisplayDimension(void) |
| { |
| w_ = 0; |
| h_ = 0; |
| portrait_ = false; |
| } |
| DisplayDimension& operator=(const DisplayDimension& other) |
| { |
| w_ = other.w_; |
| h_ = other.h_; |
| portrait_ = other.portrait_; |
| |
| return (*this); |
| } |
| |
| bool IsSameRatio(DisplayDimension& other) { return (w_ * other.h_ == h_ * other.w_); } |
| bool operator>(DisplayDimension& other) { return (w_ >= other.w_ & h_ >= other.h_); } |
| bool operator==(DisplayDimension& other) |
| { |
| return (w_ == other.w_ && h_ == other.h_ && portrait_ == other.portrait_); |
| } |
| DisplayDimension operator-(DisplayDimension& other) |
| { |
| DisplayDimension delta(w_ - other.w_, h_ - other.h_); |
| return delta; |
| } |
| void Flip(void) { portrait_ = !portrait_; } |
| bool IsPortrait(void) { return portrait_; } |
| int32_t width(void) { return w_; } |
| int32_t height(void) { return h_; } |
| int32_t org_width(void) { return (portrait_ ? h_ : w_); } |
| int32_t org_height(void) { return (portrait_ ? w_ : h_); } |
| |
| private: |
| int32_t w_, h_; |
| bool portrait_; |
| }; |
| |
| bool ImsMediaCamera::MatchCaptureSizeRequest(ANativeWindow* window) |
| { |
| DisplayDimension disp(ANativeWindow_getWidth(window), ANativeWindow_getHeight(window)); |
| IMLOGD3("[MatchCaptureSizeRequest] request width[%d], height[%d], camOrientation[%d]", |
| disp.width(), disp.height(), mCameraOrientation); |
| if (mCameraOrientation == 90 || mCameraOrientation == 270) |
| { |
| disp.Flip(); |
| } |
| |
| ACameraMetadata* metadata; |
| ACameraManager_getCameraCharacteristics(mManager, mActiveCameraId.c_str(), &metadata); |
| ACameraMetadata_const_entry entry; |
| ACameraMetadata_getConstEntry(metadata, ACAMERA_SCALER_AVAILABLE_STREAM_CONFIGURATIONS, &entry); |
| |
| for (int32_t i = 0; i < entry.count; i += 4) |
| { |
| int32_t input = entry.data.i32[i + 3]; |
| int32_t format = entry.data.i32[i + 0]; |
| if (input) |
| continue; |
| |
| if (format == AIMAGE_FORMAT_YUV_420_888 || format == AIMAGE_FORMAT_JPEG) |
| { |
| DisplayDimension dimension(entry.data.i32[i + 1], entry.data.i32[i + 2]); |
| if (!disp.IsSameRatio(dimension)) |
| continue; |
| |
| // here only width and height should be compared and not portrait flag. |
| if (disp.width() == dimension.width() && disp.height() == dimension.height()) |
| { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |