michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "DOMCameraControl.h" michael@0: #include "base/basictypes.h" michael@0: #include "nsCOMPtr.h" michael@0: #include "nsDOMClassInfo.h" michael@0: #include "nsHashPropertyBag.h" michael@0: #include "nsThread.h" michael@0: #include "DeviceStorage.h" michael@0: #include "DeviceStorageFileDescriptor.h" michael@0: #include "mozilla/dom/TabChild.h" michael@0: #include "mozilla/ipc/FileDescriptorUtils.h" michael@0: #include "mozilla/MediaManager.h" michael@0: #include "mozilla/Services.h" michael@0: #include "mozilla/unused.h" michael@0: #include "nsIAppsService.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIDOMDeviceStorage.h" michael@0: #include "nsIDOMEventListener.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "Navigator.h" michael@0: #include "nsXULAppAPI.h" michael@0: #include "DOMCameraManager.h" michael@0: #include "DOMCameraCapabilities.h" michael@0: #include "CameraCommon.h" michael@0: #include "nsGlobalWindow.h" michael@0: #include "CameraPreviewMediaStream.h" michael@0: #include "mozilla/dom/CameraControlBinding.h" michael@0: #include "mozilla/dom/CameraManagerBinding.h" michael@0: #include "mozilla/dom/CameraCapabilitiesBinding.h" michael@0: #include "DOMCameraDetectedFace.h" michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::ipc; michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMMediaStream) michael@0: NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream) michael@0: michael@0: NS_IMPL_ADDREF_INHERITED(nsDOMCameraControl, DOMMediaStream) michael@0: NS_IMPL_RELEASE_INHERITED(nsDOMCameraControl, DOMMediaStream) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_INHERITED(nsDOMCameraControl, DOMMediaStream, michael@0: mCapabilities, michael@0: mWindow, michael@0: mGetCameraOnSuccessCb, michael@0: mGetCameraOnErrorCb, michael@0: mAutoFocusOnSuccessCb, michael@0: mAutoFocusOnErrorCb, michael@0: mTakePictureOnSuccessCb, michael@0: mTakePictureOnErrorCb, michael@0: mStartRecordingOnSuccessCb, michael@0: mStartRecordingOnErrorCb, michael@0: mReleaseOnSuccessCb, michael@0: mReleaseOnErrorCb, michael@0: mSetConfigurationOnSuccessCb, michael@0: mSetConfigurationOnErrorCb, michael@0: mOnShutterCb, michael@0: mOnClosedCb, michael@0: mOnRecorderStateChangeCb, michael@0: mOnPreviewStateChangeCb, michael@0: mOnAutoFocusMovingCb, michael@0: mOnFacesDetectedCb) michael@0: michael@0: /* static */ michael@0: bool michael@0: nsDOMCameraControl::HasSupport(JSContext* aCx, JSObject* aGlobal) michael@0: { michael@0: return Navigator::HasCameraSupport(aCx, aGlobal); michael@0: } michael@0: michael@0: class mozilla::StartRecordingHelper : public nsIDOMEventListener michael@0: { michael@0: public: michael@0: NS_DECL_ISUPPORTS michael@0: NS_DECL_NSIDOMEVENTLISTENER michael@0: michael@0: StartRecordingHelper(nsDOMCameraControl* aDOMCameraControl) michael@0: : mDOMCameraControl(aDOMCameraControl) michael@0: { michael@0: MOZ_COUNT_CTOR(StartRecordingHelper); michael@0: } michael@0: michael@0: protected: michael@0: virtual ~StartRecordingHelper() michael@0: { michael@0: MOZ_COUNT_DTOR(StartRecordingHelper); michael@0: } michael@0: michael@0: protected: michael@0: nsRefPtr mDOMCameraControl; michael@0: }; michael@0: michael@0: NS_IMETHODIMP michael@0: StartRecordingHelper::HandleEvent(nsIDOMEvent* aEvent) michael@0: { michael@0: nsString eventType; michael@0: aEvent->GetType(eventType); michael@0: michael@0: mDOMCameraControl->OnCreatedFileDescriptor(eventType.EqualsLiteral("success")); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMPL_ISUPPORTS(mozilla::StartRecordingHelper, nsIDOMEventListener) michael@0: michael@0: nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration() michael@0: : CameraConfiguration() michael@0: , mMaxFocusAreas(0) michael@0: , mMaxMeteringAreas(0) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration); michael@0: } michael@0: michael@0: nsDOMCameraControl::DOMCameraConfiguration::DOMCameraConfiguration(const CameraConfiguration& aConfiguration) michael@0: : CameraConfiguration(aConfiguration) michael@0: , mMaxFocusAreas(0) michael@0: , mMaxMeteringAreas(0) michael@0: { michael@0: MOZ_COUNT_CTOR(nsDOMCameraControl::DOMCameraConfiguration); michael@0: } michael@0: michael@0: nsDOMCameraControl::DOMCameraConfiguration::~DOMCameraConfiguration() michael@0: { michael@0: MOZ_COUNT_DTOR(nsDOMCameraControl::DOMCameraConfiguration); michael@0: } michael@0: michael@0: nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, michael@0: const CameraConfiguration& aInitialConfig, michael@0: GetCameraCallback* aOnSuccess, michael@0: CameraErrorCallback* aOnError, michael@0: nsPIDOMWindow* aWindow) michael@0: : DOMMediaStream() michael@0: , mCameraControl(nullptr) michael@0: , mAudioChannelAgent(nullptr) michael@0: , mGetCameraOnSuccessCb(aOnSuccess) michael@0: , mGetCameraOnErrorCb(aOnError) michael@0: , mAutoFocusOnSuccessCb(nullptr) michael@0: , mAutoFocusOnErrorCb(nullptr) michael@0: , mTakePictureOnSuccessCb(nullptr) michael@0: , mTakePictureOnErrorCb(nullptr) michael@0: , mStartRecordingOnSuccessCb(nullptr) michael@0: , mStartRecordingOnErrorCb(nullptr) michael@0: , mReleaseOnSuccessCb(nullptr) michael@0: , mReleaseOnErrorCb(nullptr) michael@0: , mSetConfigurationOnSuccessCb(nullptr) michael@0: , mSetConfigurationOnErrorCb(nullptr) michael@0: , mOnShutterCb(nullptr) michael@0: , mOnClosedCb(nullptr) michael@0: , mOnRecorderStateChangeCb(nullptr) michael@0: , mOnPreviewStateChangeCb(nullptr) michael@0: , mOnAutoFocusMovingCb(nullptr) michael@0: , mOnFacesDetectedCb(nullptr) michael@0: , mWindow(aWindow) michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); michael@0: mInput = new CameraPreviewMediaStream(this); michael@0: michael@0: SetIsDOMBinding(); michael@0: michael@0: nsRefPtr initialConfig = michael@0: new DOMCameraConfiguration(aInitialConfig); michael@0: michael@0: // Create and initialize the underlying camera. michael@0: ICameraControl::Configuration config; michael@0: michael@0: switch (aInitialConfig.mMode) { michael@0: case CameraMode::Picture: michael@0: config.mMode = ICameraControl::kPictureMode; michael@0: break; michael@0: michael@0: case CameraMode::Video: michael@0: config.mMode = ICameraControl::kVideoMode; michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode!"); michael@0: } michael@0: michael@0: config.mPreviewSize.width = aInitialConfig.mPreviewSize.mWidth; michael@0: config.mPreviewSize.height = aInitialConfig.mPreviewSize.mHeight; michael@0: config.mRecorderProfile = aInitialConfig.mRecorderProfile; michael@0: michael@0: mCameraControl = ICameraControl::Create(aCameraId); michael@0: mCurrentConfiguration = initialConfig.forget(); michael@0: michael@0: // Attach our DOM-facing media stream to our viewfinder stream. michael@0: mStream = mInput; michael@0: MOZ_ASSERT(mWindow, "Shouldn't be created with a null window!"); michael@0: if (mWindow->GetExtantDoc()) { michael@0: CombineWithPrincipal(mWindow->GetExtantDoc()->NodePrincipal()); michael@0: } michael@0: michael@0: // Register a listener for camera events. michael@0: mListener = new DOMCameraControlListener(this, mInput); michael@0: mCameraControl->AddListener(mListener); michael@0: michael@0: // Start the camera... michael@0: nsresult rv = mCameraControl->Start(&config); michael@0: if (NS_FAILED(rv)) { michael@0: mListener->OnError(DOMCameraControlListener::kInStartCamera, michael@0: DOMCameraControlListener::kErrorApiFailed); michael@0: } michael@0: } michael@0: michael@0: nsDOMCameraControl::~nsDOMCameraControl() michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); michael@0: } michael@0: michael@0: JSObject* michael@0: nsDOMCameraControl::WrapObject(JSContext* aCx) michael@0: { michael@0: return CameraControlBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: bool michael@0: nsDOMCameraControl::IsWindowStillActive() michael@0: { michael@0: return nsDOMCameraManager::IsWindowStillActive(mWindow->WindowID()); michael@0: } michael@0: michael@0: // JS-to-native helpers michael@0: // Setter for weighted regions: { top, bottom, left, right, weight } michael@0: nsresult michael@0: nsDOMCameraControl::Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit) michael@0: { michael@0: if (aLimit == 0) { michael@0: DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__, __LINE__); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (!aValue.isObject()) { michael@0: return NS_ERROR_INVALID_ARG; michael@0: } michael@0: michael@0: uint32_t length = 0; michael@0: michael@0: JS::Rooted regions(aCx, &aValue.toObject()); michael@0: if (!JS_GetArrayLength(aCx, regions, &length)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit); michael@0: if (length > aLimit) { michael@0: length = aLimit; michael@0: } michael@0: michael@0: nsTArray regionArray; michael@0: regionArray.SetCapacity(length); michael@0: michael@0: for (uint32_t i = 0; i < length; ++i) { michael@0: JS::Rooted v(aCx); michael@0: michael@0: if (!JS_GetElement(aCx, regions, i, &v)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: CameraRegion region; michael@0: if (!region.Init(aCx, v)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: ICameraControl::Region* r = regionArray.AppendElement(); michael@0: r->top = region.mTop; michael@0: r->left = region.mLeft; michael@0: r->bottom = region.mBottom; michael@0: r->right = region.mRight; michael@0: r->weight = region.mWeight; michael@0: michael@0: DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%u\n", michael@0: i, michael@0: r->top, michael@0: r->left, michael@0: r->bottom, michael@0: r->right, michael@0: r->weight michael@0: ); michael@0: } michael@0: return mCameraControl->Set(aKey, regionArray); michael@0: } michael@0: michael@0: // Getter for weighted regions: { top, bottom, left, right, weight } michael@0: nsresult michael@0: nsDOMCameraControl::Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue) michael@0: { michael@0: nsTArray regionArray; michael@0: michael@0: nsresult rv = mCameraControl->Get(aKey, regionArray); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: JS::Rooted array(aCx, JS_NewArrayObject(aCx, 0)); michael@0: if (!array) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: uint32_t length = regionArray.Length(); michael@0: DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length); michael@0: michael@0: for (uint32_t i = 0; i < length; ++i) { michael@0: ICameraControl::Region* r = ®ionArray[i]; michael@0: JS::Rooted v(aCx); michael@0: michael@0: JS::Rooted o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: if (!o) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: DOM_CAMERA_LOGI("top=%d\n", r->top); michael@0: v = INT_TO_JSVAL(r->top); michael@0: if (!JS_SetProperty(aCx, o, "top", v)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: DOM_CAMERA_LOGI("left=%d\n", r->left); michael@0: v = INT_TO_JSVAL(r->left); michael@0: if (!JS_SetProperty(aCx, o, "left", v)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: DOM_CAMERA_LOGI("bottom=%d\n", r->bottom); michael@0: v = INT_TO_JSVAL(r->bottom); michael@0: if (!JS_SetProperty(aCx, o, "bottom", v)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: DOM_CAMERA_LOGI("right=%d\n", r->right); michael@0: v = INT_TO_JSVAL(r->right); michael@0: if (!JS_SetProperty(aCx, o, "right", v)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: DOM_CAMERA_LOGI("weight=%d\n", r->weight); michael@0: v = INT_TO_JSVAL(r->weight); michael@0: if (!JS_SetProperty(aCx, o, "weight", v)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: if (!JS_SetElement(aCx, array, i, o)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: } michael@0: michael@0: *aValue = JS::ObjectValue(*array); michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::GetEffect(nsString& aEffect, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_EFFECT, aEffect); michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetEffect(const nsAString& aEffect, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Set(CAMERA_PARAM_EFFECT, aEffect); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::GetWhiteBalanceMode(nsString& aWhiteBalanceMode, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode); michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetWhiteBalanceMode(const nsAString& aWhiteBalanceMode, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Set(CAMERA_PARAM_WHITEBALANCE, aWhiteBalanceMode); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::GetSceneMode(nsString& aSceneMode, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_SCENEMODE, aSceneMode); michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetSceneMode(const nsAString& aSceneMode, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Set(CAMERA_PARAM_SCENEMODE, aSceneMode); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::GetFlashMode(nsString& aFlashMode, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_FLASHMODE, aFlashMode); michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetFlashMode(const nsAString& aFlashMode, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Set(CAMERA_PARAM_FLASHMODE, aFlashMode); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::GetFocusMode(nsString& aFocusMode, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSMODE, aFocusMode); michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetFocusMode(const nsAString& aFocusMode, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Set(CAMERA_PARAM_FOCUSMODE, aFocusMode); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::GetIsoMode(nsString& aIsoMode, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_ISOMODE, aIsoMode); michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetIsoMode(const nsAString& aIsoMode, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Set(CAMERA_PARAM_ISOMODE, aIsoMode); michael@0: } michael@0: michael@0: double michael@0: nsDOMCameraControl::GetZoom(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: double zoom; michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_ZOOM, zoom); michael@0: return zoom; michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::SetZoom(double aZoom, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->Set(CAMERA_PARAM_ZOOM, aZoom); michael@0: } michael@0: michael@0: /* attribute jsval meteringAreas; */ michael@0: void michael@0: nsDOMCameraControl::GetMeteringAreas(JSContext* cx, michael@0: JS::MutableHandle aMeteringAreas, michael@0: ErrorResult& aRv) michael@0: { michael@0: aRv = Get(cx, CAMERA_PARAM_METERINGAREAS, aMeteringAreas.address()); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::SetMeteringAreas(JSContext* cx, JS::Handle aMeteringAreas, ErrorResult& aRv) michael@0: { michael@0: aRv = Set(cx, CAMERA_PARAM_METERINGAREAS, aMeteringAreas, michael@0: mCurrentConfiguration->mMaxMeteringAreas); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::GetFocusAreas(JSContext* cx, michael@0: JS::MutableHandle aFocusAreas, michael@0: ErrorResult& aRv) michael@0: { michael@0: aRv = Get(cx, CAMERA_PARAM_FOCUSAREAS, aFocusAreas.address()); michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetFocusAreas(JSContext* cx, JS::Handle aFocusAreas, ErrorResult& aRv) michael@0: { michael@0: aRv = Set(cx, CAMERA_PARAM_FOCUSAREAS, aFocusAreas, michael@0: mCurrentConfiguration->mMaxFocusAreas); michael@0: } michael@0: michael@0: static nsresult michael@0: GetSize(JSContext* aCx, JS::Value* aValue, const ICameraControl::Size& aSize) michael@0: { michael@0: JS::Rooted o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr())); michael@0: if (!o) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: JS::Rooted v(aCx); michael@0: michael@0: v = INT_TO_JSVAL(aSize.width); michael@0: if (!JS_SetProperty(aCx, o, "width", v)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: v = INT_TO_JSVAL(aSize.height); michael@0: if (!JS_SetProperty(aCx, o, "height", v)) { michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: *aValue = JS::ObjectValue(*o); michael@0: return NS_OK; michael@0: } michael@0: michael@0: /* attribute any pictureSize */ michael@0: void michael@0: nsDOMCameraControl::GetPictureSize(JSContext* cx, michael@0: JS::MutableHandle aSize, michael@0: ErrorResult& aRv) michael@0: { michael@0: ICameraControl::Size size; michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_PICTURE_SIZE, size); michael@0: if (aRv.Failed()) { michael@0: return; michael@0: } michael@0: michael@0: aRv = GetSize(cx, aSize.address(), size); michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetPictureSize(JSContext* aCx, JS::Handle aSize, ErrorResult& aRv) michael@0: { michael@0: CameraSize size; michael@0: if (!size.Init(aCx, aSize)) { michael@0: aRv = NS_ERROR_FAILURE; michael@0: return; michael@0: } michael@0: michael@0: ICameraControl::Size s = { size.mWidth, size.mHeight }; michael@0: aRv = mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, s); michael@0: } michael@0: michael@0: /* attribute any thumbnailSize */ michael@0: void michael@0: nsDOMCameraControl::GetThumbnailSize(JSContext* aCx, michael@0: JS::MutableHandle aSize, michael@0: ErrorResult& aRv) michael@0: { michael@0: ICameraControl::Size size; michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_THUMBNAILSIZE, size); michael@0: if (aRv.Failed()) { michael@0: return; michael@0: } michael@0: michael@0: aRv = GetSize(aCx, aSize.address(), size); michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetThumbnailSize(JSContext* aCx, JS::Handle aSize, ErrorResult& aRv) michael@0: { michael@0: CameraSize size; michael@0: if (!size.Init(aCx, aSize)) { michael@0: aRv = NS_ERROR_FAILURE; michael@0: return; michael@0: } michael@0: michael@0: ICameraControl::Size s = { size.mWidth, size.mHeight }; michael@0: aRv = mCameraControl->Set(CAMERA_PARAM_THUMBNAILSIZE, s); michael@0: } michael@0: michael@0: double michael@0: nsDOMCameraControl::GetFocalLength(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: double focalLength; michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_FOCALLENGTH, focalLength); michael@0: return focalLength; michael@0: } michael@0: michael@0: double michael@0: nsDOMCameraControl::GetFocusDistanceNear(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: double distance; michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCENEAR, distance); michael@0: return distance; michael@0: } michael@0: michael@0: double michael@0: nsDOMCameraControl::GetFocusDistanceOptimum(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: double distance; michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEOPTIMUM, distance); michael@0: return distance; michael@0: } michael@0: michael@0: double michael@0: nsDOMCameraControl::GetFocusDistanceFar(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: double distance; michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_FOCUSDISTANCEFAR, distance); michael@0: return distance; michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::SetExposureCompensation(const Optional& aCompensation, ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: if (!aCompensation.WasPassed()) { michael@0: // use NaN to switch the camera back into auto mode michael@0: aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, NAN); michael@0: return; michael@0: } michael@0: michael@0: aRv = mCameraControl->Set(CAMERA_PARAM_EXPOSURECOMPENSATION, aCompensation.Value()); michael@0: } michael@0: michael@0: double michael@0: nsDOMCameraControl::GetExposureCompensation(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: double compensation; michael@0: aRv = mCameraControl->Get(CAMERA_PARAM_EXPOSURECOMPENSATION, compensation); michael@0: return compensation; michael@0: } michael@0: michael@0: int32_t michael@0: nsDOMCameraControl::SensorAngle() michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: int32_t angle = 0; michael@0: mCameraControl->Get(CAMERA_PARAM_SENSORANGLE, angle); michael@0: return angle; michael@0: } michael@0: michael@0: // Callback attributes michael@0: michael@0: CameraShutterCallback* michael@0: nsDOMCameraControl::GetOnShutter() michael@0: { michael@0: return mOnShutterCb; michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetOnShutter(CameraShutterCallback* aCb) michael@0: { michael@0: mOnShutterCb = aCb; michael@0: } michael@0: michael@0: CameraClosedCallback* michael@0: nsDOMCameraControl::GetOnClosed() michael@0: { michael@0: return mOnClosedCb; michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetOnClosed(CameraClosedCallback* aCb) michael@0: { michael@0: mOnClosedCb = aCb; michael@0: } michael@0: michael@0: CameraRecorderStateChange* michael@0: nsDOMCameraControl::GetOnRecorderStateChange() michael@0: { michael@0: return mOnRecorderStateChangeCb; michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetOnRecorderStateChange(CameraRecorderStateChange* aCb) michael@0: { michael@0: mOnRecorderStateChangeCb = aCb; michael@0: } michael@0: michael@0: CameraPreviewStateChange* michael@0: nsDOMCameraControl::GetOnPreviewStateChange() michael@0: { michael@0: return mOnPreviewStateChangeCb; michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetOnPreviewStateChange(CameraPreviewStateChange* aCb) michael@0: { michael@0: mOnPreviewStateChangeCb = aCb; michael@0: } michael@0: michael@0: CameraAutoFocusMovingCallback* michael@0: nsDOMCameraControl::GetOnAutoFocusMoving() michael@0: { michael@0: return mOnAutoFocusMovingCb; michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetOnAutoFocusMoving(CameraAutoFocusMovingCallback* aCb) michael@0: { michael@0: mOnAutoFocusMovingCb = aCb; michael@0: } michael@0: michael@0: CameraFaceDetectionCallback* michael@0: nsDOMCameraControl::GetOnFacesDetected() michael@0: { michael@0: return mOnFacesDetectedCb; michael@0: } michael@0: void michael@0: nsDOMCameraControl::SetOnFacesDetected(CameraFaceDetectionCallback* aCb) michael@0: { michael@0: mOnFacesDetectedCb = aCb; michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsDOMCameraControl::Capabilities() michael@0: { michael@0: nsRefPtr caps = mCapabilities; michael@0: michael@0: if (!caps) { michael@0: caps = new CameraCapabilities(mWindow); michael@0: nsresult rv = caps->Populate(mCameraControl); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGW("Failed to populate camera capabilities (%d)\n", rv); michael@0: return nullptr; michael@0: } michael@0: mCapabilities = caps; michael@0: } michael@0: michael@0: return caps.forget(); michael@0: } michael@0: michael@0: // Methods. michael@0: void michael@0: nsDOMCameraControl::StartRecording(const CameraStartRecordingOptions& aOptions, michael@0: nsDOMDeviceStorage& aStorageArea, michael@0: const nsAString& aFilename, michael@0: CameraStartRecordingCallback& aOnSuccess, michael@0: const Optional >& aOnError, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: NotifyRecordingStatusChange(NS_LITERAL_STRING("starting")); michael@0: michael@0: #ifdef MOZ_B2G michael@0: if (!mAudioChannelAgent) { michael@0: mAudioChannelAgent = do_CreateInstance("@mozilla.org/audiochannelagent;1"); michael@0: if (mAudioChannelAgent) { michael@0: // Camera app will stop recording when it falls to the background, so no callback is necessary. michael@0: mAudioChannelAgent->Init(mWindow, (int32_t)AudioChannel::Content, nullptr); michael@0: // Video recording doesn't output any sound, so it's not necessary to check canPlay. michael@0: int32_t canPlay; michael@0: mAudioChannelAgent->StartPlaying(&canPlay); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: nsCOMPtr request; michael@0: mDSFileDescriptor = new DeviceStorageFileDescriptor(); michael@0: aRv = aStorageArea.CreateFileDescriptor(aFilename, mDSFileDescriptor.get(), michael@0: getter_AddRefs(request)); michael@0: if (aRv.Failed()) { michael@0: return; michael@0: } michael@0: michael@0: mOptions = aOptions; michael@0: mStartRecordingOnSuccessCb = &aOnSuccess; michael@0: mStartRecordingOnErrorCb = nullptr; michael@0: if (aOnError.WasPassed()) { michael@0: mStartRecordingOnErrorCb = &aOnError.Value(); michael@0: } michael@0: michael@0: nsCOMPtr listener = new StartRecordingHelper(this); michael@0: request->AddEventListener(NS_LITERAL_STRING("success"), listener, false); michael@0: request->AddEventListener(NS_LITERAL_STRING("error"), listener, false); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::OnCreatedFileDescriptor(bool aSucceeded) michael@0: { michael@0: if (aSucceeded && mDSFileDescriptor->mFileDescriptor.IsValid()) { michael@0: ICameraControl::StartRecordingOptions o; michael@0: michael@0: o.rotation = mOptions.mRotation; michael@0: o.maxFileSizeBytes = mOptions.mMaxFileSizeBytes; michael@0: o.maxVideoLengthMs = mOptions.mMaxVideoLengthMs; michael@0: o.autoEnableLowLightTorch = mOptions.mAutoEnableLowLightTorch; michael@0: nsresult rv = mCameraControl->StartRecording(mDSFileDescriptor.get(), &o); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: return; michael@0: } michael@0: } michael@0: OnError(CameraControlListener::kInStartRecording, NS_LITERAL_STRING("FAILURE")); michael@0: michael@0: if (mDSFileDescriptor->mFileDescriptor.IsValid()) { michael@0: // An error occured. We need to manually close the file associated with the michael@0: // FileDescriptor, and we shouldn't do this on the main thread, so we michael@0: // use a little helper. michael@0: nsRefPtr closer = michael@0: new CloseFileRunnable(mDSFileDescriptor->mFileDescriptor); michael@0: closer->Dispatch(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::StopRecording(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: #ifdef MOZ_B2G michael@0: if (mAudioChannelAgent) { michael@0: mAudioChannelAgent->StopPlaying(); michael@0: mAudioChannelAgent = nullptr; michael@0: } michael@0: #endif michael@0: michael@0: aRv = mCameraControl->StopRecording(); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::ResumePreview(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->StartPreview(); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::SetConfiguration(const CameraConfiguration& aConfiguration, michael@0: const Optional >& aOnSuccess, michael@0: const Optional >& aOnError, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: nsRefPtr cb = mTakePictureOnSuccessCb; michael@0: if (cb) { michael@0: // We're busy taking a picture, can't change modes right now. michael@0: if (aOnError.WasPassed()) { michael@0: ErrorResult ignored; michael@0: aOnError.Value().Call(NS_LITERAL_STRING("Busy"), ignored); michael@0: } michael@0: aRv = NS_ERROR_FAILURE; michael@0: return; michael@0: } michael@0: michael@0: ICameraControl::Configuration config; michael@0: config.mRecorderProfile = aConfiguration.mRecorderProfile; michael@0: config.mPreviewSize.width = aConfiguration.mPreviewSize.mWidth; michael@0: config.mPreviewSize.height = aConfiguration.mPreviewSize.mHeight; michael@0: config.mMode = ICameraControl::kPictureMode; michael@0: if (aConfiguration.mMode == CameraMode::Video) { michael@0: config.mMode = ICameraControl::kVideoMode; michael@0: } michael@0: michael@0: mSetConfigurationOnSuccessCb = nullptr; michael@0: if (aOnSuccess.WasPassed()) { michael@0: mSetConfigurationOnSuccessCb = &aOnSuccess.Value(); michael@0: } michael@0: mSetConfigurationOnErrorCb = nullptr; michael@0: if (aOnError.WasPassed()) { michael@0: mSetConfigurationOnErrorCb = &aOnError.Value(); michael@0: } michael@0: michael@0: aRv = mCameraControl->SetConfiguration(config); michael@0: } michael@0: michael@0: class ImmediateErrorCallback : public nsRunnable michael@0: { michael@0: public: michael@0: ImmediateErrorCallback(CameraErrorCallback* aCallback, const nsAString& aMessage) michael@0: : mCallback(aCallback) michael@0: , mMessage(aMessage) michael@0: { } michael@0: michael@0: NS_IMETHODIMP michael@0: Run() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: ErrorResult ignored; michael@0: mCallback->Call(mMessage, ignored); michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: nsRefPtr mCallback; michael@0: nsString mMessage; michael@0: }; michael@0: michael@0: michael@0: void michael@0: nsDOMCameraControl::AutoFocus(CameraAutoFocusCallback& aOnSuccess, michael@0: const Optional >& aOnError, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: nsRefPtr cb = mAutoFocusOnSuccessCb; michael@0: if (cb) { michael@0: if (aOnError.WasPassed()) { michael@0: // There is already a call to AutoFocus() in progress, abort this new one michael@0: // and invoke the error callback (if one was passed in). michael@0: NS_DispatchToMainThread(new ImmediateErrorCallback(&aOnError.Value(), michael@0: NS_LITERAL_STRING("AutoFocusAlreadyInProgress"))); michael@0: } michael@0: aRv = NS_ERROR_FAILURE; michael@0: return; michael@0: } michael@0: michael@0: mAutoFocusOnSuccessCb = &aOnSuccess; michael@0: mAutoFocusOnErrorCb = nullptr; michael@0: if (aOnError.WasPassed()) { michael@0: mAutoFocusOnErrorCb = &aOnError.Value(); michael@0: } michael@0: michael@0: aRv = mCameraControl->AutoFocus(); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::StartFaceDetection(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->StartFaceDetection(); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::StopFaceDetection(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->StopFaceDetection(); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::TakePicture(const CameraPictureOptions& aOptions, michael@0: CameraTakePictureCallback& aOnSuccess, michael@0: const Optional >& aOnError, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: nsRefPtr cb = mTakePictureOnSuccessCb; michael@0: if (cb) { michael@0: if (aOnError.WasPassed()) { michael@0: // There is already a call to TakePicture() in progress, abort this new michael@0: // one and invoke the error callback (if one was passed in). michael@0: NS_DispatchToMainThread(new ImmediateErrorCallback(&aOnError.Value(), michael@0: NS_LITERAL_STRING("TakePictureAlreadyInProgress"))); michael@0: } michael@0: aRv = NS_ERROR_FAILURE; michael@0: return; michael@0: } michael@0: michael@0: { michael@0: ICameraControlParameterSetAutoEnter batch(mCameraControl); michael@0: michael@0: // XXXmikeh - remove this: see bug 931155 michael@0: ICameraControl::Size s; michael@0: s.width = aOptions.mPictureSize.mWidth; michael@0: s.height = aOptions.mPictureSize.mHeight; michael@0: michael@0: ICameraControl::Position p; michael@0: p.latitude = aOptions.mPosition.mLatitude; michael@0: p.longitude = aOptions.mPosition.mLongitude; michael@0: p.altitude = aOptions.mPosition.mAltitude; michael@0: p.timestamp = aOptions.mPosition.mTimestamp; michael@0: michael@0: if (s.width && s.height) { michael@0: mCameraControl->Set(CAMERA_PARAM_PICTURE_SIZE, s); michael@0: } michael@0: mCameraControl->Set(CAMERA_PARAM_PICTURE_ROTATION, aOptions.mRotation); michael@0: mCameraControl->Set(CAMERA_PARAM_PICTURE_FILEFORMAT, aOptions.mFileFormat); michael@0: mCameraControl->Set(CAMERA_PARAM_PICTURE_DATETIME, aOptions.mDateTime); michael@0: mCameraControl->SetLocation(p); michael@0: } michael@0: michael@0: mTakePictureOnSuccessCb = &aOnSuccess; michael@0: mTakePictureOnErrorCb = nullptr; michael@0: if (aOnError.WasPassed()) { michael@0: mTakePictureOnErrorCb = &aOnError.Value(); michael@0: } michael@0: michael@0: aRv = mCameraControl->TakePicture(); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::ReleaseHardware(const Optional >& aOnSuccess, michael@0: const Optional >& aOnError, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: mReleaseOnSuccessCb = nullptr; michael@0: if (aOnSuccess.WasPassed()) { michael@0: mReleaseOnSuccessCb = &aOnSuccess.Value(); michael@0: } michael@0: mReleaseOnErrorCb = nullptr; michael@0: if (aOnError.WasPassed()) { michael@0: mReleaseOnErrorCb = &aOnError.Value(); michael@0: } michael@0: michael@0: aRv = mCameraControl->Stop(); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::ResumeContinuousFocus(ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: aRv = mCameraControl->ResumeContinuousFocus(); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::Shutdown() michael@0: { michael@0: DOM_CAMERA_LOGI("%s:%d\n", __func__, __LINE__); michael@0: MOZ_ASSERT(mCameraControl); michael@0: michael@0: // Remove any pending solicited event handlers; these michael@0: // reference our window object, which in turn references michael@0: // us. If we don't remove them, we can leak DOM objects. michael@0: mGetCameraOnSuccessCb = nullptr; michael@0: mGetCameraOnErrorCb = nullptr; michael@0: mAutoFocusOnSuccessCb = nullptr; michael@0: mAutoFocusOnErrorCb = nullptr; michael@0: mTakePictureOnSuccessCb = nullptr; michael@0: mTakePictureOnErrorCb = nullptr; michael@0: mStartRecordingOnSuccessCb = nullptr; michael@0: mStartRecordingOnErrorCb = nullptr; michael@0: mReleaseOnSuccessCb = nullptr; michael@0: mReleaseOnErrorCb = nullptr; michael@0: mSetConfigurationOnSuccessCb = nullptr; michael@0: mSetConfigurationOnErrorCb = nullptr; michael@0: michael@0: // Remove all of the unsolicited event handlers too. michael@0: mOnShutterCb = nullptr; michael@0: mOnClosedCb = nullptr; michael@0: mOnRecorderStateChangeCb = nullptr; michael@0: mOnPreviewStateChangeCb = nullptr; michael@0: mOnAutoFocusMovingCb = nullptr; michael@0: mOnFacesDetectedCb = nullptr; michael@0: michael@0: mCameraControl->Shutdown(); michael@0: } michael@0: michael@0: nsresult michael@0: nsDOMCameraControl::NotifyRecordingStatusChange(const nsString& aMsg) michael@0: { michael@0: NS_ENSURE_TRUE(mWindow, NS_ERROR_FAILURE); michael@0: michael@0: return MediaManager::NotifyRecordingStatusChange(mWindow, michael@0: aMsg, michael@0: true /* aIsAudio */, michael@0: true /* aIsVideo */); michael@0: } michael@0: michael@0: // Camera Control event handlers--must only be called from the Main Thread! michael@0: void michael@0: nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState aState) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: ErrorResult ignored; michael@0: michael@0: DOM_CAMERA_LOGI("DOM OnHardwareStateChange(%d)\n", aState); michael@0: michael@0: switch (aState) { michael@0: case CameraControlListener::kHardwareOpen: michael@0: // The hardware is open, so we can return a camera to JS, even if michael@0: // the preview hasn't started yet. michael@0: if (mGetCameraOnSuccessCb) { michael@0: nsRefPtr cb = mGetCameraOnSuccessCb.forget(); michael@0: ErrorResult ignored; michael@0: mGetCameraOnErrorCb = nullptr; michael@0: cb->Call(*this, *mCurrentConfiguration, ignored); michael@0: } michael@0: break; michael@0: michael@0: case CameraControlListener::kHardwareClosed: michael@0: if (mReleaseOnSuccessCb) { michael@0: // If we have this event handler, this was a solicited hardware close. michael@0: nsRefPtr cb = mReleaseOnSuccessCb.forget(); michael@0: mReleaseOnErrorCb = nullptr; michael@0: cb->Call(ignored); michael@0: } else if(mOnClosedCb) { michael@0: // If not, something else closed the hardware. michael@0: nsRefPtr cb = mOnClosedCb; michael@0: cb->Call(ignored); michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unanticipated camera hardware state"); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::OnShutter() michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: DOM_CAMERA_LOGI("DOM ** SNAP **\n"); michael@0: michael@0: nsRefPtr cb = mOnShutterCb; michael@0: if (cb) { michael@0: ErrorResult ignored; michael@0: cb->Call(ignored); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::OnPreviewStateChange(CameraControlListener::PreviewState aState) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: if (!mOnPreviewStateChangeCb) { michael@0: return; michael@0: } michael@0: michael@0: nsString state; michael@0: switch (aState) { michael@0: case CameraControlListener::kPreviewStarted: michael@0: state = NS_LITERAL_STRING("started"); michael@0: break; michael@0: michael@0: default: michael@0: state = NS_LITERAL_STRING("stopped"); michael@0: break; michael@0: } michael@0: michael@0: nsRefPtr cb = mOnPreviewStateChangeCb; michael@0: ErrorResult ignored; michael@0: cb->Call(state, ignored); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::OnRecorderStateChange(CameraControlListener::RecorderState aState, michael@0: int32_t aArg, int32_t aTrackNum) michael@0: { michael@0: // For now, we do nothing with 'aStatus' and 'aTrackNum'. michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: ErrorResult ignored; michael@0: nsString state; michael@0: michael@0: switch (aState) { michael@0: case CameraControlListener::kRecorderStarted: michael@0: if (mStartRecordingOnSuccessCb) { michael@0: nsRefPtr cb = mStartRecordingOnSuccessCb.forget(); michael@0: mStartRecordingOnErrorCb = nullptr; michael@0: cb->Call(ignored); michael@0: } michael@0: state = NS_LITERAL_STRING("Started"); michael@0: break; michael@0: michael@0: case CameraControlListener::kRecorderStopped: michael@0: NotifyRecordingStatusChange(NS_LITERAL_STRING("shutdown")); michael@0: state = NS_LITERAL_STRING("Stopped"); michael@0: break; michael@0: michael@0: #ifdef MOZ_B2G_CAMERA michael@0: case CameraControlListener::kFileSizeLimitReached: michael@0: state = NS_LITERAL_STRING("FileSizeLimitReached"); michael@0: break; michael@0: michael@0: case CameraControlListener::kVideoLengthLimitReached: michael@0: state = NS_LITERAL_STRING("VideoLengthLimitReached"); michael@0: break; michael@0: michael@0: case CameraControlListener::kTrackCompleted: michael@0: state = NS_LITERAL_STRING("TrackCompleted"); michael@0: break; michael@0: michael@0: case CameraControlListener::kTrackFailed: michael@0: state = NS_LITERAL_STRING("TrackFailed"); michael@0: break; michael@0: michael@0: case CameraControlListener::kMediaRecorderFailed: michael@0: state = NS_LITERAL_STRING("MediaRecorderFailed"); michael@0: break; michael@0: michael@0: case CameraControlListener::kMediaServerFailed: michael@0: state = NS_LITERAL_STRING("MediaServerFailed"); michael@0: break; michael@0: #endif michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Unanticipated video recorder error"); michael@0: return; michael@0: } michael@0: michael@0: nsRefPtr cb = mOnRecorderStateChangeCb; michael@0: if (cb) { michael@0: cb->Call(state, ignored); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::OnConfigurationChange(DOMCameraConfiguration* aConfiguration) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: // Update our record of the current camera configuration michael@0: mCurrentConfiguration = aConfiguration; michael@0: michael@0: DOM_CAMERA_LOGI("DOM OnConfigurationChange: this=%p\n", this); michael@0: DOM_CAMERA_LOGI(" mode : %s\n", michael@0: mCurrentConfiguration->mMode == CameraMode::Video ? "video" : "picture"); michael@0: DOM_CAMERA_LOGI(" maximum focus areas : %d\n", michael@0: mCurrentConfiguration->mMaxFocusAreas); michael@0: DOM_CAMERA_LOGI(" maximum metering areas : %d\n", michael@0: mCurrentConfiguration->mMaxMeteringAreas); michael@0: DOM_CAMERA_LOGI(" preview size (w x h) : %d x %d\n", michael@0: mCurrentConfiguration->mPreviewSize.mWidth, mCurrentConfiguration->mPreviewSize.mHeight); michael@0: DOM_CAMERA_LOGI(" recorder profile : %s\n", michael@0: NS_ConvertUTF16toUTF8(mCurrentConfiguration->mRecorderProfile).get()); michael@0: michael@0: nsRefPtr cb = mSetConfigurationOnSuccessCb.forget(); michael@0: mSetConfigurationOnErrorCb = nullptr; michael@0: if (cb) { michael@0: ErrorResult ignored; michael@0: cb->Call(*mCurrentConfiguration, ignored); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::OnAutoFocusComplete(bool aAutoFocusSucceeded) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr cb = mAutoFocusOnSuccessCb.forget(); michael@0: mAutoFocusOnErrorCb = nullptr; michael@0: if (cb) { michael@0: ErrorResult ignored; michael@0: cb->Call(aAutoFocusSucceeded, ignored); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::OnAutoFocusMoving(bool aIsMoving) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr cb = mOnAutoFocusMovingCb; michael@0: if (cb) { michael@0: ErrorResult ignored; michael@0: cb->Call(aIsMoving, ignored); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::OnFacesDetected(const nsTArray& aFaces) michael@0: { michael@0: DOM_CAMERA_LOGI("DOM OnFacesDetected %u face(s)\n", aFaces.Length()); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr cb = mOnFacesDetectedCb; michael@0: if (!cb) { michael@0: return; michael@0: } michael@0: michael@0: Sequence > faces; michael@0: uint32_t len = aFaces.Length(); michael@0: michael@0: if (faces.SetCapacity(len)) { michael@0: nsRefPtr f; michael@0: for (uint32_t i = 0; i < len; ++i) { michael@0: f = new DOMCameraDetectedFace(this, aFaces[i]); michael@0: *faces.AppendElement() = f.forget().take(); michael@0: } michael@0: } michael@0: michael@0: ErrorResult ignored; michael@0: cb->Call(faces, ignored); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::OnTakePictureComplete(nsIDOMBlob* aPicture) michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr cb = mTakePictureOnSuccessCb.forget(); michael@0: mTakePictureOnErrorCb = nullptr; michael@0: if (!cb) { michael@0: // Warn because it shouldn't be possible to get here without michael@0: // having passed a success callback into takePicture(), even michael@0: // though we guard against a nullptr dereference. michael@0: NS_WARNING("DOM Null success callback in OnTakePictureComplete()"); michael@0: return; michael@0: } michael@0: michael@0: ErrorResult ignored; michael@0: cb->Call(aPicture, ignored); michael@0: } michael@0: michael@0: void michael@0: nsDOMCameraControl::OnError(CameraControlListener::CameraErrorContext aContext, const nsAString& aError) michael@0: { michael@0: DOM_CAMERA_LOGI("DOM OnError context=%d, error='%s'\n", aContext, michael@0: NS_LossyConvertUTF16toASCII(aError).get()); michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr errorCb; michael@0: michael@0: switch (aContext) { michael@0: case CameraControlListener::kInStartCamera: michael@0: mGetCameraOnSuccessCb = nullptr; michael@0: errorCb = mGetCameraOnErrorCb.forget(); michael@0: break; michael@0: michael@0: case CameraControlListener::kInStopCamera: michael@0: mReleaseOnSuccessCb = nullptr; michael@0: errorCb = mReleaseOnErrorCb.forget(); michael@0: break; michael@0: michael@0: case CameraControlListener::kInSetConfiguration: michael@0: mSetConfigurationOnSuccessCb = nullptr; michael@0: errorCb = mSetConfigurationOnErrorCb.forget(); michael@0: break; michael@0: michael@0: case CameraControlListener::kInAutoFocus: michael@0: mAutoFocusOnSuccessCb = nullptr; michael@0: errorCb = mAutoFocusOnErrorCb.forget(); michael@0: break; michael@0: michael@0: case CameraControlListener::kInTakePicture: michael@0: mTakePictureOnSuccessCb = nullptr; michael@0: errorCb = mTakePictureOnErrorCb.forget(); michael@0: break; michael@0: michael@0: case CameraControlListener::kInStartRecording: michael@0: mStartRecordingOnSuccessCb = nullptr; michael@0: errorCb = mStartRecordingOnErrorCb.forget(); michael@0: break; michael@0: michael@0: case CameraControlListener::kInStopRecording: michael@0: // This method doesn't have any callbacks, so all we can do is log the michael@0: // failure. This only happens after the hardware has been released. michael@0: NS_WARNING("Failed to stop recording"); michael@0: return; michael@0: michael@0: case CameraControlListener::kInStartPreview: michael@0: // This method doesn't have any callbacks, so all we can do is log the michael@0: // failure. This only happens after the hardware has been released. michael@0: NS_WARNING("Failed to (re)start preview"); michael@0: return; michael@0: michael@0: case CameraControlListener::kInUnspecified: michael@0: if (aError.EqualsASCII("ErrorServiceFailed")) { michael@0: // If the camera service fails, we will get preview-stopped and michael@0: // hardware-closed events, so nothing to do here. michael@0: NS_WARNING("Camera service failed"); michael@0: return; michael@0: } michael@0: if (aError.EqualsASCII("ErrorSetPictureSizeFailed") || michael@0: aError.EqualsASCII("ErrorSetThumbnailSizeFailed")) { michael@0: // We currently don't handle attribute setter failure. Practically, michael@0: // this only ever happens if a setter is called after the hardware michael@0: // has gone away before an asynchronous set gets to happen, so we michael@0: // swallow these. michael@0: NS_WARNING("Failed to set either picture or thumbnail size"); michael@0: return; michael@0: } michael@0: // fallthrough michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Error occurred in unanticipated camera state"); michael@0: return; michael@0: } michael@0: michael@0: if (!errorCb) { michael@0: DOM_CAMERA_LOGW("DOM No error handler for error '%s' in context=%d\n", michael@0: NS_LossyConvertUTF16toASCII(aError).get(), aContext); michael@0: return; michael@0: } michael@0: michael@0: ErrorResult ignored; michael@0: errorCb->Call(aError, ignored); michael@0: } michael@0: