/* * Copyright (C) 2011 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. */ /* * Contains implementation of a class EmulatedCamera that encapsulates * functionality common to all emulated cameras ("fake", "webcam", "video file", * etc.). Instances of this class (for each emulated camera) are created during * the construction of the EmulatedCameraFactory instance. This class serves as * an entry point for all camera API calls that defined by camera_device_ops_t * API. */ #define LOG_NDEBUG 0 #define LOG_TAG "EmulatedCamera_Camera" #include #include #include "EmulatedCamera.h" //#include "EmulatedFakeCameraDevice.h" #include "Converters.h" /* Defines whether we should trace parameter changes. */ #define DEBUG_PARAM 1 namespace android { static const char* kValidFocusModes[] = { CameraParameters::FOCUS_MODE_AUTO, CameraParameters::FOCUS_MODE_INFINITY, CameraParameters::FOCUS_MODE_MACRO, CameraParameters::FOCUS_MODE_FIXED, CameraParameters::FOCUS_MODE_EDOF, CameraParameters::FOCUS_MODE_CONTINUOUS_VIDEO, CameraParameters::FOCUS_MODE_CONTINUOUS_PICTURE, }; #if DEBUG_PARAM /* Calculates and logs parameter changes. * Param: * current - Current set of camera parameters. * new_par - String representation of new parameters. */ static void PrintParamDiff(const CameraParameters& current, const char* new_par); #else #define PrintParamDiff(current, new_par) (void(0)) #endif /* DEBUG_PARAM */ /* * Check if a given string |value| equals at least one of the strings in |list| */ template static bool IsValueInList(const char* value, const char* const (&list)[N]) { for (size_t i = 0; i < N; ++i) { if (strcmp(value, list[i]) == 0) { return true; } } return false; } static bool StringsEqual(const char* str1, const char* str2) { if (str1 == nullptr && str2 == nullptr) { return true; } if (str1 == nullptr || str2 == nullptr) { return false; } return strcmp(str1, str2) == 0; } static bool GetFourCcFormatFromCameraParam(const char* fmt_str, uint32_t* fmt_val) { if (strcmp(fmt_str, CameraParameters::PIXEL_FORMAT_YUV420P) == 0) { // Despite the name above this is a YVU format, specifically YV12 *fmt_val = V4L2_PIX_FMT_YVU420; return true; } else if (strcmp(fmt_str, CameraParameters::PIXEL_FORMAT_RGBA8888) == 0) { *fmt_val = V4L2_PIX_FMT_RGB32; return true; } else if (strcmp(fmt_str, CameraParameters::PIXEL_FORMAT_YUV420SP) == 0) { *fmt_val = V4L2_PIX_FMT_NV21; return true; } return false; } EmulatedCamera::EmulatedCamera(int cameraId, struct hw_module_t* module, GraphicBufferMapper* gbm) : EmulatedBaseCamera(cameraId, HARDWARE_DEVICE_API_VERSION(1, 0), &common, module), mPreviewWindow(gbm), mCallbackNotifier() { /* camera_device v1 fields. */ common.close = EmulatedCamera::close; ops = &mDeviceOps; priv = this; } EmulatedCamera::~EmulatedCamera() { } /**************************************************************************** * Public API ***************************************************************************/ status_t EmulatedCamera::Initialize() { /* Preview formats supported by this HAL. */ char preview_formats[1024]; snprintf(preview_formats, sizeof(preview_formats), "%s,%s,%s", CameraParameters::PIXEL_FORMAT_YUV420SP, CameraParameters::PIXEL_FORMAT_YUV420P, CameraParameters::PIXEL_FORMAT_RGBA8888); /* * Fake required parameters. */ mParameters.set(CameraParameters::KEY_RECORDING_HINT, CameraParameters::FALSE); mParameters.set(CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES, "320x240,0x0"); mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, "320"); mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, "240"); mParameters.set(CameraParameters::KEY_JPEG_QUALITY, "90"); // Camera values for a Logitech B910 HD Webcam // Focal length: 4.90 mm (from specs) // Horizontal view angle: 61 degrees for 4:3 sizes, // 70 degrees for 16:9 sizes (empirical) // Vertical view angle: 45.8 degrees (= 61 * 3 / 4) // (The Mac has only "4:3" image sizes; the correct angle // is 51.0 degrees. [MacBook Pro (Retina, 15-inch, Mid 2014)]) mParameters.set(CameraParameters::KEY_FOCAL_LENGTH, "4.90"); mParameters.set(CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE, "61.0"); mParameters.set(CameraParameters::KEY_VERTICAL_VIEW_ANGLE, "45.8"); mParameters.set(CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY, "90"); /* Preview format settings used here are related to panoramic view only. It's * not related to the preview window that works only with RGB frames, which * is explicitly stated when set_buffers_geometry is called on the preview * window object. */ mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FORMATS, preview_formats); mParameters.setPreviewFormat(CameraParameters::PIXEL_FORMAT_YUV420SP); /* We don't rely on the actual frame rates supported by the camera device, * since we will emulate them through timeouts in the emulated camera device * worker thread. */ mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FRAME_RATES, "30,24,20,15,10,5"); mParameters.set(CameraParameters::KEY_SUPPORTED_PREVIEW_FPS_RANGE, "(30000,30000)"); mParameters.set(CameraParameters::KEY_PREVIEW_FPS_RANGE, "30000,30000"); mParameters.setPreviewFrameRate(30); /* Only PIXEL_FORMAT_YUV420P is accepted by video framework in emulator! */ mParameters.set(CameraParameters::KEY_VIDEO_FRAME_FORMAT, CameraParameters::PIXEL_FORMAT_YUV420P); mParameters.set(CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS, CameraParameters::PIXEL_FORMAT_JPEG); mParameters.setPictureFormat(CameraParameters::PIXEL_FORMAT_JPEG); /* Set exposure compensation. */ mParameters.set(CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION, "6"); mParameters.set(CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION, "-6"); mParameters.set(CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP, "0.5"); mParameters.set(CameraParameters::KEY_EXPOSURE_COMPENSATION, "0"); /* Sets the white balance modes and the device-dependent scale factors. */ char supported_white_balance[1024]; snprintf(supported_white_balance, sizeof(supported_white_balance), "%s,%s,%s,%s", CameraParameters::WHITE_BALANCE_AUTO, CameraParameters::WHITE_BALANCE_INCANDESCENT, CameraParameters::WHITE_BALANCE_DAYLIGHT, CameraParameters::WHITE_BALANCE_TWILIGHT); mParameters.set(CameraParameters::KEY_SUPPORTED_WHITE_BALANCE, supported_white_balance); mParameters.set(CameraParameters::KEY_WHITE_BALANCE, CameraParameters::WHITE_BALANCE_AUTO); getCameraDevice()->initializeWhiteBalanceModes( CameraParameters::WHITE_BALANCE_AUTO, 1.0f, 1.0f); getCameraDevice()->initializeWhiteBalanceModes( CameraParameters::WHITE_BALANCE_INCANDESCENT, 1.38f, 0.60f); getCameraDevice()->initializeWhiteBalanceModes( CameraParameters::WHITE_BALANCE_DAYLIGHT, 1.09f, 0.92f); getCameraDevice()->initializeWhiteBalanceModes( CameraParameters::WHITE_BALANCE_TWILIGHT, 0.92f, 1.22f); getCameraDevice()->setWhiteBalanceMode(CameraParameters::WHITE_BALANCE_AUTO); /* Set suported antibanding values */ mParameters.set(CameraParameters::KEY_SUPPORTED_ANTIBANDING, CameraParameters::ANTIBANDING_AUTO); mParameters.set(CameraParameters::KEY_ANTIBANDING, CameraParameters::ANTIBANDING_AUTO); /* Set control effect mode * Bug: 30862244 * */ mParameters.set(CameraParameters::KEY_SUPPORTED_EFFECTS, CameraParameters::EFFECT_NONE); mParameters.set(CameraParameters::KEY_EFFECT, CameraParameters::EFFECT_NONE); /* Set focus distances for "near,optimal,far" */ mParameters.set(CameraParameters::KEY_FOCUS_DISTANCES, "Infinity,Infinity,Infinity"); /* Not supported features */ mParameters.set(CameraParameters::KEY_SUPPORTED_FOCUS_MODES, CameraParameters::FOCUS_MODE_FIXED); mParameters.set(CameraParameters::KEY_FOCUS_MODE, CameraParameters::FOCUS_MODE_FIXED); return NO_ERROR; } void EmulatedCamera::onNextFrameAvailable(nsecs_t timestamp, EmulatedCameraDevice* camera_dev) { /* Notify the preview window first. */ mPreviewWindow.onNextFrameAvailable(timestamp, camera_dev); /* Notify callback notifier next. */ mCallbackNotifier.onNextFrameAvailable(timestamp, camera_dev); } void EmulatedCamera::onCameraDeviceError(int err) { /* Errors are reported through the callback notifier */ mCallbackNotifier.onCameraDeviceError(err); } void EmulatedCamera::setTakingPicture(bool takingPicture) { mCallbackNotifier.setTakingPicture(takingPicture); } /**************************************************************************** * Camera API implementation. ***************************************************************************/ status_t EmulatedCamera::connectCamera(hw_device_t** device) { ALOGV("%s", __FUNCTION__); status_t res = EINVAL; EmulatedCameraDevice* const camera_dev = getCameraDevice(); ALOGE_IF(camera_dev == NULL, "%s: No camera device instance.", __FUNCTION__); if (camera_dev != NULL) { /* Connect to the camera device. */ res = getCameraDevice()->connectDevice(); if (res == NO_ERROR) { *device = &common; } } return -res; } status_t EmulatedCamera::closeCamera() { ALOGV("%s", __FUNCTION__); return cleanupCamera(); } status_t EmulatedCamera::getCameraInfo(struct camera_info* info) { ALOGV("%s", __FUNCTION__); const char* valstr = NULL; valstr = mParameters.get(EmulatedCamera::FACING_KEY); if (valstr != NULL) { if (strcmp(valstr, EmulatedCamera::FACING_FRONT) == 0) { info->facing = CAMERA_FACING_FRONT; } else if (strcmp(valstr, EmulatedCamera::FACING_BACK) == 0) { info->facing = CAMERA_FACING_BACK; } } else { info->facing = CAMERA_FACING_BACK; } valstr = mParameters.get(EmulatedCamera::ORIENTATION_KEY); if (valstr != NULL) { info->orientation = atoi(valstr); } else { info->orientation = 0; } return EmulatedBaseCamera::getCameraInfo(info); } void EmulatedCamera::autoFocusComplete() { mCallbackNotifier.autoFocusComplete(); } status_t EmulatedCamera::setPreviewWindow(struct preview_stream_ops* window) { /* Callback should return a negative errno. */ return -mPreviewWindow.setPreviewWindow(window, mParameters.getPreviewFrameRate()); } void EmulatedCamera::setCallbacks(camera_notify_callback notify_cb, camera_data_callback data_cb, camera_data_timestamp_callback data_cb_timestamp, camera_request_memory get_memory, void* user) { mCallbackNotifier.setCallbacks(notify_cb, data_cb, data_cb_timestamp, get_memory, user); } void EmulatedCamera::enableMsgType(int32_t msg_type) { mCallbackNotifier.enableMessage(msg_type); } void EmulatedCamera::disableMsgType(int32_t msg_type) { mCallbackNotifier.disableMessage(msg_type); } int EmulatedCamera::isMsgTypeEnabled(int32_t msg_type) { return mCallbackNotifier.isMessageEnabled(msg_type); } status_t EmulatedCamera::startPreview() { /* Callback should return a negative errno. */ return -doStartPreview(); } void EmulatedCamera::stopPreview() { /* The camera client will not pass on calls to set the preview window to * NULL if the preview is not enabled. If preview is not enabled the camera * client will instead simply destroy the preview window without notifying * the HAL. Later on when preview is enabled again that means the HAL will * attempt to use the old, destroyed window which will cause a crash. * Instead we need to clear the preview window here, the client will set * a preview window when needed. The preview window is cleared here instead * of inside doStopPreview to prevent the window from being cleared when * restarting the preview because of a parameter change. */ mPreviewWindow.setPreviewWindow(nullptr, 0); doStopPreview(); } int EmulatedCamera::isPreviewEnabled() { return mPreviewWindow.isPreviewEnabled(); } status_t EmulatedCamera::storeMetaDataInBuffers(int enable) { /* Callback should return a negative errno. */ return mCallbackNotifier.storeMetaDataInBuffers(enable); } status_t EmulatedCamera::startRecording() { /* This callback should return a negative errno, hence all the negations */ if (!mPreviewWindow.isPreviewEnabled()) { ALOGE("%s: start recording without preview enabled", __FUNCTION__); return INVALID_OPERATION; } int frameRate = mParameters.getPreviewFrameRate(); status_t res = mCallbackNotifier.enableVideoRecording(frameRate); if (res != NO_ERROR) { ALOGE("%s: CallbackNotifier failed to enable video recording", __FUNCTION__); stopRecording(); return -res; } EmulatedCameraDevice* const camera_dev = getCameraDevice(); if (camera_dev == nullptr || !camera_dev->isStarted()) { // No need for restarts, the next preview start will use correct params return NO_ERROR; } // If the camera is running we might have to restart it to accomodate // whatever pixel format and frame size the caller wants. uint32_t conf_fmt = 0; res = getConfiguredPixelFormat(&conf_fmt); if (res != NO_ERROR) { stopRecording(); return -res; } uint32_t cur_fmt = camera_dev->getOriginalPixelFormat(); int conf_width = -1, conf_height = -1; res = getConfiguredFrameSize(&conf_width, &conf_height); if (res != NO_ERROR) { stopRecording(); return -res; } int cur_width = camera_dev->getFrameWidth(); int cur_height = camera_dev->getFrameHeight(); if (cur_fmt != conf_fmt || cur_width != conf_width || cur_height != conf_height) { // We need to perform a restart to use the new format or size and it // has to be an asynchronous restart or this might block if the camera // thread is currently delivering a frame. if (!camera_dev->requestRestart(conf_width, conf_height, conf_fmt, false /* takingPicture */, false /* oneBurst */)) { ALOGE("%s: Could not restart preview with new pixel format", __FUNCTION__); stopRecording(); return -EINVAL; } } ALOGD("go all the way to the end"); return NO_ERROR; } void EmulatedCamera::stopRecording() { mCallbackNotifier.disableVideoRecording(); } int EmulatedCamera::isRecordingEnabled() { return mCallbackNotifier.isVideoRecordingEnabled(); } void EmulatedCamera::releaseRecordingFrame(const void* opaque) { mCallbackNotifier.releaseRecordingFrame(opaque); } status_t EmulatedCamera::setAutoFocus() { // Make sure to check that a preview is in progress. Otherwise this will // silently fail because no callback will be called until the preview starts // which might be never. if (!isPreviewEnabled()) { return EINVAL; } EmulatedCameraDevice* const camera_dev = getCameraDevice(); if (camera_dev && camera_dev->isStarted()) { return camera_dev->setAutoFocus(); } return EINVAL; } status_t EmulatedCamera::cancelAutoFocus() { // In this case we don't check if a preview is in progress or not. Unlike // setAutoFocus this call will not silently fail without the check. If an // auto-focus request is somehow pending without having preview enabled this // will correctly cancel that pending auto-focus which seems reasonable. EmulatedCameraDevice* const camera_dev = getCameraDevice(); if (camera_dev && camera_dev->isStarted()) { return camera_dev->cancelAutoFocus(); } return EINVAL; } status_t EmulatedCamera::takePicture() { ALOGV("%s", __FUNCTION__); int width, height; uint32_t org_fmt; /* Collect frame info for the picture. */ mParameters.getPictureSize(&width, &height); const char* pix_fmt = mParameters.getPictureFormat(); if (!GetFourCcFormatFromCameraParam(pix_fmt, &org_fmt)) { // Also check for JPEG here, the function above does not do this since // this is very specific to this use case. if (strcmp(pix_fmt, CameraParameters::PIXEL_FORMAT_JPEG) == 0) { /* We only have JPEG converted for NV21 format. */ org_fmt = V4L2_PIX_FMT_NV21; } else { ALOGE("%s: Unsupported pixel format %s", __FUNCTION__, pix_fmt); return EINVAL; } } /* Get JPEG quality. */ int jpeg_quality = mParameters.getInt(CameraParameters::KEY_JPEG_QUALITY); if (jpeg_quality <= 0) { jpeg_quality = 90; /* Fall back to default. */ } /* * Make sure preview is not running, and device is stopped before taking * picture. */ EmulatedCameraDevice* const camera_dev = getCameraDevice(); mCallbackNotifier.setJpegQuality(jpeg_quality); mCallbackNotifier.setCameraParameters(mParameters); ALOGD("Starting camera for picture: %.4s(%s)[%dx%d]", reinterpret_cast(&org_fmt), pix_fmt, width, height); if (mPreviewWindow.isPreviewEnabled()) { mPreviewWindow.stopPreview(); /* If the camera preview is enabled we need to perform an asynchronous * restart. A blocking restart could deadlock this thread as it's * currently holding the camera client lock and the frame delivery could * be stuck on waiting for that lock. If this was synchronous then this * thread would in turn get stuck on waiting for the delivery thread. */ if (!camera_dev->requestRestart(width, height, org_fmt, true /* takingPicture */, true /* oneBurst */)) { return UNKNOWN_ERROR; } return NO_ERROR; } else { ALOGE("%s: preview has not been enabled", __FUNCTION__); return EINVAL; } } status_t EmulatedCamera::cancelPicture() { ALOGV("%s", __FUNCTION__); return NO_ERROR; } status_t EmulatedCamera::setParameters(const char* parms) { ALOGV("%s", __FUNCTION__); PrintParamDiff(mParameters, parms); CameraParameters new_param; String8 str8_param(parms); new_param.unflatten(str8_param); bool restartPreview = false; /* * Check for new exposure compensation parameter. */ int new_exposure_compensation = new_param.getInt( CameraParameters::KEY_EXPOSURE_COMPENSATION); const int min_exposure_compensation = new_param.getInt( CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION); const int max_exposure_compensation = new_param.getInt( CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION); // Checks if the exposure compensation change is supported. if ((min_exposure_compensation != 0) || (max_exposure_compensation != 0)) { if (new_exposure_compensation > max_exposure_compensation) { new_exposure_compensation = max_exposure_compensation; } if (new_exposure_compensation < min_exposure_compensation) { new_exposure_compensation = min_exposure_compensation; } const int current_exposure_compensation = mParameters.getInt( CameraParameters::KEY_EXPOSURE_COMPENSATION); if (current_exposure_compensation != new_exposure_compensation) { const float exposure_value = new_exposure_compensation * new_param.getFloat( CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP); getCameraDevice()->setExposureCompensation( exposure_value); } } const char* new_white_balance = new_param.get( CameraParameters::KEY_WHITE_BALANCE); const char* supported_white_balance = new_param.get( CameraParameters::KEY_SUPPORTED_WHITE_BALANCE); if ((supported_white_balance != NULL) && (new_white_balance != NULL) && (strstr(supported_white_balance, new_white_balance) != NULL)) { const char* current_white_balance = mParameters.get( CameraParameters::KEY_WHITE_BALANCE); if ((current_white_balance == NULL) || (strcmp(current_white_balance, new_white_balance) != 0)) { ALOGV("Setting white balance to %s", new_white_balance); getCameraDevice()->setWhiteBalanceMode(new_white_balance); } } int old_frame_rate = mParameters.getPreviewFrameRate(); int new_frame_rate = new_param.getPreviewFrameRate(); if (old_frame_rate != new_frame_rate) { getCameraDevice()->setPreviewFrameRate(new_frame_rate); } // Validate KEY_PREVIEW_FPS_RANGE i.e., "preview-fps-range" const char* preview_fps_range = new_param.get(CameraParameters::KEY_PREVIEW_FPS_RANGE); if (preview_fps_range) { char tmp[1024]; snprintf(tmp, sizeof(tmp), "%s", preview_fps_range); int low=-1, high=-1; if (sscanf(tmp, "%d,%d", &low, &high) != 2) { ALOGE("incorrect preview-fps-range %s", tmp); return BAD_VALUE; } if (low < 0 || high < 0) { ALOGE("negative preview_fps_range in %s", tmp); return BAD_VALUE; } if (low > high) { ALOGE("invalid preview_fps_range in %s", tmp); return BAD_VALUE; } } // Validate focus mode const char* focus_mode = new_param.get(CameraParameters::KEY_FOCUS_MODE); if (focus_mode && !IsValueInList(focus_mode, kValidFocusModes)) { return BAD_VALUE; } // Validate preview size, if there is no preview size the initial values of // the integers below will be preserved thus intentionally failing the test int new_preview_width = -1, new_preview_height = -1; new_param.getPreviewSize(&new_preview_width, &new_preview_height); if (new_preview_width < 0 || new_preview_height < 0) { return BAD_VALUE; } // If the preview size has changed we have to restart the preview to make // sure we provide frames of the correct size. The receiver assumes the // frame size is correct and will copy all data provided into a buffer whose // size is determined by the preview size without checks, potentially // causing buffer overruns or underruns if there is a size mismatch. int old_preview_width = -1, old_preview_height = -1; mParameters.getPreviewSize(&old_preview_width, &old_preview_height); if (old_preview_width != new_preview_width || old_preview_height != new_preview_height) { restartPreview = true; } // For the same reasons as with the preview size we have to look for changes // in video size and restart the preview if the size has changed. int old_video_width = -1, old_video_height = -1; int new_video_width = -1, new_video_height = -1; mParameters.getVideoSize(&old_video_width, &old_video_height); new_param.getVideoSize(&new_video_width, &new_video_height); if (old_video_width != new_video_width || old_video_height != new_video_height) { restartPreview = true; } // Restart the preview if the pixel format changes to make sure we serve // the selected encoding to the client. const char* old_format = mParameters.getPreviewFormat(); const char* new_format = new_param.getPreviewFormat(); if (!StringsEqual(old_format, new_format)) { restartPreview = true; } const char* old_hint = mParameters.get(CameraParameters::KEY_RECORDING_HINT); const char* new_hint = new_param.get(CameraParameters::KEY_RECORDING_HINT); if (!StringsEqual(old_hint, new_hint)) { // The recording hint changed, this indicates we transitioned from // recording to non-recording or the other way around. We need to look // at a new pixel format for this and that requires a restart. restartPreview = true; } mParameters = new_param; // Now that the parameters have been assigned check if the preview needs to // be restarted. If necessary this will then use the new parameters to set // up the preview as requested by the caller. if (restartPreview && isPreviewEnabled()) { status_t status = doStopPreview(); if (status != NO_ERROR) { ALOGE("%s: Stopping preview failed: %d", __FUNCTION__, status); return status; } status = doStartPreview(); if (status != NO_ERROR) { ALOGE("%s: Starting preview failed: %d", __FUNCTION__, status); return status; } } return NO_ERROR; } /* A variable indicating "no params" / error on the exit from * EmulatedCamera::getParameters(). */ static char lNoParam = '\0'; char* EmulatedCamera::getParameters() { // Read the image size and set the camera's Field of View. // These values are valid for a Logitech B910 HD Webcam. int width=0, height=0; mParameters.getPictureSize(&width, &height); if (height > 0) { if (((double)width / height) < 1.55) { // Closer to 4:3 (1.33), set the FOV to 61.0 degrees mParameters.set(CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE, "61.0"); } else { // Closer to 16:9 (1.77), set the FOV to 70.0 degrees mParameters.set(CameraParameters::KEY_HORIZONTAL_VIEW_ANGLE, "70.0"); } } String8 params(mParameters.flatten()); char* ret_str = reinterpret_cast(malloc(sizeof(char) * (params.length()+1))); memset(ret_str, 0, params.length()+1); if (ret_str != NULL) { strncpy(ret_str, params.string(), params.length()+1); return ret_str; } else { ALOGE("%s: Unable to allocate string for %s", __FUNCTION__, params.string()); /* Apparently, we can't return NULL fron this routine. */ return &lNoParam; } } void EmulatedCamera::putParameters(char* params) { /* This method simply frees parameters allocated in getParameters(). */ if (params != NULL && params != &lNoParam) { free(params); } } status_t EmulatedCamera::sendCommand(int32_t cmd, int32_t arg1, int32_t arg2) { ALOGV("%s: cmd = %d, arg1 = %d, arg2 = %d", __FUNCTION__, cmd, arg1, arg2); switch (cmd) { case CAMERA_CMD_START_FACE_DETECTION: case CAMERA_CMD_STOP_FACE_DETECTION: // We do not support hardware face detection so we need to indicate // that any attempt to start/stop face detection is invalid return BAD_VALUE; } /* TODO: Future enhancements. */ return 0; } void EmulatedCamera::releaseCamera() { ALOGV("%s", __FUNCTION__); cleanupCamera(); } status_t EmulatedCamera::dumpCamera(int fd) { ALOGV("%s", __FUNCTION__); /* TODO: Future enhancements. */ dprintf(fd, "dump camera unimplemented\n"); return 0; } status_t EmulatedCamera::getConfiguredPixelFormat(uint32_t* pixelFormat) const { const char* pix_fmt = nullptr; const char* recordingHint = mParameters.get(CameraParameters::KEY_RECORDING_HINT); bool recordingHintOn = recordingHint && strcmp(recordingHint, CameraParameters::TRUE) == 0; bool recordingEnabled = mCallbackNotifier.isVideoRecordingEnabled(); if (recordingHintOn || recordingEnabled) { // We're recording a video, use the video pixel format pix_fmt = mParameters.get(CameraParameters::KEY_VIDEO_FRAME_FORMAT); } if (pix_fmt == nullptr) { pix_fmt = mParameters.getPreviewFormat(); } if (pix_fmt == nullptr) { ALOGE("%s: Unable to obtain configured pixel format", __FUNCTION__); return EINVAL; } /* Convert framework's pixel format to the FOURCC one. */ if (!GetFourCcFormatFromCameraParam(pix_fmt, pixelFormat)) { ALOGE("%s: Unsupported pixel format %s", __FUNCTION__, pix_fmt); return EINVAL; } return NO_ERROR; } status_t EmulatedCamera::getConfiguredFrameSize(int* outWidth, int* outHeight) const { int width = -1, height = -1; if (mParameters.get(CameraParameters::KEY_VIDEO_SIZE) != nullptr) { mParameters.getVideoSize(&width, &height); } else { mParameters.getPreviewSize(&width, &height); } if (width < 0 || height < 0) { ALOGE("%s: No frame size configured for camera", __FUNCTION__); return EINVAL; } // Only modify the out parameters once we know we succeeded *outWidth = width; *outHeight = height; return NO_ERROR; } /**************************************************************************** * Preview management. ***************************************************************************/ status_t EmulatedCamera::doStartPreview() { ALOGV("%s", __FUNCTION__); EmulatedCameraDevice* camera_dev = getCameraDevice(); if (camera_dev->isStarted()) { camera_dev->stopDeliveringFrames(); camera_dev->stopDevice(); } status_t res = mPreviewWindow.startPreview(); if (res != NO_ERROR) { return res; } /* Make sure camera device is connected. */ if (!camera_dev->isConnected()) { res = camera_dev->connectDevice(); if (res != NO_ERROR) { mPreviewWindow.stopPreview(); return res; } } /* Lets see what should we use for frame width, and height. */ int width, height; res = getConfiguredFrameSize(&width, &height); if (res != NO_ERROR) { mPreviewWindow.stopPreview(); return res; } uint32_t org_fmt = 0; res = getConfiguredPixelFormat(&org_fmt); if (res != NO_ERROR) { mPreviewWindow.stopPreview(); return res; } camera_dev->setPreviewFrameRate(mParameters.getPreviewFrameRate()); ALOGD("Starting camera: %dx%d -> %.4s", width, height, reinterpret_cast(&org_fmt)); res = camera_dev->startDevice(width, height, org_fmt); if (res != NO_ERROR) { mPreviewWindow.stopPreview(); return res; } res = camera_dev->startDeliveringFrames(false); if (res != NO_ERROR) { camera_dev->stopDevice(); mPreviewWindow.stopPreview(); } return res; } status_t EmulatedCamera::doStopPreview() { ALOGV("%s", __FUNCTION__); status_t res = NO_ERROR; if (mPreviewWindow.isPreviewEnabled()) { /* Stop the camera. */ if (getCameraDevice()->isStarted()) { getCameraDevice()->stopDeliveringFrames(); res = getCameraDevice()->stopDevice(); } if (res == NO_ERROR) { /* Disable preview as well. */ mPreviewWindow.stopPreview(); } } return NO_ERROR; } /**************************************************************************** * Private API. ***************************************************************************/ status_t EmulatedCamera::cleanupCamera() { status_t res = NO_ERROR; /* If preview is running - stop it. */ res = doStopPreview(); if (res != NO_ERROR) { return -res; } /* Stop and disconnect the camera device. */ EmulatedCameraDevice* const camera_dev = getCameraDevice(); if (camera_dev != NULL) { if (camera_dev->isStarted()) { camera_dev->stopDeliveringFrames(); res = camera_dev->stopDevice(); if (res != NO_ERROR) { return -res; } } if (camera_dev->isConnected()) { res = camera_dev->disconnectDevice(); if (res != NO_ERROR) { return -res; } } } mCallbackNotifier.cleanupCBNotifier(); /* Re-init the camera settings in case settings were changed */ Initialize(); return NO_ERROR; } /**************************************************************************** * Camera API callbacks as defined by camera_device_ops structure. * * Callbacks here simply dispatch the calls to an appropriate method inside * EmulatedCamera instance, defined by the 'dev' parameter. ***************************************************************************/ int EmulatedCamera::set_preview_window(struct camera_device* dev, struct preview_stream_ops* window) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->setPreviewWindow(window); } void EmulatedCamera::set_callbacks( struct camera_device* dev, camera_notify_callback notify_cb, camera_data_callback data_cb, camera_data_timestamp_callback data_cb_timestamp, camera_request_memory get_memory, void* user) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return; } ec->setCallbacks(notify_cb, data_cb, data_cb_timestamp, get_memory, user); } void EmulatedCamera::enable_msg_type(struct camera_device* dev, int32_t msg_type) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return; } ec->enableMsgType(msg_type); } void EmulatedCamera::disable_msg_type(struct camera_device* dev, int32_t msg_type) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return; } ec->disableMsgType(msg_type); } int EmulatedCamera::msg_type_enabled(struct camera_device* dev, int32_t msg_type) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->isMsgTypeEnabled(msg_type); } int EmulatedCamera::start_preview(struct camera_device* dev) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->startPreview(); } void EmulatedCamera::stop_preview(struct camera_device* dev) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return; } ec->stopPreview(); } int EmulatedCamera::preview_enabled(struct camera_device* dev) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->isPreviewEnabled(); } int EmulatedCamera::store_meta_data_in_buffers(struct camera_device* dev, int enable) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->storeMetaDataInBuffers(enable); } int EmulatedCamera::start_recording(struct camera_device* dev) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->startRecording(); } void EmulatedCamera::stop_recording(struct camera_device* dev) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return; } ec->stopRecording(); } int EmulatedCamera::recording_enabled(struct camera_device* dev) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->isRecordingEnabled(); } void EmulatedCamera::release_recording_frame(struct camera_device* dev, const void* opaque) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return; } ec->releaseRecordingFrame(opaque); } int EmulatedCamera::auto_focus(struct camera_device* dev) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->setAutoFocus(); } int EmulatedCamera::cancel_auto_focus(struct camera_device* dev) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->cancelAutoFocus(); } int EmulatedCamera::take_picture(struct camera_device* dev) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->takePicture(); } int EmulatedCamera::cancel_picture(struct camera_device* dev) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->cancelPicture(); } int EmulatedCamera::set_parameters(struct camera_device* dev, const char* parms) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->setParameters(parms); } char* EmulatedCamera::get_parameters(struct camera_device* dev) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return NULL; } return ec->getParameters(); } void EmulatedCamera::put_parameters(struct camera_device* dev, char* params) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return; } ec->putParameters(params); } int EmulatedCamera::send_command(struct camera_device* dev, int32_t cmd, int32_t arg1, int32_t arg2) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->sendCommand(cmd, arg1, arg2); } void EmulatedCamera::release(struct camera_device* dev) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return; } ec->releaseCamera(); } int EmulatedCamera::dump(struct camera_device* dev, int fd) { EmulatedCamera* ec = reinterpret_cast(dev->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->dumpCamera(fd); } int EmulatedCamera::close(struct hw_device_t* device) { EmulatedCamera* ec = reinterpret_cast(reinterpret_cast(device)->priv); if (ec == NULL) { ALOGE("%s: Unexpected NULL camera device", __FUNCTION__); return -EINVAL; } return ec->closeCamera(); } /**************************************************************************** * Static initializer for the camera callback API ****************************************************************************/ camera_device_ops_t EmulatedCamera::mDeviceOps = { EmulatedCamera::set_preview_window, EmulatedCamera::set_callbacks, EmulatedCamera::enable_msg_type, EmulatedCamera::disable_msg_type, EmulatedCamera::msg_type_enabled, EmulatedCamera::start_preview, EmulatedCamera::stop_preview, EmulatedCamera::preview_enabled, EmulatedCamera::store_meta_data_in_buffers, EmulatedCamera::start_recording, EmulatedCamera::stop_recording, EmulatedCamera::recording_enabled, EmulatedCamera::release_recording_frame, EmulatedCamera::auto_focus, EmulatedCamera::cancel_auto_focus, EmulatedCamera::take_picture, EmulatedCamera::cancel_picture, EmulatedCamera::set_parameters, EmulatedCamera::get_parameters, EmulatedCamera::put_parameters, EmulatedCamera::send_command, EmulatedCamera::release, EmulatedCamera::dump }; /**************************************************************************** * Common keys ***************************************************************************/ const char EmulatedCamera::FACING_KEY[] = "prop-facing"; const char EmulatedCamera::ORIENTATION_KEY[] = "prop-orientation"; const char EmulatedCamera::RECORDING_HINT_KEY[] = "recording-hint"; /**************************************************************************** * Common string values ***************************************************************************/ const char EmulatedCamera::FACING_BACK[] = "back"; const char EmulatedCamera::FACING_FRONT[] = "front"; /**************************************************************************** * Parameter debugging helpers ***************************************************************************/ #if DEBUG_PARAM static void PrintParamDiff(const CameraParameters& current, const char* new_par) { char tmp[2048]; const char* wrk = new_par; /* Divided with ';' */ const char* next = strchr(wrk, ';'); while (next != NULL) { snprintf(tmp, sizeof(tmp), "%.*s", (int)(intptr_t)(next-wrk), wrk); /* in the form key=value */ char* val = strchr(tmp, '='); if (val != NULL) { *val = '\0'; val++; const char* in_current = current.get(tmp); if (in_current != NULL) { if (strcmp(in_current, val)) { ALOGD("=== Value changed: %s: %s -> %s", tmp, in_current, val); } } else { ALOGD("+++ New parameter: %s=%s", tmp, val); } } else { ALOGW("No value separator in %s", tmp); } wrk = next + 1; next = strchr(wrk, ';'); } } #endif /* DEBUG_PARAM */ }; /* namespace android */