From 557d8c2d5c0540c86af589fe99f8cd4ab05c028c Mon Sep 17 00:00:00 2001 From: Mike Habicher Date: Fri, 14 Feb 2014 17:41:07 -0500 Subject: [PATCH] Bug 909542 - refactor CameraControl API, r=dhylands,jst,jesup,onecyrenus --- b2g/installer/package-manifest.in | 1 - browser/installer/package-manifest.in | 1 - content/media/webrtc/MediaEngineWebRTC.cpp | 17 +- content/media/webrtc/MediaEngineWebRTC.h | 81 +- .../media/webrtc/MediaEngineWebRTCVideo.cpp | 130 +- dom/base/nsDOMClassInfo.cpp | 8 - dom/base/nsDOMClassInfoClasses.h | 2 - dom/bindings/Bindings.conf | 17 +- dom/camera/AutoRwLock.h | 49 + dom/camera/CameraCommon.h | 39 - dom/camera/CameraControlImpl.cpp | 893 +++++---- dom/camera/CameraControlImpl.h | 763 +------- dom/camera/CameraControlListener.h | 100 + dom/camera/CameraRecorderProfiles.cpp | 2 +- dom/camera/CameraRecorderProfiles.h | 2 - dom/camera/DOMCameraCapabilities.cpp | 632 +++--- dom/camera/DOMCameraCapabilities.h | 115 +- dom/camera/DOMCameraControl.cpp | 1102 ++++++++--- dom/camera/DOMCameraControl.h | 185 +- dom/camera/DOMCameraControlListener.cpp | 329 ++++ dom/camera/DOMCameraControlListener.h | 48 + dom/camera/DOMCameraManager.cpp | 55 +- dom/camera/DOMCameraManager.h | 48 +- dom/camera/DOMCameraPreview.cpp | 304 --- dom/camera/DOMCameraPreview.h | 99 - dom/camera/FallbackCameraCapabilities.cpp | 3 +- dom/camera/FallbackCameraControl.cpp | 243 +-- dom/camera/FallbackCameraManager.cpp | 22 +- dom/camera/GonkCameraControl.cpp | 1740 +++++++---------- dom/camera/GonkCameraControl.h | 166 +- dom/camera/GonkCameraHwMgr.cpp | 64 +- dom/camera/GonkCameraHwMgr.h | 14 +- dom/camera/GonkCameraManager.cpp | 59 +- dom/camera/GonkCameraParameters.cpp | 620 ++++++ dom/camera/GonkCameraParameters.h | 184 ++ dom/camera/GonkCameraSource.cpp | 6 +- dom/camera/GonkRecorderProfiles.cpp | 2 +- dom/camera/GonkRecorderProfiles.h | 5 +- dom/camera/ICameraControl.h | 194 +- dom/camera/moz.build | 9 +- dom/camera/nsIDOMCameraManager.idl | 229 --- dom/camera/test/mochitest.ini | 1 + dom/camera/test/test_camera.html | 64 +- dom/camera/test/test_camera_2.html | 205 ++ dom/media/MediaManager.cpp | 4 - dom/webidl/CameraCapabilities.webidl | 33 + dom/webidl/CameraControl.webidl | 453 +++-- dom/webidl/CameraManager.webidl | 83 +- dom/webidl/moz.build | 1 + js/xpconnect/src/dictionary_helper_gen.conf | 5 - mobile/android/installer/package-manifest.in | 1 - testing/mochitest/b2g-debug.json | 1 - .../gonk/nativewindow/GonkNativeWindowICS.cpp | 56 +- 53 files changed, 5052 insertions(+), 4437 deletions(-) create mode 100644 dom/camera/AutoRwLock.h create mode 100644 dom/camera/CameraControlListener.h create mode 100644 dom/camera/DOMCameraControlListener.cpp create mode 100644 dom/camera/DOMCameraControlListener.h delete mode 100644 dom/camera/DOMCameraPreview.cpp delete mode 100644 dom/camera/DOMCameraPreview.h create mode 100644 dom/camera/GonkCameraParameters.cpp create mode 100644 dom/camera/GonkCameraParameters.h delete mode 100644 dom/camera/nsIDOMCameraManager.idl create mode 100644 dom/camera/test/test_camera_2.html create mode 100644 dom/webidl/CameraCapabilities.webidl diff --git a/b2g/installer/package-manifest.in b/b2g/installer/package-manifest.in index 299dc693d8c2..d615f5fefa30 100644 --- a/b2g/installer/package-manifest.in +++ b/b2g/installer/package-manifest.in @@ -167,7 +167,6 @@ #ifdef MOZ_B2G_BT @BINPATH@/components/dom_bluetooth.xpt #endif -@BINPATH@/components/dom_camera.xpt @BINPATH@/components/dom_canvas.xpt @BINPATH@/components/dom_contacts.xpt @BINPATH@/components/dom_alarm.xpt diff --git a/browser/installer/package-manifest.in b/browser/installer/package-manifest.in index 1a4e0dc8eabc..6f6bd8b5b149 100644 --- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -197,7 +197,6 @@ #ifdef MOZ_B2G_BT @BINPATH@/components/dom_bluetooth.xpt #endif -@BINPATH@/components/dom_camera.xpt @BINPATH@/components/dom_canvas.xpt @BINPATH@/components/dom_alarm.xpt @BINPATH@/components/dom_core.xpt diff --git a/content/media/webrtc/MediaEngineWebRTC.cpp b/content/media/webrtc/MediaEngineWebRTC.cpp index 3023f4fbc388..c76aef8557fa 100644 --- a/content/media/webrtc/MediaEngineWebRTC.cpp +++ b/content/media/webrtc/MediaEngineWebRTC.cpp @@ -42,7 +42,7 @@ GetUserMediaLog() #define LOG(args) PR_LOG(GetUserMediaLog(), PR_LOG_DEBUG, args) namespace mozilla { -#ifndef MOZ_B2G_CAMERA + MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs) : mMutex("mozilla::MediaEngineWebRTC") , mVideoEngine(nullptr) @@ -51,27 +51,26 @@ MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs) , mAudioEngineInit(false) , mHasTabVideoSource(false) { +#ifndef MOZ_B2G_CAMERA nsCOMPtr compMgr; NS_GetComponentRegistrar(getter_AddRefs(compMgr)); if (compMgr) { compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID, &mHasTabVideoSource); } +#else + AsyncLatencyLogger::Get()->AddRef(); +#endif if (aPrefs.mLoadAdapt) { mLoadMonitor = new LoadMonitor(); mLoadMonitor->Init(mLoadMonitor); } } -#endif - void MediaEngineWebRTC::EnumerateVideoDevices(nsTArray >* aVSources) { #ifdef MOZ_B2G_CAMERA MutexAutoLock lock(mMutex); - if (!mCameraManager) { - return; - } /** * We still enumerate every time, in case a new device was plugged in since @@ -83,14 +82,14 @@ MediaEngineWebRTC::EnumerateVideoDevices(nsTArrayGetNumberOfCameras(num); + result = ICameraControl::GetNumberOfCameras(num); if (num <= 0 || result != NS_OK) { return; } for (int i = 0; i < num; i++) { nsCString cameraName; - result = mCameraManager->GetCameraName(i, cameraName); + result = ICameraControl::GetCameraName(i, cameraName); if (result != NS_OK) { continue; } @@ -101,7 +100,7 @@ MediaEngineWebRTC::EnumerateVideoDevices(nsTArrayAppendElement(vSource.get()); } else { - vSource = new MediaEngineWebRTCVideoSource(mCameraManager, i, mWindowId); + vSource = new MediaEngineWebRTCVideoSource(i); mVideoSources.Put(uuid, vSource); // Hashtable takes ownership. aVSources->AppendElement(vSource); } diff --git a/content/media/webrtc/MediaEngineWebRTC.h b/content/media/webrtc/MediaEngineWebRTC.h index b7ee479caad1..cb06a60cfecf 100644 --- a/content/media/webrtc/MediaEngineWebRTC.h +++ b/content/media/webrtc/MediaEngineWebRTC.h @@ -46,9 +46,8 @@ #include "webrtc/video_engine/include/vie_render.h" #include "webrtc/video_engine/include/vie_capture.h" #ifdef MOZ_B2G_CAMERA -#include "CameraPreviewMediaStream.h" -#include "DOMCameraManager.h" -#include "GonkCameraControl.h" +#include "CameraControlListener.h" +#include "ICameraControl.h" #include "ImageContainer.h" #include "nsGlobalWindow.h" #include "prprf.h" @@ -73,7 +72,7 @@ class GetCameraNameRunnable; * mSources, mImageContainer, mSources, mState, mImage, mLastCapture * * MainThread: - * mDOMCameraControl, mCaptureIndex, mCameraThread, mWindowId, mCameraManager, + * mCaptureIndex, mWindowId, * mNativeCameraControl, mPreviewStream, mState, mLastCapture, mWidth, mHeight * * Where mWidth, mHeight, mImage are protected by mMonitor @@ -83,24 +82,15 @@ class GetCameraNameRunnable; class MediaEngineWebRTCVideoSource : public MediaEngineVideoSource , public nsRunnable #ifdef MOZ_B2G_CAMERA - , public nsICameraGetCameraCallback - , public nsICameraPreviewStreamCallback - , public nsICameraTakePictureCallback - , public nsICameraReleaseCallback - , public nsICameraErrorCallback - , public CameraPreviewFrameCallback + , public CameraControlListener #else , public webrtc::ExternalRenderer #endif { public: #ifdef MOZ_B2G_CAMERA - MediaEngineWebRTCVideoSource(nsDOMCameraManager* aCameraManager, - int aIndex, uint64_t aWindowId) - : mCameraManager(aCameraManager) - , mNativeCameraControl(nullptr) - , mPreviewStream(nullptr) - , mWindowId(aWindowId) + MediaEngineWebRTCVideoSource(int aIndex) + : mCameraControl(nullptr) , mCallbackMonitor("WebRTCCamera.CallbackMonitor") , mCaptureIndex(aIndex) , mMonitor("WebRTCCamera.Monitor") @@ -111,7 +101,6 @@ public: , mSnapshotPath(nullptr) { mState = kReleased; - NS_NewNamedThread("CameraThread", getter_AddRefs(mCameraThread)); Init(); } #else @@ -167,20 +156,17 @@ public: NS_DECL_THREADSAFE_ISUPPORTS #ifdef MOZ_B2G_CAMERA - NS_DECL_NSICAMERAGETCAMERACALLBACK - NS_DECL_NSICAMERAPREVIEWSTREAMCALLBACK - NS_DECL_NSICAMERATAKEPICTURECALLBACK - NS_DECL_NSICAMERARELEASECALLBACK - NS_DECL_NSICAMERAERRORCALLBACK + void OnHardwareStateChange(HardwareState aState); + void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration); + bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight); + void OnError(CameraErrorContext aContext, const nsACString& aError); + void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType); void AllocImpl(); void DeallocImpl(); void StartImpl(webrtc::CaptureCapability aCapability); void StopImpl(); void SnapshotImpl(); - - virtual void OnNewFrame(const gfxIntSize& aIntrinsicSize, layers::Image* aImage); - #endif // This runnable is for creating a temporary file on the main thread. @@ -212,20 +198,8 @@ private: // Engine variables. #ifdef MOZ_B2G_CAMERA - // MediaEngine hold this DOM object, and the MediaEngine is hold by Navigator - // Their life time is always much longer than this object. Use a raw-pointer - // here should be safe. - // We need raw pointer here since such DOM-object should not addref/release on - // any thread other than main thread, but we must use this object for now. To - // avoid any bad thing do to addref/release DOM-object on other thread, we use - // raw-pointer for now. - nsDOMCameraManager* mCameraManager; - nsRefPtr mDOMCameraControl; - nsRefPtr mNativeCameraControl; - nsRefPtr mPreviewStream; - uint64_t mWindowId; + nsRefPtr mCameraControl; mozilla::ReentrantMonitor mCallbackMonitor; // Monitor for camera callback handling - nsRefPtr mCameraThread; nsRefPtr mLastCapture; #else webrtc::VideoEngine* mVideoEngine; // Weak reference, don't free. @@ -351,24 +325,7 @@ private: class MediaEngineWebRTC : public MediaEngine { public: -#ifdef MOZ_B2G_CAMERA - MediaEngineWebRTC(nsDOMCameraManager* aCameraManager, uint64_t aWindowId) - : mMutex("mozilla::MediaEngineWebRTC") - , mVideoEngine(nullptr) - , mVoiceEngine(nullptr) - , mVideoEngineInit(false) - , mAudioEngineInit(false) - , mCameraManager(aCameraManager) - , mWindowId(aWindowId) - , mHasTabVideoSource(false) - { - AsyncLatencyLogger::Get(true)->AddRef(); - mLoadMonitor = new LoadMonitor(); - mLoadMonitor->Init(mLoadMonitor); - } -#else MediaEngineWebRTC(MediaEnginePrefs &aPrefs); -#endif ~MediaEngineWebRTC() { Shutdown(); #ifdef MOZ_B2G_CAMERA @@ -400,19 +357,7 @@ private: nsRefPtrHashtable mVideoSources; nsRefPtrHashtable mAudioSources; -#ifdef MOZ_B2G_CAMERA - // MediaEngine hold this DOM object, and the MediaEngine is hold by Navigator - // Their life time is always much longer than this object. Use a raw-pointer - // here should be safe. - // We need raw pointer here since such DOM-object should not addref/release on - // any thread other than main thread, but we must use this object for now. To - // avoid any bad thing do to addref/release DOM-object on other thread, we use - // raw-pointer for now. - nsDOMCameraManager* mCameraManager; - uint64_t mWindowId; -#endif - - nsRefPtr mLoadMonitor; + nsRefPtr mLoadMonitor; }; } diff --git a/content/media/webrtc/MediaEngineWebRTCVideo.cpp b/content/media/webrtc/MediaEngineWebRTCVideo.cpp index d4b39d86ab8c..7d772801ce66 100644 --- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp +++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp @@ -312,7 +312,9 @@ nsresult MediaEngineWebRTCVideoSource::Start(SourceMediaStream* aStream, TrackID aID) { LOG((__FUNCTION__)); +#ifndef MOZ_B2G_CAMERA int error = 0; +#endif if (!mInitDone || !aStream) { return NS_ERROR_FAILURE; } @@ -413,7 +415,7 @@ MediaEngineWebRTCVideoSource::Init() { #ifdef MOZ_B2G_CAMERA nsAutoCString deviceName; - mCameraManager->GetCameraName(mCaptureIndex, deviceName); + ICameraControl::GetCameraName(mCaptureIndex, deviceName); CopyUTF8toUTF16(deviceName, mDeviceName); CopyUTF8toUTF16(deviceName, mUniqueId); #else @@ -492,125 +494,95 @@ void MediaEngineWebRTCVideoSource::AllocImpl() { MOZ_ASSERT(NS_IsMainThread()); - mDOMCameraControl = new nsDOMCameraControl(mCaptureIndex, - mCameraThread, - this, - this, - nsGlobalWindow::GetInnerWindowWithId(mWindowId)); - mCameraManager->Register(mDOMCameraControl); + mCameraControl = ICameraControl::Create(mCaptureIndex, nullptr); + mCameraControl->AddListener(this); } void MediaEngineWebRTCVideoSource::DeallocImpl() { MOZ_ASSERT(NS_IsMainThread()); - mNativeCameraControl->ReleaseHardware(this, this); - mNativeCameraControl = nullptr; + mCameraControl->ReleaseHardware(); + mCameraControl = nullptr; } void MediaEngineWebRTCVideoSource::StartImpl(webrtc::CaptureCapability aCapability) { MOZ_ASSERT(NS_IsMainThread()); - idl::CameraSize size; - size.width = aCapability.width; - size.height = aCapability.height; - mNativeCameraControl->GetPreviewStream(size, this, this); + ICameraControl::Configuration config; + config.mMode = ICameraControl::kPictureMode; + config.mPreviewSize.width = aCapability.width; + config.mPreviewSize.height = aCapability.height; + mCameraControl->SetConfiguration(config); + mCameraControl->Set(CAMERA_PARAM_PICTURESIZE, config.mPreviewSize); } void MediaEngineWebRTCVideoSource::StopImpl() { MOZ_ASSERT(NS_IsMainThread()); - mNativeCameraControl->StopPreview(); - mPreviewStream = nullptr; + mCameraControl->StopPreview(); } void MediaEngineWebRTCVideoSource::SnapshotImpl() { - MOZ_ASSERT(NS_IsMainThread()); - - idl::CameraSize size; - size.width = mCapability.width; - size.height = mCapability.height; - - idl::CameraPosition cameraPosition; - cameraPosition.latitude = NAN; - cameraPosition.longitude = NAN; - cameraPosition.altitude = NAN; - cameraPosition.timestamp = NAN; - - mNativeCameraControl->TakePicture(size, 0, NS_LITERAL_STRING("jpeg"), cameraPosition, PR_Now() / 1000000, this, this); + mCameraControl->TakePicture(); } -// nsICameraGetCameraCallback -nsresult -MediaEngineWebRTCVideoSource::HandleEvent(nsISupports* /* unused */) { - MOZ_ASSERT(NS_IsMainThread()); +void +MediaEngineWebRTCVideoSource::OnHardwareStateChange(HardwareState aState) +{ ReentrantMonitorAutoEnter sync(mCallbackMonitor); - mNativeCameraControl = static_cast(mDOMCameraControl->GetNativeCameraControl().get()); - mState = kAllocated; + if (aState == CameraControlListener::kHardwareOpen) { + mState = kAllocated; + } else { + mState = kReleased; + mCameraControl->RemoveListener(this); + } mCallbackMonitor.Notify(); - return NS_OK; } -// nsICameraPreviewStreamCallback -nsresult -MediaEngineWebRTCVideoSource::HandleEvent(nsIDOMMediaStream* stream) { - MOZ_ASSERT(NS_IsMainThread()); +void +MediaEngineWebRTCVideoSource::OnConfigurationChange(const CameraListenerConfiguration& aConfiguration) +{ ReentrantMonitorAutoEnter sync(mCallbackMonitor); - mPreviewStream = static_cast(stream); - mPreviewStream->Start(); - CameraPreviewMediaStream* cameraStream = static_cast(mPreviewStream->GetStream()); - cameraStream->SetFrameCallback(this); mState = kStarted; mCallbackMonitor.Notify(); - return NS_OK; } -// nsICameraTakePictureCallback -nsresult -MediaEngineWebRTCVideoSource::HandleEvent(nsIDOMBlob* picture) { - MOZ_ASSERT(NS_IsMainThread()); - ReentrantMonitorAutoEnter sync(mCallbackMonitor); - mLastCapture = static_cast(picture); - mCallbackMonitor.Notify(); - return NS_OK; -} - -// nsICameraReleaseCallback -nsresult -MediaEngineWebRTCVideoSource::HandleEvent() { - MOZ_ASSERT(NS_IsMainThread()); - ReentrantMonitorAutoEnter sync(mCallbackMonitor); - mState = kReleased; - mCallbackMonitor.Notify(); - return NS_OK; -} - -// nsICameraErrorCallback -nsresult -MediaEngineWebRTCVideoSource::HandleEvent(const nsAString& error) { - MOZ_ASSERT(NS_IsMainThread()); - ReentrantMonitorAutoEnter sync(mCallbackMonitor); - mCallbackMonitor.Notify(); - return NS_OK; -} - -//Except this one. This callback should called on camera preview thread. void -MediaEngineWebRTCVideoSource::OnNewFrame(const gfxIntSize& aIntrinsicSize, layers::Image* aImage) { +MediaEngineWebRTCVideoSource::OnError(CameraErrorContext aContext, const nsACString& aError) +{ + ReentrantMonitorAutoEnter sync(mCallbackMonitor); + mCallbackMonitor.Notify(); +} + +void +MediaEngineWebRTCVideoSource::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) +{ + ReentrantMonitorAutoEnter sync(mCallbackMonitor); + mLastCapture = + static_cast(new nsDOMMemoryFile(static_cast(aData), + static_cast(aLength), + aMimeType)); + mCallbackMonitor.Notify(); +} + +bool +MediaEngineWebRTCVideoSource::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) { MonitorAutoLock enter(mMonitor); if (mState == kStopped) { - return; + return false; } mImage = aImage; - if (mWidth != aIntrinsicSize.width || mHeight != aIntrinsicSize.height) { - mWidth = aIntrinsicSize.width; - mHeight = aIntrinsicSize.height; + if (mWidth != static_cast(aWidth) || mHeight != static_cast(aHeight)) { + mWidth = aWidth; + mHeight = aHeight; LOG(("Video FrameSizeChange: %ux%u", mWidth, mHeight)); } + return true; // return true because we're accepting the frame } #endif diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 2fdba154e3db..39831289b93d 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -160,7 +160,6 @@ #include "FMRadio.h" #endif -#include "nsIDOMCameraManager.h" #include "nsIDOMGlobalObjectConstructor.h" #include "nsIDOMLockedFile.h" #include "nsDebug.h" @@ -470,9 +469,6 @@ static nsDOMClassInfoData sClassInfoData[] = { DOM_DEFAULT_SCRIPTABLE_FLAGS) #endif - NS_DEFINE_CLASSINFO_DATA(CameraCapabilities, nsDOMGenericSH, - DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(LockedFile, nsEventTargetSH, EVENTTARGET_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(CSSFontFeatureValuesRule, nsDOMGenericSH, @@ -1176,10 +1172,6 @@ nsDOMClassInfo::Init() #endif - DOM_CLASSINFO_MAP_BEGIN(CameraCapabilities, nsICameraCapabilities) - DOM_CLASSINFO_MAP_ENTRY(nsICameraCapabilities) - DOM_CLASSINFO_MAP_END - DOM_CLASSINFO_MAP_BEGIN(LockedFile, nsIDOMLockedFile) DOM_CLASSINFO_MAP_ENTRY(nsIDOMLockedFile) DOM_CLASSINFO_MAP_END diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index 848c134b038a..d97d91a68dcc 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -103,8 +103,6 @@ DOMCI_CLASS(CSSPageRule) DOMCI_CLASS(MozIccManager) #endif -DOMCI_CLASS(CameraCapabilities) - DOMCI_CLASS(LockedFile) DOMCI_CLASS(CSSFontFeatureValuesRule) diff --git a/dom/bindings/Bindings.conf b/dom/bindings/Bindings.conf index 8fca550cde05..cf23d4e0f6e9 100644 --- a/dom/bindings/Bindings.conf +++ b/dom/bindings/Bindings.conf @@ -180,6 +180,11 @@ DOMInterfaces = { 'headerFile': 'BluetoothManager.h' }, +'CameraCapabilities': { + 'nativeType': 'mozilla::dom::CameraCapabilities', + 'headerFile': 'DOMCameraCapabilities.h' +}, + 'CameraControl': { 'nativeType': 'mozilla::nsDOMCameraControl', 'headerFile': 'DOMCameraControl.h', @@ -1967,15 +1972,3 @@ addExternalIface('XPathExpression') addExternalIface('XPathNSResolver') addExternalIface('XULCommandDispatcher') addExternalIface('DataTransfer', notflattened=True) -addExternalIface('GetCameraCallback', nativeType='nsICameraGetCameraCallback', headerFile='nsIDOMCameraManager.h') -addExternalIface('CameraErrorCallback', nativeType='nsICameraErrorCallback', headerFile='nsIDOMCameraManager.h') -addExternalIface('CameraCapabilities', nativeType='nsICameraCapabilities', headerFile='nsIDOMCameraManager.h') -addExternalIface('CameraAutoFocusCallback', nativeType='nsICameraAutoFocusCallback', headerFile='nsIDOMCameraManager.h') -addExternalIface('CameraShutterCallback', nativeType='nsICameraShutterCallback', headerFile='nsIDOMCameraManager.h') -addExternalIface('CameraClosedCallback', nativeType='nsICameraClosedCallback', headerFile='nsIDOMCameraManager.h') -addExternalIface('CameraTakePictureCallback', nativeType='nsICameraTakePictureCallback', headerFile='nsIDOMCameraManager.h') -addExternalIface('CameraReleaseCallback', nativeType='nsICameraReleaseCallback', headerFile='nsIDOMCameraManager.h') -addExternalIface('CameraStartRecordingCallback', nativeType='nsICameraStartRecordingCallback', headerFile='nsIDOMCameraManager.h') -addExternalIface('CameraPreviewStateChange', nativeType='nsICameraPreviewStateChange', headerFile='nsIDOMCameraManager.h') -addExternalIface('CameraPreviewStreamCallback', nativeType='nsICameraPreviewStreamCallback', headerFile='nsIDOMCameraManager.h') -addExternalIface('CameraRecorderStateChange', nativeType='nsICameraRecorderStateChange', headerFile='nsIDOMCameraManager.h') diff --git a/dom/camera/AutoRwLock.h b/dom/camera/AutoRwLock.h new file mode 100644 index 000000000000..95b9cf3795cf --- /dev/null +++ b/dom/camera/AutoRwLock.h @@ -0,0 +1,49 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef RWLOCK_AUTO_ENTER_H +#define RWLOCK_AUTO_ENTER_H + +#include "prrwlock.h" +#include "mozilla/Assertions.h" + +class RwLockAutoEnterRead +{ +public: + RwLockAutoEnterRead(PRRWLock* aRwLock) + : mRwLock(aRwLock) + { + MOZ_ASSERT(mRwLock); + PR_RWLock_Rlock(mRwLock); + } + + ~RwLockAutoEnterRead() + { + PR_RWLock_Unlock(mRwLock); + } + +protected: + PRRWLock* mRwLock; +}; + +class RwLockAutoEnterWrite +{ +public: + RwLockAutoEnterWrite(PRRWLock* aRwLock) + : mRwLock(aRwLock) + { + MOZ_ASSERT(mRwLock); + PR_RWLock_Wlock(mRwLock); + } + + ~RwLockAutoEnterWrite() + { + PR_RWLock_Unlock(mRwLock); + } + +protected: + PRRWLock* mRwLock; +}; + +#endif // RWLOCK_AUTO_ENTER_H diff --git a/dom/camera/CameraCommon.h b/dom/camera/CameraCommon.h index e615a3c750f6..6251d4812f13 100644 --- a/dom/camera/CameraCommon.h +++ b/dom/camera/CameraCommon.h @@ -19,7 +19,6 @@ #define NAN std::numeric_limits::quiet_NaN() #endif -#include "nsIDOMCameraManager.h" #include "prlog.h" #ifdef PR_LOGGING @@ -62,44 +61,6 @@ enum { #define DOM_CAMERA_LOGW( ... ) DOM_CAMERA_LOG( DOM_CAMERA_LOG_WARNING, __VA_ARGS__ ) #define DOM_CAMERA_LOGE( ... ) DOM_CAMERA_LOG( DOM_CAMERA_LOG_ERROR, __VA_ARGS__ ) -enum { - CAMERA_PARAM_EFFECT, - CAMERA_PARAM_WHITEBALANCE, - CAMERA_PARAM_SCENEMODE, - CAMERA_PARAM_FLASHMODE, - CAMERA_PARAM_FOCUSMODE, - CAMERA_PARAM_ZOOM, - CAMERA_PARAM_METERINGAREAS, - CAMERA_PARAM_FOCUSAREAS, - CAMERA_PARAM_FOCALLENGTH, - CAMERA_PARAM_FOCUSDISTANCENEAR, - CAMERA_PARAM_FOCUSDISTANCEOPTIMUM, - CAMERA_PARAM_FOCUSDISTANCEFAR, - CAMERA_PARAM_EXPOSURECOMPENSATION, - CAMERA_PARAM_PICTURESIZE, - CAMERA_PARAM_THUMBNAILSIZE, - CAMERA_PARAM_THUMBNAILQUALITY, - CAMERA_PARAM_SENSORANGLE, - - CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, - CAMERA_PARAM_SUPPORTED_VIDEOSIZES, - CAMERA_PARAM_SUPPORTED_PICTURESIZES, - CAMERA_PARAM_SUPPORTED_PICTUREFORMATS, - CAMERA_PARAM_SUPPORTED_WHITEBALANCES, - CAMERA_PARAM_SUPPORTED_SCENEMODES, - CAMERA_PARAM_SUPPORTED_EFFECTS, - CAMERA_PARAM_SUPPORTED_FLASHMODES, - CAMERA_PARAM_SUPPORTED_FOCUSMODES, - CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, - CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, - CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, - CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION, - CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, - CAMERA_PARAM_SUPPORTED_ZOOM, - CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, - CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES -}; - #ifdef PR_LOGGING static inline void nsLogAddRefCamera(const char *file, uint32_t line, void* p, uint32_t count, const char *clazz, uint32_t size) diff --git a/dom/camera/CameraControlImpl.cpp b/dom/camera/CameraControlImpl.cpp index 4a0294b561e5..8896ddcee527 100644 --- a/dom/camera/CameraControlImpl.cpp +++ b/dom/camera/CameraControlImpl.cpp @@ -2,278 +2,51 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "CameraControlImpl.h" #include "base/basictypes.h" #include "mozilla/Assertions.h" -#include "DOMCameraPreview.h" +#include "mozilla/unused.h" +#include "nsIWeakReferenceUtils.h" #include "CameraRecorderProfiles.h" -#include "CameraControlImpl.h" #include "CameraCommon.h" #include "nsGlobalWindow.h" #include "DeviceStorageFileDescriptor.h" +#include "CameraControlListener.h" using namespace mozilla; -using namespace mozilla::dom; -using namespace mozilla::idl; -CameraControlImpl::CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId) +nsWeakPtr CameraControlImpl::sCameraThread; + +CameraControlImpl::CameraControlImpl(uint32_t aCameraId) : mCameraId(aCameraId) - , mCameraThread(aCameraThread) - , mWindowId(aWindowId) - , mFileFormat() - , mMaxMeteringAreas(0) - , mMaxFocusAreas(0) - , mPreviewState(PREVIEW_STOPPED) - , mDOMPreview(nullptr) - , mAutoFocusOnSuccessCb(nullptr) - , mAutoFocusOnErrorCb(nullptr) - , mTakePictureOnSuccessCb(nullptr) - , mTakePictureOnErrorCb(nullptr) - , mOnShutterCb(nullptr) - , mOnClosedCb(nullptr) - , mOnRecorderStateChangeCb(nullptr) - , mOnPreviewStateChangeCb(nullptr) + , mPreviewState(CameraControlListener::kPreviewStopped) + , mHardwareState(CameraControlListener::kHardwareClosed) { DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + + // reuse the same camera thread to conserve resources + nsCOMPtr ct = do_QueryReferent(sCameraThread); + if (ct) { + mCameraThread = ct.forget(); + } else { + nsresult rv = NS_NewNamedThread("CameraThread", getter_AddRefs(mCameraThread)); + unused << rv; // swallow rv to suppress a compiler warning when the macro + // is #defined to nothing (i.e. in non-DEBUG builds). + MOZ_ASSERT(NS_SUCCEEDED(rv)); + + // keep a weak reference to the new thread + sCameraThread = do_GetWeakReference(mCameraThread); + } + + mListenerLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "CameraControlImpl.Listeners.Lock"); } CameraControlImpl::~CameraControlImpl() { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); -} - -// Helpers for string properties. -nsresult -CameraControlImpl::Set(uint32_t aKey, const nsAString& aValue) -{ - SetParameter(aKey, NS_ConvertUTF16toUTF8(aValue).get()); - return NS_OK; -} - -nsresult -CameraControlImpl::Get(uint32_t aKey, nsAString& aValue) -{ - const char* value = GetParameterConstChar(aKey); - if (!value) { - return NS_ERROR_FAILURE; + if (mListenerLock) { + PR_DestroyRWLock(mListenerLock); + mListenerLock = nullptr; } - - aValue.AssignASCII(value); - return NS_OK; -} - -// Helpers for doubles. -nsresult -CameraControlImpl::Set(uint32_t aKey, double aValue) -{ - SetParameter(aKey, aValue); - return NS_OK; -} - -nsresult -CameraControlImpl::Get(uint32_t aKey, double* aValue) -{ - MOZ_ASSERT(aValue); - *aValue = GetParameterDouble(aKey); - return NS_OK; -} - -// Helper for weighted regions. -nsresult -CameraControlImpl::Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit) -{ - if (aLimit == 0) { - DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__, __LINE__); - return NS_OK; - } - - if (!aValue.isObject()) { - return NS_ERROR_INVALID_ARG; - } - - uint32_t length = 0; - - JS::Rooted regions(aCx, &aValue.toObject()); - if (!JS_GetArrayLength(aCx, regions, &length)) { - return NS_ERROR_FAILURE; - } - - DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit); - if (length > aLimit) { - length = aLimit; - } - - nsTArray regionArray; - regionArray.SetCapacity(length); - - for (uint32_t i = 0; i < length; ++i) { - JS::Rooted v(aCx); - - if (!JS_GetElement(aCx, regions, i, &v)) { - return NS_ERROR_FAILURE; - } - - CameraRegion* r = regionArray.AppendElement(); - /** - * These are the default values. We can remove these when the xpidl - * dictionary parser gains the ability to grok default values. - */ - r->top = -1000; - r->left = -1000; - r->bottom = 1000; - r->right = 1000; - r->weight = 1000; - - nsresult rv = r->Init(aCx, v.address()); - NS_ENSURE_SUCCESS(rv, rv); - - DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%d\n", - i, - r->top, - r->left, - r->bottom, - r->right, - r->weight - ); - } - SetParameter(aKey, regionArray); - return NS_OK; -} - -nsresult -CameraControlImpl::Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue) -{ - nsTArray regionArray; - - GetParameter(aKey, regionArray); - - JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); - if (!array) { - return NS_ERROR_OUT_OF_MEMORY; - } - - uint32_t length = regionArray.Length(); - DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length); - - for (uint32_t i = 0; i < length; ++i) { - CameraRegion* r = ®ionArray[i]; - JS::Rooted v(aCx); - - JS::Rooted o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); - if (!o) { - return NS_ERROR_OUT_OF_MEMORY; - } - - DOM_CAMERA_LOGI("top=%d\n", r->top); - v = INT_TO_JSVAL(r->top); - if (!JS_SetProperty(aCx, o, "top", v)) { - return NS_ERROR_FAILURE; - } - DOM_CAMERA_LOGI("left=%d\n", r->left); - v = INT_TO_JSVAL(r->left); - if (!JS_SetProperty(aCx, o, "left", v)) { - return NS_ERROR_FAILURE; - } - DOM_CAMERA_LOGI("bottom=%d\n", r->bottom); - v = INT_TO_JSVAL(r->bottom); - if (!JS_SetProperty(aCx, o, "bottom", v)) { - return NS_ERROR_FAILURE; - } - DOM_CAMERA_LOGI("right=%d\n", r->right); - v = INT_TO_JSVAL(r->right); - if (!JS_SetProperty(aCx, o, "right", v)) { - return NS_ERROR_FAILURE; - } - DOM_CAMERA_LOGI("weight=%d\n", r->weight); - v = INT_TO_JSVAL(r->weight); - if (!JS_SetProperty(aCx, o, "weight", v)) { - return NS_ERROR_FAILURE; - } - - if (!JS_SetElement(aCx, array, i, o)) { - return NS_ERROR_FAILURE; - } - } - - *aValue = JS::ObjectValue(*array); - return NS_OK; -} - -nsresult -CameraControlImpl::Set(nsICameraShutterCallback* aOnShutter) -{ - mOnShutterCb = new nsMainThreadPtrHolder(aOnShutter); - return NS_OK; -} - -nsresult -CameraControlImpl::Get(nsICameraShutterCallback** aOnShutter) -{ - *aOnShutter = mOnShutterCb; - return NS_OK; -} - -nsresult -CameraControlImpl::Set(nsICameraClosedCallback* aOnClosed) -{ - mOnClosedCb = new nsMainThreadPtrHolder(aOnClosed); - return NS_OK; -} - -nsresult -CameraControlImpl::Get(nsICameraClosedCallback** aOnClosed) -{ - *aOnClosed = mOnClosedCb; - return NS_OK; -} - -nsresult -CameraControlImpl::Set(nsICameraRecorderStateChange* aOnRecorderStateChange) -{ - mOnRecorderStateChangeCb = new nsMainThreadPtrHolder(aOnRecorderStateChange); - return NS_OK; -} - -nsresult -CameraControlImpl::Get(nsICameraRecorderStateChange** aOnRecorderStateChange) -{ - *aOnRecorderStateChange = mOnRecorderStateChangeCb; - return NS_OK; -} - -nsresult -CameraControlImpl::Set(nsICameraPreviewStateChange* aOnPreviewStateChange) -{ - mOnPreviewStateChangeCb = new nsMainThreadPtrHolder(aOnPreviewStateChange); - return NS_OK; -} - -nsresult -CameraControlImpl::Get(nsICameraPreviewStateChange** aOnPreviewStateChange) -{ - *aOnPreviewStateChange = mOnPreviewStateChangeCb; - return NS_OK; -} - -nsresult -CameraControlImpl::Set(uint32_t aKey, const idl::CameraSize& aSize) -{ - SetParameter(aKey, aSize); - return NS_OK; -} - -nsresult -CameraControlImpl::Get(uint32_t aKey, idl::CameraSize& aSize) -{ - GetParameter(aKey, aSize); - return NS_OK; -} - -nsresult -CameraControlImpl::Get(uint32_t aKey, int32_t* aValue) -{ - MOZ_ASSERT(aValue); - *aValue = GetParameterInt32(aKey); - return NS_OK; } already_AddRefed @@ -286,238 +59,518 @@ void CameraControlImpl::Shutdown() { DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - mAutoFocusOnSuccessCb = nullptr; - mAutoFocusOnErrorCb = nullptr; - mTakePictureOnSuccessCb = nullptr; - mTakePictureOnErrorCb = nullptr; - mOnShutterCb = nullptr; - mOnClosedCb = nullptr; - mOnRecorderStateChangeCb = nullptr; - mOnPreviewStateChangeCb = nullptr; } void -CameraControlImpl::OnShutterInternal() +CameraControlImpl::OnHardwareStateChange(CameraControlListener::HardwareState aNewState) { - DOM_CAMERA_LOGI("** SNAP **\n"); - if (mOnShutterCb.get()) { - mOnShutterCb->HandleEvent(); + // This callback can run on threads other than the Main Thread and + // the Camera Thread. On Gonk, it may be called from the camera's + // local binder thread, should the mediaserver process die. + RwLockAutoEnterRead lock(mListenerLock); + + if (aNewState == mHardwareState) { + DOM_CAMERA_LOGI("OnHardwareStateChange: state did not change from %d\n", mHardwareState); + return; + } + +#ifdef PR_LOGGING + const char* state[] = { "open", "closed", "failed" }; + MOZ_ASSERT(aNewState >= 0); + if (static_cast(aNewState) < sizeof(state) / sizeof(state[0])) { + DOM_CAMERA_LOGI("New hardware state is '%s'\n", state[aNewState]); + } else { + DOM_CAMERA_LOGE("OnHardwareStateChange: got invalid HardwareState value %d\n", aNewState); + } +#endif + + mHardwareState = aNewState; + + for (uint32_t i = 0; i < mListeners.Length(); ++i) { + CameraControlListener* l = mListeners[i]; + l->OnHardwareStateChange(mHardwareState); + } +} + +void +CameraControlImpl::OnConfigurationChange() +{ + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); + RwLockAutoEnterRead lock(mListenerLock); + + DOM_CAMERA_LOGI("OnConfigurationChange : %d listeners\n", mListeners.Length()); + + for (uint32_t i = 0; i < mListeners.Length(); ++i) { + CameraControlListener* l = mListeners[i]; + l->OnConfigurationChange(mCurrentConfiguration); + } +} + +void +CameraControlImpl::OnAutoFocusComplete(bool aAutoFocusSucceeded) +{ + // This callback can run on threads other than the Main Thread and + // the Camera Thread. On Gonk, it is called from the camera + // library's auto focus thread. + RwLockAutoEnterRead lock(mListenerLock); + + for (uint32_t i = 0; i < mListeners.Length(); ++i) { + CameraControlListener* l = mListeners[i]; + l->OnAutoFocusComplete(aAutoFocusSucceeded); + } +} + +void +CameraControlImpl::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) +{ + // This callback can run on threads other than the Main Thread and + // the Camera Thread. On Gonk, it is called from the camera + // library's snapshot thread. + RwLockAutoEnterRead lock(mListenerLock); + + for (uint32_t i = 0; i < mListeners.Length(); ++i) { + CameraControlListener* l = mListeners[i]; + l->OnTakePictureComplete(aData, aLength, aMimeType); } } void CameraControlImpl::OnShutter() { - nsCOMPtr onShutter = NS_NewRunnableMethod(this, &CameraControlImpl::OnShutterInternal); - nsresult rv = NS_DispatchToMainThread(onShutter); - if (NS_FAILED(rv)) { - DOM_CAMERA_LOGW("Failed to dispatch onShutter event to main thread (%d)\n", rv); + // This callback can run on threads other than the Main Thread and + // the Camera Thread. On Gonk, it is called from the camera driver's + // preview thread. + RwLockAutoEnterRead lock(mListenerLock); + + for (uint32_t i = 0; i < mListeners.Length(); ++i) { + CameraControlListener* l = mListeners[i]; + l->OnShutter(); } } -class OnClosedTask : public nsRunnable -{ -public: - OnClosedTask(nsMainThreadPtrHandle onClosed, uint64_t aWindowId) - : mOnClosedCb(onClosed) - , mWindowId(aWindowId) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - virtual ~OnClosedTask() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - NS_IMETHOD Run() - { - MOZ_ASSERT(NS_IsMainThread()); - - if (mOnClosedCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { - mOnClosedCb->HandleEvent(); - } - return NS_OK; - } - -protected: - nsMainThreadPtrHandle mOnClosedCb; - uint64_t mWindowId; -}; - void CameraControlImpl::OnClosed() { - nsCOMPtr onClosed = new OnClosedTask(mOnClosedCb, mWindowId); - nsresult rv = NS_DispatchToMainThread(onClosed); - if (NS_FAILED(rv)) { - DOM_CAMERA_LOGW("Failed to dispatch onClosed event to main thread (%d)\n", rv); + // This callback can run on threads other than the Main Thread and + // the Camera Thread. + RwLockAutoEnterRead lock(mListenerLock); + + for (uint32_t i = 0; i < mListeners.Length(); ++i) { + CameraControlListener* l = mListeners[i]; + l->OnHardwareStateChange(CameraControlListener::kHardwareClosed); } } void -CameraControlImpl::OnRecorderStateChange(const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber) +CameraControlImpl::OnRecorderStateChange(CameraControlListener::RecorderState aState, + int32_t aStatus, int32_t aTrackNumber) { - DOM_CAMERA_LOGI("OnRecorderStateChange: '%s'\n", NS_ConvertUTF16toUTF8(aStateMsg).get()); + // This callback can run on threads other than the Main Thread and + // the Camera Thread. On Gonk, it is called from the media encoder + // thread. + RwLockAutoEnterRead lock(mListenerLock); - nsCOMPtr onRecorderStateChange = new CameraRecorderStateChange(mOnRecorderStateChangeCb, aStateMsg, aStatus, aTrackNumber, mWindowId); - nsresult rv = NS_DispatchToMainThread(onRecorderStateChange); - if (NS_FAILED(rv)) { - DOM_CAMERA_LOGE("Failed to dispatch onRecorderStateChange event to main thread (%d)\n", rv); + for (uint32_t i = 0; i < mListeners.Length(); ++i) { + CameraControlListener* l = mListeners[i]; + l->OnRecorderStateChange(aState, aStatus, aTrackNumber); } } void -CameraControlImpl::OnPreviewStateChange(PreviewState aNewState) +CameraControlImpl::OnPreviewStateChange(CameraControlListener::PreviewState aNewState) { + // This callback runs on the Main Thread and the Camera Thread, and + // may run on the local binder thread, should the mediaserver + // process die. + RwLockAutoEnterRead lock(mListenerLock); + if (aNewState == mPreviewState) { DOM_CAMERA_LOGI("OnPreviewStateChange: state did not change from %d\n", mPreviewState); return; } - nsString msg; - switch (aNewState) { - case PREVIEW_STOPPED: - msg = NS_LITERAL_STRING("stopped"); - break; - - case PREVIEW_STARTED: - msg = NS_LITERAL_STRING("started"); - break; - - default: - MOZ_ASSUME_UNREACHABLE("Preview state can only be PREVIEW_STOPPED or _STARTED!"); +#ifdef PR_LOGGING + const char* state[] = { "stopped", "paused", "started" }; + MOZ_ASSERT(aNewState >= 0); + if (static_cast(aNewState) < sizeof(state) / sizeof(state[0])) { + DOM_CAMERA_LOGI("New preview state is '%s'\n", state[aNewState]); + } else { + DOM_CAMERA_LOGE("OnPreviewStateChange: got unknown PreviewState value %d\n", aNewState); } +#endif - // const nsString& aStateMsg) - DOM_CAMERA_LOGI("OnPreviewStateChange: '%s'\n", NS_ConvertUTF16toUTF8(msg).get()); mPreviewState = aNewState; - nsCOMPtr onPreviewStateChange = new CameraPreviewStateChange(mOnPreviewStateChangeCb, msg, mWindowId); - nsresult rv = NS_DispatchToMainThread(onPreviewStateChange); - if (NS_FAILED(rv)) { - DOM_CAMERA_LOGE("Failed to dispatch onPreviewStateChange event to main thread (%d)\n", rv); + for (uint32_t i = 0; i < mListeners.Length(); ++i) { + CameraControlListener* l = mListeners[i]; + l->OnPreviewStateChange(mPreviewState); } } -nsresult -CameraControlImpl::GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) +bool +CameraControlImpl::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) { - nsCOMPtr getPreviewStreamTask = new GetPreviewStreamTask(this, aSize, onSuccess, onError); - return mCameraThread->Dispatch(getPreviewStreamTask, NS_DISPATCH_NORMAL); + // This function runs on neither the Main Thread nor the Camera Thread. + // On Gonk, it is called from the camera driver's preview thread. + RwLockAutoEnterRead lock(mListenerLock); + + DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %d preview frame listener(s)\n", + mListeners.Length()); + + bool consumed = false; + + for (uint32_t i = 0; i < mListeners.Length(); ++i) { + CameraControlListener* l = mListeners[i]; + consumed = l->OnNewPreviewFrame(aImage, aWidth, aHeight) || consumed; + } + return consumed; } -nsresult -CameraControlImpl::AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError) +void +CameraControlImpl::OnError(CameraControlListener::CameraErrorContext aContext, + CameraControlListener::CameraError aError) { - MOZ_ASSERT(NS_IsMainThread()); - bool cancel = false; + // This callback can run on threads other than the Main Thread and + // the Camera Thread. + RwLockAutoEnterRead lock(mListenerLock); - nsCOMPtr cb = mAutoFocusOnSuccessCb.get(); - if (cb) { - /** - * We already have a callback, so someone has already - * called autoFocus() -- cancel it. - */ - mAutoFocusOnSuccessCb = nullptr; - mAutoFocusOnErrorCb = nullptr; - cancel = true; +#ifdef PR_LOGGING + const char* error[] = { "camera-service-failed", "unknown" }; + if (static_cast(aError) < sizeof(error) / sizeof(error[0])) { + DOM_CAMERA_LOGW("CameraControlImpl::OnError : aContext=%u, msg='%s'\n", + aContext, error[aError]); + } else { + DOM_CAMERA_LOGE("CameraControlImpl::OnError : aContext=%u, unknown error=%d\n", + aContext, aError); + } +#endif + + for (uint32_t i = 0; i < mListeners.Length(); ++i) { + CameraControlListener* l = mListeners[i]; + l->OnError(aContext, aError); + } +} + +// Camera control asynchronous message; these are dispatched from +// the Main Thread to the Camera Thread, where they are consumed. + +class CameraControlImpl::ControlMessage : public nsRunnable +{ +public: + ControlMessage(CameraControlImpl* aCameraControl, + CameraControlListener::CameraErrorContext aContext) + : mCameraControl(aCameraControl) + , mContext(aContext) + { + MOZ_COUNT_CTOR(CameraControlImpl::ControlMessage); } - nsCOMPtr autoFocusTask = new AutoFocusTask(this, cancel, onSuccess, onError); - return mCameraThread->Dispatch(autoFocusTask, NS_DISPATCH_NORMAL); -} - -nsresult -CameraControlImpl::TakePicture(const CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) -{ - MOZ_ASSERT(NS_IsMainThread()); - bool cancel = false; - - nsCOMPtr cb = mTakePictureOnSuccessCb.get(); - if (cb) { - /** - * We already have a callback, so someone has already - * called takePicture() -- cancel it. - */ - mTakePictureOnSuccessCb = nullptr; - mTakePictureOnErrorCb = nullptr; - cancel = true; + virtual ~ControlMessage() + { + MOZ_COUNT_DTOR(CameraControlImpl::ControlMessage); } - nsCOMPtr takePictureTask = new TakePictureTask(this, cancel, aSize, aRotation, aFileFormat, aPosition, aDateTime, onSuccess, onError); - return mCameraThread->Dispatch(takePictureTask, NS_DISPATCH_NORMAL); + virtual nsresult RunImpl() = 0; + + NS_IMETHOD + Run() MOZ_OVERRIDE + { + MOZ_ASSERT(mCameraControl); + MOZ_ASSERT(NS_GetCurrentThread() == mCameraControl->mCameraThread); + + nsresult rv = RunImpl(); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGW("Camera control API failed at %d with 0x%x\n", mContext, rv); + // XXXmikeh - do we want to report a more specific error code? + mCameraControl->OnError(mContext, CameraControlListener::kErrorApiFailed); + } + + return NS_OK; + } + +protected: + nsRefPtr mCameraControl; + CameraControlListener::CameraErrorContext mContext; +}; + +nsresult +CameraControlImpl::SetConfiguration(const Configuration& aConfig) +{ + class Message : public ControlMessage + { + public: + Message(CameraControlImpl* aCameraControl, + CameraControlListener::CameraErrorContext aContext, + const Configuration& aConfig) + : ControlMessage(aCameraControl, aContext) + , mConfig(aConfig) + { } + + nsresult + RunImpl() MOZ_OVERRIDE + { + return mCameraControl->SetConfigurationImpl(mConfig); + } + + protected: + Configuration mConfig; + }; + + return mCameraThread->Dispatch( + new Message(this, CameraControlListener::kInSetConfiguration, aConfig), NS_DISPATCH_NORMAL); } nsresult -CameraControlImpl::StartRecording(CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor* aFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) +CameraControlImpl::AutoFocus(bool aCancelExistingCall) { - nsCOMPtr startRecordingTask = new StartRecordingTask(this, *aOptions, aFileDescriptor, onSuccess, onError, mWindowId); - return mCameraThread->Dispatch(startRecordingTask, NS_DISPATCH_NORMAL); + class Message : public ControlMessage + { + public: + Message(CameraControlImpl* aCameraControl, + CameraControlListener::CameraErrorContext aContext, + bool aCancelExistingCall) + : ControlMessage(aCameraControl, aContext) + , mCancelExistingCall(aCancelExistingCall) + { } + + nsresult + RunImpl() MOZ_OVERRIDE + { + return mCameraControl->AutoFocusImpl(mCancelExistingCall); + } + + protected: + bool mCancelExistingCall; + }; + + return mCameraThread->Dispatch( + new Message(this, CameraControlListener::kInAutoFocus, aCancelExistingCall), NS_DISPATCH_NORMAL); +} + +nsresult +CameraControlImpl::TakePicture() +{ + class Message : public ControlMessage + { + public: + Message(CameraControlImpl* aCameraControl, + CameraControlListener::CameraErrorContext aContext) + : ControlMessage(aCameraControl, aContext) + { } + + nsresult + RunImpl() MOZ_OVERRIDE + { + return mCameraControl->TakePictureImpl(); + } + }; + + return mCameraThread->Dispatch( + new Message(this, CameraControlListener::kInTakePicture), NS_DISPATCH_NORMAL); +} + +nsresult +CameraControlImpl::StartRecording(DeviceStorageFileDescriptor* aFileDescriptor, + const StartRecordingOptions* aOptions) +{ + class Message : public ControlMessage + { + public: + Message(CameraControlImpl* aCameraControl, + CameraControlListener::CameraErrorContext aContext, + const StartRecordingOptions* aOptions, + DeviceStorageFileDescriptor* aFileDescriptor) + : ControlMessage(aCameraControl, aContext) + , mOptionsPassed(false) + , mFileDescriptor(aFileDescriptor) + { + if (aOptions) { + mOptions = *aOptions; + mOptionsPassed = true; + } + } + + nsresult + RunImpl() MOZ_OVERRIDE + { + return mCameraControl->StartRecordingImpl(mFileDescriptor, + mOptionsPassed ? &mOptions : nullptr); + } + + protected: + StartRecordingOptions mOptions; + bool mOptionsPassed; + nsRefPtr mFileDescriptor; + }; + + + return mCameraThread->Dispatch(new Message(this, CameraControlListener::kInStartRecording, + aOptions, aFileDescriptor), NS_DISPATCH_NORMAL); } nsresult CameraControlImpl::StopRecording() { - nsCOMPtr stopRecordingTask = new StopRecordingTask(this); - return mCameraThread->Dispatch(stopRecordingTask, NS_DISPATCH_NORMAL); + class Message : public ControlMessage + { + public: + Message(CameraControlImpl* aCameraControl, + CameraControlListener::CameraErrorContext aContext) + : ControlMessage(aCameraControl, aContext) + { } + + nsresult + RunImpl() MOZ_OVERRIDE + { + return mCameraControl->StopRecordingImpl(); + } + }; + + return mCameraThread->Dispatch( + new Message(this, CameraControlListener::kInStopRecording), NS_DISPATCH_NORMAL); } nsresult -CameraControlImpl::StartPreview(DOMCameraPreview* aDOMPreview) +CameraControlImpl::StartPreview() { - nsCOMPtr startPreviewTask = new StartPreviewTask(this, aDOMPreview); - return mCameraThread->Dispatch(startPreviewTask, NS_DISPATCH_NORMAL); + class Message : public ControlMessage + { + public: + Message(CameraControlImpl* aCameraControl, + CameraControlListener::CameraErrorContext aContext) + : ControlMessage(aCameraControl, aContext) + { } + + nsresult + RunImpl() MOZ_OVERRIDE + { + return mCameraControl->StartPreviewImpl(); + } + }; + + return mCameraThread->Dispatch( + new Message(this, CameraControlListener::kInStartPreview), NS_DISPATCH_NORMAL); +} + +nsresult +CameraControlImpl::StopPreview() +{ + class Message : public ControlMessage + { + public: + Message(CameraControlImpl* aCameraControl, + CameraControlListener::CameraErrorContext aContext) + : ControlMessage(aCameraControl, aContext) + { } + + nsresult + RunImpl() MOZ_OVERRIDE + { + return mCameraControl->StopPreviewImpl(); + } + }; + + return mCameraThread->Dispatch( + new Message(this, CameraControlListener::kInStopPreview), NS_DISPATCH_NORMAL); +} + +nsresult +CameraControlImpl::ReleaseHardware() +{ + class Message : public ControlMessage + { + public: + Message(CameraControlImpl* aCameraControl, + CameraControlListener::CameraErrorContext aContext) + : ControlMessage(aCameraControl, aContext) + { } + + nsresult + RunImpl() MOZ_OVERRIDE + { + return mCameraControl->ReleaseHardwareImpl(); + } + }; + + return mCameraThread->Dispatch( + new Message(this, CameraControlListener::kInReleaseHardware), NS_DISPATCH_NORMAL); +} + +class CameraControlImpl::ListenerMessage : public CameraControlImpl::ControlMessage +{ +public: + ListenerMessage(CameraControlImpl* aCameraControl, + CameraControlListener* aListener) + : ControlMessage(aCameraControl, CameraControlListener::kInUnspecified) + , mListener(aListener) + { } + +protected: + nsRefPtr mListener; +}; + +void +CameraControlImpl::AddListenerImpl(already_AddRefed aListener) +{ + RwLockAutoEnterWrite lock(mListenerLock); + + CameraControlListener* l = *mListeners.AppendElement() = aListener; + + // Update the newly-added listener's state + l->OnConfigurationChange(mCurrentConfiguration); + l->OnHardwareStateChange(mHardwareState); + l->OnPreviewStateChange(mPreviewState); } void -CameraControlImpl::StopPreview() -{ - nsCOMPtr stopPreviewTask = new StopPreviewTask(this); - mCameraThread->Dispatch(stopPreviewTask, NS_DISPATCH_NORMAL); +CameraControlImpl::AddListener(CameraControlListener* aListener) + { + class Message : public ListenerMessage + { + public: + Message(CameraControlImpl* aCameraControl, + CameraControlListener* aListener) + : ListenerMessage(aCameraControl, aListener) + { } + + nsresult + RunImpl() MOZ_OVERRIDE + { + mCameraControl->AddListenerImpl(mListener.forget()); + return NS_OK; + } + }; + + mCameraThread->Dispatch(new Message(this, aListener), NS_DISPATCH_NORMAL); } -nsresult -CameraControlImpl::GetPreviewStreamVideoMode(CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) +void +CameraControlImpl::RemoveListenerImpl(CameraControlListener* aListener) { - nsCOMPtr getPreviewStreamVideoModeTask = new GetPreviewStreamVideoModeTask(this, *aOptions, onSuccess, onError); - return mCameraThread->Dispatch(getPreviewStreamVideoModeTask, NS_DISPATCH_NORMAL); + RwLockAutoEnterWrite lock(mListenerLock); + + nsRefPtr l(aListener); + mListeners.RemoveElement(l); + // XXXmikeh - do we want to notify the listener that it has been removed? } -nsresult -CameraControlImpl::ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError) -{ - nsCOMPtr releaseHardwareTask = new ReleaseHardwareTask(this, onSuccess, onError); - return mCameraThread->Dispatch(releaseHardwareTask, NS_DISPATCH_NORMAL); -} - -bool -CameraControlImpl::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder) -{ - if (!mDOMPreview) { - return false; - } - - return mDOMPreview->ReceiveFrame(aBuffer, aFormat, aBuilder); -} - -NS_IMETHODIMP -GetPreviewStreamResult::Run() -{ - /** - * The camera preview stream object is DOM-facing, and as such - * must be a cycle-collection participant created on the main - * thread. - */ - MOZ_ASSERT(NS_IsMainThread()); - - nsCOMPtr onSuccess = mOnSuccessCb.get(); - nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId); - if (onSuccess && nsDOMCameraManager::IsWindowStillActive(mWindowId) && window) { - nsCOMPtr stream = - new DOMCameraPreview(window, mCameraControl, mWidth, mHeight, - mFramesPerSecond); - onSuccess->HandleEvent(stream); - } - return NS_OK; +void +CameraControlImpl::RemoveListener(CameraControlListener* aListener) + { + class Message : public ListenerMessage + { + public: + Message(CameraControlImpl* aCameraControl, CameraControlListener* aListener) + : ListenerMessage(aCameraControl, aListener) + { } + + nsresult + RunImpl() MOZ_OVERRIDE + { + mCameraControl->RemoveListenerImpl(mListener); + return NS_OK; + } + }; + + mCameraThread->Dispatch(new Message(this, aListener), NS_DISPATCH_NORMAL); } diff --git a/dom/camera/CameraControlImpl.h b/dom/camera/CameraControlImpl.h index c12ffd1ffb2c..cb0a8ea57545 100644 --- a/dom/camera/CameraControlImpl.h +++ b/dom/camera/CameraControlImpl.h @@ -5,746 +5,119 @@ #ifndef DOM_CAMERA_CAMERACONTROLIMPL_H #define DOM_CAMERA_CAMERACONTROLIMPL_H +#include "nsTArray.h" +#include "nsWeakPtr.h" #include "mozilla/Attributes.h" -#include "nsDOMFile.h" +#include "mozilla/ReentrantMonitor.h" +#include "nsIFile.h" #include "nsProxyRelease.h" -#include "DictionaryHelpers.h" +#include "AutoRwLock.h" #include "nsIDOMDeviceStorage.h" -#include "DOMCameraManager.h" -#include "DOMCameraPreview.h" #include "ICameraControl.h" #include "CameraCommon.h" #include "DeviceStorage.h" #include "DeviceStorageFileDescriptor.h" +#include "CameraControlListener.h" class DeviceStorageFileDescriptor; namespace mozilla { -class GetPreviewStreamTask; -class StartPreviewTask; -class StopPreviewTask; -class AutoFocusTask; -class TakePictureTask; -class StartRecordingTask; -class StopRecordingTask; -class SetParameterTask; -class GetParameterTask; -class GetPreviewStreamVideoModeTask; -class ReleaseHardwareTask; +namespace layers { + class Image; +} -class DOMCameraPreview; class RecorderProfileManager; class CameraControlImpl : public ICameraControl { - friend class GetPreviewStreamTask; - friend class StartPreviewTask; - friend class StopPreviewTask; - friend class AutoFocusTask; - friend class TakePictureTask; - friend class StartRecordingTask; - friend class StopRecordingTask; - friend class SetParameterTask; - friend class GetParameterTask; - friend class GetPreviewStreamVideoModeTask; - friend class ReleaseHardwareTask; - public: - CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId); + CameraControlImpl(uint32_t aCameraId); + virtual void AddListener(CameraControlListener* aListener) MOZ_OVERRIDE; + virtual void RemoveListener(CameraControlListener* aListener) MOZ_OVERRIDE; - nsresult GetPreviewStream(idl::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError); - nsresult StartPreview(DOMCameraPreview* aDOMPreview); - void StopPreview(); - nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError); - nsresult TakePicture(const idl::CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, idl::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError); - nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor *aDSFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError); - nsresult StopRecording(); - nsresult GetPreviewStreamVideoMode(idl::CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError); - nsresult ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError); - - nsresult Set(uint32_t aKey, const nsAString& aValue); - nsresult Get(uint32_t aKey, nsAString& aValue); - nsresult Set(uint32_t aKey, double aValue); - nsresult Get(uint32_t aKey, double* aValue); - nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit); - nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue); - nsresult Set(nsICameraShutterCallback* aOnShutter); - nsresult Get(nsICameraShutterCallback** aOnShutter); - nsresult Set(nsICameraClosedCallback* aOnClosed); - nsresult Get(nsICameraClosedCallback** aOnClosed); - nsresult Set(nsICameraRecorderStateChange* aOnRecorderStateChange); - nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange); - nsresult Set(nsICameraPreviewStateChange* aOnPreviewStateChange); - nsresult Get(nsICameraPreviewStateChange** aOnPreviewStateChange); - nsresult Set(uint32_t aKey, const idl::CameraSize& aSize); - nsresult Get(uint32_t aKey, idl::CameraSize& aSize); - nsresult Get(uint32_t aKey, int32_t* aValue); - - nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue) - { - return Set(aCx, CAMERA_PARAM_FOCUSAREAS, aValue, mMaxFocusAreas); - } - - nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue) - { - return Set(aCx, CAMERA_PARAM_METERINGAREAS, aValue, mMaxMeteringAreas); - } + virtual nsresult SetConfiguration(const Configuration& aConfig) MOZ_OVERRIDE; + virtual nsresult StartPreview() MOZ_OVERRIDE; + virtual nsresult StopPreview() MOZ_OVERRIDE; + virtual nsresult AutoFocus(bool aCancelExistingCall) MOZ_OVERRIDE; + virtual nsresult TakePicture() MOZ_OVERRIDE; + virtual nsresult StartRecording(DeviceStorageFileDescriptor* aFileDescriptor, + const StartRecordingOptions* aOptions) MOZ_OVERRIDE; + virtual nsresult StopRecording() MOZ_OVERRIDE; + virtual nsresult ReleaseHardware() MOZ_OVERRIDE; already_AddRefed GetRecorderProfileManager(); uint32_t GetCameraId() { return mCameraId; } - virtual const char* GetParameter(const char* aKey) = 0; - virtual const char* GetParameterConstChar(uint32_t aKey) = 0; - virtual double GetParameterDouble(uint32_t aKey) = 0; - virtual int32_t GetParameterInt32(uint32_t aKey) = 0; - virtual void GetParameter(uint32_t aKey, nsTArray& aRegions) = 0; - virtual void GetParameter(uint32_t aKey, idl::CameraSize& aSize) = 0; - virtual void SetParameter(const char* aKey, const char* aValue) = 0; - virtual void SetParameter(uint32_t aKey, const char* aValue) = 0; - virtual void SetParameter(uint32_t aKey, double aValue) = 0; - virtual void SetParameter(uint32_t aKey, const nsTArray& aRegions) = 0; - virtual void SetParameter(uint32_t aKey, const idl::CameraSize& aSize) = 0; - virtual nsresult GetVideoSizes(nsTArray& aVideoSizes) = 0; - virtual nsresult PushParameters() = 0; - virtual void Shutdown(); + virtual void Shutdown() MOZ_OVERRIDE; - bool ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder); void OnShutter(); void OnClosed(); - void OnRecorderStateChange(const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber); - - enum PreviewState { - PREVIEW_STOPPED, - PREVIEW_STARTED - }; - void OnPreviewStateChange(PreviewState aNewState); - - uint64_t GetWindowId() - { - return mWindowId; - } + void OnError(CameraControlListener::CameraErrorContext aContext, + CameraControlListener::CameraError aError); protected: + // Event handlers. + void OnAutoFocusComplete(bool aAutoFocusSucceeded); + void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType); + + bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight); + void OnRecorderStateChange(CameraControlListener::RecorderState aState, + int32_t aStatus, int32_t aTrackNumber); + void OnPreviewStateChange(CameraControlListener::PreviewState aState); + void OnHardwareStateChange(CameraControlListener::HardwareState aState); + void OnConfigurationChange(); + + // When we create a new CameraThread, we keep a static reference to it so + // that multiple CameraControl instances can find and reuse it; but we + // don't want that reference to keep the thread object around unnecessarily, + // so we make it a weak reference. The strong dynamic references will keep + // the thread object alive as needed. + static nsWeakPtr sCameraThread; + nsCOMPtr mCameraThread; + virtual ~CameraControlImpl(); - virtual nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream) = 0; - virtual nsresult StartPreviewImpl(StartPreviewTask* aStartPreview) = 0; - virtual nsresult StopPreviewImpl(StopPreviewTask* aStopPreview) = 0; - virtual nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus) = 0; - virtual nsresult TakePictureImpl(TakePictureTask* aTakePicture) = 0; - virtual nsresult StartRecordingImpl(StartRecordingTask* aStartRecording) = 0; - virtual nsresult StopRecordingImpl(StopRecordingTask* aStopRecording) = 0; + virtual void BeginBatchParameterSet() MOZ_OVERRIDE { } + virtual void EndBatchParameterSet() MOZ_OVERRIDE { } + + // Manage camera event listeners. + void AddListenerImpl(already_AddRefed aListener); + void RemoveListenerImpl(CameraControlListener* aListener); + nsTArray > mListeners; + PRRWLock* mListenerLock; + + class ControlMessage; + class ListenerMessage; + + virtual nsresult SetConfigurationImpl(const Configuration& aConfig) = 0; + virtual nsresult StartPreviewImpl() = 0; + virtual nsresult StopPreviewImpl() = 0; + virtual nsresult AutoFocusImpl(bool aCancelExistingCall) = 0; + virtual nsresult TakePictureImpl() = 0; + virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor, + const StartRecordingOptions* aOptions) = 0; + virtual nsresult StopRecordingImpl() = 0; virtual nsresult PushParametersImpl() = 0; virtual nsresult PullParametersImpl() = 0; - virtual nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode) = 0; - virtual nsresult ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware) = 0; + virtual nsresult ReleaseHardwareImpl() = 0; virtual already_AddRefed GetRecorderProfileManagerImpl() = 0; void OnShutterInternal(); void OnClosedInternal(); - uint32_t mCameraId; - nsCOMPtr mCameraThread; - uint64_t mWindowId; - nsString mFileFormat; - uint32_t mMaxMeteringAreas; - uint32_t mMaxFocusAreas; - PreviewState mPreviewState; + uint32_t mCameraId; - /** - * 'mDOMPreview' is a raw pointer to the object that will receive incoming - * preview frames. This is guaranteed to be valid, or null. - * - * It is set by a call to StartPreview(), and set to null on StopPreview(). - * It is up to the caller to ensure that the object will not disappear - * out from under this pointer--usually by calling NS_ADDREF(). - */ - DOMCameraPreview* mDOMPreview; + CameraControlListener::CameraListenerConfiguration mCurrentConfiguration; - nsMainThreadPtrHandle mAutoFocusOnSuccessCb; - nsMainThreadPtrHandle mAutoFocusOnErrorCb; - nsMainThreadPtrHandle mTakePictureOnSuccessCb; - nsMainThreadPtrHandle mTakePictureOnErrorCb; - nsMainThreadPtrHandle mOnShutterCb; - nsMainThreadPtrHandle mOnClosedCb; - nsMainThreadPtrHandle mOnRecorderStateChangeCb; - nsMainThreadPtrHandle mOnPreviewStateChangeCb; + CameraControlListener::PreviewState mPreviewState; + CameraControlListener::HardwareState mHardwareState; private: CameraControlImpl(const CameraControlImpl&) MOZ_DELETE; CameraControlImpl& operator=(const CameraControlImpl&) MOZ_DELETE; }; -// Error result runnable -class CameraErrorResult : public nsRunnable -{ -public: - CameraErrorResult(nsMainThreadPtrHandle onError, const nsString& aErrorMsg, uint64_t aWindowId) - : mOnErrorCb(onError) - , mErrorMsg(aErrorMsg) - , mWindowId(aWindowId) - { } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - MOZ_ASSERT(NS_IsMainThread()); - - if (mOnErrorCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { - mOnErrorCb->HandleEvent(mErrorMsg); - } - return NS_OK; - } - -protected: - nsMainThreadPtrHandle mOnErrorCb; - const nsString mErrorMsg; - uint64_t mWindowId; -}; - -// Return the resulting preview stream to JS. Runs on the main thread. -class GetPreviewStreamResult : public nsRunnable -{ -public: - GetPreviewStreamResult(CameraControlImpl* aCameraControl, uint32_t aWidth, uint32_t aHeight, uint32_t aFramesPerSecond, nsMainThreadPtrHandle& onSuccess, uint64_t aWindowId) - : mCameraControl(aCameraControl) - , mWidth(aWidth) - , mHeight(aHeight) - , mFramesPerSecond(aFramesPerSecond) - , mOnSuccessCb(onSuccess) - , mWindowId(aWindowId) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - virtual ~GetPreviewStreamResult() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - // Run() method is implementation specific. - NS_IMETHOD Run() MOZ_OVERRIDE; - -protected: - nsRefPtr mCameraControl; - uint32_t mWidth; - uint32_t mHeight; - uint32_t mFramesPerSecond; - nsMainThreadPtrHandle mOnSuccessCb; - uint64_t mWindowId; -}; - -// Get the desired preview stream. -class GetPreviewStreamTask : public nsRunnable -{ -public: - GetPreviewStreamTask(CameraControlImpl* aCameraControl, idl::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) - : mSize(aSize) - , mCameraControl(aCameraControl) - , mOnSuccessCb(new nsMainThreadPtrHolder(onSuccess)) - , mOnErrorCb(new nsMainThreadPtrHolder(onError)) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - virtual ~GetPreviewStreamTask() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - nsresult rv = mCameraControl->GetPreviewStreamImpl(this); - - if (NS_FAILED(rv)) { - nsCOMPtr cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()); - rv = NS_DispatchToMainThread(cameraErrorResult); - NS_ENSURE_SUCCESS(rv, rv); - } - return rv; - } - - idl::CameraSize mSize; - nsRefPtr mCameraControl; - nsMainThreadPtrHandle mOnSuccessCb; - nsMainThreadPtrHandle mOnErrorCb; -}; - -// Return the autofocus status to JS. Runs on the main thread. -class AutoFocusResult : public nsRunnable -{ -public: - AutoFocusResult(bool aSuccess, nsMainThreadPtrHandle onSuccess, uint64_t aWindowId) - : mSuccess(aSuccess) - , mOnSuccessCb(onSuccess) - , mWindowId(aWindowId) - { } - - virtual ~AutoFocusResult() { } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - MOZ_ASSERT(NS_IsMainThread()); - - if (mOnSuccessCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { - mOnSuccessCb->HandleEvent(mSuccess); - } - return NS_OK; - } - -protected: - bool mSuccess; - nsMainThreadPtrHandle mOnSuccessCb; - uint64_t mWindowId; -}; - -// Autofocus the camera. -class AutoFocusTask : public nsRunnable -{ -public: - AutoFocusTask(CameraControlImpl* aCameraControl, bool aCancel, nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError) - : mCameraControl(aCameraControl) - , mCancel(aCancel) - , mOnSuccessCb(new nsMainThreadPtrHolder(onSuccess)) - , mOnErrorCb(new nsMainThreadPtrHolder(onError)) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - virtual ~AutoFocusTask() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - nsresult rv = mCameraControl->AutoFocusImpl(this); - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - - if (NS_FAILED(rv)) { - nsCOMPtr cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()); - rv = NS_DispatchToMainThread(cameraErrorResult); - NS_ENSURE_SUCCESS(rv, rv); - } - return rv; - } - - nsRefPtr mCameraControl; - bool mCancel; - nsMainThreadPtrHandle mOnSuccessCb; - nsMainThreadPtrHandle mOnErrorCb; -}; - -// Return the captured picture to JS. Runs on the main thread. -class TakePictureResult : public nsRunnable -{ -public: - TakePictureResult(uint8_t* aData, uint64_t aLength, const nsAString& aMimeType, nsMainThreadPtrHandle onSuccess, uint64_t aWindowId) - : mData(aData) - , mLength(aLength) - , mMimeType(aMimeType) - , mOnSuccessCb(onSuccess) - , mWindowId(aWindowId) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - virtual ~TakePictureResult() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - MOZ_ASSERT(NS_IsMainThread()); - - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - if (mOnSuccessCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { - nsCOMPtr image = new nsDOMMemoryFile(static_cast(mData), static_cast(mLength), mMimeType); - mOnSuccessCb->HandleEvent(image); - } else { - delete[] mData; - } - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - return NS_OK; - } - -protected: - uint8_t* mData; - uint64_t mLength; - nsString mMimeType; - nsMainThreadPtrHandle mOnSuccessCb; - uint64_t mWindowId; -}; - -// Capture a still image with the camera. -class TakePictureTask : public nsRunnable -{ -public: - TakePictureTask(CameraControlImpl* aCameraControl, bool aCancel, const idl::CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, idl::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) - : mCameraControl(aCameraControl) - , mCancel(aCancel) - , mSize(aSize) - , mRotation(aRotation) - , mFileFormat(aFileFormat) - , mPosition(aPosition) - , mDateTime(aDateTime) - , mOnSuccessCb(new nsMainThreadPtrHolder(onSuccess)) - , mOnErrorCb(new nsMainThreadPtrHolder(onError)) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - virtual ~TakePictureTask() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - nsresult rv = mCameraControl->TakePictureImpl(this); - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - - if (NS_FAILED(rv)) { - nsCOMPtr cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()); - rv = NS_DispatchToMainThread(cameraErrorResult); - NS_ENSURE_SUCCESS(rv, rv); - } - return rv; - } - - nsRefPtr mCameraControl; - bool mCancel; - idl::CameraSize mSize; - int32_t mRotation; - nsString mFileFormat; - idl::CameraPosition mPosition; - uint64_t mDateTime; - nsMainThreadPtrHandle mOnSuccessCb; - nsMainThreadPtrHandle mOnErrorCb; -}; - -// Return the result of starting recording. Runs on the main thread. -class StartRecordingResult : public nsRunnable -{ -public: - StartRecordingResult(nsMainThreadPtrHandle onSuccess, uint64_t aWindowId) - : mOnSuccessCb(onSuccess) - , mWindowId(aWindowId) - { } - - virtual ~StartRecordingResult() { } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - MOZ_ASSERT(NS_IsMainThread()); - - if (mOnSuccessCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { - mOnSuccessCb->HandleEvent(); - } - return NS_OK; - } - -protected: - nsMainThreadPtrHandle mOnSuccessCb; - uint64_t mWindowId; -}; - -// Start video recording. -class StartRecordingTask : public nsRunnable -{ -public: - StartRecordingTask(CameraControlImpl* aCameraControl, - idl::CameraStartRecordingOptions aOptions, - DeviceStorageFileDescriptor *aDSFileDescriptor, - nsICameraStartRecordingCallback* onSuccess, - nsICameraErrorCallback* onError, - uint64_t aWindowId) - : mCameraControl(aCameraControl) - , mOptions(aOptions) - , mDSFileDescriptor(aDSFileDescriptor) - , mOnSuccessCb(new nsMainThreadPtrHolder(onSuccess)) - , mOnErrorCb(new nsMainThreadPtrHolder(onError)) - , mWindowId(aWindowId) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - virtual ~StartRecordingTask() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - nsresult rv = mCameraControl->StartRecordingImpl(this); - DOM_CAMERA_LOGT("%s:%d : result %d\n", __func__, __LINE__, rv); - - // dispatch the callback - nsCOMPtr startRecordingResult; - if (NS_SUCCEEDED(rv)) { - startRecordingResult = new StartRecordingResult(mOnSuccessCb, mWindowId); - } else { - startRecordingResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mWindowId); - } - rv = NS_DispatchToMainThread(startRecordingResult); - if (NS_FAILED(rv)) { - DOM_CAMERA_LOGE("Failed to dispatch start recording result to main thread (%d)!", rv); - } - return rv; - } - - nsRefPtr mCameraControl; - idl::CameraStartRecordingOptions mOptions; - nsRefPtr mDSFileDescriptor; - nsMainThreadPtrHandle mOnSuccessCb; - nsMainThreadPtrHandle mOnErrorCb; - uint64_t mWindowId; -}; - -// Stop video recording. -class StopRecordingTask : public nsRunnable -{ -public: - StopRecordingTask(CameraControlImpl* aCameraControl) - : mCameraControl(aCameraControl) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - virtual ~StopRecordingTask() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - nsresult rv = mCameraControl->StopRecordingImpl(this); - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; - } - - nsRefPtr mCameraControl; -}; - -// Start the preview. -class StartPreviewTask : public nsRunnable -{ -public: - StartPreviewTask(CameraControlImpl* aCameraControl, DOMCameraPreview* aDOMPreview) - : mCameraControl(aCameraControl) - , mDOMPreview(aDOMPreview) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - virtual ~StartPreviewTask() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - nsresult rv = mCameraControl->StartPreviewImpl(this); - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; - } - - nsRefPtr mCameraControl; - DOMCameraPreview* mDOMPreview; // DOMCameraPreview NS_ADDREFs itself for us -}; - -// Stop the preview. -class StopPreviewTask : public nsRunnable -{ -public: - StopPreviewTask(CameraControlImpl* aCameraControl) - : mCameraControl(aCameraControl) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - virtual ~StopPreviewTask() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - mCameraControl->StopPreviewImpl(this); - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - - return NS_OK; - } - - nsRefPtr mCameraControl; -}; - -// Get the video mode preview stream. -class GetPreviewStreamVideoModeTask : public nsRunnable -{ -public: - GetPreviewStreamVideoModeTask(CameraControlImpl* aCameraControl, idl::CameraRecorderOptions aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) - : mCameraControl(aCameraControl) - , mOptions(aOptions) - , mOnSuccessCb(new nsMainThreadPtrHolder(onSuccess)) - , mOnErrorCb(new nsMainThreadPtrHolder(onError)) - { } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - DOM_CAMERA_LOGI("%s:%d -- BEFORE IMPL\n", __func__, __LINE__); - nsresult rv = mCameraControl->GetPreviewStreamVideoModeImpl(this); - DOM_CAMERA_LOGI("%s:%d -- AFTER IMPL : rv = %d\n", __func__, __LINE__, rv); - - if (NS_FAILED(rv)) { - nsCOMPtr cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()); - rv = NS_DispatchToMainThread(cameraErrorResult); - NS_ENSURE_SUCCESS(rv, rv); - } - return NS_OK; - } - - nsRefPtr mCameraControl; - idl::CameraRecorderOptions mOptions; - nsMainThreadPtrHandle mOnSuccessCb; - nsMainThreadPtrHandle mOnErrorCb; -}; - -// Return the result of releasing the camera hardware. Runs on the main thread. -class ReleaseHardwareResult : public nsRunnable -{ -public: - ReleaseHardwareResult(nsMainThreadPtrHandle onSuccess, uint64_t aWindowId) - : mOnSuccessCb(onSuccess) - , mWindowId(aWindowId) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - virtual ~ReleaseHardwareResult() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - MOZ_ASSERT(NS_IsMainThread()); - - if (mOnSuccessCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { - mOnSuccessCb->HandleEvent(); - } - return NS_OK; - } - -protected: - nsMainThreadPtrHandle mOnSuccessCb; - uint64_t mWindowId; -}; - -// Release the camera hardware. -class ReleaseHardwareTask : public nsRunnable -{ -public: - ReleaseHardwareTask(CameraControlImpl* aCameraControl, nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError) - : mCameraControl(aCameraControl) - , mOnSuccessCb(new nsMainThreadPtrHolder(onSuccess)) - , mOnErrorCb(new nsMainThreadPtrHolder(onError)) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - virtual ~ReleaseHardwareTask() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - nsresult rv = mCameraControl->ReleaseHardwareImpl(this); - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - - if (NS_FAILED(rv)) { - nsCOMPtr cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId()); - rv = NS_DispatchToMainThread(cameraErrorResult); - NS_ENSURE_SUCCESS(rv, rv); - } - return rv; - } - - nsRefPtr mCameraControl; - nsMainThreadPtrHandle mOnSuccessCb; - nsMainThreadPtrHandle mOnErrorCb; -}; - -// Report that the video recorder state has changed. -class CameraRecorderStateChange : public nsRunnable -{ -public: - CameraRecorderStateChange(nsMainThreadPtrHandle onStateChange, const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber, uint64_t aWindowId) - : mOnStateChangeCb(onStateChange) - , mStateMsg(aStateMsg) - , mStatus(aStatus) - , mTrackNumber(aTrackNumber) - , mWindowId(aWindowId) - { } - - NS_IMETHOD Run() MOZ_OVERRIDE - { - MOZ_ASSERT(NS_IsMainThread()); - - if (mOnStateChangeCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { - // For now, just pass the state message and swallow mStatus and mTrackNumber - mOnStateChangeCb->HandleStateChange(mStateMsg); - } - return NS_OK; - } - -protected: - nsMainThreadPtrHandle mOnStateChangeCb; - const nsString mStateMsg; - int32_t mStatus; - int32_t mTrackNumber; - uint64_t mWindowId; -}; - -// Report that the preview stream state has changed. -class CameraPreviewStateChange : public nsRunnable -{ -public: - CameraPreviewStateChange(nsMainThreadPtrHandle onStateChange, const nsString& aStateMsg, uint64_t aWindowId) - : mOnStateChangeCb(onStateChange) - , mStateMsg(aStateMsg) - , mWindowId(aWindowId) - { } - - NS_IMETHOD Run() - { - MOZ_ASSERT(NS_IsMainThread()); - - if (mOnStateChangeCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { - mOnStateChangeCb->HandleStateChange(mStateMsg); - } - return NS_OK; - } - -protected: - nsMainThreadPtrHandle mOnStateChangeCb; - const nsString mStateMsg; - uint64_t mWindowId; -}; - } // namespace mozilla #endif // DOM_CAMERA_CAMERACONTROLIMPL_H diff --git a/dom/camera/CameraControlListener.h b/dom/camera/CameraControlListener.h new file mode 100644 index 000000000000..76065ddb3003 --- /dev/null +++ b/dom/camera/CameraControlListener.h @@ -0,0 +1,100 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DOM_CAMERA_CAMERACONTROLLISTENER_H +#define DOM_CAMERA_CAMERACONTROLLISTENER_H + +#include +#include "ICameraControl.h" + +namespace mozilla { + +namespace layers { + class Image; +} + +class CameraControlListener +{ +public: + virtual ~CameraControlListener() { } + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CameraControlListener); + + enum HardwareState + { + kHardwareOpen, + kHardwareClosed + }; + virtual void OnHardwareStateChange(HardwareState aState) { } + + enum PreviewState + { + kPreviewStopped, + kPreviewPaused, + kPreviewStarted + }; + virtual void OnPreviewStateChange(PreviewState aState) { } + + enum RecorderState + { + kRecorderStopped, + kRecorderStarted, +#ifdef MOZ_B2G_CAMERA + kFileSizeLimitReached, + kVideoLengthLimitReached, + kTrackCompleted, + kTrackFailed, + kMediaRecorderFailed, + kMediaServerFailed +#endif + }; + enum { kNoTrackNumber = -1 }; + virtual void OnRecorderStateChange(RecorderState aState, int32_t aStatus, int32_t aTrackNum) { } + + virtual void OnShutter() { } + virtual bool OnNewPreviewFrame(layers::Image* aFrame, uint32_t aWidth, uint32_t aHeight) + { + return false; + } + + class CameraListenerConfiguration : public ICameraControl::Configuration + { + public: + uint32_t mMaxMeteringAreas; + uint32_t mMaxFocusAreas; + }; + virtual void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration) { } + + virtual void OnAutoFocusComplete(bool aAutoFocusSucceeded) { } + virtual void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) { } + + enum CameraErrorContext + { + kInGetCamera, + kInAutoFocus, + kInTakePicture, + kInStartRecording, + kInStopRecording, + kInSetConfiguration, + kInReleaseHardware, + kInStartPreview, + kInStopPreview, + kInUnspecified + }; + enum CameraError + { + kErrorApiFailed, + kErrorInitFailed, + kErrorInvalidConfiguration, + kErrorServiceFailed, + kErrorSetPictureSizeFailed, + kErrorSetThumbnailSizeFailed, + kErrorUnknown + }; + virtual void OnError(CameraErrorContext aContext, CameraError aError) { } +}; + +} // namespace mozilla + +#endif // DOM_CAMERA_CAMERACONTROLLISTENER_H diff --git a/dom/camera/CameraRecorderProfiles.cpp b/dom/camera/CameraRecorderProfiles.cpp index 57f0fd000866..9c00b540cce6 100644 --- a/dom/camera/CameraRecorderProfiles.cpp +++ b/dom/camera/CameraRecorderProfiles.cpp @@ -2,8 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "jsapi.h" #include "CameraRecorderProfiles.h" +#include "jsapi.h" #include "CameraCommon.h" using namespace mozilla; diff --git a/dom/camera/CameraRecorderProfiles.h b/dom/camera/CameraRecorderProfiles.h index a6b448937fa8..67b9d1e2a6e6 100644 --- a/dom/camera/CameraRecorderProfiles.h +++ b/dom/camera/CameraRecorderProfiles.h @@ -10,10 +10,8 @@ #include "nsAutoPtr.h" #include "nsTArray.h" #include "jsapi.h" -#include "DictionaryHelpers.h" #include "CameraCommon.h" - namespace mozilla { class CameraControlImpl; diff --git a/dom/camera/DOMCameraCapabilities.cpp b/dom/camera/DOMCameraCapabilities.cpp index 34d2c4af7ffa..01c27e46d1be 100644 --- a/dom/camera/DOMCameraCapabilities.cpp +++ b/dom/camera/DOMCameraCapabilities.cpp @@ -1,424 +1,282 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include -#include -#include "base/basictypes.h" -#include "nsDOMClassInfo.h" -#include "jsapi.h" -#include "CameraRecorderProfiles.h" -#include "DOMCameraControl.h" #include "DOMCameraCapabilities.h" +#include "nsPIDOMWindow.h" +#include "nsContentUtils.h" +#include "mozilla/dom/CameraManagerBinding.h" +#include "mozilla/dom/CameraCapabilitiesBinding.h" #include "CameraCommon.h" +#include "ICameraControl.h" +#include "CameraRecorderProfiles.h" -using namespace mozilla; -using namespace mozilla::dom; +namespace mozilla { +namespace dom { -DOMCI_DATA(CameraCapabilities, nsICameraCapabilities) +NS_IMPL_CYCLE_COLLECTION_CLASS(CameraCapabilities) -NS_INTERFACE_MAP_BEGIN(DOMCameraCapabilities) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CameraCapabilities) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) + tmp->mRecorderProfiles = JS::UndefinedValue(); + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CameraCapabilities) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CameraCapabilities) + NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mRecorderProfiles) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraCapabilities) +NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraCapabilities) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraCapabilities) + NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) - NS_INTERFACE_MAP_ENTRY(nsICameraCapabilities) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CameraCapabilities) NS_INTERFACE_MAP_END -NS_IMPL_ADDREF(DOMCameraCapabilities) -NS_IMPL_RELEASE(DOMCameraCapabilities) - -static nsresult -ParseZoomRatioItemAndAdd(JSContext* aCx, JS::Handle aArray, - uint32_t aIndex, const char* aStart, char** aEnd) +CameraCapabilities::CameraCapabilities(nsPIDOMWindow* aWindow) + : mRecorderProfiles(JS::UndefinedValue()) + , mWindow(aWindow) { - if (!*aEnd) { - // make 'aEnd' follow the same semantics as strchr(). - aEnd = nullptr; - } - - /** - * The by-100 divisor is Gonk-specific. For now, assume other platforms - * return actual fractional multipliers. - */ - double d = strtod(aStart, aEnd); -#if MOZ_WIDGET_GONK - d /= 100; -#endif - - if (!JS_SetElement(aCx, aArray, aIndex, d)) { - return NS_ERROR_FAILURE; - } - - return NS_OK; + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + MOZ_COUNT_CTOR(CameraCapabilities); + mozilla::HoldJSObjects(this); + SetIsDOMBinding(); } -static nsresult -ParseStringItemAndAdd(JSContext* aCx, JS::Handle aArray, - uint32_t aIndex, const char* aStart, char** aEnd) +CameraCapabilities::~CameraCapabilities() { - JS::Rooted s(aCx); - - if (*aEnd) { - s = JS_NewStringCopyN(aCx, aStart, *aEnd - aStart); - } else { - s = JS_NewStringCopyZ(aCx, aStart); - } - if (!s) { - return NS_ERROR_OUT_OF_MEMORY; - } - - if (!JS_SetElement(aCx, aArray, aIndex, s)) { - return NS_ERROR_FAILURE; - } - - return NS_OK; + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + mRecorderProfiles = JS::UndefinedValue(); + mozilla::DropJSObjects(this); + MOZ_COUNT_DTOR(CameraCapabilities); } -static nsresult -ParseDimensionItemAndAdd(JSContext* aCx, JS::Handle aArray, - uint32_t aIndex, const char* aStart, char** aEnd) +JSObject* +CameraCapabilities::WrapObject(JSContext* aCx, JS::Handle aScope) { - char* x; - - if (!*aEnd) { - // make 'aEnd' follow the same semantics as strchr(). - aEnd = nullptr; - } - - JS::Rooted w(aCx, INT_TO_JSVAL(strtol(aStart, &x, 10))); - JS::Rooted h(aCx, INT_TO_JSVAL(strtol(x + 1, aEnd, 10))); - - JS::Rooted o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); - if (!o) { - return NS_ERROR_OUT_OF_MEMORY; - } - - if (!JS_SetProperty(aCx, o, "width", w)) { - return NS_ERROR_FAILURE; - } - if (!JS_SetProperty(aCx, o, "height", h)) { - return NS_ERROR_FAILURE; - } - - if (!JS_SetElement(aCx, aArray, aIndex, o)) { - return NS_ERROR_FAILURE; - } - - return NS_OK; + return CameraCapabilitiesBinding::Wrap(aCx, aScope, this); } +#define LOG_IF_ERROR(rv, param) \ + do { \ + if (NS_FAILED(rv)) { \ + DOM_CAMERA_LOGW("Error %x trying to get " #param "\n", \ + (rv)); \ + } \ + } while(0) + nsresult -DOMCameraCapabilities::ParameterListToNewArray(JSContext* aCx, - JS::MutableHandle aArray, - uint32_t aKey, - ParseItemAndAddFunc aParseItemAndAdd) +CameraCapabilities::TranslateToDictionary(ICameraControl* aCameraControl, + uint32_t aKey, nsTArray& aSizes) { - NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE); - - const char* value = mCamera->GetParameterConstChar(aKey); - if (!value) { - // in case we get nonsense data back - aArray.set(nullptr); - return NS_OK; - } - - aArray.set(JS_NewArrayObject(aCx, 0)); - if (!aArray) { - return NS_ERROR_OUT_OF_MEMORY; - } - - const char* p = value; - uint32_t index = 0; nsresult rv; - char* q; + nsTArray sizes; - while (p) { - /** - * In C's string.h, strchr() is declared as returning 'char*'; in C++'s - * cstring, it is declared as returning 'const char*', _except_ in MSVC, - * where the C version is declared to return const like the C++ version. - * - * Unfortunately, for both cases, strtod() and strtol() take a 'char**' as - * the end-of-conversion pointer, so we need to cast away strchr()'s - * const-ness here to make the MSVC build everything happy. - */ - q = const_cast(strchr(p, ',')); - if (q != p) { // skip consecutive delimiters, just in case - rv = aParseItemAndAdd(aCx, aArray, index, p, &q); - NS_ENSURE_SUCCESS(rv, rv); - ++index; - } - p = q; - if (p) { - ++p; - } - } - - return JS_FreezeObject(aCx, aArray) ? NS_OK : NS_ERROR_FAILURE; -} - -nsresult -DOMCameraCapabilities::StringListToNewObject(JSContext* aCx, - JS::MutableHandle aArray, - uint32_t aKey) -{ - JS::Rooted array(aCx); - - nsresult rv = ParameterListToNewArray(aCx, &array, aKey, ParseStringItemAndAdd); - NS_ENSURE_SUCCESS(rv, rv); - - aArray.setObjectOrNull(array); - return NS_OK; -} - -nsresult -DOMCameraCapabilities::DimensionListToNewObject(JSContext* aCx, - JS::MutableHandle aArray, - uint32_t aKey) -{ - JS::Rooted array(aCx); - - nsresult rv = ParameterListToNewArray(aCx, &array, aKey, ParseDimensionItemAndAdd); - NS_ENSURE_SUCCESS(rv, rv); - - aArray.setObjectOrNull(array); - return NS_OK; -} - -/* readonly attribute jsval previewSizes; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetPreviewSizes(JSContext* cx, - JS::MutableHandle aPreviewSizes) -{ - return DimensionListToNewObject(cx, aPreviewSizes, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES); -} - -/* readonly attribute jsval pictureSizes; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetPictureSizes(JSContext* cx, - JS::MutableHandle aPictureSizes) -{ - return DimensionListToNewObject(cx, aPictureSizes, CAMERA_PARAM_SUPPORTED_PICTURESIZES); -} - -/* readonly attribute jsval thumbnailSizes; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetThumbnailSizes(JSContext* cx, - JS::MutableHandle aThumbnailSizes) -{ - return DimensionListToNewObject(cx, aThumbnailSizes, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES); -} - -/* readonly attribute jsval fileFormats; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetFileFormats(JSContext* cx, - JS::MutableHandle aFileFormats) -{ - return StringListToNewObject(cx, aFileFormats, CAMERA_PARAM_SUPPORTED_PICTUREFORMATS); -} - -/* readonly attribute jsval whiteBalanceModes; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetWhiteBalanceModes(JSContext* cx, - JS::MutableHandle aWhiteBalanceModes) -{ - return StringListToNewObject(cx, aWhiteBalanceModes, CAMERA_PARAM_SUPPORTED_WHITEBALANCES); -} - -/* readonly attribute jsval sceneModes; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetSceneModes(JSContext* cx, - JS::MutableHandle aSceneModes) -{ - return StringListToNewObject(cx, aSceneModes, CAMERA_PARAM_SUPPORTED_SCENEMODES); -} - -/* readonly attribute jsval effects; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetEffects(JSContext* cx, - JS::MutableHandle aEffects) -{ - return StringListToNewObject(cx, aEffects, CAMERA_PARAM_SUPPORTED_EFFECTS); -} - -/* readonly attribute jsval flashModes; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetFlashModes(JSContext* cx, - JS::MutableHandle aFlashModes) -{ - return StringListToNewObject(cx, aFlashModes, CAMERA_PARAM_SUPPORTED_FLASHMODES); -} - -/* readonly attribute jsval focusModes; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetFocusModes(JSContext* cx, - JS::MutableHandle aFocusModes) -{ - return StringListToNewObject(cx, aFocusModes, CAMERA_PARAM_SUPPORTED_FOCUSMODES); -} - -/* readonly attribute long maxFocusAreas; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetMaxFocusAreas(JSContext* cx, int32_t* aMaxFocusAreas) -{ - NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE); - - const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS); - if (!value) { - // in case we get nonsense data back - *aMaxFocusAreas = 0; - return NS_OK; - } - - *aMaxFocusAreas = atoi(value); - return NS_OK; -} - -/* readonly attribute double minExposureCompensation; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetMinExposureCompensation(JSContext* cx, double* aMinExposureCompensation) -{ - NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE); - - const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION); - if (!value) { - // in case we get nonsense data back - *aMinExposureCompensation = 0; - return NS_OK; - } - - *aMinExposureCompensation = atof(value); - return NS_OK; -} - -/* readonly attribute double maxExposureCompensation; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetMaxExposureCompensation(JSContext* cx, double* aMaxExposureCompensation) -{ - NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE); - - const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION); - if (!value) { - // in case we get nonsense data back - *aMaxExposureCompensation = 0; - return NS_OK; - } - - *aMaxExposureCompensation = atof(value); - return NS_OK; -} - -/* readonly attribute double stepExposureCompensation; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetStepExposureCompensation(JSContext* cx, double* aStepExposureCompensation) -{ - NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE); - - const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP); - if (!value) { - // in case we get nonsense data back - *aStepExposureCompensation = 0; - return NS_OK; - } - - *aStepExposureCompensation = atof(value); - return NS_OK; -} - -/* readonly attribute long maxMeteringAreas; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetMaxMeteringAreas(JSContext* cx, int32_t* aMaxMeteringAreas) -{ - NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE); - - const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS); - if (!value) { - // in case we get nonsense data back - *aMaxMeteringAreas = 0; - return NS_OK; - } - - *aMaxMeteringAreas = atoi(value); - return NS_OK; -} - -/* readonly attribute jsval zoomRatios; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetZoomRatios(JSContext* cx, JS::MutableHandle aZoomRatios) -{ - NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE); - - const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_ZOOM); - if (!value || strcmp(value, "true") != 0) { - // if zoom is not supported, return a null object - aZoomRatios.setNull(); - return NS_OK; - } - - JS::Rooted array(cx); - - nsresult rv = ParameterListToNewArray(cx, &array, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, ParseZoomRatioItemAndAdd); - NS_ENSURE_SUCCESS(rv, rv); - - aZoomRatios.setObjectOrNull(array); - return NS_OK; -} - -/* readonly attribute jsval videoSizes; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetVideoSizes(JSContext* cx, JS::MutableHandle aVideoSizes) -{ - NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE); - - nsTArray sizes; - nsresult rv = mCamera->GetVideoSizes(sizes); - NS_ENSURE_SUCCESS(rv, rv); - if (sizes.Length() == 0) { - // video recording not supported, return null - aVideoSizes.setNull(); - return NS_OK; - } - - JS::Rooted array(cx, JS_NewArrayObject(cx, 0)); - if (!array) { - return NS_ERROR_OUT_OF_MEMORY; + rv = aCameraControl->Get(aKey, sizes); + if (NS_FAILED(rv)) { + return rv; } + aSizes.Clear(); + aSizes.SetCapacity(sizes.Length()); for (uint32_t i = 0; i < sizes.Length(); ++i) { - JS::Rooted o(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr())); - JS::Rooted v(cx, INT_TO_JSVAL(sizes[i].width)); - if (!JS_SetProperty(cx, o, "width", v)) { - return NS_ERROR_FAILURE; - } - v = INT_TO_JSVAL(sizes[i].height); - if (!JS_SetProperty(cx, o, "height", v)) { - return NS_ERROR_FAILURE; - } - - if (!JS_SetElement(cx, array, i, o)) { - return NS_ERROR_FAILURE; - } + CameraSize* s = aSizes.AppendElement(); + s->mWidth = sizes[i].width; + s->mHeight = sizes[i].height; } - aVideoSizes.setObject(*array); return NS_OK; } -/* readonly attribute jsval recorderProfiles; */ -NS_IMETHODIMP -DOMCameraCapabilities::GetRecorderProfiles(JSContext* cx, JS::MutableHandle aRecorderProfiles) +nsresult +CameraCapabilities::Populate(ICameraControl* aCameraControl) { - NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE); + NS_ENSURE_TRUE(aCameraControl, NS_ERROR_INVALID_ARG); - nsRefPtr profileMgr = mCamera->GetRecorderProfileManager(); - if (!profileMgr) { - aRecorderProfiles.setNull(); - return NS_OK; + nsresult rv; + + rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, mPreviewSizes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES); + + rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_PICTURESIZES, mPictureSizes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTURESIZES); + + rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, mThumbnailSizes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES); + + rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_VIDEOSIZES, mVideoSizes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_VIDEOSIZES); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_PICTUREFORMATS, mFileFormats); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTUREFORMATS); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_WHITEBALANCES, mWhiteBalanceModes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_WHITEBALANCES); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_SCENEMODES, mSceneModes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_SCENEMODES); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_EFFECTS, mEffects); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EFFECTS); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_FLASHMODES, mFlashModes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FLASHMODES); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_FOCUSMODES, mFocusModes); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FOCUSMODES); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS); + + int32_t areas; + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS); + mMaxFocusAreas = areas < 0 ? 0 : areas; + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, areas); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS); + mMaxMeteringAreas = areas < 0 ? 0 : areas; + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, mMinExposureCompensation); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION, mMaxExposureCompensation); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION); + + rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, mExposureCompensationStep); + LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP); + + mRecorderProfileManager = aCameraControl->GetRecorderProfileManager(); + if (!mRecorderProfileManager) { + DOM_CAMERA_LOGW("Unable to get recorder profile manager\n"); + } else { + AutoJSContext js; + + JS::Rooted o(js); + nsresult rv = mRecorderProfileManager->GetJsObject(js, o.address()); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Failed to JS-objectify profile manager (%d)\n", rv); + return rv; + } + + mRecorderProfiles = JS::ObjectValue(*o); } - JS::Rooted o(cx); - nsresult rv = profileMgr->GetJsObject(cx, o.address()); - NS_ENSURE_SUCCESS(rv, rv); - - aRecorderProfiles.setObject(*o); + // For now, always return success, since the presence or absence of capabilities + // indicates whether or not they are supported. return NS_OK; } + +void +CameraCapabilities::GetPreviewSizes(nsTArray& retval) const +{ + retval = mPreviewSizes; +} + +void +CameraCapabilities::GetPictureSizes(nsTArray& retval) const +{ + retval = mPictureSizes; +} + +void +CameraCapabilities::GetThumbnailSizes(nsTArray& retval) const +{ + retval = mThumbnailSizes; +} + +void +CameraCapabilities::GetVideoSizes(nsTArray& retval) const +{ + retval = mVideoSizes; +} + +void +CameraCapabilities::GetFileFormats(nsTArray& retval) const +{ + retval = mFileFormats; +} + +void +CameraCapabilities::GetWhiteBalanceModes(nsTArray& retval) const +{ + retval = mWhiteBalanceModes; +} + +void +CameraCapabilities::GetSceneModes(nsTArray& retval) const +{ + retval = mSceneModes; +} + +void +CameraCapabilities::GetEffects(nsTArray& retval) const +{ + retval = mEffects; +} + +void +CameraCapabilities::GetFlashModes(nsTArray& retval) const +{ + retval = mFlashModes; +} + +void +CameraCapabilities::GetFocusModes(nsTArray& retval) const +{ + retval = mFocusModes; +} + +void +CameraCapabilities::GetZoomRatios(nsTArray& retval) const +{ + retval = mZoomRatios; +} + +uint32_t +CameraCapabilities::MaxFocusAreas() const +{ + return mMaxFocusAreas; +} + +uint32_t +CameraCapabilities::MaxMeteringAreas() const +{ + return mMaxMeteringAreas; +} + +double +CameraCapabilities::MinExposureCompensation() const +{ + return mMinExposureCompensation; +} + +double +CameraCapabilities::MaxExposureCompensation() const +{ + return mMaxExposureCompensation; +} + +double +CameraCapabilities::ExposureCompensationStep() const +{ + return mExposureCompensationStep; +} + +JS::Value +CameraCapabilities::RecorderProfiles(JSContext* aCx) const +{ + return mRecorderProfiles; +} + +} // namespace dom +} // namespace mozilla diff --git a/dom/camera/DOMCameraCapabilities.h b/dom/camera/DOMCameraCapabilities.h index 35cfcaeafa4e..f8da7f2ea4f2 100644 --- a/dom/camera/DOMCameraCapabilities.h +++ b/dom/camera/DOMCameraCapabilities.h @@ -1,59 +1,96 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ /* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef DOM_CAMERA_DOMCAMERACAPABILITIES_H -#define DOM_CAMERA_DOMCAMERACAPABILITIES_H +#ifndef mozilla_dom_CameraCapabilities_h__ +#define mozilla_dom_CameraCapabilities_h__ -#include "ICameraControl.h" +#include "nsString.h" #include "nsAutoPtr.h" -#include "CameraCommon.h" +#include "mozilla/Attributes.h" +#include "mozilla/ErrorResult.h" +#include "mozilla/dom/CameraManagerBinding.h" +#include "nsCycleCollectionParticipant.h" +#include "nsWrapperCache.h" + +struct JSContext; +class nsPIDOMWindow; namespace mozilla { -typedef nsresult (*ParseItemAndAddFunc)(JSContext* aCx, JS::Handle aArray, - uint32_t aIndex, const char* aStart, char** aEnd); +class ICameraControl; +class RecorderProfileManager; -class DOMCameraCapabilities MOZ_FINAL : public nsICameraCapabilities +namespace dom { + +class CameraCapabilities MOZ_FINAL : public nsISupports + , public nsWrapperCache { public: - NS_DECL_ISUPPORTS - NS_DECL_NSICAMERACAPABILITIES + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraCapabilities) - DOMCameraCapabilities(ICameraControl* aCamera) - : mCamera(aCamera) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } + CameraCapabilities(nsPIDOMWindow* aWindow); + ~CameraCapabilities(); - nsresult ParameterListToNewArray( - JSContext* cx, - JS::MutableHandle aArray, - uint32_t aKey, - ParseItemAndAddFunc aParseItemAndAdd - ); - nsresult StringListToNewObject(JSContext* aCx, - JS::MutableHandle aArray, - uint32_t aKey); - nsresult DimensionListToNewObject(JSContext* aCx, - JS::MutableHandle aArray, - uint32_t aKey); + nsresult Populate(ICameraControl* aCameraControl); -private: - DOMCameraCapabilities(const DOMCameraCapabilities&) MOZ_DELETE; - DOMCameraCapabilities& operator=(const DOMCameraCapabilities&) MOZ_DELETE; + nsPIDOMWindow* GetParentObject() const { return mWindow; } + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aScope) MOZ_OVERRIDE; + + void GetPreviewSizes(nsTArray& aRetVal) const; + void GetPictureSizes(nsTArray& aRetVal) const; + void GetThumbnailSizes(nsTArray& aRetVal) const; + void GetVideoSizes(nsTArray& aRetVal) const; + void GetFileFormats(nsTArray& aRetVal) const; + void GetWhiteBalanceModes(nsTArray& aRetVal) const; + void GetSceneModes(nsTArray& aRetVal) const; + void GetEffects(nsTArray& aRetVal) const; + void GetFlashModes(nsTArray& aRetVal) const; + void GetFocusModes(nsTArray& aRetVal) const; + void GetZoomRatios(nsTArray& aRetVal) const; + uint32_t MaxFocusAreas() const; + uint32_t MaxMeteringAreas() const; + double MinExposureCompensation() const; + double MaxExposureCompensation() const; + double ExposureCompensationStep() const; + JS::Value RecorderProfiles(JSContext* cx) const; protected: - /* additional members */ - ~DOMCameraCapabilities() - { - // destructor code - DOM_CAMERA_LOGT("%s:%d : this=%p, mCamera=%p\n", __func__, __LINE__, this, mCamera.get()); - } + nsresult TranslateToDictionary(ICameraControl* aCameraControl, + uint32_t aKey, nsTArray& aSizes); - nsRefPtr mCamera; + nsTArray mPreviewSizes; + nsTArray mPictureSizes; + nsTArray mThumbnailSizes; + nsTArray mVideoSizes; + + nsTArray mFileFormats; + nsTArray mWhiteBalanceModes; + nsTArray mSceneModes; + nsTArray mEffects; + nsTArray mFlashModes; + nsTArray mFocusModes; + + nsTArray mZoomRatios; + + uint32_t mMaxFocusAreas; + uint32_t mMaxMeteringAreas; + + double mMinExposureCompensation; + double mMaxExposureCompensation; + double mExposureCompensationStep; + + nsRefPtr mRecorderProfileManager; + JS::Heap mRecorderProfiles; + + nsRefPtr mWindow; }; +} // namespace dom } // namespace mozilla -#endif // DOM_CAMERA_DOMCAMERACAPABILITIES_H +#endif // mozilla_dom_CameraCapabilities_h__ diff --git a/dom/camera/DOMCameraControl.cpp b/dom/camera/DOMCameraControl.cpp index 923bef5f7d31..8b550923fe52 100644 --- a/dom/camera/DOMCameraControl.cpp +++ b/dom/camera/DOMCameraControl.cpp @@ -2,6 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "DOMCameraControl.h" #include "base/basictypes.h" #include "nsCOMPtr.h" #include "nsDOMClassInfo.h" @@ -9,7 +10,6 @@ #include "nsThread.h" #include "DeviceStorage.h" #include "DeviceStorageFileDescriptor.h" -#include "mozilla/dom/CameraControlBinding.h" #include "mozilla/dom/TabChild.h" #include "mozilla/MediaManager.h" #include "mozilla/Services.h" @@ -17,28 +17,204 @@ #include "nsIAppsService.h" #include "nsIObserverService.h" #include "nsIDOMDeviceStorage.h" +#include "nsIDOMEventListener.h" #include "nsIScriptSecurityManager.h" #include "nsXULAppAPI.h" #include "DOMCameraManager.h" #include "DOMCameraCapabilities.h" -#include "DOMCameraControl.h" #include "CameraCommon.h" +#include "DictionaryHelpers.h" +#include "nsGlobalWindow.h" +#include "CameraPreviewMediaStream.h" +#include "mozilla/dom/CameraControlBinding.h" #include "mozilla/dom/CameraManagerBinding.h" +#include "mozilla/dom/CameraCapabilitiesBinding.h" #include "mozilla/dom/BindingUtils.h" using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::idl; -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(nsDOMCameraControl, mDOMCapabilities, mWindow) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraControl) +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END + NS_INTERFACE_MAP_ENTRY(nsIDOMMediaStream) +NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream) -NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMCameraControl) -NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMCameraControl) +NS_IMPL_ADDREF_INHERITED(nsDOMCameraControl, DOMMediaStream) +NS_IMPL_RELEASE_INHERITED(nsDOMCameraControl, DOMMediaStream) + +NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMCameraControl) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMCameraControl, DOMMediaStream) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mCapabilities) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGetCameraOnSuccessCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGetCameraOnErrorCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAutoFocusOnSuccessCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mAutoFocusOnErrorCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTakePictureOnSuccessCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mTakePictureOnErrorCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartRecordingOnSuccessCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mStartRecordingOnErrorCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mReleaseOnSuccessCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mReleaseOnErrorCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSetConfigurationOnSuccessCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mSetConfigurationOnErrorCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnShutterCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnClosedCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnRecorderStateChangeCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOnPreviewStateChangeCb) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMCameraControl, DOMMediaStream) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCapabilities) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGetCameraOnSuccessCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGetCameraOnErrorCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAutoFocusOnSuccessCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAutoFocusOnErrorCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTakePictureOnSuccessCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTakePictureOnErrorCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartRecordingOnSuccessCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartRecordingOnErrorCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReleaseOnSuccessCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReleaseOnErrorCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSetConfigurationOnSuccessCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSetConfigurationOnErrorCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnShutterCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnClosedCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnRecorderStateChangeCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOnPreviewStateChangeCb) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDOMCameraControl) + +class mozilla::StartRecordingHelper : public nsIDOMEventListener +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMEVENTLISTENER + + StartRecordingHelper(nsDOMCameraControl* aDOMCameraControl) + : mDOMCameraControl(aDOMCameraControl) + { + MOZ_COUNT_CTOR(StartRecordingHelper); + } + +protected: + virtual ~StartRecordingHelper() + { + MOZ_COUNT_DTOR(StartRecordingHelper); + } + +protected: + nsRefPtr mDOMCameraControl; +}; + +NS_IMETHODIMP +StartRecordingHelper::HandleEvent(nsIDOMEvent* aEvent) +{ + nsString eventType; + aEvent->GetType(eventType); + + mDOMCameraControl->OnCreatedFileDescriptor(eventType.EqualsLiteral("success")); + return NS_OK; +} + +NS_IMPL_ISUPPORTS0(mozilla::StartRecordingHelper) + +nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration() + : CameraConfiguration() + , mMaxFocusAreas(0) + , mMaxMeteringAreas(0) +{ + MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration); +} + +nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration(const CameraConfiguration& aConfiguration) + : CameraConfiguration(aConfiguration) + , mMaxFocusAreas(0) + , mMaxMeteringAreas(0) +{ + MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration); +} + +nsDOMCameraControl::DOMCameraConfiguration::~DOMCameraConfiguration() +{ + MOZ_COUNT_DTOR(nsDOMCameraControl::DOMCameraConfiguration); +} + +nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, + const CameraConfiguration& aInitialConfig, + GetCameraCallback* aOnSuccess, + CameraErrorCallback* aOnError, + nsPIDOMWindow* aWindow) + : DOMMediaStream() + , mCameraControl(nullptr) + , mAudioChannelAgent(nullptr) + , mGetCameraOnSuccessCb(aOnSuccess) + , mGetCameraOnErrorCb(aOnError) + , mAutoFocusOnSuccessCb(nullptr) + , mAutoFocusOnErrorCb(nullptr) + , mTakePictureOnSuccessCb(nullptr) + , mTakePictureOnErrorCb(nullptr) + , mStartRecordingOnSuccessCb(nullptr) + , mStartRecordingOnErrorCb(nullptr) + , mReleaseOnSuccessCb(nullptr) + , mReleaseOnErrorCb(nullptr) + , mSetConfigurationOnSuccessCb(nullptr) + , mSetConfigurationOnErrorCb(nullptr) + , mOnShutterCb(nullptr) + , mOnClosedCb(nullptr) + , mOnRecorderStateChangeCb(nullptr) + , mOnPreviewStateChangeCb(nullptr) + , mWindow(aWindow) +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + mInput = new CameraPreviewMediaStream(this); + + SetIsDOMBinding(); + + nsRefPtr initialConfig = + new DOMCameraConfiguration(aInitialConfig); + + // Create and initialize the underlying camera. + ICameraControl::Configuration config; + + switch (aInitialConfig.mMode) { + case CameraMode::Picture: + config.mMode = ICameraControl::kPictureMode; + break; + + case CameraMode::Video: + config.mMode = ICameraControl::kVideoMode; + break; + + default: + MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode!"); + } + + config.mPreviewSize.width = aInitialConfig.mPreviewSize.mWidth; + config.mPreviewSize.height = aInitialConfig.mPreviewSize.mHeight; + config.mRecorderProfile = aInitialConfig.mRecorderProfile; + + mCameraControl = ICameraControl::Create(aCameraId, &config); + mCurrentConfiguration = initialConfig.forget(); + + // Attach our DOM-facing media stream to our viewfinder stream. + mStream = mInput; + MOZ_ASSERT(mWindow, "Shouldn't be created with a null window!"); + if (mWindow->GetExtantDoc()) { + CombineWithPrincipal(mWindow->GetExtantDoc()->NodePrincipal()); + } + + // Register a listener for camera events. + mListener = new DOMCameraControlListener(this, mInput); + mCameraControl->AddListener(mListener); +} nsDOMCameraControl::~nsDOMCameraControl() { @@ -51,82 +227,212 @@ nsDOMCameraControl::WrapObject(JSContext* aCx, JS::Handle aScope) return CameraControlBinding::Wrap(aCx, aScope, this); } -nsICameraCapabilities* -nsDOMCameraControl::Capabilities() +bool +nsDOMCameraControl::IsWindowStillActive() { - if (!mDOMCapabilities) { - mDOMCapabilities = new DOMCameraCapabilities(mCameraControl); + return nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID()); +} + +// JS-to-native helpers +// Setter for weighted regions: { top, bottom, left, right, weight } +nsresult +nsDOMCameraControl::Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit) +{ + if (aLimit == 0) { + DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__, __LINE__); + return NS_OK; } - return mDOMCapabilities; + if (!aValue.isObject()) { + return NS_ERROR_INVALID_ARG; + } + + uint32_t length = 0; + + JS::Rooted regions(aCx, &aValue.toObject()); + if (!JS_GetArrayLength(aCx, regions, &length)) { + return NS_ERROR_FAILURE; + } + + DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit); + if (length > aLimit) { + length = aLimit; + } + + nsTArray regionArray; + regionArray.SetCapacity(length); + + for (uint32_t i = 0; i < length; ++i) { + JS::Rooted v(aCx); + + if (!JS_GetElement(aCx, regions, i, &v)) { + return NS_ERROR_FAILURE; + } + + CameraRegion region; + if (!region.Init(aCx, v)) { + return NS_ERROR_FAILURE; + } + + ICameraControl::Region* r = regionArray.AppendElement(); + r->top = region.mTop; + r->left = region.mLeft; + r->bottom = region.mBottom; + r->right = region.mRight; + r->weight = region.mWeight; + + DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%u\n", + i, + r->top, + r->left, + r->bottom, + r->right, + r->weight + ); + } + return mCameraControl->Set(aKey, regionArray); +} + +// Getter for weighted regions: { top, bottom, left, right, weight } +nsresult +nsDOMCameraControl::Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue) +{ + nsTArray regionArray; + + nsresult rv = mCameraControl->Get(aKey, regionArray); + NS_ENSURE_SUCCESS(rv, rv); + + JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); + if (!array) { + return NS_ERROR_OUT_OF_MEMORY; + } + + uint32_t length = regionArray.Length(); + DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length); + + for (uint32_t i = 0; i < length; ++i) { + ICameraControl::Region* r = ®ionArray[i]; + JS::Rooted v(aCx); + + JS::Rooted o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); + if (!o) { + return NS_ERROR_OUT_OF_MEMORY; + } + + DOM_CAMERA_LOGI("top=%d\n", r->top); + v = INT_TO_JSVAL(r->top); + if (!JS_SetProperty(aCx, o, "top", v)) { + return NS_ERROR_FAILURE; + } + DOM_CAMERA_LOGI("left=%d\n", r->left); + v = INT_TO_JSVAL(r->left); + if (!JS_SetProperty(aCx, o, "left", v)) { + return NS_ERROR_FAILURE; + } + DOM_CAMERA_LOGI("bottom=%d\n", r->bottom); + v = INT_TO_JSVAL(r->bottom); + if (!JS_SetProperty(aCx, o, "bottom", v)) { + return NS_ERROR_FAILURE; + } + DOM_CAMERA_LOGI("right=%d\n", r->right); + v = INT_TO_JSVAL(r->right); + if (!JS_SetProperty(aCx, o, "right", v)) { + return NS_ERROR_FAILURE; + } + DOM_CAMERA_LOGI("weight=%d\n", r->weight); + v = INT_TO_JSVAL(r->weight); + if (!JS_SetProperty(aCx, o, "weight", v)) { + return NS_ERROR_FAILURE; + } + + if (!JS_SetElement(aCx, array, i, o)) { + return NS_ERROR_FAILURE; + } + } + + *aValue = JS::ObjectValue(*array); + return NS_OK; } void nsDOMCameraControl::GetEffect(nsString& aEffect, ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); aRv = mCameraControl->Get(CAMERA_PARAM_EFFECT, aEffect); } void nsDOMCameraControl::SetEffect(const nsAString& aEffect, ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); aRv = mCameraControl->Set(CAMERA_PARAM_EFFECT, aEffect); } void nsDOMCameraControl::GetWhiteBalanceMode(nsString& aWhiteBalanceMode, ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); aRv = mCameraControl->Get(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode); } void nsDOMCameraControl::SetWhiteBalanceMode(const nsAString& aWhiteBalanceMode, ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); aRv = mCameraControl->Set(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode); } void nsDOMCameraControl::GetSceneMode(nsString& aSceneMode, ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); aRv = mCameraControl->Get(CAMERA_PARAM_SCENEMODE, aSceneMode); } void nsDOMCameraControl::SetSceneMode(const nsAString& aSceneMode, ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); aRv = mCameraControl->Set(CAMERA_PARAM_SCENEMODE, aSceneMode); } void nsDOMCameraControl::GetFlashMode(nsString& aFlashMode, ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); aRv = mCameraControl->Get(CAMERA_PARAM_FLASHMODE, aFlashMode); } void nsDOMCameraControl::SetFlashMode(const nsAString& aFlashMode, ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); aRv = mCameraControl->Set(CAMERA_PARAM_FLASHMODE, aFlashMode); } void nsDOMCameraControl::GetFocusMode(nsString& aFocusMode, ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSMODE, aFocusMode); } void nsDOMCameraControl::SetFocusMode(const nsAString& aFocusMode, ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); aRv = mCameraControl->Set(CAMERA_PARAM_FOCUSMODE, aFocusMode); } double nsDOMCameraControl::GetZoom(ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); + double zoom; - aRv = mCameraControl->Get(CAMERA_PARAM_ZOOM, &zoom); + aRv = mCameraControl->Get(CAMERA_PARAM_ZOOM, zoom); return zoom; } void nsDOMCameraControl::SetZoom(double aZoom, ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); aRv = mCameraControl->Set(CAMERA_PARAM_ZOOM, aZoom); } @@ -135,31 +441,33 @@ JS::Value nsDOMCameraControl::GetMeteringAreas(JSContext* cx, ErrorResult& aRv) { JS::Rooted areas(cx); - aRv = mCameraControl->Get(cx, CAMERA_PARAM_METERINGAREAS, areas.address()); + aRv = Get(cx, CAMERA_PARAM_METERINGAREAS, areas.address()); return areas; } void nsDOMCameraControl::SetMeteringAreas(JSContext* cx, JS::Handle aMeteringAreas, ErrorResult& aRv) { - aRv = mCameraControl->SetMeteringAreas(cx, aMeteringAreas); + aRv = Set(cx, CAMERA_PARAM_METERINGAREAS, aMeteringAreas, + mCurrentConfiguration->mMaxMeteringAreas); } JS::Value nsDOMCameraControl::GetFocusAreas(JSContext* cx, ErrorResult& aRv) { JS::Rooted value(cx); - aRv = mCameraControl->Get(cx, CAMERA_PARAM_FOCUSAREAS, value.address()); + aRv = Get(cx, CAMERA_PARAM_FOCUSAREAS, value.address()); return value; } void nsDOMCameraControl::SetFocusAreas(JSContext* cx, JS::Handle aFocusAreas, ErrorResult& aRv) { - aRv = mCameraControl->SetFocusAreas(cx, aFocusAreas); + aRv = Set(cx, CAMERA_PARAM_FOCUSAREAS, aFocusAreas, + mCurrentConfiguration->mMaxFocusAreas); } static nsresult -GetSize(JSContext* aCx, JS::Value* aValue, const CameraSize& aSize) +GetSize(JSContext* aCx, JS::Value* aValue, const ICameraControl::Size& aSize) { JS::Rooted o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); if (!o) { @@ -186,8 +494,8 @@ JS::Value nsDOMCameraControl::GetPictureSize(JSContext* cx, ErrorResult& aRv) { JS::Rooted value(cx); - - CameraSize size; + + ICameraControl::Size size; aRv = mCameraControl->Get(CAMERA_PARAM_PICTURESIZE, size); if (aRv.Failed()) { return value; @@ -197,82 +505,95 @@ nsDOMCameraControl::GetPictureSize(JSContext* cx, ErrorResult& aRv) return value; } void -nsDOMCameraControl::SetPictureSize(JSContext* cx, JS::Handle aSize, ErrorResult& aRv) +nsDOMCameraControl::SetPictureSize(JSContext* aCx, JS::Handle aSize, ErrorResult& aRv) { CameraSize size; - aRv = size.Init(cx, aSize.address()); - if (aRv.Failed()) { + if (!size.Init(aCx, aSize)) { + aRv = NS_ERROR_FAILURE; return; } - aRv = mCameraControl->Set(CAMERA_PARAM_PICTURESIZE, size); + ICameraControl::Size s = { size.mWidth, size.mHeight }; + aRv = mCameraControl->Set(CAMERA_PARAM_PICTURESIZE, s); } /* attribute any thumbnailSize */ JS::Value -nsDOMCameraControl::GetThumbnailSize(JSContext* cx, ErrorResult& aRv) +nsDOMCameraControl::GetThumbnailSize(JSContext* aCx, ErrorResult& aRv) { - JS::Rooted value(cx); - - CameraSize size; + JS::Rooted value(aCx); + + ICameraControl::Size size; aRv = mCameraControl->Get(CAMERA_PARAM_THUMBNAILSIZE, size); if (aRv.Failed()) { return value; } - aRv = GetSize(cx, value.address(), size); + aRv = GetSize(aCx, value.address(), size); return value; } void -nsDOMCameraControl::SetThumbnailSize(JSContext* cx, JS::Handle aSize, ErrorResult& aRv) +nsDOMCameraControl::SetThumbnailSize(JSContext* aCx, JS::Handle aSize, ErrorResult& aRv) { CameraSize size; - aRv = size.Init(cx, aSize.address()); - if (aRv.Failed()) { + if (!size.Init(aCx, aSize)) { + aRv = NS_ERROR_FAILURE; return; } - aRv = mCameraControl->Set(CAMERA_PARAM_THUMBNAILSIZE, size); + ICameraControl::Size s = { size.mWidth, size.mHeight }; + aRv = mCameraControl->Set(CAMERA_PARAM_THUMBNAILSIZE, s); } double nsDOMCameraControl::GetFocalLength(ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); + double focalLength; - aRv = mCameraControl->Get(CAMERA_PARAM_FOCALLENGTH, &focalLength); + aRv = mCameraControl->Get(CAMERA_PARAM_FOCALLENGTH, focalLength); return focalLength; } double nsDOMCameraControl::GetFocusDistanceNear(ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); + double distance; - aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCENEAR, &distance); + aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCENEAR, distance); return distance; } double nsDOMCameraControl::GetFocusDistanceOptimum(ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); + double distance; - aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEOPTIMUM, &distance); + aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEOPTIMUM, distance); return distance; } double nsDOMCameraControl::GetFocusDistanceFar(ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); + double distance; - aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEFAR, &distance); + aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEFAR, distance); return distance; } void nsDOMCameraControl::SetExposureCompensation(const Optional& aCompensation, ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); + if (!aCompensation.WasPassed()) { // use NaN to switch the camera back into auto mode aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, NAN); + return; } aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, aCompensation.Value()); @@ -281,88 +602,108 @@ nsDOMCameraControl::SetExposureCompensation(const Optional& aCompensatio double nsDOMCameraControl::GetExposureCompensation(ErrorResult& aRv) { + MOZ_ASSERT(mCameraControl); + double compensation; - aRv = mCameraControl->Get(CAMERA_PARAM_EXPOSURECOMPENSATION, &compensation); + aRv = mCameraControl->Get(CAMERA_PARAM_EXPOSURECOMPENSATION, compensation); return compensation; } int32_t nsDOMCameraControl::SensorAngle() { + MOZ_ASSERT(mCameraControl); + int32_t angle; - mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, &angle); + mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, angle); return angle; } -already_AddRefed -nsDOMCameraControl::GetOnShutter(ErrorResult& aRv) +already_AddRefed +nsDOMCameraControl::GetOnShutter() { - nsCOMPtr cb; - aRv = mCameraControl->Get(getter_AddRefs(cb)); + nsCOMPtr cb = mOnShutterCb; return cb.forget(); } void -nsDOMCameraControl::SetOnShutter(nsICameraShutterCallback* aOnShutter, - ErrorResult& aRv) +nsDOMCameraControl::SetOnShutter(CameraShutterCallback* aCb) { - aRv = mCameraControl->Set(aOnShutter); + mOnShutterCb = aCb; } -/* attribute nsICameraClosedCallback onClosed; */ -already_AddRefed -nsDOMCameraControl::GetOnClosed(ErrorResult& aRv) +/* attribute CameraClosedCallback onClosed; */ +already_AddRefed +nsDOMCameraControl::GetOnClosed() { - nsCOMPtr onClosed; - aRv = mCameraControl->Get(getter_AddRefs(onClosed)); + nsCOMPtr onClosed = mOnClosedCb; return onClosed.forget(); } void -nsDOMCameraControl::SetOnClosed(nsICameraClosedCallback* aOnClosed, - ErrorResult& aRv) +nsDOMCameraControl::SetOnClosed(CameraClosedCallback* aCb) { - aRv = mCameraControl->Set(aOnClosed); + mOnClosedCb = aCb; } -already_AddRefed -nsDOMCameraControl::GetOnRecorderStateChange(ErrorResult& aRv) +already_AddRefed +nsDOMCameraControl::GetOnRecorderStateChange() { - nsCOMPtr cb; - aRv = mCameraControl->Get(getter_AddRefs(cb)); + nsCOMPtr cb = mOnRecorderStateChangeCb; return cb.forget(); } void -nsDOMCameraControl::SetOnRecorderStateChange(nsICameraRecorderStateChange* aOnRecorderStateChange, - ErrorResult& aRv) +nsDOMCameraControl::SetOnRecorderStateChange(CameraRecorderStateChange* aCb) { - aRv = mCameraControl->Set(aOnRecorderStateChange); + mOnRecorderStateChangeCb = aCb; } -void -nsDOMCameraControl::StartRecording(JSContext* aCx, - JS::Handle aOptions, - nsDOMDeviceStorage& storageArea, - const nsAString& filename, - nsICameraStartRecordingCallback* onSuccess, - const Optional& onError, - ErrorResult& aRv) +/* attribute CameraPreviewStateChange onPreviewStateChange; */ +already_AddRefed +nsDOMCameraControl::GetOnPreviewStateChange() { - MOZ_ASSERT(onSuccess, "no onSuccess handler passed"); + nsCOMPtr cb = mOnPreviewStateChangeCb; + return cb.forget(); +} +void +nsDOMCameraControl::SetOnPreviewStateChange(CameraPreviewStateChange* aCb) +{ + mOnPreviewStateChangeCb = aCb; +} - // Default values, until the dictionary parser can handle them. - mOptions.rotation = 0; - mOptions.maxFileSizeBytes = 0; - mOptions.maxVideoLengthMs = 0; - aRv = mOptions.Init(aCx, aOptions.address()); - if (aRv.Failed()) { - return; +already_AddRefed +nsDOMCameraControl::Capabilities() +{ + nsRefPtr caps = mCapabilities; + + if (!caps) { + caps = new CameraCapabilities(mWindow); + nsresult rv = caps->Populate(mCameraControl); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGW("Failed to populate camera capabilities (%d)\n", rv); + return nullptr; + } + mCapabilities = caps; } - aRv = NotifyRecordingStatusChange(NS_LITERAL_STRING("starting")); + return caps.forget(); +} - #ifdef MOZ_B2G +// Methods. +void +nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions, + nsDOMDeviceStorage& aStorageArea, + const nsAString& aFilename, + CameraStartRecordingCallback& aOnSuccess, + const Optional >& aOnError, + ErrorResult& aRv) +{ + MOZ_ASSERT(mCameraControl); + + NotifyRecordingStatusChange(NS_LITERAL_STRING("starting")); + +#ifdef MOZ_B2G if (!mAudioChannelAgent) { mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1"); if (mAudioChannelAgent) { @@ -373,251 +714,234 @@ nsDOMCameraControl::StartRecording(JSContext* aCx, mAudioChannelAgent->StartPlaying(&canPlay); } } - #endif +#endif nsCOMPtr request; mDSFileDescriptor = new DeviceStorageFileDescriptor(); - aRv = storageArea.CreateFileDescriptor(filename, mDSFileDescriptor.get(), + aRv = aStorageArea.CreateFileDescriptor(aFilename, mDSFileDescriptor.get(), getter_AddRefs(request)); if (aRv.Failed()) { return; } - mOnSuccessCb = onSuccess; - mOnErrorCb = onError.WasPassed() ? onError.Value() : nullptr; + mOptions = aOptions; + mStartRecordingOnSuccessCb = &aOnSuccess; + mStartRecordingOnErrorCb = nullptr; + if (aOnError.WasPassed()) { + mStartRecordingOnErrorCb = &aOnError.Value(); + } - request->AddEventListener(NS_LITERAL_STRING("success"), this, false); - request->AddEventListener(NS_LITERAL_STRING("error"), this, false); + nsCOMPtr listener = new StartRecordingHelper(this); + request->AddEventListener(NS_LITERAL_STRING("success"), listener, false); + request->AddEventListener(NS_LITERAL_STRING("error"), listener, false); } -NS_IMETHODIMP -nsDOMCameraControl::HandleEvent(nsIDOMEvent* aEvent) +void +nsDOMCameraControl::OnCreatedFileDescriptor(bool aSucceeded) { - nsString eventType; - aEvent->GetType(eventType); - ErrorResult rv; + if (aSucceeded && mDSFileDescriptor->mFileDescriptor.IsValid()) { + ICameraControl::StartRecordingOptions o; - if ((eventType.EqualsLiteral("success")) && - mDSFileDescriptor->mFileDescriptor.IsValid()) { - - rv = mCameraControl->StartRecording(&mOptions, - mDSFileDescriptor.get(), - mOnSuccessCb.get(), - mOnErrorCb.get()); - if (!rv.Failed()) { - return rv.ErrorCode(); + o.rotation = mOptions.mRotation; + o.maxFileSizeBytes = mOptions.mMaxFileSizeBytes; + o.maxVideoLengthMs = mOptions.mMaxVideoLengthMs; + nsresult rv = mCameraControl->StartRecording(mDSFileDescriptor.get(), &o); + if (NS_SUCCEEDED(rv)) { + return; } - - // An error happened. Fall through and call the error callback. } - // We're already be on the main thread, so go ahead and call the - // error callback directly. - - MOZ_ASSERT(NS_IsMainThread()); - - if (mOnErrorCb && - nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID())) { - mOnErrorCb->HandleEvent(NS_LITERAL_STRING("FAILURE")); - } - - return NS_OK; + OnError(CameraControlListener::kInStartRecording, NS_LITERAL_STRING("FAILURE")); } void nsDOMCameraControl::StopRecording(ErrorResult& aRv) { - aRv = NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown")); + MOZ_ASSERT(mCameraControl); - #ifdef MOZ_B2G +#ifdef MOZ_B2G if (mAudioChannelAgent) { mAudioChannelAgent->StopPlaying(); mAudioChannelAgent = nullptr; } - #endif +#endif aRv = mCameraControl->StopRecording(); } -void -nsDOMCameraControl::GetPreviewStream(JSContext* aCx, - JS::Handle aOptions, - nsICameraPreviewStreamCallback* onSuccess, - const Optional& onError, - ErrorResult& aRv) -{ - mozilla::idl::CameraSize size; - aRv = size.Init(aCx, aOptions.address()); - if (aRv.Failed()) { - return; - } - - aRv = mCameraControl->GetPreviewStream(size, onSuccess, - onError.WasPassed() - ? onError.Value() : nullptr); -} - void nsDOMCameraControl::ResumePreview(ErrorResult& aRv) { - aRv = mCameraControl->StartPreview(nullptr); -} - -already_AddRefed -nsDOMCameraControl::GetOnPreviewStateChange() const -{ - nsCOMPtr cb; - mCameraControl->Get(getter_AddRefs(cb)); - return cb.forget(); + MOZ_ASSERT(mCameraControl); + aRv = mCameraControl->StartPreview(); } void -nsDOMCameraControl::SetOnPreviewStateChange(nsICameraPreviewStateChange* aCb) +nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration, + const Optional >& aOnSuccess, + const Optional >& aOnError, + ErrorResult& aRv) { - mCameraControl->Set(aCb); + MOZ_ASSERT(mCameraControl); + + nsCOMPtr cb = mTakePictureOnSuccessCb; + if (cb) { + // We're busy taking a picture, can't change modes right now. + if (aOnError.WasPassed()) { + ErrorResult ignored; + aOnError.Value().Call(NS_LITERAL_STRING("Busy"), ignored); + } + aRv = NS_ERROR_FAILURE; + return; + } + + ICameraControl::Configuration config; + config.mRecorderProfile = aConfiguration.mRecorderProfile; + config.mPreviewSize.width = aConfiguration.mPreviewSize.mWidth; + config.mPreviewSize.height = aConfiguration.mPreviewSize.mHeight; + config.mMode = ICameraControl::kPictureMode; + if (aConfiguration.mMode == CameraMode::Video) { + config.mMode = ICameraControl::kVideoMode; + } + + mSetConfigurationOnSuccessCb = nullptr; + if (aOnSuccess.WasPassed()) { + mSetConfigurationOnSuccessCb = &aOnSuccess.Value(); + } + mSetConfigurationOnErrorCb = nullptr; + if (aOnError.WasPassed()) { + mSetConfigurationOnErrorCb = &aOnError.Value(); + } + + aRv = mCameraControl->SetConfiguration(config); } void -nsDOMCameraControl::AutoFocus(nsICameraAutoFocusCallback* onSuccess, - const Optional& onError, +nsDOMCameraControl::AutoFocus(CameraAutoFocusCallback& aOnSuccess, + const Optional >& aOnError, ErrorResult& aRv) { - aRv = mCameraControl->AutoFocus(onSuccess, - onError.WasPassed() ? onError.Value() : nullptr); + MOZ_ASSERT(mCameraControl); + + nsCOMPtr cb = mAutoFocusOnSuccessCb.forget(); + bool cancel = false; + if (cb) { + // we have a callback, which means we're already in the process of + // auto-focusing--cancel the old callback + nsCOMPtr ecb = mAutoFocusOnErrorCb.forget(); + ErrorResult ignored; + ecb->Call(NS_LITERAL_STRING("Interrupted"), ignored); + cancel = true; + } + + mAutoFocusOnSuccessCb = &aOnSuccess; + mAutoFocusOnErrorCb = nullptr; + if (aOnError.WasPassed()) { + mAutoFocusOnErrorCb = &aOnError.Value(); + } + + aRv = mCameraControl->AutoFocus(cancel); } void -nsDOMCameraControl::TakePicture(JSContext* aCx, - const CameraPictureOptions& aOptions, - nsICameraTakePictureCallback* onSuccess, - const Optional& aOnError, +nsDOMCameraControl::TakePicture(const CameraPictureOptions& aOptions, + CameraTakePictureCallback& aOnSuccess, + const Optional >& aOnError, ErrorResult& aRv) { - mozilla::idl::CameraSize size; - mozilla::idl::CameraPosition pos; + MOZ_ASSERT(mCameraControl); - aRv = size.Init(aCx, &aOptions.mPictureSize); - if (aRv.Failed()) { + nsCOMPtr cb = mTakePictureOnSuccessCb; + if (cb) { + // There is already a call to TakePicture() in progress, abort this one and + // invoke the error callback (if one was passed in). + if (aOnError.WasPassed()) { + ErrorResult ignored; + aOnError.Value().Call(NS_LITERAL_STRING("TakePictureAlreadyInProgress"), ignored); + } + aRv = NS_ERROR_FAILURE; return; } - /** - * Default values, until the dictionary parser can handle them. - * NaN indicates no value provided. - */ - pos.latitude = NAN; - pos.longitude = NAN; - pos.altitude = NAN; - pos.timestamp = NAN; - aRv = pos.Init(aCx, &aOptions.mPosition); - if (aRv.Failed()) { - return; + { + ICameraControlParameterSetAutoEnter batch(mCameraControl); + + // XXXmikeh - remove this: see bug 931155 + ICameraControl::Size s; + s.width = aOptions.mPictureSize.mWidth; + s.height = aOptions.mPictureSize.mHeight; + + ICameraControl::Position p; + p.latitude = aOptions.mPosition.mLatitude; + p.longitude = aOptions.mPosition.mLongitude; + p.altitude = aOptions.mPosition.mAltitude; + p.timestamp = aOptions.mPosition.mTimestamp; + + if (s.width && s.height) { + mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, s); + } + mCameraControl->Set(CAMERA_PARAM_PICTURE_ROTATION, aOptions.mRotation); + mCameraControl->Set(CAMERA_PARAM_PICTURE_FILEFORMAT, aOptions.mFileFormat); + mCameraControl->Set(CAMERA_PARAM_PICTURE_DATETIME, aOptions.mDateTime); + mCameraControl->SetLocation(p); } - nsICameraErrorCallback* onError = - aOnError.WasPassed() ? aOnError.Value() : nullptr; - aRv = mCameraControl->TakePicture(size, aOptions.mRotation, - aOptions.mFileFormat, pos, - aOptions.mDateTime, onSuccess, onError); + mTakePictureOnSuccessCb = &aOnSuccess; + mTakePictureOnErrorCb = nullptr; + if (aOnError.WasPassed()) { + mTakePictureOnErrorCb = &aOnError.Value(); + } + + aRv = mCameraControl->TakePicture(); } void -nsDOMCameraControl::GetPreviewStreamVideoMode(JSContext* aCx, - JS::Handle aOptions, - nsICameraPreviewStreamCallback* onSuccess, - const Optional& onError, - ErrorResult& aRv) -{ - mozilla::idl::CameraRecorderOptions options; - aRv = options.Init(aCx, aOptions.address()); - if (aRv.Failed()) { - return; - } - - aRv = mCameraControl->GetPreviewStreamVideoMode(&options, onSuccess, - onError.WasPassed() - ? onError.Value() : nullptr); -} - -void -nsDOMCameraControl::ReleaseHardware(const Optional& onSuccess, - const Optional& onError, +nsDOMCameraControl::ReleaseHardware(const Optional >& aOnSuccess, + const Optional >& aOnError, ErrorResult& aRv) { - aRv = - mCameraControl->ReleaseHardware( - onSuccess.WasPassed() ? onSuccess.Value() : nullptr, - onError.WasPassed() ? onError.Value() : nullptr); -} + MOZ_ASSERT(mCameraControl); -class GetCameraResult : public nsRunnable -{ -public: - GetCameraResult(nsDOMCameraControl* aDOMCameraControl, - nsresult aResult, - const nsMainThreadPtrHandle& onSuccess, - const nsMainThreadPtrHandle& onError, - uint64_t aWindowId) - : mDOMCameraControl(aDOMCameraControl) - , mResult(aResult) - , mOnSuccessCb(onSuccess) - , mOnErrorCb(onError) - , mWindowId(aWindowId) - { } - - NS_IMETHOD Run() - { - MOZ_ASSERT(NS_IsMainThread()); - - if (nsDOMCameraManager::IsWindowStillActive(mWindowId)) { - DOM_CAMERA_LOGT("%s : this=%p -- BEFORE CALLBACK\n", __func__, this); - if (NS_FAILED(mResult)) { - if (mOnErrorCb.get()) { - mOnErrorCb->HandleEvent(NS_LITERAL_STRING("FAILURE")); - } - } else { - if (mOnSuccessCb.get()) { - mOnSuccessCb->HandleEvent(mDOMCameraControl); - } - } - DOM_CAMERA_LOGT("%s : this=%p -- AFTER CALLBACK\n", __func__, this); - } - - /** - * Finally, release the extra reference to the DOM-facing camera control. - * See the nsDOMCameraControl constructor for the corresponding call to - * NS_ADDREF_THIS(). - */ - NS_RELEASE(mDOMCameraControl); - return NS_OK; + mReleaseOnSuccessCb = nullptr; + if (aOnSuccess.WasPassed()) { + mReleaseOnSuccessCb = &aOnSuccess.Value(); + } + mReleaseOnErrorCb = nullptr; + if (aOnError.WasPassed()) { + mReleaseOnErrorCb = &aOnError.Value(); } -protected: - /** - * 'mDOMCameraControl' is a raw pointer to a previously ADDREF()ed object, - * which is released in Run(). - */ - nsDOMCameraControl* mDOMCameraControl; - nsresult mResult; - nsMainThreadPtrHandle mOnSuccessCb; - nsMainThreadPtrHandle mOnErrorCb; - uint64_t mWindowId; -}; - -nsresult -nsDOMCameraControl::Result(nsresult aResult, - const nsMainThreadPtrHandle& onSuccess, - const nsMainThreadPtrHandle& onError, - uint64_t aWindowId) -{ - nsCOMPtr getCameraResult = new GetCameraResult(this, aResult, onSuccess, onError, aWindowId); - return NS_DispatchToMainThread(getCameraResult); + aRv = mCameraControl->ReleaseHardware(); } void nsDOMCameraControl::Shutdown() { DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__); + MOZ_ASSERT(mCameraControl); + + // Remove any pending solicited event handlers; these + // reference our window object, which in turn references + // us. If we don't remove them, we can leak DOM objects. + mGetCameraOnSuccessCb = nullptr; + mGetCameraOnErrorCb = nullptr; + mAutoFocusOnSuccessCb = nullptr; + mAutoFocusOnErrorCb = nullptr; + mTakePictureOnSuccessCb = nullptr; + mTakePictureOnErrorCb = nullptr; + mStartRecordingOnSuccessCb = nullptr; + mStartRecordingOnErrorCb = nullptr; + mReleaseOnSuccessCb = nullptr; + mReleaseOnErrorCb = nullptr; + mSetConfigurationOnSuccessCb = nullptr; + mSetConfigurationOnErrorCb = nullptr; + + // Remove all of the unsolicited event handlers too. + mOnShutterCb = nullptr; + mOnClosedCb = nullptr; + mOnRecorderStateChangeCb = nullptr; + mOnPreviewStateChangeCb = nullptr; + mCameraControl->Shutdown(); } @@ -638,3 +962,265 @@ nsDOMCameraControl::NotifyRecordingStatusChange(const nsString& aMsg) true /* aIsVideo */); } +// Camera Control event handlers--must only be called from the Main Thread! +void +nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState aState) +{ + MOZ_ASSERT(NS_IsMainThread()); + ErrorResult ignored; + + DOM_CAMERA_LOGI("DOM OnHardwareStateChange(%d)\n", aState); + + switch (aState) { + case CameraControlListener::kHardwareOpen: + // The hardware is open, so we can return a camera to JS, even if + // the preview hasn't started yet. + if (mGetCameraOnSuccessCb) { + nsCOMPtr cb = mGetCameraOnSuccessCb.forget(); + ErrorResult ignored; + mGetCameraOnErrorCb = nullptr; + cb->Call(*this, *mCurrentConfiguration, ignored); + } + break; + + case CameraControlListener::kHardwareClosed: + if (mReleaseOnSuccessCb) { + // If we have this event handler, this was a solicited hardware close. + nsCOMPtr cb = mReleaseOnSuccessCb.forget(); + mReleaseOnErrorCb = nullptr; + cb->Call(ignored); + } else if(mOnClosedCb) { + // If not, something else closed the hardware. + nsCOMPtr cb = mOnClosedCb; + cb->Call(ignored); + } + break; + + default: + MOZ_ASSUME_UNREACHABLE("Unanticipated camera hardware state"); + } +} + +void +nsDOMCameraControl::OnShutter() +{ + MOZ_ASSERT(NS_IsMainThread()); + + DOM_CAMERA_LOGI("DOM ** SNAP **\n"); + + nsCOMPtr cb = mOnShutterCb; + if (cb) { + ErrorResult ignored; + cb->Call(ignored); + } +} + +void +nsDOMCameraControl::OnPreviewStateChange(CameraControlListener::PreviewState aState) +{ + MOZ_ASSERT(NS_IsMainThread()); + + if (!mOnPreviewStateChangeCb) { + return; + } + + nsString state; + switch (aState) { + case CameraControlListener::kPreviewStarted: + state = NS_LITERAL_STRING("started"); + break; + + default: + state = NS_LITERAL_STRING("stopped"); + break; + } + + nsCOMPtr cb = mOnPreviewStateChangeCb; + ErrorResult ignored; + cb->Call(state, ignored); +} + +void +nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState aState, + int32_t aArg, int32_t aTrackNum) +{ + // For now, we do nothing with 'aStatus' and 'aTrackNum'. + MOZ_ASSERT(NS_IsMainThread()); + + ErrorResult ignored; + nsString state; + + switch (aState) { + case CameraControlListener::kRecorderStarted: + if (mStartRecordingOnSuccessCb) { + nsCOMPtr cb = mStartRecordingOnSuccessCb.forget(); + mStartRecordingOnErrorCb = nullptr; + cb->Call(ignored); + } + return; + + case CameraControlListener::kRecorderStopped: + NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown")); + return; + +#ifdef MOZ_B2G_CAMERA + case CameraControlListener::kFileSizeLimitReached: + state = NS_LITERAL_STRING("FileSizeLimitReached"); + break; + + case CameraControlListener::kVideoLengthLimitReached: + state = NS_LITERAL_STRING("VideoLengthLimitReached"); + break; + + case CameraControlListener::kTrackCompleted: + state = NS_LITERAL_STRING("TrackCompleted"); + break; + + case CameraControlListener::kTrackFailed: + state = NS_LITERAL_STRING("TrackFailed"); + break; + + case CameraControlListener::kMediaRecorderFailed: + state = NS_LITERAL_STRING("MediaRecorderFailed"); + break; + + case CameraControlListener::kMediaServerFailed: + state = NS_LITERAL_STRING("MediaServerFailed"); + break; +#endif + + default: + MOZ_ASSUME_UNREACHABLE("Unanticipated video recorder error"); + return; + } + + nsCOMPtr cb = mOnRecorderStateChangeCb; + if (cb) { + cb->Call(state, ignored); + } +} + +void +nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration) +{ + MOZ_ASSERT(NS_IsMainThread()); + + // Update our record of the current camera configuration + mCurrentConfiguration = aConfiguration; + + DOM_CAMERA_LOGI("DOM OnConfigurationChange: this=%p\n", this); + DOM_CAMERA_LOGI(" mode : %s\n", + mCurrentConfiguration->mMode == CameraMode::Video ? "video" : "picture"); + DOM_CAMERA_LOGI(" maximum focus areas : %d\n", + mCurrentConfiguration->mMaxFocusAreas); + DOM_CAMERA_LOGI(" maximum metering areas : %d\n", + mCurrentConfiguration->mMaxMeteringAreas); + DOM_CAMERA_LOGI(" preview size (w x h) : %d x %d\n", + mCurrentConfiguration->mPreviewSize.mWidth, mCurrentConfiguration->mPreviewSize.mHeight); + DOM_CAMERA_LOGI(" recorder profile : %s\n", + NS_ConvertUTF16toUTF8(mCurrentConfiguration->mRecorderProfile).get()); + + nsCOMPtr cb = mSetConfigurationOnSuccessCb.forget(); + mSetConfigurationOnErrorCb = nullptr; + if (cb) { + ErrorResult ignored; + cb->Call(*mCurrentConfiguration, ignored); + } +} + +void +nsDOMCameraControl::OnAutoFocusComplete(bool aAutoFocusSucceeded) +{ + MOZ_ASSERT(NS_IsMainThread()); + ErrorResult ignored; + + nsCOMPtr cb = mAutoFocusOnSuccessCb.forget(); + mAutoFocusOnErrorCb = nullptr; + cb->Call(aAutoFocusSucceeded, ignored); +} + +void +nsDOMCameraControl::OnTakePictureComplete(nsIDOMBlob* aPicture) +{ + MOZ_ASSERT(NS_IsMainThread()); + ErrorResult ignored; + + nsCOMPtr cb = mTakePictureOnSuccessCb.forget(); + mTakePictureOnErrorCb = nullptr; + cb->Call(aPicture, ignored); +} + +void +nsDOMCameraControl::OnError(CameraControlListener::CameraErrorContext aContext, const nsAString& aError) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr* errorCb; + switch (aContext) { + case CameraControlListener::kInGetCamera: + mGetCameraOnSuccessCb = nullptr; + errorCb = &mGetCameraOnErrorCb; + break; + + case CameraControlListener::kInSetConfiguration: + mSetConfigurationOnSuccessCb = nullptr; + errorCb = &mSetConfigurationOnErrorCb; + break; + + case CameraControlListener::kInAutoFocus: + mAutoFocusOnSuccessCb = nullptr; + errorCb = &mAutoFocusOnErrorCb; + break; + + case CameraControlListener::kInTakePicture: + mTakePictureOnSuccessCb = nullptr; + errorCb = &mTakePictureOnErrorCb; + break; + + case CameraControlListener::kInStartRecording: + mStartRecordingOnSuccessCb = nullptr; + errorCb = &mStartRecordingOnErrorCb; + break; + + case CameraControlListener::kInReleaseHardware: + mReleaseOnSuccessCb = nullptr; + errorCb = &mReleaseOnErrorCb; + break; + + case CameraControlListener::kInStopRecording: + NS_WARNING("Failed to stop recording (which shouldn't happen)!"); + MOZ_CRASH(); + break; + + case CameraControlListener::kInStartPreview: + NS_WARNING("Failed to (re)start preview!"); + MOZ_CRASH(); + break; + + case CameraControlListener::kInUnspecified: + if (aError.EqualsASCII("ErrorServiceFailed")) { + // If the camera service fails, we will get preview-stopped and + // hardware-closed events, so nothing to do here. + return; + } + // fallthrough + + default: + MOZ_ASSUME_UNREACHABLE("Error occurred in unanticipated camera state"); + return; + } + + MOZ_ASSERT(errorCb); + + if (!*errorCb) { + DOM_CAMERA_LOGW("DOM No error handler for error '%s' at %d\n", + NS_LossyConvertUTF16toASCII(aError).get(), aContext); + return; + } + + // kung-fu death grip + nsCOMPtr cb = (*errorCb).forget(); + ErrorResult ignored; + cb->Call(aError, ignored); +} + diff --git a/dom/camera/DOMCameraControl.h b/dom/camera/DOMCameraControl.h index 874bb5c7107e..30cb14de83b9 100644 --- a/dom/camera/DOMCameraControl.h +++ b/dom/camera/DOMCameraControl.h @@ -8,51 +8,50 @@ #include "nsCOMPtr.h" #include "nsAutoPtr.h" #include "nsCycleCollectionParticipant.h" -#include "nsIDOMEventListener.h" -#include "DictionaryHelpers.h" +#include "mozilla/dom/CameraControlBinding.h" #include "ICameraControl.h" -#include "DOMCameraPreview.h" -#include "nsIDOMCameraManager.h" #include "CameraCommon.h" +#include "DOMMediaStream.h" #include "AudioChannelAgent.h" #include "nsProxyRelease.h" #include "nsHashPropertyBag.h" #include "DeviceStorage.h" +#include "DOMCameraControlListener.h" class nsDOMDeviceStorage; class nsPIDOMWindow; +class nsIDOMBlob; namespace mozilla { + namespace dom { -class CameraPictureOptions; -template class Optional; + class CameraCapabilities; + class CameraPictureOptions; + class CameraStartRecordingOptions; + template class Optional; } class ErrorResult; +class StartRecordingHelper; // Main camera control. -class nsDOMCameraControl MOZ_FINAL : public nsIDOMEventListener, - public nsWrapperCache +class nsDOMCameraControl MOZ_FINAL : public DOMMediaStream { public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_NSIDOMEVENTLISTENER - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMCameraControl) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsDOMCameraControl, DOMMediaStream) + NS_DECL_ISUPPORTS_INHERITED - nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, - nsICameraGetCameraCallback* onSuccess, - nsICameraErrorCallback* onError, nsPIDOMWindow* aWindow); - nsresult Result(nsresult aResult, - const nsMainThreadPtrHandle& onSuccess, - const nsMainThreadPtrHandle& onError, - uint64_t aWindowId); + nsDOMCameraControl(uint32_t aCameraId, + const dom::CameraConfiguration& aInitialConfig, + dom::GetCameraCallback* aOnSuccess, + dom::CameraErrorCallback* aOnError, + nsPIDOMWindow* aWindow); nsRefPtr GetNativeCameraControl(); void Shutdown(); nsPIDOMWindow* GetParentObject() const { return mWindow; } - // WebIDL - nsICameraCapabilities* Capabilities(); + // Attributes. void GetEffect(nsString& aEffect, ErrorResult& aRv); void SetEffect(const nsAString& aEffect, ErrorResult& aRv); void GetWhiteBalanceMode(nsString& aMode, ErrorResult& aRv); @@ -80,48 +79,130 @@ public: void SetExposureCompensation(const dom::Optional& aCompensation, ErrorResult& aRv); double GetExposureCompensation(ErrorResult& aRv); int32_t SensorAngle(); - already_AddRefed GetOnShutter(ErrorResult& aRv); - void SetOnShutter(nsICameraShutterCallback* aCb, ErrorResult& aRv); - already_AddRefed GetOnClosed(ErrorResult& aRv); - void SetOnClosed(nsICameraClosedCallback* aCb, ErrorResult& aRv); - already_AddRefed GetOnRecorderStateChange(ErrorResult& aRv); - void SetOnRecorderStateChange(nsICameraRecorderStateChange* aCb, ErrorResult& aRv); - void AutoFocus(nsICameraAutoFocusCallback* aOnSuccess, const dom::Optional& aOnErro, ErrorResult& aRvr); - void TakePicture(JSContext* aCx, const dom::CameraPictureOptions& aOptions, - nsICameraTakePictureCallback* onSuccess, - const dom::Optional& onError, + already_AddRefed Capabilities(); + + // Unsolicited event handlers. + already_AddRefed GetOnShutter(); + void SetOnShutter(dom::CameraShutterCallback* aCb); + already_AddRefed GetOnClosed(); + void SetOnClosed(dom::CameraClosedCallback* aCb); + already_AddRefed GetOnRecorderStateChange(); + void SetOnRecorderStateChange(dom::CameraRecorderStateChange* aCb); + already_AddRefed GetOnPreviewStateChange(); + void SetOnPreviewStateChange(dom::CameraPreviewStateChange* aCb); + + // Methods. + void SetConfiguration(const dom::CameraConfiguration& aConfiguration, + const dom::Optional >& aOnSuccess, + const dom::Optional >& aOnError, + ErrorResult& aRv); + void AutoFocus(dom::CameraAutoFocusCallback& aOnSuccess, + const dom::Optional >& aOnError, + ErrorResult& aRv); + void TakePicture(const dom::CameraPictureOptions& aOptions, + dom::CameraTakePictureCallback& aOnSuccess, + const dom::Optional >& aOnError, ErrorResult& aRv); - already_AddRefed GetOnPreviewStateChange() const; - void SetOnPreviewStateChange(nsICameraPreviewStateChange* aOnStateChange); - void GetPreviewStreamVideoMode(JSContext* cx, JS::Handle aOptions, nsICameraPreviewStreamCallback* onSuccess, const dom::Optional& onError, ErrorResult& aRv); - void StartRecording(JSContext* cx, JS::Handle aOptions, nsDOMDeviceStorage& storageArea, const nsAString& filename, nsICameraStartRecordingCallback* onSuccess, const dom::Optional& onError, ErrorResult& aRv); + void StartRecording(const dom::CameraStartRecordingOptions& aOptions, + nsDOMDeviceStorage& storageArea, + const nsAString& filename, + dom::CameraStartRecordingCallback& aOnSuccess, + const dom::Optional >& aOnError, + ErrorResult& aRv); void StopRecording(ErrorResult& aRv); - void GetPreviewStream(JSContext* cx, JS::Handle aOptions, nsICameraPreviewStreamCallback* onSuccess, const dom::Optional& onError, ErrorResult& aRv); void ResumePreview(ErrorResult& aRv); - void ReleaseHardware(const dom::Optional& onSuccess, const dom::Optional& onError, ErrorResult& aRv); + void ReleaseHardware(const dom::Optional >& aOnSuccess, + const dom::Optional >& aOnError, + ErrorResult& aRv); + + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aScope) MOZ_OVERRIDE; protected: virtual ~nsDOMCameraControl(); + class DOMCameraConfiguration : public dom::CameraConfiguration + { + public: + NS_INLINE_DECL_REFCOUNTING(DOMCameraConfiguration) + + DOMCameraConfiguration(); + DOMCameraConfiguration(const dom::CameraConfiguration& aConfiguration); + + // Additional configuration options that aren't exposed to the DOM + uint32_t mMaxFocusAreas; + uint32_t mMaxMeteringAreas; + + protected: + ~DOMCameraConfiguration(); + }; + + friend class DOMCameraControlListener; + friend class mozilla::StartRecordingHelper; + + void OnCreatedFileDescriptor(bool aSucceeded); + + void OnAutoFocusComplete(bool aAutoFocusSucceeded); + void OnTakePictureComplete(nsIDOMBlob* aPicture); + + void OnHardwareStateChange(DOMCameraControlListener::HardwareState aState); + void OnPreviewStateChange(DOMCameraControlListener::PreviewState aState); + void OnRecorderStateChange(CameraControlListener::RecorderState aState, int32_t aStatus, int32_t aTrackNum); + void OnConfigurationChange(DOMCameraConfiguration* aConfiguration); + void OnShutter(); + void OnError(CameraControlListener::CameraErrorContext aContext, const nsAString& mError); + + bool IsWindowStillActive(); + + nsresult NotifyRecordingStatusChange(const nsString& aMsg); + + nsRefPtr mCameraControl; // non-DOM camera control + + // An agent used to join audio channel service. + nsCOMPtr mAudioChannelAgent; + + nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit); + nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue); + + nsRefPtr mCurrentConfiguration; + nsRefPtr mCapabilities; + + // solicited camera control event handlers + nsCOMPtr mGetCameraOnSuccessCb; + nsCOMPtr mGetCameraOnErrorCb; + nsCOMPtr mAutoFocusOnSuccessCb; + nsCOMPtr mAutoFocusOnErrorCb; + nsCOMPtr mTakePictureOnSuccessCb; + nsCOMPtr mTakePictureOnErrorCb; + nsCOMPtr mStartRecordingOnSuccessCb; + nsCOMPtr mStartRecordingOnErrorCb; + nsCOMPtr mReleaseOnSuccessCb; + nsCOMPtr mReleaseOnErrorCb; + nsCOMPtr mSetConfigurationOnSuccessCb; + nsCOMPtr mSetConfigurationOnErrorCb; + + // unsolicited event handlers + nsCOMPtr mOnShutterCb; + nsCOMPtr mOnClosedCb; + nsCOMPtr mOnRecorderStateChangeCb; + nsCOMPtr mOnPreviewStateChangeCb; + + // Camera event listener; we only need this weak reference so that + // we can remove the listener from the camera when we're done + // with it. + DOMCameraControlListener* mListener; + + // our viewfinder stream + CameraPreviewMediaStream* mInput; + + // set once when this object is created + nsCOMPtr mWindow; + + dom::CameraStartRecordingOptions mOptions; + nsRefPtr mDSFileDescriptor; + private: nsDOMCameraControl(const nsDOMCameraControl&) MOZ_DELETE; nsDOMCameraControl& operator=(const nsDOMCameraControl&) MOZ_DELETE; - - virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aScope) MOZ_OVERRIDE; - nsresult NotifyRecordingStatusChange(const nsString& aMsg); - - mozilla::idl::CameraStartRecordingOptions mOptions; - nsRefPtr mDSFileDescriptor; - nsCOMPtr mOnSuccessCb; - nsCOMPtr mOnErrorCb; - -protected: - /* additional members */ - nsRefPtr mCameraControl; // non-DOM camera control - nsCOMPtr mDOMCapabilities; - // An agent used to join audio channel service. - nsCOMPtr mAudioChannelAgent; - nsCOMPtr mWindow; }; } // namespace mozilla diff --git a/dom/camera/DOMCameraControlListener.cpp b/dom/camera/DOMCameraControlListener.cpp new file mode 100644 index 000000000000..de91c59043f3 --- /dev/null +++ b/dom/camera/DOMCameraControlListener.cpp @@ -0,0 +1,329 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public +* License, v. 2.0. If a copy of the MPL was not distributed with this file, +* You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMCameraControlListener.h" +#include "nsThreadUtils.h" +#include "nsDOMFile.h" +#include "CameraCommon.h" +#include "DOMCameraControl.h" +#include "CameraPreviewMediaStream.h" +#include "mozilla/dom/CameraManagerBinding.h" + +using namespace mozilla; +using namespace mozilla::dom; + +// Boilerplate callback runnable +class DOMCameraControlListener::DOMCallback : public nsRunnable +{ +public: + DOMCallback(nsMainThreadPtrHandle aDOMCameraControl) + : mDOMCameraControl(aDOMCameraControl) + { } + virtual ~DOMCallback() { } + + virtual void RunCallback(nsDOMCameraControl* aDOMCameraControl) = 0; + + NS_IMETHOD + Run() MOZ_OVERRIDE + { + MOZ_ASSERT(NS_IsMainThread()); + + nsRefPtr camera = mDOMCameraControl.get(); + if (camera) { + RunCallback(camera); + } + return NS_OK; + } + +protected: + nsMainThreadPtrHandle mDOMCameraControl; +}; + +// Specific callback handlers +void +DOMCameraControlListener::OnHardwareStateChange(HardwareState aState) +{ + class Callback : public DOMCallback + { + public: + Callback(nsMainThreadPtrHandle aDOMCameraControl, + HardwareState aState) + : DOMCallback(aDOMCameraControl) + , mState(aState) + { } + + void + RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE + { + aDOMCameraControl->OnHardwareStateChange(mState); + } + + protected: + HardwareState mState; + }; + + NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState)); +} + +void +DOMCameraControlListener::OnPreviewStateChange(PreviewState aState) +{ + class Callback : public DOMCallback + { + public: + Callback(nsMainThreadPtrHandle aDOMCameraControl, + PreviewState aState) + : DOMCallback(aDOMCameraControl) + , mState(aState) + { } + + void + RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE + { + aDOMCameraControl->OnPreviewStateChange(mState); + } + + protected: + PreviewState mState; + }; + + switch (aState) { + case kPreviewStopped: + // Clear the current frame right away, without dispatching a + // runnable. This is an ugly coupling between the camera's + // SurfaceTextureClient and the MediaStream/ImageContainer, + // but without it, the preview can fail to start. + DOM_CAMERA_LOGI("Preview stopped, clearing current frame\n"); + mStream->ClearCurrentFrame(); + break; + + case kPreviewPaused: + // In the paused state, we still want to reflect the change + // in preview state, but we don't want to clear the current + // frame as above, since doing so seems to cause genlock + // problems when we restart the preview. See bug 957749. + DOM_CAMERA_LOGI("Preview paused\n"); + break; + + case kPreviewStarted: + DOM_CAMERA_LOGI("Preview started\n"); + break; + + default: + DOM_CAMERA_LOGE("Unknown preview state %d\n", aState); + MOZ_ASSUME_UNREACHABLE("Invalid preview state"); + return; + } + NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState)); +} + +void +DOMCameraControlListener::OnRecorderStateChange(RecorderState aState, + int32_t aStatus, int32_t aTrackNum) +{ + class Callback : public DOMCallback + { + public: + Callback(nsMainThreadPtrHandle aDOMCameraControl, + RecorderState aState, + int32_t aStatus, + int32_t aTrackNum) + : DOMCallback(aDOMCameraControl) + , mState(aState) + , mStatus(aStatus) + , mTrackNum(aTrackNum) + { } + + void + RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE + { + aDOMCameraControl->OnRecorderStateChange(mState, mStatus, mTrackNum); + } + + protected: + RecorderState mState; + int32_t mStatus; + int32_t mTrackNum; + }; + + NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState, aStatus, aTrackNum)); +} + +void +DOMCameraControlListener::OnConfigurationChange(const CameraListenerConfiguration& aConfiguration) +{ + class Callback : public DOMCallback + { + public: + Callback(nsMainThreadPtrHandle aDOMCameraControl, + const CameraListenerConfiguration& aConfiguration) + : DOMCallback(aDOMCameraControl) + , mConfiguration(aConfiguration) + { } + + void + RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE + { + nsRefPtr config = + new nsDOMCameraControl::DOMCameraConfiguration(); + + switch (mConfiguration.mMode) { + case ICameraControl::kVideoMode: + config->mMode = CameraMode::Video; + break; + + case ICameraControl::kPictureMode: + config->mMode = CameraMode::Picture; + break; + + default: + MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode!"); + } + + // Map CameraControl parameters to their DOM-facing equivalents + config->mRecorderProfile = mConfiguration.mRecorderProfile; + config->mPreviewSize.mWidth = mConfiguration.mPreviewSize.width; + config->mPreviewSize.mHeight = mConfiguration.mPreviewSize.height; + config->mMaxMeteringAreas = mConfiguration.mMaxMeteringAreas; + config->mMaxFocusAreas = mConfiguration.mMaxFocusAreas; + + aDOMCameraControl->OnConfigurationChange(config); + } + + protected: + const CameraListenerConfiguration mConfiguration; + }; + + NS_DispatchToMainThread(new Callback(mDOMCameraControl, aConfiguration)); +} + +void +DOMCameraControlListener::OnShutter() +{ + class Callback : public DOMCallback + { + public: + Callback(nsMainThreadPtrHandle aDOMCameraControl) + : DOMCallback(aDOMCameraControl) + { } + + void + RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE + { + aDOMCameraControl->OnShutter(); + } + }; + + NS_DispatchToMainThread(new Callback(mDOMCameraControl)); +} + +bool +DOMCameraControlListener::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) +{ + DOM_CAMERA_LOGI("OnNewPreviewFrame: got %d x %d frame\n", aWidth, aHeight); + + mStream->SetCurrentFrame(gfxIntSize(aWidth, aHeight), aImage); + return true; +} + +void +DOMCameraControlListener::OnAutoFocusComplete(bool aAutoFocusSucceeded) +{ + class Callback : public DOMCallback + { + public: + Callback(nsMainThreadPtrHandle aDOMCameraControl, + bool aAutoFocusSucceeded) + : DOMCallback(aDOMCameraControl) + , mAutoFocusSucceeded(aAutoFocusSucceeded) + { } + + void + RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE + { + aDOMCameraControl->OnAutoFocusComplete(mAutoFocusSucceeded); + } + + protected: + bool mAutoFocusSucceeded; + }; + + NS_DispatchToMainThread(new Callback(mDOMCameraControl, aAutoFocusSucceeded)); +} + +void +DOMCameraControlListener::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) +{ + class Callback : public DOMCallback + { + public: + Callback(nsMainThreadPtrHandle aDOMCameraControl, + uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) + : DOMCallback(aDOMCameraControl) + , mData(aData) + , mLength(aLength) + , mMimeType(aMimeType) + { } + + void + RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE + { + nsCOMPtr picture = new nsDOMMemoryFile(static_cast(mData), + static_cast(mLength), + mMimeType); + aDOMCameraControl->OnTakePictureComplete(picture); + } + + protected: + uint8_t* mData; + uint32_t mLength; + nsString mMimeType; + }; + + NS_DispatchToMainThread(new Callback(mDOMCameraControl, aData, aLength, aMimeType)); +} + +void +DOMCameraControlListener::OnError(CameraErrorContext aContext, CameraError aError) +{ + class Callback : public DOMCallback + { + public: + Callback(nsMainThreadPtrHandle aDOMCameraControl, + CameraErrorContext aContext, + CameraError aError) + : DOMCallback(aDOMCameraControl) + , mContext(aContext) + , mError(aError) + { } + + void + RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE + { + nsString error; + + switch (mError) { + case kErrorServiceFailed: + error = NS_LITERAL_STRING("ErrorServiceFailed"); + break; + + case kErrorApiFailed: + // XXXmikeh legacy error placeholder + error = NS_LITERAL_STRING("FAILURE"); + break; + + default: + error = NS_LITERAL_STRING("ErrorUnknown"); + break; + } + aDOMCameraControl->OnError(mContext, error); + } + + protected: + CameraErrorContext mContext; + CameraError mError; + }; + + NS_DispatchToMainThread(new Callback(mDOMCameraControl, aContext, aError)); +} diff --git a/dom/camera/DOMCameraControlListener.h b/dom/camera/DOMCameraControlListener.h new file mode 100644 index 000000000000..c8638f7a9f10 --- /dev/null +++ b/dom/camera/DOMCameraControlListener.h @@ -0,0 +1,48 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DOM_CAMERA_DOMCAMERACONTROLLISTENER_H +#define DOM_CAMERA_DOMCAMERACONTROLLISTENER_H + +#include "nsProxyRelease.h" +#include "CameraControlListener.h" + +namespace mozilla { + +class nsDOMCameraControl; +class CameraPreviewMediaStream; + +class DOMCameraControlListener : public CameraControlListener +{ +protected: + nsMainThreadPtrHandle mDOMCameraControl; + CameraPreviewMediaStream* mStream; + + class DOMCallback; + +public: + DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl, CameraPreviewMediaStream* aStream) + : mDOMCameraControl(new nsMainThreadPtrHolder(aDOMCameraControl)) + , mStream(aStream) + { } + + void OnAutoFocusComplete(bool aAutoFocusSucceeded); + void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType); + + void OnHardwareStateChange(HardwareState aState); + void OnPreviewStateChange(PreviewState aState); + void OnRecorderStateChange(RecorderState aState, int32_t aStatus, int32_t aTrackNum); + void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration); + void OnShutter(); + bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight); + void OnError(CameraErrorContext aContext, CameraError aError); + +private: + DOMCameraControlListener(const DOMCameraControlListener&) MOZ_DELETE; + DOMCameraControlListener& operator=(const DOMCameraControlListener&) MOZ_DELETE; +}; + +} // namespace mozilla + +#endif // DOM_CAMERA_DOMCAMERACONTROLLISTENER_H diff --git a/dom/camera/DOMCameraManager.cpp b/dom/camera/DOMCameraManager.cpp index fbdb8714726c..454d701fdc66 100644 --- a/dom/camera/DOMCameraManager.cpp +++ b/dom/camera/DOMCameraManager.cpp @@ -2,16 +2,18 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "DOMCameraManager.h" #include "nsDebug.h" +#include "jsapi.h" #include "nsPIDOMWindow.h" #include "mozilla/Services.h" #include "nsObserverService.h" #include "nsIPermissionManager.h" #include "DOMCameraControl.h" -#include "DOMCameraManager.h" #include "nsDOMClassInfo.h" #include "DictionaryHelpers.h" #include "CameraCommon.h" +#include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/CameraManagerBinding.h" using namespace mozilla; @@ -22,6 +24,7 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMCameraManager, mWindow) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraManager) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) + NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_END @@ -38,37 +41,35 @@ PRLogModuleInfo* GetCameraLog() { static PRLogModuleInfo *sLog; - if (!sLog) + if (!sLog) { sLog = PR_NewLogModule("Camera"); + } return sLog; } -/** - * nsDOMCameraManager::GetListOfCameras - * is implementation-specific, and can be found in (e.g.) - * GonkCameraManager.cpp and FallbackCameraManager.cpp. - */ - WindowTable* nsDOMCameraManager::sActiveWindows = nullptr; nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindow* aWindow) : mWindowId(aWindow->WindowID()) - , mCameraThread(nullptr) , mWindow(aWindow) { /* member initializers and constructor code */ DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%llx\n", __func__, __LINE__, this, mWindowId); + MOZ_COUNT_CTOR(nsDOMCameraManager); SetIsDOMBinding(); } nsDOMCameraManager::~nsDOMCameraManager() { /* destructor code */ + MOZ_COUNT_DTOR(nsDOMCameraManager); DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - nsCOMPtr obs = services::GetObserverService(); - if (obs) { - obs->RemoveObserver(this, "xpcom-shutdown"); - } +} + +void +nsDOMCameraManager::GetListOfCameras(nsTArray& aList, ErrorResult& aRv) +{ + aRv = ICameraControl::GetListOfCameras(aList); } bool @@ -106,30 +107,28 @@ nsDOMCameraManager::CreateInstance(nsPIDOMWindow* aWindow) } void -nsDOMCameraManager::GetCamera(const CameraSelector& aOptions, - nsICameraGetCameraCallback* onSuccess, - const Optional& onError, +nsDOMCameraManager::GetCamera(const nsAString& aCamera, + const CameraConfiguration& aInitialConfig, + GetCameraCallback& aOnSuccess, + const Optional >& aOnError, ErrorResult& aRv) { + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); + uint32_t cameraId = 0; // back (or forward-facing) camera by default - if (aOptions.mCamera.EqualsLiteral("front")) { + if (aCamera.EqualsLiteral("front")) { cameraId = 1; } - // reuse the same camera thread to conserve resources - if (!mCameraThread) { - aRv = NS_NewThread(getter_AddRefs(mCameraThread)); - if (aRv.Failed()) { - return; - } + nsCOMPtr errorCallback = nullptr; + if (aOnError.WasPassed()) { + errorCallback = &aOnError.Value(); } - DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - - // Creating this object will trigger the onSuccess handler + // Creating this object will trigger the aOnSuccess callback + // (or the aOnError one, if it fails). nsRefPtr cameraControl = - new nsDOMCameraControl(cameraId, mCameraThread, - onSuccess, onError.WasPassed() ? onError.Value() : nullptr, mWindow); + new nsDOMCameraControl(cameraId, aInitialConfig, &aOnSuccess, errorCallback, mWindow); Register(cameraControl); } diff --git a/dom/camera/DOMCameraManager.h b/dom/camera/DOMCameraManager.h index 5758a475fd42..ac55937212cf 100644 --- a/dom/camera/DOMCameraManager.h +++ b/dom/camera/DOMCameraManager.h @@ -10,24 +10,24 @@ #include "mozilla/dom/BindingDeclarations.h" #include "nsCOMPtr.h" #include "nsAutoPtr.h" -#include "nsIThread.h" #include "nsIObserver.h" -#include "nsThreadUtils.h" #include "nsHashKeys.h" #include "nsWrapperCache.h" #include "nsWeakReference.h" #include "nsClassHashtable.h" -#include "nsIDOMCameraManager.h" #include "nsCycleCollectionParticipant.h" #include "mozilla/Attributes.h" -#include "nsPIDOMWindow.h" + +class nsPIDOMWindow; namespace mozilla { class ErrorResult; -class nsDOMCameraControl; -namespace dom { -class CameraSelector; -} + class nsDOMCameraControl; + namespace dom { + class CameraConfiguration; + class GetCameraCallback; + class CameraErrorCallback; + } } typedef nsTArray > CameraControls; @@ -52,13 +52,11 @@ public: void Register(mozilla::nsDOMCameraControl* aDOMCameraControl); void OnNavigation(uint64_t aWindowId); - nsresult GetNumberOfCameras(int32_t& aDeviceCount); - nsresult GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName); - // WebIDL - void GetCamera(const mozilla::dom::CameraSelector& aOptions, - nsICameraGetCameraCallback* aCallback, - const mozilla::dom::Optional& ErrorCallback, + void GetCamera(const nsAString& aCamera, + const mozilla::dom::CameraConfiguration& aOptions, + mozilla::dom::GetCameraCallback& aOnSuccess, + const mozilla::dom::Optional >& aOnError, mozilla::ErrorResult& aRv); void GetListOfCameras(nsTArray& aList, mozilla::ErrorResult& aRv); @@ -79,32 +77,12 @@ private: protected: uint64_t mWindowId; - nsCOMPtr mCameraThread; nsCOMPtr mWindow; /** - * 'mActiveWindows' is only ever accessed while in the main thread, + * 'sActiveWindows' is only ever accessed while in the Main Thread, * so it is not otherwise protected. */ static ::WindowTable* sActiveWindows; }; -class GetCameraTask : public nsRunnable -{ -public: - GetCameraTask(uint32_t aCameraId, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, nsIThread* aCameraThread) - : mCameraId(aCameraId) - , mOnSuccessCb(onSuccess) - , mOnErrorCb(onError) - , mCameraThread(aCameraThread) - { } - - NS_IMETHOD Run() MOZ_OVERRIDE; - -protected: - uint32_t mCameraId; - nsCOMPtr mOnSuccessCb; - nsCOMPtr mOnErrorCb; - nsCOMPtr mCameraThread; -}; - #endif // DOM_CAMERA_DOMCAMERAMANAGER_H diff --git a/dom/camera/DOMCameraPreview.cpp b/dom/camera/DOMCameraPreview.cpp deleted file mode 100644 index 903ceb3e653f..000000000000 --- a/dom/camera/DOMCameraPreview.cpp +++ /dev/null @@ -1,304 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "base/basictypes.h" -#include "Layers.h" -#include "VideoUtils.h" -#include "DOMCameraPreview.h" -#include "CameraCommon.h" -#include "nsGlobalWindow.h" -#include "nsIDocument.h" -#include "nsPIDOMWindow.h" - -using namespace mozilla; -using namespace mozilla::layers; - -/** - * 'PreviewControl' is a helper class that dispatches preview control - * events from the main thread. - * - * NS_NewRunnableMethod() can't be used because it AddRef()s the method's - * object, which can't be done off the main thread for cycle collection - * participants. - * - * Before using this class, 'aDOMPreview' must be appropriately AddRef()ed. - */ -class PreviewControl : public nsRunnable -{ -public: - enum { - START, - STOP, - STARTED, - STOPPED - }; - PreviewControl(DOMCameraPreview* aDOMPreview, uint32_t aControl) - : mDOMPreview(aDOMPreview) - , mControl(aControl) - { } - - NS_IMETHOD Run() - { - NS_ASSERTION(NS_IsMainThread(), "PreviewControl not run on main thread"); - - switch (mControl) { - case START: - mDOMPreview->Start(); - break; - - case STOP: - mDOMPreview->StopPreview(); - break; - - case STARTED: - mDOMPreview->SetStateStarted(); - break; - - case STOPPED: - mDOMPreview->SetStateStopped(); - break; - - default: - DOM_CAMERA_LOGE("PreviewControl: invalid control %d\n", mControl); - break; - } - - return NS_OK; - } - -protected: - /** - * This must be a raw pointer because this class is not created on the - * main thread, and DOMCameraPreview is not threadsafe. Prior to - * issuing a preview control event, the caller must ensure that - * mDOMPreview will not disappear. - */ - DOMCameraPreview* mDOMPreview; - uint32_t mControl; -}; - -class DOMCameraPreviewListener : public MediaStreamListener -{ -public: - DOMCameraPreviewListener(DOMCameraPreview* aDOMPreview) : - mDOMPreview(aDOMPreview) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - ~DOMCameraPreviewListener() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - void NotifyConsumptionChanged(MediaStreamGraph* aGraph, Consumption aConsuming) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - -#ifdef PR_LOGGING - const char* state; - - switch (aConsuming) { - case NOT_CONSUMED: - state = "not consuming"; - break; - - case CONSUMED: - state = "consuming"; - break; - - default: - state = "unknown"; - break; - } - - DOM_CAMERA_LOGA("camera viewfinder is %s\n", state); -#endif - nsCOMPtr previewControl; - - switch (aConsuming) { - case NOT_CONSUMED: - previewControl = new PreviewControl(mDOMPreview, PreviewControl::STOP); - break; - - case CONSUMED: - previewControl = new PreviewControl(mDOMPreview, PreviewControl::START); - break; - - default: - return; - } - - nsresult rv = NS_DispatchToMainThread(previewControl); - if (NS_FAILED(rv)) { - DOM_CAMERA_LOGE("Failed to dispatch preview control (%d)!\n", rv); - } - } - -protected: - // Raw pointer; if we exist, 'mDOMPreview' exists as well - DOMCameraPreview* mDOMPreview; -}; - -DOMCameraPreview::DOMCameraPreview(nsGlobalWindow* aWindow, - ICameraControl* aCameraControl, - uint32_t aWidth, uint32_t aHeight, - uint32_t aFrameRate) - : DOMMediaStream() - , mState(STOPPED) - , mWidth(aWidth) - , mHeight(aHeight) - , mFramesPerSecond(aFrameRate) - , mFrameCount(0) - , mCameraControl(aCameraControl) -{ - DOM_CAMERA_LOGT("%s:%d : this=%p : mWidth=%d, mHeight=%d, mFramesPerSecond=%d\n", __func__, __LINE__, this, mWidth, mHeight, mFramesPerSecond); - - mImageContainer = LayerManager::CreateImageContainer(); - mWindow = aWindow; - mInput = new CameraPreviewMediaStream(this); - mStream = mInput; - - mListener = new DOMCameraPreviewListener(this); - mInput->AddListener(mListener); - - if (aWindow->GetExtantDoc()) { - CombineWithPrincipal(aWindow->GetExtantDoc()->NodePrincipal()); - } -} - -DOMCameraPreview::~DOMCameraPreview() -{ - DOM_CAMERA_LOGT("%s:%d : this=%p, mListener=%p\n", __func__, __LINE__, this, mListener); - mInput->RemoveListener(mListener); -} - -bool -DOMCameraPreview::HaveEnoughBuffered() -{ - return true; -} - -bool -DOMCameraPreview::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder) -{ - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - if (!aBuffer || !aBuilder) { - return false; - } - if (mState != STARTED) { - return false; - } - - nsRefPtr image = mImageContainer->CreateImage(aFormat); - aBuilder(image, aBuffer, mWidth, mHeight); - - mInput->SetCurrentFrame(gfxIntSize(mWidth, mHeight), image); - return true; -} - -void -DOMCameraPreview::Start() -{ - NS_ASSERTION(NS_IsMainThread(), "Start() not called from main thread"); - if (mState != STOPPED) { - return; - } - - DOM_CAMERA_LOGI("Starting preview stream\n"); - - /** - * Add a reference to ourselves to make sure we stay alive while - * the preview is running, as the CameraControlImpl object holds a - * weak reference to us. - * - * This reference is removed in SetStateStopped(). - */ - NS_ADDREF_THIS(); - DOM_CAMERA_SETSTATE(STARTING); - mCameraControl->StartPreview(this); -} - -void -DOMCameraPreview::SetStateStarted() -{ - NS_ASSERTION(NS_IsMainThread(), "SetStateStarted() not called from main thread"); - - DOM_CAMERA_SETSTATE(STARTED); - DOM_CAMERA_LOGI("Preview stream started\n"); -} - -void -DOMCameraPreview::Started() -{ - if (mState != STARTING) { - return; - } - - DOM_CAMERA_LOGI("Dispatching preview stream started\n"); - nsCOMPtr started = new PreviewControl(this, PreviewControl::STARTED); - nsresult rv = NS_DispatchToMainThread(started); - if (NS_FAILED(rv)) { - DOM_CAMERA_LOGE("failed to set statrted state (%d), POTENTIAL MEMORY LEAK!\n", rv); - } -} - -void -DOMCameraPreview::StopPreview() -{ - NS_ASSERTION(NS_IsMainThread(), "StopPreview() not called from main thread"); - if (mState != STARTED) { - return; - } - - DOM_CAMERA_LOGI("Stopping preview stream\n"); - DOM_CAMERA_SETSTATE(STOPPING); - mCameraControl->StopPreview(); - //mInput->EndTrack(TRACK_VIDEO); - //mInput->Finish(); -} - -void -DOMCameraPreview::SetStateStopped() -{ - NS_ASSERTION(NS_IsMainThread(), "SetStateStopped() not called from main thread"); - - // see bug 809259 and bug 817367. - if (mState != STOPPING) { - //mInput->EndTrack(TRACK_VIDEO); - //mInput->Finish(); - } - DOM_CAMERA_SETSTATE(STOPPED); - DOM_CAMERA_LOGI("Preview stream stopped\n"); - - /** - * Only remove the reference added in Start() once the preview - * has stopped completely. - */ - NS_RELEASE_THIS(); -} - -void -DOMCameraPreview::Stopped(bool aForced) -{ - if (mState != STOPPING && !aForced) { - return; - } - - mInput->ClearCurrentFrame(); - - DOM_CAMERA_LOGI("Dispatching preview stream stopped\n"); - nsCOMPtr stopped = new PreviewControl(this, PreviewControl::STOPPED); - nsresult rv = NS_DispatchToMainThread(stopped); - if (NS_FAILED(rv)) { - DOM_CAMERA_LOGE("failed to decrement reference count (%d), MEMORY LEAK!\n", rv); - } -} - -void -DOMCameraPreview::Error() -{ - DOM_CAMERA_LOGE("Error occurred changing preview state!\n"); - Stopped(true); -} diff --git a/dom/camera/DOMCameraPreview.h b/dom/camera/DOMCameraPreview.h deleted file mode 100644 index 96ffad40ae03..000000000000 --- a/dom/camera/DOMCameraPreview.h +++ /dev/null @@ -1,99 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef DOM_CAMERA_DOMCAMERAPREVIEW_H -#define DOM_CAMERA_DOMCAMERAPREVIEW_H - -#include "nsCycleCollectionParticipant.h" -#include "MediaStreamGraph.h" -#include "StreamBuffer.h" -#include "ICameraControl.h" -#include "DOMMediaStream.h" -#include "CameraPreviewMediaStream.h" -#include "CameraCommon.h" - -class nsGlobalWindow; - -namespace mozilla { - -typedef void (*FrameBuilder)(mozilla::layers::Image* aImage, void* aBuffer, uint32_t aWidth, uint32_t aHeight); - -/** - * DOMCameraPreview is only exposed to the DOM as an nsDOMMediaStream, - * which is a cycle-collection participant already, and we don't - * add any traceable fields here, so we don't need to declare any - * more cycle-collection goop. - */ -class DOMCameraPreview : public DOMMediaStream -{ -protected: - enum { TRACK_VIDEO = 1 }; - -public: - DOMCameraPreview(nsGlobalWindow* aWindow, ICameraControl* aCameraControl, - uint32_t aWidth, uint32_t aHeight, uint32_t aFramesPerSecond = 30); - - bool ReceiveFrame(void* aBuffer, ImageFormat aFormat, mozilla::FrameBuilder aBuilder); - bool HaveEnoughBuffered(); - - void Start(); // called by the MediaStreamListener to start preview - void Started(); // called by the CameraControl when preview is started - void StopPreview(); // called by the MediaStreamListener to stop preview - void Stopped(bool aForced = false); - // called by the CameraControl when preview is stopped - void Error(); // something went wrong, NS_RELEASE needed - - void SetStateStarted(); - void SetStateStopped(); - -protected: - virtual ~DOMCameraPreview(); - - enum { - STOPPED, - STARTING, - STARTED, - STOPPING - }; - uint32_t mState; - - // Helper function, used in conjunction with the macro below, to make - // it easy to track state changes, which must happen only on the main - // thread. - void - SetState(uint32_t aNewState, const char* aFileOrFunc, int aLine) - { -#ifdef PR_LOGGING - const char* states[] = { "stopped", "starting", "started", "stopping" }; - MOZ_ASSERT(mState < sizeof(states) / sizeof(states[0])); - MOZ_ASSERT(aNewState < sizeof(states) / sizeof(states[0])); - DOM_CAMERA_LOGI("SetState: (this=%p) '%s' --> '%s' : %s:%d\n", this, states[mState], states[aNewState], aFileOrFunc, aLine); -#endif - - NS_ASSERTION(NS_IsMainThread(), "Preview state set OFF OF main thread!"); - mState = aNewState; - } - - uint32_t mWidth; - uint32_t mHeight; - uint32_t mFramesPerSecond; - CameraPreviewMediaStream* mInput; - nsRefPtr mImageContainer; - VideoSegment mVideoSegment; - uint32_t mFrameCount; - nsRefPtr mCameraControl; - - // Raw pointer; AddListener() keeps the reference for us - MediaStreamListener* mListener; - -private: - DOMCameraPreview(const DOMCameraPreview&) MOZ_DELETE; - DOMCameraPreview& operator=(const DOMCameraPreview&) MOZ_DELETE; -}; - -} // namespace mozilla - -#define DOM_CAMERA_SETSTATE(newState) SetState((newState), __func__, __LINE__) - -#endif // DOM_CAMERA_DOMCAMERAPREVIEW_H diff --git a/dom/camera/FallbackCameraCapabilities.cpp b/dom/camera/FallbackCameraCapabilities.cpp index 808f67912635..568a52ef7f52 100644 --- a/dom/camera/FallbackCameraCapabilities.cpp +++ b/dom/camera/FallbackCameraCapabilities.cpp @@ -2,9 +2,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "nsDOMClassInfoID.h" -#include "DOMCameraControl.h" #include "DOMCameraCapabilities.h" +#include "DOMCameraControl.h" #include "CameraCommon.h" using namespace mozilla; diff --git a/dom/camera/FallbackCameraControl.cpp b/dom/camera/FallbackCameraControl.cpp index 4a74d071c0ba..5b33b11a77ff 100644 --- a/dom/camera/FallbackCameraControl.cpp +++ b/dom/camera/FallbackCameraControl.cpp @@ -2,215 +2,74 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "DOMCameraControl.h" #include "CameraControlImpl.h" using namespace mozilla; using namespace mozilla::dom; namespace mozilla { -class RecorderProfileManager; + class RecorderProfileManager; + + namespace layers { + class GraphicBufferLocked; + } } /** - * Fallback camera control subclass. Can be used as a template for the + * Fallback camera control subclass. Can be used as a template for the * definition of new camera support classes. */ -class nsFallbackCameraControl : public CameraControlImpl +class FallbackCameraControl : public CameraControlImpl { public: - nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId); + FallbackCameraControl(uint32_t aCameraId) : CameraControlImpl(aCameraId) { } - const char* GetParameter(const char* aKey); - const char* GetParameterConstChar(uint32_t aKey); - double GetParameterDouble(uint32_t aKey); - int32_t GetParameterInt32(uint32_t aKey); - void GetParameter(uint32_t aKey, nsTArray& aRegions); - void GetParameter(uint32_t aKey, idl::CameraSize& aSize); - void SetParameter(const char* aKey, const char* aValue); - void SetParameter(uint32_t aKey, const char* aValue); - void SetParameter(uint32_t aKey, double aValue); - void SetParameter(uint32_t aKey, const nsTArray& aRegions); - void SetParameter(uint32_t aKey, const idl::CameraSize& aSize); - nsresult GetVideoSizes(nsTArray& aVideoSizes); - nsresult PushParameters(); + void OnAutoFocusComplete(bool aSuccess); + void OnTakePictureComplete(uint8_t* aData, uint32_t aLength) { } + void OnTakePictureError() { } + void OnNewPreviewFrame(layers::GraphicBufferLocked* aBuffer) { } + void OnRecorderEvent(int msg, int ext1, int ext2) { } + void OnError(CameraControlListener::CameraErrorContext aWhere, + CameraControlListener::CameraError aError) { } + + virtual nsresult Set(uint32_t aKey, const nsAString& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Get(uint32_t aKey, nsAString& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Set(uint32_t aKey, double aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Get(uint32_t aKey, double& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Set(uint32_t aKey, int32_t aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Get(uint32_t aKey, int32_t& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Set(uint32_t aKey, int64_t aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Get(uint32_t aKey, int64_t& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Set(uint32_t aKey, const Size& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Get(uint32_t aKey, Size& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Set(uint32_t aKey, const nsTArray& aRegions) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Get(uint32_t aKey, nsTArray& aRegions) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + + virtual nsresult SetLocation(const Position& aLocation) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + + virtual nsresult Get(uint32_t aKey, nsTArray& aSizes) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Get(uint32_t aKey, nsTArray& aValues) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + virtual nsresult Get(uint32_t aKey, nsTArray& aValues) MOZ_OVERRIDE { return NS_ERROR_FAILURE; } + + nsresult PushParameters() { return NS_ERROR_FAILURE; } + nsresult PullParameters() { return NS_ERROR_FAILURE; } protected: - ~nsFallbackCameraControl(); + ~FallbackCameraControl(); - nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream); - nsresult StartPreviewImpl(StartPreviewTask* aStartPreview); - nsresult StopPreviewImpl(StopPreviewTask* aStopPreview); - nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus); - nsresult TakePictureImpl(TakePictureTask* aTakePicture); - nsresult StartRecordingImpl(StartRecordingTask* aStartRecording); - nsresult StopRecordingImpl(StopRecordingTask* aStopRecording); - nsresult PushParametersImpl(); - nsresult PullParametersImpl(); - already_AddRefed GetRecorderProfileManagerImpl(); + virtual nsresult StartPreviewImpl() { return NS_ERROR_FAILURE; } + virtual nsresult StopPreviewImpl() { return NS_ERROR_FAILURE; } + virtual nsresult AutoFocusImpl(bool aCancelExistingCall) { return NS_ERROR_FAILURE; } + virtual nsresult TakePictureImpl() { return NS_ERROR_FAILURE; } + virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor, + const StartRecordingOptions* aOptions = nullptr) + { return NS_ERROR_FAILURE; } + virtual nsresult StopRecordingImpl() { return NS_ERROR_FAILURE; } + virtual nsresult PushParametersImpl() { return NS_ERROR_FAILURE; } + virtual nsresult PullParametersImpl() { return NS_ERROR_FAILURE; } + virtual already_AddRefed GetRecorderProfileManagerImpl() { return nullptr; } private: - nsFallbackCameraControl(const nsFallbackCameraControl&) MOZ_DELETE; - nsFallbackCameraControl& operator=(const nsFallbackCameraControl&) MOZ_DELETE; + FallbackCameraControl(const FallbackCameraControl&) MOZ_DELETE; + FallbackCameraControl& operator=(const FallbackCameraControl&) MOZ_DELETE; }; - -/** - * Stub implementation of the DOM-facing camera control constructor. - * - * This should never get called--it exists to keep the linker happy; if - * implemented, it should construct (e.g.) nsFallbackCameraControl and - * store a reference in the 'mCameraControl' member (which is why it is - * defined here). - */ -nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, nsPIDOMWindow* aWindow) : - mWindow(aWindow) -{ - MOZ_ASSERT(aWindow, "shouldn't be created with null window!"); - SetIsDOMBinding(); -} - -/** - * Stub implemetations of the fallback camera control. - * - * None of these should ever get called--they exist to keep the linker happy, - * and may be used as templates for new camera support classes. - */ -nsFallbackCameraControl::nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId) - : CameraControlImpl(aCameraId, aCameraThread, aWindowId) -{ -} - -nsFallbackCameraControl::~nsFallbackCameraControl() -{ -} - -const char* -nsFallbackCameraControl::GetParameter(const char* aKey) -{ - return nullptr; -} - -const char* -nsFallbackCameraControl::GetParameterConstChar(uint32_t aKey) -{ - return nullptr; -} - -double -nsFallbackCameraControl::GetParameterDouble(uint32_t aKey) -{ - return NAN; -} - -int32_t -nsFallbackCameraControl::GetParameterInt32(uint32_t aKey) -{ - return 0; -} - -void -nsFallbackCameraControl::GetParameter(uint32_t aKey, nsTArray& aRegions) -{ -} - -void -nsFallbackCameraControl::GetParameter(uint32_t aKey, idl::CameraSize& aSize) -{ -} - -void -nsFallbackCameraControl::SetParameter(const char* aKey, const char* aValue) -{ -} - -void -nsFallbackCameraControl::SetParameter(uint32_t aKey, const char* aValue) -{ -} - -void -nsFallbackCameraControl::SetParameter(uint32_t aKey, double aValue) -{ -} - -void -nsFallbackCameraControl::SetParameter(uint32_t aKey, const nsTArray& aRegions) -{ -} - -void -nsFallbackCameraControl::SetParameter(uint32_t aKey, const idl::CameraSize& aSize) -{ -} - -nsresult -nsFallbackCameraControl::PushParameters() -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -nsresult -nsFallbackCameraControl::GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -nsresult -nsFallbackCameraControl::StartPreviewImpl(StartPreviewTask* aGetPreviewStream) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -nsresult -nsFallbackCameraControl::StopPreviewImpl(StopPreviewTask* aGetPreviewStream) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -nsresult -nsFallbackCameraControl::AutoFocusImpl(AutoFocusTask* aAutoFocus) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -nsresult -nsFallbackCameraControl::TakePictureImpl(TakePictureTask* aTakePicture) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -nsresult -nsFallbackCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -nsresult -nsFallbackCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -nsresult -nsFallbackCameraControl::PushParametersImpl() -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -nsresult -nsFallbackCameraControl::PullParametersImpl() -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -nsresult -nsFallbackCameraControl::GetVideoSizes(nsTArray& aVideoSizes) -{ - return NS_ERROR_NOT_IMPLEMENTED; -} - -already_AddRefed -nsFallbackCameraControl::GetRecorderProfileManagerImpl() -{ - return nullptr; -} diff --git a/dom/camera/FallbackCameraManager.cpp b/dom/camera/FallbackCameraManager.cpp index d097ba86a0ae..513db97d35c6 100644 --- a/dom/camera/FallbackCameraManager.cpp +++ b/dom/camera/FallbackCameraManager.cpp @@ -2,27 +2,31 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "DOMCameraManager.h" - -#include "mozilla/ErrorResult.h" +#include "ICameraControl.h" using namespace mozilla; -// From nsDOMCameraManager. +// From ICameraControl. nsresult -nsDOMCameraManager::GetNumberOfCameras(int32_t& aDeviceCount) +ICameraControl::GetNumberOfCameras(int32_t& aDeviceCount) { return NS_ERROR_NOT_IMPLEMENTED; }; nsresult -nsDOMCameraManager::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName) +ICameraControl::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName) { return NS_ERROR_NOT_IMPLEMENTED; } -void -nsDOMCameraManager::GetListOfCameras(nsTArray& aList, ErrorResult& aRv) +nsresult +ICameraControl::GetListOfCameras(nsTArray& aList) { - aRv.Throw(NS_ERROR_NOT_IMPLEMENTED); + return NS_ERROR_NOT_IMPLEMENTED; +} + +already_AddRefed +ICameraControl::Create(uint32_t aCameraId, const Configuration* aInitialConfig) +{ + return nullptr; } diff --git a/dom/camera/GonkCameraControl.cpp b/dom/camera/GonkCameraControl.cpp index 51ae420301b1..de4973b8ac0b 100644 --- a/dom/camera/GonkCameraControl.cpp +++ b/dom/camera/GonkCameraControl.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Mozilla Foundation + * Copyright (C) 2012-2014 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,6 +14,7 @@ * limitations under the License. */ +#include "GonkCameraControl.h" #include #include #include @@ -23,42 +24,29 @@ #include "base/basictypes.h" #include "camera/CameraParameters.h" #include "nsCOMPtr.h" -#include "nsDOMClassInfo.h" #include "nsMemory.h" #include "nsThread.h" #include #include "mozilla/FileUtils.h" #include "mozilla/Services.h" +#include "mozilla/unused.h" #include "nsAlgorithm.h" #include #include "nsPrintfCString.h" #include "nsIObserverService.h" #include "nsIVolume.h" #include "nsIVolumeService.h" -#include "DOMCameraManager.h" +#include "AutoRwLock.h" #include "GonkCameraHwMgr.h" -#include "DOMCameraCapabilities.h" -#include "DOMCameraControl.h" #include "GonkRecorderProfiles.h" -#include "GonkCameraControl.h" #include "CameraCommon.h" +#include "GonkCameraParameters.h" #include "DeviceStorageFileDescriptor.h" using namespace mozilla; -using namespace mozilla::dom; using namespace mozilla::layers; +using namespace mozilla::gfx; using namespace android; -using mozilla::gfx::IntSize; - -/** - * See bug 783682. Most camera implementations, despite claiming they - * support 'yuv420p' as a preview format, actually ignore this setting - * and return 'yuv420sp' data anyway. We have come across a new implementation - * that, while reporting that 'yuv420p' is supported *and* has been accepted, - * still returns the frame data in 'yuv420sp' anyway. So for now, since - * everyone seems to return this format, we just force it. - */ -#define FORCE_PREVIEW_FORMAT_YUV420SP 1 #define RETURN_IF_NO_CAMERA_HW() \ do { \ @@ -68,179 +56,111 @@ using mozilla::gfx::IntSize; } \ } while(0) -static const char* getKeyText(uint32_t aKey) -{ - switch (aKey) { - case CAMERA_PARAM_EFFECT: - return CameraParameters::KEY_EFFECT; - case CAMERA_PARAM_WHITEBALANCE: - return CameraParameters::KEY_WHITE_BALANCE; - case CAMERA_PARAM_SCENEMODE: - return CameraParameters::KEY_SCENE_MODE; - case CAMERA_PARAM_FLASHMODE: - return CameraParameters::KEY_FLASH_MODE; - case CAMERA_PARAM_FOCUSMODE: - return CameraParameters::KEY_FOCUS_MODE; - case CAMERA_PARAM_ZOOM: - return CameraParameters::KEY_ZOOM; - case CAMERA_PARAM_METERINGAREAS: - return CameraParameters::KEY_METERING_AREAS; - case CAMERA_PARAM_FOCUSAREAS: - return CameraParameters::KEY_FOCUS_AREAS; - case CAMERA_PARAM_FOCALLENGTH: - return CameraParameters::KEY_FOCAL_LENGTH; - case CAMERA_PARAM_FOCUSDISTANCENEAR: - return CameraParameters::KEY_FOCUS_DISTANCES; - case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM: - return CameraParameters::KEY_FOCUS_DISTANCES; - case CAMERA_PARAM_FOCUSDISTANCEFAR: - return CameraParameters::KEY_FOCUS_DISTANCES; - case CAMERA_PARAM_EXPOSURECOMPENSATION: - return CameraParameters::KEY_EXPOSURE_COMPENSATION; - case CAMERA_PARAM_PICTURESIZE: - return CameraParameters::KEY_PICTURE_SIZE; - case CAMERA_PARAM_THUMBNAILQUALITY: - return CameraParameters::KEY_JPEG_THUMBNAIL_QUALITY; - - case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES: - return CameraParameters::KEY_SUPPORTED_PREVIEW_SIZES; - case CAMERA_PARAM_SUPPORTED_VIDEOSIZES: - return CameraParameters::KEY_SUPPORTED_VIDEO_SIZES; - case CAMERA_PARAM_SUPPORTED_PICTURESIZES: - return CameraParameters::KEY_SUPPORTED_PICTURE_SIZES; - case CAMERA_PARAM_SUPPORTED_PICTUREFORMATS: - return CameraParameters::KEY_SUPPORTED_PICTURE_FORMATS; - case CAMERA_PARAM_SUPPORTED_WHITEBALANCES: - return CameraParameters::KEY_SUPPORTED_WHITE_BALANCE; - case CAMERA_PARAM_SUPPORTED_SCENEMODES: - return CameraParameters::KEY_SUPPORTED_SCENE_MODES; - case CAMERA_PARAM_SUPPORTED_EFFECTS: - return CameraParameters::KEY_SUPPORTED_EFFECTS; - case CAMERA_PARAM_SUPPORTED_FLASHMODES: - return CameraParameters::KEY_SUPPORTED_FLASH_MODES; - case CAMERA_PARAM_SUPPORTED_FOCUSMODES: - return CameraParameters::KEY_SUPPORTED_FOCUS_MODES; - case CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS: - return CameraParameters::KEY_MAX_NUM_FOCUS_AREAS; - case CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS: - return CameraParameters::KEY_MAX_NUM_METERING_AREAS; - case CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION: - return CameraParameters::KEY_MIN_EXPOSURE_COMPENSATION; - case CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION: - return CameraParameters::KEY_MAX_EXPOSURE_COMPENSATION; - case CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP: - return CameraParameters::KEY_EXPOSURE_COMPENSATION_STEP; - case CAMERA_PARAM_SUPPORTED_ZOOM: - return CameraParameters::KEY_ZOOM_SUPPORTED; - case CAMERA_PARAM_SUPPORTED_ZOOMRATIOS: - return CameraParameters::KEY_ZOOM_RATIOS; - case CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES: - return CameraParameters::KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES; - default: - return nullptr; - } -} - -// nsDOMCameraControl implementation-specific constructor -nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, nsPIDOMWindow* aWindow) - : mDOMCapabilities(nullptr), mWindow(aWindow) -{ - MOZ_ASSERT(aWindow, "shouldn't be created with null window!"); - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - SetIsDOMBinding(); - - /** - * nsDOMCameraControl is a cycle-collection participant, which means it is - * not threadsafe--so we need to bump up its reference count here to make - * sure that it exists long enough to be initialized. - * - * Once it is initialized, the GetCameraResult main-thread runnable will - * decrement it again to make sure it can be cleaned up. - * - * nsGonkCameraControl MUST NOT hold a strong reference to this - * nsDOMCameraControl or memory will leak! - */ - NS_ADDREF_THIS(); - nsRefPtr control = new nsGonkCameraControl(aCameraId, aCameraThread, this, onSuccess, onError, aWindow->WindowID()); - control->DispatchInit(this, onSuccess, onError, aWindow->WindowID()); - mCameraControl = control; -} - -// Gonk-specific CameraControl implementation. - -// Initialize nsGonkCameraControl instance--runs on camera thread. -class InitGonkCameraControl : public nsRunnable -{ -public: - InitGonkCameraControl(nsGonkCameraControl* aCameraControl, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId) - : mCameraControl(aCameraControl) - , mDOMCameraControl(aDOMCameraControl) - , mOnSuccessCb(new nsMainThreadPtrHolder(onSuccess)) - , mOnErrorCb(new nsMainThreadPtrHolder(onError)) - , mWindowId(aWindowId) - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - ~InitGonkCameraControl() - { - DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - } - - NS_IMETHOD Run() - { - nsresult rv = mCameraControl->Init(); - return mDOMCameraControl->Result(rv, mOnSuccessCb, mOnErrorCb, mWindowId); - } - - nsRefPtr mCameraControl; - // Raw pointer to DOM-facing camera control--it must NS_ADDREF itself for us - nsDOMCameraControl* mDOMCameraControl; - nsMainThreadPtrHandle mOnSuccessCb; - nsMainThreadPtrHandle mOnErrorCb; - uint64_t mWindowId; -}; - // Construct nsGonkCameraControl on the main thread. -nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId) - : CameraControlImpl(aCameraId, aCameraThread, aWindowId) - , mExposureCompensationMin(0.0) - , mExposureCompensationStep(0.0) - , mDeferConfigUpdate(false) - , mWidth(0) - , mHeight(0) - , mLastPictureWidth(0) - , mLastPictureHeight(0) - , mLastThumbnailWidth(0) - , mLastThumbnailHeight(0) -#if !FORCE_PREVIEW_FORMAT_YUV420SP - , mFormat(PREVIEW_FORMAT_UNKNOWN) -#else - , mFormat(PREVIEW_FORMAT_YUV420SP) -#endif - , mFps(30) - , mDiscardedFrameCount(0) +nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId) + : CameraControlImpl(aCameraId) + , mLastPictureSize({0, 0}) + , mLastThumbnailSize({0, 0}) + , mPreviewFps(30) + , mResumePreviewAfterTakingPicture(false) // XXXmikeh - see bug 950102 + , mDeferConfigUpdate(0) , mMediaProfiles(nullptr) , mRecorder(nullptr) , mProfileManager(nullptr) , mRecorderProfile(nullptr) , mVideoFile(nullptr) + , mReentrantMonitor("GonkCameraControl::OnTakePictureMonitor") { // Constructor runs on the main thread... DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); - mRwLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraControl.Parameters.Lock"); -} - -void nsGonkCameraControl::DispatchInit(nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId) -{ - // ...but initialization is carried out on the camera thread. - nsCOMPtr init = new InitGonkCameraControl(this, aDOMCameraControl, onSuccess, onError, aWindowId); - mCameraThread->Dispatch(init, NS_DISPATCH_NORMAL); + mImageContainer = LayerManager::CreateImageContainer(); } nsresult -nsGonkCameraControl::Init() +nsGonkCameraControl::Init(const Configuration* aInitialConfig) { + class InitGonkCameraControl : public nsRunnable + { + public: + InitGonkCameraControl(nsGonkCameraControl* aCameraControl, + const Configuration* aConfig) + : mCameraControl(aCameraControl) + , mHaveInitialConfig(false) + { + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + if (aConfig) { + mConfig = *aConfig; + mHaveInitialConfig = true; + } + } + + ~InitGonkCameraControl() + { + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + } + + /** + * For initialization, we try to return the camera control to the upper + * upper layer (i.e. the DOM) as quickly as possible. To do this, the + * camera is initialized in the following stages: + * + * 0. InitImpl() initializes the hardware; + * 1. SetConfigurationInternal() does the minimal configuration + * required so that we can start the preview -and- report a valid + * configuration to the upper layer; + * 2. OnHardwareStateChange() reports that the hardware is ready, + * which the upper layer can (and does) use to return the camera + * control object; + * 3. StartPreviewImpl() starts the flow of preview frames from the + * camera hardware. + * + * The intent of the above flow is to let the Main Thread do as much work + * up-front as possible without waiting for blocking Camera Thread calls + * to complete. + */ + NS_IMETHODIMP + Run() MOZ_OVERRIDE + { + nsresult rv = mCameraControl->InitImpl(); + if (NS_FAILED(rv)) { + mCameraControl->OnError(CameraControlListener::kInGetCamera, + CameraControlListener::kErrorInitFailed); + // The hardware failed to initialize, so close it up + mCameraControl->ReleaseHardware(); + return rv; + } + + if (mHaveInitialConfig) { + rv = mCameraControl->SetConfigurationInternal(mConfig); + if (NS_FAILED(rv)) { + mCameraControl->OnError(CameraControlListener::kInGetCamera, + CameraControlListener::kErrorInvalidConfiguration); + // The initial configuration failed, close up the hardware + mCameraControl->ReleaseHardware(); + return rv; + } + } + + mCameraControl->OnHardwareStateChange(CameraControlListener::kHardwareOpen); + return mCameraControl->StartPreviewImpl(); + } + + protected: + nsRefPtr mCameraControl; + Configuration mConfig; + bool mHaveInitialConfig; + }; + + // Initialization is carried out on the camera thread. + return mCameraThread->Dispatch( + new InitGonkCameraControl(this, aInitialConfig), NS_DISPATCH_NORMAL); +} + +nsresult +nsGonkCameraControl::InitImpl() +{ + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); + mCameraHw = GonkCameraHardware::Connect(this, mCameraId); if (!mCameraHw.get()) { DOM_CAMERA_LOGE("Failed to connect to camera %d (this=%p)\n", mCameraId, this); @@ -252,62 +172,37 @@ nsGonkCameraControl::Init() // Initialize our camera configuration database. PullParametersImpl(); - // Try to set preferred image format and frame rate -#if !FORCE_PREVIEW_FORMAT_YUV420SP - DOM_CAMERA_LOGI("Camera preview formats: %s\n", mParams.get(mParams.KEY_SUPPORTED_PREVIEW_FORMATS)); - const char* const PREVIEW_FORMAT = "yuv420p"; - const char* const BAD_PREVIEW_FORMAT = "yuv420sp"; - mParams.setPreviewFormat(PREVIEW_FORMAT); - mParams.setPreviewFrameRate(mFps); -#else - mParams.setPreviewFormat("yuv420sp"); - mParams.setPreviewFrameRate(mFps); -#endif + // Set preferred preview frame format. + mParams.Set(CAMERA_PARAM_PREVIEWFORMAT, NS_LITERAL_STRING("yuv420sp")); PushParametersImpl(); - // Check that our settings stuck - PullParametersImpl(); -#if !FORCE_PREVIEW_FORMAT_YUV420SP - const char* format = mParams.getPreviewFormat(); - if (strcmp(format, PREVIEW_FORMAT) == 0) { - mFormat = PREVIEW_FORMAT_YUV420P; /* \o/ */ - } else if (strcmp(format, BAD_PREVIEW_FORMAT) == 0) { - mFormat = PREVIEW_FORMAT_YUV420SP; - DOM_CAMERA_LOGA("Camera ignored our request for '%s' preview, will have to convert (from %d)\n", PREVIEW_FORMAT, mFormat); - } else { - mFormat = PREVIEW_FORMAT_UNKNOWN; - DOM_CAMERA_LOGE("Camera ignored our request for '%s' preview, returned UNSUPPORTED format '%s'\n", PREVIEW_FORMAT, format); - } -#endif - - // Check the frame rate and log if the camera ignored our setting - uint32_t fps = mParams.getPreviewFrameRate(); - if (fps != mFps) { - DOM_CAMERA_LOGA("We asked for %d fps but camera returned %d fps, using that", mFps, fps); - mFps = fps; - } - // Grab any other settings we'll need later. - mExposureCompensationMin = mParams.getFloat(mParams.KEY_MIN_EXPOSURE_COMPENSATION); - mExposureCompensationStep = mParams.getFloat(mParams.KEY_EXPOSURE_COMPENSATION_STEP); - mMaxMeteringAreas = mParams.getInt(mParams.KEY_MAX_NUM_METERING_AREAS); - mMaxFocusAreas = mParams.getInt(mParams.KEY_MAX_NUM_FOCUS_AREAS); - mLastThumbnailWidth = mParams.getInt(mParams.KEY_JPEG_THUMBNAIL_WIDTH); - mLastThumbnailHeight = mParams.getInt(mParams.KEY_JPEG_THUMBNAIL_HEIGHT); + mParams.Get(CAMERA_PARAM_PICTURE_FILEFORMAT, mFileFormat); + mParams.Get(CAMERA_PARAM_THUMBNAILSIZE, mLastThumbnailSize); - int w; - int h; - mParams.getPictureSize(&w, &h); - MOZ_ASSERT(w > 0 && h > 0); // make sure the driver returns sane values - mLastPictureWidth = static_cast(w); - mLastPictureHeight = static_cast(h); + // The emulator's camera returns -1 for these values; bump them up to 0 + int areas; + mParams.Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, areas); + mCurrentConfiguration.mMaxMeteringAreas = areas != -1 ? areas : 0; + mParams.Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas); + mCurrentConfiguration.mMaxFocusAreas = areas != -1 ? areas : 0; - DOM_CAMERA_LOGI(" - minimum exposure compensation: %f\n", mExposureCompensationMin); - DOM_CAMERA_LOGI(" - exposure compensation step: %f\n", mExposureCompensationStep); - DOM_CAMERA_LOGI(" - maximum metering areas: %d\n", mMaxMeteringAreas); - DOM_CAMERA_LOGI(" - maximum focus areas: %d\n", mMaxFocusAreas); - DOM_CAMERA_LOGI(" - default picture size: %u x %u\n", mLastPictureWidth, mLastPictureHeight); - DOM_CAMERA_LOGI(" - default thumbnail size: %u x %u\n", mLastThumbnailWidth, mLastThumbnailHeight); + mParams.Get(CAMERA_PARAM_PICTURE_SIZE, mLastPictureSize); + mParams.Get(CAMERA_PARAM_PREVIEWSIZE, mCurrentConfiguration.mPreviewSize); + mParams.Get(CAMERA_PARAM_VIDEOSIZE, mLastRecorderSize); + + DOM_CAMERA_LOGI(" - maximum metering areas: %u\n", mCurrentConfiguration.mMaxMeteringAreas); + DOM_CAMERA_LOGI(" - maximum focus areas: %u\n", mCurrentConfiguration.mMaxFocusAreas); + DOM_CAMERA_LOGI(" - default picture size: %u x %u\n", + mLastPictureSize.width, mLastPictureSize.height); + DOM_CAMERA_LOGI(" - default thumbnail size: %u x %u\n", + mLastThumbnailSize.width, mLastThumbnailSize.height); + DOM_CAMERA_LOGI(" - default preview size: %u x %u\n", + mCurrentConfiguration.mPreviewSize.width, mCurrentConfiguration.mPreviewSize.height); + DOM_CAMERA_LOGI(" - default video recorder size: %u x %u\n", + mLastRecorderSize.width, mLastRecorderSize.height); + DOM_CAMERA_LOGI(" - default picture file format: %s\n", + NS_ConvertUTF16toUTF8(mFileFormat).get()); return NS_OK; } @@ -316,268 +211,105 @@ nsGonkCameraControl::~nsGonkCameraControl() { DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraHw = %p\n", __func__, __LINE__, this, mCameraHw.get()); - ReleaseHardwareImpl(nullptr); - if (mRwLock) { - PRRWLock* lock = mRwLock; - mRwLock = nullptr; - PR_DestroyRWLock(lock); - } - + ReleaseHardwareImpl(); DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); } -class RwAutoLockRead +nsresult +nsGonkCameraControl::SetConfigurationInternal(const Configuration& aConfig) { -public: - RwAutoLockRead(PRRWLock* aRwLock) - : mRwLock(aRwLock) - { - PR_RWLock_Rlock(mRwLock); - } + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); - ~RwAutoLockRead() - { - PR_RWLock_Unlock(mRwLock); - } + nsresult rv; -protected: - PRRWLock* mRwLock; -}; + switch (aConfig.mMode) { + case kPictureMode: + rv = SetPictureConfiguration(aConfig); + break; -class RwAutoLockWrite -{ -public: - RwAutoLockWrite(PRRWLock* aRwLock) - : mRwLock(aRwLock) - { - PR_RWLock_Wlock(mRwLock); - } - - ~RwAutoLockWrite() - { - PR_RWLock_Unlock(mRwLock); - } - -protected: - PRRWLock* mRwLock; -}; - -const char* -nsGonkCameraControl::GetParameter(const char* aKey) -{ - RwAutoLockRead lock(mRwLock); - return mParams.get(aKey); -} - -const char* -nsGonkCameraControl::GetParameterConstChar(uint32_t aKey) -{ - const char* key = getKeyText(aKey); - if (!key) { - return nullptr; - } - - RwAutoLockRead lock(mRwLock); - return mParams.get(key); -} - -double -nsGonkCameraControl::GetParameterDouble(uint32_t aKey) -{ - double val; - int index = 0; - double focusDistance[3]; - const char* s; - - const char* key = getKeyText(aKey); - if (!key) { - // return 1x when zooming is not supported - return aKey == CAMERA_PARAM_ZOOM ? 1.0 : 0.0; - } - - RwAutoLockRead lock(mRwLock); - switch (aKey) { - case CAMERA_PARAM_ZOOM: - val = mParams.getInt(key); - return val / 100; - - /** - * The gonk camera parameters API only exposes one focus distance property - * that contains "Near,Optimum,Far" distances, in metres, where 'Far' may - * be 'Infinity'. - */ - case CAMERA_PARAM_FOCUSDISTANCEFAR: - ++index; - // intentional fallthrough - - case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM: - ++index; - // intentional fallthrough - - case CAMERA_PARAM_FOCUSDISTANCENEAR: - s = mParams.get(key); - if (sscanf(s, "%lf,%lf,%lf", &focusDistance[0], &focusDistance[1], &focusDistance[2]) == 3) { - return focusDistance[index]; - } - return 0.0; - - case CAMERA_PARAM_EXPOSURECOMPENSATION: - index = mParams.getInt(key); - if (!index) { - // NaN indicates automatic exposure compensation - return NAN; - } - val = (index - 1) * mExposureCompensationStep + mExposureCompensationMin; - DOM_CAMERA_LOGI("index = %d --> compensation = %f\n", index, val); - return val; + case kVideoMode: + rv = SetVideoConfiguration(aConfig); + break; default: - return mParams.getFloat(key); - } -} - -int32_t -nsGonkCameraControl::GetParameterInt32(uint32_t aKey) -{ - if (aKey == CAMERA_PARAM_SENSORANGLE) { - if (!mCameraHw.get()) { - return 0; - } - return mCameraHw->GetSensorOrientation(); + MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode in SetConfigurationInternal()"); } - const char* key = getKeyText(aKey); - if (!key) { - return 0; + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); + NS_ENSURE_SUCCESS(rv, rv); + + mCurrentConfiguration.mMode = aConfig.mMode; + mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile; + if (aConfig.mMode == kVideoMode) { + mCurrentConfiguration.mPreviewSize = mLastRecorderSize; } - RwAutoLockRead lock(mRwLock); - return mParams.getInt(key); -} - -void -nsGonkCameraControl::GetParameter(uint32_t aKey, - nsTArray& aRegions) -{ - aRegions.Clear(); - - const char* key = getKeyText(aKey); - if (!key) { - return; - } - - RwAutoLockRead lock(mRwLock); - - const char* value = mParams.get(key); - DOM_CAMERA_LOGI("key='%s' --> value='%s'\n", key, value); - if (!value) { - return; - } - - const char* p = value; - uint32_t count = 1; - - // count the number of regions in the string - while ((p = strstr(p, "),("))) { - ++count; - p += 3; - } - - aRegions.SetCapacity(count); - idl::CameraRegion* r; - - // parse all of the region sets - uint32_t i; - for (i = 0, p = value; p && i < count; ++i, p = strchr(p + 1, '(')) { - r = aRegions.AppendElement(); - if (sscanf(p, "(%d,%d,%d,%d,%u)", &r->top, &r->left, &r->bottom, &r->right, &r->weight) != 5) { - DOM_CAMERA_LOGE("%s:%d : region tuple has bad format: '%s'\n", __func__, __LINE__, p); - aRegions.Clear(); - return; - } - } - - return; -} - -void -nsGonkCameraControl::GetParameter(uint32_t aKey, - nsTArray& aSizes) -{ - const char* key = getKeyText(aKey); - if (!key) { - return; - } - - RwAutoLockRead lock(mRwLock); - - const char* value = mParams.get(key); - DOM_CAMERA_LOGI("key='%s' --> value='%s'\n", key, value); - if (!value) { - return; - } - - const char* p = value; - idl::CameraSize* s; - - // The 'value' string is in the format "w1xh1,w2xh2,w3xh3,..." - while (p) { - s = aSizes.AppendElement(); - if (sscanf(p, "%dx%d", &s->width, &s->height) != 2) { - DOM_CAMERA_LOGE("%s:%d : size tuple has bad format: '%s'\n", __func__, __LINE__, p); - aSizes.Clear(); - return; - } - // Look for the next record... - p = strchr(p, ','); - if (p) { - // ...skip the comma too - ++p; - } - } - - return; -} - -void -nsGonkCameraControl::GetParameter(uint32_t aKey, idl::CameraSize& aSize) -{ - if (aKey == CAMERA_PARAM_THUMBNAILSIZE) { - // This is a special case--for some reason the thumbnail size - // is accessed as two separate values instead of a tuple. - RwAutoLockRead lock(mRwLock); - - aSize.width = mParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH); - aSize.height = mParams.getInt(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT); - DOM_CAMERA_LOGI("thumbnail size --> value='%ux%u'\n", aSize.width, aSize.height); - return; - } - - const char* key = getKeyText(aKey); - if (!key) { - return; - } - - RwAutoLockRead lock(mRwLock); - - const char* value = mParams.get(key); - DOM_CAMERA_LOGI("key='%s' --> value='%s'\n", key, value); - if (!value) { - return; - } - - if (sscanf(value, "%ux%u", &aSize.width, &aSize.height) != 2) { - DOM_CAMERA_LOGE("%s:%d : size tuple has bad format: '%s'\n", __func__, __LINE__, value); - aSize.width = 0; - aSize.height = 0; - } + OnConfigurationChange(); + return NS_OK; } +nsresult +nsGonkCameraControl::SetConfigurationImpl(const Configuration& aConfig) +{ + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); + + // Stop any currently running preview + StopPreviewImpl(); + + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); + nsresult rv = SetConfigurationInternal(aConfig); + NS_ENSURE_SUCCESS(rv, rv); + + // Restart the preview + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); + return StartPreviewImpl(); +} + +nsresult +nsGonkCameraControl::SetPictureConfiguration(const Configuration& aConfig) +{ + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); + + // remove any existing recorder profile + mRecorderProfile = nullptr; + + nsresult rv = SetPreviewSize(aConfig.mPreviewSize); + NS_ENSURE_SUCCESS(rv, rv); + + mParams.Get(CAMERA_PARAM_PREVIEWFRAMERATE, mPreviewFps); + + DOM_CAMERA_LOGI("picture mode preview: wanted %ux%u, got %ux%u (%u fps)\n", + aConfig.mPreviewSize.width, aConfig.mPreviewSize.height, + mCurrentConfiguration.mPreviewSize.width, mCurrentConfiguration.mPreviewSize.height, + mPreviewFps); + + return NS_OK; +} + +nsresult +nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig) +{ + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); + + nsresult rv = SetupVideoMode(aConfig.mRecorderProfile); + NS_ENSURE_SUCCESS(rv, rv); + + DOM_CAMERA_LOGI("video mode preview: profile '%s', got %ux%u (%u fps)\n", + NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile).get(), + mLastRecorderSize.width, mLastRecorderSize.height, + mPreviewFps); + + return rv; +} + +// Parameter management. nsresult nsGonkCameraControl::PushParameters() { - if (mDeferConfigUpdate) { - DOM_CAMERA_LOGT("%s:%d - defering config update\n", __func__, __LINE__); + uint32_t dcu = mDeferConfigUpdate; + if (dcu > 0) { + DOM_CAMERA_LOGI("Defering config update (nest level %u)\n", dcu); return NS_OK; } @@ -587,9 +319,10 @@ nsGonkCameraControl::PushParameters() * require this so that changes take effect immediately before * we can proceed. */ - if (NS_IsMainThread()) { - DOM_CAMERA_LOGT("%s:%d - dispatching to camera thread\n", __func__, __LINE__); - nsCOMPtr pushParametersTask = NS_NewRunnableMethod(this, &nsGonkCameraControl::PushParametersImpl); + if (NS_GetCurrentThread() != mCameraThread) { + DOM_CAMERA_LOGT("%s:%d - dispatching to Camera Thread\n", __func__, __LINE__); + nsCOMPtr pushParametersTask = + NS_NewRunnableMethod(this, &nsGonkCameraControl::PushParametersImpl); return mCameraThread->Dispatch(pushParametersTask, NS_DISPATCH_NORMAL); } @@ -598,257 +331,264 @@ nsGonkCameraControl::PushParameters() } void -nsGonkCameraControl::SetParameter(const char* aKey, const char* aValue) +nsGonkCameraControl::BeginBatchParameterSet() { - { - RwAutoLockWrite lock(mRwLock); - mParams.set(aKey, aValue); + uint32_t dcu = ++mDeferConfigUpdate; + if (dcu == 0) { + NS_WARNING("Overflow weirdness incrementing mDeferConfigUpdate!"); + MOZ_CRASH(); } - PushParameters(); + DOM_CAMERA_LOGI("Begin deferring camera configuration updates (nest level %u)\n", dcu); } void -nsGonkCameraControl::SetParameter(uint32_t aKey, const char* aValue) +nsGonkCameraControl::EndBatchParameterSet() { - const char* key = getKeyText(aKey); - if (!key) { - return; + uint32_t dcu = mDeferConfigUpdate--; + if (dcu == 0) { + NS_WARNING("Underflow badness decrementing mDeferConfigUpdate!"); + MOZ_CRASH(); } + DOM_CAMERA_LOGI("End deferring camera configuration updates (nest level %u)\n", dcu); - { - RwAutoLockWrite lock(mRwLock); - mParams.set(key, aValue); - } - PushParameters(); -} - -void -nsGonkCameraControl::SetParameter(uint32_t aKey, double aValue) -{ - uint32_t index; - - const char* key = getKeyText(aKey); - if (!key) { - return; - } - - { - RwAutoLockWrite lock(mRwLock); - if (aKey == CAMERA_PARAM_EXPOSURECOMPENSATION) { - /** - * Convert from real value to a Gonk index, round - * to the nearest step; index is 1-based. - */ - index = (aValue - mExposureCompensationMin + mExposureCompensationStep / 2) / mExposureCompensationStep + 1; - DOM_CAMERA_LOGI("compensation = %f --> index = %d\n", aValue, index); - mParams.set(key, index); - } else { - mParams.setFloat(key, aValue); - } - } - PushParameters(); -} - -void -nsGonkCameraControl::SetParameter(uint32_t aKey, - const nsTArray& aRegions) -{ - const char* key = getKeyText(aKey); - if (!key) { - return; - } - - uint32_t length = aRegions.Length(); - - if (!length) { - // This tells the camera driver to revert to automatic regioning. - { - RwAutoLockWrite lock(mRwLock); - mParams.set(key, "(0,0,0,0,0)"); - } + if (dcu == 1) { PushParameters(); - return; } - - nsCString s; - - for (uint32_t i = 0; i < length; ++i) { - const idl::CameraRegion* r = &aRegions[i]; - s.AppendPrintf("(%d,%d,%d,%d,%d),", r->top, r->left, r->bottom, r->right, r->weight); - } - - // remove the trailing comma - s.Trim(",", false, true, true); - - DOM_CAMERA_LOGI("camera region string '%s'\n", s.get()); - - { - RwAutoLockWrite lock(mRwLock); - mParams.set(key, s.get()); - } - PushParameters(); } -void -nsGonkCameraControl::SetParameter(uint32_t aKey, int aValue) +template nsresult +nsGonkCameraControl::SetAndPush(uint32_t aKey, const T& aValue) { - const char* key = getKeyText(aKey); - if (!key) { - return; + nsresult rv = mParams.Set(aKey, aValue); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Camera parameter aKey=%d failed to set (0x%x)\n", aKey, rv); + return rv; } - { - RwAutoLockWrite lock(mRwLock); - mParams.set(key, aValue); - } - PushParameters(); + return PushParameters(); } -void -nsGonkCameraControl::SetParameter(uint32_t aKey, const idl::CameraSize& aSize) +// Array-of-Size parameter accessor. +nsresult +nsGonkCameraControl::Get(uint32_t aKey, nsTArray& aSizes) +{ + if (aKey == CAMERA_PARAM_SUPPORTED_VIDEOSIZES) { + nsresult rv = mParams.Get(aKey, aSizes); + if (aSizes.Length() != 0) { + return rv; + } + DOM_CAMERA_LOGI("Camera doesn't support video independent of the preview\n"); + aKey = CAMERA_PARAM_SUPPORTED_PREVIEWSIZES; + } + + return mParams.Get(aKey, aSizes); +} + +// Array-of-doubles parameter accessor. +nsresult +nsGonkCameraControl::Get(uint32_t aKey, nsTArray& aValues) +{ + return mParams.Get(aKey, aValues); +} + +// Array-of-nsString parameter accessor. +nsresult +nsGonkCameraControl::Get(uint32_t aKey, nsTArray& aValues) +{ + return mParams.Get(aKey, aValues); +} + +// nsString-valued parameter accessors +nsresult +nsGonkCameraControl::Set(uint32_t aKey, const nsAString& aValue) +{ + nsresult rv = mParams.Set(aKey, aValue); + if (NS_FAILED(rv)) { + return rv; + } + + if (aKey == CAMERA_PARAM_PICTURE_FILEFORMAT) { + // Picture format -- need to keep it for the TakePicture() callback. + mFileFormat = aValue; + } + + return PushParameters(); +} + +nsresult +nsGonkCameraControl::Get(uint32_t aKey, nsAString& aRet) +{ + return mParams.Get(aKey, aRet); +} + +// Double-valued parameter accessors +nsresult +nsGonkCameraControl::Set(uint32_t aKey, double aValue) +{ + return SetAndPush(aKey, aValue); +} + +nsresult +nsGonkCameraControl::Get(uint32_t aKey, double& aRet) +{ + return mParams.Get(aKey, aRet); +} + +// Signed-64-bit parameter accessors. +nsresult +nsGonkCameraControl::Set(uint32_t aKey, int64_t aValue) +{ + return SetAndPush(aKey, aValue); +} + +nsresult +nsGonkCameraControl::Get(uint32_t aKey, int64_t& aRet) +{ + return mParams.Get(aKey, aRet); +} + +// Weighted-region parameter accessors. +nsresult +nsGonkCameraControl::Set(uint32_t aKey, const nsTArray& aRegions) +{ + return SetAndPush(aKey, aRegions); +} + +nsresult +nsGonkCameraControl::Get(uint32_t aKey, nsTArray& aRegions) +{ + return mParams.Get(aKey, aRegions); +} + +// Singleton-size parameter accessors. +nsresult +nsGonkCameraControl::Set(uint32_t aKey, const Size& aSize) { switch (aKey) { case CAMERA_PARAM_PICTURESIZE: DOM_CAMERA_LOGI("setting picture size to %ux%u\n", aSize.width, aSize.height); - SetPictureSize(aSize.width, aSize.height); - break; + return SetPictureSize(aSize); case CAMERA_PARAM_THUMBNAILSIZE: DOM_CAMERA_LOGI("setting thumbnail size to %ux%u\n", aSize.width, aSize.height); - SetThumbnailSize(aSize.width, aSize.height); - break; + return SetThumbnailSize(aSize); default: - { - const char* key = getKeyText(aKey); - if (!key) { - return; - } - - nsCString s; - s.AppendPrintf("%ux%u", aSize.width, aSize.height); - DOM_CAMERA_LOGI("setting '%s' to %s\n", key, s.get()); - - RwAutoLockWrite lock(mRwLock); - mParams.set(key, s.get()); - } - break; + return SetAndPush(aKey, aSize); } - PushParameters(); } nsresult -nsGonkCameraControl::GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream) +nsGonkCameraControl::Get(uint32_t aKey, Size& aSize) { - // stop any currently running preview - StopPreviewInternal(true /* forced */); + return mParams.Get(aKey, aSize); +} - // remove any existing recorder profile - mRecorderProfile = nullptr; - - SetPreviewSize(aGetPreviewStream->mSize.width, aGetPreviewStream->mSize.height); - DOM_CAMERA_LOGI("picture preview: wanted %d x %d, got %d x %d (%d fps, format %d)\n", aGetPreviewStream->mSize.width, aGetPreviewStream->mSize.height, mWidth, mHeight, mFps, mFormat); - - nsMainThreadPtrHandle onSuccess = aGetPreviewStream->mOnSuccessCb; - nsCOMPtr getPreviewStreamResult = new GetPreviewStreamResult(this, mWidth, mHeight, mFps, onSuccess, mWindowId); - return NS_DispatchToMainThread(getPreviewStreamResult); +// Signed int parameter accessors. +nsresult +nsGonkCameraControl::Set(uint32_t aKey, int aValue) +{ + if (aKey == CAMERA_PARAM_PICTURE_ROTATION) { + aValue = RationalizeRotation(aValue + mCameraHw->GetSensorOrientation()); + } + return SetAndPush(aKey, aValue); } nsresult -nsGonkCameraControl::StartPreviewImpl(StartPreviewTask* aStartPreview) +nsGonkCameraControl::Get(uint32_t aKey, int& aRet) { - /** - * If 'aStartPreview->mDOMPreview' is null, we are just restarting - * the preview after taking a picture. No need to monkey with the - * currently set DOM-facing preview object. - */ - if (aStartPreview->mDOMPreview) { - StopPreviewInternal(true /* forced */); - mDOMPreview = aStartPreview->mDOMPreview; - } else if (!mDOMPreview) { - return NS_ERROR_INVALID_ARG; + if (aKey == CAMERA_PARAM_SENSORANGLE) { + if (!mCameraHw.get()) { + return NS_ERROR_NOT_AVAILABLE; + } + aRet = mCameraHw->GetSensorOrientation(); + return NS_OK; } - DOM_CAMERA_LOGI("%s: starting preview (mDOMPreview=%p)\n", __func__, mDOMPreview); + return mParams.Get(aKey, aRet); +} +// GPS location parameter accessors. +nsresult +nsGonkCameraControl::SetLocation(const Position& aLocation) +{ + return SetAndPush(CAMERA_PARAM_PICTURE_LOCATION, aLocation); +} + +nsresult +nsGonkCameraControl::StartPreviewImpl() +{ + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); RETURN_IF_NO_CAMERA_HW(); + + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + + if (mPreviewState == CameraControlListener::kPreviewStarted) { + DOM_CAMERA_LOGW("Camera preview already started, nothing to do\n"); + return NS_OK; + } + + DOM_CAMERA_LOGI("Starting preview (this=%p)\n", this); + if (mCameraHw->StartPreview() != OK) { - DOM_CAMERA_LOGE("%s: failed to start preview\n", __func__); + DOM_CAMERA_LOGE("Failed to start camera preview\n"); return NS_ERROR_FAILURE; } - if (aStartPreview->mDOMPreview) { - mDOMPreview->Started(); - } - - OnPreviewStateChange(PREVIEW_STARTED); + OnPreviewStateChange(CameraControlListener::kPreviewStarted); return NS_OK; } nsresult -nsGonkCameraControl::StopPreviewInternal(bool aForced) +nsGonkCameraControl::StopPreviewImpl() { - DOM_CAMERA_LOGI("%s: stopping preview (mDOMPreview=%p)\n", __func__, mDOMPreview); + RETURN_IF_NO_CAMERA_HW(); - // StopPreview() is a synchronous call--it doesn't return - // until the camera preview thread exits. - if (mDOMPreview) { - if (mCameraHw.get()) { - mCameraHw->StopPreview(); - } - mDOMPreview->Stopped(aForced); - mDOMPreview = nullptr; - } + DOM_CAMERA_LOGI("Stopping preview (this=%p)\n", this); - OnPreviewStateChange(PREVIEW_STOPPED); + mCameraHw->StopPreview(); + + OnPreviewStateChange(CameraControlListener::kPreviewStopped); return NS_OK; } nsresult -nsGonkCameraControl::StopPreviewImpl(StopPreviewTask* aStopPreview) +nsGonkCameraControl::AutoFocusImpl(bool aCancelExistingCall) { - return StopPreviewInternal(); -} - -nsresult -nsGonkCameraControl::AutoFocusImpl(AutoFocusTask* aAutoFocus) -{ - if (aAutoFocus->mCancel) { + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); + RETURN_IF_NO_CAMERA_HW(); + if (aCancelExistingCall) { if (mCameraHw.get()) { mCameraHw->CancelAutoFocus(); } } - mAutoFocusOnSuccessCb = aAutoFocus->mOnSuccessCb; - mAutoFocusOnErrorCb = aAutoFocus->mOnErrorCb; - - RETURN_IF_NO_CAMERA_HW(); if (mCameraHw->AutoFocus() != OK) { return NS_ERROR_FAILURE; } return NS_OK; } -void -nsGonkCameraControl::SetThumbnailSize(uint32_t aWidth, uint32_t aHeight) +nsresult +nsGonkCameraControl::SetThumbnailSizeImpl(const Size& aSize) { + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); + /** * We keep a copy of the specified size so that if the picture size * changes, we can choose a new thumbnail size close to what was asked for * last time. */ - mLastThumbnailWidth = aWidth; - mLastThumbnailHeight = aHeight; + mLastThumbnailSize = aSize; /** * If either of width or height is zero, set the other to zero as well. * This should disable inclusion of a thumbnail in the final picture. */ - if (!aWidth || !aHeight) { - DOM_CAMERA_LOGW("Requested thumbnail size %ux%u, disabling thumbnail\n", aWidth, aHeight); - RwAutoLockWrite write(mRwLock); - mParams.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, 0); - mParams.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, 0); - return; + if (!aSize.width || !aSize.height) { + DOM_CAMERA_LOGW("Requested thumbnail size %ux%u, disabling thumbnail\n", + aSize.width, aSize.height); + Size size = { 0, 0 }; + return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size); } /** @@ -859,10 +599,10 @@ nsGonkCameraControl::SetThumbnailSize(uint32_t aWidth, uint32_t aHeight) */ int smallestDelta = INT_MAX; uint32_t smallestDeltaIndex = UINT32_MAX; - int targetArea = aWidth * aHeight; + int targetArea = aSize.width * aSize.height; - nsAutoTArray supportedSizes; - GetParameter(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, supportedSizes); + nsAutoTArray supportedSizes; + Get(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, supportedSizes); for (uint32_t i = 0; i < supportedSizes.Length(); ++i) { int area = supportedSizes[i].width * supportedSizes[i].height; @@ -870,7 +610,8 @@ nsGonkCameraControl::SetThumbnailSize(uint32_t aWidth, uint32_t aHeight) if (area != 0 && delta < smallestDelta - && supportedSizes[i].width * mLastPictureHeight / supportedSizes[i].height == mLastPictureWidth + && supportedSizes[i].width * mLastPictureSize.height / + supportedSizes[i].height == mLastPictureSize.width ) { smallestDelta = delta; smallestDeltaIndex = i; @@ -878,45 +619,83 @@ nsGonkCameraControl::SetThumbnailSize(uint32_t aWidth, uint32_t aHeight) } if (smallestDeltaIndex == UINT32_MAX) { - DOM_CAMERA_LOGW("Unable to find a thumbnail size close to %ux%u\n", aWidth, aHeight); - return; + DOM_CAMERA_LOGW("Unable to find a thumbnail size close to %ux%u\n", + aSize.width, aSize.height); + return NS_ERROR_INVALID_ARG; } - uint32_t w = supportedSizes[smallestDeltaIndex].width; - uint32_t h = supportedSizes[smallestDeltaIndex].height; - DOM_CAMERA_LOGI("Requested thumbnail size %ux%u --> using supported size %ux%u\n", aWidth, aHeight, w, h); - if (w > INT32_MAX || h > INT32_MAX) { + Size size = supportedSizes[smallestDeltaIndex]; + DOM_CAMERA_LOGI("camera-param set picture-size = %ux%u (requested %ux%u)\n", + size.width, size.height, aSize.width, aSize.height); + if (size.width > INT32_MAX || size.height > INT32_MAX) { DOM_CAMERA_LOGE("Supported thumbnail size is too big, no change\n"); - return; + return NS_ERROR_FAILURE; } - RwAutoLockWrite write(mRwLock); - mParams.set(CameraParameters::KEY_JPEG_THUMBNAIL_WIDTH, static_cast(w)); - mParams.set(CameraParameters::KEY_JPEG_THUMBNAIL_HEIGHT, static_cast(h)); + return SetAndPush(CAMERA_PARAM_THUMBNAILSIZE, size); } -void +nsresult +nsGonkCameraControl::SetThumbnailSize(const Size& aSize) +{ + class SetThumbnailSize : public nsRunnable + { + public: + SetThumbnailSize(nsGonkCameraControl* aCameraControl, const Size& aSize) + : mCameraControl(aCameraControl) + , mSize(aSize) + { + MOZ_COUNT_CTOR(SetThumbnailSize); + } + ~SetThumbnailSize() { MOZ_COUNT_DTOR(SetThumbnailSize); } + + NS_IMETHODIMP + Run() MOZ_OVERRIDE + { + nsresult rv = mCameraControl->SetThumbnailSizeImpl(mSize); + if (NS_FAILED(rv)) { + mCameraControl->OnError(CameraControlListener::kInUnspecified, + CameraControlListener::kErrorSetThumbnailSizeFailed); + } + return NS_OK; + } + + protected: + nsRefPtr mCameraControl; + Size mSize; + }; + + if (NS_GetCurrentThread() == mCameraThread) { + return SetThumbnailSizeImpl(aSize); + } + + return mCameraThread->Dispatch(new SetThumbnailSize(this, aSize), NS_DISPATCH_NORMAL); +} + +nsresult nsGonkCameraControl::UpdateThumbnailSize() { - SetThumbnailSize(mLastThumbnailWidth, mLastThumbnailHeight); + return SetThumbnailSize(mLastThumbnailSize); } -void -nsGonkCameraControl::SetPictureSize(uint32_t aWidth, uint32_t aHeight) +nsresult +nsGonkCameraControl::SetPictureSizeImpl(const Size& aSize) { + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); + /** * Some drivers are less friendly about getting one of these set to zero, * so if either is not specified, ignore both and go with current or * default settings. */ - if (!aWidth || !aHeight) { - DOM_CAMERA_LOGW("Ignoring requested picture size of %ux%u\n", aWidth, aHeight); - return; + if (!aSize.width || !aSize.height) { + DOM_CAMERA_LOGW("Ignoring requested picture size of %ux%u\n", aSize.width, aSize.height); + return NS_ERROR_INVALID_ARG; } - if (aWidth == mLastPictureWidth && aHeight == mLastPictureHeight) { - DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aWidth, aHeight); - return; + if (aSize.width == mLastPictureSize.width && aSize.height == mLastPictureSize.height) { + DOM_CAMERA_LOGI("Requested picture size %ux%u unchanged\n", aSize.width, aSize.height); + return NS_OK; } /** @@ -927,10 +706,10 @@ nsGonkCameraControl::SetPictureSize(uint32_t aWidth, uint32_t aHeight) */ int smallestDelta = INT_MAX; uint32_t smallestDeltaIndex = UINT32_MAX; - int targetArea = aWidth * aHeight; - - nsAutoTArray supportedSizes; - GetParameter(CAMERA_PARAM_SUPPORTED_PICTURESIZES, supportedSizes); + int targetArea = aSize.width * aSize.height; + + nsAutoTArray supportedSizes; + Get(CAMERA_PARAM_SUPPORTED_PICTURESIZES, supportedSizes); for (uint32_t i = 0; i < supportedSizes.Length(); ++i) { int area = supportedSizes[i].width * supportedSizes[i].height; @@ -943,29 +722,29 @@ nsGonkCameraControl::SetPictureSize(uint32_t aWidth, uint32_t aHeight) } if (smallestDeltaIndex == UINT32_MAX) { - DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n", aWidth, aHeight); - return; + DOM_CAMERA_LOGW("Unable to find a picture size close to %ux%u\n", + aSize.width, aSize.height); + return NS_ERROR_INVALID_ARG; } - uint32_t w = supportedSizes[smallestDeltaIndex].width; - uint32_t h = supportedSizes[smallestDeltaIndex].height; - DOM_CAMERA_LOGI("Requested picture size %ux%u --> using supported size %ux%u\n", aWidth, aHeight, w, h); - if (w > INT32_MAX || h > INT32_MAX) { + Size size = supportedSizes[smallestDeltaIndex]; + DOM_CAMERA_LOGI("camera-param set picture-size = %ux%u (requested %ux%u)\n", + size.width, size.height, aSize.width, aSize.height); + if (size.width > INT32_MAX || size.height > INT32_MAX) { DOM_CAMERA_LOGE("Supported picture size is too big, no change\n"); - return; + return NS_ERROR_FAILURE; } - mLastPictureWidth = w; - mLastPictureHeight = h; - - { - // We must release the write-lock before updating the thumbnail size - RwAutoLockWrite write(mRwLock); - mParams.setPictureSize(static_cast(w), static_cast(h)); + nsresult rv = mParams.Set(CAMERA_PARAM_PICTURESIZE, size); + if (NS_FAILED(rv)) { + return rv; } - // Finally, update the thumbnail size - UpdateThumbnailSize(); + mLastPictureSize = size; + + // Finally, update the thumbnail size in case the picture + // aspect ratio changed. + return UpdateThumbnailSize(); } int32_t @@ -993,100 +772,65 @@ nsGonkCameraControl::RationalizeRotation(int32_t aRotation) } nsresult -nsGonkCameraControl::TakePictureImpl(TakePictureTask* aTakePicture) +nsGonkCameraControl::SetPictureSize(const Size& aSize) { - if (aTakePicture->mCancel) { - if (mCameraHw.get()) { - mCameraHw->CancelTakePicture(); + class SetPictureSize : public nsRunnable + { + public: + SetPictureSize(nsGonkCameraControl* aCameraControl, const Size& aSize) + : mCameraControl(aCameraControl) + , mSize(aSize) + { + MOZ_COUNT_CTOR(SetPictureSize); } - } + ~SetPictureSize() { MOZ_COUNT_DTOR(SetPictureSize); } - mTakePictureOnSuccessCb = aTakePicture->mOnSuccessCb; - mTakePictureOnErrorCb = aTakePicture->mOnErrorCb; - - RETURN_IF_NO_CAMERA_HW(); - - // batch-update camera configuration - mDeferConfigUpdate = true; - - SetPictureSize(aTakePicture->mSize.width, aTakePicture->mSize.height); - - // Picture format -- need to keep it for the callback. - mFileFormat = aTakePicture->mFileFormat; - SetParameter(CameraParameters::KEY_PICTURE_FORMAT, NS_ConvertUTF16toUTF8(mFileFormat).get()); - - // Round 'rotation' up to a positive value from 0..270 degrees, in steps of 90. - int32_t r = static_cast(aTakePicture->mRotation); - r += mCameraHw->GetSensorOrientation(GonkCameraHardware::OFFSET_SENSOR_ORIENTATION); - r = RationalizeRotation(r); - DOM_CAMERA_LOGI("setting picture rotation to %d degrees (mapped from %d)\n", r, aTakePicture->mRotation); - SetParameter(CameraParameters::KEY_ROTATION, nsPrintfCString("%u", r).get()); - - // Add any specified positional information -- don't care if these fail. - if (!isnan(aTakePicture->mPosition.latitude)) { - DOM_CAMERA_LOGI("setting picture latitude to %lf\n", aTakePicture->mPosition.latitude); - SetParameter(CameraParameters::KEY_GPS_LATITUDE, nsPrintfCString("%lf", aTakePicture->mPosition.latitude).get()); - } - if (!isnan(aTakePicture->mPosition.longitude)) { - DOM_CAMERA_LOGI("setting picture longitude to %lf\n", aTakePicture->mPosition.longitude); - SetParameter(CameraParameters::KEY_GPS_LONGITUDE, nsPrintfCString("%lf", aTakePicture->mPosition.longitude).get()); - } - if (!isnan(aTakePicture->mPosition.altitude)) { - DOM_CAMERA_LOGI("setting picture altitude to %lf\n", aTakePicture->mPosition.altitude); - SetParameter(CameraParameters::KEY_GPS_ALTITUDE, nsPrintfCString("%lf", aTakePicture->mPosition.altitude).get()); - } - if (!isnan(aTakePicture->mPosition.timestamp)) { - DOM_CAMERA_LOGI("setting picture timestamp to %lf\n", aTakePicture->mPosition.timestamp); - SetParameter(CameraParameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aTakePicture->mPosition.timestamp).get()); - } - - // Add the non-GPS timestamp. The EXIF date/time field is formatted as - // "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time - // is meant to be stored as a local time. Since we are given seconds from - // Epoch GMT, we use localtime_r() to handle the conversion. - time_t time = aTakePicture->mDateTime; - if ((uint64_t)time != aTakePicture->mDateTime) { - DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aTakePicture->mDateTime); - } else { - struct tm t; - if (localtime_r(&time, &t)) { - char dateTime[20]; - if (strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) { - DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime); - // Not every platform defines a CameraParameters::KEY_EXIF_DATETIME; - // for those who don't, we use the raw string key, and if the platform - // doesn't support it, it will be ignored. - // - // See bug 832494. - SetParameter("exif-datetime", dateTime); - } else { - DOM_CAMERA_LOGE("picture date/time couldn't be converted to string\n"); + NS_IMETHODIMP + Run() MOZ_OVERRIDE + { + nsresult rv = mCameraControl->SetPictureSizeImpl(mSize); + if (NS_FAILED(rv)) { + mCameraControl->OnError(CameraControlListener::kInUnspecified, + CameraControlListener::kErrorSetPictureSizeFailed); } - } else { - DOM_CAMERA_LOGE("picture date/time couldn't be converted to local time: (%d) %s\n", errno, strerror(errno)); + return NS_OK; } + + protected: + nsRefPtr mCameraControl; + Size mSize; + }; + + if (NS_GetCurrentThread() == mCameraThread) { + return SetPictureSizeImpl(aSize); } - mDeferConfigUpdate = false; - PushParameters(); + return mCameraThread->Dispatch(new SetPictureSize(this, aSize), NS_DISPATCH_NORMAL); +} + +nsresult +nsGonkCameraControl::TakePictureImpl() +{ + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); + RETURN_IF_NO_CAMERA_HW(); if (mCameraHw->TakePicture() != OK) { return NS_ERROR_FAILURE; } - - // In Gonk, taking a picture implicitly kills the preview stream, + + // In Gonk, taking a picture implicitly stops the preview stream, // so we need to reflect that here. - OnPreviewStateChange(PREVIEW_STOPPED); + OnPreviewStateChange(CameraControlListener::kPreviewPaused); return NS_OK; } nsresult nsGonkCameraControl::PushParametersImpl() { + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); DOM_CAMERA_LOGI("Pushing camera parameters\n"); RETURN_IF_NO_CAMERA_HW(); - RwAutoLockRead lock(mRwLock); if (mCameraHw->PushParameters(mParams) != OK) { return NS_ERROR_FAILURE; } @@ -1097,16 +841,16 @@ nsGonkCameraControl::PushParametersImpl() nsresult nsGonkCameraControl::PullParametersImpl() { + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); DOM_CAMERA_LOGI("Pulling camera parameters\n"); RETURN_IF_NO_CAMERA_HW(); - RwAutoLockWrite lock(mRwLock); - mCameraHw->PullParameters(mParams); - return NS_OK; + return mCameraHw->PullParameters(mParams); } nsresult -nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording) +nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor, + const StartRecordingOptions* aOptions) { NS_ENSURE_TRUE(mRecorderProfile, NS_ERROR_NOT_INITIALIZED); NS_ENSURE_FALSE(mRecorder, NS_ERROR_FAILURE); @@ -1119,10 +863,9 @@ nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording) * The camera app needs to provide the file extension '.3gp' for now. * See bug 795202. */ - nsRefPtr dsfd = aStartRecording->mDSFileDescriptor; - NS_ENSURE_TRUE(dsfd, NS_ERROR_FAILURE); + NS_ENSURE_TRUE(aFileDescriptor, NS_ERROR_FAILURE); nsAutoString fullPath; - mVideoFile = dsfd->mDSFile; + mVideoFile = aFileDescriptor->mDSFile; mVideoFile->GetFullPath(fullPath); DOM_CAMERA_LOGI("Video filename is '%s'\n", NS_LossyConvertUTF16toASCII(fullPath).get()); @@ -1133,10 +876,13 @@ nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording) } nsresult rv; - rv = SetupRecording(dsfd->mFileDescriptor.PlatformHandle(), - aStartRecording->mOptions.rotation, - aStartRecording->mOptions.maxFileSizeBytes, - aStartRecording->mOptions.maxVideoLengthMs); + int fd = aFileDescriptor->mFileDescriptor.PlatformHandle(); + if (aOptions) { + rv = SetupRecording(fd, aOptions->rotation, aOptions->maxFileSizeBytes, + aOptions->maxVideoLengthMs); + } else { + rv = SetupRecording(fd, 0, 0, 0); + } NS_ENSURE_SUCCESS(rv, rv); if (mRecorder->start() != OK) { @@ -1146,174 +892,201 @@ nsGonkCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording) return NS_ERROR_FAILURE; } + OnRecorderStateChange(CameraControlListener::kRecorderStarted, -1, -1); return NS_OK; } -class RecordingComplete : public nsRunnable -{ -public: - RecordingComplete(DeviceStorageFile* aFile) - : mFile(aFile) - { } - - ~RecordingComplete() { } - - NS_IMETHOD Run() - { - MOZ_ASSERT(NS_IsMainThread()); - - nsCOMPtr obs = mozilla::services::GetObserverService(); - obs->NotifyObservers(mFile, "file-watcher-notify", MOZ_UTF16("modified")); - return NS_OK; - } - -private: - nsRefPtr mFile; -}; - nsresult -nsGonkCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording) +nsGonkCameraControl::StopRecordingImpl() { + class RecordingComplete : public nsRunnable + { + public: + RecordingComplete(DeviceStorageFile* aFile) + : mFile(aFile) + { } + + ~RecordingComplete() { } + + NS_IMETHODIMP + Run() + { + MOZ_ASSERT(NS_IsMainThread()); + + nsCOMPtr obs = mozilla::services::GetObserverService(); + obs->NotifyObservers(mFile, "file-watcher-notify", NS_LITERAL_STRING("modified").get()); + return NS_OK; + } + + private: + nsRefPtr mFile; + }; + + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); + // nothing to do if we have no mRecorder NS_ENSURE_TRUE(mRecorder, NS_OK); mRecorder->stop(); mRecorder = nullptr; + OnRecorderStateChange(CameraControlListener::kRecorderStopped, -1, -1); // notify DeviceStorage that the new video file is closed and ready - nsCOMPtr recordingComplete = new RecordingComplete(mVideoFile); - return NS_DispatchToMainThread(recordingComplete, NS_DISPATCH_NORMAL); + return NS_DispatchToMainThread(new RecordingComplete(mVideoFile), NS_DISPATCH_NORMAL); } void -nsGonkCameraControl::AutoFocusComplete(bool aSuccess) +nsGonkCameraControl::OnAutoFocusComplete(bool aSuccess) { - /** - * Auto focusing can change some of the camera's parameters, so - * we need to pull a new set before sending the result to the - * main thread. - */ - PullParametersImpl(); + class AutoFocusComplete : public nsRunnable + { + public: + AutoFocusComplete(nsGonkCameraControl* aCameraControl, bool aSuccess) + : mCameraControl(aCameraControl) + , mSuccess(aSuccess) + { } - /** - * If we make it here, regardless of the value of 'aSuccess', we - * consider the autofocus _process_ to have succeeded. It is up - * to the onSuccess callback to determine how to handle the case - * where the camera wasn't actually able to acquire focus. - */ - nsCOMPtr autoFocusResult = new AutoFocusResult(aSuccess, mAutoFocusOnSuccessCb, mWindowId); - /** - * Remember to set these to null so that we don't hold any extra - * references to our document's window. - */ - mAutoFocusOnSuccessCb = nullptr; - mAutoFocusOnErrorCb = nullptr; - nsresult rv = NS_DispatchToMainThread(autoFocusResult); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch autoFocus() onSuccess callback to main thread!"); + NS_IMETHODIMP + Run() MOZ_OVERRIDE + { + mCameraControl->OnAutoFocusComplete(mSuccess); + return NS_OK; + } + + protected: + nsRefPtr mCameraControl; + bool mSuccess; + }; + + if (NS_GetCurrentThread() == mCameraThread) { + /** + * Auto focusing can change some of the camera's parameters, so + * we need to pull a new set before notifying any clients. + */ + PullParametersImpl(); + CameraControlImpl::OnAutoFocusComplete(aSuccess); + return; } + + /** + * Because the callback needs to call PullParametersImpl(), + * we need to dispatch this callback through the Camera Thread. + */ + mCameraThread->Dispatch(new AutoFocusComplete(this, aSuccess), NS_DISPATCH_NORMAL); } void -nsGonkCameraControl::TakePictureComplete(uint8_t* aData, uint32_t aLength) +nsGonkCameraControl::OnTakePictureComplete(uint8_t* aData, uint32_t aLength) { + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + uint8_t* data = new uint8_t[aLength]; memcpy(data, aData, aLength); - // TODO: see bug 779144. - nsCOMPtr takePictureResult = new TakePictureResult(data, aLength, NS_LITERAL_STRING("image/jpeg"), mTakePictureOnSuccessCb, mWindowId); - /** - * Remember to set these to null so that we don't hold any extra - * references to our document's window. - */ - mTakePictureOnSuccessCb = nullptr; - mTakePictureOnErrorCb = nullptr; - nsresult rv = NS_DispatchToMainThread(takePictureResult); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch takePicture() onSuccess callback to main thread!"); + nsString s(NS_LITERAL_STRING("image/")); + s.Append(mFileFormat); + DOM_CAMERA_LOGI("Got picture, type '%s', %u bytes\n", NS_ConvertUTF16toUTF8(s).get(), aLength); + OnTakePictureComplete(data, aLength, s); + + if (mResumePreviewAfterTakingPicture) { + nsresult rv = StartPreview(); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Failed to restart camera preview (%x)\n", rv); + OnPreviewStateChange(CameraControlListener::kPreviewStopped); + } } + + DOM_CAMERA_LOGI("nsGonkCameraControl::OnTakePictureComplete() done\n"); } void -nsGonkCameraControl::TakePictureError() +nsGonkCameraControl::OnTakePictureError() { - nsCOMPtr takePictureError = new CameraErrorResult(mTakePictureOnErrorCb, NS_LITERAL_STRING("FAILURE"), mWindowId); - mTakePictureOnSuccessCb = nullptr; - mTakePictureOnErrorCb = nullptr; - nsresult rv = NS_DispatchToMainThread(takePictureError); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch takePicture() onError callback to main thread!"); - } + CameraControlImpl::OnError(CameraControlListener::kInTakePicture, + CameraControlListener::kErrorApiFailed); } -void -nsGonkCameraControl::SetPreviewSize(uint32_t aWidth, uint32_t aHeight) +nsresult +nsGonkCameraControl::SetPreviewSize(const Size& aSize) { - android::Vector previewSizes; - uint32_t bestWidth = aWidth; - uint32_t bestHeight = aHeight; + MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); + + nsTArray previewSizes; + nsresult rv = mParams.Get(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, previewSizes); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Camera failed to return any preview sizes (0x%x)\n", rv); + return rv; + } + + Size best = aSize; uint32_t minSizeDelta = UINT32_MAX; uint32_t delta; - Size size; - { - RwAutoLockRead lock(mRwLock); - mParams.getSupportedPreviewSizes(previewSizes); - } - - if (!aWidth && !aHeight) { + if (!aSize.width && !aSize.height) { // no size specified, take the first supported size - size = previewSizes[0]; - bestWidth = size.width; - bestHeight = size.height; - } else if (aWidth && aHeight) { + best = previewSizes[0]; + } else if (aSize.width && aSize.height) { // both height and width specified, find the supported size closest to requested size - for (uint32_t i = 0; i < previewSizes.size(); i++) { + uint32_t targetArea = aSize.width * aSize.height; + for (uint32_t i = 0; i < previewSizes.Length(); i++) { Size size = previewSizes[i]; - uint32_t delta = abs((long int)(size.width * size.height - aWidth * aHeight)); + uint32_t delta = abs((long int)(size.width * size.height - targetArea)); if (delta < minSizeDelta) { minSizeDelta = delta; - bestWidth = size.width; - bestHeight = size.height; + best = size; } } - } else if (!aWidth) { + } else if (!aSize.width) { // width not specified, find closest height match - for (uint32_t i = 0; i < previewSizes.size(); i++) { - size = previewSizes[i]; - delta = abs((long int)(size.height - aHeight)); + for (uint32_t i = 0; i < previewSizes.Length(); i++) { + Size size = previewSizes[i]; + delta = abs((long int)(size.height - aSize.height)); if (delta < minSizeDelta) { minSizeDelta = delta; - bestWidth = size.width; - bestHeight = size.height; + best = size; } } - } else if (!aHeight) { + } else if (!aSize.height) { // height not specified, find closest width match - for (uint32_t i = 0; i < previewSizes.size(); i++) { - size = previewSizes[i]; - delta = abs((long int)(size.width - aWidth)); + for (uint32_t i = 0; i < previewSizes.Length(); i++) { + Size size = previewSizes[i]; + delta = abs((long int)(size.width - aSize.width)); if (delta < minSizeDelta) { minSizeDelta = delta; - bestWidth = size.width; - bestHeight = size.height; + best = size; } } } - mWidth = bestWidth; - mHeight = bestHeight; { - RwAutoLockWrite lock(mRwLock); - mParams.setPreviewSize(mWidth, mHeight); + ICameraControlParameterSetAutoEnter set(this); + + // Some camera drivers will ignore our preview size if it's larger + // that the currently set video recording size, so we need to set + // both here just in case. + rv = SetAndPush(CAMERA_PARAM_PREVIEWSIZE, best); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Failed to set picture mode preview size (0x%x)\n", rv); + return rv; + } + + rv = SetAndPush(CAMERA_PARAM_VIDEOSIZE, best); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Failed to bump up picture mode video size (0x%x)\n", rv); + return rv; + } } - PushParameters(); + + mCurrentConfiguration.mPreviewSize = best; + return NS_OK; } nsresult nsGonkCameraControl::SetupVideoMode(const nsAString& aProfile) { + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); + // read preferences for camcorder mMediaProfiles = MediaProfiles::getInstance(); @@ -1328,33 +1101,46 @@ nsGonkCameraControl::SetupVideoMode(const nsAString& aProfile) int width = video->GetWidth(); int height = video->GetHeight(); int fps = video->GetFramerate(); - if (fps == -1 || width == -1 || height == -1) { - DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n", fps, width, height); + if (fps == -1 || width < 0 || height < 0) { + DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n", + fps, width, height); return NS_ERROR_FAILURE; } PullParametersImpl(); - // configure camera video recording parameters - const size_t SIZE = 256; - char buffer[SIZE]; + Size size; + size.width = static_cast(width); + size.height = static_cast(height); { - RwAutoLockWrite lock(mRwLock); - mParams.setPreviewSize(width, height); - mParams.setPreviewFrameRate(fps); + ICameraControlParameterSetAutoEnter set(this); - /** - * "record-size" is probably deprecated in later ICS; - * might need to set "video-size" instead of "record-size". - * See bug 795332. - */ - snprintf(buffer, SIZE, "%dx%d", width, height); - mParams.set("record-size", buffer); + // The camera interface allows for hardware to provide two video + // streams, a low resolution preview and a potentially high resolution + // stream for encoding. For now we don't use this and set preview and video + // size to the same thing. + nsresult rv = SetAndPush(CAMERA_PARAM_PREVIEWSIZE, size); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Failed to set video mode preview size (0x%x)\n", rv); + return rv; + } + + rv = SetAndPush(CAMERA_PARAM_VIDEOSIZE, size); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Failed to set video mode video size (0x%x)\n", rv); + return rv; + } + + rv = SetAndPush(CAMERA_PARAM_PREVIEWFRAMERATE, fps); + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Failed to set video mode frame rate (0x%x)\n", rv); + return rv; + } + mPreviewFps = fps; } - // push the updated camera configuration immediately - PushParameters(); + mLastRecorderSize = size; return NS_OK; } @@ -1364,13 +1150,14 @@ public: GonkRecorderListener(nsGonkCameraControl* aCameraControl) : mCameraControl(aCameraControl) { - DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n", __func__, __LINE__, this, mCameraControl.get()); + DOM_CAMERA_LOGT("%s:%d : this=%p, aCameraControl=%p\n", + __func__, __LINE__, this, mCameraControl.get()); } void notify(int msg, int ext1, int ext2) { if (mCameraControl) { - mCameraControl->HandleRecorderEvent(msg, ext1, ext2); + mCameraControl->OnRecorderEvent(msg, ext1, ext2); } } @@ -1386,7 +1173,7 @@ protected: }; void -nsGonkCameraControl::HandleRecorderEvent(int msg, int ext1, int ext2) +nsGonkCameraControl::OnRecorderEvent(int msg, int ext1, int ext2) { /** * Refer to base/include/media/mediarecorder.h for a complete list @@ -1434,7 +1221,7 @@ nsGonkCameraControl::HandleRecorderEvent(int msg, int ext1, int ext2) * MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED values are defined in this * enum, they are used with different ext1 codes. /o\ */ - int trackNum = -1; // no track + int trackNum = CameraControlListener::kNoTrackNumber; switch (msg) { // Recorder-related events @@ -1442,17 +1229,17 @@ nsGonkCameraControl::HandleRecorderEvent(int msg, int ext1, int ext2) switch (ext1) { case MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED: DOM_CAMERA_LOGI("recorder-event : info: maximum file size reached\n"); - OnRecorderStateChange(NS_LITERAL_STRING("FileSizeLimitReached"), ext2, trackNum); + OnRecorderStateChange(CameraControlListener::kFileSizeLimitReached, ext2, trackNum); return; case MEDIA_RECORDER_INFO_MAX_DURATION_REACHED: DOM_CAMERA_LOGI("recorder-event : info: maximum video duration reached\n"); - OnRecorderStateChange(NS_LITERAL_STRING("VideoLengthLimitReached"), ext2, trackNum); + OnRecorderStateChange(CameraControlListener::kVideoLengthLimitReached, ext2, trackNum); return; case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS: DOM_CAMERA_LOGI("recorder-event : info: track completed\n"); - OnRecorderStateChange(NS_LITERAL_STRING("TrackCompleted"), ext2, trackNum); + OnRecorderStateChange(CameraControlListener::kTrackCompleted, ext2, trackNum); return; } break; @@ -1461,12 +1248,12 @@ nsGonkCameraControl::HandleRecorderEvent(int msg, int ext1, int ext2) switch (ext1) { case MEDIA_RECORDER_ERROR_UNKNOWN: DOM_CAMERA_LOGE("recorder-event : recorder-error: %d (0x%08x)\n", ext2, ext2); - OnRecorderStateChange(NS_LITERAL_STRING("MediaRecorderFailed"), ext2, trackNum); + OnRecorderStateChange(CameraControlListener::kMediaRecorderFailed, ext2, trackNum); return; case MEDIA_ERROR_SERVER_DIED: DOM_CAMERA_LOGE("recorder-event : recorder-error: server died\n"); - OnRecorderStateChange(NS_LITERAL_STRING("MediaServerFailed"), ext2, trackNum); + OnRecorderStateChange(CameraControlListener::kMediaServerFailed, ext2, trackNum); return; } break; @@ -1479,11 +1266,11 @@ nsGonkCameraControl::HandleRecorderEvent(int msg, int ext1, int ext2) case MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS: if (ext2 == OK) { DOM_CAMERA_LOGI("recorder-event : track-complete: track %d, %d (0x%08x)\n", trackNum, ext2, ext2); - OnRecorderStateChange(NS_LITERAL_STRING("TrackCompleted"), ext2, trackNum); + OnRecorderStateChange(CameraControlListener::kTrackCompleted, ext2, trackNum); return; } DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2); - OnRecorderStateChange(NS_LITERAL_STRING("TrackFailed"), ext2, trackNum); + OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum); return; case MEDIA_RECORDER_TRACK_INFO_PROGRESS_IN_TIME: @@ -1496,7 +1283,7 @@ nsGonkCameraControl::HandleRecorderEvent(int msg, int ext1, int ext2) trackNum = (ext1 & 0xF0000000) >> 28; ext1 &= 0xFFFF; DOM_CAMERA_LOGE("recorder-event : track-error: track %d, %d (0x%08x)\n", trackNum, ext2, ext2); - OnRecorderStateChange(NS_LITERAL_STRING("TrackFailed"), ext2, trackNum); + OnRecorderStateChange(CameraControlListener::kTrackFailed, ext2, trackNum); return; } @@ -1552,34 +1339,7 @@ nsGonkCameraControl::SetupRecording(int aFd, int aRotation, int64_t aMaxFileSize } nsresult -nsGonkCameraControl::GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode) -{ - // stop any currently running preview - StopPreviewInternal(true /* forced */); - - // setup the video mode - nsresult rv = SetupVideoMode(aGetPreviewStreamVideoMode->mOptions.profile); - NS_ENSURE_SUCCESS(rv, rv); - - const RecorderVideoProfile* video = mRecorderProfile->GetVideoProfile(); - int width = video->GetWidth(); - int height = video->GetHeight(); - int fps = video->GetFramerate(); - DOM_CAMERA_LOGI("recording preview format: %d x %d (%d fps)\n", width, height, fps); - - // create and return new preview stream object - nsCOMPtr getPreviewStreamResult = new GetPreviewStreamResult(this, width, height, fps, aGetPreviewStreamVideoMode->mOnSuccessCb, mWindowId); - rv = NS_DispatchToMainThread(getPreviewStreamResult); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to dispatch GetPreviewStreamVideoMode() onSuccess callback to main thread!"); - return rv; - } - - return NS_OK; -} - -nsresult -nsGonkCameraControl::ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware) +nsGonkCameraControl::ReleaseHardwareImpl() { DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); @@ -1591,7 +1351,7 @@ nsGonkCameraControl::ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware) } // stop the preview - StopPreviewInternal(true /* forced */); + StopPreviewImpl(); // release the hardware handle if (mCameraHw.get()){ @@ -1599,11 +1359,7 @@ nsGonkCameraControl::ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware) mCameraHw.clear(); } - if (aReleaseHardware) { - nsCOMPtr releaseHardwareResult = new ReleaseHardwareResult(aReleaseHardware->mOnSuccessCb, mWindowId); - return NS_DispatchToMainThread(releaseHardwareResult); - } - + OnHardwareStateChange(CameraControlListener::kHardwareClosed); return NS_OK; } @@ -1611,8 +1367,8 @@ already_AddRefed nsGonkCameraControl::GetGonkRecorderProfileManager() { if (!mProfileManager) { - nsTArray sizes; - nsresult rv = GetVideoSizes(sizes); + nsTArray sizes; + nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes); NS_ENSURE_SUCCESS(rv, nullptr); mProfileManager = new GonkRecorderProfileManager(mCameraId); @@ -1630,75 +1386,60 @@ nsGonkCameraControl::GetRecorderProfileManagerImpl() return profileMgr.forget(); } -nsresult -nsGonkCameraControl::GetVideoSizes(nsTArray& aVideoSizes) +void +nsGonkCameraControl::OnNewPreviewFrame(layers::GraphicBufferLocked* aBuffer) { - aVideoSizes.Clear(); + nsRefPtr frame = mImageContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR); - android::Vector sizes; - { - RwAutoLockRead lock(mRwLock); + GrallocImage* videoImage = static_cast(frame.get()); - mParams.getSupportedVideoSizes(sizes); - if (sizes.size() == 0) { - DOM_CAMERA_LOGI("Camera doesn't support video independent of the preview\n"); - mParams.getSupportedPreviewSizes(sizes); - } + GrallocImage::GrallocData data; + data.mGraphicBuffer = static_cast(aBuffer); + data.mPicSize = IntSize(mCurrentConfiguration.mPreviewSize.width, + mCurrentConfiguration.mPreviewSize.height); + videoImage->SetData(data); + + OnNewPreviewFrame(frame, mCurrentConfiguration.mPreviewSize.width, + mCurrentConfiguration.mPreviewSize.height); +} + +void +nsGonkCameraControl::OnError(CameraControlListener::CameraErrorContext aWhere, + CameraControlListener::CameraError aError) +{ + if (aError == CameraControlListener::kErrorServiceFailed) { + OnPreviewStateChange(CameraControlListener::kPreviewStopped); + OnHardwareStateChange(CameraControlListener::kHardwareClosed); } - if (sizes.size() == 0) { - DOM_CAMERA_LOGW("Camera doesn't report any supported video sizes at all\n"); - return NS_OK; - } - - for (size_t i = 0; i < sizes.size(); ++i) { - idl::CameraSize size; - size.width = sizes[i].width; - size.height = sizes[i].height; - aVideoSizes.AppendElement(size); - } - return NS_OK; + CameraControlImpl::OnError(aWhere, aError); } // Gonk callback handlers. namespace mozilla { void -ReceiveImage(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength) +OnTakePictureComplete(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength) { - gc->TakePictureComplete(aData, aLength); + gc->OnTakePictureComplete(aData, aLength); } void -ReceiveImageError(nsGonkCameraControl* gc) +OnTakePictureError(nsGonkCameraControl* gc) { - gc->TakePictureError(); + gc->OnTakePictureError(); } void -AutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess) +OnAutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess) { - gc->AutoFocusComplete(aSuccess); -} - -static void -GonkFrameBuilder(Image* aImage, void* aBuffer, uint32_t aWidth, uint32_t aHeight) -{ - /** - * Cast the generic Image back to our platform-specific type and - * populate it. - */ - GrallocImage* videoImage = static_cast(aImage); - GrallocImage::GrallocData data; - data.mGraphicBuffer = static_cast(aBuffer); - data.mPicSize = IntSize(aWidth, aHeight); - videoImage->SetData(data); + gc->OnAutoFocusComplete(aSuccess); } void -ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer) +OnNewPreviewFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer) { - gc->ReceiveFrame(aBuffer, ImageFormat::GRALLOC_PLANAR_YCBCR, GonkFrameBuilder); + gc->OnNewPreviewFrame(aBuffer); } void @@ -1713,4 +1454,17 @@ OnClosed(nsGonkCameraControl* gc) gc->OnClosed(); } +void +OnError(nsGonkCameraControl* gc, CameraControlListener::CameraError aError, + int32_t aArg1, int32_t aArg2) +{ +#ifdef PR_LOGGING + DOM_CAMERA_LOGE("OnError : aError=%d, aArg1=%d, aArg2=%d\n", aError, aArg1, aArg2); +#else + unused << aArg1; + unused << aArg2; +#endif + gc->OnError(CameraControlListener::kInUnspecified, aError); +} + } // namespace mozilla diff --git a/dom/camera/GonkCameraControl.h b/dom/camera/GonkCameraControl.h index a36ea6ac7b59..c16950ff5f87 100644 --- a/dom/camera/GonkCameraControl.h +++ b/dom/camera/GonkCameraControl.h @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Mozilla Foundation + * Copyright (C) 2012-2013 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,26 +18,26 @@ #define DOM_CAMERA_GONKCAMERACONTROL_H #include "base/basictypes.h" -#include "prrwlock.h" #include +#include "mozilla/ReentrantMonitor.h" #include "DeviceStorage.h" -#include "nsIDOMCameraManager.h" -#include "DOMCameraControl.h" #include "CameraControlImpl.h" #include "CameraCommon.h" #include "GonkRecorder.h" #include "GonkCameraHwMgr.h" +#include "GonkCameraParameters.h" namespace android { -class GonkCameraHardware; -class MediaProfiles; -class GonkRecorder; + class GonkCameraHardware; + class MediaProfiles; + class GonkRecorder; } namespace mozilla { namespace layers { -class GraphicBufferLocked; + class GraphicBufferLocked; + class ImageContainer; } class GonkRecorderProfile; @@ -46,89 +46,111 @@ class GonkRecorderProfileManager; class nsGonkCameraControl : public CameraControlImpl { public: - nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId); - void DispatchInit(nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId); - nsresult Init(); + nsGonkCameraControl(uint32_t aCameraId); + nsresult Init(const Configuration* aInitialConfig); + nsresult InitImpl(); + + void OnAutoFocusComplete(bool aSuccess); + void OnTakePictureComplete(uint8_t* aData, uint32_t aLength); + void OnTakePictureError(); + void OnNewPreviewFrame(layers::GraphicBufferLocked* aBuffer); + void OnRecorderEvent(int msg, int ext1, int ext2); + void OnError(CameraControlListener::CameraErrorContext aWhere, + CameraControlListener::CameraError aError); + + virtual nsresult Set(uint32_t aKey, const nsAString& aValue) MOZ_OVERRIDE; + virtual nsresult Get(uint32_t aKey, nsAString& aValue) MOZ_OVERRIDE; + virtual nsresult Set(uint32_t aKey, double aValue) MOZ_OVERRIDE; + virtual nsresult Get(uint32_t aKey, double& aValue) MOZ_OVERRIDE; + virtual nsresult Set(uint32_t aKey, int32_t aValue) MOZ_OVERRIDE; + virtual nsresult Get(uint32_t aKey, int32_t& aValue) MOZ_OVERRIDE; + virtual nsresult Set(uint32_t aKey, int64_t aValue) MOZ_OVERRIDE; + virtual nsresult Get(uint32_t aKey, int64_t& aValue) MOZ_OVERRIDE; + virtual nsresult Set(uint32_t aKey, const Size& aValue) MOZ_OVERRIDE; + virtual nsresult Get(uint32_t aKey, Size& aValue) MOZ_OVERRIDE; + virtual nsresult Set(uint32_t aKey, const nsTArray& aRegions) MOZ_OVERRIDE; + virtual nsresult Get(uint32_t aKey, nsTArray& aRegions) MOZ_OVERRIDE; + + virtual nsresult SetLocation(const Position& aLocation) MOZ_OVERRIDE; + + virtual nsresult Get(uint32_t aKey, nsTArray& aSizes) MOZ_OVERRIDE; + virtual nsresult Get(uint32_t aKey, nsTArray& aValues) MOZ_OVERRIDE; + virtual nsresult Get(uint32_t aKey, nsTArray& aValues) MOZ_OVERRIDE; - const char* GetParameter(const char* aKey); - const char* GetParameterConstChar(uint32_t aKey); - double GetParameterDouble(uint32_t aKey); - int32_t GetParameterInt32(uint32_t aKey); - void GetParameter(uint32_t aKey, nsTArray& aRegions); - void GetParameter(uint32_t aKey, nsTArray& aSizes); - void GetParameter(uint32_t aKey, idl::CameraSize& aSize); - void SetParameter(const char* aKey, const char* aValue); - void SetParameter(uint32_t aKey, const char* aValue); - void SetParameter(uint32_t aKey, double aValue); - void SetParameter(uint32_t aKey, const nsTArray& aRegions); - void SetParameter(uint32_t aKey, int aValue); - void SetParameter(uint32_t aKey, const idl::CameraSize& aSize); - nsresult GetVideoSizes(nsTArray& aVideoSizes); nsresult PushParameters(); - - void AutoFocusComplete(bool aSuccess); - void TakePictureComplete(uint8_t* aData, uint32_t aLength); - void TakePictureError(); - void HandleRecorderEvent(int msg, int ext1, int ext2); + nsresult PullParameters(); protected: ~nsGonkCameraControl(); - nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream); - nsresult StartPreviewImpl(StartPreviewTask* aStartPreview); - nsresult StopPreviewImpl(StopPreviewTask* aStopPreview); - nsresult StopPreviewInternal(bool aForced = false); - nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus); - nsresult TakePictureImpl(TakePictureTask* aTakePicture); - nsresult StartRecordingImpl(StartRecordingTask* aStartRecording); - nsresult StopRecordingImpl(StopRecordingTask* aStopRecording); - nsresult PushParametersImpl(); - nsresult PullParametersImpl(); - nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode); - nsresult ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware); - already_AddRefed GetRecorderProfileManagerImpl(); + using CameraControlImpl::OnNewPreviewFrame; + using CameraControlImpl::OnAutoFocusComplete; + using CameraControlImpl::OnTakePictureComplete; + using CameraControlImpl::OnConfigurationChange; + using CameraControlImpl::OnError; + + virtual void BeginBatchParameterSet() MOZ_OVERRIDE; + virtual void EndBatchParameterSet() MOZ_OVERRIDE; + + virtual nsresult SetConfigurationImpl(const Configuration& aConfig) MOZ_OVERRIDE; + nsresult SetConfigurationInternal(const Configuration& aConfig); + nsresult SetPictureConfiguration(const Configuration& aConfig); + nsresult SetVideoConfiguration(const Configuration& aConfig); + + template nsresult SetAndPush(uint32_t aKey, const T& aValue); + + virtual nsresult StartPreviewImpl() MOZ_OVERRIDE; + virtual nsresult StopPreviewImpl() MOZ_OVERRIDE; + virtual nsresult AutoFocusImpl(bool aCancelExistingCall) MOZ_OVERRIDE; + virtual nsresult TakePictureImpl() MOZ_OVERRIDE; + virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor, + const StartRecordingOptions* aOptions = nullptr) MOZ_OVERRIDE; + virtual nsresult StopRecordingImpl() MOZ_OVERRIDE; + virtual nsresult PushParametersImpl() MOZ_OVERRIDE; + virtual nsresult PullParametersImpl() MOZ_OVERRIDE; + virtual nsresult ReleaseHardwareImpl() MOZ_OVERRIDE; + virtual already_AddRefed GetRecorderProfileManagerImpl() MOZ_OVERRIDE; already_AddRefed GetGonkRecorderProfileManager(); nsresult SetupRecording(int aFd, int aRotation, int64_t aMaxFileSizeBytes, int64_t aMaxVideoLengthMs); nsresult SetupVideoMode(const nsAString& aProfile); - void SetPreviewSize(uint32_t aWidth, uint32_t aHeight); - void SetThumbnailSize(uint32_t aWidth, uint32_t aHeight); - void UpdateThumbnailSize(); - void SetPictureSize(uint32_t aWidth, uint32_t aHeight); + nsresult SetPreviewSize(const Size& aSize); + + friend class SetPictureSize; + friend class SetThumbnailSize; + nsresult SetPictureSize(const Size& aSize); + nsresult SetPictureSizeImpl(const Size& aSize); + nsresult SetThumbnailSize(const Size& aSize); + nsresult UpdateThumbnailSize(); + nsresult SetThumbnailSizeImpl(const Size& aSize); int32_t RationalizeRotation(int32_t aRotation); android::sp mCameraHw; - double mExposureCompensationMin; - double mExposureCompensationStep; - bool mDeferConfigUpdate; - PRRWLock* mRwLock; - android::CameraParameters mParams; - uint32_t mWidth; - uint32_t mHeight; - uint32_t mLastPictureWidth; - uint32_t mLastPictureHeight; - uint32_t mLastThumbnailWidth; - uint32_t mLastThumbnailHeight; - enum { - PREVIEW_FORMAT_UNKNOWN, - PREVIEW_FORMAT_YUV420P, - PREVIEW_FORMAT_YUV420SP - }; - uint32_t mFormat; + Size mLastPictureSize; + Size mLastThumbnailSize; + Size mLastRecorderSize; + uint32_t mPreviewFps; + bool mResumePreviewAfterTakingPicture; - uint32_t mFps; - uint32_t mDiscardedFrameCount; + Atomic mDeferConfigUpdate; + GonkCameraParameters mParams; + + nsRefPtr mImageContainer; android::MediaProfiles* mMediaProfiles; nsRefPtr mRecorder; - // camcorder profile settings for the desired quality level + // Camcorder profile settings for the desired quality level nsRefPtr mProfileManager; nsRefPtr mRecorderProfile; nsRefPtr mVideoFile; + nsString mFileFormat; + + // Guards against calling StartPreviewImpl() while in OnTakePictureComplete(). + ReentrantMonitor mReentrantMonitor; private: nsGonkCameraControl(const nsGonkCameraControl&) MOZ_DELETE; @@ -136,12 +158,14 @@ private: }; // camera driver callbacks -void ReceiveImage(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength); -void ReceiveImageError(nsGonkCameraControl* gc); -void AutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess); -void ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer); +void OnTakePictureComplete(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength); +void OnTakePictureError(nsGonkCameraControl* gc); +void OnAutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess); +void OnNewPreviewFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer); void OnShutter(nsGonkCameraControl* gc); void OnClosed(nsGonkCameraControl* gc); +void OnError(nsGonkCameraControl* gc, CameraControlListener::CameraError aError, + int32_t aArg1, int32_t aArg2); } // namespace mozilla diff --git a/dom/camera/GonkCameraHwMgr.cpp b/dom/camera/GonkCameraHwMgr.cpp index ef54b6f1e293..e93e8caa1569 100644 --- a/dom/camera/GonkCameraHwMgr.cpp +++ b/dom/camera/GonkCameraHwMgr.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2012 Mozilla Foundation + * Copyright (C) 2012-2013 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,13 +14,14 @@ * limitations under the License. */ +#include "GonkCameraHwMgr.h" + #include #include #include "base/basictypes.h" #include "nsDebug.h" #include "GonkCameraControl.h" -#include "GonkCameraHwMgr.h" #include "GonkNativeWindow.h" #include "CameraCommon.h" @@ -28,39 +29,16 @@ using namespace mozilla; using namespace mozilla::layers; using namespace android; -#if GIHM_TIMING_RECEIVEFRAME -#define INCLUDE_TIME_H 1 -#endif -#if GIHM_TIMING_OVERALL -#define INCLUDE_TIME_H 1 -#endif - -#if INCLUDE_TIME_H -#include - -static __inline void timespecSubtract(struct timespec* a, struct timespec* b) -{ - // a = b - a - if (b->tv_nsec < a->tv_nsec) { - b->tv_nsec += 1000000000; - b->tv_sec -= 1; - } - a->tv_nsec = b->tv_nsec - a->tv_nsec; - a->tv_sec = b->tv_sec - a->tv_sec; -} -#endif - GonkCameraHardware::GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId, const sp& aCamera) : mCameraId(aCameraId) , mClosing(false) - , mMonitor("GonkCameraHardware.Monitor") , mNumFrames(0) , mCamera(aCamera) , mTarget(aTarget) , mInitialized(false) , mSensorOrientation(0) { - DOM_CAMERA_LOGT( "%s:%d : this=%p (aTarget=%p)\n", __func__, __LINE__, (void*)this, (void*)aTarget ); + DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n", __func__, __LINE__, (void*)this, (void*)aTarget); Init(); } @@ -75,7 +53,7 @@ GonkCameraHardware::OnNewFrame() DOM_CAMERA_LOGW("received null frame"); return; } - ReceiveFrame(mTarget, buffer); + OnNewPreviewFrame(mTarget, buffer); } // Android data callback @@ -93,9 +71,9 @@ GonkCameraHardware::postData(int32_t aMsgType, const sp& aDataPtr, came case CAMERA_MSG_COMPRESSED_IMAGE: if (aDataPtr != nullptr) { - ReceiveImage(mTarget, (uint8_t*)aDataPtr->pointer(), aDataPtr->size()); + OnTakePictureComplete(mTarget, static_cast(aDataPtr->pointer()), aDataPtr->size()); } else { - ReceiveImageError(mTarget); + OnTakePictureError(mTarget); } break; @@ -109,27 +87,23 @@ GonkCameraHardware::postData(int32_t aMsgType, const sp& aDataPtr, came void GonkCameraHardware::notify(int32_t aMsgType, int32_t ext1, int32_t ext2) { - bool bSuccess; if (mClosing) { return; } switch (aMsgType) { case CAMERA_MSG_FOCUS: - if (ext1) { - DOM_CAMERA_LOGI("Autofocus complete"); - bSuccess = true; - } else { - DOM_CAMERA_LOGW("Autofocus failed"); - bSuccess = false; - } - AutoFocusComplete(mTarget, bSuccess); + OnAutoFocusComplete(mTarget, !!ext1); break; case CAMERA_MSG_SHUTTER: OnShutter(mTarget); break; + case CAMERA_MSG_ERROR: + OnError(mTarget, CameraControlListener::kErrorServiceFailed, ext1, ext2); + break; + default: DOM_CAMERA_LOGE("Unhandled notify callback event %d\n", aMsgType); break; @@ -295,6 +269,13 @@ GonkCameraHardware::CancelTakePicture() DOM_CAMERA_LOGW("%s: android::Camera do not provide this capability\n", __func__); } +int +GonkCameraHardware::PushParameters(const GonkCameraParameters& aParams) +{ + const String8 s = aParams.Flatten(); + return mCamera->setParameters(s); +} + int GonkCameraHardware::PushParameters(const CameraParameters& aParams) { @@ -302,6 +283,13 @@ GonkCameraHardware::PushParameters(const CameraParameters& aParams) return mCamera->setParameters(s); } +nsresult +GonkCameraHardware::PullParameters(GonkCameraParameters& aParams) +{ + const String8 s = mCamera->getParameters(); + return aParams.Unflatten(s); +} + void GonkCameraHardware::PullParameters(CameraParameters& aParams) { diff --git a/dom/camera/GonkCameraHwMgr.h b/dom/camera/GonkCameraHwMgr.h index ee1724f61221..948eeed05797 100644 --- a/dom/camera/GonkCameraHwMgr.h +++ b/dom/camera/GonkCameraHwMgr.h @@ -27,15 +27,12 @@ #include "GonkCameraListener.h" #include "GonkNativeWindow.h" +#include "GonkCameraParameters.h" #include "mozilla/ReentrantMonitor.h" -// config -#define GIHM_TIMING_RECEIVEFRAME 0 -#define GIHM_TIMING_OVERALL 1 - - namespace mozilla { class nsGonkCameraControl; + class GonkCameraParameters; } namespace android { @@ -86,7 +83,9 @@ public: void CancelTakePicture(); int StartPreview(); void StopPreview(); + int PushParameters(const mozilla::GonkCameraParameters& aParams); int PushParameters(const CameraParameters& aParams); + nsresult PullParameters(mozilla::GonkCameraParameters& aParams); void PullParameters(CameraParameters& aParams); int StartRecording(); int StopRecording(); @@ -98,15 +97,10 @@ protected: uint32_t mCameraId; bool mClosing; - mozilla::ReentrantMonitor mMonitor; uint32_t mNumFrames; sp mCamera; mozilla::nsGonkCameraControl* mTarget; sp mNativeWindow; -#if GIHM_TIMING_OVERALL - struct timespec mStart; - struct timespec mAutoFocusStart; -#endif sp mListener; bool mInitialized; int mRawSensorOrientation; diff --git a/dom/camera/GonkCameraManager.cpp b/dom/camera/GonkCameraManager.cpp index 23087d7e55ae..46d7bacde161 100644 --- a/dom/camera/GonkCameraManager.cpp +++ b/dom/camera/GonkCameraManager.cpp @@ -16,35 +16,36 @@ #include -#include "GonkCameraControl.h" -#include "DOMCameraManager.h" #include "CameraCommon.h" -#include "mozilla/ErrorResult.h" +#include "GonkCameraControl.h" +#include "ICameraControl.h" using namespace mozilla; -// From nsDOMCameraManager, but gonk-specific! +// From ICameraControl, gonk-specific management functions nsresult -nsDOMCameraManager::GetNumberOfCameras(int32_t& aDeviceCount) +ICameraControl::GetNumberOfCameras(int32_t& aDeviceCount) { aDeviceCount = android::Camera::getNumberOfCameras(); return NS_OK; } nsresult -nsDOMCameraManager::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName) +ICameraControl::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName) { int32_t count = android::Camera::getNumberOfCameras(); + int32_t deviceNum = static_cast(aDeviceNum); + DOM_CAMERA_LOGI("GetCameraName : getNumberOfCameras() returned %d\n", count); - if (aDeviceNum > count) { - DOM_CAMERA_LOGE("GetCameraName : invalid device number"); + if (deviceNum < 0 || deviceNum > count) { + DOM_CAMERA_LOGE("GetCameraName : invalid device number (%u)\n", aDeviceNum); return NS_ERROR_NOT_AVAILABLE; } android::CameraInfo info; - int rv = android::Camera::getCameraInfo(aDeviceNum, &info); + int rv = android::Camera::getCameraInfo(deviceNum, &info); if (rv != 0) { - DOM_CAMERA_LOGE("GetCameraName : get_camera_info(%d) failed: %d\n", aDeviceNum, rv); + DOM_CAMERA_LOGE("GetCameraName : get_camera_info(%d) failed: %d\n", deviceNum, rv); return NS_ERROR_NOT_AVAILABLE; } @@ -59,27 +60,27 @@ nsDOMCameraManager::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName) default: aDeviceName.Assign("extra-camera-"); - aDeviceName.AppendInt(aDeviceNum); + aDeviceName.AppendInt(deviceNum); break; } return NS_OK; } -void -nsDOMCameraManager::GetListOfCameras(nsTArray& aList, ErrorResult& aRv) +nsresult +ICameraControl::GetListOfCameras(nsTArray& aList) { int32_t count = android::Camera::getNumberOfCameras(); - if (count <= 0) { - return; - } - DOM_CAMERA_LOGI("getListOfCameras : getNumberOfCameras() returned %d\n", count); + if (count <= 0) { + return NS_OK; + } // Allocate 2 extra slots to reserve space for 'front' and 'back' cameras // at the front of the array--we will collapse any empty slots below. aList.SetLength(2); uint32_t extraIdx = 2; - bool gotFront = false, gotBack = false; + bool gotFront = false; + bool gotBack = false; while (count--) { nsCString cameraName; nsresult result = GetCameraName(count, cameraName); @@ -104,8 +105,28 @@ nsDOMCameraManager::GetListOfCameras(nsTArray& aList, ErrorResult& aRv if (!gotFront) { aList.RemoveElementAt(1); } - + if (!gotBack) { aList.RemoveElementAt(0); } + + return NS_OK; +} + +// implementation-specific camera factory +already_AddRefed +ICameraControl::Create(uint32_t aCameraId, const Configuration* aInitialConfig) +{ + if (aInitialConfig) { + DOM_CAMERA_LOGI("Creating camera %d control, initial mode '%s'\n", + aCameraId, aInitialConfig->mMode == kVideoMode ? "video" : "picture"); + } else { + DOM_CAMERA_LOGI("Creating camera %d control, no intial configuration\n", aCameraId); + } + + nsRefPtr control = new nsGonkCameraControl(aCameraId); + nsresult rv = control->Init(aInitialConfig); + NS_ENSURE_SUCCESS(rv, nullptr); + + return control.forget(); } diff --git a/dom/camera/GonkCameraParameters.cpp b/dom/camera/GonkCameraParameters.cpp new file mode 100644 index 000000000000..6023487fa4b3 --- /dev/null +++ b/dom/camera/GonkCameraParameters.cpp @@ -0,0 +1,620 @@ +/* + * Copyright (C) 2013 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "GonkCameraParameters.h" +#include "camera/CameraParameters.h" +#include "ICameraControl.h" +#include "CameraCommon.h" + +using namespace mozilla; +using namespace android; + +/* static */ const char* +GonkCameraParameters::Parameters::GetTextKey(uint32_t aKey) +{ + switch (aKey) { + case CAMERA_PARAM_PREVIEWSIZE: + return KEY_PREVIEW_SIZE; + case CAMERA_PARAM_PREVIEWFORMAT: + return KEY_PREVIEW_FORMAT; + case CAMERA_PARAM_PREVIEWFRAMERATE: + return KEY_PREVIEW_FRAME_RATE; + case CAMERA_PARAM_EFFECT: + return KEY_EFFECT; + case CAMERA_PARAM_WHITEBALANCE: + return KEY_WHITE_BALANCE; + case CAMERA_PARAM_SCENEMODE: + return KEY_SCENE_MODE; + case CAMERA_PARAM_FLASHMODE: + return KEY_FLASH_MODE; + case CAMERA_PARAM_FOCUSMODE: + return KEY_FOCUS_MODE; + case CAMERA_PARAM_ZOOM: + return KEY_ZOOM; + case CAMERA_PARAM_METERINGAREAS: + return KEY_METERING_AREAS; + case CAMERA_PARAM_FOCUSAREAS: + return KEY_FOCUS_AREAS; + case CAMERA_PARAM_FOCALLENGTH: + return KEY_FOCAL_LENGTH; + case CAMERA_PARAM_FOCUSDISTANCENEAR: + return KEY_FOCUS_DISTANCES; + case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM: + return KEY_FOCUS_DISTANCES; + case CAMERA_PARAM_FOCUSDISTANCEFAR: + return KEY_FOCUS_DISTANCES; + case CAMERA_PARAM_EXPOSURECOMPENSATION: + return KEY_EXPOSURE_COMPENSATION; + case CAMERA_PARAM_PICTURESIZE: + return KEY_PICTURE_SIZE; + case CAMERA_PARAM_THUMBNAILQUALITY: + return KEY_JPEG_THUMBNAIL_QUALITY; + case CAMERA_PARAM_PICTURE_SIZE: + return KEY_PICTURE_SIZE; + case CAMERA_PARAM_PICTURE_FILEFORMAT: + return KEY_PICTURE_FORMAT; + case CAMERA_PARAM_PICTURE_ROTATION: + return KEY_ROTATION; + case CAMERA_PARAM_PICTURE_DATETIME: + // Not every platform defines a KEY_EXIF_DATETIME; + // for those that don't, we use the raw string key, and if the platform + // doesn't support it, it will be ignored. + // + // See bug 832494. + return "exif-datetime"; + case CAMERA_PARAM_VIDEOSIZE: + return KEY_VIDEO_SIZE; + + case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES: + return KEY_SUPPORTED_PREVIEW_SIZES; + case CAMERA_PARAM_SUPPORTED_PICTURESIZES: + return KEY_SUPPORTED_PICTURE_SIZES; + case CAMERA_PARAM_SUPPORTED_VIDEOSIZES: + return KEY_SUPPORTED_VIDEO_SIZES; + case CAMERA_PARAM_SUPPORTED_PICTUREFORMATS: + return KEY_SUPPORTED_PICTURE_FORMATS; + case CAMERA_PARAM_SUPPORTED_WHITEBALANCES: + return KEY_SUPPORTED_WHITE_BALANCE; + case CAMERA_PARAM_SUPPORTED_SCENEMODES: + return KEY_SUPPORTED_SCENE_MODES; + case CAMERA_PARAM_SUPPORTED_EFFECTS: + return KEY_SUPPORTED_EFFECTS; + case CAMERA_PARAM_SUPPORTED_FLASHMODES: + return KEY_SUPPORTED_FLASH_MODES; + case CAMERA_PARAM_SUPPORTED_FOCUSMODES: + return KEY_SUPPORTED_FOCUS_MODES; + case CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS: + return KEY_MAX_NUM_FOCUS_AREAS; + case CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS: + return KEY_MAX_NUM_METERING_AREAS; + case CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION: + return KEY_MIN_EXPOSURE_COMPENSATION; + case CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION: + return KEY_MAX_EXPOSURE_COMPENSATION; + case CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP: + return KEY_EXPOSURE_COMPENSATION_STEP; + case CAMERA_PARAM_SUPPORTED_ZOOM: + return KEY_ZOOM_SUPPORTED; + case CAMERA_PARAM_SUPPORTED_ZOOMRATIOS: + return KEY_ZOOM_RATIOS; + case CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES: + return KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES; + default: + DOM_CAMERA_LOGE("Unhandled camera parameter value %u\n", aKey); + return nullptr; + } +} + +GonkCameraParameters::GonkCameraParameters() + : mLock(PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraParameters.Lock")) + , mDirty(false) + , mInitialized(false) +{ + MOZ_COUNT_CTOR(GonkCameraParameters); + if (!mLock) { + MOZ_CRASH("OOM getting new PRRWLock"); + } +} + +GonkCameraParameters::~GonkCameraParameters() +{ + MOZ_COUNT_DTOR(GonkCameraParameters); + if (mLock) { + PR_DestroyRWLock(mLock); + mLock = nullptr; + } +} + +// Any members that need to be initialized on the first parameter pull +// need to get handled in here. +nsresult +GonkCameraParameters::Initialize() +{ + nsresult rv; + + rv = GetImpl(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, mExposureCompensationMin); + if (NS_FAILED(rv)) { + return rv; + } + rv = GetImpl(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, mExposureCompensationStep); + if (NS_FAILED(rv)) { + return rv; + } + + mInitialized = true; + return NS_OK; +} + +// Handle nsAStrings +nsresult +GonkCameraParameters::SetTranslated(uint32_t aKey, const nsAString& aValue) +{ + return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get()); +} + +nsresult +GonkCameraParameters::GetTranslated(uint32_t aKey, nsAString& aValue) +{ + const char* val; + nsresult rv = GetImpl(aKey, val); + if (NS_FAILED(rv)) { + return rv; + } + aValue.AssignASCII(val); + return NS_OK; +} + +// Handle ICameraControl::Sizes +nsresult +GonkCameraParameters::SetTranslated(uint32_t aKey, const ICameraControl::Size& aSize) +{ + if (aSize.width > INT_MAX || aSize.height > INT_MAX) { + // AOSP can only handle signed ints. + DOM_CAMERA_LOGE("Camera parameter aKey=%d out of bounds (width=%u, height=%u)\n", + aSize.width, aSize.height); + return NS_ERROR_INVALID_ARG; + } + + nsresult rv; + + switch (aKey) { + case CAMERA_PARAM_THUMBNAILSIZE: + // This is a special case--for some reason the thumbnail size + // is accessed as two separate values instead of a tuple. + // XXXmikeh - make this restore the original values on error + rv = SetImpl(Parameters::KEY_JPEG_THUMBNAIL_WIDTH, static_cast(aSize.width)); + if (NS_SUCCEEDED(rv)) { + rv = SetImpl(Parameters::KEY_JPEG_THUMBNAIL_HEIGHT, static_cast(aSize.height)); + } + break; + + case CAMERA_PARAM_VIDEOSIZE: + // "record-size" is probably deprecated in later ICS; + // might need to set "video-size" instead of "record-size"; + // for the time being, set both. See bug 795332. + rv = SetImpl("record-size", nsPrintfCString("%ux%u", aSize.width, aSize.height).get()); + if (NS_FAILED(rv)) { + break; + } + // intentional fallthrough + + default: + rv = SetImpl(aKey, nsPrintfCString("%ux%u", aSize.width, aSize.height).get()); + break; + } + + if (NS_FAILED(rv)) { + DOM_CAMERA_LOGE("Camera parameter aKey=%d failed to set (0x%x)\n", aKey, rv); + } + return rv; +} + +nsresult +GonkCameraParameters::GetTranslated(uint32_t aKey, ICameraControl::Size& aSize) +{ + nsresult rv; + + if (aKey == CAMERA_PARAM_THUMBNAILSIZE) { + int width; + int height; + + rv = GetImpl(Parameters::KEY_JPEG_THUMBNAIL_WIDTH, width); + if (NS_FAILED(rv) || width < 0) { + return NS_ERROR_FAILURE; + } + rv = GetImpl(Parameters::KEY_JPEG_THUMBNAIL_HEIGHT, height); + if (NS_FAILED(rv) || height < 0) { + return NS_ERROR_FAILURE; + } + + aSize.width = static_cast(width); + aSize.height = static_cast(height); + return NS_OK; + } + + const char* value; + rv = GetImpl(aKey, value); + if (NS_FAILED(rv) || !value || *value == '\0') { + DOM_CAMERA_LOGW("Camera parameter aKey=%d not available (0x%x)\n", aKey, rv); + return NS_ERROR_NOT_AVAILABLE; + } + if (sscanf(value, "%ux%u", &aSize.width, &aSize.height) != 2) { + DOM_CAMERA_LOGE("Camera parameter aKey=%d size tuple '%s' is invalid\n", aKey, value); + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +// Handle arrays of ICameraControl::Regions +nsresult +GonkCameraParameters::SetTranslated(uint32_t aKey, const nsTArray& aRegions) +{ + uint32_t length = aRegions.Length(); + + if (!length) { + // This tells the camera driver to revert to automatic regioning. + return SetImpl(aKey, "(0,0,0,0,0)"); + } + + nsCString s; + + for (uint32_t i = 0; i < length; ++i) { + const ICameraControl::Region* r = &aRegions[i]; + s.AppendPrintf("(%d,%d,%d,%d,%d),", r->top, r->left, r->bottom, r->right, r->weight); + } + + // remove the trailing comma + s.Trim(",", false, true, true); + + return SetImpl(aKey, s.get()); +} + +nsresult +GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray& aRegions) +{ + aRegions.Clear(); + + const char* value; + nsresult rv = GetImpl(aKey, value); + if (NS_FAILED(rv) || !value || *value == '\0') { + return NS_ERROR_FAILURE; + } + + const char* p = value; + uint32_t count = 1; + + // count the number of regions in the string + while ((p = strstr(p, "),("))) { + ++count; + p += 3; + } + + aRegions.SetCapacity(count); + ICameraControl::Region* r; + + // parse all of the region sets + uint32_t i; + for (i = 0, p = value; p && i < count; ++i, p = strchr(p + 1, '(')) { + r = aRegions.AppendElement(); + if (sscanf(p, "(%d,%d,%d,%d,%u)", &r->top, &r->left, &r->bottom, &r->right, &r->weight) != 5) { + DOM_CAMERA_LOGE("%s:%d : region tuple has bad format: '%s'\n", __func__, __LINE__, p); + aRegions.Clear(); + return NS_ERROR_FAILURE; + } + } + + return NS_OK; +} + +// Handle ICameraControl::Positions +nsresult +GonkCameraParameters::SetTranslated(uint32_t aKey, const ICameraControl::Position& aPosition) +{ + MOZ_ASSERT(aKey == CAMERA_PARAM_PICTURE_LOCATION); + + // Add any specified location information -- we don't care if these fail. + if (!isnan(aPosition.latitude)) { + DOM_CAMERA_LOGI("setting picture latitude to %lf\n", aPosition.latitude); + SetImpl(Parameters::KEY_GPS_LATITUDE, nsPrintfCString("%lf", aPosition.latitude).get()); + } + if (!isnan(aPosition.longitude)) { + DOM_CAMERA_LOGI("setting picture longitude to %lf\n", aPosition.longitude); + SetImpl(Parameters::KEY_GPS_LONGITUDE, nsPrintfCString("%lf", aPosition.longitude).get()); + } + if (!isnan(aPosition.altitude)) { + DOM_CAMERA_LOGI("setting picture altitude to %lf\n", aPosition.altitude); + SetImpl(Parameters::KEY_GPS_ALTITUDE, nsPrintfCString("%lf", aPosition.altitude).get()); + } + if (!isnan(aPosition.timestamp)) { + DOM_CAMERA_LOGI("setting picture timestamp to %lf\n", aPosition.timestamp); + SetImpl(Parameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aPosition.timestamp).get()); + } + return NS_OK; +} + +// Handle int64_ts +nsresult +GonkCameraParameters::SetTranslated(uint32_t aKey, const int64_t& aValue) +{ + if (aKey == CAMERA_PARAM_PICTURE_DATETIME) { + // Add the non-GPS timestamp. The EXIF date/time field is formatted as + // "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time + // is meant to be stored as a local time. Since we are given seconds from + // Epoch GMT, we use localtime_r() to handle the conversion. + time_t time = aValue; + if (time != aValue) { + DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aValue); + return NS_ERROR_INVALID_ARG; + } + + struct tm t; + if (!localtime_r(&time, &t)) { + DOM_CAMERA_LOGE("picture date/time couldn't be converted to local time: (%d) %s\n", errno, strerror(errno)); + return NS_ERROR_FAILURE; + } + + char dateTime[20]; + if (!strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) { + DOM_CAMERA_LOGE("picture date/time couldn't be converted to string\n"); + return NS_ERROR_FAILURE; + } + + DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime); + + return SetImpl(CAMERA_PARAM_PICTURE_DATETIME, dateTime); + } + + // You can't actually pass 64-bit parameters to Gonk. :( + int32_t v = static_cast(aValue); + if (static_cast(v) != aValue) { + return NS_ERROR_INVALID_ARG;; + } + return SetImpl(aKey, v); +} + +nsresult +GonkCameraParameters::GetTranslated(uint32_t aKey, int64_t& aValue) +{ + int val; + nsresult rv = GetImpl(aKey, val); + if (NS_FAILED(rv)) { + return rv; + } + aValue = val; + return NS_OK; +} + +// Handle doubles +nsresult +GonkCameraParameters::SetTranslated(uint32_t aKey, const double& aValue) +{ + if (aKey == CAMERA_PARAM_EXPOSURECOMPENSATION) { + /** + * Convert from real value to a Gonk index, round + * to the nearest step; index is 1-based. + */ + int index = + (aValue - mExposureCompensationMin + mExposureCompensationStep / 2) / + mExposureCompensationStep + 1; + DOM_CAMERA_LOGI("Exposure compensation = %f --> index = %d\n", aValue, index); + return SetImpl(CAMERA_PARAM_EXPOSURECOMPENSATION, index); + } + + return SetImpl(aKey, aValue); +} + +nsresult +GonkCameraParameters::GetTranslated(uint32_t aKey, double& aValue) +{ + double val; + int index = 0; + double focusDistance[3]; + const char* s; + nsresult rv; + + switch (aKey) { + case CAMERA_PARAM_ZOOM: + rv = GetImpl(CAMERA_PARAM_ZOOM, val); + if (NS_SUCCEEDED(rv)) { + val /= 100.0; + } else { + // return 1x when zooming is not supported + val = 1.0; + } + break; + + /** + * The gonk camera parameters API only exposes one focus distance property + * that contains "Near,Optimum,Far" distances, in metres, where 'Far' may + * be 'Infinity'. + */ + case CAMERA_PARAM_FOCUSDISTANCEFAR: + ++index; + // intentional fallthrough + + case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM: + ++index; + // intentional fallthrough + + case CAMERA_PARAM_FOCUSDISTANCENEAR: + rv = GetImpl(aKey, s); + if (NS_SUCCEEDED(rv)) { + if (sscanf(s, "%lf,%lf,%lf", &focusDistance[0], &focusDistance[1], &focusDistance[2]) == 3) { + val = focusDistance[index]; + } else { + val = 0.0; + } + } + break; + + case CAMERA_PARAM_EXPOSURECOMPENSATION: + rv = GetImpl(aKey, index); + if (NS_SUCCEEDED(rv)) { + if (!index) { + // NaN indicates automatic exposure compensation + val = NAN; + } else { + val = (index - 1) * mExposureCompensationStep + mExposureCompensationMin; + DOM_CAMERA_LOGI("index = %d --> compensation = %f\n", index, val); + } + } + break; + + default: + rv = GetImpl(aKey, val); + break; + } + + if (NS_SUCCEEDED(rv)) { + aValue = val; + } + return rv; +} + +// Handle ints +nsresult +GonkCameraParameters::SetTranslated(uint32_t aKey, const int& aValue) +{ + return SetImpl(aKey, aValue); +} + +nsresult +GonkCameraParameters::GetTranslated(uint32_t aKey, int& aValue) +{ + return GetImpl(aKey, aValue); +} + +// Handle uint32_ts -- Gonk only speaks int +nsresult +GonkCameraParameters::SetTranslated(uint32_t aKey, const uint32_t& aValue) +{ + if (aValue > INT_MAX) { + return NS_ERROR_INVALID_ARG; + } + + int val = static_cast(aValue); + return SetImpl(aKey, val); +} + +nsresult +GonkCameraParameters::GetTranslated(uint32_t aKey, uint32_t& aValue) +{ + int val; + nsresult rv = GetImpl(aKey, val); + if (NS_FAILED(rv)) { + return rv; + } + if (val < 0) { + return NS_ERROR_FAILURE; + } + + aValue = val; + return NS_OK; +} + +nsresult +ParseItem(const char* aStart, const char* aEnd, ICameraControl::Size* aItem) +{ + if (sscanf(aStart, "%ux%u", &aItem->width, &aItem->height) == 2) { + return NS_OK; + } + + DOM_CAMERA_LOGE("Size tuple has bad format: '%s'\n", __func__, __LINE__, aStart); + return NS_ERROR_FAILURE; +} + +nsresult +ParseItem(const char* aStart, const char* aEnd, nsAString* aItem) +{ + if (aEnd) { + aItem->AssignASCII(aStart, aEnd - aStart); + } else { + aItem->AssignASCII(aStart); + } + return NS_OK; +} + +nsresult +ParseItem(const char* aStart, const char* aEnd, double* aItem) +{ + if (sscanf(aStart, "%lf", aItem) == 1) { + return NS_OK; + } + + return NS_ERROR_FAILURE; +} + +template nsresult +GonkCameraParameters::GetListAsArray(uint32_t aKey, nsTArray& aArray) +{ + const char* p; + nsresult rv = GetImpl(aKey, p); + if (NS_FAILED(rv)) { + return rv; + } + if (!p) { + DOM_CAMERA_LOGW("Camera parameter %d not available (value is null)\n", aKey); + return NS_ERROR_NOT_AVAILABLE; + } + if (*p == '\0') { + DOM_CAMERA_LOGW("Camera parameter %d not available (value is empty string)\n", aKey); + return NS_ERROR_NOT_AVAILABLE; + } + + aArray.Clear(); + const char* comma; + + while (p) { + T* v = aArray.AppendElement(); + if (!v) { + aArray.Clear(); + return NS_ERROR_OUT_OF_MEMORY; + } + comma = strchr(p, ','); + if (comma != p) { + rv = ParseItem(p, comma, v); + if (NS_FAILED(rv)) { + aArray.Clear(); + return rv; + } + p = comma; + } + if (p) { + ++p; + } + } + + return NS_OK; +} + +nsresult +GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray& aValues) +{ + return GetListAsArray(aKey, aValues); +} + +nsresult +GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray& aValues) +{ + return GetListAsArray(aKey, aValues); +} + +nsresult +GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray& aSizes) +{ + return GetListAsArray(aKey, aSizes); +} + diff --git a/dom/camera/GonkCameraParameters.h b/dom/camera/GonkCameraParameters.h new file mode 100644 index 000000000000..396f47925d0e --- /dev/null +++ b/dom/camera/GonkCameraParameters.h @@ -0,0 +1,184 @@ +/* + * Copyright (C) 2013 Mozilla Foundation + * + * 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. + */ + +#ifndef DOM_CAMERA_GONKCAMERAPARAMETERS_H +#define DOM_CAMERA_GONKCAMERAPARAMETERS_H + +#include +#include "camera/CameraParameters.h" +#include "nsTArray.h" +#include "nsString.h" +#include "AutoRwLock.h" +#include "nsPrintfCString.h" +#include "ICameraControl.h" + +namespace mozilla { + +class GonkCameraParameters +{ +public: + GonkCameraParameters(); + virtual ~GonkCameraParameters(); + + // IMPORTANT: This class is read and written by multiple threads -- + // ALL public methods must hold mLock, for either reading or writing, + // for the life of their operation. Not doing so was the cause of + // bug 928856, which was -painful- to track down. + template nsresult + Set(uint32_t aKey, const T& aValue) + { + RwLockAutoEnterWrite lock(mLock); + nsresult rv = SetTranslated(aKey, aValue); + mDirty = mDirty || NS_SUCCEEDED(rv); + return rv; + } + + template nsresult + Get(uint32_t aKey, T& aValue) + { + RwLockAutoEnterRead lock(mLock); + return GetTranslated(aKey, aValue); + } + + bool + TestAndClearDirtyFlag() + { + bool dirty; + + RwLockAutoEnterWrite lock(mLock); + dirty = mDirty; + mDirty = false; + return dirty; + } + + android::String8 + Flatten() const + { + RwLockAutoEnterRead lock(mLock); + return mParams.flatten(); + } + + nsresult + Unflatten(const android::String8& aFlatParameters) + { + RwLockAutoEnterWrite lock(mLock); + mParams.unflatten(aFlatParameters); + if (mInitialized) { + return NS_OK; + } + + // We call Initialize() once when the parameter set is first loaded, + // to set up any constant values this class requires internally, + // e.g. the exposure compensation step and limits. + return Initialize(); + } + +protected: + PRRWLock* mLock; + bool mDirty; + bool mInitialized; + + // Required internal properties + double mExposureCompensationMin; + double mExposureCompensationStep; + + // This subclass of android::CameraParameters just gives + // all of the AOSP getters and setters the same signature. + class Parameters : public android::CameraParameters + { + public: + using android::CameraParameters::set; + + void set(const char* aKey, float aValue) { setFloat(aKey, aValue); } + void set(const char* aKey, double aValue) { setFloat(aKey, aValue); } + void get(const char* aKey, float& aRet) { aRet = getFloat(aKey); } + void get(const char* aKey, double& aRet) { aRet = getFloat(aKey); } + void get(const char* aKey, const char*& aRet) { aRet = android::CameraParameters::get(aKey); } + void get(const char* aKey, int& aRet) { aRet = getInt(aKey); } + + static const char* GetTextKey(uint32_t aKey); + }; + + Parameters mParams; + + // The *Impl() templates handle converting the parameter keys from + // their enum values to string types, if necessary. These are the + // bottom layer accessors to mParams. + template nsresult + SetImpl(uint32_t aKey, const T& aValue) + { + const char* key = Parameters::GetTextKey(aKey); + NS_ENSURE_TRUE(key, NS_ERROR_NOT_AVAILABLE); + + mParams.set(key, aValue); + return NS_OK; + } + + template nsresult + GetImpl(uint32_t aKey, T& aValue) + { + const char* key = Parameters::GetTextKey(aKey); + NS_ENSURE_TRUE(key, NS_ERROR_NOT_AVAILABLE); + + mParams.get(key, aValue); + return NS_OK; + } + + template nsresult + SetImpl(const char* aKey, const T& aValue) + { + mParams.set(aKey, aValue); + return NS_OK; + } + + template nsresult + GetImpl(const char* aKey, T& aValue) + { + mParams.get(aKey, aValue); + return NS_OK; + } + + // The *Translated() functions allow us to handle special cases; + // for example, where the thumbnail size setting is exposed as an + // ICameraControl::Size object, but is handled by the AOSP layer + // as two separate parameters. + nsresult SetTranslated(uint32_t aKey, const nsAString& aValue); + nsresult GetTranslated(uint32_t aKey, nsAString& aValue); + nsresult SetTranslated(uint32_t aKey, const ICameraControl::Size& aSize); + nsresult GetTranslated(uint32_t aKey, ICameraControl::Size& aSize); + nsresult GetTranslated(uint32_t aKey, nsTArray& aSizes); + nsresult SetTranslated(uint32_t aKey, const nsTArray& aRegions); + nsresult GetTranslated(uint32_t aKey, nsTArray& aRegions); + nsresult SetTranslated(uint32_t aKey, const ICameraControl::Position& aPosition); + nsresult SetTranslated(uint32_t aKey, const int64_t& aValue); + nsresult GetTranslated(uint32_t aKey, int64_t& aValue); + nsresult SetTranslated(uint32_t aKey, const double& aValue); + nsresult GetTranslated(uint32_t aKey, double& aValue); + nsresult SetTranslated(uint32_t aKey, const int& aValue); + nsresult GetTranslated(uint32_t aKey, int& aValue); + nsresult SetTranslated(uint32_t aKey, const uint32_t& aValue); + nsresult GetTranslated(uint32_t aKey, uint32_t& aValue); + nsresult GetTranslated(uint32_t aKey, nsTArray& aValues); + nsresult GetTranslated(uint32_t aKey, nsTArray& aValues); + + template nsresult GetListAsArray(uint32_t aKey, nsTArray& aArray); + + nsresult Initialize(); +}; + +} // namespace mozilla + +#endif // DOM_CAMERA_GONKCAMERAPARAMETERS_H diff --git a/dom/camera/GonkCameraSource.cpp b/dom/camera/GonkCameraSource.cpp index 778219a9731a..38ff03f76b16 100644 --- a/dom/camera/GonkCameraSource.cpp +++ b/dom/camera/GonkCameraSource.cpp @@ -158,8 +158,7 @@ GonkCameraSource::GonkCameraSource( Size videoSize, int32_t frameRate, bool storeMetaDataInVideoBuffers) - : mCameraHw(aCameraHw), - mCameraFlags(0), + : mCameraFlags(0), mNumInputBuffers(0), mVideoFrameRate(-1), mNumFramesReceived(0), @@ -171,7 +170,8 @@ GonkCameraSource::GonkCameraSource( mNumFramesDropped(0), mNumGlitches(0), mGlitchDurationThresholdUs(200000), - mCollectStats(false) { + mCollectStats(false), + mCameraHw(aCameraHw) { mVideoSize.width = -1; mVideoSize.height = -1; diff --git a/dom/camera/GonkRecorderProfiles.cpp b/dom/camera/GonkRecorderProfiles.cpp index 135d9d45a7fa..98d02238602e 100644 --- a/dom/camera/GonkRecorderProfiles.cpp +++ b/dom/camera/GonkRecorderProfiles.cpp @@ -14,10 +14,10 @@ * limitations under the License. */ +#include "GonkRecorderProfiles.h" #include #include "GonkRecorder.h" #include "CameraControlImpl.h" -#include "GonkRecorderProfiles.h" #include "CameraCommon.h" using namespace mozilla; diff --git a/dom/camera/GonkRecorderProfiles.h b/dom/camera/GonkRecorderProfiles.h index bfa77538002b..a64bf66249d3 100644 --- a/dom/camera/GonkRecorderProfiles.h +++ b/dom/camera/GonkRecorderProfiles.h @@ -7,6 +7,7 @@ #include #include "CameraRecorderProfiles.h" +#include "ICameraControl.h" #ifndef CHECK_SETARG #define CHECK_SETARG(x) \ @@ -86,7 +87,7 @@ public: * supported by the camera hardware. (Just because it appears in a recorder * profile doesn't mean the hardware can handle it.) */ - void SetSupportedResolutions(const nsTArray& aSizes) + void SetSupportedResolutions(const nsTArray& aSizes) { mSupportedSizes = aSizes; } /** @@ -104,7 +105,7 @@ public: protected: virtual ~GonkRecorderProfileManager(); - nsTArray mSupportedSizes; + nsTArray mSupportedSizes; }; }; // namespace mozilla diff --git a/dom/camera/ICameraControl.h b/dom/camera/ICameraControl.h index 52cc1a662734..0c68d1571f7e 100644 --- a/dom/camera/ICameraControl.h +++ b/dom/camera/ICameraControl.h @@ -5,69 +5,185 @@ #ifndef DOM_CAMERA_ICAMERACONTROL_H #define DOM_CAMERA_ICAMERACONTROL_H -#include "nsIFile.h" -#include "nsIDOMCameraManager.h" -#include "DictionaryHelpers.h" -#include "CameraCommon.h" +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsAutoPtr.h" +#include "nsISupportsImpl.h" class DeviceStorageFileDescriptor; +class nsIFile; + namespace mozilla { -class DOMCameraPreview; +class CameraControlListener; class RecorderProfileManager; +// XXXmikeh - In some future patch this should move into the ICameraControl +// class as well, and the names updated to the 'k'-style; +// e.g. kParamPreviewSize, etc. +enum { + // current settings + CAMERA_PARAM_PREVIEWSIZE, + CAMERA_PARAM_PREVIEWFORMAT, + CAMERA_PARAM_PREVIEWFRAMERATE, + CAMERA_PARAM_VIDEOSIZE, + CAMERA_PARAM_PICTURE_SIZE, + CAMERA_PARAM_PICTURE_FILEFORMAT, + CAMERA_PARAM_PICTURE_ROTATION, + CAMERA_PARAM_PICTURE_LOCATION, + CAMERA_PARAM_PICTURE_DATETIME, + CAMERA_PARAM_EFFECT, + CAMERA_PARAM_WHITEBALANCE, + CAMERA_PARAM_SCENEMODE, + CAMERA_PARAM_FLASHMODE, + CAMERA_PARAM_FOCUSMODE, + CAMERA_PARAM_ZOOM, + CAMERA_PARAM_METERINGAREAS, + CAMERA_PARAM_FOCUSAREAS, + CAMERA_PARAM_FOCALLENGTH, + CAMERA_PARAM_FOCUSDISTANCENEAR, + CAMERA_PARAM_FOCUSDISTANCEOPTIMUM, + CAMERA_PARAM_FOCUSDISTANCEFAR, + CAMERA_PARAM_EXPOSURECOMPENSATION, + CAMERA_PARAM_PICTURESIZE, + CAMERA_PARAM_THUMBNAILSIZE, + CAMERA_PARAM_THUMBNAILQUALITY, + CAMERA_PARAM_SENSORANGLE, + + // supported features + CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, + CAMERA_PARAM_SUPPORTED_PICTURESIZES, + CAMERA_PARAM_SUPPORTED_VIDEOSIZES, + CAMERA_PARAM_SUPPORTED_PICTUREFORMATS, + CAMERA_PARAM_SUPPORTED_WHITEBALANCES, + CAMERA_PARAM_SUPPORTED_SCENEMODES, + CAMERA_PARAM_SUPPORTED_EFFECTS, + CAMERA_PARAM_SUPPORTED_FLASHMODES, + CAMERA_PARAM_SUPPORTED_FOCUSMODES, + CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, + CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, + CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, + CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION, + CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, + CAMERA_PARAM_SUPPORTED_ZOOM, + CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, + CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES +}; + class ICameraControl { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ICameraControl) - virtual nsresult GetPreviewStream(idl::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0; - virtual nsresult StartPreview(DOMCameraPreview* aDOMPreview) = 0; - virtual void StopPreview() = 0; - virtual nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError) = 0; - virtual nsresult TakePicture(const idl::CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, idl::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) = 0; - virtual nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor *aFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0; + static nsresult GetNumberOfCameras(int32_t& aDeviceCount); + static nsresult GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName); + static nsresult GetListOfCameras(nsTArray& aList); + + enum Mode { + kUnspecifiedMode, + kPictureMode, + kVideoMode, + }; + + struct Size { + uint32_t width; + uint32_t height; + }; + + struct Region { + int32_t top; + int32_t left; + int32_t bottom; + int32_t right; + uint32_t weight; + }; + + struct Position { + double latitude; + double longitude; + double altitude; + double timestamp; + }; + + struct StartRecordingOptions { + uint32_t rotation; + uint32_t maxFileSizeBytes; + uint32_t maxVideoLengthMs; + }; + + struct Configuration { + Mode mMode; + Size mPreviewSize; + nsString mRecorderProfile; + }; + static already_AddRefed Create(uint32_t aCameraId, + const Configuration* aInitialConfig); + virtual nsresult SetConfiguration(const Configuration& aConfig) = 0; + + virtual void AddListener(CameraControlListener* aListener) = 0; + virtual void RemoveListener(CameraControlListener* aListener) = 0; + + virtual nsresult StartPreview() = 0; + virtual nsresult StopPreview() = 0; + virtual nsresult AutoFocus(bool aCancelExistingCall) = 0; + virtual nsresult TakePicture() = 0; + virtual nsresult StartRecording(DeviceStorageFileDescriptor *aFileDescriptor, + const StartRecordingOptions* aOptions = nullptr) = 0; virtual nsresult StopRecording() = 0; - virtual nsresult GetPreviewStreamVideoMode(idl::CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0; - virtual nsresult ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError) = 0; + virtual nsresult ReleaseHardware() = 0; virtual nsresult Set(uint32_t aKey, const nsAString& aValue) = 0; virtual nsresult Get(uint32_t aKey, nsAString& aValue) = 0; virtual nsresult Set(uint32_t aKey, double aValue) = 0; - virtual nsresult Get(uint32_t aKey, double* aValue) = 0; - virtual nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit) = 0; - virtual nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue) = 0; - virtual nsresult Set(nsICameraShutterCallback* aOnShutter) = 0; - virtual nsresult Get(nsICameraShutterCallback** aOnShutter) = 0; - virtual nsresult Set(nsICameraClosedCallback* aOnClosed) = 0; - virtual nsresult Get(nsICameraClosedCallback** aOnClosed) = 0; - virtual nsresult Set(nsICameraRecorderStateChange* aOnRecorderStateChange) = 0; - virtual nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange) = 0; - virtual nsresult Set(nsICameraPreviewStateChange* aOnPreviewStateChange) = 0; - virtual nsresult Get(nsICameraPreviewStateChange** aOnPreviewStateChange) = 0; - virtual nsresult Set(uint32_t aKey, const idl::CameraSize& aSize) = 0; - virtual nsresult Get(uint32_t aKey, idl::CameraSize& aSize) = 0; - virtual nsresult Get(uint32_t aKey, int32_t* aValue) = 0; - virtual nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue) = 0; - virtual nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue) = 0; - virtual nsresult GetVideoSizes(nsTArray& aVideoSizes) = 0; + virtual nsresult Get(uint32_t aKey, double& aValue) = 0; + virtual nsresult Set(uint32_t aKey, int32_t aValue) = 0; + virtual nsresult Get(uint32_t aKey, int32_t& aValue) = 0; + virtual nsresult Set(uint32_t aKey, int64_t aValue) = 0; + virtual nsresult Get(uint32_t aKey, int64_t& aValue) = 0; + virtual nsresult Set(uint32_t aKey, const Size& aValue) = 0; + virtual nsresult Get(uint32_t aKey, Size& aValue) = 0; + virtual nsresult Set(uint32_t aKey, const nsTArray& aRegions) = 0; + virtual nsresult Get(uint32_t aKey, nsTArray& aRegions) = 0; + + virtual nsresult SetLocation(const Position& aLocation) = 0; + + virtual nsresult Get(uint32_t aKey, nsTArray& aSizes) = 0; + virtual nsresult Get(uint32_t aKey, nsTArray& aValues) = 0; + virtual nsresult Get(uint32_t aKey, nsTArray& aValues) = 0; + virtual already_AddRefed GetRecorderProfileManager() = 0; virtual uint32_t GetCameraId() = 0; - virtual const char* GetParameter(const char* aKey) = 0; - virtual const char* GetParameterConstChar(uint32_t aKey) = 0; - virtual double GetParameterDouble(uint32_t aKey) = 0; - virtual void GetParameter(uint32_t aKey, nsTArray& aRegions) = 0; - virtual void SetParameter(const char* aKey, const char* aValue) = 0; - virtual void SetParameter(uint32_t aKey, const char* aValue) = 0; - virtual void SetParameter(uint32_t aKey, double aValue) = 0; - virtual void SetParameter(uint32_t aKey, const nsTArray& aRegions) = 0; - virtual void Shutdown() = 0; protected: virtual ~ICameraControl() { } + + friend class ICameraControlParameterSetAutoEnter; + + virtual void BeginBatchParameterSet() = 0; + virtual void EndBatchParameterSet() = 0; +}; + +// Helper class to make it easy to update a batch of camera parameters; +// the parameters are applied atomically when this object goes out of +// scope. +class ICameraControlParameterSetAutoEnter +{ +public: + ICameraControlParameterSetAutoEnter(ICameraControl* aCameraControl) + : mCameraControl(aCameraControl) + { + mCameraControl->BeginBatchParameterSet(); + } + virtual ~ICameraControlParameterSetAutoEnter() + { + mCameraControl->EndBatchParameterSet(); + } + +protected: + nsRefPtr mCameraControl; }; } // namespace mozilla diff --git a/dom/camera/moz.build b/dom/camera/moz.build index a7dc8869483c..02de152bdff7 100644 --- a/dom/camera/moz.build +++ b/dom/camera/moz.build @@ -4,15 +4,9 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -XPIDL_SOURCES += [ - 'nsIDOMCameraManager.idl', -] - if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': TEST_DIRS += ['test'] -XPIDL_MODULE = 'dom_camera' - EXPORTS += [ 'CameraCommon.h', 'CameraPreviewMediaStream.h', @@ -26,8 +20,8 @@ SOURCES += [ 'CameraRecorderProfiles.cpp', 'DOMCameraCapabilities.cpp', 'DOMCameraControl.cpp', + 'DOMCameraControlListener.cpp', 'DOMCameraManager.cpp', - 'DOMCameraPreview.cpp', ] if CONFIG['MOZ_B2G_CAMERA']: @@ -35,6 +29,7 @@ if CONFIG['MOZ_B2G_CAMERA']: 'GonkCameraControl.cpp', 'GonkCameraHwMgr.cpp', 'GonkCameraManager.cpp', + 'GonkCameraParameters.cpp', 'GonkCameraSource.cpp', 'GonkRecorder.cpp', 'GonkRecorderProfiles.cpp', diff --git a/dom/camera/nsIDOMCameraManager.idl b/dom/camera/nsIDOMCameraManager.idl deleted file mode 100644 index 803ffea5ed07..000000000000 --- a/dom/camera/nsIDOMCameraManager.idl +++ /dev/null @@ -1,229 +0,0 @@ -#include "domstubs.idl" - -#include "nsIDOMMediaStream.idl" -#include "nsIDOMDOMRequest.idl" - - -interface nsIDOMBlob; -interface nsIDOMDeviceStorage; - -/* Used to set the dimensions of a captured picture, - a preview stream, a video capture stream, etc. */ -dictionary CameraSize { - unsigned long width; - unsigned long height; -}; - -/* Camera regions are used to set focus and metering areas; - the coordinates are referenced to the sensor: - (-1000, -1000) is the top left corner - (1000, 1000) is the bottom left corner - The weight of the region can range from 0 to 1000. */ -dictionary CameraRegion { - long top; - long left; - long bottom; - long right; - unsigned long weight; -}; - -/* The position information to record in the image header. - 'NaN' indicates the information is not available. */ -dictionary CameraPosition { - double latitude; - double longitude; - double altitude; - double timestamp; -}; - -[scriptable, uuid(0711a4af-73c2-481d-85bc-0ba3ec36c004)] -interface nsICameraCapabilities : nsISupports -{ - /* an array of objects with 'height' and 'width' properties - supported for the preview stream */ - [implicit_jscontext] - readonly attribute jsval previewSizes; - - /* an array of objects with 'height' and 'width' properties - supported for picture taking */ - [implicit_jscontext] - readonly attribute jsval pictureSizes; - - /* an array of objects with 'height' and 'width' properties - supported for thumbnail sizes in taken pictures */ - [implicit_jscontext] - readonly attribute jsval thumbnailSizes; - - /* an array of strings, e.g. [ "jpeg", "rgb565" ] */ - [implicit_jscontext] - readonly attribute jsval fileFormats; - - /* an array of strings, e.g. [ "auto", "fluorescent", etc. ] */ - [implicit_jscontext] - readonly attribute jsval whiteBalanceModes; - - /* an array of strings, e.g. [ "auto", "night", "beach", etc. ] */ - [implicit_jscontext] - readonly attribute jsval sceneModes; - - /* an array of strings, e.g. [ "normal", "sepia", "mono", etc. ] */ - [implicit_jscontext] - readonly attribute jsval effects; - - /* an array of strings, e.g. [ "auto", "off", "on", etc. ] */ - [implicit_jscontext] - readonly attribute jsval flashModes; - - /* an array of strings, e.g. [ "auto", "fixed", "macro", etc. ] */ - [implicit_jscontext] - readonly attribute jsval focusModes; - - /* the maximum number of focus areas supported by the camera */ - [implicit_jscontext] - readonly attribute long maxFocusAreas; - - /* the minimum supported exposure compensation value */ - [implicit_jscontext] - readonly attribute double minExposureCompensation; - - /* the maximum supported exposure compensation value */ - [implicit_jscontext] - readonly attribute double maxExposureCompensation; - - /* exposure compensation minimum step-size */ - [implicit_jscontext] - readonly attribute double stepExposureCompensation; - - /* the maximum number of metering areas supported by the camera */ - [implicit_jscontext] - readonly attribute long maxMeteringAreas; - - /* an array of doubles, e.g. [ 1.0, 1.2, 1.5, 2.0, 3.0, etc. ], - or null if zooming is not supported */ - [implicit_jscontext] - readonly attribute jsval zoomRatios; - - /* an array of objects with 'height' and 'width' properties - supported for video recording */ - [implicit_jscontext] - readonly attribute jsval videoSizes; - - /* an object with attributes for each of the supported recorder - profiles, e.g. recorderProfiles.cif, recorderProfiles.qvga, - etc. */ - [implicit_jscontext] - readonly attribute jsval recorderProfiles; -}; - -/* These properties affect the video recording preview, e.g. - { - profile: "1080p", - rotation: 0 - } - - 'profile' is one of the profiles returned by - nsICameraCapabilities.recorderProfiles'; if this profile is missing, - an arbitrary profile will be chosen. - - 'rotation' is the degrees clockwise to rotate the preview; if - this option is not supported, it will be ignored; if this option - is missing, the default is 0. -*/ -dictionary CameraRecorderOptions -{ - DOMString profile; - long rotation; -}; - -/* These properties affect the actual video recording, e.g. - { - rotation: 0, - maxFileSizeBytes: 1024 * 1024, - maxVideoLengthMs: 0 - } - - 'rotation' is the degrees clockwise to rotate the recorded video; if - this options is not supported, it will be ignored; if this option is - missing, the default is 0. - - 'maxFileSizeBytes' is the maximum size in bytes to which the recorded - video file will be allowed to grow. - - 'maxVideoLengthMs' is the maximum length in milliseconds to which the - recorded video will be allowed to grow. - - if either 'maxFileSizeBytes' or 'maxVideoLengthMs' is missing, zero, - or negative, that limit will be disabled. -*/ -dictionary CameraStartRecordingOptions -{ - long rotation; - long long maxFileSizeBytes; - long long maxVideoLengthMs; -}; - -[scriptable, function, uuid(0444a687-4bc9-462c-8246-5423f0fe46a4)] -interface nsICameraPreviewStreamCallback : nsISupports -{ - void handleEvent(in nsIDOMMediaStream stream); -}; - -[scriptable, function, uuid(6baa4ac7-9c25-4c48-9bb0-5193b38b9b0a)] -interface nsICameraAutoFocusCallback : nsISupports -{ - void handleEvent(in boolean success); -}; - -[scriptable, function, uuid(17af779e-cb6f-4ca5-890c-06468ff82e4f)] -interface nsICameraTakePictureCallback : nsISupports -{ - void handleEvent(in nsIDOMBlob picture); -}; - -[scriptable, function, uuid(89a762f8-581b-410a-ad86-e2bd2113ad82)] -interface nsICameraStartRecordingCallback : nsISupports -{ - void handleEvent(); -}; - -[scriptable, function, uuid(fb80db71-e315-42f0-9ea9-dd3dd312ed70)] -interface nsICameraShutterCallback : nsISupports -{ - void handleEvent(); -}; - -[scriptable, function, uuid(0ef0f01e-ce74-4741-9bba-54376adfb7a2)] -interface nsICameraClosedCallback : nsISupports -{ - void handleEvent(); -}; - -[scriptable, function, uuid(550d675a-257d-4713-8b3d-0da53eba68fc)] -interface nsICameraRecorderStateChange : nsISupports -{ - void handleStateChange(in DOMString newState); -}; - -[scriptable, function, uuid(d1634592-43fd-4117-a2b2-419aec841cc4)] -interface nsICameraPreviewStateChange : nsISupports -{ - void handleStateChange(in DOMString newState); -}; - -[scriptable, function, uuid(f84d607b-554c-413d-8810-cf848642765a)] -interface nsICameraReleaseCallback : nsISupports -{ - void handleEvent(); -}; - -[scriptable, function, uuid(a302c6c9-3776-4d1d-a395-f4105d47c3d3)] -interface nsICameraErrorCallback : nsISupports -{ - void handleEvent(in DOMString error); -}; - -[scriptable, function, uuid(16de7703-dc43-4766-99c5-ff30a9ab92d7)] -interface nsICameraGetCameraCallback : nsISupports -{ - void handleEvent(in nsISupports camera); -}; diff --git a/dom/camera/test/mochitest.ini b/dom/camera/test/mochitest.ini index 0188b1bad179..8bd4a3a65701 100644 --- a/dom/camera/test/mochitest.ini +++ b/dom/camera/test/mochitest.ini @@ -1,3 +1,4 @@ [DEFAULT] [test_camera.html] +[test_camera_2.html] diff --git a/dom/camera/test/test_camera.html b/dom/camera/test/test_camera.html index ec39ffd33ca2..ed425ee73545 100644 --- a/dom/camera/test/test_camera.html +++ b/dom/camera/test/test_camera.html @@ -1,7 +1,7 @@ - Test for mozCameras + Test for mozCameras.getCamera() with separate .setConfiguration() call @@ -11,8 +11,14 @@ This image is going to load + + + + +