/* 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_CAMERACONTROLIMPL_H #define DOM_CAMERA_CAMERACONTROLIMPL_H #include "nsCOMPtr.h" #include "nsDOMFile.h" #include "DictionaryHelpers.h" #include "nsIDOMDeviceStorage.h" #include "DOMCameraManager.h" #include "DOMCameraPreview.h" #include "ICameraControl.h" #include "CameraCommon.h" namespace mozilla { using namespace dom; class GetPreviewStreamTask; class StartPreviewTask; class StopPreviewTask; class AutoFocusTask; class TakePictureTask; class StartRecordingTask; class StopRecordingTask; class SetParameterTask; class GetParameterTask; class GetPreviewStreamVideoModeTask; 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; public: CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId); nsresult GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError); nsresult StartPreview(DOMCameraPreview* aDOMPreview); void StopPreview(); nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError); nsresult TakePicture(CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError); nsresult StartRecording(CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError); nsresult StopRecording(); nsresult GetPreviewStreamVideoMode(CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* 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 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); } 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 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 nsresult GetVideoSizes(nsTArray& aVideoSizes) = 0; virtual nsresult PushParameters() = 0; virtual void Shutdown(); bool ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder); void OnShutter(); void OnClosed(); uint64_t GetWindowId() { return mWindowId; } protected: 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 nsresult PushParametersImpl() = 0; virtual nsresult PullParametersImpl() = 0; virtual nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode) = 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; /** * '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; nsCOMPtr mAutoFocusOnSuccessCb; nsCOMPtr mAutoFocusOnErrorCb; nsCOMPtr mTakePictureOnSuccessCb; nsCOMPtr mTakePictureOnErrorCb; nsCOMPtr mStartRecordingOnSuccessCb; nsCOMPtr mStartRecordingOnErrorCb; nsCOMPtr mOnShutterCb; nsCOMPtr mOnClosedCb; private: CameraControlImpl(const CameraControlImpl&) MOZ_DELETE; CameraControlImpl& operator=(const CameraControlImpl&) MOZ_DELETE; }; // Error result runnable class CameraErrorResult : public nsRunnable { public: CameraErrorResult(nsICameraErrorCallback* onError, const nsString& aErrorMsg, uint64_t aWindowId) : mOnErrorCb(onError) , mErrorMsg(aErrorMsg) , mWindowId(aWindowId) { } NS_IMETHOD Run() { MOZ_ASSERT(NS_IsMainThread()); if (mOnErrorCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { mOnErrorCb->HandleEvent(mErrorMsg); } return NS_OK; } protected: nsCOMPtr 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, nsICameraPreviewStreamCallback* 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(); protected: nsRefPtr mCameraControl; uint32_t mWidth; uint32_t mHeight; uint32_t mFramesPerSecond; nsCOMPtr mOnSuccessCb; uint64_t mWindowId; }; // Get the desired preview stream. class GetPreviewStreamTask : public nsRunnable { public: GetPreviewStreamTask(CameraControlImpl* aCameraControl, CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) : mSize(aSize) , mCameraControl(aCameraControl) , mOnSuccessCb(onSuccess) , mOnErrorCb(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() { nsresult rv = mCameraControl->GetPreviewStreamImpl(this); if (NS_FAILED(rv) && mOnErrorCb) { rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId())); NS_ENSURE_SUCCESS(rv, rv); } return rv; } CameraSize mSize; nsRefPtr mCameraControl; nsCOMPtr mOnSuccessCb; nsCOMPtr mOnErrorCb; }; // Return the autofocus status to JS. Runs on the main thread. class AutoFocusResult : public nsRunnable { public: AutoFocusResult(bool aSuccess, nsICameraAutoFocusCallback* onSuccess, uint64_t aWindowId) : mSuccess(aSuccess) , mOnSuccessCb(onSuccess) , mWindowId(aWindowId) { } virtual ~AutoFocusResult() { } NS_IMETHOD Run() { MOZ_ASSERT(NS_IsMainThread()); if (mOnSuccessCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { mOnSuccessCb->HandleEvent(mSuccess); } return NS_OK; } protected: bool mSuccess; nsCOMPtr mOnSuccessCb; uint64_t mWindowId; }; // Autofocus the camera. class AutoFocusTask : public nsRunnable { public: AutoFocusTask(CameraControlImpl* aCameraControl, nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError) : mCameraControl(aCameraControl) , mOnSuccessCb(onSuccess) , mOnErrorCb(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() { 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) && mOnErrorCb) { rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId())); NS_ENSURE_SUCCESS(rv, rv); } return rv; } nsRefPtr mCameraControl; nsCOMPtr mOnSuccessCb; nsCOMPtr mOnErrorCb; }; // Return the captured picture to JS. Runs on the main thread. class TakePictureResult : public nsRunnable { public: TakePictureResult(nsIDOMBlob* aImage, nsICameraTakePictureCallback* onSuccess, uint64_t aWindowId) : mImage(aImage) , 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_ASSERT(NS_IsMainThread()); DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); if (mOnSuccessCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { mOnSuccessCb->HandleEvent(mImage); } DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); return NS_OK; } protected: nsCOMPtr mImage; nsCOMPtr mOnSuccessCb; uint64_t mWindowId; }; // Capture a still image with the camera. class TakePictureTask : public nsRunnable { public: TakePictureTask(CameraControlImpl* aCameraControl, CameraSize aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) : mCameraControl(aCameraControl) , mSize(aSize) , mRotation(aRotation) , mFileFormat(aFileFormat) , mPosition(aPosition) , mOnSuccessCb(onSuccess) , mOnErrorCb(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() { 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) && mOnErrorCb) { rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId())); NS_ENSURE_SUCCESS(rv, rv); } return rv; } nsRefPtr mCameraControl; CameraSize mSize; int32_t mRotation; nsString mFileFormat; CameraPosition mPosition; nsCOMPtr mOnSuccessCb; nsCOMPtr mOnErrorCb; }; // Return the result of starting recording. Runs on the main thread. class StartRecordingResult : public nsRunnable { public: StartRecordingResult(nsICameraStartRecordingCallback* onSuccess, uint64_t aWindowId) : mOnSuccessCb(onSuccess) , mWindowId(aWindowId) { } virtual ~StartRecordingResult() { } NS_IMETHOD Run() { MOZ_ASSERT(NS_IsMainThread()); if (mOnSuccessCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { mOnSuccessCb->HandleEvent(); } return NS_OK; } protected: nsCOMPtr mOnSuccessCb; uint64_t mWindowId; }; // Start video recording. class StartRecordingTask : public nsRunnable { public: StartRecordingTask(CameraControlImpl* aCameraControl, CameraStartRecordingOptions aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId) : mCameraControl(aCameraControl) , mOptions(aOptions) , mFolder(aFolder) , mFilename(aFilename) , mOnSuccessCb(onSuccess) , mOnErrorCb(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() { 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; CameraStartRecordingOptions mOptions; nsCOMPtr mFolder; nsString mFilename; nsCOMPtr mOnSuccessCb; nsCOMPtr 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() { 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() { 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() { DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); mCameraControl->StopPreviewImpl(this); DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); return NS_OK; } nsRefPtr mCameraControl; }; // Return the resulting preview stream to JS. Runs on the main thread. class GetPreviewStreamVideoModeResult : public nsRunnable { public: GetPreviewStreamVideoModeResult(nsIDOMMediaStream* aStream, nsICameraPreviewStreamCallback* onSuccess) : mStream(aStream) , mOnSuccessCb(onSuccess) { DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); } virtual ~GetPreviewStreamVideoModeResult() { DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); } NS_IMETHOD Run() { MOZ_ASSERT(NS_IsMainThread()); if (mOnSuccessCb) { mOnSuccessCb->HandleEvent(mStream); } return NS_OK; } protected: nsCOMPtr mStream; nsCOMPtr mOnSuccessCb; }; // Get the video mode preview stream. class GetPreviewStreamVideoModeTask : public nsRunnable { public: GetPreviewStreamVideoModeTask(CameraControlImpl* aCameraControl, CameraRecorderOptions aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) : mCameraControl(aCameraControl) , mOptions(aOptions) , mOnSuccessCb(onSuccess) , mOnErrorCb(onError) { } NS_IMETHOD Run() { 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) && mOnErrorCb) { rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId())); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; } nsRefPtr mCameraControl; CameraRecorderOptions mOptions; nsCOMPtr mOnSuccessCb; nsCOMPtr mOnErrorCb; }; } // namespace mozilla #endif // DOM_CAMERA_CAMERACONTROLIMPL_H