diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml
index e0a2c866b4e8..ee9994f26bf8 100644
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -12,7 +12,7 @@
-
+
diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml
index 08a521270b18..df15da588e41 100644
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml
index e0a2c866b4e8..ee9994f26bf8 100644
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -12,7 +12,7 @@
-
+
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index 84d84441b554..d305018e6a37 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,4 +1,4 @@
{
- "revision": "b9f86acc02f79d37ad0e10e9ee3b9cc75f870126",
+ "revision": "973662e5df416f77550a9cdbc7ec597c623489c3",
"repo_path": "/integration/gaia-central"
}
diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml
index 6aa42f13b079..9a9ce833d390 100644
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml
index 650f52dfdd31..da75692a708b 100644
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,7 +10,7 @@
-
+
diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml
index 22d372ecb91e..f740c1f9bc88 100644
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -12,7 +12,7 @@
-
+
diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml
index b5d049a37684..940a2629df33 100644
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml
index cb3bfe05d7ea..a9fcbeaaecbe 100644
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml
index a5c9f83e4f70..a5b860a44330 100644
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -11,7 +11,7 @@
-
+
diff --git a/content/media/webrtc/MediaEngineWebRTC.h b/content/media/webrtc/MediaEngineWebRTC.h
index cb06a60cfecf..05b88ed8f429 100644
--- a/content/media/webrtc/MediaEngineWebRTC.h
+++ b/content/media/webrtc/MediaEngineWebRTC.h
@@ -154,12 +154,16 @@ public:
return false;
}
+#ifndef MOZ_B2G_CAMERA
NS_DECL_THREADSAFE_ISUPPORTS
-#ifdef MOZ_B2G_CAMERA
+#else
+ // We are subclassed from CameraControlListener, which implements a
+ // threadsafe reference-count for us.
+ NS_DECL_ISUPPORTS_INHERITED
+
void OnHardwareStateChange(HardwareState aState);
- void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration);
bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
- void OnError(CameraErrorContext aContext, const nsACString& aError);
+ void OnError(CameraErrorContext aContext, CameraError aError);
void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
void AllocImpl();
diff --git a/content/media/webrtc/MediaEngineWebRTCVideo.cpp b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
index 7d772801ce66..49d2cbd3c8d2 100644
--- a/content/media/webrtc/MediaEngineWebRTCVideo.cpp
+++ b/content/media/webrtc/MediaEngineWebRTCVideo.cpp
@@ -25,7 +25,13 @@ extern PRLogModuleInfo* GetMediaManagerLog();
/**
* Webrtc video source.
*/
+#ifndef MOZ_B2G_CAMERA
NS_IMPL_ISUPPORTS1(MediaEngineWebRTCVideoSource, nsIRunnable)
+#else
+NS_IMPL_QUERY_INTERFACE1(MediaEngineWebRTCVideoSource, nsIRunnable)
+NS_IMPL_ADDREF_INHERITED(MediaEngineWebRTCVideoSource, CameraControlListener)
+NS_IMPL_RELEASE_INHERITED(MediaEngineWebRTCVideoSource, CameraControlListener)
+#endif
// ViEExternalRenderer Callback.
#ifndef MOZ_B2G_CAMERA
@@ -494,15 +500,21 @@ void
MediaEngineWebRTCVideoSource::AllocImpl() {
MOZ_ASSERT(NS_IsMainThread());
- mCameraControl = ICameraControl::Create(mCaptureIndex, nullptr);
- mCameraControl->AddListener(this);
+ mCameraControl = ICameraControl::Create(mCaptureIndex);
+ if (mCameraControl) {
+ mState = kAllocated;
+ // Add this as a listener for CameraControl events. We don't need
+ // to explicitly remove this--destroying the CameraControl object
+ // in DeallocImpl() will do that for us.
+ mCameraControl->AddListener(this);
+ }
+ mCallbackMonitor.Notify();
}
void
MediaEngineWebRTCVideoSource::DeallocImpl() {
MOZ_ASSERT(NS_IsMainThread());
- mCameraControl->ReleaseHardware();
mCameraControl = nullptr;
}
@@ -514,7 +526,7 @@ MediaEngineWebRTCVideoSource::StartImpl(webrtc::CaptureCapability aCapability) {
config.mMode = ICameraControl::kPictureMode;
config.mPreviewSize.width = aCapability.width;
config.mPreviewSize.height = aCapability.height;
- mCameraControl->SetConfiguration(config);
+ mCameraControl->Start(&config);
mCameraControl->Set(CAMERA_PARAM_PICTURESIZE, config.mPreviewSize);
}
@@ -522,7 +534,7 @@ void
MediaEngineWebRTCVideoSource::StopImpl() {
MOZ_ASSERT(NS_IsMainThread());
- mCameraControl->StopPreview();
+ mCameraControl->Stop();
}
void
@@ -535,25 +547,23 @@ void
MediaEngineWebRTCVideoSource::OnHardwareStateChange(HardwareState aState)
{
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
- if (aState == CameraControlListener::kHardwareOpen) {
- mState = kAllocated;
+ if (aState == CameraControlListener::kHardwareClosed) {
+ // When the first CameraControl listener is added, it gets pushed
+ // the current state of the camera--normally 'closed'. We only
+ // pay attention to that state if we've progressed out of the
+ // allocated state.
+ if (mState != kAllocated) {
+ mState = kReleased;
+ mCallbackMonitor.Notify();
+ }
} else {
- mState = kReleased;
- mCameraControl->RemoveListener(this);
+ mState = kStarted;
+ mCallbackMonitor.Notify();
}
- mCallbackMonitor.Notify();
}
void
-MediaEngineWebRTCVideoSource::OnConfigurationChange(const CameraListenerConfiguration& aConfiguration)
-{
- ReentrantMonitorAutoEnter sync(mCallbackMonitor);
- mState = kStarted;
- mCallbackMonitor.Notify();
-}
-
-void
-MediaEngineWebRTCVideoSource::OnError(CameraErrorContext aContext, const nsACString& aError)
+MediaEngineWebRTCVideoSource::OnError(CameraErrorContext aContext, CameraError aError)
{
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
mCallbackMonitor.Notify();
diff --git a/dom/browser-element/mochitest/test_browserElement_NoPref.html b/dom/browser-element/mochitest/test_browserElement_NoPref.html
index 12c289e6e3fa..d7924850f96b 100644
--- a/dom/browser-element/mochitest/test_browserElement_NoPref.html
+++ b/dom/browser-element/mochitest/test_browserElement_NoPref.html
@@ -41,7 +41,7 @@ function runTest() {
iframe.src = browserElementTestHelpers.emptyPage1;
}
-addEventListener('load', function() { SimpleTest.executeSoon(runTest); });
+addEventListener('testready', runTest);
diff --git a/dom/browser-element/mochitest/test_browserElement_NoWhitelist.html b/dom/browser-element/mochitest/test_browserElement_NoWhitelist.html
index 1d9971577c1c..6e71280372ca 100644
--- a/dom/browser-element/mochitest/test_browserElement_NoWhitelist.html
+++ b/dom/browser-element/mochitest/test_browserElement_NoWhitelist.html
@@ -42,7 +42,7 @@ function runTest() {
iframe.src = browserElementTestHelpers.emptyPage1;
}
-addEventListener('load', function() { SimpleTest.executeSoon(runTest); });
+addEventListener('testready', runTest);
diff --git a/dom/camera/CameraControlImpl.cpp b/dom/camera/CameraControlImpl.cpp
index 8896ddcee527..17902486645b 100644
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -235,12 +235,33 @@ CameraControlImpl::OnError(CameraControlListener::CameraErrorContext aContext,
RwLockAutoEnterRead lock(mListenerLock);
#ifdef PR_LOGGING
- const char* error[] = { "camera-service-failed", "unknown" };
- if (static_cast(aError) < sizeof(error) / sizeof(error[0])) {
- DOM_CAMERA_LOGW("CameraControlImpl::OnError : aContext=%u, msg='%s'\n",
- aContext, error[aError]);
+ const char* error[] = {
+ "api-failed",
+ "init-failed",
+ "invalid-configuration",
+ "service-failed",
+ "set-picture-size-failred",
+ "set-thumbnail-size-failed",
+ "unknown"
+ };
+ const char* context[] = {
+ "StartCamera",
+ "StopCamera",
+ "AutoFocus",
+ "TakePicture",
+ "StartRecording",
+ "StopRecording",
+ "SetConfiguration",
+ "StartPreview",
+ "StopPreview",
+ "Unspecified"
+ };
+ if (static_cast(aError) < sizeof(error) / sizeof(error[0]) &&
+ static_cast(aContext) < sizeof(context) / sizeof(context[0])) {
+ DOM_CAMERA_LOGW("CameraControlImpl::OnError : aContext='%s' (%u), aError='%s' (%u)\n",
+ context[aContext], aContext, error[aError], aError);
} else {
- DOM_CAMERA_LOGE("CameraControlImpl::OnError : aContext=%u, unknown error=%d\n",
+ DOM_CAMERA_LOGE("CameraControlImpl::OnError : aContext=%u, aError=%d\n",
aContext, aError);
}
#endif
@@ -293,6 +314,42 @@ protected:
CameraControlListener::CameraErrorContext mContext;
};
+nsresult
+CameraControlImpl::Start(const Configuration* aConfig)
+{
+ class Message : public ControlMessage
+ {
+ public:
+ Message(CameraControlImpl* aCameraControl,
+ CameraControlListener::CameraErrorContext aContext,
+ const Configuration* aConfig)
+ : ControlMessage(aCameraControl, aContext)
+ , mHaveInitialConfig(false)
+ {
+ if (aConfig) {
+ mConfig = *aConfig;
+ mHaveInitialConfig = true;
+ }
+ }
+
+ nsresult
+ RunImpl() MOZ_OVERRIDE
+ {
+ if (mHaveInitialConfig) {
+ return mCameraControl->StartImpl(&mConfig);
+ }
+ return mCameraControl->StartImpl();
+ }
+
+ protected:
+ bool mHaveInitialConfig;
+ Configuration mConfig;
+ };
+
+ return mCameraThread->Dispatch(
+ new Message(this, CameraControlListener::kInStartCamera, aConfig), NS_DISPATCH_NORMAL);
+}
+
nsresult
CameraControlImpl::SetConfiguration(const Configuration& aConfig)
{
@@ -475,7 +532,7 @@ CameraControlImpl::StopPreview()
}
nsresult
-CameraControlImpl::ReleaseHardware()
+CameraControlImpl::Stop()
{
class Message : public ControlMessage
{
@@ -488,12 +545,12 @@ CameraControlImpl::ReleaseHardware()
nsresult
RunImpl() MOZ_OVERRIDE
{
- return mCameraControl->ReleaseHardwareImpl();
+ return mCameraControl->StopImpl();
}
};
return mCameraThread->Dispatch(
- new Message(this, CameraControlListener::kInReleaseHardware), NS_DISPATCH_NORMAL);
+ new Message(this, CameraControlListener::kInStopCamera), NS_DISPATCH_NORMAL);
}
class CameraControlImpl::ListenerMessage : public CameraControlImpl::ControlMessage
@@ -515,6 +572,7 @@ CameraControlImpl::AddListenerImpl(already_AddRefed aList
RwLockAutoEnterWrite lock(mListenerLock);
CameraControlListener* l = *mListeners.AppendElement() = aListener;
+ DOM_CAMERA_LOGI("Added camera control listener %p\n", l);
// Update the newly-added listener's state
l->OnConfigurationChange(mCurrentConfiguration);
@@ -551,6 +609,7 @@ CameraControlImpl::RemoveListenerImpl(CameraControlListener* aListener)
nsRefPtr l(aListener);
mListeners.RemoveElement(l);
+ DOM_CAMERA_LOGI("Removed camera control listener %p\n", l.get());
// XXXmikeh - do we want to notify the listener that it has been removed?
}
diff --git a/dom/camera/CameraControlImpl.h b/dom/camera/CameraControlImpl.h
index cb0a8ea57545..93e6ff40f93a 100644
--- a/dom/camera/CameraControlImpl.h
+++ b/dom/camera/CameraControlImpl.h
@@ -36,6 +36,9 @@ public:
virtual void AddListener(CameraControlListener* aListener) MOZ_OVERRIDE;
virtual void RemoveListener(CameraControlListener* aListener) MOZ_OVERRIDE;
+ virtual nsresult Start(const Configuration* aConfig = nullptr) MOZ_OVERRIDE;
+ virtual nsresult Stop() MOZ_OVERRIDE;
+
virtual nsresult SetConfiguration(const Configuration& aConfig) MOZ_OVERRIDE;
virtual nsresult StartPreview() MOZ_OVERRIDE;
virtual nsresult StopPreview() MOZ_OVERRIDE;
@@ -44,7 +47,6 @@ public:
virtual nsresult StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
const StartRecordingOptions* aOptions) MOZ_OVERRIDE;
virtual nsresult StopRecording() MOZ_OVERRIDE;
- virtual nsresult ReleaseHardware() MOZ_OVERRIDE;
already_AddRefed GetRecorderProfileManager();
uint32_t GetCameraId() { return mCameraId; }
@@ -63,7 +65,7 @@ protected:
bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
void OnRecorderStateChange(CameraControlListener::RecorderState aState,
- int32_t aStatus, int32_t aTrackNumber);
+ int32_t aStatus = -1, int32_t aTrackNumber = -1);
void OnPreviewStateChange(CameraControlListener::PreviewState aState);
void OnHardwareStateChange(CameraControlListener::HardwareState aState);
void OnConfigurationChange();
@@ -90,6 +92,8 @@ protected:
class ControlMessage;
class ListenerMessage;
+ virtual nsresult StartImpl(const Configuration* aConfig = nullptr) = 0;
+ virtual nsresult StopImpl() = 0;
virtual nsresult SetConfigurationImpl(const Configuration& aConfig) = 0;
virtual nsresult StartPreviewImpl() = 0;
virtual nsresult StopPreviewImpl() = 0;
@@ -100,7 +104,6 @@ protected:
virtual nsresult StopRecordingImpl() = 0;
virtual nsresult PushParametersImpl() = 0;
virtual nsresult PullParametersImpl() = 0;
- virtual nsresult ReleaseHardwareImpl() = 0;
virtual already_AddRefed GetRecorderProfileManagerImpl() = 0;
void OnShutterInternal();
diff --git a/dom/camera/CameraControlListener.h b/dom/camera/CameraControlListener.h
index 76065ddb3003..c7a58fc16d0b 100644
--- a/dom/camera/CameraControlListener.h
+++ b/dom/camera/CameraControlListener.h
@@ -17,7 +17,15 @@ namespace layers {
class CameraControlListener
{
public:
- virtual ~CameraControlListener() { }
+ CameraControlListener()
+ {
+ MOZ_COUNT_CTOR(CameraControlListener);
+ }
+
+ virtual ~CameraControlListener()
+ {
+ MOZ_COUNT_DTOR(CameraControlListener);
+ }
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CameraControlListener);
@@ -71,13 +79,13 @@ public:
enum CameraErrorContext
{
- kInGetCamera,
+ kInStartCamera,
+ kInStopCamera,
kInAutoFocus,
kInTakePicture,
kInStartRecording,
kInStopRecording,
kInSetConfiguration,
- kInReleaseHardware,
kInStartPreview,
kInStopPreview,
kInUnspecified
diff --git a/dom/camera/DOMCameraControl.cpp b/dom/camera/DOMCameraControl.cpp
index 8b550923fe52..c9e750759264 100644
--- a/dom/camera/DOMCameraControl.cpp
+++ b/dom/camera/DOMCameraControl.cpp
@@ -201,7 +201,7 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
config.mPreviewSize.height = aInitialConfig.mPreviewSize.mHeight;
config.mRecorderProfile = aInitialConfig.mRecorderProfile;
- mCameraControl = ICameraControl::Create(aCameraId, &config);
+ mCameraControl = ICameraControl::Create(aCameraId);
mCurrentConfiguration = initialConfig.forget();
// Attach our DOM-facing media stream to our viewfinder stream.
@@ -214,6 +214,13 @@ nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId,
// Register a listener for camera events.
mListener = new DOMCameraControlListener(this, mInput);
mCameraControl->AddListener(mListener);
+
+ // Start the camera...
+ nsresult rv = mCameraControl->Start(&config);
+ if (NS_FAILED(rv)) {
+ mListener->OnError(DOMCameraControlListener::kInStartCamera,
+ DOMCameraControlListener::kErrorApiFailed);
+ }
}
nsDOMCameraControl::~nsDOMCameraControl()
@@ -911,7 +918,7 @@ nsDOMCameraControl::ReleaseHardware(const OptionalReleaseHardware();
+ aRv = mCameraControl->Stop();
}
void
@@ -1157,11 +1164,16 @@ nsDOMCameraControl::OnError(CameraControlListener::CameraErrorContext aContext,
nsCOMPtr* errorCb;
switch (aContext) {
- case CameraControlListener::kInGetCamera:
+ case CameraControlListener::kInStartCamera:
mGetCameraOnSuccessCb = nullptr;
errorCb = &mGetCameraOnErrorCb;
break;
+ case CameraControlListener::kInStopCamera:
+ mReleaseOnSuccessCb = nullptr;
+ errorCb = &mReleaseOnErrorCb;
+ break;
+
case CameraControlListener::kInSetConfiguration:
mSetConfigurationOnSuccessCb = nullptr;
errorCb = &mSetConfigurationOnErrorCb;
@@ -1182,11 +1194,6 @@ nsDOMCameraControl::OnError(CameraControlListener::CameraErrorContext aContext,
errorCb = &mStartRecordingOnErrorCb;
break;
- case CameraControlListener::kInReleaseHardware:
- mReleaseOnSuccessCb = nullptr;
- errorCb = &mReleaseOnErrorCb;
- break;
-
case CameraControlListener::kInStopRecording:
NS_WARNING("Failed to stop recording (which shouldn't happen)!");
MOZ_CRASH();
diff --git a/dom/camera/DOMCameraControlListener.cpp b/dom/camera/DOMCameraControlListener.cpp
index de91c59043f3..4918a2e76588 100644
--- a/dom/camera/DOMCameraControlListener.cpp
+++ b/dom/camera/DOMCameraControlListener.cpp
@@ -13,14 +13,33 @@
using namespace mozilla;
using namespace mozilla::dom;
+DOMCameraControlListener::DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl,
+ CameraPreviewMediaStream* aStream)
+ : mDOMCameraControl(new nsMainThreadPtrHolder(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:
DOMCallback(nsMainThreadPtrHandle aDOMCameraControl)
: mDOMCameraControl(aDOMCameraControl)
- { }
- virtual ~DOMCallback() { }
+ {
+ MOZ_COUNT_CTOR(DOMCameraControlListener::DOMCallback);
+ }
+ virtual ~DOMCallback()
+ {
+ MOZ_COUNT_DTOR(DOMCameraControlListener::DOMCallback);
+ }
virtual void RunCallback(nsDOMCameraControl* aDOMCameraControl) = 0;
@@ -178,7 +197,8 @@ DOMCameraControlListener::OnConfigurationChange(const CameraListenerConfiguratio
break;
default:
- MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode!");
+ DOM_CAMERA_LOGI("Camera mode still unspecified, nothing to do\n");
+ return;
}
// Map CameraControl parameters to their DOM-facing equivalents
@@ -298,7 +318,7 @@ DOMCameraControlListener::OnError(CameraErrorContext aContext, CameraError aErro
, mError(aError)
{ }
- void
+ virtual void
RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
{
nsString error;
diff --git a/dom/camera/DOMCameraControlListener.h b/dom/camera/DOMCameraControlListener.h
index c8638f7a9f10..5840a347f55e 100644
--- a/dom/camera/DOMCameraControlListener.h
+++ b/dom/camera/DOMCameraControlListener.h
@@ -15,29 +15,28 @@ class CameraPreviewMediaStream;
class DOMCameraControlListener : public CameraControlListener
{
+public:
+ DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl, CameraPreviewMediaStream* aStream);
+
+ virtual void OnAutoFocusComplete(bool aAutoFocusSucceeded) MOZ_OVERRIDE;
+ virtual void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) MOZ_OVERRIDE;
+
+ virtual void OnHardwareStateChange(HardwareState aState) MOZ_OVERRIDE;
+ virtual void OnPreviewStateChange(PreviewState aState) MOZ_OVERRIDE;
+ virtual void OnRecorderStateChange(RecorderState aState, int32_t aStatus, int32_t aTrackNum) MOZ_OVERRIDE;
+ virtual void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration) MOZ_OVERRIDE;
+ virtual void OnShutter() MOZ_OVERRIDE;
+ virtual bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) MOZ_OVERRIDE;
+ virtual void OnError(CameraErrorContext aContext, CameraError aError) MOZ_OVERRIDE;
+
protected:
+ virtual ~DOMCameraControlListener();
+
nsMainThreadPtrHandle mDOMCameraControl;
CameraPreviewMediaStream* mStream;
class DOMCallback;
-public:
- DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl, CameraPreviewMediaStream* aStream)
- : mDOMCameraControl(new nsMainThreadPtrHolder(aDOMCameraControl))
- , mStream(aStream)
- { }
-
- void OnAutoFocusComplete(bool aAutoFocusSucceeded);
- void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
-
- void OnHardwareStateChange(HardwareState aState);
- void OnPreviewStateChange(PreviewState aState);
- void OnRecorderStateChange(RecorderState aState, int32_t aStatus, int32_t aTrackNum);
- void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration);
- void OnShutter();
- bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
- void OnError(CameraErrorContext aContext, CameraError aError);
-
private:
DOMCameraControlListener(const DOMCameraControlListener&) MOZ_DELETE;
DOMCameraControlListener& operator=(const DOMCameraControlListener&) MOZ_DELETE;
diff --git a/dom/camera/FallbackCameraManager.cpp b/dom/camera/FallbackCameraManager.cpp
index 513db97d35c6..720b3931a4eb 100644
--- a/dom/camera/FallbackCameraManager.cpp
+++ b/dom/camera/FallbackCameraManager.cpp
@@ -26,7 +26,7 @@ ICameraControl::GetListOfCameras(nsTArray& aList)
}
already_AddRefed
-ICameraControl::Create(uint32_t aCameraId, const Configuration* aInitialConfig)
+ICameraControl::Create(uint32_t aCameraId)
{
return nullptr;
}
diff --git a/dom/camera/GonkCameraControl.cpp b/dom/camera/GonkCameraControl.cpp
index de4973b8ac0b..db47acbb1188 100644
--- a/dom/camera/GonkCameraControl.cpp
+++ b/dom/camera/GonkCameraControl.cpp
@@ -77,90 +77,50 @@ nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId)
}
nsresult
-nsGonkCameraControl::Init(const Configuration* aInitialConfig)
+nsGonkCameraControl::StartImpl(const Configuration* aInitialConfig)
{
- class InitGonkCameraControl : public nsRunnable
- {
- public:
- InitGonkCameraControl(nsGonkCameraControl* aCameraControl,
- const Configuration* aConfig)
- : mCameraControl(aCameraControl)
- , mHaveInitialConfig(false)
- {
- DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
- if (aConfig) {
- mConfig = *aConfig;
- mHaveInitialConfig = true;
- }
+ /**
+ * For initialization, we try to return the camera control to the upper
+ * upper layer (i.e. the DOM) as quickly as possible. To do this, the
+ * camera is initialized in the following stages:
+ *
+ * 0. Initialize() initializes the hardware;
+ * 1. SetConfigurationInternal() does the minimal configuration
+ * required so that we can start the preview -and- report a valid
+ * configuration to the upper layer;
+ * 2. OnHardwareStateChange() reports that the hardware is ready,
+ * which the upper (e.g. DOM) layer can (and does) use to return
+ * the camera control object;
+ * 3. StartPreviewImpl() starts the flow of preview frames from the
+ * camera hardware.
+ *
+ * The intent of the above flow is to let the Main Thread do as much work
+ * up-front as possible without waiting for blocking Camera Thread calls
+ * to complete.
+ */
+ MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
+
+ nsresult rv = Initialize();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (aInitialConfig) {
+ rv = SetConfigurationInternal(*aInitialConfig);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // The initial configuration failed, close up the hardware
+ StopImpl();
+ return rv;
}
+ }
- ~InitGonkCameraControl()
- {
- DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
- }
-
- /**
- * For initialization, we try to return the camera control to the upper
- * upper layer (i.e. the DOM) as quickly as possible. To do this, the
- * camera is initialized in the following stages:
- *
- * 0. InitImpl() initializes the hardware;
- * 1. SetConfigurationInternal() does the minimal configuration
- * required so that we can start the preview -and- report a valid
- * configuration to the upper layer;
- * 2. OnHardwareStateChange() reports that the hardware is ready,
- * which the upper layer can (and does) use to return the camera
- * control object;
- * 3. StartPreviewImpl() starts the flow of preview frames from the
- * camera hardware.
- *
- * The intent of the above flow is to let the Main Thread do as much work
- * up-front as possible without waiting for blocking Camera Thread calls
- * to complete.
- */
- NS_IMETHODIMP
- Run() MOZ_OVERRIDE
- {
- nsresult rv = mCameraControl->InitImpl();
- if (NS_FAILED(rv)) {
- mCameraControl->OnError(CameraControlListener::kInGetCamera,
- CameraControlListener::kErrorInitFailed);
- // The hardware failed to initialize, so close it up
- mCameraControl->ReleaseHardware();
- return rv;
- }
-
- if (mHaveInitialConfig) {
- rv = mCameraControl->SetConfigurationInternal(mConfig);
- if (NS_FAILED(rv)) {
- mCameraControl->OnError(CameraControlListener::kInGetCamera,
- CameraControlListener::kErrorInvalidConfiguration);
- // The initial configuration failed, close up the hardware
- mCameraControl->ReleaseHardware();
- return rv;
- }
- }
-
- mCameraControl->OnHardwareStateChange(CameraControlListener::kHardwareOpen);
- return mCameraControl->StartPreviewImpl();
- }
-
- protected:
- nsRefPtr mCameraControl;
- Configuration mConfig;
- bool mHaveInitialConfig;
- };
-
- // Initialization is carried out on the camera thread.
- return mCameraThread->Dispatch(
- new InitGonkCameraControl(this, aInitialConfig), NS_DISPATCH_NORMAL);
+ OnHardwareStateChange(CameraControlListener::kHardwareOpen);
+ return StartPreviewImpl();
}
nsresult
-nsGonkCameraControl::InitImpl()
+nsGonkCameraControl::Initialize()
{
- MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
-
mCameraHw = GonkCameraHardware::Connect(this, mCameraId);
if (!mCameraHw.get()) {
DOM_CAMERA_LOGE("Failed to connect to camera %d (this=%p)\n", mCameraId, this);
@@ -211,7 +171,7 @@ nsGonkCameraControl::~nsGonkCameraControl()
{
DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraHw = %p\n", __func__, __LINE__, this, mCameraHw.get());
- ReleaseHardwareImpl();
+ StopImpl();
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
}
@@ -255,11 +215,18 @@ nsGonkCameraControl::SetConfigurationImpl(const Configuration& aConfig)
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
// Stop any currently running preview
- StopPreviewImpl();
+ nsresult rv = PausePreview();
+ if (NS_FAILED(rv)) {
+ // warn, but plow ahead
+ NS_WARNING("PausePreview() in SetConfigurationImpl() failed");
+ }
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
- nsresult rv = SetConfigurationInternal(aConfig);
- NS_ENSURE_SUCCESS(rv, rv);
+ rv = SetConfigurationInternal(aConfig);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ StopPreviewImpl();
+ return rv;
+ }
// Restart the preview
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
@@ -546,11 +513,22 @@ nsGonkCameraControl::StopPreviewImpl()
DOM_CAMERA_LOGI("Stopping preview (this=%p)\n", this);
mCameraHw->StopPreview();
-
OnPreviewStateChange(CameraControlListener::kPreviewStopped);
return NS_OK;
}
+nsresult
+nsGonkCameraControl::PausePreview()
+{
+ RETURN_IF_NO_CAMERA_HW();
+
+ DOM_CAMERA_LOGI("Pausing preview (this=%p)\n", this);
+
+ mCameraHw->StopPreview();
+ OnPreviewStateChange(CameraControlListener::kPreviewPaused);
+ return NS_OK;
+}
+
nsresult
nsGonkCameraControl::AutoFocusImpl(bool aCancelExistingCall)
{
@@ -892,7 +870,7 @@ nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescri
return NS_ERROR_FAILURE;
}
- OnRecorderStateChange(CameraControlListener::kRecorderStarted, -1, -1);
+ OnRecorderStateChange(CameraControlListener::kRecorderStarted);
return NS_OK;
}
@@ -929,7 +907,7 @@ nsGonkCameraControl::StopRecordingImpl()
mRecorder->stop();
mRecorder = nullptr;
- OnRecorderStateChange(CameraControlListener::kRecorderStopped, -1, -1);
+ OnRecorderStateChange(CameraControlListener::kRecorderStopped);
// notify DeviceStorage that the new video file is closed and ready
return NS_DispatchToMainThread(new RecordingComplete(mVideoFile), NS_DISPATCH_NORMAL);
@@ -1339,15 +1317,16 @@ nsGonkCameraControl::SetupRecording(int aFd, int aRotation, int64_t aMaxFileSize
}
nsresult
-nsGonkCameraControl::ReleaseHardwareImpl()
+nsGonkCameraControl::StopImpl()
{
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");
+ DOM_CAMERA_LOGI("Stopping existing video recorder\n");
mRecorder->stop();
mRecorder = nullptr;
+ OnRecorderStateChange(CameraControlListener::kRecorderStopped);
}
// stop the preview
diff --git a/dom/camera/GonkCameraControl.h b/dom/camera/GonkCameraControl.h
index c16950ff5f87..c62aec903cd6 100644
--- a/dom/camera/GonkCameraControl.h
+++ b/dom/camera/GonkCameraControl.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Mozilla Foundation
+ * Copyright (C) 2012-2014 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -47,8 +47,6 @@ class nsGonkCameraControl : public CameraControlImpl
{
public:
nsGonkCameraControl(uint32_t aCameraId);
- nsresult Init(const Configuration* aInitialConfig);
- nsresult InitImpl();
void OnAutoFocusComplete(bool aSuccess);
void OnTakePictureComplete(uint8_t* aData, uint32_t aLength);
@@ -92,6 +90,10 @@ protected:
virtual void BeginBatchParameterSet() MOZ_OVERRIDE;
virtual void EndBatchParameterSet() MOZ_OVERRIDE;
+ virtual nsresult StartImpl(const Configuration* aInitialConfig = nullptr) MOZ_OVERRIDE;
+ virtual nsresult StopImpl() MOZ_OVERRIDE;
+ nsresult Initialize();
+
virtual nsresult SetConfigurationImpl(const Configuration& aConfig) MOZ_OVERRIDE;
nsresult SetConfigurationInternal(const Configuration& aConfig);
nsresult SetPictureConfiguration(const Configuration& aConfig);
@@ -108,13 +110,13 @@ protected:
virtual nsresult StopRecordingImpl() MOZ_OVERRIDE;
virtual nsresult PushParametersImpl() MOZ_OVERRIDE;
virtual nsresult PullParametersImpl() MOZ_OVERRIDE;
- virtual nsresult ReleaseHardwareImpl() MOZ_OVERRIDE;
virtual already_AddRefed GetRecorderProfileManagerImpl() MOZ_OVERRIDE;
already_AddRefed GetGonkRecorderProfileManager();
nsresult SetupRecording(int aFd, int aRotation, int64_t aMaxFileSizeBytes, int64_t aMaxVideoLengthMs);
nsresult SetupVideoMode(const nsAString& aProfile);
nsresult SetPreviewSize(const Size& aSize);
+ nsresult PausePreview();
friend class SetPictureSize;
friend class SetThumbnailSize;
diff --git a/dom/camera/GonkCameraHwMgr.cpp b/dom/camera/GonkCameraHwMgr.cpp
index e93e8caa1569..c71a4634e2c7 100644
--- a/dom/camera/GonkCameraHwMgr.cpp
+++ b/dom/camera/GonkCameraHwMgr.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2013 Mozilla Foundation
+ * Copyright (C) 2012-2014 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,12 +15,14 @@
*/
#include "GonkCameraHwMgr.h"
+#include "TestGonkCameraHardware.h"
#include
#include
#include "base/basictypes.h"
#include "nsDebug.h"
+#include "mozilla/Preferences.h"
#include "GonkCameraControl.h"
#include "GonkNativeWindow.h"
#include "CameraCommon.h"
@@ -35,11 +37,9 @@ GonkCameraHardware::GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, ui
, mNumFrames(0)
, mCamera(aCamera)
, mTarget(aTarget)
- , mInitialized(false)
, mSensorOrientation(0)
{
DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n", __func__, __LINE__, (void*)this, (void*)aTarget);
- Init();
}
void
@@ -127,7 +127,7 @@ GonkCameraHardware::postDataTimestamp(nsecs_t aTimestamp, int32_t aMsgType, cons
}
}
-void
+nsresult
GonkCameraHardware::Init()
{
DOM_CAMERA_LOGT("%s: this=%p\n", __func__, (void* )this);
@@ -136,7 +136,7 @@ GonkCameraHardware::Init()
int rv = Camera::getCameraInfo(mCameraId, &info);
if (rv != 0) {
DOM_CAMERA_LOGE("%s: failed to get CameraInfo mCameraId %d\n", __func__, mCameraId);
- return;
+ return NS_ERROR_FAILURE;
}
mRawSensorOrientation = info.orientation;
@@ -168,7 +168,8 @@ GonkCameraHardware::Init()
#else
mCamera->setPreviewTexture(mNativeWindow);
#endif
- mInitialized = true;
+
+ return NS_OK;
}
sp
@@ -180,18 +181,33 @@ GonkCameraHardware::Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCam
sp camera = Camera::connect(aCameraId);
#endif
-
if (camera.get() == nullptr) {
return nullptr;
}
- sp cameraHardware = new GonkCameraHardware(aTarget, aCameraId, camera);
+
+ const nsAdoptingCString& test =
+ mozilla::Preferences::GetCString("camera.control.test.enabled");
+ sp cameraHardware;
+ if (test.EqualsASCII("hardware")) {
+ NS_WARNING("Using test Gonk hardware layer");
+ cameraHardware = new TestGonkCameraHardware(aTarget, aCameraId, camera);
+ } else {
+ cameraHardware = new GonkCameraHardware(aTarget, aCameraId, camera);
+ }
+
+ nsresult rv = cameraHardware->Init();
+ if (NS_FAILED(rv)) {
+ DOM_CAMERA_LOGE("Failed to initialize camera hardware (0x%X)\n", rv);
+ return nullptr;
+ }
+
return cameraHardware;
- }
+}
void
GonkCameraHardware::Close()
{
- DOM_CAMERA_LOGT( "%s:%d : this=%p\n", __func__, __LINE__, (void*)this );
+ DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, (void*)this);
mClosing = true;
mCamera->stopPreview();
@@ -208,7 +224,7 @@ GonkCameraHardware::Close()
GonkCameraHardware::~GonkCameraHardware()
{
- DOM_CAMERA_LOGT( "%s:%d : this=%p\n", __func__, __LINE__, (void*)this );
+ DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, (void*)this);
mCamera.clear();
mNativeWindow.clear();
@@ -308,7 +324,7 @@ void
GonkCameraHardware::StopPreview()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
- return mCamera->stopPreview();
+ mCamera->stopPreview();
}
int
diff --git a/dom/camera/GonkCameraHwMgr.h b/dom/camera/GonkCameraHwMgr.h
index 948eeed05797..936f6b278d6e 100644
--- a/dom/camera/GonkCameraHwMgr.h
+++ b/dom/camera/GonkCameraHwMgr.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Mozilla Foundation
+ * Copyright (C) 2012-2014 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,14 +43,14 @@ class GonkCameraHardware : public GonkNativeWindowNewFrameCallback
protected:
GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId, const sp& aCamera);
virtual ~GonkCameraHardware();
- void Init();
+ virtual nsresult Init();
public:
- static sp Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId);
- void Close();
+ static sp Connect(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId);
+ virtual void Close();
// derived from GonkNativeWindowNewFrameCallback
- virtual void OnNewFrame() MOZ_OVERRIDE;
+ virtual void OnNewFrame() MOZ_OVERRIDE;
// derived from CameraListener
virtual void notify(int32_t aMsgType, int32_t ext1, int32_t ext2);
@@ -75,26 +75,25 @@ public:
RAW_SENSOR_ORIENTATION,
OFFSET_SENSOR_ORIENTATION
};
- int GetSensorOrientation(uint32_t aType = RAW_SENSOR_ORIENTATION);
+ virtual int GetSensorOrientation(uint32_t aType = RAW_SENSOR_ORIENTATION);
- int AutoFocus();
- void CancelAutoFocus();
- int TakePicture();
- void CancelTakePicture();
- int StartPreview();
- void StopPreview();
- int PushParameters(const mozilla::GonkCameraParameters& aParams);
- int PushParameters(const CameraParameters& aParams);
- nsresult PullParameters(mozilla::GonkCameraParameters& aParams);
- void PullParameters(CameraParameters& aParams);
- int StartRecording();
- int StopRecording();
- int SetListener(const sp& aListener);
- void ReleaseRecordingFrame(const sp& aFrame);
- int StoreMetaDataInBuffers(bool aEnabled);
+ virtual int AutoFocus();
+ virtual void CancelAutoFocus();
+ virtual int TakePicture();
+ virtual void CancelTakePicture();
+ virtual int StartPreview();
+ virtual void StopPreview();
+ virtual int PushParameters(const mozilla::GonkCameraParameters& aParams);
+ virtual int PushParameters(const CameraParameters& aParams);
+ virtual nsresult PullParameters(mozilla::GonkCameraParameters& aParams);
+ virtual void PullParameters(CameraParameters& aParams);
+ virtual int StartRecording();
+ virtual int StopRecording();
+ virtual int SetListener(const sp& aListener);
+ virtual void ReleaseRecordingFrame(const sp& aFrame);
+ virtual int StoreMetaDataInBuffers(bool aEnabled);
protected:
-
uint32_t mCameraId;
bool mClosing;
uint32_t mNumFrames;
@@ -102,15 +101,9 @@ protected:
mozilla::nsGonkCameraControl* mTarget;
sp mNativeWindow;
sp mListener;
- bool mInitialized;
int mRawSensorOrientation;
int mSensorOrientation;
- bool IsInitialized()
- {
- return mInitialized;
- }
-
private:
GonkCameraHardware(const GonkCameraHardware&) MOZ_DELETE;
GonkCameraHardware& operator=(const GonkCameraHardware&) MOZ_DELETE;
diff --git a/dom/camera/GonkCameraManager.cpp b/dom/camera/GonkCameraManager.cpp
index 46d7bacde161..80631383feaa 100644
--- a/dom/camera/GonkCameraManager.cpp
+++ b/dom/camera/GonkCameraManager.cpp
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012 Mozilla Foundation
+ * Copyright (C) 2012-2014 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -115,18 +115,8 @@ ICameraControl::GetListOfCameras(nsTArray& aList)
// implementation-specific camera factory
already_AddRefed
-ICameraControl::Create(uint32_t aCameraId, const Configuration* aInitialConfig)
+ICameraControl::Create(uint32_t aCameraId)
{
- if (aInitialConfig) {
- DOM_CAMERA_LOGI("Creating camera %d control, initial mode '%s'\n",
- aCameraId, aInitialConfig->mMode == kVideoMode ? "video" : "picture");
- } else {
- DOM_CAMERA_LOGI("Creating camera %d control, no intial configuration\n", aCameraId);
- }
-
nsRefPtr control = new nsGonkCameraControl(aCameraId);
- nsresult rv = control->Init(aInitialConfig);
- NS_ENSURE_SUCCESS(rv, nullptr);
-
return control.forget();
}
diff --git a/dom/camera/ICameraControl.h b/dom/camera/ICameraControl.h
index 0c68d1571f7e..c6ca140743c1 100644
--- a/dom/camera/ICameraControl.h
+++ b/dom/camera/ICameraControl.h
@@ -117,8 +117,11 @@ public:
Size mPreviewSize;
nsString mRecorderProfile;
};
- static already_AddRefed Create(uint32_t aCameraId,
- const Configuration* aInitialConfig);
+ static already_AddRefed Create(uint32_t aCameraId);
+
+ virtual nsresult Start(const Configuration* aInitialConfig = nullptr) = 0;
+ virtual nsresult Stop() = 0;
+
virtual nsresult SetConfiguration(const Configuration& aConfig) = 0;
virtual void AddListener(CameraControlListener* aListener) = 0;
@@ -131,7 +134,6 @@ public:
virtual nsresult StartRecording(DeviceStorageFileDescriptor *aFileDescriptor,
const StartRecordingOptions* aOptions = nullptr) = 0;
virtual nsresult StopRecording() = 0;
- virtual nsresult ReleaseHardware() = 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/TestGonkCameraHardware.cpp b/dom/camera/TestGonkCameraHardware.cpp
new file mode 100644
index 000000000000..05ed6c6a850c
--- /dev/null
+++ b/dom/camera/TestGonkCameraHardware.cpp
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2013-2014 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "TestGonkCameraHardware.h"
+
+#include "mozilla/Preferences.h"
+#include "nsThreadUtils.h"
+
+using namespace android;
+using namespace mozilla;
+
+TestGonkCameraHardware::TestGonkCameraHardware(nsGonkCameraControl* aTarget,
+ uint32_t aCameraId,
+ const sp& aCamera)
+ : GonkCameraHardware(aTarget, aCameraId, aCamera)
+{
+ DOM_CAMERA_LOGA("+===== Created TestGonkCameraHardware =====+\n");
+ DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n",
+ __func__, __LINE__, this, aTarget);
+ MOZ_COUNT_CTOR(TestGonkCameraHardware);
+}
+
+TestGonkCameraHardware::~TestGonkCameraHardware()
+{
+ MOZ_COUNT_DTOR(TestGonkCameraHardware);
+ DOM_CAMERA_LOGA("+===== Destroyed TestGonkCameraHardware =====+\n");
+}
+
+nsresult
+TestGonkCameraHardware::Init()
+{
+ if (IsTestCase("init-failure")) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return GonkCameraHardware::Init();
+}
+
+const nsCString
+TestGonkCameraHardware::TestCase()
+{
+ const nsCString test = Preferences::GetCString("camera.control.test.hardware");
+ return test;
+}
+
+bool
+TestGonkCameraHardware::IsTestCaseInternal(const char* aTest, const char* aFile, int aLine)
+{
+ if (TestCase().EqualsASCII(aTest)) {
+ DOM_CAMERA_LOGA("TestGonkCameraHardware : test-case '%s' (%s:%d)\n",
+ aTest, aFile, aLine);
+ return true;
+ }
+
+ return false;
+}
+
+int
+TestGonkCameraHardware::TestCaseError(int aDefaultError)
+{
+ // for now, just return the default error
+ return aDefaultError;
+}
+
+int
+TestGonkCameraHardware::AutoFocus()
+{
+ class AutoFocusFailure : public nsRunnable
+ {
+ public:
+ AutoFocusFailure(nsGonkCameraControl* aTarget)
+ : mTarget(aTarget)
+ { }
+
+ NS_IMETHODIMP
+ Run()
+ {
+ OnAutoFocusComplete(mTarget, false);
+ return NS_OK;
+ }
+
+ protected:
+ nsGonkCameraControl* mTarget;
+ };
+
+ if (IsTestCase("auto-focus-failure")) {
+ return TestCaseError(UNKNOWN_ERROR);
+ }
+ if (IsTestCase("auto-focus-process-failure")) {
+ nsresult rv = NS_DispatchToCurrentThread(new AutoFocusFailure(mTarget));
+ if (NS_SUCCEEDED(rv)) {
+ return OK;
+ }
+ DOM_CAMERA_LOGE("Failed to dispatch AutoFocusFailure runnable (0x%08x)\n", rv);
+ return UNKNOWN_ERROR;
+ }
+
+ return GonkCameraHardware::AutoFocus();
+}
+
+int
+TestGonkCameraHardware::TakePicture()
+{
+ class TakePictureFailure : public nsRunnable
+ {
+ public:
+ TakePictureFailure(nsGonkCameraControl* aTarget)
+ : mTarget(aTarget)
+ { }
+
+ NS_IMETHODIMP
+ Run()
+ {
+ OnTakePictureError(mTarget);
+ return NS_OK;
+ }
+
+ protected:
+ nsGonkCameraControl* mTarget;
+ };
+
+ if (IsTestCase("take-picture-failure")) {
+ return TestCaseError(UNKNOWN_ERROR);
+ }
+ if (IsTestCase("take-picture-process-failure")) {
+ nsresult rv = NS_DispatchToCurrentThread(new TakePictureFailure(mTarget));
+ if (NS_SUCCEEDED(rv)) {
+ return OK;
+ }
+ DOM_CAMERA_LOGE("Failed to dispatch TakePictureFailure runnable (0x%08x)\n", rv);
+ return UNKNOWN_ERROR;
+ }
+
+ return GonkCameraHardware::TakePicture();
+}
+
+int
+TestGonkCameraHardware::StartPreview()
+{
+ if (IsTestCase("start-preview-failure")) {
+ return TestCaseError(UNKNOWN_ERROR);
+ }
+
+ return GonkCameraHardware::StartPreview();
+}
+
+int
+TestGonkCameraHardware::PushParameters(const GonkCameraParameters& aParams)
+{
+ if (IsTestCase("push-parameters-failure")) {
+ return TestCaseError(UNKNOWN_ERROR);
+ }
+
+ return GonkCameraHardware::PushParameters(aParams);
+}
+
+nsresult
+TestGonkCameraHardware::PullParameters(GonkCameraParameters& aParams)
+{
+ if (IsTestCase("pull-parameters-failure")) {
+ return static_cast(TestCaseError(UNKNOWN_ERROR));
+ }
+
+ return GonkCameraHardware::PullParameters(aParams);
+}
+
+int
+TestGonkCameraHardware::StartRecording()
+{
+ if (IsTestCase("start-recording-failure")) {
+ return TestCaseError(UNKNOWN_ERROR);
+ }
+
+ return GonkCameraHardware::StartRecording();
+}
+
+int
+TestGonkCameraHardware::StopRecording()
+{
+ if (IsTestCase("stop-recording-failure")) {
+ return TestCaseError(UNKNOWN_ERROR);
+ }
+
+ return GonkCameraHardware::StopRecording();
+}
+
+int
+TestGonkCameraHardware::SetListener(const sp& aListener)
+{
+ if (IsTestCase("set-listener-failure")) {
+ return TestCaseError(UNKNOWN_ERROR);
+ }
+
+ return GonkCameraHardware::SetListener(aListener);
+}
+
+int
+TestGonkCameraHardware::StoreMetaDataInBuffers(bool aEnabled)
+{
+ if (IsTestCase("store-metadata-in-buffers-failure")) {
+ return TestCaseError(UNKNOWN_ERROR);
+ }
+
+ return GonkCameraHardware::StoreMetaDataInBuffers(aEnabled);
+}
diff --git a/dom/camera/TestGonkCameraHardware.h b/dom/camera/TestGonkCameraHardware.h
new file mode 100644
index 000000000000..2ee7d700bb24
--- /dev/null
+++ b/dom/camera/TestGonkCameraHardware.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2013-2014 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DOM_CAMERA_TESTGONKCAMERAHARDWARE_H
+#define DOM_CAMERA_TESTGONKCAMERAHARDWARE_H
+
+#include "GonkCameraHwMgr.h"
+
+namespace android {
+
+class TestGonkCameraHardware : public android::GonkCameraHardware
+{
+public:
+ virtual int AutoFocus() MOZ_OVERRIDE;
+ virtual int TakePicture() MOZ_OVERRIDE;
+ virtual int StartPreview() MOZ_OVERRIDE;
+ virtual int PushParameters(const mozilla::GonkCameraParameters& aParams) MOZ_OVERRIDE;
+ virtual nsresult PullParameters(mozilla::GonkCameraParameters& aParams) MOZ_OVERRIDE;
+ virtual int StartRecording() MOZ_OVERRIDE;
+ virtual int StopRecording() MOZ_OVERRIDE;
+ virtual int SetListener(const sp& aListener) MOZ_OVERRIDE;
+ virtual int StoreMetaDataInBuffers(bool aEnabled) MOZ_OVERRIDE;
+
+ virtual int
+ PushParameters(const CameraParameters& aParams) MOZ_OVERRIDE
+ {
+ return GonkCameraHardware::PushParameters(aParams);
+ }
+
+ virtual void
+ PullParameters(CameraParameters& aParams) MOZ_OVERRIDE
+ {
+ GonkCameraHardware::PullParameters(aParams);
+ }
+
+ TestGonkCameraHardware(mozilla::nsGonkCameraControl* aTarget,
+ uint32_t aCameraId,
+ const sp& aCamera);
+ virtual ~TestGonkCameraHardware();
+
+ virtual nsresult Init() MOZ_OVERRIDE;
+
+protected:
+ const nsCString TestCase();
+ bool IsTestCaseInternal(const char* aTest, const char* aFile, int aLine);
+ int TestCaseError(int aDefaultError);
+
+private:
+ TestGonkCameraHardware(const TestGonkCameraHardware&) MOZ_DELETE;
+ TestGonkCameraHardware& operator=(const TestGonkCameraHardware&) MOZ_DELETE;
+};
+
+#define IsTestCase(test) IsTestCaseInternal((test), __FILE__, __LINE__)
+
+} // namespace android
+
+#endif // DOM_CAMERA_TESTGONKCAMERAHARDWARE_H
diff --git a/dom/camera/moz.build b/dom/camera/moz.build
index 02de152bdff7..70729eb71383 100644
--- a/dom/camera/moz.build
+++ b/dom/camera/moz.build
@@ -33,6 +33,7 @@ if CONFIG['MOZ_B2G_CAMERA']:
'GonkCameraSource.cpp',
'GonkRecorder.cpp',
'GonkRecorderProfiles.cpp',
+ 'TestGonkCameraHardware.cpp',
]
else:
SOURCES += [
diff --git a/dom/camera/test/camera_common.js b/dom/camera/test/camera_common.js
new file mode 100644
index 000000000000..0718e7f63de3
--- /dev/null
+++ b/dom/camera/test/camera_common.js
@@ -0,0 +1,105 @@
+var CameraTest = (function() {
+ 'use strict';
+
+ /**
+ * 'camera.control.test.enabled' is queried in Gecko to enable different
+ * test modes in the camera stack. The only currently-supported setting
+ * is 'hardware', which wraps the Gonk camera abstraction class in a
+ * shim class that supports injecting hardware camera API failures into
+ * the execution path.
+ *
+ * The affected API is specified by the 'camera.control.test.hardware'
+ * pref. Currently supported values should be determined by inspecting
+ * TestGonkCameraHardware.cpp.
+ *
+ * Some API calls are simple: e.g. 'start-recording-failure' will cause
+ * the DOM-facing startRecording() call to fail. More complex tests like
+ * 'take-picture-failure' will cause the takePicture() API to fail, while
+ * 'take-picture-process-failure' will simulate a failure of the
+ * asynchronous picture-taking process, even if the initial API call
+ * path seems to have succeeded.
+ */
+ const PREF_TEST_ENABLED = "camera.control.test.enabled";
+ const PREF_TEST_HARDWARE = "camera.control.test.hardware";
+ var oldTestEnabled;
+ var oldTestHw;
+ var testMode;
+
+ function testHardwareSet(test, callback) {
+ SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_HARDWARE, test]]}, function() {
+ var setTest = SpecialPowers.getCharPref(PREF_TEST_HARDWARE);
+ ise(setTest, test, "Test subtype set to " + setTest);
+ if (callback) {
+ callback(setTest);
+ }
+ });
+ }
+
+ function testHardwareDone(callback) {
+ testMode = null;
+ if (oldTestHw) {
+ SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_HARDWARE, oldTestHw]]}, callback);
+ } else {
+ SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_HARDWARE]]}, callback);
+ }
+ }
+
+ function testBegin(mode, callback) {
+ SimpleTest.waitForExplicitFinish();
+ try {
+ oldTestEnabled = SpecialPowers.getCharPref(PREF_TEST_ENABLED);
+ } catch(e) { }
+ SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_ENABLED, mode]]}, function() {
+ var setMode = SpecialPowers.getCharPref(PREF_TEST_ENABLED);
+ ise(setMode, mode, "Test mode set to " + setMode);
+ if (setMode === "hardware") {
+ try {
+ oldTestHw = SpecialPowers.getCharPref(PREF_TEST_HARDWARE);
+ } catch(e) { }
+ testMode = {
+ set: testHardwareSet,
+ done: testHardwareDone
+ };
+ if (callback) {
+ callback(testMode);
+ }
+ }
+ });
+ }
+
+ function testEnd(callback) {
+ function allDone(cb) {
+ function cb2() {
+ SimpleTest.finish();
+ if (cb) {
+ cb();
+ }
+ }
+ if (oldTestEnabled) {
+ SpecialPowers.pushPrefEnv({'set': [[PREF_TEST_ENABLED, oldTestEnabled]]}, cb2);
+ } else {
+ SpecialPowers.pushPrefEnv({'clear': [[PREF_TEST_ENABLED]]}, cb2);
+ }
+ }
+
+ if (testMode) {
+ testMode.done(function() {
+ allDone(callback);
+ });
+ testMode = null;
+ } else {
+ allDone(function() {
+ if (callback) {
+ callback();
+ }
+ });
+ }
+ }
+
+ ise(SpecialPowers.sanityCheck(), "foo", "SpecialPowers passed sanity check");
+ return {
+ begin: testBegin,
+ end: testEnd
+ };
+
+})();
diff --git a/dom/camera/test/mochitest.ini b/dom/camera/test/mochitest.ini
index 8bd4a3a65701..187fe40fca67 100644
--- a/dom/camera/test/mochitest.ini
+++ b/dom/camera/test/mochitest.ini
@@ -1,4 +1,8 @@
[DEFAULT]
+support-files = camera_common.js
[test_camera.html]
[test_camera_2.html]
+[test_camera_3.html]
+[test_camera_hardware_init_failure.html]
+[test_camera_hardware_failures.html]
diff --git a/dom/camera/test/test_camera.html b/dom/camera/test/test_camera.html
index ed425ee73545..37a1615ad88d 100644
--- a/dom/camera/test/test_camera.html
+++ b/dom/camera/test/test_camera.html
@@ -7,7 +7,7 @@
-
+
+
+
+
+
+
+
+
+
+