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 "CameraControlImpl.h" michael@0: #include "base/basictypes.h" michael@0: #include "mozilla/Assertions.h" michael@0: #include "mozilla/unused.h" michael@0: #include "nsIWeakReferenceUtils.h" michael@0: #include "CameraRecorderProfiles.h" michael@0: #include "CameraCommon.h" michael@0: #include "nsGlobalWindow.h" michael@0: #include "DeviceStorageFileDescriptor.h" michael@0: #include "CameraControlListener.h" michael@0: michael@0: using namespace mozilla; michael@0: michael@0: nsWeakPtr CameraControlImpl::sCameraThread; michael@0: michael@0: CameraControlImpl::CameraControlImpl(uint32_t aCameraId) michael@0: : mCameraId(aCameraId) michael@0: , mPreviewState(CameraControlListener::kPreviewStopped) michael@0: , mHardwareState(CameraControlListener::kHardwareClosed) michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); michael@0: michael@0: // reuse the same camera thread to conserve resources michael@0: nsCOMPtr ct = do_QueryReferent(sCameraThread); michael@0: if (ct) { michael@0: mCameraThread = ct.forget(); michael@0: } else { michael@0: nsresult rv = NS_NewNamedThread("CameraThread", getter_AddRefs(mCameraThread)); michael@0: unused << rv; // swallow rv to suppress a compiler warning when the macro michael@0: // is #defined to nothing (i.e. in non-DEBUG builds). michael@0: MOZ_ASSERT(NS_SUCCEEDED(rv)); michael@0: michael@0: // keep a weak reference to the new thread michael@0: sCameraThread = do_GetWeakReference(mCameraThread); michael@0: } michael@0: michael@0: // Care must be taken with the mListenerLock read-write lock to prevent michael@0: // deadlocks. Currently this is handled by ensuring that any attempts to michael@0: // acquire the lock for writing (as in Add/RemoveListener()) happen in a michael@0: // runnable dispatched to the Camera Thread--even if the method is being michael@0: // called from that thread. This ensures that if a registered listener michael@0: // (which is invoked with a read-lock) tries to call Add/RemoveListener(), michael@0: // the lock-for-writing attempt won't happen until the listener has michael@0: // completed. michael@0: // michael@0: // Multiple parallel listeners being invoked are not a problem because michael@0: // the read-write lock allows multiple simultaneous read-locks. michael@0: mListenerLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "CameraControlImpl.Listeners.Lock"); michael@0: } michael@0: michael@0: CameraControlImpl::~CameraControlImpl() michael@0: { michael@0: if (mListenerLock) { michael@0: PR_DestroyRWLock(mListenerLock); michael@0: mListenerLock = nullptr; michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: CameraControlImpl::GetRecorderProfileManager() michael@0: { michael@0: return GetRecorderProfileManagerImpl(); michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::Shutdown() michael@0: { michael@0: DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::OnHardwareStateChange(CameraControlListener::HardwareState aNewState) michael@0: { michael@0: // This callback can run on threads other than the Main Thread and michael@0: // the Camera Thread. On Gonk, it may be called from the camera's michael@0: // local binder thread, should the mediaserver process die. michael@0: RwLockAutoEnterRead lock(mListenerLock); michael@0: michael@0: if (aNewState == mHardwareState) { michael@0: DOM_CAMERA_LOGI("OnHardwareStateChange: state did not change from %d\n", mHardwareState); michael@0: return; michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: const char* state[] = { "open", "closed", "failed" }; michael@0: MOZ_ASSERT(aNewState >= 0); michael@0: if (static_cast(aNewState) < sizeof(state) / sizeof(state[0])) { michael@0: DOM_CAMERA_LOGI("New hardware state is '%s'\n", state[aNewState]); michael@0: } else { michael@0: DOM_CAMERA_LOGE("OnHardwareStateChange: got invalid HardwareState value %d\n", aNewState); michael@0: } michael@0: #endif michael@0: michael@0: mHardwareState = aNewState; michael@0: michael@0: for (uint32_t i = 0; i < mListeners.Length(); ++i) { michael@0: CameraControlListener* l = mListeners[i]; michael@0: l->OnHardwareStateChange(mHardwareState); michael@0: } michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::OnConfigurationChange() michael@0: { michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread); michael@0: RwLockAutoEnterRead lock(mListenerLock); michael@0: michael@0: DOM_CAMERA_LOGI("OnConfigurationChange : %d listeners\n", mListeners.Length()); michael@0: michael@0: for (uint32_t i = 0; i < mListeners.Length(); ++i) { michael@0: CameraControlListener* l = mListeners[i]; michael@0: l->OnConfigurationChange(mCurrentConfiguration); michael@0: } michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::OnAutoFocusComplete(bool aAutoFocusSucceeded) michael@0: { michael@0: // This callback can run on threads other than the Main Thread and michael@0: // the Camera Thread. On Gonk, it is called from the camera michael@0: // library's auto focus thread. michael@0: RwLockAutoEnterRead lock(mListenerLock); michael@0: michael@0: for (uint32_t i = 0; i < mListeners.Length(); ++i) { michael@0: CameraControlListener* l = mListeners[i]; michael@0: l->OnAutoFocusComplete(aAutoFocusSucceeded); michael@0: } michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::OnAutoFocusMoving(bool aIsMoving) michael@0: { michael@0: RwLockAutoEnterRead lock(mListenerLock); michael@0: michael@0: for (uint32_t i = 0; i < mListeners.Length(); ++i) { michael@0: CameraControlListener* l = mListeners[i]; michael@0: l->OnAutoFocusMoving(aIsMoving); michael@0: } michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::OnFacesDetected(const nsTArray& aFaces) michael@0: { michael@0: // This callback can run on threads other than the Main Thread and michael@0: // the Camera Thread. On Gonk, it is called from the camera michael@0: // library's face detection thread. michael@0: RwLockAutoEnterRead lock(mListenerLock); michael@0: michael@0: for (uint32_t i = 0; i < mListeners.Length(); ++i) { michael@0: CameraControlListener* l = mListeners[i]; michael@0: l->OnFacesDetected(aFaces); michael@0: } michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) michael@0: { michael@0: // This callback can run on threads other than the Main Thread and michael@0: // the Camera Thread. On Gonk, it is called from the camera michael@0: // library's snapshot thread. michael@0: RwLockAutoEnterRead lock(mListenerLock); michael@0: michael@0: for (uint32_t i = 0; i < mListeners.Length(); ++i) { michael@0: CameraControlListener* l = mListeners[i]; michael@0: l->OnTakePictureComplete(aData, aLength, aMimeType); michael@0: } michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::OnShutter() michael@0: { michael@0: // This callback can run on threads other than the Main Thread and michael@0: // the Camera Thread. On Gonk, it is called from the camera driver's michael@0: // preview thread. michael@0: RwLockAutoEnterRead lock(mListenerLock); michael@0: michael@0: for (uint32_t i = 0; i < mListeners.Length(); ++i) { michael@0: CameraControlListener* l = mListeners[i]; michael@0: l->OnShutter(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::OnClosed() michael@0: { michael@0: // This callback can run on threads other than the Main Thread and michael@0: // the Camera Thread. michael@0: RwLockAutoEnterRead lock(mListenerLock); michael@0: michael@0: for (uint32_t i = 0; i < mListeners.Length(); ++i) { michael@0: CameraControlListener* l = mListeners[i]; michael@0: l->OnHardwareStateChange(CameraControlListener::kHardwareClosed); michael@0: } michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::OnRecorderStateChange(CameraControlListener::RecorderState aState, michael@0: int32_t aStatus, int32_t aTrackNumber) michael@0: { michael@0: // This callback can run on threads other than the Main Thread and michael@0: // the Camera Thread. On Gonk, it is called from the media encoder michael@0: // thread. michael@0: RwLockAutoEnterRead lock(mListenerLock); michael@0: michael@0: for (uint32_t i = 0; i < mListeners.Length(); ++i) { michael@0: CameraControlListener* l = mListeners[i]; michael@0: l->OnRecorderStateChange(aState, aStatus, aTrackNumber); michael@0: } michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::OnPreviewStateChange(CameraControlListener::PreviewState aNewState) michael@0: { michael@0: // This callback runs on the Main Thread and the Camera Thread, and michael@0: // may run on the local binder thread, should the mediaserver michael@0: // process die. michael@0: RwLockAutoEnterRead lock(mListenerLock); michael@0: michael@0: if (aNewState == mPreviewState) { michael@0: DOM_CAMERA_LOGI("OnPreviewStateChange: state did not change from %d\n", mPreviewState); michael@0: return; michael@0: } michael@0: michael@0: #ifdef PR_LOGGING michael@0: const char* state[] = { "stopped", "paused", "started" }; michael@0: MOZ_ASSERT(aNewState >= 0); michael@0: if (static_cast(aNewState) < sizeof(state) / sizeof(state[0])) { michael@0: DOM_CAMERA_LOGI("New preview state is '%s'\n", state[aNewState]); michael@0: } else { michael@0: DOM_CAMERA_LOGE("OnPreviewStateChange: got unknown PreviewState value %d\n", aNewState); michael@0: } michael@0: #endif michael@0: michael@0: mPreviewState = aNewState; michael@0: michael@0: for (uint32_t i = 0; i < mListeners.Length(); ++i) { michael@0: CameraControlListener* l = mListeners[i]; michael@0: l->OnPreviewStateChange(mPreviewState); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: CameraControlImpl::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) michael@0: { michael@0: // This function runs on neither the Main Thread nor the Camera Thread. michael@0: // On Gonk, it is called from the camera driver's preview thread. michael@0: RwLockAutoEnterRead lock(mListenerLock); michael@0: michael@0: DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %d preview frame listener(s)\n", michael@0: mListeners.Length()); michael@0: michael@0: bool consumed = false; michael@0: michael@0: for (uint32_t i = 0; i < mListeners.Length(); ++i) { michael@0: CameraControlListener* l = mListeners[i]; michael@0: consumed = l->OnNewPreviewFrame(aImage, aWidth, aHeight) || consumed; michael@0: } michael@0: return consumed; michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::OnError(CameraControlListener::CameraErrorContext aContext, michael@0: CameraControlListener::CameraError aError) michael@0: { michael@0: // This callback can run on threads other than the Main Thread and michael@0: // the Camera Thread. michael@0: RwLockAutoEnterRead lock(mListenerLock); michael@0: michael@0: #ifdef PR_LOGGING michael@0: const char* error[] = { michael@0: "api-failed", michael@0: "init-failed", michael@0: "invalid-configuration", michael@0: "service-failed", michael@0: "set-picture-size-failed", michael@0: "set-thumbnail-size-failed", michael@0: "unknown" michael@0: }; michael@0: const char* context[] = { michael@0: "StartCamera", michael@0: "StopCamera", michael@0: "AutoFocus", michael@0: "StartFaceDetection", michael@0: "StopFaceDetection", michael@0: "TakePicture", michael@0: "StartRecording", michael@0: "StopRecording", michael@0: "SetConfiguration", michael@0: "StartPreview", michael@0: "StopPreview", michael@0: "ResumeContinuousFocus", michael@0: "Unspecified" michael@0: }; michael@0: if (static_cast(aError) < sizeof(error) / sizeof(error[0]) && michael@0: static_cast(aContext) < sizeof(context) / sizeof(context[0])) { michael@0: DOM_CAMERA_LOGW("CameraControlImpl::OnError : aContext='%s' (%u), aError='%s' (%u)\n", michael@0: context[aContext], aContext, error[aError], aError); michael@0: } else { michael@0: DOM_CAMERA_LOGE("CameraControlImpl::OnError : aContext=%u, aError=%d\n", michael@0: aContext, aError); michael@0: } michael@0: #endif michael@0: michael@0: for (uint32_t i = 0; i < mListeners.Length(); ++i) { michael@0: CameraControlListener* l = mListeners[i]; michael@0: l->OnError(aContext, aError); michael@0: } michael@0: } michael@0: michael@0: // Camera control asynchronous message; these are dispatched from michael@0: // the Main Thread to the Camera Thread, where they are consumed. michael@0: michael@0: class CameraControlImpl::ControlMessage : public nsRunnable michael@0: { michael@0: public: michael@0: ControlMessage(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext) michael@0: : mCameraControl(aCameraControl) michael@0: , mContext(aContext) michael@0: { michael@0: MOZ_COUNT_CTOR(CameraControlImpl::ControlMessage); michael@0: } michael@0: michael@0: virtual ~ControlMessage() michael@0: { michael@0: MOZ_COUNT_DTOR(CameraControlImpl::ControlMessage); michael@0: } michael@0: michael@0: virtual nsresult RunImpl() = 0; michael@0: michael@0: NS_IMETHOD michael@0: Run() MOZ_OVERRIDE michael@0: { michael@0: MOZ_ASSERT(mCameraControl); michael@0: MOZ_ASSERT(NS_GetCurrentThread() == mCameraControl->mCameraThread); michael@0: michael@0: nsresult rv = RunImpl(); michael@0: if (NS_FAILED(rv)) { michael@0: DOM_CAMERA_LOGW("Camera control API failed at %d with 0x%x\n", mContext, rv); michael@0: // XXXmikeh - do we want to report a more specific error code? michael@0: mCameraControl->OnError(mContext, CameraControlListener::kErrorApiFailed); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: protected: michael@0: nsRefPtr mCameraControl; michael@0: CameraControlListener::CameraErrorContext mContext; michael@0: }; michael@0: michael@0: nsresult michael@0: CameraControlImpl::Start(const Configuration* aConfig) michael@0: { michael@0: class Message : public ControlMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext, michael@0: const Configuration* aConfig) michael@0: : ControlMessage(aCameraControl, aContext) michael@0: , mHaveInitialConfig(false) michael@0: { michael@0: if (aConfig) { michael@0: mConfig = *aConfig; michael@0: mHaveInitialConfig = true; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: if (mHaveInitialConfig) { michael@0: return mCameraControl->StartImpl(&mConfig); michael@0: } michael@0: return mCameraControl->StartImpl(); michael@0: } michael@0: michael@0: protected: michael@0: bool mHaveInitialConfig; michael@0: Configuration mConfig; michael@0: }; michael@0: michael@0: return mCameraThread->Dispatch( michael@0: new Message(this, CameraControlListener::kInStartCamera, aConfig), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: CameraControlImpl::SetConfiguration(const Configuration& aConfig) michael@0: { michael@0: class Message : public ControlMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext, michael@0: const Configuration& aConfig) michael@0: : ControlMessage(aCameraControl, aContext) michael@0: , mConfig(aConfig) michael@0: { } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: return mCameraControl->SetConfigurationImpl(mConfig); michael@0: } michael@0: michael@0: protected: michael@0: Configuration mConfig; michael@0: }; michael@0: michael@0: return mCameraThread->Dispatch( michael@0: new Message(this, CameraControlListener::kInSetConfiguration, aConfig), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: CameraControlImpl::AutoFocus() michael@0: { michael@0: class Message : public ControlMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext) michael@0: : ControlMessage(aCameraControl, aContext) michael@0: { } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: return mCameraControl->AutoFocusImpl(); michael@0: } michael@0: }; michael@0: michael@0: return mCameraThread->Dispatch( michael@0: new Message(this, CameraControlListener::kInAutoFocus), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: CameraControlImpl::StartFaceDetection() michael@0: { michael@0: class Message : public ControlMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext) michael@0: : ControlMessage(aCameraControl, aContext) michael@0: { } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: return mCameraControl->StartFaceDetectionImpl(); michael@0: } michael@0: }; michael@0: michael@0: return mCameraThread->Dispatch( michael@0: new Message(this, CameraControlListener::kInStartFaceDetection), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: CameraControlImpl::StopFaceDetection() michael@0: { michael@0: class Message : public ControlMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext) michael@0: : ControlMessage(aCameraControl, aContext) michael@0: { } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: return mCameraControl->StopFaceDetectionImpl(); michael@0: } michael@0: }; michael@0: michael@0: return mCameraThread->Dispatch( michael@0: new Message(this, CameraControlListener::kInStopFaceDetection), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: CameraControlImpl::TakePicture() michael@0: { michael@0: class Message : public ControlMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext) michael@0: : ControlMessage(aCameraControl, aContext) michael@0: { } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: return mCameraControl->TakePictureImpl(); michael@0: } michael@0: }; michael@0: michael@0: return mCameraThread->Dispatch( michael@0: new Message(this, CameraControlListener::kInTakePicture), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: CameraControlImpl::StartRecording(DeviceStorageFileDescriptor* aFileDescriptor, michael@0: const StartRecordingOptions* aOptions) michael@0: { michael@0: class Message : public ControlMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext, michael@0: const StartRecordingOptions* aOptions, michael@0: DeviceStorageFileDescriptor* aFileDescriptor) michael@0: : ControlMessage(aCameraControl, aContext) michael@0: , mOptionsPassed(false) michael@0: , mFileDescriptor(aFileDescriptor) michael@0: { michael@0: if (aOptions) { michael@0: mOptions = *aOptions; michael@0: mOptionsPassed = true; michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: return mCameraControl->StartRecordingImpl(mFileDescriptor, michael@0: mOptionsPassed ? &mOptions : nullptr); michael@0: } michael@0: michael@0: protected: michael@0: StartRecordingOptions mOptions; michael@0: bool mOptionsPassed; michael@0: nsRefPtr mFileDescriptor; michael@0: }; michael@0: michael@0: michael@0: return mCameraThread->Dispatch(new Message(this, CameraControlListener::kInStartRecording, michael@0: aOptions, aFileDescriptor), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: CameraControlImpl::StopRecording() michael@0: { michael@0: class Message : public ControlMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext) michael@0: : ControlMessage(aCameraControl, aContext) michael@0: { } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: return mCameraControl->StopRecordingImpl(); michael@0: } michael@0: }; michael@0: michael@0: return mCameraThread->Dispatch( michael@0: new Message(this, CameraControlListener::kInStopRecording), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: CameraControlImpl::StartPreview() michael@0: { michael@0: class Message : public ControlMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext) michael@0: : ControlMessage(aCameraControl, aContext) michael@0: { } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: return mCameraControl->StartPreviewImpl(); michael@0: } michael@0: }; michael@0: michael@0: return mCameraThread->Dispatch( michael@0: new Message(this, CameraControlListener::kInStartPreview), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: CameraControlImpl::StopPreview() michael@0: { michael@0: class Message : public ControlMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext) michael@0: : ControlMessage(aCameraControl, aContext) michael@0: { } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: return mCameraControl->StopPreviewImpl(); michael@0: } michael@0: }; michael@0: michael@0: return mCameraThread->Dispatch( michael@0: new Message(this, CameraControlListener::kInStopPreview), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: CameraControlImpl::ResumeContinuousFocus() michael@0: { michael@0: class Message : public ControlMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext) michael@0: : ControlMessage(aCameraControl, aContext) michael@0: { } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: return mCameraControl->ResumeContinuousFocusImpl(); michael@0: } michael@0: }; michael@0: michael@0: return mCameraThread->Dispatch( michael@0: new Message(this, CameraControlListener::kInResumeContinuousFocus), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: nsresult michael@0: CameraControlImpl::Stop() michael@0: { michael@0: class Message : public ControlMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener::CameraErrorContext aContext) michael@0: : ControlMessage(aCameraControl, aContext) michael@0: { } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: return mCameraControl->StopImpl(); michael@0: } michael@0: }; michael@0: michael@0: return mCameraThread->Dispatch( michael@0: new Message(this, CameraControlListener::kInStopCamera), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: class CameraControlImpl::ListenerMessage : public CameraControlImpl::ControlMessage michael@0: { michael@0: public: michael@0: ListenerMessage(CameraControlImpl* aCameraControl, michael@0: CameraControlListener* aListener) michael@0: : ControlMessage(aCameraControl, CameraControlListener::kInUnspecified) michael@0: , mListener(aListener) michael@0: { } michael@0: michael@0: protected: michael@0: nsRefPtr mListener; michael@0: }; michael@0: michael@0: void michael@0: CameraControlImpl::AddListenerImpl(already_AddRefed aListener) michael@0: { michael@0: RwLockAutoEnterWrite lock(mListenerLock); michael@0: michael@0: CameraControlListener* l = *mListeners.AppendElement() = aListener; michael@0: DOM_CAMERA_LOGI("Added camera control listener %p\n", l); michael@0: michael@0: // Update the newly-added listener's state michael@0: l->OnConfigurationChange(mCurrentConfiguration); michael@0: l->OnHardwareStateChange(mHardwareState); michael@0: l->OnPreviewStateChange(mPreviewState); michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::AddListener(CameraControlListener* aListener) michael@0: { michael@0: class Message : public ListenerMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, michael@0: CameraControlListener* aListener) michael@0: : ListenerMessage(aCameraControl, aListener) michael@0: { } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: mCameraControl->AddListenerImpl(mListener.forget()); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: mCameraThread->Dispatch(new Message(this, aListener), NS_DISPATCH_NORMAL); michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::RemoveListenerImpl(CameraControlListener* aListener) michael@0: { michael@0: RwLockAutoEnterWrite lock(mListenerLock); michael@0: michael@0: nsRefPtr l(aListener); michael@0: mListeners.RemoveElement(l); michael@0: DOM_CAMERA_LOGI("Removed camera control listener %p\n", l.get()); michael@0: // XXXmikeh - do we want to notify the listener that it has been removed? michael@0: } michael@0: michael@0: void michael@0: CameraControlImpl::RemoveListener(CameraControlListener* aListener) michael@0: { michael@0: class Message : public ListenerMessage michael@0: { michael@0: public: michael@0: Message(CameraControlImpl* aCameraControl, CameraControlListener* aListener) michael@0: : ListenerMessage(aCameraControl, aListener) michael@0: { } michael@0: michael@0: nsresult michael@0: RunImpl() MOZ_OVERRIDE michael@0: { michael@0: mCameraControl->RemoveListenerImpl(mListener); michael@0: return NS_OK; michael@0: } michael@0: }; michael@0: michael@0: mCameraThread->Dispatch(new Message(this, aListener), NS_DISPATCH_NORMAL); michael@0: }