From 518afe6c97cde4939789494b190c68a30f8b5446 Mon Sep 17 00:00:00 2001 From: Mike Habicher Date: Sun, 23 Dec 2012 10:54:54 -0500 Subject: [PATCH] Bug 818933 - Provide a hardware release API so that we don't have to rely on CC of CameraControl objects. r=mhabicher --- dom/camera/CameraControlImpl.cpp | 7 +++ dom/camera/CameraControlImpl.h | 72 +++++++++++++++++++++++++++++- dom/camera/DOMCameraControl.cpp | 6 +++ dom/camera/DOMCameraPreview.cpp | 8 ++-- dom/camera/DOMCameraPreview.h | 22 ++++++++- dom/camera/GonkCameraControl.cpp | 28 +++++++++++- dom/camera/GonkCameraControl.h | 1 + dom/camera/ICameraControl.h | 1 + dom/camera/nsIDOMCameraManager.idl | 19 +++++++- 9 files changed, 155 insertions(+), 9 deletions(-) diff --git a/dom/camera/CameraControlImpl.cpp b/dom/camera/CameraControlImpl.cpp index 8649397e862..07b433e6c9a 100644 --- a/dom/camera/CameraControlImpl.cpp +++ b/dom/camera/CameraControlImpl.cpp @@ -367,6 +367,13 @@ CameraControlImpl::GetPreviewStreamVideoMode(CameraRecorderOptions* aOptions, ns return mCameraThread->Dispatch(getPreviewStreamVideoModeTask, NS_DISPATCH_NORMAL); } +nsresult +CameraControlImpl::ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError) +{ + nsCOMPtr releaseHardwareTask = new ReleaseHardwareTask(this, onSuccess, onError); + return mCameraThread->Dispatch(releaseHardwareTask, NS_DISPATCH_NORMAL); +} + bool CameraControlImpl::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder) { diff --git a/dom/camera/CameraControlImpl.h b/dom/camera/CameraControlImpl.h index 1ccf163b7c4..e9c3a88a4e2 100644 --- a/dom/camera/CameraControlImpl.h +++ b/dom/camera/CameraControlImpl.h @@ -27,6 +27,7 @@ class StopRecordingTask; class SetParameterTask; class GetParameterTask; class GetPreviewStreamVideoModeTask; +class ReleaseHardwareTask; class DOMCameraPreview; class RecorderProfileManager; @@ -43,6 +44,7 @@ class CameraControlImpl : public ICameraControl friend class SetParameterTask; friend class GetParameterTask; friend class GetPreviewStreamVideoModeTask; + friend class ReleaseHardwareTask; public: CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId); @@ -55,6 +57,7 @@ public: nsresult StartRecording(dom::CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError); nsresult StopRecording(); nsresult GetPreviewStreamVideoMode(dom::CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError); + nsresult ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError); nsresult Set(uint32_t aKey, const nsAString& aValue); nsresult Get(uint32_t aKey, nsAString& aValue); @@ -117,6 +120,7 @@ protected: virtual nsresult PushParametersImpl() = 0; virtual nsresult PullParametersImpl() = 0; virtual nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode) = 0; + virtual nsresult ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware) = 0; virtual already_AddRefed GetRecorderProfileManagerImpl() = 0; void OnShutterInternal(); @@ -610,7 +614,73 @@ public: nsCOMPtr mOnErrorCb; }; -// Error result runnable +// Return the result of releasing the camera hardware. Runs on the main thread. +class ReleaseHardwareResult : public nsRunnable +{ +public: + ReleaseHardwareResult(nsICameraReleaseCallback* onSuccess, uint64_t aWindowId) + : mOnSuccessCb(onSuccess) + , mWindowId(aWindowId) + { + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + } + + virtual ~ReleaseHardwareResult() + { + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + } + + NS_IMETHOD Run() + { + MOZ_ASSERT(NS_IsMainThread()); + + if (mOnSuccessCb && nsDOMCameraManager::IsWindowStillActive(mWindowId)) { + mOnSuccessCb->HandleEvent(); + } + return NS_OK; + } + +protected: + nsCOMPtr mOnSuccessCb; + uint64_t mWindowId; +}; + +// Release the camera hardware. +class ReleaseHardwareTask : public nsRunnable +{ +public: + ReleaseHardwareTask(CameraControlImpl* aCameraControl, nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError) + : mCameraControl(aCameraControl) + , mOnSuccessCb(onSuccess) + , mOnErrorCb(onError) + { + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + } + + virtual ~ReleaseHardwareTask() + { + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + } + + NS_IMETHOD Run() + { + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); + nsresult rv = mCameraControl->ReleaseHardwareImpl(this); + DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__); + + if (NS_FAILED(rv) && mOnErrorCb) { + rv = NS_DispatchToMainThread(new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId())); + NS_ENSURE_SUCCESS(rv, rv); + } + return rv; + } + + nsRefPtr mCameraControl; + nsCOMPtr mOnSuccessCb; + nsCOMPtr mOnErrorCb; +}; + +// Report that the video recorder state has changed. class CameraRecorderStateChange : public nsRunnable { public: diff --git a/dom/camera/DOMCameraControl.cpp b/dom/camera/DOMCameraControl.cpp index 51064105155..3076b530946 100755 --- a/dom/camera/DOMCameraControl.cpp +++ b/dom/camera/DOMCameraControl.cpp @@ -373,6 +373,12 @@ nsDOMCameraControl::GetPreviewStreamVideoMode(const JS::Value& aOptions, nsICame return mCameraControl->GetPreviewStreamVideoMode(&options, onSuccess, onError); } +NS_IMETHODIMP +nsDOMCameraControl::ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError) +{ + return mCameraControl->ReleaseHardware(onSuccess, onError); +} + class GetCameraResult : public nsRunnable { public: diff --git a/dom/camera/DOMCameraPreview.cpp b/dom/camera/DOMCameraPreview.cpp index bea992186da..b8ea7710a2c 100644 --- a/dom/camera/DOMCameraPreview.cpp +++ b/dom/camera/DOMCameraPreview.cpp @@ -212,7 +212,7 @@ DOMCameraPreview::Start() * This reference is removed in SetStateStopped(). */ NS_ADDREF_THIS(); - mState = STARTING; + DOM_CAMERA_SETSTATE(STARTING); mCameraControl->StartPreview(this); } @@ -221,7 +221,7 @@ DOMCameraPreview::SetStateStarted() { NS_ASSERTION(NS_IsMainThread(), "SetStateStarted() not called from main thread"); - mState = STARTED; + DOM_CAMERA_SETSTATE(STARTED); DOM_CAMERA_LOGI("Preview stream started\n"); } @@ -249,7 +249,7 @@ DOMCameraPreview::StopPreview() } DOM_CAMERA_LOGI("Stopping preview stream\n"); - mState = STOPPING; + DOM_CAMERA_SETSTATE(STOPPING); mCameraControl->StopPreview(); mInput->EndTrack(TRACK_VIDEO); mInput->Finish(); @@ -265,7 +265,7 @@ DOMCameraPreview::SetStateStopped() mInput->EndTrack(TRACK_VIDEO); mInput->Finish(); } - mState = STOPPED; + DOM_CAMERA_SETSTATE(STOPPED); DOM_CAMERA_LOGI("Preview stream stopped\n"); /** diff --git a/dom/camera/DOMCameraPreview.h b/dom/camera/DOMCameraPreview.h index 0e27352e2f3..d2ad4585c78 100644 --- a/dom/camera/DOMCameraPreview.h +++ b/dom/camera/DOMCameraPreview.h @@ -12,7 +12,6 @@ #include "nsDOMMediaStream.h" #include "CameraCommon.h" - namespace mozilla { typedef void (*FrameBuilder)(mozilla::layers::Image* aImage, void* aBuffer, uint32_t aWidth, uint32_t aHeight); @@ -38,7 +37,7 @@ public: void Start(); // called by the MediaStreamListener to start preview void Started(); // called by the CameraControl when preview is started - void StopPreview(); // called by the MediaStreamListener to stop preview + void StopPreview(); // called by the MediaStreamListener to stop preview void Stopped(bool aForced = false); // called by the CameraControl when preview is stopped void Error(); // something went wrong, NS_RELEASE needed @@ -57,6 +56,23 @@ protected: }; uint32_t mState; + // Helper function, used in conjunction with the macro below, to make + // it easy to track state changes, which must happen only on the main + // thread. + void + SetState(uint32_t aNewState, const char* aFileOrFunc, int aLine) + { +#ifdef PR_LOGGING + const char* states[] = { "stopped", "starting", "started", "stopping" }; + MOZ_ASSERT(mState < sizeof(states) / sizeof(states[0])); + MOZ_ASSERT(aNewState < sizeof(states) / sizeof(states[0])); + DOM_CAMERA_LOGI("SetState: (this=%p) '%s' --> '%s' : %s:%d\n", this, states[mState], states[aNewState], aFileOrFunc, aLine); +#endif + + NS_ASSERTION(NS_IsMainThread(), "Preview state set OFF OF main thread!"); + mState = aNewState; + } + uint32_t mWidth; uint32_t mHeight; uint32_t mFramesPerSecond; @@ -76,4 +92,6 @@ private: } // namespace mozilla +#define DOM_CAMERA_SETSTATE(newState) SetState((newState), __func__, __LINE__) + #endif // DOM_CAMERA_DOMCAMERAPREVIEW_H diff --git a/dom/camera/GonkCameraControl.cpp b/dom/camera/GonkCameraControl.cpp index 08daf0bd6b8..39d76e2e015 100644 --- a/dom/camera/GonkCameraControl.cpp +++ b/dom/camera/GonkCameraControl.cpp @@ -282,7 +282,7 @@ nsGonkCameraControl::Init() nsGonkCameraControl::~nsGonkCameraControl() { DOM_CAMERA_LOGT("%s:%d : this=%p, mHwHandle = %d\n", __func__, __LINE__, this, mHwHandle); - GonkCameraHardware::ReleaseHandle(mHwHandle); + ReleaseHardwareImpl(nullptr); if (mRwLock) { PRRWLock* lock = mRwLock; mRwLock = nullptr; @@ -1309,6 +1309,32 @@ nsGonkCameraControl::GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask return NS_OK; } +nsresult +nsGonkCameraControl::ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware) +{ + DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this); + + // if we're recording, stop recording + if (mRecorder) { + DOM_CAMERA_LOGI("shutting down existing video recorder\n"); + mRecorder->stop(); + mRecorder = nullptr; + } + + // stop the preview + StopPreviewInternal(true /* forced */); + + // release the hardware handle + GonkCameraHardware::ReleaseHandle(mHwHandle); + + if (aReleaseHardware && aReleaseHardware->mOnSuccessCb) { + nsCOMPtr releaseHardwareResult = new ReleaseHardwareResult(aReleaseHardware->mOnSuccessCb, mWindowId); + return NS_DispatchToMainThread(releaseHardwareResult); + } + + return NS_OK; +} + already_AddRefed nsGonkCameraControl::GetGonkRecorderProfileManager() { diff --git a/dom/camera/GonkCameraControl.h b/dom/camera/GonkCameraControl.h index d28150b53bf..4591d358618 100644 --- a/dom/camera/GonkCameraControl.h +++ b/dom/camera/GonkCameraControl.h @@ -76,6 +76,7 @@ protected: nsresult PushParametersImpl(); nsresult PullParametersImpl(); nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode); + nsresult ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware); already_AddRefed GetRecorderProfileManagerImpl(); already_AddRefed GetGonkRecorderProfileManager(); diff --git a/dom/camera/ICameraControl.h b/dom/camera/ICameraControl.h index 1dc6db6b136..8205b6e8e2f 100644 --- a/dom/camera/ICameraControl.h +++ b/dom/camera/ICameraControl.h @@ -30,6 +30,7 @@ public: virtual nsresult StartRecording(dom::CameraStartRecordingOptions* aOptions, nsIFile* aFolder, const nsAString& aFilename, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0; virtual nsresult StopRecording() = 0; virtual nsresult GetPreviewStreamVideoMode(dom::CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0; + virtual nsresult ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError) = 0; virtual nsresult Set(uint32_t aKey, const nsAString& aValue) = 0; virtual nsresult Get(uint32_t aKey, nsAString& aValue) = 0; diff --git a/dom/camera/nsIDOMCameraManager.idl b/dom/camera/nsIDOMCameraManager.idl index 5e2b3de5dfe..717ffdc162d 100644 --- a/dom/camera/nsIDOMCameraManager.idl +++ b/dom/camera/nsIDOMCameraManager.idl @@ -241,6 +241,12 @@ interface nsICameraRecorderStateChange : nsISupports void handleStateChange(in DOMString newState); }; +[scriptable, function, uuid(f84d607b-554c-413d-8810-cf848642765a)] +interface nsICameraReleaseCallback : nsISupports +{ + void handleEvent(); +}; + [scriptable, function, uuid(a302c6c9-3776-4d1d-a395-f4105d47c3d3)] interface nsICameraErrorCallback : nsISupports { @@ -251,7 +257,7 @@ interface nsICameraErrorCallback : nsISupports attributes here affect the preview, any pictures taken, and/or any video recorded by the camera. */ -[scriptable, uuid(70f45209-b69b-4937-bbac-57d82600e2af)] +[scriptable, uuid(c8e7418d-8913-4b66-bd9f-562fba627266)] interface nsICameraControl : nsISupports { readonly attribute nsICameraCapabilities capabilities; @@ -391,6 +397,17 @@ interface nsICameraControl : nsISupports /* call in or after the takePicture() onSuccess callback to resume the camera preview stream. */ void resumePreview(); + + /* release the camera so that other applications can use it; you should + probably call this whenever the camera is not longer in the foreground + (depending on your usage model). + + the callbacks are optional, unless you really need to know when + the hardware is ultimately released. + + once this is called, the camera control object is to be considered + defunct; a new instance will need to be created to access the camera. */ + [binaryname(ReleaseHardware)] void release([optional] in nsICameraReleaseCallback onSuccess, [optional] in nsICameraErrorCallback onError); }; [scriptable, function, uuid(a267afbc-d91c-413a-8de5-0b94aecffa3e)]