Bug 818933 - Provide a hardware release API so that we don't have to rely on CC of CameraControl objects. r=mhabicher

This commit is contained in:
Mike Habicher 2012-12-23 10:54:54 -05:00
parent 2b635dfd91
commit 518afe6c97
9 changed files with 155 additions and 9 deletions

View File

@ -367,6 +367,13 @@ CameraControlImpl::GetPreviewStreamVideoMode(CameraRecorderOptions* aOptions, ns
return mCameraThread->Dispatch(getPreviewStreamVideoModeTask, NS_DISPATCH_NORMAL);
}
nsresult
CameraControlImpl::ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError)
{
nsCOMPtr<nsIRunnable> releaseHardwareTask = new ReleaseHardwareTask(this, onSuccess, onError);
return mCameraThread->Dispatch(releaseHardwareTask, NS_DISPATCH_NORMAL);
}
bool
CameraControlImpl::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder)
{

View File

@ -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<RecorderProfileManager> GetRecorderProfileManagerImpl() = 0;
void OnShutterInternal();
@ -610,7 +614,73 @@ public:
nsCOMPtr<nsICameraErrorCallback> 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<nsICameraReleaseCallback> 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<CameraControlImpl> mCameraControl;
nsCOMPtr<nsICameraReleaseCallback> mOnSuccessCb;
nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
};
// Report that the video recorder state has changed.
class CameraRecorderStateChange : public nsRunnable
{
public:

View File

@ -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:

View File

@ -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");
/**

View File

@ -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);
@ -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

View File

@ -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<nsIRunnable> releaseHardwareResult = new ReleaseHardwareResult(aReleaseHardware->mOnSuccessCb, mWindowId);
return NS_DispatchToMainThread(releaseHardwareResult);
}
return NS_OK;
}
already_AddRefed<GonkRecorderProfileManager>
nsGonkCameraControl::GetGonkRecorderProfileManager()
{

View File

@ -76,6 +76,7 @@ protected:
nsresult PushParametersImpl();
nsresult PullParametersImpl();
nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode);
nsresult ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware);
already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl();
already_AddRefed<GonkRecorderProfileManager> GetGonkRecorderProfileManager();

View File

@ -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;

View File

@ -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)]