michael@0: /* michael@0: * Copyright (C) 2012-2014 Mozilla Foundation michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: #include "GonkCameraHwMgr.h" michael@0: #include "TestGonkCameraHardware.h" michael@0: michael@0: #include michael@0: #include michael@0: michael@0: #include "base/basictypes.h" michael@0: #include "nsDebug.h" michael@0: #include "mozilla/layers/TextureClient.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/RefPtr.h" michael@0: #include "GonkCameraControl.h" michael@0: #include "GonkNativeWindow.h" michael@0: #include "CameraCommon.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::layers; michael@0: using namespace android; michael@0: michael@0: GonkCameraHardware::GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId, const sp& aCamera) michael@0: : mCameraId(aCameraId) michael@0: , mClosing(false) michael@0: , mNumFrames(0) michael@0: , mCamera(aCamera) michael@0: , mTarget(aTarget) michael@0: , mSensorOrientation(0) michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n", __func__, __LINE__, (void*)this, (void*)aTarget); michael@0: } michael@0: michael@0: void michael@0: GonkCameraHardware::OnNewFrame() michael@0: { michael@0: if (mClosing) { michael@0: return; michael@0: } michael@0: RefPtr buffer = mNativeWindow->getCurrentBuffer(); michael@0: if (!buffer) { michael@0: DOM_CAMERA_LOGW("received null frame"); michael@0: return; michael@0: } michael@0: OnNewPreviewFrame(mTarget, buffer); michael@0: } michael@0: michael@0: // Android data callback michael@0: void michael@0: GonkCameraHardware::postData(int32_t aMsgType, const sp& aDataPtr, camera_frame_metadata_t* metadata) michael@0: { michael@0: if (mClosing) { michael@0: return; michael@0: } michael@0: michael@0: switch (aMsgType) { michael@0: case CAMERA_MSG_PREVIEW_FRAME: michael@0: // Do nothing michael@0: break; michael@0: michael@0: case CAMERA_MSG_COMPRESSED_IMAGE: michael@0: if (aDataPtr != nullptr) { michael@0: OnTakePictureComplete(mTarget, static_cast(aDataPtr->pointer()), aDataPtr->size()); michael@0: } else { michael@0: OnTakePictureError(mTarget); michael@0: } michael@0: break; michael@0: michael@0: case CAMERA_MSG_PREVIEW_METADATA: michael@0: OnFacesDetected(mTarget, metadata); michael@0: break; michael@0: michael@0: default: michael@0: DOM_CAMERA_LOGE("Unhandled data callback event %d\n", aMsgType); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // Android notify callback michael@0: void michael@0: GonkCameraHardware::notify(int32_t aMsgType, int32_t ext1, int32_t ext2) michael@0: { michael@0: if (mClosing) { michael@0: return; michael@0: } michael@0: michael@0: switch (aMsgType) { michael@0: case CAMERA_MSG_FOCUS: michael@0: OnAutoFocusComplete(mTarget, !!ext1); michael@0: break; michael@0: michael@0: #if ANDROID_VERSION >= 16 michael@0: case CAMERA_MSG_FOCUS_MOVE: michael@0: OnAutoFocusMoving(mTarget, !!ext1); michael@0: break; michael@0: #endif michael@0: michael@0: case CAMERA_MSG_SHUTTER: michael@0: OnShutter(mTarget); michael@0: break; michael@0: michael@0: case CAMERA_MSG_ERROR: michael@0: OnError(mTarget, CameraControlListener::kErrorServiceFailed, ext1, ext2); michael@0: break; michael@0: michael@0: default: michael@0: DOM_CAMERA_LOGE("Unhandled notify callback event %d\n", aMsgType); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: void michael@0: GonkCameraHardware::postDataTimestamp(nsecs_t aTimestamp, int32_t aMsgType, const sp& aDataPtr) michael@0: { michael@0: DOM_CAMERA_LOGI("%s",__func__); michael@0: if (mClosing) { michael@0: return; michael@0: } michael@0: michael@0: if (mListener.get()) { michael@0: DOM_CAMERA_LOGI("Listener registered, posting recording frame!"); michael@0: mListener->postDataTimestamp(aTimestamp, aMsgType, aDataPtr); michael@0: } else { michael@0: DOM_CAMERA_LOGW("No listener was set. Drop a recording frame."); michael@0: mCamera->releaseRecordingFrame(aDataPtr); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraHardware::Init() michael@0: { michael@0: DOM_CAMERA_LOGT("%s: this=%p\n", __func__, (void* )this); michael@0: michael@0: CameraInfo info; michael@0: int rv = Camera::getCameraInfo(mCameraId, &info); michael@0: if (rv != 0) { michael@0: DOM_CAMERA_LOGE("%s: failed to get CameraInfo mCameraId %d\n", __func__, mCameraId); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: mRawSensorOrientation = info.orientation; michael@0: mSensorOrientation = mRawSensorOrientation; michael@0: michael@0: /** michael@0: * Non-V4L2-based camera driver adds extra offset onto picture orientation michael@0: * set by gecko, so we have to adjust it back. michael@0: */ michael@0: char propname[PROP_NAME_MAX]; michael@0: char prop[PROP_VALUE_MAX]; michael@0: int offset = 0; michael@0: snprintf(propname, sizeof(propname), "ro.moz.cam.%d.sensor_offset", mCameraId); michael@0: if (__system_property_get(propname, prop) > 0) { michael@0: offset = clamped(atoi(prop), 0, 270); michael@0: mSensorOrientation += offset; michael@0: mSensorOrientation %= 360; michael@0: } michael@0: DOM_CAMERA_LOGI("Sensor orientation: base=%d, offset=%d, final=%d\n", info.orientation, offset, mSensorOrientation); michael@0: michael@0: // Disable shutter sound in android CameraService because gaia camera app will play it michael@0: mCamera->sendCommand(CAMERA_CMD_ENABLE_SHUTTER_SOUND, 0, 0); michael@0: michael@0: mNativeWindow = new GonkNativeWindow(); michael@0: mNativeWindow->setNewFrameCallback(this); michael@0: mCamera->setListener(this); michael@0: michael@0: #if defined(MOZ_WIDGET_GONK) michael@0: michael@0: #if ANDROID_VERSION >= 19 michael@0: mCamera->setPreviewTarget(mNativeWindow->getBufferQueue()); michael@0: #elif (ANDROID_VERSION == 17) || (ANDROID_VERSION == 18) michael@0: mCamera->setPreviewTexture(mNativeWindow->getBufferQueue()); michael@0: #else michael@0: mCamera->setPreviewTexture(mNativeWindow); michael@0: #endif michael@0: michael@0: #if ANDROID_VERSION >= 16 michael@0: rv = mCamera->sendCommand(CAMERA_CMD_ENABLE_FOCUS_MOVE_MSG, 1, 0); michael@0: if (rv != OK) { michael@0: NS_WARNING("Failed to send command CAMERA_CMD_ENABLE_FOCUS_MOVE_MSG"); michael@0: } michael@0: #endif michael@0: michael@0: #endif michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: sp michael@0: GonkCameraHardware::Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId) michael@0: { michael@0: #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 18 michael@0: sp camera = Camera::connect(aCameraId, /* clientPackageName */String16("gonk.camera"), Camera::USE_CALLING_UID); michael@0: #else michael@0: sp camera = Camera::connect(aCameraId); michael@0: #endif michael@0: michael@0: if (camera.get() == nullptr) { michael@0: return nullptr; michael@0: } michael@0: michael@0: const nsAdoptingCString& test = michael@0: mozilla::Preferences::GetCString("camera.control.test.enabled"); michael@0: sp cameraHardware; michael@0: if (test.EqualsASCII("hardware")) { michael@0: NS_WARNING("Using test Gonk hardware layer"); michael@0: cameraHardware = new TestGonkCameraHardware(aTarget, aCameraId, camera); michael@0: } else { michael@0: cameraHardware = new GonkCameraHardware(aTarget, aCameraId, camera); michael@0: } michael@0: michael@0: nsresult rv = cameraHardware->Init(); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Failed to initialize camera hardware (0x%X)\n", rv); michael@0: return nullptr; michael@0: } michael@0: michael@0: return cameraHardware; michael@0: } michael@0: michael@0: void michael@0: GonkCameraHardware::Close() michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, (void*)this); michael@0: michael@0: mClosing = true; michael@0: mCamera->stopPreview(); michael@0: mCamera->disconnect(); michael@0: if (mNativeWindow.get()) { michael@0: mNativeWindow->abandon(); michael@0: } michael@0: mCamera.clear(); michael@0: mNativeWindow.clear(); michael@0: michael@0: // Ensure that ICamera's destructor is actually executed michael@0: IPCThreadState::self()->flushCommands(); michael@0: } michael@0: michael@0: GonkCameraHardware::~GonkCameraHardware() michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, (void*)this); michael@0: mCamera.clear(); michael@0: mNativeWindow.clear(); michael@0: michael@0: if (mClosing) { michael@0: return; michael@0: } michael@0: michael@0: /** michael@0: * Trigger the OnClosed event; the upper layers can't do anything michael@0: * with the hardware layer once they receive this event. michael@0: */ michael@0: if (mTarget) { michael@0: OnClosed(mTarget); michael@0: } michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::GetSensorOrientation(uint32_t aType) michael@0: { michael@0: DOM_CAMERA_LOGI("%s\n", __func__); michael@0: michael@0: switch (aType) { michael@0: case OFFSET_SENSOR_ORIENTATION: michael@0: return mSensorOrientation; michael@0: michael@0: case RAW_SENSOR_ORIENTATION: michael@0: return mRawSensorOrientation; michael@0: michael@0: default: michael@0: DOM_CAMERA_LOGE("%s:%d : unknown aType=%d\n", __func__, __LINE__, aType); michael@0: return 0; michael@0: } michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::AutoFocus() michael@0: { michael@0: DOM_CAMERA_LOGI("%s\n", __func__); michael@0: return mCamera->autoFocus(); michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::CancelAutoFocus() michael@0: { michael@0: DOM_CAMERA_LOGI("%s\n", __func__); michael@0: return mCamera->cancelAutoFocus(); michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::StartFaceDetection() michael@0: { michael@0: DOM_CAMERA_LOGI("%s\n", __func__); michael@0: int rv = INVALID_OPERATION; michael@0: michael@0: #if ANDROID_VERSION >= 15 michael@0: rv = mCamera->sendCommand(CAMERA_CMD_START_FACE_DETECTION, CAMERA_FACE_DETECTION_HW, 0); michael@0: #endif michael@0: if (rv != OK) { michael@0: DOM_CAMERA_LOGE("Start face detection failed with status %d", rv); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::StopFaceDetection() michael@0: { michael@0: DOM_CAMERA_LOGI("%s\n", __func__); michael@0: int rv = INVALID_OPERATION; michael@0: michael@0: #if ANDROID_VERSION >= 15 michael@0: rv = mCamera->sendCommand(CAMERA_CMD_STOP_FACE_DETECTION, 0, 0); michael@0: #endif michael@0: if (rv != OK) { michael@0: DOM_CAMERA_LOGE("Stop face detection failed with status %d", rv); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::TakePicture() michael@0: { michael@0: return mCamera->takePicture(CAMERA_MSG_SHUTTER | CAMERA_MSG_COMPRESSED_IMAGE); michael@0: } michael@0: michael@0: void michael@0: GonkCameraHardware::CancelTakePicture() michael@0: { michael@0: DOM_CAMERA_LOGW("%s: android::Camera do not provide this capability\n", __func__); michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::PushParameters(const GonkCameraParameters& aParams) michael@0: { michael@0: const String8 s = aParams.Flatten(); michael@0: return mCamera->setParameters(s); michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::PushParameters(const CameraParameters& aParams) michael@0: { michael@0: String8 s = aParams.flatten(); michael@0: return mCamera->setParameters(s); michael@0: } michael@0: michael@0: nsresult michael@0: GonkCameraHardware::PullParameters(GonkCameraParameters& aParams) michael@0: { michael@0: const String8 s = mCamera->getParameters(); michael@0: return aParams.Unflatten(s); michael@0: } michael@0: michael@0: void michael@0: GonkCameraHardware::PullParameters(CameraParameters& aParams) michael@0: { michael@0: const String8 s = mCamera->getParameters(); michael@0: aParams.unflatten(s); michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::StartPreview() michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); michael@0: return mCamera->startPreview(); michael@0: } michael@0: michael@0: void michael@0: GonkCameraHardware::StopPreview() michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); michael@0: mCamera->stopPreview(); michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::StartRecording() michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); michael@0: int rv = OK; michael@0: michael@0: rv = mCamera->startRecording(); michael@0: if (rv != OK) { michael@0: DOM_CAMERA_LOGE("mHardware->startRecording() failed with status %d", rv); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::StopRecording() michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); michael@0: mCamera->stopRecording(); michael@0: return OK; michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::SetListener(const sp& aListener) michael@0: { michael@0: mListener = aListener; michael@0: return OK; michael@0: } michael@0: michael@0: void michael@0: GonkCameraHardware::ReleaseRecordingFrame(const sp& aFrame) michael@0: { michael@0: mCamera->releaseRecordingFrame(aFrame); michael@0: } michael@0: michael@0: int michael@0: GonkCameraHardware::StoreMetaDataInBuffers(bool aEnabled) michael@0: { michael@0: return mCamera->storeMetaDataInBuffers(aEnabled); michael@0: }