/* 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 "CameraCommon.h" #include "DOMCameraControl.h" #include "CameraPreviewMediaStream.h" #include "mozilla/dom/CameraManagerBinding.h" #include "mozilla/dom/File.h" #include "nsQueryObject.h" using namespace mozilla; using namespace mozilla::dom; DOMCameraControlListener::DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl, CameraPreviewMediaStream* aStream) : mDOMCameraControl( new nsMainThreadPtrHolder(static_cast(aDOMCameraControl))) , mStream(aStream) { DOM_CAMERA_LOGT("%s:%d : this=%p, camera=%p, stream=%p\n", __func__, __LINE__, this, aDOMCameraControl, aStream); } DOMCameraControlListener::~DOMCameraControlListener() { DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); } // Boilerplate callback runnable class DOMCameraControlListener::DOMCallback : public nsRunnable { public: explicit DOMCallback(nsMainThreadPtrHandle aDOMCameraControl) : mDOMCameraControl(aDOMCameraControl) { MOZ_COUNT_CTOR(DOMCameraControlListener::DOMCallback); } protected: virtual ~DOMCallback() { MOZ_COUNT_DTOR(DOMCameraControlListener::DOMCallback); } public: virtual void RunCallback(nsDOMCameraControl* aDOMCameraControl) = 0; NS_IMETHOD Run() override { MOZ_ASSERT(NS_IsMainThread()); RefPtr camera = do_QueryObject(mDOMCameraControl.get()); if (!camera) { DOM_CAMERA_LOGE("do_QueryObject failed to get an nsDOMCameraControl\n"); return NS_ERROR_INVALID_ARG; } RunCallback(camera); return NS_OK; } protected: nsMainThreadPtrHandle mDOMCameraControl; }; // Specific callback handlers void DOMCameraControlListener::OnHardwareStateChange(HardwareState aState, nsresult aReason) { class Callback : public DOMCallback { public: Callback(nsMainThreadPtrHandle aDOMCameraControl, HardwareState aState, nsresult aReason) : DOMCallback(aDOMCameraControl) , mState(aState) , mReason(aReason) { } void RunCallback(nsDOMCameraControl* aDOMCameraControl) override { aDOMCameraControl->OnHardwareStateChange(mState, mReason); } protected: HardwareState mState; nsresult mReason; }; NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState, aReason)); } void DOMCameraControlListener::OnPreviewStateChange(PreviewState aState) { class Callback : public DOMCallback { public: Callback(nsMainThreadPtrHandle aDOMCameraControl, PreviewState aState) : DOMCallback(aDOMCameraControl) , mState(aState) { } void RunCallback(nsDOMCameraControl* aDOMCameraControl) 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_ASSERT_UNREACHABLE("Invalid preview state"); return; } mStream->OnPreviewStateChange(aState == kPreviewStarted); 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) 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) override { RefPtr config = new nsDOMCameraControl::DOMCameraConfiguration(); switch (mConfiguration.mMode) { case ICameraControl::kVideoMode: config->mMode = CameraMode::Video; break; case ICameraControl::kPictureMode: config->mMode = CameraMode::Picture; break; default: DOM_CAMERA_LOGI("Camera mode still unspecified, nothing to do\n"); return; } // 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->mPictureSize.mWidth = mConfiguration.mPictureSize.width; config->mPictureSize.mHeight = mConfiguration.mPictureSize.height; config->mMaxMeteringAreas = mConfiguration.mMaxMeteringAreas; config->mMaxFocusAreas = mConfiguration.mMaxFocusAreas; aDOMCameraControl->OnConfigurationChange(config); } protected: const CameraListenerConfiguration mConfiguration; }; NS_DispatchToMainThread(new Callback(mDOMCameraControl, aConfiguration)); } void DOMCameraControlListener::OnAutoFocusMoving(bool aIsMoving) { class Callback : public DOMCallback { public: Callback(nsMainThreadPtrHandle aDOMCameraControl, bool aIsMoving) : DOMCallback(aDOMCameraControl) , mIsMoving(aIsMoving) { } void RunCallback(nsDOMCameraControl* aDOMCameraControl) override { aDOMCameraControl->OnAutoFocusMoving(mIsMoving); } protected: bool mIsMoving; }; NS_DispatchToMainThread(new Callback(mDOMCameraControl, aIsMoving)); } void DOMCameraControlListener::OnFacesDetected(const nsTArray& aFaces) { class Callback : public DOMCallback { public: Callback(nsMainThreadPtrHandle aDOMCameraControl, const nsTArray& aFaces) : DOMCallback(aDOMCameraControl) , mFaces(aFaces) { } void RunCallback(nsDOMCameraControl* aDOMCameraControl) override { aDOMCameraControl->OnFacesDetected(mFaces); } protected: const nsTArray mFaces; }; NS_DispatchToMainThread(new Callback(mDOMCameraControl, aFaces)); } void DOMCameraControlListener::OnShutter() { class Callback : public DOMCallback { public: explicit Callback(nsMainThreadPtrHandle aDOMCameraControl) : DOMCallback(aDOMCameraControl) { } void RunCallback(nsDOMCameraControl* aDOMCameraControl) override { aDOMCameraControl->OnShutter(); } }; NS_DispatchToMainThread(new Callback(mDOMCameraControl)); } void DOMCameraControlListener::OnRateLimitPreview(bool aLimit) { mStream->RateLimit(aLimit); } 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(gfx::IntSize(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) override { aDOMCameraControl->OnAutoFocusComplete(mAutoFocusSucceeded); } protected: bool mAutoFocusSucceeded; }; NS_DispatchToMainThread(new Callback(mDOMCameraControl, aAutoFocusSucceeded)); } void DOMCameraControlListener::OnTakePictureComplete(const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) { class Callback : public DOMCallback { public: Callback(nsMainThreadPtrHandle aDOMCameraControl, const uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) : DOMCallback(aDOMCameraControl) , mLength(aLength) , mMimeType(aMimeType) { mData = (uint8_t*) malloc(aLength); memcpy(mData, aData, aLength); } void RunCallback(nsDOMCameraControl* aDOMCameraControl) override { nsCOMPtr picture = Blob::CreateMemoryBlob(mDOMCameraControl.get(), static_cast(mData), static_cast(mLength), mMimeType); aDOMCameraControl->OnTakePictureComplete(picture); mData = NULL; } protected: virtual ~Callback() { free(mData); } uint8_t* mData; uint32_t mLength; nsString mMimeType; }; NS_DispatchToMainThread(new Callback(mDOMCameraControl, aData, aLength, aMimeType)); } void DOMCameraControlListener::OnUserError(UserContext aContext, nsresult aError) { class Callback : public DOMCallback { public: Callback(nsMainThreadPtrHandle aDOMCameraControl, UserContext aContext, nsresult aError) : DOMCallback(aDOMCameraControl) , mContext(aContext) , mError(aError) { } virtual void RunCallback(nsDOMCameraControl* aDOMCameraControl) override { aDOMCameraControl->OnUserError(mContext, mError); } protected: UserContext mContext; nsresult mError; }; NS_DispatchToMainThread(new Callback(mDOMCameraControl, aContext, aError)); } void DOMCameraControlListener::OnPoster(BlobImpl* aBlobImpl) { class Callback : public DOMCallback { public: Callback(nsMainThreadPtrHandle aDOMCameraControl, BlobImpl* aBlobImpl) : DOMCallback(aDOMCameraControl) , mBlobImpl(aBlobImpl) { } void RunCallback(nsDOMCameraControl* aDOMCameraControl) override { aDOMCameraControl->OnPoster(mBlobImpl); } protected: RefPtr mBlobImpl; }; NS_DispatchToMainThread(new Callback(mDOMCameraControl, aBlobImpl)); }