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 "DOMCameraControlListener.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsDOMFile.h" michael@0: #include "CameraCommon.h" michael@0: #include "DOMCameraControl.h" michael@0: #include "CameraPreviewMediaStream.h" michael@0: #include "mozilla/dom/CameraManagerBinding.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: DOMCameraControlListener::DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl, michael@0: CameraPreviewMediaStream* aStream) michael@0: : mDOMCameraControl(new nsMainThreadPtrHolder(aDOMCameraControl)) michael@0: , mStream(aStream) michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p, camera=%p, stream=%p\n", michael@0: __func__, __LINE__, this, aDOMCameraControl, aStream); michael@0: } michael@0: michael@0: DOMCameraControlListener::~DOMCameraControlListener() michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); michael@0: } michael@0: michael@0: // Boilerplate callback runnable michael@0: class DOMCameraControlListener::DOMCallback : public nsRunnable michael@0: { michael@0: public: michael@0: DOMCallback(nsMainThreadPtrHandle aDOMCameraControl) michael@0: : mDOMCameraControl(aDOMCameraControl) michael@0: { michael@0: MOZ_COUNT_CTOR(DOMCameraControlListener::DOMCallback); michael@0: } michael@0: virtual ~DOMCallback() michael@0: { michael@0: MOZ_COUNT_DTOR(DOMCameraControlListener::DOMCallback); michael@0: } michael@0: michael@0: virtual void RunCallback(nsDOMCameraControl* aDOMCameraControl) = 0; michael@0: michael@0: NS_IMETHOD michael@0: Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(NS_IsMainThread()); michael@0: michael@0: nsRefPtr camera = mDOMCameraControl.get(); michael@0: if (camera) { michael@0: RunCallback(camera); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: nsMainThreadPtrHandle mDOMCameraControl; michael@0: }; michael@0: michael@0: // Specific callback handlers michael@0: void michael@0: DOMCameraControlListener::OnHardwareStateChange(HardwareState aState) michael@0: { michael@0: class Callback : public DOMCallback michael@0: { michael@0: public: michael@0: Callback(nsMainThreadPtrHandle aDOMCameraControl, michael@0: HardwareState aState) michael@0: : DOMCallback(aDOMCameraControl) michael@0: , mState(aState) michael@0: { } michael@0: michael@0: void michael@0: RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE michael@0: { michael@0: aDOMCameraControl->OnHardwareStateChange(mState); michael@0: } michael@0: michael@0: protected: michael@0: HardwareState mState; michael@0: }; michael@0: michael@0: NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState)); michael@0: } michael@0: michael@0: void michael@0: DOMCameraControlListener::OnPreviewStateChange(PreviewState aState) michael@0: { michael@0: class Callback : public DOMCallback michael@0: { michael@0: public: michael@0: Callback(nsMainThreadPtrHandle aDOMCameraControl, michael@0: PreviewState aState) michael@0: : DOMCallback(aDOMCameraControl) michael@0: , mState(aState) michael@0: { } michael@0: michael@0: void michael@0: RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE michael@0: { michael@0: aDOMCameraControl->OnPreviewStateChange(mState); michael@0: } michael@0: michael@0: protected: michael@0: PreviewState mState; michael@0: }; michael@0: michael@0: switch (aState) { michael@0: case kPreviewStopped: michael@0: // Clear the current frame right away, without dispatching a michael@0: // runnable. This is an ugly coupling between the camera's michael@0: // SurfaceTextureClient and the MediaStream/ImageContainer, michael@0: // but without it, the preview can fail to start. michael@0: DOM_CAMERA_LOGI("Preview stopped, clearing current frame\n"); michael@0: mStream->ClearCurrentFrame(); michael@0: break; michael@0: michael@0: case kPreviewPaused: michael@0: // In the paused state, we still want to reflect the change michael@0: // in preview state, but we don't want to clear the current michael@0: // frame as above, since doing so seems to cause genlock michael@0: // problems when we restart the preview. See bug 957749. michael@0: DOM_CAMERA_LOGI("Preview paused\n"); michael@0: break; michael@0: michael@0: case kPreviewStarted: michael@0: DOM_CAMERA_LOGI("Preview started\n"); michael@0: break; michael@0: michael@0: default: michael@0: DOM_CAMERA_LOGE("Unknown preview state %d\n", aState); michael@0: MOZ_ASSUME_UNREACHABLE("Invalid preview state"); michael@0: return; michael@0: } michael@0: NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState)); michael@0: } michael@0: michael@0: void michael@0: DOMCameraControlListener::OnRecorderStateChange(RecorderState aState, michael@0: int32_t aStatus, int32_t aTrackNum) michael@0: { michael@0: class Callback : public DOMCallback michael@0: { michael@0: public: michael@0: Callback(nsMainThreadPtrHandle aDOMCameraControl, michael@0: RecorderState aState, michael@0: int32_t aStatus, michael@0: int32_t aTrackNum) michael@0: : DOMCallback(aDOMCameraControl) michael@0: , mState(aState) michael@0: , mStatus(aStatus) michael@0: , mTrackNum(aTrackNum) michael@0: { } michael@0: michael@0: void michael@0: RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE michael@0: { michael@0: aDOMCameraControl->OnRecorderStateChange(mState, mStatus, mTrackNum); michael@0: } michael@0: michael@0: protected: michael@0: RecorderState mState; michael@0: int32_t mStatus; michael@0: int32_t mTrackNum; michael@0: }; michael@0: michael@0: NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState, aStatus, aTrackNum)); michael@0: } michael@0: michael@0: void michael@0: DOMCameraControlListener::OnConfigurationChange(const CameraListenerConfiguration& aConfiguration) michael@0: { michael@0: class Callback : public DOMCallback michael@0: { michael@0: public: michael@0: Callback(nsMainThreadPtrHandle aDOMCameraControl, michael@0: const CameraListenerConfiguration& aConfiguration) michael@0: : DOMCallback(aDOMCameraControl) michael@0: , mConfiguration(aConfiguration) michael@0: { } michael@0: michael@0: void michael@0: RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE michael@0: { michael@0: nsRefPtr config = michael@0: new nsDOMCameraControl::DOMCameraConfiguration(); michael@0: michael@0: switch (mConfiguration.mMode) { michael@0: case ICameraControl::kVideoMode: michael@0: config->mMode = CameraMode::Video; michael@0: break; michael@0: michael@0: case ICameraControl::kPictureMode: michael@0: config->mMode = CameraMode::Picture; michael@0: break; michael@0: michael@0: default: michael@0: DOM_CAMERA_LOGI("Camera mode still unspecified, nothing to do\n"); michael@0: return; michael@0: } michael@0: michael@0: // Map CameraControl parameters to their DOM-facing equivalents michael@0: config->mRecorderProfile = mConfiguration.mRecorderProfile; michael@0: config->mPreviewSize.mWidth = mConfiguration.mPreviewSize.width; michael@0: config->mPreviewSize.mHeight = mConfiguration.mPreviewSize.height; michael@0: config->mMaxMeteringAreas = mConfiguration.mMaxMeteringAreas; michael@0: config->mMaxFocusAreas = mConfiguration.mMaxFocusAreas; michael@0: michael@0: aDOMCameraControl->OnConfigurationChange(config); michael@0: } michael@0: michael@0: protected: michael@0: const CameraListenerConfiguration mConfiguration; michael@0: }; michael@0: michael@0: NS_DispatchToMainThread(new Callback(mDOMCameraControl, aConfiguration)); michael@0: } michael@0: michael@0: void michael@0: DOMCameraControlListener::OnAutoFocusMoving(bool aIsMoving) michael@0: { michael@0: class Callback : public DOMCallback michael@0: { michael@0: public: michael@0: Callback(nsMainThreadPtrHandle aDOMCameraControl, bool aIsMoving) michael@0: : DOMCallback(aDOMCameraControl) michael@0: , mIsMoving(aIsMoving) michael@0: { } michael@0: michael@0: void michael@0: RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE michael@0: { michael@0: aDOMCameraControl->OnAutoFocusMoving(mIsMoving); michael@0: } michael@0: michael@0: protected: michael@0: bool mIsMoving; michael@0: }; michael@0: michael@0: NS_DispatchToMainThread(new Callback(mDOMCameraControl, aIsMoving)); michael@0: } michael@0: michael@0: void michael@0: DOMCameraControlListener::OnFacesDetected(const nsTArray& aFaces) michael@0: { michael@0: class Callback : public DOMCallback michael@0: { michael@0: public: michael@0: Callback(nsMainThreadPtrHandle aDOMCameraControl, michael@0: const nsTArray& aFaces) michael@0: : DOMCallback(aDOMCameraControl) michael@0: , mFaces(aFaces) michael@0: { } michael@0: michael@0: void michael@0: RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE michael@0: { michael@0: aDOMCameraControl->OnFacesDetected(mFaces); michael@0: } michael@0: michael@0: protected: michael@0: const nsTArray mFaces; michael@0: }; michael@0: michael@0: NS_DispatchToMainThread(new Callback(mDOMCameraControl, aFaces)); michael@0: } michael@0: michael@0: void michael@0: DOMCameraControlListener::OnShutter() michael@0: { michael@0: class Callback : public DOMCallback michael@0: { michael@0: public: michael@0: Callback(nsMainThreadPtrHandle aDOMCameraControl) michael@0: : DOMCallback(aDOMCameraControl) michael@0: { } michael@0: michael@0: void michael@0: RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE michael@0: { michael@0: aDOMCameraControl->OnShutter(); michael@0: } michael@0: }; michael@0: michael@0: NS_DispatchToMainThread(new Callback(mDOMCameraControl)); michael@0: } michael@0: michael@0: bool michael@0: DOMCameraControlListener::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) michael@0: { michael@0: DOM_CAMERA_LOGI("OnNewPreviewFrame: got %d x %d frame\n", aWidth, aHeight); michael@0: michael@0: mStream->SetCurrentFrame(gfxIntSize(aWidth, aHeight), aImage); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: DOMCameraControlListener::OnAutoFocusComplete(bool aAutoFocusSucceeded) michael@0: { michael@0: class Callback : public DOMCallback michael@0: { michael@0: public: michael@0: Callback(nsMainThreadPtrHandle aDOMCameraControl, michael@0: bool aAutoFocusSucceeded) michael@0: : DOMCallback(aDOMCameraControl) michael@0: , mAutoFocusSucceeded(aAutoFocusSucceeded) michael@0: { } michael@0: michael@0: void michael@0: RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE michael@0: { michael@0: aDOMCameraControl->OnAutoFocusComplete(mAutoFocusSucceeded); michael@0: } michael@0: michael@0: protected: michael@0: bool mAutoFocusSucceeded; michael@0: }; michael@0: michael@0: NS_DispatchToMainThread(new Callback(mDOMCameraControl, aAutoFocusSucceeded)); michael@0: } michael@0: michael@0: void michael@0: DOMCameraControlListener::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) michael@0: { michael@0: class Callback : public DOMCallback michael@0: { michael@0: public: michael@0: Callback(nsMainThreadPtrHandle aDOMCameraControl, michael@0: uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) michael@0: : DOMCallback(aDOMCameraControl) michael@0: , mData(aData) michael@0: , mLength(aLength) michael@0: , mMimeType(aMimeType) michael@0: { } michael@0: michael@0: void michael@0: RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE michael@0: { michael@0: nsCOMPtr picture = new nsDOMMemoryFile(static_cast(mData), michael@0: static_cast(mLength), michael@0: mMimeType); michael@0: aDOMCameraControl->OnTakePictureComplete(picture); michael@0: } michael@0: michael@0: protected: michael@0: uint8_t* mData; michael@0: uint32_t mLength; michael@0: nsString mMimeType; michael@0: }; michael@0: michael@0: NS_DispatchToMainThread(new Callback(mDOMCameraControl, aData, aLength, aMimeType)); michael@0: } michael@0: michael@0: void michael@0: DOMCameraControlListener::OnError(CameraErrorContext aContext, CameraError aError) michael@0: { michael@0: class Callback : public DOMCallback michael@0: { michael@0: public: michael@0: Callback(nsMainThreadPtrHandle aDOMCameraControl, michael@0: CameraErrorContext aContext, michael@0: CameraError aError) michael@0: : DOMCallback(aDOMCameraControl) michael@0: , mContext(aContext) michael@0: , mError(aError) michael@0: { } michael@0: michael@0: virtual void michael@0: RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE michael@0: { michael@0: nsString error; michael@0: michael@0: switch (mError) { michael@0: case kErrorServiceFailed: michael@0: error = NS_LITERAL_STRING("ErrorServiceFailed"); michael@0: break; michael@0: michael@0: case kErrorSetPictureSizeFailed: michael@0: error = NS_LITERAL_STRING("ErrorSetPictureSizeFailed"); michael@0: break; michael@0: michael@0: case kErrorSetThumbnailSizeFailed: michael@0: error = NS_LITERAL_STRING("ErrorSetThumbnailSizeFailed"); michael@0: break; michael@0: michael@0: case kErrorApiFailed: michael@0: // XXXmikeh legacy error placeholder michael@0: error = NS_LITERAL_STRING("FAILURE"); michael@0: break; michael@0: michael@0: default: michael@0: error = NS_LITERAL_STRING("ErrorUnknown"); michael@0: break; michael@0: } michael@0: aDOMCameraControl->OnError(mContext, error); michael@0: } michael@0: michael@0: protected: michael@0: CameraErrorContext mContext; michael@0: CameraError mError; michael@0: }; michael@0: michael@0: NS_DispatchToMainThread(new Callback(mDOMCameraControl, aContext, aError)); michael@0: }