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 "GonkCameraControl.h" michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include "base/basictypes.h" michael@0: #include "camera/CameraParameters.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsMemory.h" michael@0: #include "nsThread.h" michael@0: #include michael@0: #include "mozilla/FileUtils.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/unused.h" michael@0: #include "mozilla/ipc/FileDescriptorUtils.h" michael@0: #include "nsAlgorithm.h" michael@0: #include michael@0: #include "nsPrintfCString.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIVolume.h" michael@0: #include "nsIVolumeService.h" michael@0: #include "AutoRwLock.h" michael@0: #include "GonkCameraHwMgr.h" michael@0: #include "GonkRecorderProfiles.h" michael@0: #include "CameraCommon.h" michael@0: #include "GonkCameraParameters.h" michael@0: #include "DeviceStorageFileDescriptor.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::layers; michael@0: using namespace mozilla::gfx; michael@0: using namespace mozilla::ipc; michael@0: using namespace android; michael@0: michael@0: #define RETURN_IF_NO_CAMERA_HW() \ michael@0: do { \ michael@0: if (!mCameraHw.get()) { \ michael@0: DOM_CAMERA_LOGE("%s:%d : mCameraHw is null\n", __func__, __LINE__); \ michael@0: return NS_ERROR_NOT_AVAILABLE; \ michael@0: } \ michael@0: } while(0) michael@0: michael@0: // Construct nsGonkCameraControl on the main thread. michael@0: nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId) michael@0: : CameraControlImpl(aCameraId) michael@0: , mLastPictureSize({0, 0}) michael@0: , mLastThumbnailSize({0, 0}) michael@0: , mPreviewFps(30) michael@0: , mResumePreviewAfterTakingPicture(false) // XXXmikeh - see bug 950102 michael@0: , mFlashSupported(false) michael@0: , mLuminanceSupported(false) michael@0: , mAutoFlashModeOverridden(false) michael@0: , mDeferConfigUpdate(0) michael@0: , mMediaProfiles(nullptr) michael@0: , mRecorder(nullptr) michael@0: , mProfileManager(nullptr) michael@0: , mRecorderProfile(nullptr) michael@0: , mVideoFile(nullptr) michael@0: , mReentrantMonitor("GonkCameraControl::OnTakePictureMonitor") michael@0: { michael@0: // Constructor runs on the main thread... michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); michael@0: mImageContainer = LayerManager::CreateImageContainer(); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::StartImpl(const Configuration* aInitialConfig) michael@0: { michael@0: /** michael@0: * For initialization, we try to return the camera control to the upper michael@0: * upper layer (i.e. the DOM) as quickly as possible. To do this, the michael@0: * camera is initialized in the following stages: michael@0: * michael@0: * 0. Initialize() initializes the hardware; michael@0: * 1. SetConfigurationInternal() does the minimal configuration michael@0: * required so that we can start the preview -and- report a valid michael@0: * configuration to the upper layer; michael@0: * 2. OnHardwareStateChange() reports that the hardware is ready, michael@0: * which the upper (e.g. DOM) layer can (and does) use to return michael@0: * the camera control object; michael@0: * 3. StartPreviewImpl() starts the flow of preview frames from the michael@0: * camera hardware. michael@0: * michael@0: * The intent of the above flow is to let the Main Thread do as much work michael@0: * up-front as possible without waiting for blocking Camera Thread calls michael@0: * to complete. michael@0: */ michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: michael@0: nsresult rv = Initialize(); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: if (aInitialConfig) { michael@0: rv = SetConfigurationInternal(*aInitialConfig); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: // The initial configuration failed, close up the hardware michael@0: StopImpl(); michael@0: return rv; michael@0: } michael@0: } michael@0: michael@0: OnHardwareStateChange(CameraControlListener::kHardwareOpen); michael@0: return StartPreviewImpl(); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::Initialize() michael@0: { michael@0: mCameraHw = GonkCameraHardware::Connect(this, mCameraId); michael@0: if (!mCameraHw.get()) { michael@0: DOM_CAMERA_LOGE("Failed to connect to camera %d (this=%p)\n", mCameraId, this); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: DOM_CAMERA_LOGI("Initializing camera %d (this=%p, mCameraHw=%p)\n", mCameraId, this, mCameraHw.get()); michael@0: michael@0: // Initialize our camera configuration database. michael@0: PullParametersImpl(); michael@0: michael@0: // Set preferred preview frame format. michael@0: mParams.Set(CAMERA_PARAM_PREVIEWFORMAT, NS_LITERAL_STRING("yuv420sp")); michael@0: // Turn off any normal pictures returned by the HDR scene mode michael@0: mParams.Set(CAMERA_PARAM_SCENEMODE_HDR_RETURNNORMALPICTURE, false); michael@0: PushParametersImpl(); michael@0: michael@0: // Grab any other settings we'll need later. michael@0: mParams.Get(CAMERA_PARAM_PICTURE_FILEFORMAT, mFileFormat); michael@0: mParams.Get(CAMERA_PARAM_THUMBNAILSIZE, mLastThumbnailSize); michael@0: michael@0: // The emulator's camera returns -1 for these values; bump them up to 0 michael@0: int areas; michael@0: mParams.Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, areas); michael@0: mCurrentConfiguration.mMaxMeteringAreas = areas != -1 ? areas : 0; michael@0: mParams.Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas); michael@0: mCurrentConfiguration.mMaxFocusAreas = areas != -1 ? areas : 0; michael@0: michael@0: mParams.Get(CAMERA_PARAM_PICTURE_SIZE, mLastPictureSize); michael@0: mParams.Get(CAMERA_PARAM_PREVIEWSIZE, mCurrentConfiguration.mPreviewSize); michael@0: mParams.Get(CAMERA_PARAM_VIDEOSIZE, mLastRecorderSize); michael@0: michael@0: nsString luminance; // check for support michael@0: mParams.Get(CAMERA_PARAM_LUMINANCE, luminance); michael@0: mLuminanceSupported = !luminance.IsEmpty(); michael@0: michael@0: nsString flashMode; michael@0: mParams.Get(CAMERA_PARAM_FLASHMODE, flashMode); michael@0: mFlashSupported = !flashMode.IsEmpty(); michael@0: michael@0: DOM_CAMERA_LOGI(" - maximum metering areas: %u\n", mCurrentConfiguration.mMaxMeteringAreas); michael@0: DOM_CAMERA_LOGI(" - maximum focus areas: %u\n", mCurrentConfiguration.mMaxFocusAreas); michael@0: DOM_CAMERA_LOGI(" - default picture size: %u x %u\n", michael@0: mLastPictureSize.width, mLastPictureSize.height); michael@0: DOM_CAMERA_LOGI(" - default thumbnail size: %u x %u\n", michael@0: mLastThumbnailSize.width, mLastThumbnailSize.height); michael@0: DOM_CAMERA_LOGI(" - default preview size: %u x %u\n", michael@0: mCurrentConfiguration.mPreviewSize.width, mCurrentConfiguration.mPreviewSize.height); michael@0: DOM_CAMERA_LOGI(" - default video recorder size: %u x %u\n", michael@0: mLastRecorderSize.width, mLastRecorderSize.height); michael@0: DOM_CAMERA_LOGI(" - default picture file format: %s\n", michael@0: NS_ConvertUTF16toUTF8(mFileFormat).get()); michael@0: DOM_CAMERA_LOGI(" - luminance reporting: %ssupported\n", michael@0: mLuminanceSupported ? "" : "NOT "); michael@0: if (mFlashSupported) { michael@0: DOM_CAMERA_LOGI(" - flash: supported, default mode '%s'\n", michael@0: NS_ConvertUTF16toUTF8(flashMode).get()); michael@0: } else { michael@0: DOM_CAMERA_LOGI(" - flash: NOT supported\n"); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsGonkCameraControl::~nsGonkCameraControl() michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraHw = %p\n", __func__, __LINE__, this, mCameraHw.get()); michael@0: michael@0: StopImpl(); michael@0: DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetConfigurationInternal(const Configuration& aConfig) michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); michael@0: michael@0: nsresult rv; michael@0: michael@0: switch (aConfig.mMode) { michael@0: case kPictureMode: michael@0: rv = SetPictureConfiguration(aConfig); michael@0: break; michael@0: michael@0: case kVideoMode: michael@0: rv = SetVideoConfiguration(aConfig); michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode in SetConfigurationInternal()"); michael@0: } michael@0: michael@0: DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mCurrentConfiguration.mMode = aConfig.mMode; michael@0: mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile; michael@0: if (aConfig.mMode == kVideoMode) { michael@0: mCurrentConfiguration.mPreviewSize = mLastRecorderSize; michael@0: } michael@0: michael@0: OnConfigurationChange(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetConfigurationImpl(const Configuration& aConfig) michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: michael@0: // Stop any currently running preview michael@0: nsresult rv = PausePreview(); michael@0: if (NS_FAILED(rv)) { michael@0: // warn, but plow ahead michael@0: NS_WARNING("PausePreview() in SetConfigurationImpl() failed"); michael@0: } michael@0: michael@0: DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); michael@0: rv = SetConfigurationInternal(aConfig); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: StopPreviewImpl(); michael@0: return rv; michael@0: } michael@0: michael@0: // Restart the preview michael@0: DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); michael@0: return StartPreviewImpl(); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetPictureConfiguration(const Configuration& aConfig) michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); michael@0: michael@0: // remove any existing recorder profile michael@0: mRecorderProfile = nullptr; michael@0: michael@0: nsresult rv = SetPreviewSize(aConfig.mPreviewSize); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: rv = PushParameters(); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: mParams.Get(CAMERA_PARAM_PREVIEWFRAMERATE, mPreviewFps); michael@0: michael@0: DOM_CAMERA_LOGI("picture mode preview: wanted %ux%u, got %ux%u (%u fps)\n", michael@0: aConfig.mPreviewSize.width, aConfig.mPreviewSize.height, michael@0: mCurrentConfiguration.mPreviewSize.width, mCurrentConfiguration.mPreviewSize.height, michael@0: mPreviewFps); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig) michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); michael@0: michael@0: nsresult rv = SetupVideoMode(aConfig.mRecorderProfile); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: DOM_CAMERA_LOGI("video mode preview: profile '%s', got %ux%u (%u fps)\n", michael@0: NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile).get(), michael@0: mLastRecorderSize.width, mLastRecorderSize.height, michael@0: mPreviewFps); michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: // Parameter management. michael@0: nsresult michael@0: nsGonkCameraControl::PushParameters() michael@0: { michael@0: uint32_t dcu = mDeferConfigUpdate; michael@0: if (dcu > 0) { michael@0: DOM_CAMERA_LOGI("Defering config update (nest level %u)\n", dcu); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * If we're already on the camera thread, call PushParametersImpl() michael@0: * directly, so that it executes synchronously. Some callers michael@0: * require this so that changes take effect immediately before michael@0: * we can proceed. michael@0: */ michael@0: if (NS_GetCurrentThread() != mCameraThread) { michael@0: DOM_CAMERA_LOGT("%s:%d - dispatching to Camera Thread\n", __func__, __LINE__); michael@0: nsCOMPtr pushParametersTask = michael@0: NS_NewRunnableMethod(this, &nsGonkCameraControl::PushParametersImpl); michael@0: return mCameraThread->Dispatch(pushParametersTask, NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); michael@0: return PushParametersImpl(); michael@0: } michael@0: michael@0: void michael@0: nsGonkCameraControl::BeginBatchParameterSet() michael@0: { michael@0: uint32_t dcu = ++mDeferConfigUpdate; michael@0: if (dcu == 0) { michael@0: NS_WARNING("Overflow weirdness incrementing mDeferConfigUpdate!"); michael@0: MOZ_CRASH(); michael@0: } michael@0: DOM_CAMERA_LOGI("Begin deferring camera configuration updates (nest level %u)\n", dcu); michael@0: } michael@0: michael@0: void michael@0: nsGonkCameraControl::EndBatchParameterSet() michael@0: { michael@0: uint32_t dcu = mDeferConfigUpdate--; michael@0: if (dcu == 0) { michael@0: NS_WARNING("Underflow badness decrementing mDeferConfigUpdate!"); michael@0: MOZ_CRASH(); michael@0: } michael@0: DOM_CAMERA_LOGI("End deferring camera configuration updates (nest level %u)\n", dcu); michael@0: michael@0: if (dcu == 1) { michael@0: PushParameters(); michael@0: } michael@0: } michael@0: michael@0: template nsresult michael@0: nsGonkCameraControl::SetAndPush(uint32_t aKey, const T& aValue) michael@0: { michael@0: nsresult rv = mParams.Set(aKey, aValue); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Camera parameter aKey=%d failed to set (0x%x)\n", aKey, rv); michael@0: return rv; michael@0: } michael@0: return PushParameters(); michael@0: } michael@0: michael@0: // Array-of-Size parameter accessor. michael@0: nsresult michael@0: nsGonkCameraControl::Get(uint32_t aKey, nsTArray& aSizes) michael@0: { michael@0: if (aKey == CAMERA_PARAM_SUPPORTED_VIDEOSIZES) { michael@0: nsresult rv = mParams.Get(aKey, aSizes); michael@0: if (aSizes.Length() != 0) { michael@0: return rv; michael@0: } michael@0: DOM_CAMERA_LOGI("Camera doesn't support video independent of the preview\n"); michael@0: aKey = CAMERA_PARAM_SUPPORTED_PREVIEWSIZES; michael@0: } michael@0: michael@0: return mParams.Get(aKey, aSizes); michael@0: } michael@0: michael@0: // Array-of-doubles parameter accessor. michael@0: nsresult michael@0: nsGonkCameraControl::Get(uint32_t aKey, nsTArray& aValues) michael@0: { michael@0: return mParams.Get(aKey, aValues); michael@0: } michael@0: michael@0: // Array-of-nsString parameter accessor. michael@0: nsresult michael@0: nsGonkCameraControl::Get(uint32_t aKey, nsTArray& aValues) michael@0: { michael@0: return mParams.Get(aKey, aValues); michael@0: } michael@0: michael@0: // nsString-valued parameter accessors michael@0: nsresult michael@0: nsGonkCameraControl::Set(uint32_t aKey, const nsAString& aValue) michael@0: { michael@0: nsresult rv = mParams.Set(aKey, aValue); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: switch (aKey) { michael@0: case CAMERA_PARAM_PICTURE_FILEFORMAT: michael@0: // Picture format -- need to keep it for the TakePicture() callback. michael@0: mFileFormat = aValue; michael@0: break; michael@0: michael@0: case CAMERA_PARAM_FLASHMODE: michael@0: // Explicit flash mode changes always win and stick. michael@0: mAutoFlashModeOverridden = false; michael@0: break; michael@0: } michael@0: michael@0: return PushParameters(); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::Get(uint32_t aKey, nsAString& aRet) michael@0: { michael@0: return mParams.Get(aKey, aRet); michael@0: } michael@0: michael@0: // Double-valued parameter accessors michael@0: nsresult michael@0: nsGonkCameraControl::Set(uint32_t aKey, double aValue) michael@0: { michael@0: return SetAndPush(aKey, aValue); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::Get(uint32_t aKey, double& aRet) michael@0: { michael@0: return mParams.Get(aKey, aRet); michael@0: } michael@0: michael@0: // Signed-64-bit parameter accessors. michael@0: nsresult michael@0: nsGonkCameraControl::Set(uint32_t aKey, int64_t aValue) michael@0: { michael@0: return SetAndPush(aKey, aValue); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::Get(uint32_t aKey, int64_t& aRet) michael@0: { michael@0: return mParams.Get(aKey, aRet); michael@0: } michael@0: michael@0: // Weighted-region parameter accessors. michael@0: nsresult michael@0: nsGonkCameraControl::Set(uint32_t aKey, const nsTArray& aRegions) michael@0: { michael@0: return SetAndPush(aKey, aRegions); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::Get(uint32_t aKey, nsTArray& aRegions) michael@0: { michael@0: return mParams.Get(aKey, aRegions); michael@0: } michael@0: michael@0: // Singleton-size parameter accessors. michael@0: nsresult michael@0: nsGonkCameraControl::Set(uint32_t aKey, const Size& aSize) michael@0: { michael@0: switch (aKey) { michael@0: case CAMERA_PARAM_PICTURE_SIZE: michael@0: DOM_CAMERA_LOGI("setting picture size to %ux%u\n", aSize.width, aSize.height); michael@0: return SetPictureSize(aSize); michael@0: michael@0: case CAMERA_PARAM_THUMBNAILSIZE: michael@0: DOM_CAMERA_LOGI("setting thumbnail size to %ux%u\n", aSize.width, aSize.height); michael@0: return SetThumbnailSize(aSize); michael@0: michael@0: default: michael@0: return SetAndPush(aKey, aSize); michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::Get(uint32_t aKey, Size& aSize) michael@0: { michael@0: return mParams.Get(aKey, aSize); michael@0: } michael@0: michael@0: // Signed int parameter accessors. michael@0: nsresult michael@0: nsGonkCameraControl::Set(uint32_t aKey, int aValue) michael@0: { michael@0: if (aKey == CAMERA_PARAM_PICTURE_ROTATION) { michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: aValue = RationalizeRotation(aValue + mCameraHw->GetSensorOrientation()); michael@0: } michael@0: return SetAndPush(aKey, aValue); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::Get(uint32_t aKey, int& aRet) michael@0: { michael@0: if (aKey == CAMERA_PARAM_SENSORANGLE) { michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: aRet = mCameraHw->GetSensorOrientation(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: return mParams.Get(aKey, aRet); michael@0: } michael@0: michael@0: // GPS location parameter accessors. michael@0: nsresult michael@0: nsGonkCameraControl::SetLocation(const Position& aLocation) michael@0: { michael@0: return SetAndPush(CAMERA_PARAM_PICTURE_LOCATION, aLocation); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::StartPreviewImpl() michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: michael@0: if (mPreviewState == CameraControlListener::kPreviewStarted) { michael@0: DOM_CAMERA_LOGW("Camera preview already started, nothing to do\n"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: DOM_CAMERA_LOGI("Starting preview (this=%p)\n", this); michael@0: michael@0: if (mCameraHw->StartPreview() != OK) { michael@0: DOM_CAMERA_LOGE("Failed to start camera preview\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: OnPreviewStateChange(CameraControlListener::kPreviewStarted); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::StopPreviewImpl() michael@0: { michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: michael@0: DOM_CAMERA_LOGI("Stopping preview (this=%p)\n", this); michael@0: michael@0: mCameraHw->StopPreview(); michael@0: OnPreviewStateChange(CameraControlListener::kPreviewStopped); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::PausePreview() michael@0: { michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: michael@0: DOM_CAMERA_LOGI("Pausing preview (this=%p)\n", this); michael@0: michael@0: mCameraHw->StopPreview(); michael@0: OnPreviewStateChange(CameraControlListener::kPreviewPaused); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::AutoFocusImpl() michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: michael@0: DOM_CAMERA_LOGI("Starting auto focus\n"); michael@0: michael@0: if (mCameraHw->AutoFocus() != OK) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::StartFaceDetectionImpl() michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: michael@0: DOM_CAMERA_LOGI("Starting face detection\n"); michael@0: michael@0: if (mCameraHw->StartFaceDetection() != OK) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::StopFaceDetectionImpl() michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: michael@0: DOM_CAMERA_LOGI("Stopping face detection\n"); michael@0: michael@0: if (mCameraHw->StopFaceDetection() != OK) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetThumbnailSizeImpl(const Size& aSize) michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: michael@0: /** michael@0: * We keep a copy of the specified size so that if the picture size michael@0: * changes, we can choose a new thumbnail size close to what was asked for michael@0: * last time. michael@0: */ michael@0: mLastThumbnailSize = aSize; michael@0: michael@0: /** michael@0: * If either of width or height is zero, set the other to zero as well. michael@0: * This should disable inclusion of a thumbnail in the final picture. michael@0: */ michael@0: if (!aSize.width || !aSize.height) { michael@0: DOM_CAMERA_LOGW("Requested thumbnail size %ux%u, disabling thumbnail\n", michael@0: aSize.width, aSize.height); michael@0: Size size = { 0, 0 }; michael@0: return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size); michael@0: } michael@0: michael@0: /** michael@0: * Choose the supported thumbnail size that is closest to the specified size. michael@0: * Some drivers will fail to take a picture if the thumbnail does not have michael@0: * the same aspect ratio as the set picture size, so we need to enforce that michael@0: * too. michael@0: */ michael@0: int smallestDelta = INT_MAX; michael@0: uint32_t smallestDeltaIndex = UINT32_MAX; michael@0: int targetArea = aSize.width * aSize.height; michael@0: michael@0: nsAutoTArray supportedSizes; michael@0: Get(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, supportedSizes); michael@0: michael@0: for (uint32_t i = 0; i < supportedSizes.Length(); ++i) { michael@0: int area = supportedSizes[i].width * supportedSizes[i].height; michael@0: int delta = abs(area - targetArea); michael@0: michael@0: if (area != 0 michael@0: && delta < smallestDelta michael@0: && supportedSizes[i].width * mLastPictureSize.height / michael@0: supportedSizes[i].height == mLastPictureSize.width michael@0: ) { michael@0: smallestDelta = delta; michael@0: smallestDeltaIndex = i; michael@0: } michael@0: } michael@0: michael@0: if (smallestDeltaIndex == UINT32_MAX) { michael@0: DOM_CAMERA_LOGW("Unable to find a thumbnail size close to %ux%u, disabling thumbnail\n", michael@0: aSize.width, aSize.height); michael@0: // If we are unable to find a thumbnail size with a suitable aspect ratio, michael@0: // just disable the thumbnail altogether. michael@0: Size size = { 0, 0 }; michael@0: return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size); michael@0: } michael@0: michael@0: Size size = supportedSizes[smallestDeltaIndex]; michael@0: DOM_CAMERA_LOGI("camera-param set thumbnail-size = %ux%u (requested %ux%u)\n", michael@0: size.width, size.height, aSize.width, aSize.height); michael@0: if (size.width > INT32_MAX || size.height > INT32_MAX) { michael@0: DOM_CAMERA_LOGE("Supported thumbnail size is too big, no change\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetThumbnailSize(const Size& aSize) michael@0: { michael@0: class SetThumbnailSize : public nsRunnable michael@0: { michael@0: public: michael@0: SetThumbnailSize(nsGonkCameraControl* aCameraControl, const Size& aSize) michael@0: : mCameraControl(aCameraControl) michael@0: , mSize(aSize) michael@0: { michael@0: MOZ_COUNT_CTOR(SetThumbnailSize); michael@0: } michael@0: ~SetThumbnailSize() { MOZ_COUNT_DTOR(SetThumbnailSize); } michael@0: michael@0: NS_IMETHODIMP michael@0: Run() MOZ_OVERRIDE michael@0: { michael@0: nsresult rv = mCameraControl->SetThumbnailSizeImpl(mSize); michael@0: if (NS_FAILED(rv)) { michael@0: mCameraControl->OnError(CameraControlListener::kInUnspecified, michael@0: CameraControlListener::kErrorSetThumbnailSizeFailed); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: nsRefPtr mCameraControl; michael@0: Size mSize; michael@0: }; michael@0: michael@0: if (NS_GetCurrentThread() == mCameraThread) { michael@0: return SetThumbnailSizeImpl(aSize); michael@0: } michael@0: michael@0: return mCameraThread->Dispatch(new SetThumbnailSize(this, aSize), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::UpdateThumbnailSize() michael@0: { michael@0: return SetThumbnailSize(mLastThumbnailSize); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetPictureSizeImpl(const Size& aSize) michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: michael@0: /** michael@0: * Some drivers are less friendly about getting one of these set to zero, michael@0: * so if either is not specified, ignore both and go with current or michael@0: * default settings. michael@0: */ michael@0: if (!aSize.width || !aSize.height) { michael@0: DOM_CAMERA_LOGW("Ignoring requested picture size of %ux%u\n", aSize.width, aSize.height); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: if (aSize.width == mLastPictureSize.width && aSize.height == mLastPictureSize.height) { michael@0: DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aSize.width, aSize.height); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Choose the supported picture size that is closest in area to the michael@0: * specified size. Some drivers will fail to take a picture if the michael@0: * thumbnail size is not the same aspect ratio, so we update that michael@0: * as well to a size closest to the last user-requested one. michael@0: */ michael@0: int smallestDelta = INT_MAX; michael@0: uint32_t smallestDeltaIndex = UINT32_MAX; michael@0: int targetArea = aSize.width * aSize.height; michael@0: michael@0: nsAutoTArray supportedSizes; michael@0: Get(CAMERA_PARAM_SUPPORTED_PICTURESIZES, supportedSizes); michael@0: michael@0: for (uint32_t i = 0; i < supportedSizes.Length(); ++i) { michael@0: int area = supportedSizes[i].width * supportedSizes[i].height; michael@0: int delta = abs(area - targetArea); michael@0: michael@0: if (area != 0 && delta < smallestDelta) { michael@0: smallestDelta = delta; michael@0: smallestDeltaIndex = i; michael@0: } michael@0: } michael@0: michael@0: if (smallestDeltaIndex == UINT32_MAX) { michael@0: DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n", michael@0: aSize.width, aSize.height); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: Size size = supportedSizes[smallestDeltaIndex]; michael@0: DOM_CAMERA_LOGI("camera-param set picture-size = %ux%u (requested %ux%u)\n", michael@0: size.width, size.height, aSize.width, aSize.height); michael@0: if (size.width > INT32_MAX || size.height > INT32_MAX) { michael@0: DOM_CAMERA_LOGE("Supported picture size is too big, no change\n"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsresult rv = mParams.Set(CAMERA_PARAM_PICTURE_SIZE, size); michael@0: if (NS_FAILED(rv)) { michael@0: return rv; michael@0: } michael@0: michael@0: mLastPictureSize = size; michael@0: michael@0: // Finally, update the thumbnail size in case the picture michael@0: // aspect ratio changed. michael@0: return UpdateThumbnailSize(); michael@0: } michael@0: michael@0: int32_t michael@0: nsGonkCameraControl::RationalizeRotation(int32_t aRotation) michael@0: { michael@0: int32_t r = aRotation; michael@0: michael@0: // The result of this operation is an angle from 0..270 degrees, michael@0: // in steps of 90 degrees. Angles are rounded to the nearest michael@0: // magnitude, so 45 will be rounded to 90, and -45 will be rounded michael@0: // to -90 (not 0). michael@0: if (r >= 0) { michael@0: r += 45; michael@0: } else { michael@0: r -= 45; michael@0: } michael@0: r /= 90; michael@0: r %= 4; michael@0: r *= 90; michael@0: if (r < 0) { michael@0: r += 360; michael@0: } michael@0: michael@0: return r; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetPictureSize(const Size& aSize) michael@0: { michael@0: class SetPictureSize : public nsRunnable michael@0: { michael@0: public: michael@0: SetPictureSize(nsGonkCameraControl* aCameraControl, const Size& aSize) michael@0: : mCameraControl(aCameraControl) michael@0: , mSize(aSize) michael@0: { michael@0: MOZ_COUNT_CTOR(SetPictureSize); michael@0: } michael@0: ~SetPictureSize() { MOZ_COUNT_DTOR(SetPictureSize); } michael@0: michael@0: NS_IMETHODIMP michael@0: Run() MOZ_OVERRIDE michael@0: { michael@0: nsresult rv = mCameraControl->SetPictureSizeImpl(mSize); michael@0: if (NS_FAILED(rv)) { michael@0: mCameraControl->OnError(CameraControlListener::kInUnspecified, michael@0: CameraControlListener::kErrorSetPictureSizeFailed); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: nsRefPtr mCameraControl; michael@0: Size mSize; michael@0: }; michael@0: michael@0: if (NS_GetCurrentThread() == mCameraThread) { michael@0: return SetPictureSizeImpl(aSize); michael@0: } michael@0: michael@0: return mCameraThread->Dispatch(new SetPictureSize(this, aSize), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::TakePictureImpl() michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: michael@0: if (mCameraHw->TakePicture() != OK) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: // In Gonk, taking a picture implicitly stops the preview stream, michael@0: // so we need to reflect that here. michael@0: OnPreviewStateChange(CameraControlListener::kPreviewPaused); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::PushParametersImpl() michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: DOM_CAMERA_LOGI("Pushing camera parameters\n"); michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: michael@0: if (mCameraHw->PushParameters(mParams) != OK) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::PullParametersImpl() michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: DOM_CAMERA_LOGI("Pulling camera parameters\n"); michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: michael@0: return mCameraHw->PullParameters(mParams); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetupRecordingFlash(bool aAutoEnableLowLightTorch) michael@0: { michael@0: mAutoFlashModeOverridden = false; michael@0: michael@0: if (!aAutoEnableLowLightTorch || !mLuminanceSupported || !mFlashSupported) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: DOM_CAMERA_LOGI("Luminance reporting and flash supported\n"); michael@0: michael@0: nsresult rv = PullParametersImpl(); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: nsString luminance; michael@0: rv = mParams.Get(CAMERA_PARAM_LUMINANCE, luminance); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: // If we failed to get the luminance, assume it's "high" michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsString flashMode; michael@0: rv = mParams.Get(CAMERA_PARAM_FLASHMODE, flashMode); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: // If we failed to get the current flash mode, swallow the error michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (luminance.EqualsASCII("low") && flashMode.EqualsASCII("auto")) { michael@0: DOM_CAMERA_LOGI("Low luminance detected, turning on flash\n"); michael@0: rv = SetAndPush(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("torch")); michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: // If we failed to turn on the flash, swallow the error michael@0: return NS_OK; michael@0: } michael@0: michael@0: mAutoFlashModeOverridden = true; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor, michael@0: const StartRecordingOptions* aOptions) michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: michael@0: NS_ENSURE_TRUE(mRecorderProfile, NS_ERROR_NOT_INITIALIZED); michael@0: NS_ENSURE_FALSE(mRecorder, NS_ERROR_FAILURE); michael@0: michael@0: /** michael@0: * Get the base path from device storage and append the app-specified michael@0: * filename to it. The filename may include a relative subpath michael@0: * (e.g.) "DCIM/IMG_0001.jpg". michael@0: * michael@0: * The camera app needs to provide the file extension '.3gp' for now. michael@0: * See bug 795202. michael@0: */ michael@0: NS_ENSURE_TRUE(aFileDescriptor, NS_ERROR_FAILURE); michael@0: nsAutoString fullPath; michael@0: mVideoFile = aFileDescriptor->mDSFile; michael@0: mVideoFile->GetFullPath(fullPath); michael@0: DOM_CAMERA_LOGI("Video filename is '%s'\n", michael@0: NS_LossyConvertUTF16toASCII(fullPath).get()); michael@0: michael@0: if (!mVideoFile->IsSafePath()) { michael@0: DOM_CAMERA_LOGE("Invalid video file name\n"); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: // SetupRecording creates a dup of the file descriptor, so we need to michael@0: // close the file descriptor when we leave this function. Also note, that michael@0: // since we're already off the main thread, we don't need to dispatch this. michael@0: // We just let the CloseFileRunnable destructor do the work. michael@0: nsRefPtr closer; michael@0: if (aFileDescriptor->mFileDescriptor.IsValid()) { michael@0: closer = new CloseFileRunnable(aFileDescriptor->mFileDescriptor); michael@0: } michael@0: nsresult rv; michael@0: int fd = aFileDescriptor->mFileDescriptor.PlatformHandle(); michael@0: if (aOptions) { michael@0: rv = SetupRecording(fd, aOptions->rotation, aOptions->maxFileSizeBytes, michael@0: aOptions->maxVideoLengthMs); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: rv = SetupRecordingFlash(aOptions->autoEnableLowLightTorch); michael@0: } michael@0: } else { michael@0: rv = SetupRecording(fd, 0, 0, 0); michael@0: } michael@0: if (NS_WARN_IF(NS_FAILED(rv))) { michael@0: return rv; michael@0: } michael@0: michael@0: if (mRecorder->start() != OK) { michael@0: DOM_CAMERA_LOGE("mRecorder->start() failed\n"); michael@0: // important: we MUST destroy the recorder if start() fails! michael@0: mRecorder = nullptr; michael@0: // put the flash back to the 'auto' state michael@0: if (mAutoFlashModeOverridden) { michael@0: SetAndPush(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("auto")); michael@0: } michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: OnRecorderStateChange(CameraControlListener::kRecorderStarted); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::StopRecordingImpl() michael@0: { michael@0: class RecordingComplete : public nsRunnable michael@0: { michael@0: public: michael@0: RecordingComplete(DeviceStorageFile* aFile) michael@0: : mFile(aFile) michael@0: { } michael@0: michael@0: ~RecordingComplete() { } michael@0: michael@0: NS_IMETHODIMP michael@0: Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsCOMPtr obs = mozilla::services::GetObserverService(); michael@0: obs->NotifyObservers(mFile, "file-watcher-notify", NS_LITERAL_STRING("modified").get()); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mFile; michael@0: }; michael@0: michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: michael@0: // nothing to do if we have no mRecorder michael@0: NS_ENSURE_TRUE(mRecorder, NS_OK); michael@0: michael@0: mRecorder->stop(); michael@0: mRecorder = nullptr; michael@0: OnRecorderStateChange(CameraControlListener::kRecorderStopped); michael@0: michael@0: if (mAutoFlashModeOverridden) { michael@0: SetAndPush(CAMERA_PARAM_FLASHMODE, NS_LITERAL_STRING("auto")); michael@0: } michael@0: michael@0: // notify DeviceStorage that the new video file is closed and ready michael@0: return NS_DispatchToMainThread(new RecordingComplete(mVideoFile), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::ResumeContinuousFocusImpl() michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: michael@0: DOM_CAMERA_LOGI("Resuming continuous autofocus\n"); michael@0: michael@0: // see michael@0: // http://developer.android.com/reference/android/hardware/Camera.Parameters.html#FOCUS_MODE_CONTINUOUS_PICTURE michael@0: if (NS_WARN_IF(mCameraHw->CancelAutoFocus() != OK)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsGonkCameraControl::OnAutoFocusComplete(bool aSuccess) michael@0: { michael@0: class AutoFocusComplete : public nsRunnable michael@0: { michael@0: public: michael@0: AutoFocusComplete(nsGonkCameraControl* aCameraControl, bool aSuccess) michael@0: : mCameraControl(aCameraControl) michael@0: , mSuccess(aSuccess) michael@0: { } michael@0: michael@0: NS_IMETHODIMP michael@0: Run() MOZ_OVERRIDE michael@0: { michael@0: mCameraControl->OnAutoFocusComplete(mSuccess); michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: nsRefPtr mCameraControl; michael@0: bool mSuccess; michael@0: }; michael@0: michael@0: if (NS_GetCurrentThread() == mCameraThread) { michael@0: /** michael@0: * Auto focusing can change some of the camera's parameters, so michael@0: * we need to pull a new set before notifying any clients. michael@0: */ michael@0: PullParametersImpl(); michael@0: CameraControlImpl::OnAutoFocusComplete(aSuccess); michael@0: return; michael@0: } michael@0: michael@0: /** michael@0: * Because the callback needs to call PullParametersImpl(), michael@0: * we need to dispatch this callback through the Camera Thread. michael@0: */ michael@0: mCameraThread->Dispatch(new AutoFocusComplete(this, aSuccess), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: bool michael@0: FeatureDetected(int32_t feature[]) michael@0: { michael@0: /** michael@0: * For information on what constitutes a valid feature, see: michael@0: * http://androidxref.com/4.0.4/xref/system/core/include/system/camera.h#202 michael@0: * michael@0: * Although the comments explicitly state that undetected features are michael@0: * indicated using the value -2000, we conservatively include anything michael@0: * outside the explicitly valid range of [-1000, 1000] as undetected michael@0: * as well. michael@0: */ michael@0: const int32_t kLowerFeatureBound = -1000; michael@0: const int32_t kUpperFeatureBound = 1000; michael@0: return (feature[0] >= kLowerFeatureBound && feature[0] <= kUpperFeatureBound) || michael@0: (feature[1] >= kLowerFeatureBound && feature[1] <= kUpperFeatureBound); michael@0: } michael@0: michael@0: void michael@0: nsGonkCameraControl::OnFacesDetected(camera_frame_metadata_t* aMetaData) michael@0: { michael@0: NS_ENSURE_TRUE_VOID(aMetaData); michael@0: michael@0: nsTArray faces; michael@0: uint32_t numFaces = aMetaData->number_of_faces; michael@0: DOM_CAMERA_LOGI("Camera detected %d face(s)", numFaces); michael@0: michael@0: faces.SetCapacity(numFaces); michael@0: michael@0: for (uint32_t i = 0; i < numFaces; ++i) { michael@0: Face* f = faces.AppendElement(); michael@0: michael@0: f->id = aMetaData->faces[i].id; michael@0: f->score = aMetaData->faces[i].score; michael@0: if (f->score > 100) { michael@0: f->score = 100; michael@0: } michael@0: f->bound.left = aMetaData->faces[i].rect[0]; michael@0: f->bound.top = aMetaData->faces[i].rect[1]; michael@0: f->bound.right = aMetaData->faces[i].rect[2]; michael@0: f->bound.bottom = aMetaData->faces[i].rect[3]; michael@0: DOM_CAMERA_LOGI("Camera face[%u] appended: id=%d, score=%d, bound=(%d, %d)-(%d, %d)\n", michael@0: i, f->id, f->score, f->bound.left, f->bound.top, f->bound.right, f->bound.bottom); michael@0: michael@0: f->hasLeftEye = FeatureDetected(aMetaData->faces[i].left_eye); michael@0: if (f->hasLeftEye) { michael@0: f->leftEye.x = aMetaData->faces[i].left_eye[0]; michael@0: f->leftEye.y = aMetaData->faces[i].left_eye[1]; michael@0: DOM_CAMERA_LOGI(" Left eye detected at (%d, %d)\n", michael@0: f->leftEye.x, f->leftEye.y); michael@0: } else { michael@0: DOM_CAMERA_LOGI(" No left eye detected\n"); michael@0: } michael@0: michael@0: f->hasRightEye = FeatureDetected(aMetaData->faces[i].right_eye); michael@0: if (f->hasRightEye) { michael@0: f->rightEye.x = aMetaData->faces[i].right_eye[0]; michael@0: f->rightEye.y = aMetaData->faces[i].right_eye[1]; michael@0: DOM_CAMERA_LOGI(" Right eye detected at (%d, %d)\n", michael@0: f->rightEye.x, f->rightEye.y); michael@0: } else { michael@0: DOM_CAMERA_LOGI(" No right eye detected\n"); michael@0: } michael@0: michael@0: f->hasMouth = FeatureDetected(aMetaData->faces[i].mouth); michael@0: if (f->hasMouth) { michael@0: f->mouth.x = aMetaData->faces[i].mouth[0]; michael@0: f->mouth.y = aMetaData->faces[i].mouth[1]; michael@0: DOM_CAMERA_LOGI(" Mouth detected at (%d, %d)\n", f->mouth.x, f->mouth.y); michael@0: } else { michael@0: DOM_CAMERA_LOGI(" No mouth detected\n"); michael@0: } michael@0: } michael@0: michael@0: CameraControlImpl::OnFacesDetected(faces); michael@0: } michael@0: michael@0: void michael@0: nsGonkCameraControl::OnTakePictureComplete(uint8_t* aData, uint32_t aLength) michael@0: { michael@0: ReentrantMonitorAutoEnter mon(mReentrantMonitor); michael@0: michael@0: uint8_t* data = new uint8_t[aLength]; michael@0: michael@0: memcpy(data, aData, aLength); michael@0: michael@0: nsString s(NS_LITERAL_STRING("image/")); michael@0: s.Append(mFileFormat); michael@0: DOM_CAMERA_LOGI("Got picture, type '%s', %u bytes\n", NS_ConvertUTF16toUTF8(s).get(), aLength); michael@0: OnTakePictureComplete(data, aLength, s); michael@0: michael@0: if (mResumePreviewAfterTakingPicture) { michael@0: nsresult rv = StartPreview(); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Failed to restart camera preview (%x)\n", rv); michael@0: OnPreviewStateChange(CameraControlListener::kPreviewStopped); michael@0: } michael@0: } michael@0: michael@0: DOM_CAMERA_LOGI("nsGonkCameraControl::OnTakePictureComplete() done\n"); michael@0: } michael@0: michael@0: void michael@0: nsGonkCameraControl::OnTakePictureError() michael@0: { michael@0: CameraControlImpl::OnError(CameraControlListener::kInTakePicture, michael@0: CameraControlListener::kErrorApiFailed); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetPreviewSize(const Size& aSize) michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: michael@0: nsTArray previewSizes; michael@0: nsresult rv = mParams.Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, previewSizes); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Camera failed to return any preview sizes (0x%x)\n", rv); michael@0: return rv; michael@0: } michael@0: michael@0: Size best; michael@0: rv = GetSupportedSize(aSize, previewSizes, best); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Failed to find a supported preview size, requested size %dx%d", michael@0: aSize.width, aSize.height); michael@0: return rv; michael@0: } michael@0: michael@0: // Some camera drivers will ignore our preview size if it's larger michael@0: // than the currently set video recording size, so we need to set michael@0: // the video size here as well, just in case. michael@0: if (best.width > mLastRecorderSize.width || best.height > mLastRecorderSize.height) { michael@0: SetVideoSize(best); michael@0: } michael@0: mCurrentConfiguration.mPreviewSize = best; michael@0: return mParams.Set(CAMERA_PARAM_PREVIEWSIZE, best); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetVideoSize(const Size& aSize) michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: michael@0: nsTArray videoSizes; michael@0: nsresult rv = mParams.Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, videoSizes); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Camera failed to return any video sizes (0x%x)\n", rv); michael@0: return rv; michael@0: } michael@0: michael@0: Size best; michael@0: rv = GetSupportedSize(aSize, videoSizes, best); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Failed to find a supported video size, requested size %dx%d", michael@0: aSize.width, aSize.height); michael@0: return rv; michael@0: } michael@0: mLastRecorderSize = best; michael@0: return mParams.Set(CAMERA_PARAM_VIDEOSIZE, best); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::GetSupportedSize(const Size& aSize, michael@0: const nsTArray& supportedSizes, michael@0: Size& best) michael@0: { michael@0: nsresult rv = NS_ERROR_INVALID_ARG; michael@0: best = aSize; michael@0: uint32_t minSizeDelta = UINT32_MAX; michael@0: uint32_t delta; michael@0: michael@0: if (!aSize.width && !aSize.height) { michael@0: // no size specified, take the first supported size michael@0: best = supportedSizes[0]; michael@0: rv = NS_OK; michael@0: } else if (aSize.width && aSize.height) { michael@0: // both height and width specified, find the supported size closest to requested size michael@0: uint32_t targetArea = aSize.width * aSize.height; michael@0: for (nsTArray::index_type i = 0; i < supportedSizes.Length(); i++) { michael@0: Size size = supportedSizes[i]; michael@0: uint32_t delta = abs((long int)(size.width * size.height - targetArea)); michael@0: if (delta < minSizeDelta) { michael@0: minSizeDelta = delta; michael@0: best = size; michael@0: rv = NS_OK; michael@0: } michael@0: } michael@0: } else if (!aSize.width) { michael@0: // width not specified, find closest height match michael@0: for (nsTArray::index_type i = 0; i < supportedSizes.Length(); i++) { michael@0: Size size = supportedSizes[i]; michael@0: delta = abs((long int)(size.height - aSize.height)); michael@0: if (delta < minSizeDelta) { michael@0: minSizeDelta = delta; michael@0: best = size; michael@0: rv = NS_OK; michael@0: } michael@0: } michael@0: } else if (!aSize.height) { michael@0: // height not specified, find closest width match michael@0: for (nsTArray::index_type i = 0; i < supportedSizes.Length(); i++) { michael@0: Size size = supportedSizes[i]; michael@0: delta = abs((long int)(size.width - aSize.width)); michael@0: if (delta < minSizeDelta) { michael@0: minSizeDelta = delta; michael@0: best = size; michael@0: rv = NS_OK; michael@0: } michael@0: } michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetupVideoMode(const nsAString& aProfile) michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); michael@0: michael@0: // read preferences for camcorder michael@0: mMediaProfiles = MediaProfiles::getInstance(); michael@0: michael@0: nsAutoCString profile = NS_ConvertUTF16toUTF8(aProfile); michael@0: // XXXkhuey are we leaking? michael@0: mRecorderProfile = GetGonkRecorderProfileManager().take()->Get(profile.get()); michael@0: if (!mRecorderProfile) { michael@0: DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n", profile.get()); michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: const GonkRecorderVideoProfile* video = mRecorderProfile->GetGonkVideoProfile(); michael@0: int width = video->GetWidth(); michael@0: int height = video->GetHeight(); michael@0: int fps = video->GetFramerate(); michael@0: if (fps == -1 || width < 0 || height < 0) { michael@0: DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n", michael@0: fps, width, height); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: PullParametersImpl(); michael@0: michael@0: Size size; michael@0: size.width = static_cast(width); michael@0: size.height = static_cast(height); michael@0: michael@0: { michael@0: ICameraControlParameterSetAutoEnter set(this); michael@0: michael@0: // The camera interface allows for hardware to provide two video michael@0: // streams, a low resolution preview and a potentially high resolution michael@0: // stream for encoding. For now we don't use this and set preview and video michael@0: // size to the same thing. michael@0: nsresult rv = SetVideoSize(size); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Failed to set video mode video size (0x%x)\n", rv); michael@0: return rv; michael@0: } michael@0: michael@0: rv = SetPreviewSize(size); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Failed to set video mode preview size (0x%x)\n", rv); michael@0: return rv; michael@0: } michael@0: michael@0: rv = mParams.Set(CAMERA_PARAM_PREVIEWFRAMERATE, fps); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Failed to set video mode frame rate (0x%x)\n", rv); michael@0: return rv; michael@0: } michael@0: michael@0: rv = PushParameters(); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGE("Failed to set video mode settings (0x%x)\n", rv); michael@0: return rv; michael@0: } michael@0: michael@0: mPreviewFps = fps; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: class GonkRecorderListener : public IMediaRecorderClient michael@0: { michael@0: public: michael@0: GonkRecorderListener(nsGonkCameraControl* aCameraControl) michael@0: : mCameraControl(aCameraControl) michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n", michael@0: __func__, __LINE__, this, mCameraControl.get()); michael@0: } michael@0: michael@0: void notify(int msg, int ext1, int ext2) michael@0: { michael@0: if (mCameraControl) { michael@0: mCameraControl->OnRecorderEvent(msg, ext1, ext2); michael@0: } michael@0: } michael@0: michael@0: IBinder* onAsBinder() michael@0: { michael@0: DOM_CAMERA_LOGE("onAsBinder() called, should NEVER get called!\n"); michael@0: return nullptr; michael@0: } michael@0: michael@0: protected: michael@0: ~GonkRecorderListener() { } michael@0: nsRefPtr mCameraControl; michael@0: }; michael@0: michael@0: void michael@0: nsGonkCameraControl::OnRecorderEvent(int msg, int ext1, int ext2) michael@0: { michael@0: /** michael@0: * Refer to base/include/media/mediarecorder.h for a complete list michael@0: * of error and info message codes. There are duplicate values michael@0: * within the status/error code space, as determined by code inspection: michael@0: * michael@0: * +------- msg michael@0: * | +----- ext1 michael@0: * | | +--- ext2 michael@0: * V V V michael@0: * 1 MEDIA_RECORDER_EVENT_ERROR michael@0: * 1 MEDIA_RECORDER_ERROR_UNKNOWN michael@0: * [3] ERROR_MALFORMED michael@0: * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED michael@0: * 0 michael@0: * 2 MEDIA_RECORDER_EVENT_INFO michael@0: * 800 MEDIA_RECORDER_INFO_MAX_DURATION_REACHED michael@0: * 0 michael@0: * 801 MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED michael@0: * 0 michael@0: * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1b] michael@0: * [3] UNKNOWN_ERROR, etc. michael@0: * 100 MEDIA_ERROR[4] michael@0: * 100 mediaplayer.h::MEDIA_ERROR_SERVER_DIED michael@0: * 0 michael@0: * 100 MEDIA_RECORDER_TRACK_EVENT_ERROR michael@0: * 100 MEDIA_RECORDER_TRACK_ERROR_GENERAL[1a] michael@0: * [3] UNKNOWN_ERROR, etc. michael@0: * 200 MEDIA_RECORDER_ERROR_VIDEO_NO_SYNC_FRAME[2] michael@0: * ? michael@0: * 101 MEDIA_RECORDER_TRACK_EVENT_INFO michael@0: * 1000 MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS[1a] michael@0: * [3] UNKNOWN_ERROR, etc. michael@0: * N see mediarecorder.h::media_recorder_info_type[5] michael@0: * michael@0: * 1. a) High 4 bits are the track number, the next 12 bits are reserved, michael@0: * and the final 16 bits are the actual error code (above). michael@0: * b) But not in this case. michael@0: * 2. Never actually used in AOSP code? michael@0: * 3. Specific error codes are from utils/Errors.h and/or michael@0: * include/media/stagefright/MediaErrors.h. michael@0: * 4. Only in frameworks/base/media/libmedia/mediaplayer.cpp. michael@0: * 5. These are mostly informational and we can ignore them; note that michael@0: * although the MEDIA_RECORDER_INFO_MAX_DURATION_REACHED and michael@0: * MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED values are defined in this michael@0: * enum, they are used with different ext1 codes. /o\ michael@0: */ michael@0: int trackNum = CameraControlListener::kNoTrackNumber; michael@0: michael@0: switch (msg) { michael@0: // Recorder-related events michael@0: case MEDIA_RECORDER_EVENT_INFO: michael@0: switch (ext1) { michael@0: case MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED: michael@0: DOM_CAMERA_LOGI("recorder-event : info: maximum file size reached\n"); michael@0: OnRecorderStateChange(CameraControlListener::kFileSizeLimitReached, ext2, trackNum); michael@0: return; michael@0: michael@0: case MEDIA_RECORDER_INFO_MAX_DURATION_REACHED: michael@0: DOM_CAMERA_LOGI("recorder-event : info: maximum video duration reached\n"); michael@0: OnRecorderStateChange(CameraControlListener::kVideoLengthLimitReached, ext2, trackNum); michael@0: return; michael@0: michael@0: case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS: michael@0: DOM_CAMERA_LOGI("recorder-event : info: track completed\n"); michael@0: OnRecorderStateChange(CameraControlListener::kTrackCompleted, ext2, trackNum); michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: case MEDIA_RECORDER_EVENT_ERROR: michael@0: switch (ext1) { michael@0: case MEDIA_RECORDER_ERROR_UNKNOWN: michael@0: DOM_CAMERA_LOGE("recorder-event : recorder-error: %d (0x%08x)\n", ext2, ext2); michael@0: OnRecorderStateChange(CameraControlListener::kMediaRecorderFailed, ext2, trackNum); michael@0: return; michael@0: michael@0: case MEDIA_ERROR_SERVER_DIED: michael@0: DOM_CAMERA_LOGE("recorder-event : recorder-error: server died\n"); michael@0: OnRecorderStateChange(CameraControlListener::kMediaServerFailed, ext2, trackNum); michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: // Track-related events, see note 1(a) above. michael@0: case MEDIA_RECORDER_TRACK_EVENT_INFO: michael@0: trackNum = (ext1 & 0xF0000000) >> 28; michael@0: ext1 &= 0xFFFF; michael@0: switch (ext1) { michael@0: case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS: michael@0: if (ext2 == OK) { michael@0: DOM_CAMERA_LOGI("recorder-event : track-complete: track %d, %d (0x%08x)\n", trackNum, ext2, ext2); michael@0: OnRecorderStateChange(CameraControlListener::kTrackCompleted, ext2, trackNum); michael@0: return; michael@0: } michael@0: DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2); michael@0: OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum); michael@0: return; michael@0: michael@0: case MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME: michael@0: DOM_CAMERA_LOGI("recorder-event : track-info: progress in time: %d ms\n", ext2); michael@0: return; michael@0: } michael@0: break; michael@0: michael@0: case MEDIA_RECORDER_TRACK_EVENT_ERROR: michael@0: trackNum = (ext1 & 0xF0000000) >> 28; michael@0: ext1 &= 0xFFFF; michael@0: DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2); michael@0: OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum); michael@0: return; michael@0: } michael@0: michael@0: // All unhandled cases wind up here michael@0: DOM_CAMERA_LOGW("recorder-event : unhandled: msg=%d, ext1=%d, ext2=%d\n", msg, ext1, ext2); michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::SetupRecording(int aFd, int aRotation, michael@0: int64_t aMaxFileSizeBytes, michael@0: int64_t aMaxVideoLengthMs) michael@0: { michael@0: RETURN_IF_NO_CAMERA_HW(); michael@0: michael@0: // choosing a size big enough to hold the params michael@0: const size_t SIZE = 256; michael@0: char buffer[SIZE]; michael@0: michael@0: mRecorder = new GonkRecorder(); michael@0: CHECK_SETARG(mRecorder->init()); michael@0: michael@0: nsresult rv = mRecorderProfile->ConfigureRecorder(mRecorder); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: CHECK_SETARG(mRecorder->setCamera(mCameraHw)); michael@0: michael@0: DOM_CAMERA_LOGI("maxVideoLengthMs=%lld\n", aMaxVideoLengthMs); michael@0: if (aMaxVideoLengthMs == 0) { michael@0: aMaxVideoLengthMs = -1; michael@0: } michael@0: snprintf(buffer, SIZE, "max-duration=%lld", aMaxVideoLengthMs); michael@0: CHECK_SETARG(mRecorder->setParameters(String8(buffer))); michael@0: michael@0: DOM_CAMERA_LOGI("maxFileSizeBytes=%lld\n", aMaxFileSizeBytes); michael@0: if (aMaxFileSizeBytes == 0) { michael@0: aMaxFileSizeBytes = -1; michael@0: } michael@0: snprintf(buffer, SIZE, "max-filesize=%lld", aMaxFileSizeBytes); michael@0: CHECK_SETARG(mRecorder->setParameters(String8(buffer))); michael@0: michael@0: // adjust rotation by camera sensor offset michael@0: int r = aRotation; michael@0: r += mCameraHw->GetSensorOrientation(); michael@0: r = RationalizeRotation(r); michael@0: DOM_CAMERA_LOGI("setting video rotation to %d degrees (mapped from %d)\n", r, aRotation); michael@0: snprintf(buffer, SIZE, "video-param-rotation-angle-degrees=%d", r); michael@0: CHECK_SETARG(mRecorder->setParameters(String8(buffer))); michael@0: michael@0: CHECK_SETARG(mRecorder->setListener(new GonkRecorderListener(this))); michael@0: michael@0: // recording API needs file descriptor of output file michael@0: CHECK_SETARG(mRecorder->setOutputFile(aFd, 0, 0)); michael@0: CHECK_SETARG(mRecorder->prepare()); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsGonkCameraControl::StopImpl() michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); michael@0: michael@0: // if we're recording, stop recording michael@0: StopRecordingImpl(); michael@0: michael@0: // stop the preview michael@0: StopPreviewImpl(); michael@0: michael@0: // release the hardware handle michael@0: if (mCameraHw.get()){ michael@0: mCameraHw->Close(); michael@0: mCameraHw.clear(); michael@0: } michael@0: michael@0: OnHardwareStateChange(CameraControlListener::kHardwareClosed); michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsGonkCameraControl::GetGonkRecorderProfileManager() michael@0: { michael@0: if (!mProfileManager) { michael@0: nsTArray sizes; michael@0: nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: mProfileManager = new GonkRecorderProfileManager(mCameraId); michael@0: mProfileManager->SetSupportedResolutions(sizes); michael@0: } michael@0: michael@0: nsRefPtr profileMgr = mProfileManager; michael@0: return profileMgr.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsGonkCameraControl::GetRecorderProfileManagerImpl() michael@0: { michael@0: nsRefPtr profileMgr = GetGonkRecorderProfileManager(); michael@0: return profileMgr.forget(); michael@0: } michael@0: michael@0: void michael@0: nsGonkCameraControl::OnNewPreviewFrame(layers::TextureClient* aBuffer) michael@0: { michael@0: nsRefPtr frame = mImageContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR); michael@0: michael@0: GrallocImage* videoImage = static_cast(frame.get()); michael@0: michael@0: GrallocImage::GrallocData data; michael@0: data.mGraphicBuffer = aBuffer; michael@0: data.mPicSize = IntSize(mCurrentConfiguration.mPreviewSize.width, michael@0: mCurrentConfiguration.mPreviewSize.height); michael@0: videoImage->SetData(data); michael@0: michael@0: OnNewPreviewFrame(frame, mCurrentConfiguration.mPreviewSize.width, michael@0: mCurrentConfiguration.mPreviewSize.height); michael@0: } michael@0: michael@0: void michael@0: nsGonkCameraControl::OnError(CameraControlListener::CameraErrorContext aWhere, michael@0: CameraControlListener::CameraError aError) michael@0: { michael@0: if (aError == CameraControlListener::kErrorServiceFailed) { michael@0: OnPreviewStateChange(CameraControlListener::kPreviewStopped); michael@0: OnHardwareStateChange(CameraControlListener::kHardwareClosed); michael@0: } michael@0: michael@0: CameraControlImpl::OnError(aWhere, aError); michael@0: } michael@0: michael@0: // Gonk callback handlers. michael@0: namespace mozilla { michael@0: michael@0: void michael@0: OnTakePictureComplete(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength) michael@0: { michael@0: gc->OnTakePictureComplete(aData, aLength); michael@0: } michael@0: michael@0: void michael@0: OnTakePictureError(nsGonkCameraControl* gc) michael@0: { michael@0: gc->OnTakePictureError(); michael@0: } michael@0: michael@0: void michael@0: OnAutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess) michael@0: { michael@0: gc->OnAutoFocusComplete(aSuccess); michael@0: } michael@0: michael@0: void michael@0: OnAutoFocusMoving(nsGonkCameraControl* gc, bool aIsMoving) michael@0: { michael@0: gc->OnAutoFocusMoving(aIsMoving); michael@0: } michael@0: michael@0: void michael@0: OnFacesDetected(nsGonkCameraControl* gc, camera_frame_metadata_t* aMetaData) michael@0: { michael@0: gc->OnFacesDetected(aMetaData); michael@0: } michael@0: michael@0: void michael@0: OnNewPreviewFrame(nsGonkCameraControl* gc, layers::TextureClient* aBuffer) michael@0: { michael@0: gc->OnNewPreviewFrame(aBuffer); michael@0: } michael@0: michael@0: void michael@0: OnShutter(nsGonkCameraControl* gc) michael@0: { michael@0: gc->OnShutter(); michael@0: } michael@0: michael@0: void michael@0: OnClosed(nsGonkCameraControl* gc) michael@0: { michael@0: gc->OnClosed(); michael@0: } michael@0: michael@0: void michael@0: OnError(nsGonkCameraControl* gc, CameraControlListener::CameraError aError, michael@0: int32_t aArg1, int32_t aArg2) michael@0: { michael@0: #ifdef PR_LOGGING michael@0: DOM_CAMERA_LOGE("OnError : aError=%d, aArg1=%d, aArg2=%d\n", aError, aArg1, aArg2); michael@0: #else michael@0: unused << aArg1; michael@0: unused << aArg2; michael@0: #endif michael@0: gc->OnError(CameraControlListener::kInUnspecified, aError); michael@0: } michael@0: michael@0: } // namespace mozilla