Bug 909542 - refactor CameraControl API, r=dhylands,jst,jesup,onecyrenus

This commit is contained in:
Mike Habicher 2014-02-14 17:41:07 -05:00
parent 2b49e82edb
commit 557d8c2d5c
53 changed files with 5052 additions and 4437 deletions

View File

@ -167,7 +167,6 @@
#ifdef MOZ_B2G_BT
@BINPATH@/components/dom_bluetooth.xpt
#endif
@BINPATH@/components/dom_camera.xpt
@BINPATH@/components/dom_canvas.xpt
@BINPATH@/components/dom_contacts.xpt
@BINPATH@/components/dom_alarm.xpt

View File

@ -197,7 +197,6 @@
#ifdef MOZ_B2G_BT
@BINPATH@/components/dom_bluetooth.xpt
#endif
@BINPATH@/components/dom_camera.xpt
@BINPATH@/components/dom_canvas.xpt
@BINPATH@/components/dom_alarm.xpt
@BINPATH@/components/dom_core.xpt

View File

@ -42,7 +42,7 @@ GetUserMediaLog()
#define LOG(args) PR_LOG(GetUserMediaLog(), PR_LOG_DEBUG, args)
namespace mozilla {
#ifndef MOZ_B2G_CAMERA
MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
: mMutex("mozilla::MediaEngineWebRTC")
, mVideoEngine(nullptr)
@ -51,27 +51,26 @@ MediaEngineWebRTC::MediaEngineWebRTC(MediaEnginePrefs &aPrefs)
, mAudioEngineInit(false)
, mHasTabVideoSource(false)
{
#ifndef MOZ_B2G_CAMERA
nsCOMPtr<nsIComponentRegistrar> compMgr;
NS_GetComponentRegistrar(getter_AddRefs(compMgr));
if (compMgr) {
compMgr->IsContractIDRegistered(NS_TABSOURCESERVICE_CONTRACTID, &mHasTabVideoSource);
}
#else
AsyncLatencyLogger::Get()->AddRef();
#endif
if (aPrefs.mLoadAdapt) {
mLoadMonitor = new LoadMonitor();
mLoadMonitor->Init(mLoadMonitor);
}
}
#endif
void
MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSource> >* aVSources)
{
#ifdef MOZ_B2G_CAMERA
MutexAutoLock lock(mMutex);
if (!mCameraManager) {
return;
}
/**
* We still enumerate every time, in case a new device was plugged in since
@ -83,14 +82,14 @@ MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSourc
*/
int num = 0;
nsresult result;
result = mCameraManager->GetNumberOfCameras(num);
result = ICameraControl::GetNumberOfCameras(num);
if (num <= 0 || result != NS_OK) {
return;
}
for (int i = 0; i < num; i++) {
nsCString cameraName;
result = mCameraManager->GetCameraName(i, cameraName);
result = ICameraControl::GetCameraName(i, cameraName);
if (result != NS_OK) {
continue;
}
@ -101,7 +100,7 @@ MediaEngineWebRTC::EnumerateVideoDevices(nsTArray<nsRefPtr<MediaEngineVideoSourc
// We've already seen this device, just append.
aVSources->AppendElement(vSource.get());
} else {
vSource = new MediaEngineWebRTCVideoSource(mCameraManager, i, mWindowId);
vSource = new MediaEngineWebRTCVideoSource(i);
mVideoSources.Put(uuid, vSource); // Hashtable takes ownership.
aVSources->AppendElement(vSource);
}

View File

@ -46,9 +46,8 @@
#include "webrtc/video_engine/include/vie_render.h"
#include "webrtc/video_engine/include/vie_capture.h"
#ifdef MOZ_B2G_CAMERA
#include "CameraPreviewMediaStream.h"
#include "DOMCameraManager.h"
#include "GonkCameraControl.h"
#include "CameraControlListener.h"
#include "ICameraControl.h"
#include "ImageContainer.h"
#include "nsGlobalWindow.h"
#include "prprf.h"
@ -73,7 +72,7 @@ class GetCameraNameRunnable;
* mSources, mImageContainer, mSources, mState, mImage, mLastCapture
*
* MainThread:
* mDOMCameraControl, mCaptureIndex, mCameraThread, mWindowId, mCameraManager,
* mCaptureIndex, mWindowId,
* mNativeCameraControl, mPreviewStream, mState, mLastCapture, mWidth, mHeight
*
* Where mWidth, mHeight, mImage are protected by mMonitor
@ -83,24 +82,15 @@ class GetCameraNameRunnable;
class MediaEngineWebRTCVideoSource : public MediaEngineVideoSource
, public nsRunnable
#ifdef MOZ_B2G_CAMERA
, public nsICameraGetCameraCallback
, public nsICameraPreviewStreamCallback
, public nsICameraTakePictureCallback
, public nsICameraReleaseCallback
, public nsICameraErrorCallback
, public CameraPreviewFrameCallback
, public CameraControlListener
#else
, public webrtc::ExternalRenderer
#endif
{
public:
#ifdef MOZ_B2G_CAMERA
MediaEngineWebRTCVideoSource(nsDOMCameraManager* aCameraManager,
int aIndex, uint64_t aWindowId)
: mCameraManager(aCameraManager)
, mNativeCameraControl(nullptr)
, mPreviewStream(nullptr)
, mWindowId(aWindowId)
MediaEngineWebRTCVideoSource(int aIndex)
: mCameraControl(nullptr)
, mCallbackMonitor("WebRTCCamera.CallbackMonitor")
, mCaptureIndex(aIndex)
, mMonitor("WebRTCCamera.Monitor")
@ -111,7 +101,6 @@ public:
, mSnapshotPath(nullptr)
{
mState = kReleased;
NS_NewNamedThread("CameraThread", getter_AddRefs(mCameraThread));
Init();
}
#else
@ -167,20 +156,17 @@ public:
NS_DECL_THREADSAFE_ISUPPORTS
#ifdef MOZ_B2G_CAMERA
NS_DECL_NSICAMERAGETCAMERACALLBACK
NS_DECL_NSICAMERAPREVIEWSTREAMCALLBACK
NS_DECL_NSICAMERATAKEPICTURECALLBACK
NS_DECL_NSICAMERARELEASECALLBACK
NS_DECL_NSICAMERAERRORCALLBACK
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 OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
void AllocImpl();
void DeallocImpl();
void StartImpl(webrtc::CaptureCapability aCapability);
void StopImpl();
void SnapshotImpl();
virtual void OnNewFrame(const gfxIntSize& aIntrinsicSize, layers::Image* aImage);
#endif
// This runnable is for creating a temporary file on the main thread.
@ -212,20 +198,8 @@ private:
// Engine variables.
#ifdef MOZ_B2G_CAMERA
// MediaEngine hold this DOM object, and the MediaEngine is hold by Navigator
// Their life time is always much longer than this object. Use a raw-pointer
// here should be safe.
// We need raw pointer here since such DOM-object should not addref/release on
// any thread other than main thread, but we must use this object for now. To
// avoid any bad thing do to addref/release DOM-object on other thread, we use
// raw-pointer for now.
nsDOMCameraManager* mCameraManager;
nsRefPtr<nsDOMCameraControl> mDOMCameraControl;
nsRefPtr<nsGonkCameraControl> mNativeCameraControl;
nsRefPtr<DOMCameraPreview> mPreviewStream;
uint64_t mWindowId;
nsRefPtr<ICameraControl> mCameraControl;
mozilla::ReentrantMonitor mCallbackMonitor; // Monitor for camera callback handling
nsRefPtr<nsIThread> mCameraThread;
nsRefPtr<nsIDOMFile> mLastCapture;
#else
webrtc::VideoEngine* mVideoEngine; // Weak reference, don't free.
@ -351,24 +325,7 @@ private:
class MediaEngineWebRTC : public MediaEngine
{
public:
#ifdef MOZ_B2G_CAMERA
MediaEngineWebRTC(nsDOMCameraManager* aCameraManager, uint64_t aWindowId)
: mMutex("mozilla::MediaEngineWebRTC")
, mVideoEngine(nullptr)
, mVoiceEngine(nullptr)
, mVideoEngineInit(false)
, mAudioEngineInit(false)
, mCameraManager(aCameraManager)
, mWindowId(aWindowId)
, mHasTabVideoSource(false)
{
AsyncLatencyLogger::Get(true)->AddRef();
mLoadMonitor = new LoadMonitor();
mLoadMonitor->Init(mLoadMonitor);
}
#else
MediaEngineWebRTC(MediaEnginePrefs &aPrefs);
#endif
~MediaEngineWebRTC() {
Shutdown();
#ifdef MOZ_B2G_CAMERA
@ -400,19 +357,7 @@ private:
nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCVideoSource > mVideoSources;
nsRefPtrHashtable<nsStringHashKey, MediaEngineWebRTCAudioSource > mAudioSources;
#ifdef MOZ_B2G_CAMERA
// MediaEngine hold this DOM object, and the MediaEngine is hold by Navigator
// Their life time is always much longer than this object. Use a raw-pointer
// here should be safe.
// We need raw pointer here since such DOM-object should not addref/release on
// any thread other than main thread, but we must use this object for now. To
// avoid any bad thing do to addref/release DOM-object on other thread, we use
// raw-pointer for now.
nsDOMCameraManager* mCameraManager;
uint64_t mWindowId;
#endif
nsRefPtr<LoadMonitor> mLoadMonitor;
nsRefPtr<LoadMonitor> mLoadMonitor;
};
}

View File

@ -312,7 +312,9 @@ nsresult
MediaEngineWebRTCVideoSource::Start(SourceMediaStream* aStream, TrackID aID)
{
LOG((__FUNCTION__));
#ifndef MOZ_B2G_CAMERA
int error = 0;
#endif
if (!mInitDone || !aStream) {
return NS_ERROR_FAILURE;
}
@ -413,7 +415,7 @@ MediaEngineWebRTCVideoSource::Init()
{
#ifdef MOZ_B2G_CAMERA
nsAutoCString deviceName;
mCameraManager->GetCameraName(mCaptureIndex, deviceName);
ICameraControl::GetCameraName(mCaptureIndex, deviceName);
CopyUTF8toUTF16(deviceName, mDeviceName);
CopyUTF8toUTF16(deviceName, mUniqueId);
#else
@ -492,125 +494,95 @@ void
MediaEngineWebRTCVideoSource::AllocImpl() {
MOZ_ASSERT(NS_IsMainThread());
mDOMCameraControl = new nsDOMCameraControl(mCaptureIndex,
mCameraThread,
this,
this,
nsGlobalWindow::GetInnerWindowWithId(mWindowId));
mCameraManager->Register(mDOMCameraControl);
mCameraControl = ICameraControl::Create(mCaptureIndex, nullptr);
mCameraControl->AddListener(this);
}
void
MediaEngineWebRTCVideoSource::DeallocImpl() {
MOZ_ASSERT(NS_IsMainThread());
mNativeCameraControl->ReleaseHardware(this, this);
mNativeCameraControl = nullptr;
mCameraControl->ReleaseHardware();
mCameraControl = nullptr;
}
void
MediaEngineWebRTCVideoSource::StartImpl(webrtc::CaptureCapability aCapability) {
MOZ_ASSERT(NS_IsMainThread());
idl::CameraSize size;
size.width = aCapability.width;
size.height = aCapability.height;
mNativeCameraControl->GetPreviewStream(size, this, this);
ICameraControl::Configuration config;
config.mMode = ICameraControl::kPictureMode;
config.mPreviewSize.width = aCapability.width;
config.mPreviewSize.height = aCapability.height;
mCameraControl->SetConfiguration(config);
mCameraControl->Set(CAMERA_PARAM_PICTURESIZE, config.mPreviewSize);
}
void
MediaEngineWebRTCVideoSource::StopImpl() {
MOZ_ASSERT(NS_IsMainThread());
mNativeCameraControl->StopPreview();
mPreviewStream = nullptr;
mCameraControl->StopPreview();
}
void
MediaEngineWebRTCVideoSource::SnapshotImpl() {
MOZ_ASSERT(NS_IsMainThread());
idl::CameraSize size;
size.width = mCapability.width;
size.height = mCapability.height;
idl::CameraPosition cameraPosition;
cameraPosition.latitude = NAN;
cameraPosition.longitude = NAN;
cameraPosition.altitude = NAN;
cameraPosition.timestamp = NAN;
mNativeCameraControl->TakePicture(size, 0, NS_LITERAL_STRING("jpeg"), cameraPosition, PR_Now() / 1000000, this, this);
mCameraControl->TakePicture();
}
// nsICameraGetCameraCallback
nsresult
MediaEngineWebRTCVideoSource::HandleEvent(nsISupports* /* unused */) {
MOZ_ASSERT(NS_IsMainThread());
void
MediaEngineWebRTCVideoSource::OnHardwareStateChange(HardwareState aState)
{
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
mNativeCameraControl = static_cast<nsGonkCameraControl*>(mDOMCameraControl->GetNativeCameraControl().get());
mState = kAllocated;
if (aState == CameraControlListener::kHardwareOpen) {
mState = kAllocated;
} else {
mState = kReleased;
mCameraControl->RemoveListener(this);
}
mCallbackMonitor.Notify();
return NS_OK;
}
// nsICameraPreviewStreamCallback
nsresult
MediaEngineWebRTCVideoSource::HandleEvent(nsIDOMMediaStream* stream) {
MOZ_ASSERT(NS_IsMainThread());
void
MediaEngineWebRTCVideoSource::OnConfigurationChange(const CameraListenerConfiguration& aConfiguration)
{
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
mPreviewStream = static_cast<DOMCameraPreview*>(stream);
mPreviewStream->Start();
CameraPreviewMediaStream* cameraStream = static_cast<CameraPreviewMediaStream*>(mPreviewStream->GetStream());
cameraStream->SetFrameCallback(this);
mState = kStarted;
mCallbackMonitor.Notify();
return NS_OK;
}
// nsICameraTakePictureCallback
nsresult
MediaEngineWebRTCVideoSource::HandleEvent(nsIDOMBlob* picture) {
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
mLastCapture = static_cast<nsIDOMFile*>(picture);
mCallbackMonitor.Notify();
return NS_OK;
}
// nsICameraReleaseCallback
nsresult
MediaEngineWebRTCVideoSource::HandleEvent() {
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
mState = kReleased;
mCallbackMonitor.Notify();
return NS_OK;
}
// nsICameraErrorCallback
nsresult
MediaEngineWebRTCVideoSource::HandleEvent(const nsAString& error) {
MOZ_ASSERT(NS_IsMainThread());
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
mCallbackMonitor.Notify();
return NS_OK;
}
//Except this one. This callback should called on camera preview thread.
void
MediaEngineWebRTCVideoSource::OnNewFrame(const gfxIntSize& aIntrinsicSize, layers::Image* aImage) {
MediaEngineWebRTCVideoSource::OnError(CameraErrorContext aContext, const nsACString& aError)
{
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
mCallbackMonitor.Notify();
}
void
MediaEngineWebRTCVideoSource::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
{
ReentrantMonitorAutoEnter sync(mCallbackMonitor);
mLastCapture =
static_cast<nsIDOMFile*>(new nsDOMMemoryFile(static_cast<void*>(aData),
static_cast<uint64_t>(aLength),
aMimeType));
mCallbackMonitor.Notify();
}
bool
MediaEngineWebRTCVideoSource::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight) {
MonitorAutoLock enter(mMonitor);
if (mState == kStopped) {
return;
return false;
}
mImage = aImage;
if (mWidth != aIntrinsicSize.width || mHeight != aIntrinsicSize.height) {
mWidth = aIntrinsicSize.width;
mHeight = aIntrinsicSize.height;
if (mWidth != static_cast<int>(aWidth) || mHeight != static_cast<int>(aHeight)) {
mWidth = aWidth;
mHeight = aHeight;
LOG(("Video FrameSizeChange: %ux%u", mWidth, mHeight));
}
return true; // return true because we're accepting the frame
}
#endif

View File

@ -160,7 +160,6 @@
#include "FMRadio.h"
#endif
#include "nsIDOMCameraManager.h"
#include "nsIDOMGlobalObjectConstructor.h"
#include "nsIDOMLockedFile.h"
#include "nsDebug.h"
@ -470,9 +469,6 @@ static nsDOMClassInfoData sClassInfoData[] = {
DOM_DEFAULT_SCRIPTABLE_FLAGS)
#endif
NS_DEFINE_CLASSINFO_DATA(CameraCapabilities, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(LockedFile, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(CSSFontFeatureValuesRule, nsDOMGenericSH,
@ -1176,10 +1172,6 @@ nsDOMClassInfo::Init()
#endif
DOM_CLASSINFO_MAP_BEGIN(CameraCapabilities, nsICameraCapabilities)
DOM_CLASSINFO_MAP_ENTRY(nsICameraCapabilities)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(LockedFile, nsIDOMLockedFile)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMLockedFile)
DOM_CLASSINFO_MAP_END

View File

@ -103,8 +103,6 @@ DOMCI_CLASS(CSSPageRule)
DOMCI_CLASS(MozIccManager)
#endif
DOMCI_CLASS(CameraCapabilities)
DOMCI_CLASS(LockedFile)
DOMCI_CLASS(CSSFontFeatureValuesRule)

View File

@ -180,6 +180,11 @@ DOMInterfaces = {
'headerFile': 'BluetoothManager.h'
},
'CameraCapabilities': {
'nativeType': 'mozilla::dom::CameraCapabilities',
'headerFile': 'DOMCameraCapabilities.h'
},
'CameraControl': {
'nativeType': 'mozilla::nsDOMCameraControl',
'headerFile': 'DOMCameraControl.h',
@ -1967,15 +1972,3 @@ addExternalIface('XPathExpression')
addExternalIface('XPathNSResolver')
addExternalIface('XULCommandDispatcher')
addExternalIface('DataTransfer', notflattened=True)
addExternalIface('GetCameraCallback', nativeType='nsICameraGetCameraCallback', headerFile='nsIDOMCameraManager.h')
addExternalIface('CameraErrorCallback', nativeType='nsICameraErrorCallback', headerFile='nsIDOMCameraManager.h')
addExternalIface('CameraCapabilities', nativeType='nsICameraCapabilities', headerFile='nsIDOMCameraManager.h')
addExternalIface('CameraAutoFocusCallback', nativeType='nsICameraAutoFocusCallback', headerFile='nsIDOMCameraManager.h')
addExternalIface('CameraShutterCallback', nativeType='nsICameraShutterCallback', headerFile='nsIDOMCameraManager.h')
addExternalIface('CameraClosedCallback', nativeType='nsICameraClosedCallback', headerFile='nsIDOMCameraManager.h')
addExternalIface('CameraTakePictureCallback', nativeType='nsICameraTakePictureCallback', headerFile='nsIDOMCameraManager.h')
addExternalIface('CameraReleaseCallback', nativeType='nsICameraReleaseCallback', headerFile='nsIDOMCameraManager.h')
addExternalIface('CameraStartRecordingCallback', nativeType='nsICameraStartRecordingCallback', headerFile='nsIDOMCameraManager.h')
addExternalIface('CameraPreviewStateChange', nativeType='nsICameraPreviewStateChange', headerFile='nsIDOMCameraManager.h')
addExternalIface('CameraPreviewStreamCallback', nativeType='nsICameraPreviewStreamCallback', headerFile='nsIDOMCameraManager.h')
addExternalIface('CameraRecorderStateChange', nativeType='nsICameraRecorderStateChange', headerFile='nsIDOMCameraManager.h')

49
dom/camera/AutoRwLock.h Normal file
View File

@ -0,0 +1,49 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef RWLOCK_AUTO_ENTER_H
#define RWLOCK_AUTO_ENTER_H
#include "prrwlock.h"
#include "mozilla/Assertions.h"
class RwLockAutoEnterRead
{
public:
RwLockAutoEnterRead(PRRWLock* aRwLock)
: mRwLock(aRwLock)
{
MOZ_ASSERT(mRwLock);
PR_RWLock_Rlock(mRwLock);
}
~RwLockAutoEnterRead()
{
PR_RWLock_Unlock(mRwLock);
}
protected:
PRRWLock* mRwLock;
};
class RwLockAutoEnterWrite
{
public:
RwLockAutoEnterWrite(PRRWLock* aRwLock)
: mRwLock(aRwLock)
{
MOZ_ASSERT(mRwLock);
PR_RWLock_Wlock(mRwLock);
}
~RwLockAutoEnterWrite()
{
PR_RWLock_Unlock(mRwLock);
}
protected:
PRRWLock* mRwLock;
};
#endif // RWLOCK_AUTO_ENTER_H

View File

@ -19,7 +19,6 @@
#define NAN std::numeric_limits<double>::quiet_NaN()
#endif
#include "nsIDOMCameraManager.h"
#include "prlog.h"
#ifdef PR_LOGGING
@ -62,44 +61,6 @@ enum {
#define DOM_CAMERA_LOGW( ... ) DOM_CAMERA_LOG( DOM_CAMERA_LOG_WARNING, __VA_ARGS__ )
#define DOM_CAMERA_LOGE( ... ) DOM_CAMERA_LOG( DOM_CAMERA_LOG_ERROR, __VA_ARGS__ )
enum {
CAMERA_PARAM_EFFECT,
CAMERA_PARAM_WHITEBALANCE,
CAMERA_PARAM_SCENEMODE,
CAMERA_PARAM_FLASHMODE,
CAMERA_PARAM_FOCUSMODE,
CAMERA_PARAM_ZOOM,
CAMERA_PARAM_METERINGAREAS,
CAMERA_PARAM_FOCUSAREAS,
CAMERA_PARAM_FOCALLENGTH,
CAMERA_PARAM_FOCUSDISTANCENEAR,
CAMERA_PARAM_FOCUSDISTANCEOPTIMUM,
CAMERA_PARAM_FOCUSDISTANCEFAR,
CAMERA_PARAM_EXPOSURECOMPENSATION,
CAMERA_PARAM_PICTURESIZE,
CAMERA_PARAM_THUMBNAILSIZE,
CAMERA_PARAM_THUMBNAILQUALITY,
CAMERA_PARAM_SENSORANGLE,
CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
CAMERA_PARAM_SUPPORTED_VIDEOSIZES,
CAMERA_PARAM_SUPPORTED_PICTURESIZES,
CAMERA_PARAM_SUPPORTED_PICTUREFORMATS,
CAMERA_PARAM_SUPPORTED_WHITEBALANCES,
CAMERA_PARAM_SUPPORTED_SCENEMODES,
CAMERA_PARAM_SUPPORTED_EFFECTS,
CAMERA_PARAM_SUPPORTED_FLASHMODES,
CAMERA_PARAM_SUPPORTED_FOCUSMODES,
CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS,
CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS,
CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION,
CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION,
CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP,
CAMERA_PARAM_SUPPORTED_ZOOM,
CAMERA_PARAM_SUPPORTED_ZOOMRATIOS,
CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES
};
#ifdef PR_LOGGING
static inline void nsLogAddRefCamera(const char *file, uint32_t line, void* p, uint32_t count, const char *clazz, uint32_t size)

View File

@ -2,278 +2,51 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "CameraControlImpl.h"
#include "base/basictypes.h"
#include "mozilla/Assertions.h"
#include "DOMCameraPreview.h"
#include "mozilla/unused.h"
#include "nsIWeakReferenceUtils.h"
#include "CameraRecorderProfiles.h"
#include "CameraControlImpl.h"
#include "CameraCommon.h"
#include "nsGlobalWindow.h"
#include "DeviceStorageFileDescriptor.h"
#include "CameraControlListener.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::idl;
CameraControlImpl::CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId)
nsWeakPtr CameraControlImpl::sCameraThread;
CameraControlImpl::CameraControlImpl(uint32_t aCameraId)
: mCameraId(aCameraId)
, mCameraThread(aCameraThread)
, mWindowId(aWindowId)
, mFileFormat()
, mMaxMeteringAreas(0)
, mMaxFocusAreas(0)
, mPreviewState(PREVIEW_STOPPED)
, mDOMPreview(nullptr)
, mAutoFocusOnSuccessCb(nullptr)
, mAutoFocusOnErrorCb(nullptr)
, mTakePictureOnSuccessCb(nullptr)
, mTakePictureOnErrorCb(nullptr)
, mOnShutterCb(nullptr)
, mOnClosedCb(nullptr)
, mOnRecorderStateChangeCb(nullptr)
, mOnPreviewStateChangeCb(nullptr)
, mPreviewState(CameraControlListener::kPreviewStopped)
, mHardwareState(CameraControlListener::kHardwareClosed)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
// reuse the same camera thread to conserve resources
nsCOMPtr<nsIThread> ct = do_QueryReferent(sCameraThread);
if (ct) {
mCameraThread = ct.forget();
} else {
nsresult rv = NS_NewNamedThread("CameraThread", getter_AddRefs(mCameraThread));
unused << rv; // swallow rv to suppress a compiler warning when the macro
// is #defined to nothing (i.e. in non-DEBUG builds).
MOZ_ASSERT(NS_SUCCEEDED(rv));
// keep a weak reference to the new thread
sCameraThread = do_GetWeakReference(mCameraThread);
}
mListenerLock = PR_NewRWLock(PR_RWLOCK_RANK_NONE, "CameraControlImpl.Listeners.Lock");
}
CameraControlImpl::~CameraControlImpl()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
// Helpers for string properties.
nsresult
CameraControlImpl::Set(uint32_t aKey, const nsAString& aValue)
{
SetParameter(aKey, NS_ConvertUTF16toUTF8(aValue).get());
return NS_OK;
}
nsresult
CameraControlImpl::Get(uint32_t aKey, nsAString& aValue)
{
const char* value = GetParameterConstChar(aKey);
if (!value) {
return NS_ERROR_FAILURE;
if (mListenerLock) {
PR_DestroyRWLock(mListenerLock);
mListenerLock = nullptr;
}
aValue.AssignASCII(value);
return NS_OK;
}
// Helpers for doubles.
nsresult
CameraControlImpl::Set(uint32_t aKey, double aValue)
{
SetParameter(aKey, aValue);
return NS_OK;
}
nsresult
CameraControlImpl::Get(uint32_t aKey, double* aValue)
{
MOZ_ASSERT(aValue);
*aValue = GetParameterDouble(aKey);
return NS_OK;
}
// Helper for weighted regions.
nsresult
CameraControlImpl::Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit)
{
if (aLimit == 0) {
DOM_CAMERA_LOGI("%s:%d : aLimit = 0, nothing to do\n", __func__, __LINE__);
return NS_OK;
}
if (!aValue.isObject()) {
return NS_ERROR_INVALID_ARG;
}
uint32_t length = 0;
JS::Rooted<JSObject*> regions(aCx, &aValue.toObject());
if (!JS_GetArrayLength(aCx, regions, &length)) {
return NS_ERROR_FAILURE;
}
DOM_CAMERA_LOGI("%s:%d : got %d regions (limited to %d)\n", __func__, __LINE__, length, aLimit);
if (length > aLimit) {
length = aLimit;
}
nsTArray<CameraRegion> regionArray;
regionArray.SetCapacity(length);
for (uint32_t i = 0; i < length; ++i) {
JS::Rooted<JS::Value> v(aCx);
if (!JS_GetElement(aCx, regions, i, &v)) {
return NS_ERROR_FAILURE;
}
CameraRegion* r = regionArray.AppendElement();
/**
* These are the default values. We can remove these when the xpidl
* dictionary parser gains the ability to grok default values.
*/
r->top = -1000;
r->left = -1000;
r->bottom = 1000;
r->right = 1000;
r->weight = 1000;
nsresult rv = r->Init(aCx, v.address());
NS_ENSURE_SUCCESS(rv, rv);
DOM_CAMERA_LOGI("region %d: top=%d, left=%d, bottom=%d, right=%d, weight=%d\n",
i,
r->top,
r->left,
r->bottom,
r->right,
r->weight
);
}
SetParameter(aKey, regionArray);
return NS_OK;
}
nsresult
CameraControlImpl::Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue)
{
nsTArray<CameraRegion> regionArray;
GetParameter(aKey, regionArray);
JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
if (!array) {
return NS_ERROR_OUT_OF_MEMORY;
}
uint32_t length = regionArray.Length();
DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length);
for (uint32_t i = 0; i < length; ++i) {
CameraRegion* r = &regionArray[i];
JS::Rooted<JS::Value> v(aCx);
JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
if (!o) {
return NS_ERROR_OUT_OF_MEMORY;
}
DOM_CAMERA_LOGI("top=%d\n", r->top);
v = INT_TO_JSVAL(r->top);
if (!JS_SetProperty(aCx, o, "top", v)) {
return NS_ERROR_FAILURE;
}
DOM_CAMERA_LOGI("left=%d\n", r->left);
v = INT_TO_JSVAL(r->left);
if (!JS_SetProperty(aCx, o, "left", v)) {
return NS_ERROR_FAILURE;
}
DOM_CAMERA_LOGI("bottom=%d\n", r->bottom);
v = INT_TO_JSVAL(r->bottom);
if (!JS_SetProperty(aCx, o, "bottom", v)) {
return NS_ERROR_FAILURE;
}
DOM_CAMERA_LOGI("right=%d\n", r->right);
v = INT_TO_JSVAL(r->right);
if (!JS_SetProperty(aCx, o, "right", v)) {
return NS_ERROR_FAILURE;
}
DOM_CAMERA_LOGI("weight=%d\n", r->weight);
v = INT_TO_JSVAL(r->weight);
if (!JS_SetProperty(aCx, o, "weight", v)) {
return NS_ERROR_FAILURE;
}
if (!JS_SetElement(aCx, array, i, o)) {
return NS_ERROR_FAILURE;
}
}
*aValue = JS::ObjectValue(*array);
return NS_OK;
}
nsresult
CameraControlImpl::Set(nsICameraShutterCallback* aOnShutter)
{
mOnShutterCb = new nsMainThreadPtrHolder<nsICameraShutterCallback>(aOnShutter);
return NS_OK;
}
nsresult
CameraControlImpl::Get(nsICameraShutterCallback** aOnShutter)
{
*aOnShutter = mOnShutterCb;
return NS_OK;
}
nsresult
CameraControlImpl::Set(nsICameraClosedCallback* aOnClosed)
{
mOnClosedCb = new nsMainThreadPtrHolder<nsICameraClosedCallback>(aOnClosed);
return NS_OK;
}
nsresult
CameraControlImpl::Get(nsICameraClosedCallback** aOnClosed)
{
*aOnClosed = mOnClosedCb;
return NS_OK;
}
nsresult
CameraControlImpl::Set(nsICameraRecorderStateChange* aOnRecorderStateChange)
{
mOnRecorderStateChangeCb = new nsMainThreadPtrHolder<nsICameraRecorderStateChange>(aOnRecorderStateChange);
return NS_OK;
}
nsresult
CameraControlImpl::Get(nsICameraRecorderStateChange** aOnRecorderStateChange)
{
*aOnRecorderStateChange = mOnRecorderStateChangeCb;
return NS_OK;
}
nsresult
CameraControlImpl::Set(nsICameraPreviewStateChange* aOnPreviewStateChange)
{
mOnPreviewStateChangeCb = new nsMainThreadPtrHolder<nsICameraPreviewStateChange>(aOnPreviewStateChange);
return NS_OK;
}
nsresult
CameraControlImpl::Get(nsICameraPreviewStateChange** aOnPreviewStateChange)
{
*aOnPreviewStateChange = mOnPreviewStateChangeCb;
return NS_OK;
}
nsresult
CameraControlImpl::Set(uint32_t aKey, const idl::CameraSize& aSize)
{
SetParameter(aKey, aSize);
return NS_OK;
}
nsresult
CameraControlImpl::Get(uint32_t aKey, idl::CameraSize& aSize)
{
GetParameter(aKey, aSize);
return NS_OK;
}
nsresult
CameraControlImpl::Get(uint32_t aKey, int32_t* aValue)
{
MOZ_ASSERT(aValue);
*aValue = GetParameterInt32(aKey);
return NS_OK;
}
already_AddRefed<RecorderProfileManager>
@ -286,238 +59,518 @@ void
CameraControlImpl::Shutdown()
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
mAutoFocusOnSuccessCb = nullptr;
mAutoFocusOnErrorCb = nullptr;
mTakePictureOnSuccessCb = nullptr;
mTakePictureOnErrorCb = nullptr;
mOnShutterCb = nullptr;
mOnClosedCb = nullptr;
mOnRecorderStateChangeCb = nullptr;
mOnPreviewStateChangeCb = nullptr;
}
void
CameraControlImpl::OnShutterInternal()
CameraControlImpl::OnHardwareStateChange(CameraControlListener::HardwareState aNewState)
{
DOM_CAMERA_LOGI("** SNAP **\n");
if (mOnShutterCb.get()) {
mOnShutterCb->HandleEvent();
// This callback can run on threads other than the Main Thread and
// the Camera Thread. On Gonk, it may be called from the camera's
// local binder thread, should the mediaserver process die.
RwLockAutoEnterRead lock(mListenerLock);
if (aNewState == mHardwareState) {
DOM_CAMERA_LOGI("OnHardwareStateChange: state did not change from %d\n", mHardwareState);
return;
}
#ifdef PR_LOGGING
const char* state[] = { "open", "closed", "failed" };
MOZ_ASSERT(aNewState >= 0);
if (static_cast<unsigned int>(aNewState) < sizeof(state) / sizeof(state[0])) {
DOM_CAMERA_LOGI("New hardware state is '%s'\n", state[aNewState]);
} else {
DOM_CAMERA_LOGE("OnHardwareStateChange: got invalid HardwareState value %d\n", aNewState);
}
#endif
mHardwareState = aNewState;
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
l->OnHardwareStateChange(mHardwareState);
}
}
void
CameraControlImpl::OnConfigurationChange()
{
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
RwLockAutoEnterRead lock(mListenerLock);
DOM_CAMERA_LOGI("OnConfigurationChange : %d listeners\n", mListeners.Length());
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
l->OnConfigurationChange(mCurrentConfiguration);
}
}
void
CameraControlImpl::OnAutoFocusComplete(bool aAutoFocusSucceeded)
{
// This callback can run on threads other than the Main Thread and
// the Camera Thread. On Gonk, it is called from the camera
// library's auto focus thread.
RwLockAutoEnterRead lock(mListenerLock);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
l->OnAutoFocusComplete(aAutoFocusSucceeded);
}
}
void
CameraControlImpl::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
{
// This callback can run on threads other than the Main Thread and
// the Camera Thread. On Gonk, it is called from the camera
// library's snapshot thread.
RwLockAutoEnterRead lock(mListenerLock);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
l->OnTakePictureComplete(aData, aLength, aMimeType);
}
}
void
CameraControlImpl::OnShutter()
{
nsCOMPtr<nsIRunnable> onShutter = NS_NewRunnableMethod(this, &CameraControlImpl::OnShutterInternal);
nsresult rv = NS_DispatchToMainThread(onShutter);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGW("Failed to dispatch onShutter event to main thread (%d)\n", rv);
// This callback can run on threads other than the Main Thread and
// the Camera Thread. On Gonk, it is called from the camera driver's
// preview thread.
RwLockAutoEnterRead lock(mListenerLock);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
l->OnShutter();
}
}
class OnClosedTask : public nsRunnable
{
public:
OnClosedTask(nsMainThreadPtrHandle<nsICameraClosedCallback> onClosed, uint64_t aWindowId)
: mOnClosedCb(onClosed)
, mWindowId(aWindowId)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~OnClosedTask()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
if (mOnClosedCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
mOnClosedCb->HandleEvent();
}
return NS_OK;
}
protected:
nsMainThreadPtrHandle<nsICameraClosedCallback> mOnClosedCb;
uint64_t mWindowId;
};
void
CameraControlImpl::OnClosed()
{
nsCOMPtr<nsIRunnable> onClosed = new OnClosedTask(mOnClosedCb, mWindowId);
nsresult rv = NS_DispatchToMainThread(onClosed);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGW("Failed to dispatch onClosed event to main thread (%d)\n", rv);
// This callback can run on threads other than the Main Thread and
// the Camera Thread.
RwLockAutoEnterRead lock(mListenerLock);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
l->OnHardwareStateChange(CameraControlListener::kHardwareClosed);
}
}
void
CameraControlImpl::OnRecorderStateChange(const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber)
CameraControlImpl::OnRecorderStateChange(CameraControlListener::RecorderState aState,
int32_t aStatus, int32_t aTrackNumber)
{
DOM_CAMERA_LOGI("OnRecorderStateChange: '%s'\n", NS_ConvertUTF16toUTF8(aStateMsg).get());
// This callback can run on threads other than the Main Thread and
// the Camera Thread. On Gonk, it is called from the media encoder
// thread.
RwLockAutoEnterRead lock(mListenerLock);
nsCOMPtr<nsIRunnable> onRecorderStateChange = new CameraRecorderStateChange(mOnRecorderStateChangeCb, aStateMsg, aStatus, aTrackNumber, mWindowId);
nsresult rv = NS_DispatchToMainThread(onRecorderStateChange);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to dispatch onRecorderStateChange event to main thread (%d)\n", rv);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
l->OnRecorderStateChange(aState, aStatus, aTrackNumber);
}
}
void
CameraControlImpl::OnPreviewStateChange(PreviewState aNewState)
CameraControlImpl::OnPreviewStateChange(CameraControlListener::PreviewState aNewState)
{
// This callback runs on the Main Thread and the Camera Thread, and
// may run on the local binder thread, should the mediaserver
// process die.
RwLockAutoEnterRead lock(mListenerLock);
if (aNewState == mPreviewState) {
DOM_CAMERA_LOGI("OnPreviewStateChange: state did not change from %d\n", mPreviewState);
return;
}
nsString msg;
switch (aNewState) {
case PREVIEW_STOPPED:
msg = NS_LITERAL_STRING("stopped");
break;
case PREVIEW_STARTED:
msg = NS_LITERAL_STRING("started");
break;
default:
MOZ_ASSUME_UNREACHABLE("Preview state can only be PREVIEW_STOPPED or _STARTED!");
#ifdef PR_LOGGING
const char* state[] = { "stopped", "paused", "started" };
MOZ_ASSERT(aNewState >= 0);
if (static_cast<unsigned int>(aNewState) < sizeof(state) / sizeof(state[0])) {
DOM_CAMERA_LOGI("New preview state is '%s'\n", state[aNewState]);
} else {
DOM_CAMERA_LOGE("OnPreviewStateChange: got unknown PreviewState value %d\n", aNewState);
}
#endif
// const nsString& aStateMsg)
DOM_CAMERA_LOGI("OnPreviewStateChange: '%s'\n", NS_ConvertUTF16toUTF8(msg).get());
mPreviewState = aNewState;
nsCOMPtr<nsIRunnable> onPreviewStateChange = new CameraPreviewStateChange(mOnPreviewStateChangeCb, msg, mWindowId);
nsresult rv = NS_DispatchToMainThread(onPreviewStateChange);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to dispatch onPreviewStateChange event to main thread (%d)\n", rv);
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
l->OnPreviewStateChange(mPreviewState);
}
}
nsresult
CameraControlImpl::GetPreviewStream(CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
bool
CameraControlImpl::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight)
{
nsCOMPtr<nsIRunnable> getPreviewStreamTask = new GetPreviewStreamTask(this, aSize, onSuccess, onError);
return mCameraThread->Dispatch(getPreviewStreamTask, NS_DISPATCH_NORMAL);
// This function runs on neither the Main Thread nor the Camera Thread.
// On Gonk, it is called from the camera driver's preview thread.
RwLockAutoEnterRead lock(mListenerLock);
DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %d preview frame listener(s)\n",
mListeners.Length());
bool consumed = false;
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
consumed = l->OnNewPreviewFrame(aImage, aWidth, aHeight) || consumed;
}
return consumed;
}
nsresult
CameraControlImpl::AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError)
void
CameraControlImpl::OnError(CameraControlListener::CameraErrorContext aContext,
CameraControlListener::CameraError aError)
{
MOZ_ASSERT(NS_IsMainThread());
bool cancel = false;
// This callback can run on threads other than the Main Thread and
// the Camera Thread.
RwLockAutoEnterRead lock(mListenerLock);
nsCOMPtr<nsICameraAutoFocusCallback> cb = mAutoFocusOnSuccessCb.get();
if (cb) {
/**
* We already have a callback, so someone has already
* called autoFocus() -- cancel it.
*/
mAutoFocusOnSuccessCb = nullptr;
mAutoFocusOnErrorCb = nullptr;
cancel = true;
#ifdef PR_LOGGING
const char* error[] = { "camera-service-failed", "unknown" };
if (static_cast<unsigned int>(aError) < sizeof(error) / sizeof(error[0])) {
DOM_CAMERA_LOGW("CameraControlImpl::OnError : aContext=%u, msg='%s'\n",
aContext, error[aError]);
} else {
DOM_CAMERA_LOGE("CameraControlImpl::OnError : aContext=%u, unknown error=%d\n",
aContext, aError);
}
#endif
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
l->OnError(aContext, aError);
}
}
// Camera control asynchronous message; these are dispatched from
// the Main Thread to the Camera Thread, where they are consumed.
class CameraControlImpl::ControlMessage : public nsRunnable
{
public:
ControlMessage(CameraControlImpl* aCameraControl,
CameraControlListener::CameraErrorContext aContext)
: mCameraControl(aCameraControl)
, mContext(aContext)
{
MOZ_COUNT_CTOR(CameraControlImpl::ControlMessage);
}
nsCOMPtr<nsIRunnable> autoFocusTask = new AutoFocusTask(this, cancel, onSuccess, onError);
return mCameraThread->Dispatch(autoFocusTask, NS_DISPATCH_NORMAL);
}
nsresult
CameraControlImpl::TakePicture(const CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
{
MOZ_ASSERT(NS_IsMainThread());
bool cancel = false;
nsCOMPtr<nsICameraTakePictureCallback> cb = mTakePictureOnSuccessCb.get();
if (cb) {
/**
* We already have a callback, so someone has already
* called takePicture() -- cancel it.
*/
mTakePictureOnSuccessCb = nullptr;
mTakePictureOnErrorCb = nullptr;
cancel = true;
virtual ~ControlMessage()
{
MOZ_COUNT_DTOR(CameraControlImpl::ControlMessage);
}
nsCOMPtr<nsIRunnable> takePictureTask = new TakePictureTask(this, cancel, aSize, aRotation, aFileFormat, aPosition, aDateTime, onSuccess, onError);
return mCameraThread->Dispatch(takePictureTask, NS_DISPATCH_NORMAL);
virtual nsresult RunImpl() = 0;
NS_IMETHOD
Run() MOZ_OVERRIDE
{
MOZ_ASSERT(mCameraControl);
MOZ_ASSERT(NS_GetCurrentThread() == mCameraControl->mCameraThread);
nsresult rv = RunImpl();
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGW("Camera control API failed at %d with 0x%x\n", mContext, rv);
// XXXmikeh - do we want to report a more specific error code?
mCameraControl->OnError(mContext, CameraControlListener::kErrorApiFailed);
}
return NS_OK;
}
protected:
nsRefPtr<CameraControlImpl> mCameraControl;
CameraControlListener::CameraErrorContext mContext;
};
nsresult
CameraControlImpl::SetConfiguration(const Configuration& aConfig)
{
class Message : public ControlMessage
{
public:
Message(CameraControlImpl* aCameraControl,
CameraControlListener::CameraErrorContext aContext,
const Configuration& aConfig)
: ControlMessage(aCameraControl, aContext)
, mConfig(aConfig)
{ }
nsresult
RunImpl() MOZ_OVERRIDE
{
return mCameraControl->SetConfigurationImpl(mConfig);
}
protected:
Configuration mConfig;
};
return mCameraThread->Dispatch(
new Message(this, CameraControlListener::kInSetConfiguration, aConfig), NS_DISPATCH_NORMAL);
}
nsresult
CameraControlImpl::StartRecording(CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor* aFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError)
CameraControlImpl::AutoFocus(bool aCancelExistingCall)
{
nsCOMPtr<nsIRunnable> startRecordingTask = new StartRecordingTask(this, *aOptions, aFileDescriptor, onSuccess, onError, mWindowId);
return mCameraThread->Dispatch(startRecordingTask, NS_DISPATCH_NORMAL);
class Message : public ControlMessage
{
public:
Message(CameraControlImpl* aCameraControl,
CameraControlListener::CameraErrorContext aContext,
bool aCancelExistingCall)
: ControlMessage(aCameraControl, aContext)
, mCancelExistingCall(aCancelExistingCall)
{ }
nsresult
RunImpl() MOZ_OVERRIDE
{
return mCameraControl->AutoFocusImpl(mCancelExistingCall);
}
protected:
bool mCancelExistingCall;
};
return mCameraThread->Dispatch(
new Message(this, CameraControlListener::kInAutoFocus, aCancelExistingCall), NS_DISPATCH_NORMAL);
}
nsresult
CameraControlImpl::TakePicture()
{
class Message : public ControlMessage
{
public:
Message(CameraControlImpl* aCameraControl,
CameraControlListener::CameraErrorContext aContext)
: ControlMessage(aCameraControl, aContext)
{ }
nsresult
RunImpl() MOZ_OVERRIDE
{
return mCameraControl->TakePictureImpl();
}
};
return mCameraThread->Dispatch(
new Message(this, CameraControlListener::kInTakePicture), NS_DISPATCH_NORMAL);
}
nsresult
CameraControlImpl::StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
const StartRecordingOptions* aOptions)
{
class Message : public ControlMessage
{
public:
Message(CameraControlImpl* aCameraControl,
CameraControlListener::CameraErrorContext aContext,
const StartRecordingOptions* aOptions,
DeviceStorageFileDescriptor* aFileDescriptor)
: ControlMessage(aCameraControl, aContext)
, mOptionsPassed(false)
, mFileDescriptor(aFileDescriptor)
{
if (aOptions) {
mOptions = *aOptions;
mOptionsPassed = true;
}
}
nsresult
RunImpl() MOZ_OVERRIDE
{
return mCameraControl->StartRecordingImpl(mFileDescriptor,
mOptionsPassed ? &mOptions : nullptr);
}
protected:
StartRecordingOptions mOptions;
bool mOptionsPassed;
nsRefPtr<DeviceStorageFileDescriptor> mFileDescriptor;
};
return mCameraThread->Dispatch(new Message(this, CameraControlListener::kInStartRecording,
aOptions, aFileDescriptor), NS_DISPATCH_NORMAL);
}
nsresult
CameraControlImpl::StopRecording()
{
nsCOMPtr<nsIRunnable> stopRecordingTask = new StopRecordingTask(this);
return mCameraThread->Dispatch(stopRecordingTask, NS_DISPATCH_NORMAL);
class Message : public ControlMessage
{
public:
Message(CameraControlImpl* aCameraControl,
CameraControlListener::CameraErrorContext aContext)
: ControlMessage(aCameraControl, aContext)
{ }
nsresult
RunImpl() MOZ_OVERRIDE
{
return mCameraControl->StopRecordingImpl();
}
};
return mCameraThread->Dispatch(
new Message(this, CameraControlListener::kInStopRecording), NS_DISPATCH_NORMAL);
}
nsresult
CameraControlImpl::StartPreview(DOMCameraPreview* aDOMPreview)
CameraControlImpl::StartPreview()
{
nsCOMPtr<nsIRunnable> startPreviewTask = new StartPreviewTask(this, aDOMPreview);
return mCameraThread->Dispatch(startPreviewTask, NS_DISPATCH_NORMAL);
class Message : public ControlMessage
{
public:
Message(CameraControlImpl* aCameraControl,
CameraControlListener::CameraErrorContext aContext)
: ControlMessage(aCameraControl, aContext)
{ }
nsresult
RunImpl() MOZ_OVERRIDE
{
return mCameraControl->StartPreviewImpl();
}
};
return mCameraThread->Dispatch(
new Message(this, CameraControlListener::kInStartPreview), NS_DISPATCH_NORMAL);
}
nsresult
CameraControlImpl::StopPreview()
{
class Message : public ControlMessage
{
public:
Message(CameraControlImpl* aCameraControl,
CameraControlListener::CameraErrorContext aContext)
: ControlMessage(aCameraControl, aContext)
{ }
nsresult
RunImpl() MOZ_OVERRIDE
{
return mCameraControl->StopPreviewImpl();
}
};
return mCameraThread->Dispatch(
new Message(this, CameraControlListener::kInStopPreview), NS_DISPATCH_NORMAL);
}
nsresult
CameraControlImpl::ReleaseHardware()
{
class Message : public ControlMessage
{
public:
Message(CameraControlImpl* aCameraControl,
CameraControlListener::CameraErrorContext aContext)
: ControlMessage(aCameraControl, aContext)
{ }
nsresult
RunImpl() MOZ_OVERRIDE
{
return mCameraControl->ReleaseHardwareImpl();
}
};
return mCameraThread->Dispatch(
new Message(this, CameraControlListener::kInReleaseHardware), NS_DISPATCH_NORMAL);
}
class CameraControlImpl::ListenerMessage : public CameraControlImpl::ControlMessage
{
public:
ListenerMessage(CameraControlImpl* aCameraControl,
CameraControlListener* aListener)
: ControlMessage(aCameraControl, CameraControlListener::kInUnspecified)
, mListener(aListener)
{ }
protected:
nsRefPtr<CameraControlListener> mListener;
};
void
CameraControlImpl::AddListenerImpl(already_AddRefed<CameraControlListener> aListener)
{
RwLockAutoEnterWrite lock(mListenerLock);
CameraControlListener* l = *mListeners.AppendElement() = aListener;
// Update the newly-added listener's state
l->OnConfigurationChange(mCurrentConfiguration);
l->OnHardwareStateChange(mHardwareState);
l->OnPreviewStateChange(mPreviewState);
}
void
CameraControlImpl::StopPreview()
{
nsCOMPtr<nsIRunnable> stopPreviewTask = new StopPreviewTask(this);
mCameraThread->Dispatch(stopPreviewTask, NS_DISPATCH_NORMAL);
CameraControlImpl::AddListener(CameraControlListener* aListener)
{
class Message : public ListenerMessage
{
public:
Message(CameraControlImpl* aCameraControl,
CameraControlListener* aListener)
: ListenerMessage(aCameraControl, aListener)
{ }
nsresult
RunImpl() MOZ_OVERRIDE
{
mCameraControl->AddListenerImpl(mListener.forget());
return NS_OK;
}
};
mCameraThread->Dispatch(new Message(this, aListener), NS_DISPATCH_NORMAL);
}
nsresult
CameraControlImpl::GetPreviewStreamVideoMode(CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
void
CameraControlImpl::RemoveListenerImpl(CameraControlListener* aListener)
{
nsCOMPtr<nsIRunnable> getPreviewStreamVideoModeTask = new GetPreviewStreamVideoModeTask(this, *aOptions, onSuccess, onError);
return mCameraThread->Dispatch(getPreviewStreamVideoModeTask, NS_DISPATCH_NORMAL);
RwLockAutoEnterWrite lock(mListenerLock);
nsRefPtr<CameraControlListener> l(aListener);
mListeners.RemoveElement(l);
// XXXmikeh - do we want to notify the listener that it has been removed?
}
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)
{
if (!mDOMPreview) {
return false;
}
return mDOMPreview->ReceiveFrame(aBuffer, aFormat, aBuilder);
}
NS_IMETHODIMP
GetPreviewStreamResult::Run()
{
/**
* The camera preview stream object is DOM-facing, and as such
* must be a cycle-collection participant created on the main
* thread.
*/
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsICameraPreviewStreamCallback> onSuccess = mOnSuccessCb.get();
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
if (onSuccess && nsDOMCameraManager::IsWindowStillActive(mWindowId) && window) {
nsCOMPtr<nsIDOMMediaStream> stream =
new DOMCameraPreview(window, mCameraControl, mWidth, mHeight,
mFramesPerSecond);
onSuccess->HandleEvent(stream);
}
return NS_OK;
void
CameraControlImpl::RemoveListener(CameraControlListener* aListener)
{
class Message : public ListenerMessage
{
public:
Message(CameraControlImpl* aCameraControl, CameraControlListener* aListener)
: ListenerMessage(aCameraControl, aListener)
{ }
nsresult
RunImpl() MOZ_OVERRIDE
{
mCameraControl->RemoveListenerImpl(mListener);
return NS_OK;
}
};
mCameraThread->Dispatch(new Message(this, aListener), NS_DISPATCH_NORMAL);
}

View File

@ -5,746 +5,119 @@
#ifndef DOM_CAMERA_CAMERACONTROLIMPL_H
#define DOM_CAMERA_CAMERACONTROLIMPL_H
#include "nsTArray.h"
#include "nsWeakPtr.h"
#include "mozilla/Attributes.h"
#include "nsDOMFile.h"
#include "mozilla/ReentrantMonitor.h"
#include "nsIFile.h"
#include "nsProxyRelease.h"
#include "DictionaryHelpers.h"
#include "AutoRwLock.h"
#include "nsIDOMDeviceStorage.h"
#include "DOMCameraManager.h"
#include "DOMCameraPreview.h"
#include "ICameraControl.h"
#include "CameraCommon.h"
#include "DeviceStorage.h"
#include "DeviceStorageFileDescriptor.h"
#include "CameraControlListener.h"
class DeviceStorageFileDescriptor;
namespace mozilla {
class GetPreviewStreamTask;
class StartPreviewTask;
class StopPreviewTask;
class AutoFocusTask;
class TakePictureTask;
class StartRecordingTask;
class StopRecordingTask;
class SetParameterTask;
class GetParameterTask;
class GetPreviewStreamVideoModeTask;
class ReleaseHardwareTask;
namespace layers {
class Image;
}
class DOMCameraPreview;
class RecorderProfileManager;
class CameraControlImpl : public ICameraControl
{
friend class GetPreviewStreamTask;
friend class StartPreviewTask;
friend class StopPreviewTask;
friend class AutoFocusTask;
friend class TakePictureTask;
friend class StartRecordingTask;
friend class StopRecordingTask;
friend class SetParameterTask;
friend class GetParameterTask;
friend class GetPreviewStreamVideoModeTask;
friend class ReleaseHardwareTask;
public:
CameraControlImpl(uint32_t aCameraId, nsIThread* aCameraThread, uint64_t aWindowId);
CameraControlImpl(uint32_t aCameraId);
virtual void AddListener(CameraControlListener* aListener) MOZ_OVERRIDE;
virtual void RemoveListener(CameraControlListener* aListener) MOZ_OVERRIDE;
nsresult GetPreviewStream(idl::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult StartPreview(DOMCameraPreview* aDOMPreview);
void StopPreview();
nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult TakePicture(const idl::CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, idl::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor *aDSFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError);
nsresult StopRecording();
nsresult GetPreviewStreamVideoMode(idl::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);
nsresult Set(uint32_t aKey, double aValue);
nsresult Get(uint32_t aKey, double* aValue);
nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit);
nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue);
nsresult Set(nsICameraShutterCallback* aOnShutter);
nsresult Get(nsICameraShutterCallback** aOnShutter);
nsresult Set(nsICameraClosedCallback* aOnClosed);
nsresult Get(nsICameraClosedCallback** aOnClosed);
nsresult Set(nsICameraRecorderStateChange* aOnRecorderStateChange);
nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange);
nsresult Set(nsICameraPreviewStateChange* aOnPreviewStateChange);
nsresult Get(nsICameraPreviewStateChange** aOnPreviewStateChange);
nsresult Set(uint32_t aKey, const idl::CameraSize& aSize);
nsresult Get(uint32_t aKey, idl::CameraSize& aSize);
nsresult Get(uint32_t aKey, int32_t* aValue);
nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue)
{
return Set(aCx, CAMERA_PARAM_FOCUSAREAS, aValue, mMaxFocusAreas);
}
nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue)
{
return Set(aCx, CAMERA_PARAM_METERINGAREAS, aValue, mMaxMeteringAreas);
}
virtual nsresult SetConfiguration(const Configuration& aConfig) MOZ_OVERRIDE;
virtual nsresult StartPreview() MOZ_OVERRIDE;
virtual nsresult StopPreview() MOZ_OVERRIDE;
virtual nsresult AutoFocus(bool aCancelExistingCall) MOZ_OVERRIDE;
virtual nsresult TakePicture() MOZ_OVERRIDE;
virtual nsresult StartRecording(DeviceStorageFileDescriptor* aFileDescriptor,
const StartRecordingOptions* aOptions) MOZ_OVERRIDE;
virtual nsresult StopRecording() MOZ_OVERRIDE;
virtual nsresult ReleaseHardware() MOZ_OVERRIDE;
already_AddRefed<RecorderProfileManager> GetRecorderProfileManager();
uint32_t GetCameraId() { return mCameraId; }
virtual const char* GetParameter(const char* aKey) = 0;
virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
virtual double GetParameterDouble(uint32_t aKey) = 0;
virtual int32_t GetParameterInt32(uint32_t aKey) = 0;
virtual void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions) = 0;
virtual void GetParameter(uint32_t aKey, idl::CameraSize& aSize) = 0;
virtual void SetParameter(const char* aKey, const char* aValue) = 0;
virtual void SetParameter(uint32_t aKey, const char* aValue) = 0;
virtual void SetParameter(uint32_t aKey, double aValue) = 0;
virtual void SetParameter(uint32_t aKey, const nsTArray<idl::CameraRegion>& aRegions) = 0;
virtual void SetParameter(uint32_t aKey, const idl::CameraSize& aSize) = 0;
virtual nsresult GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes) = 0;
virtual nsresult PushParameters() = 0;
virtual void Shutdown();
virtual void Shutdown() MOZ_OVERRIDE;
bool ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder);
void OnShutter();
void OnClosed();
void OnRecorderStateChange(const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber);
enum PreviewState {
PREVIEW_STOPPED,
PREVIEW_STARTED
};
void OnPreviewStateChange(PreviewState aNewState);
uint64_t GetWindowId()
{
return mWindowId;
}
void OnError(CameraControlListener::CameraErrorContext aContext,
CameraControlListener::CameraError aError);
protected:
// Event handlers.
void OnAutoFocusComplete(bool aAutoFocusSucceeded);
void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType);
bool OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight);
void OnRecorderStateChange(CameraControlListener::RecorderState aState,
int32_t aStatus, int32_t aTrackNumber);
void OnPreviewStateChange(CameraControlListener::PreviewState aState);
void OnHardwareStateChange(CameraControlListener::HardwareState aState);
void OnConfigurationChange();
// When we create a new CameraThread, we keep a static reference to it so
// that multiple CameraControl instances can find and reuse it; but we
// don't want that reference to keep the thread object around unnecessarily,
// so we make it a weak reference. The strong dynamic references will keep
// the thread object alive as needed.
static nsWeakPtr sCameraThread;
nsCOMPtr<nsIThread> mCameraThread;
virtual ~CameraControlImpl();
virtual nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream) = 0;
virtual nsresult StartPreviewImpl(StartPreviewTask* aStartPreview) = 0;
virtual nsresult StopPreviewImpl(StopPreviewTask* aStopPreview) = 0;
virtual nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus) = 0;
virtual nsresult TakePictureImpl(TakePictureTask* aTakePicture) = 0;
virtual nsresult StartRecordingImpl(StartRecordingTask* aStartRecording) = 0;
virtual nsresult StopRecordingImpl(StopRecordingTask* aStopRecording) = 0;
virtual void BeginBatchParameterSet() MOZ_OVERRIDE { }
virtual void EndBatchParameterSet() MOZ_OVERRIDE { }
// Manage camera event listeners.
void AddListenerImpl(already_AddRefed<CameraControlListener> aListener);
void RemoveListenerImpl(CameraControlListener* aListener);
nsTArray<nsRefPtr<CameraControlListener> > mListeners;
PRRWLock* mListenerLock;
class ControlMessage;
class ListenerMessage;
virtual nsresult SetConfigurationImpl(const Configuration& aConfig) = 0;
virtual nsresult StartPreviewImpl() = 0;
virtual nsresult StopPreviewImpl() = 0;
virtual nsresult AutoFocusImpl(bool aCancelExistingCall) = 0;
virtual nsresult TakePictureImpl() = 0;
virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
const StartRecordingOptions* aOptions) = 0;
virtual nsresult StopRecordingImpl() = 0;
virtual nsresult PushParametersImpl() = 0;
virtual nsresult PullParametersImpl() = 0;
virtual nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode) = 0;
virtual nsresult ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware) = 0;
virtual nsresult ReleaseHardwareImpl() = 0;
virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() = 0;
void OnShutterInternal();
void OnClosedInternal();
uint32_t mCameraId;
nsCOMPtr<nsIThread> mCameraThread;
uint64_t mWindowId;
nsString mFileFormat;
uint32_t mMaxMeteringAreas;
uint32_t mMaxFocusAreas;
PreviewState mPreviewState;
uint32_t mCameraId;
/**
* 'mDOMPreview' is a raw pointer to the object that will receive incoming
* preview frames. This is guaranteed to be valid, or null.
*
* It is set by a call to StartPreview(), and set to null on StopPreview().
* It is up to the caller to ensure that the object will not disappear
* out from under this pointer--usually by calling NS_ADDREF().
*/
DOMCameraPreview* mDOMPreview;
CameraControlListener::CameraListenerConfiguration mCurrentConfiguration;
nsMainThreadPtrHandle<nsICameraAutoFocusCallback> mAutoFocusOnSuccessCb;
nsMainThreadPtrHandle<nsICameraErrorCallback> mAutoFocusOnErrorCb;
nsMainThreadPtrHandle<nsICameraTakePictureCallback> mTakePictureOnSuccessCb;
nsMainThreadPtrHandle<nsICameraErrorCallback> mTakePictureOnErrorCb;
nsMainThreadPtrHandle<nsICameraShutterCallback> mOnShutterCb;
nsMainThreadPtrHandle<nsICameraClosedCallback> mOnClosedCb;
nsMainThreadPtrHandle<nsICameraRecorderStateChange> mOnRecorderStateChangeCb;
nsMainThreadPtrHandle<nsICameraPreviewStateChange> mOnPreviewStateChangeCb;
CameraControlListener::PreviewState mPreviewState;
CameraControlListener::HardwareState mHardwareState;
private:
CameraControlImpl(const CameraControlImpl&) MOZ_DELETE;
CameraControlImpl& operator=(const CameraControlImpl&) MOZ_DELETE;
};
// Error result runnable
class CameraErrorResult : public nsRunnable
{
public:
CameraErrorResult(nsMainThreadPtrHandle<nsICameraErrorCallback> onError, const nsString& aErrorMsg, uint64_t aWindowId)
: mOnErrorCb(onError)
, mErrorMsg(aErrorMsg)
, mWindowId(aWindowId)
{ }
NS_IMETHOD Run() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
if (mOnErrorCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
mOnErrorCb->HandleEvent(mErrorMsg);
}
return NS_OK;
}
protected:
nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
const nsString mErrorMsg;
uint64_t mWindowId;
};
// Return the resulting preview stream to JS. Runs on the main thread.
class GetPreviewStreamResult : public nsRunnable
{
public:
GetPreviewStreamResult(CameraControlImpl* aCameraControl, uint32_t aWidth, uint32_t aHeight, uint32_t aFramesPerSecond, nsMainThreadPtrHandle<nsICameraPreviewStreamCallback>& onSuccess, uint64_t aWindowId)
: mCameraControl(aCameraControl)
, mWidth(aWidth)
, mHeight(aHeight)
, mFramesPerSecond(aFramesPerSecond)
, mOnSuccessCb(onSuccess)
, mWindowId(aWindowId)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~GetPreviewStreamResult()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
// Run() method is implementation specific.
NS_IMETHOD Run() MOZ_OVERRIDE;
protected:
nsRefPtr<CameraControlImpl> mCameraControl;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mFramesPerSecond;
nsMainThreadPtrHandle<nsICameraPreviewStreamCallback> mOnSuccessCb;
uint64_t mWindowId;
};
// Get the desired preview stream.
class GetPreviewStreamTask : public nsRunnable
{
public:
GetPreviewStreamTask(CameraControlImpl* aCameraControl, idl::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
: mSize(aSize)
, mCameraControl(aCameraControl)
, mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraPreviewStreamCallback>(onSuccess))
, mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~GetPreviewStreamTask()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
nsresult rv = mCameraControl->GetPreviewStreamImpl(this);
if (NS_FAILED(rv)) {
nsCOMPtr<nsIRunnable> cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId());
rv = NS_DispatchToMainThread(cameraErrorResult);
NS_ENSURE_SUCCESS(rv, rv);
}
return rv;
}
idl::CameraSize mSize;
nsRefPtr<CameraControlImpl> mCameraControl;
nsMainThreadPtrHandle<nsICameraPreviewStreamCallback> mOnSuccessCb;
nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
};
// Return the autofocus status to JS. Runs on the main thread.
class AutoFocusResult : public nsRunnable
{
public:
AutoFocusResult(bool aSuccess, nsMainThreadPtrHandle<nsICameraAutoFocusCallback> onSuccess, uint64_t aWindowId)
: mSuccess(aSuccess)
, mOnSuccessCb(onSuccess)
, mWindowId(aWindowId)
{ }
virtual ~AutoFocusResult() { }
NS_IMETHOD Run() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
if (mOnSuccessCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
mOnSuccessCb->HandleEvent(mSuccess);
}
return NS_OK;
}
protected:
bool mSuccess;
nsMainThreadPtrHandle<nsICameraAutoFocusCallback> mOnSuccessCb;
uint64_t mWindowId;
};
// Autofocus the camera.
class AutoFocusTask : public nsRunnable
{
public:
AutoFocusTask(CameraControlImpl* aCameraControl, bool aCancel, nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError)
: mCameraControl(aCameraControl)
, mCancel(aCancel)
, mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraAutoFocusCallback>(onSuccess))
, mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~AutoFocusTask()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
nsresult rv = mCameraControl->AutoFocusImpl(this);
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
if (NS_FAILED(rv)) {
nsCOMPtr<nsIRunnable> cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId());
rv = NS_DispatchToMainThread(cameraErrorResult);
NS_ENSURE_SUCCESS(rv, rv);
}
return rv;
}
nsRefPtr<CameraControlImpl> mCameraControl;
bool mCancel;
nsMainThreadPtrHandle<nsICameraAutoFocusCallback> mOnSuccessCb;
nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
};
// Return the captured picture to JS. Runs on the main thread.
class TakePictureResult : public nsRunnable
{
public:
TakePictureResult(uint8_t* aData, uint64_t aLength, const nsAString& aMimeType, nsMainThreadPtrHandle<nsICameraTakePictureCallback> onSuccess, uint64_t aWindowId)
: mData(aData)
, mLength(aLength)
, mMimeType(aMimeType)
, mOnSuccessCb(onSuccess)
, mWindowId(aWindowId)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~TakePictureResult()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
if (mOnSuccessCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
nsCOMPtr<nsIDOMBlob> image = new nsDOMMemoryFile(static_cast<void*>(mData), static_cast<uint64_t>(mLength), mMimeType);
mOnSuccessCb->HandleEvent(image);
} else {
delete[] mData;
}
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
return NS_OK;
}
protected:
uint8_t* mData;
uint64_t mLength;
nsString mMimeType;
nsMainThreadPtrHandle<nsICameraTakePictureCallback> mOnSuccessCb;
uint64_t mWindowId;
};
// Capture a still image with the camera.
class TakePictureTask : public nsRunnable
{
public:
TakePictureTask(CameraControlImpl* aCameraControl, bool aCancel, const idl::CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, idl::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError)
: mCameraControl(aCameraControl)
, mCancel(aCancel)
, mSize(aSize)
, mRotation(aRotation)
, mFileFormat(aFileFormat)
, mPosition(aPosition)
, mDateTime(aDateTime)
, mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraTakePictureCallback>(onSuccess))
, mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~TakePictureTask()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
nsresult rv = mCameraControl->TakePictureImpl(this);
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
if (NS_FAILED(rv)) {
nsCOMPtr<nsIRunnable> cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId());
rv = NS_DispatchToMainThread(cameraErrorResult);
NS_ENSURE_SUCCESS(rv, rv);
}
return rv;
}
nsRefPtr<CameraControlImpl> mCameraControl;
bool mCancel;
idl::CameraSize mSize;
int32_t mRotation;
nsString mFileFormat;
idl::CameraPosition mPosition;
uint64_t mDateTime;
nsMainThreadPtrHandle<nsICameraTakePictureCallback> mOnSuccessCb;
nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
};
// Return the result of starting recording. Runs on the main thread.
class StartRecordingResult : public nsRunnable
{
public:
StartRecordingResult(nsMainThreadPtrHandle<nsICameraStartRecordingCallback> onSuccess, uint64_t aWindowId)
: mOnSuccessCb(onSuccess)
, mWindowId(aWindowId)
{ }
virtual ~StartRecordingResult() { }
NS_IMETHOD Run() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
if (mOnSuccessCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
mOnSuccessCb->HandleEvent();
}
return NS_OK;
}
protected:
nsMainThreadPtrHandle<nsICameraStartRecordingCallback> mOnSuccessCb;
uint64_t mWindowId;
};
// Start video recording.
class StartRecordingTask : public nsRunnable
{
public:
StartRecordingTask(CameraControlImpl* aCameraControl,
idl::CameraStartRecordingOptions aOptions,
DeviceStorageFileDescriptor *aDSFileDescriptor,
nsICameraStartRecordingCallback* onSuccess,
nsICameraErrorCallback* onError,
uint64_t aWindowId)
: mCameraControl(aCameraControl)
, mOptions(aOptions)
, mDSFileDescriptor(aDSFileDescriptor)
, mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraStartRecordingCallback>(onSuccess))
, mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
, mWindowId(aWindowId)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~StartRecordingTask()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
nsresult rv = mCameraControl->StartRecordingImpl(this);
DOM_CAMERA_LOGT("%s:%d : result %d\n", __func__, __LINE__, rv);
// dispatch the callback
nsCOMPtr<nsIRunnable> startRecordingResult;
if (NS_SUCCEEDED(rv)) {
startRecordingResult = new StartRecordingResult(mOnSuccessCb, mWindowId);
} else {
startRecordingResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mWindowId);
}
rv = NS_DispatchToMainThread(startRecordingResult);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to dispatch start recording result to main thread (%d)!", rv);
}
return rv;
}
nsRefPtr<CameraControlImpl> mCameraControl;
idl::CameraStartRecordingOptions mOptions;
nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
nsMainThreadPtrHandle<nsICameraStartRecordingCallback> mOnSuccessCb;
nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
uint64_t mWindowId;
};
// Stop video recording.
class StopRecordingTask : public nsRunnable
{
public:
StopRecordingTask(CameraControlImpl* aCameraControl)
: mCameraControl(aCameraControl)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~StopRecordingTask()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
nsresult rv = mCameraControl->StopRecordingImpl(this);
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsRefPtr<CameraControlImpl> mCameraControl;
};
// Start the preview.
class StartPreviewTask : public nsRunnable
{
public:
StartPreviewTask(CameraControlImpl* aCameraControl, DOMCameraPreview* aDOMPreview)
: mCameraControl(aCameraControl)
, mDOMPreview(aDOMPreview)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~StartPreviewTask()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
nsresult rv = mCameraControl->StartPreviewImpl(this);
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsRefPtr<CameraControlImpl> mCameraControl;
DOMCameraPreview* mDOMPreview; // DOMCameraPreview NS_ADDREFs itself for us
};
// Stop the preview.
class StopPreviewTask : public nsRunnable
{
public:
StopPreviewTask(CameraControlImpl* aCameraControl)
: mCameraControl(aCameraControl)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~StopPreviewTask()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
NS_IMETHOD Run() MOZ_OVERRIDE
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
mCameraControl->StopPreviewImpl(this);
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
return NS_OK;
}
nsRefPtr<CameraControlImpl> mCameraControl;
};
// Get the video mode preview stream.
class GetPreviewStreamVideoModeTask : public nsRunnable
{
public:
GetPreviewStreamVideoModeTask(CameraControlImpl* aCameraControl, idl::CameraRecorderOptions aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError)
: mCameraControl(aCameraControl)
, mOptions(aOptions)
, mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraPreviewStreamCallback>(onSuccess))
, mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(onError))
{ }
NS_IMETHOD Run() MOZ_OVERRIDE
{
DOM_CAMERA_LOGI("%s:%d -- BEFORE IMPL\n", __func__, __LINE__);
nsresult rv = mCameraControl->GetPreviewStreamVideoModeImpl(this);
DOM_CAMERA_LOGI("%s:%d -- AFTER IMPL : rv = %d\n", __func__, __LINE__, rv);
if (NS_FAILED(rv)) {
nsCOMPtr<nsIRunnable> cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId());
rv = NS_DispatchToMainThread(cameraErrorResult);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
nsRefPtr<CameraControlImpl> mCameraControl;
idl::CameraRecorderOptions mOptions;
nsMainThreadPtrHandle<nsICameraPreviewStreamCallback> mOnSuccessCb;
nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
};
// Return the result of releasing the camera hardware. Runs on the main thread.
class ReleaseHardwareResult : public nsRunnable
{
public:
ReleaseHardwareResult(nsMainThreadPtrHandle<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_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
if (mOnSuccessCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
mOnSuccessCb->HandleEvent();
}
return NS_OK;
}
protected:
nsMainThreadPtrHandle<nsICameraReleaseCallback> mOnSuccessCb;
uint64_t mWindowId;
};
// Release the camera hardware.
class ReleaseHardwareTask : public nsRunnable
{
public:
ReleaseHardwareTask(CameraControlImpl* aCameraControl, nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError)
: mCameraControl(aCameraControl)
, mOnSuccessCb(new nsMainThreadPtrHolder<nsICameraReleaseCallback>(onSuccess))
, mOnErrorCb(new nsMainThreadPtrHolder<nsICameraErrorCallback>(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() MOZ_OVERRIDE
{
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)) {
nsCOMPtr<nsIRunnable> cameraErrorResult = new CameraErrorResult(mOnErrorCb, NS_LITERAL_STRING("FAILURE"), mCameraControl->GetWindowId());
rv = NS_DispatchToMainThread(cameraErrorResult);
NS_ENSURE_SUCCESS(rv, rv);
}
return rv;
}
nsRefPtr<CameraControlImpl> mCameraControl;
nsMainThreadPtrHandle<nsICameraReleaseCallback> mOnSuccessCb;
nsMainThreadPtrHandle<nsICameraErrorCallback> mOnErrorCb;
};
// Report that the video recorder state has changed.
class CameraRecorderStateChange : public nsRunnable
{
public:
CameraRecorderStateChange(nsMainThreadPtrHandle<nsICameraRecorderStateChange> onStateChange, const nsString& aStateMsg, int32_t aStatus, int32_t aTrackNumber, uint64_t aWindowId)
: mOnStateChangeCb(onStateChange)
, mStateMsg(aStateMsg)
, mStatus(aStatus)
, mTrackNumber(aTrackNumber)
, mWindowId(aWindowId)
{ }
NS_IMETHOD Run() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
if (mOnStateChangeCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
// For now, just pass the state message and swallow mStatus and mTrackNumber
mOnStateChangeCb->HandleStateChange(mStateMsg);
}
return NS_OK;
}
protected:
nsMainThreadPtrHandle<nsICameraRecorderStateChange> mOnStateChangeCb;
const nsString mStateMsg;
int32_t mStatus;
int32_t mTrackNumber;
uint64_t mWindowId;
};
// Report that the preview stream state has changed.
class CameraPreviewStateChange : public nsRunnable
{
public:
CameraPreviewStateChange(nsMainThreadPtrHandle<nsICameraPreviewStateChange> onStateChange, const nsString& aStateMsg, uint64_t aWindowId)
: mOnStateChangeCb(onStateChange)
, mStateMsg(aStateMsg)
, mWindowId(aWindowId)
{ }
NS_IMETHOD Run()
{
MOZ_ASSERT(NS_IsMainThread());
if (mOnStateChangeCb.get() && nsDOMCameraManager::IsWindowStillActive(mWindowId)) {
mOnStateChangeCb->HandleStateChange(mStateMsg);
}
return NS_OK;
}
protected:
nsMainThreadPtrHandle<nsICameraPreviewStateChange> mOnStateChangeCb;
const nsString mStateMsg;
uint64_t mWindowId;
};
} // namespace mozilla
#endif // DOM_CAMERA_CAMERACONTROLIMPL_H

View File

@ -0,0 +1,100 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef DOM_CAMERA_CAMERACONTROLLISTENER_H
#define DOM_CAMERA_CAMERACONTROLLISTENER_H
#include <stdint.h>
#include "ICameraControl.h"
namespace mozilla {
namespace layers {
class Image;
}
class CameraControlListener
{
public:
virtual ~CameraControlListener() { }
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CameraControlListener);
enum HardwareState
{
kHardwareOpen,
kHardwareClosed
};
virtual void OnHardwareStateChange(HardwareState aState) { }
enum PreviewState
{
kPreviewStopped,
kPreviewPaused,
kPreviewStarted
};
virtual void OnPreviewStateChange(PreviewState aState) { }
enum RecorderState
{
kRecorderStopped,
kRecorderStarted,
#ifdef MOZ_B2G_CAMERA
kFileSizeLimitReached,
kVideoLengthLimitReached,
kTrackCompleted,
kTrackFailed,
kMediaRecorderFailed,
kMediaServerFailed
#endif
};
enum { kNoTrackNumber = -1 };
virtual void OnRecorderStateChange(RecorderState aState, int32_t aStatus, int32_t aTrackNum) { }
virtual void OnShutter() { }
virtual bool OnNewPreviewFrame(layers::Image* aFrame, uint32_t aWidth, uint32_t aHeight)
{
return false;
}
class CameraListenerConfiguration : public ICameraControl::Configuration
{
public:
uint32_t mMaxMeteringAreas;
uint32_t mMaxFocusAreas;
};
virtual void OnConfigurationChange(const CameraListenerConfiguration& aConfiguration) { }
virtual void OnAutoFocusComplete(bool aAutoFocusSucceeded) { }
virtual void OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType) { }
enum CameraErrorContext
{
kInGetCamera,
kInAutoFocus,
kInTakePicture,
kInStartRecording,
kInStopRecording,
kInSetConfiguration,
kInReleaseHardware,
kInStartPreview,
kInStopPreview,
kInUnspecified
};
enum CameraError
{
kErrorApiFailed,
kErrorInitFailed,
kErrorInvalidConfiguration,
kErrorServiceFailed,
kErrorSetPictureSizeFailed,
kErrorSetThumbnailSizeFailed,
kErrorUnknown
};
virtual void OnError(CameraErrorContext aContext, CameraError aError) { }
};
} // namespace mozilla
#endif // DOM_CAMERA_CAMERACONTROLLISTENER_H

View File

@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "jsapi.h"
#include "CameraRecorderProfiles.h"
#include "jsapi.h"
#include "CameraCommon.h"
using namespace mozilla;

View File

@ -10,10 +10,8 @@
#include "nsAutoPtr.h"
#include "nsTArray.h"
#include "jsapi.h"
#include "DictionaryHelpers.h"
#include "CameraCommon.h"
namespace mozilla {
class CameraControlImpl;

View File

@ -1,424 +1,282 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include <cstring>
#include <cstdlib>
#include "base/basictypes.h"
#include "nsDOMClassInfo.h"
#include "jsapi.h"
#include "CameraRecorderProfiles.h"
#include "DOMCameraControl.h"
#include "DOMCameraCapabilities.h"
#include "nsPIDOMWindow.h"
#include "nsContentUtils.h"
#include "mozilla/dom/CameraManagerBinding.h"
#include "mozilla/dom/CameraCapabilitiesBinding.h"
#include "CameraCommon.h"
#include "ICameraControl.h"
#include "CameraRecorderProfiles.h"
using namespace mozilla;
using namespace mozilla::dom;
namespace mozilla {
namespace dom {
DOMCI_DATA(CameraCapabilities, nsICameraCapabilities)
NS_IMPL_CYCLE_COLLECTION_CLASS(CameraCapabilities)
NS_INTERFACE_MAP_BEGIN(DOMCameraCapabilities)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CameraCapabilities)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
tmp->mRecorderProfiles = JS::UndefinedValue();
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CameraCapabilities)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CameraCapabilities)
NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mRecorderProfiles)
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraCapabilities)
NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraCapabilities)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraCapabilities)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsICameraCapabilities)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CameraCapabilities)
NS_INTERFACE_MAP_END
NS_IMPL_ADDREF(DOMCameraCapabilities)
NS_IMPL_RELEASE(DOMCameraCapabilities)
static nsresult
ParseZoomRatioItemAndAdd(JSContext* aCx, JS::Handle<JSObject*> aArray,
uint32_t aIndex, const char* aStart, char** aEnd)
CameraCapabilities::CameraCapabilities(nsPIDOMWindow* aWindow)
: mRecorderProfiles(JS::UndefinedValue())
, mWindow(aWindow)
{
if (!*aEnd) {
// make 'aEnd' follow the same semantics as strchr().
aEnd = nullptr;
}
/**
* The by-100 divisor is Gonk-specific. For now, assume other platforms
* return actual fractional multipliers.
*/
double d = strtod(aStart, aEnd);
#if MOZ_WIDGET_GONK
d /= 100;
#endif
if (!JS_SetElement(aCx, aArray, aIndex, d)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
MOZ_COUNT_CTOR(CameraCapabilities);
mozilla::HoldJSObjects(this);
SetIsDOMBinding();
}
static nsresult
ParseStringItemAndAdd(JSContext* aCx, JS::Handle<JSObject*> aArray,
uint32_t aIndex, const char* aStart, char** aEnd)
CameraCapabilities::~CameraCapabilities()
{
JS::Rooted<JSString*> s(aCx);
if (*aEnd) {
s = JS_NewStringCopyN(aCx, aStart, *aEnd - aStart);
} else {
s = JS_NewStringCopyZ(aCx, aStart);
}
if (!s) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!JS_SetElement(aCx, aArray, aIndex, s)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
mRecorderProfiles = JS::UndefinedValue();
mozilla::DropJSObjects(this);
MOZ_COUNT_DTOR(CameraCapabilities);
}
static nsresult
ParseDimensionItemAndAdd(JSContext* aCx, JS::Handle<JSObject*> aArray,
uint32_t aIndex, const char* aStart, char** aEnd)
JSObject*
CameraCapabilities::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
char* x;
if (!*aEnd) {
// make 'aEnd' follow the same semantics as strchr().
aEnd = nullptr;
}
JS::Rooted<JS::Value> w(aCx, INT_TO_JSVAL(strtol(aStart, &x, 10)));
JS::Rooted<JS::Value> h(aCx, INT_TO_JSVAL(strtol(x + 1, aEnd, 10)));
JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
if (!o) {
return NS_ERROR_OUT_OF_MEMORY;
}
if (!JS_SetProperty(aCx, o, "width", w)) {
return NS_ERROR_FAILURE;
}
if (!JS_SetProperty(aCx, o, "height", h)) {
return NS_ERROR_FAILURE;
}
if (!JS_SetElement(aCx, aArray, aIndex, o)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
return CameraCapabilitiesBinding::Wrap(aCx, aScope, this);
}
#define LOG_IF_ERROR(rv, param) \
do { \
if (NS_FAILED(rv)) { \
DOM_CAMERA_LOGW("Error %x trying to get " #param "\n", \
(rv)); \
} \
} while(0)
nsresult
DOMCameraCapabilities::ParameterListToNewArray(JSContext* aCx,
JS::MutableHandle<JSObject*> aArray,
uint32_t aKey,
ParseItemAndAddFunc aParseItemAndAdd)
CameraCapabilities::TranslateToDictionary(ICameraControl* aCameraControl,
uint32_t aKey, nsTArray<CameraSize>& aSizes)
{
NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
const char* value = mCamera->GetParameterConstChar(aKey);
if (!value) {
// in case we get nonsense data back
aArray.set(nullptr);
return NS_OK;
}
aArray.set(JS_NewArrayObject(aCx, 0));
if (!aArray) {
return NS_ERROR_OUT_OF_MEMORY;
}
const char* p = value;
uint32_t index = 0;
nsresult rv;
char* q;
nsTArray<ICameraControl::Size> sizes;
while (p) {
/**
* In C's string.h, strchr() is declared as returning 'char*'; in C++'s
* cstring, it is declared as returning 'const char*', _except_ in MSVC,
* where the C version is declared to return const like the C++ version.
*
* Unfortunately, for both cases, strtod() and strtol() take a 'char**' as
* the end-of-conversion pointer, so we need to cast away strchr()'s
* const-ness here to make the MSVC build everything happy.
*/
q = const_cast<char*>(strchr(p, ','));
if (q != p) { // skip consecutive delimiters, just in case
rv = aParseItemAndAdd(aCx, aArray, index, p, &q);
NS_ENSURE_SUCCESS(rv, rv);
++index;
}
p = q;
if (p) {
++p;
}
}
return JS_FreezeObject(aCx, aArray) ? NS_OK : NS_ERROR_FAILURE;
}
nsresult
DOMCameraCapabilities::StringListToNewObject(JSContext* aCx,
JS::MutableHandle<JS::Value> aArray,
uint32_t aKey)
{
JS::Rooted<JSObject*> array(aCx);
nsresult rv = ParameterListToNewArray(aCx, &array, aKey, ParseStringItemAndAdd);
NS_ENSURE_SUCCESS(rv, rv);
aArray.setObjectOrNull(array);
return NS_OK;
}
nsresult
DOMCameraCapabilities::DimensionListToNewObject(JSContext* aCx,
JS::MutableHandle<JS::Value> aArray,
uint32_t aKey)
{
JS::Rooted<JSObject*> array(aCx);
nsresult rv = ParameterListToNewArray(aCx, &array, aKey, ParseDimensionItemAndAdd);
NS_ENSURE_SUCCESS(rv, rv);
aArray.setObjectOrNull(array);
return NS_OK;
}
/* readonly attribute jsval previewSizes; */
NS_IMETHODIMP
DOMCameraCapabilities::GetPreviewSizes(JSContext* cx,
JS::MutableHandle<JS::Value> aPreviewSizes)
{
return DimensionListToNewObject(cx, aPreviewSizes, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES);
}
/* readonly attribute jsval pictureSizes; */
NS_IMETHODIMP
DOMCameraCapabilities::GetPictureSizes(JSContext* cx,
JS::MutableHandle<JS::Value> aPictureSizes)
{
return DimensionListToNewObject(cx, aPictureSizes, CAMERA_PARAM_SUPPORTED_PICTURESIZES);
}
/* readonly attribute jsval thumbnailSizes; */
NS_IMETHODIMP
DOMCameraCapabilities::GetThumbnailSizes(JSContext* cx,
JS::MutableHandle<JS::Value> aThumbnailSizes)
{
return DimensionListToNewObject(cx, aThumbnailSizes, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES);
}
/* readonly attribute jsval fileFormats; */
NS_IMETHODIMP
DOMCameraCapabilities::GetFileFormats(JSContext* cx,
JS::MutableHandle<JS::Value> aFileFormats)
{
return StringListToNewObject(cx, aFileFormats, CAMERA_PARAM_SUPPORTED_PICTUREFORMATS);
}
/* readonly attribute jsval whiteBalanceModes; */
NS_IMETHODIMP
DOMCameraCapabilities::GetWhiteBalanceModes(JSContext* cx,
JS::MutableHandle<JS::Value> aWhiteBalanceModes)
{
return StringListToNewObject(cx, aWhiteBalanceModes, CAMERA_PARAM_SUPPORTED_WHITEBALANCES);
}
/* readonly attribute jsval sceneModes; */
NS_IMETHODIMP
DOMCameraCapabilities::GetSceneModes(JSContext* cx,
JS::MutableHandle<JS::Value> aSceneModes)
{
return StringListToNewObject(cx, aSceneModes, CAMERA_PARAM_SUPPORTED_SCENEMODES);
}
/* readonly attribute jsval effects; */
NS_IMETHODIMP
DOMCameraCapabilities::GetEffects(JSContext* cx,
JS::MutableHandle<JS::Value> aEffects)
{
return StringListToNewObject(cx, aEffects, CAMERA_PARAM_SUPPORTED_EFFECTS);
}
/* readonly attribute jsval flashModes; */
NS_IMETHODIMP
DOMCameraCapabilities::GetFlashModes(JSContext* cx,
JS::MutableHandle<JS::Value> aFlashModes)
{
return StringListToNewObject(cx, aFlashModes, CAMERA_PARAM_SUPPORTED_FLASHMODES);
}
/* readonly attribute jsval focusModes; */
NS_IMETHODIMP
DOMCameraCapabilities::GetFocusModes(JSContext* cx,
JS::MutableHandle<JS::Value> aFocusModes)
{
return StringListToNewObject(cx, aFocusModes, CAMERA_PARAM_SUPPORTED_FOCUSMODES);
}
/* readonly attribute long maxFocusAreas; */
NS_IMETHODIMP
DOMCameraCapabilities::GetMaxFocusAreas(JSContext* cx, int32_t* aMaxFocusAreas)
{
NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS);
if (!value) {
// in case we get nonsense data back
*aMaxFocusAreas = 0;
return NS_OK;
}
*aMaxFocusAreas = atoi(value);
return NS_OK;
}
/* readonly attribute double minExposureCompensation; */
NS_IMETHODIMP
DOMCameraCapabilities::GetMinExposureCompensation(JSContext* cx, double* aMinExposureCompensation)
{
NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION);
if (!value) {
// in case we get nonsense data back
*aMinExposureCompensation = 0;
return NS_OK;
}
*aMinExposureCompensation = atof(value);
return NS_OK;
}
/* readonly attribute double maxExposureCompensation; */
NS_IMETHODIMP
DOMCameraCapabilities::GetMaxExposureCompensation(JSContext* cx, double* aMaxExposureCompensation)
{
NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION);
if (!value) {
// in case we get nonsense data back
*aMaxExposureCompensation = 0;
return NS_OK;
}
*aMaxExposureCompensation = atof(value);
return NS_OK;
}
/* readonly attribute double stepExposureCompensation; */
NS_IMETHODIMP
DOMCameraCapabilities::GetStepExposureCompensation(JSContext* cx, double* aStepExposureCompensation)
{
NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP);
if (!value) {
// in case we get nonsense data back
*aStepExposureCompensation = 0;
return NS_OK;
}
*aStepExposureCompensation = atof(value);
return NS_OK;
}
/* readonly attribute long maxMeteringAreas; */
NS_IMETHODIMP
DOMCameraCapabilities::GetMaxMeteringAreas(JSContext* cx, int32_t* aMaxMeteringAreas)
{
NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS);
if (!value) {
// in case we get nonsense data back
*aMaxMeteringAreas = 0;
return NS_OK;
}
*aMaxMeteringAreas = atoi(value);
return NS_OK;
}
/* readonly attribute jsval zoomRatios; */
NS_IMETHODIMP
DOMCameraCapabilities::GetZoomRatios(JSContext* cx, JS::MutableHandle<JS::Value> aZoomRatios)
{
NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_ZOOM);
if (!value || strcmp(value, "true") != 0) {
// if zoom is not supported, return a null object
aZoomRatios.setNull();
return NS_OK;
}
JS::Rooted<JSObject*> array(cx);
nsresult rv = ParameterListToNewArray(cx, &array, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, ParseZoomRatioItemAndAdd);
NS_ENSURE_SUCCESS(rv, rv);
aZoomRatios.setObjectOrNull(array);
return NS_OK;
}
/* readonly attribute jsval videoSizes; */
NS_IMETHODIMP
DOMCameraCapabilities::GetVideoSizes(JSContext* cx, JS::MutableHandle<JS::Value> aVideoSizes)
{
NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
nsTArray<mozilla::idl::CameraSize> sizes;
nsresult rv = mCamera->GetVideoSizes(sizes);
NS_ENSURE_SUCCESS(rv, rv);
if (sizes.Length() == 0) {
// video recording not supported, return null
aVideoSizes.setNull();
return NS_OK;
}
JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, 0));
if (!array) {
return NS_ERROR_OUT_OF_MEMORY;
rv = aCameraControl->Get(aKey, sizes);
if (NS_FAILED(rv)) {
return rv;
}
aSizes.Clear();
aSizes.SetCapacity(sizes.Length());
for (uint32_t i = 0; i < sizes.Length(); ++i) {
JS::Rooted<JSObject*> o(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
JS::Rooted<JS::Value> v(cx, INT_TO_JSVAL(sizes[i].width));
if (!JS_SetProperty(cx, o, "width", v)) {
return NS_ERROR_FAILURE;
}
v = INT_TO_JSVAL(sizes[i].height);
if (!JS_SetProperty(cx, o, "height", v)) {
return NS_ERROR_FAILURE;
}
if (!JS_SetElement(cx, array, i, o)) {
return NS_ERROR_FAILURE;
}
CameraSize* s = aSizes.AppendElement();
s->mWidth = sizes[i].width;
s->mHeight = sizes[i].height;
}
aVideoSizes.setObject(*array);
return NS_OK;
}
/* readonly attribute jsval recorderProfiles; */
NS_IMETHODIMP
DOMCameraCapabilities::GetRecorderProfiles(JSContext* cx, JS::MutableHandle<JS::Value> aRecorderProfiles)
nsresult
CameraCapabilities::Populate(ICameraControl* aCameraControl)
{
NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
NS_ENSURE_TRUE(aCameraControl, NS_ERROR_INVALID_ARG);
nsRefPtr<RecorderProfileManager> profileMgr = mCamera->GetRecorderProfileManager();
if (!profileMgr) {
aRecorderProfiles.setNull();
return NS_OK;
nsresult rv;
rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES, mPreviewSizes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES);
rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_PICTURESIZES, mPictureSizes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTURESIZES);
rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES, mThumbnailSizes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES);
rv = TranslateToDictionary(aCameraControl, CAMERA_PARAM_SUPPORTED_VIDEOSIZES, mVideoSizes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_VIDEOSIZES);
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_PICTUREFORMATS, mFileFormats);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTUREFORMATS);
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_WHITEBALANCES, mWhiteBalanceModes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_WHITEBALANCES);
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_SCENEMODES, mSceneModes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_SCENEMODES);
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_EFFECTS, mEffects);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EFFECTS);
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_FLASHMODES, mFlashModes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FLASHMODES);
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_FOCUSMODES, mFocusModes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FOCUSMODES);
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS);
int32_t areas;
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS, areas);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS);
mMaxFocusAreas = areas < 0 ? 0 : areas;
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS, areas);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS);
mMaxMeteringAreas = areas < 0 ? 0 : areas;
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, mMinExposureCompensation);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION);
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION, mMaxExposureCompensation);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION);
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, mExposureCompensationStep);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP);
mRecorderProfileManager = aCameraControl->GetRecorderProfileManager();
if (!mRecorderProfileManager) {
DOM_CAMERA_LOGW("Unable to get recorder profile manager\n");
} else {
AutoJSContext js;
JS::Rooted<JSObject*> o(js);
nsresult rv = mRecorderProfileManager->GetJsObject(js, o.address());
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to JS-objectify profile manager (%d)\n", rv);
return rv;
}
mRecorderProfiles = JS::ObjectValue(*o);
}
JS::Rooted<JSObject*> o(cx);
nsresult rv = profileMgr->GetJsObject(cx, o.address());
NS_ENSURE_SUCCESS(rv, rv);
aRecorderProfiles.setObject(*o);
// For now, always return success, since the presence or absence of capabilities
// indicates whether or not they are supported.
return NS_OK;
}
void
CameraCapabilities::GetPreviewSizes(nsTArray<dom::CameraSize>& retval) const
{
retval = mPreviewSizes;
}
void
CameraCapabilities::GetPictureSizes(nsTArray<dom::CameraSize>& retval) const
{
retval = mPictureSizes;
}
void
CameraCapabilities::GetThumbnailSizes(nsTArray<dom::CameraSize>& retval) const
{
retval = mThumbnailSizes;
}
void
CameraCapabilities::GetVideoSizes(nsTArray<dom::CameraSize>& retval) const
{
retval = mVideoSizes;
}
void
CameraCapabilities::GetFileFormats(nsTArray<nsString>& retval) const
{
retval = mFileFormats;
}
void
CameraCapabilities::GetWhiteBalanceModes(nsTArray<nsString>& retval) const
{
retval = mWhiteBalanceModes;
}
void
CameraCapabilities::GetSceneModes(nsTArray<nsString>& retval) const
{
retval = mSceneModes;
}
void
CameraCapabilities::GetEffects(nsTArray<nsString>& retval) const
{
retval = mEffects;
}
void
CameraCapabilities::GetFlashModes(nsTArray<nsString>& retval) const
{
retval = mFlashModes;
}
void
CameraCapabilities::GetFocusModes(nsTArray<nsString>& retval) const
{
retval = mFocusModes;
}
void
CameraCapabilities::GetZoomRatios(nsTArray<double>& retval) const
{
retval = mZoomRatios;
}
uint32_t
CameraCapabilities::MaxFocusAreas() const
{
return mMaxFocusAreas;
}
uint32_t
CameraCapabilities::MaxMeteringAreas() const
{
return mMaxMeteringAreas;
}
double
CameraCapabilities::MinExposureCompensation() const
{
return mMinExposureCompensation;
}
double
CameraCapabilities::MaxExposureCompensation() const
{
return mMaxExposureCompensation;
}
double
CameraCapabilities::ExposureCompensationStep() const
{
return mExposureCompensationStep;
}
JS::Value
CameraCapabilities::RecorderProfiles(JSContext* aCx) const
{
return mRecorderProfiles;
}
} // namespace dom
} // namespace mozilla

View File

@ -1,59 +1,96 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim:set ts=2 sw=2 sts=2 et cindent: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef DOM_CAMERA_DOMCAMERACAPABILITIES_H
#define DOM_CAMERA_DOMCAMERACAPABILITIES_H
#ifndef mozilla_dom_CameraCapabilities_h__
#define mozilla_dom_CameraCapabilities_h__
#include "ICameraControl.h"
#include "nsString.h"
#include "nsAutoPtr.h"
#include "CameraCommon.h"
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/CameraManagerBinding.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
struct JSContext;
class nsPIDOMWindow;
namespace mozilla {
typedef nsresult (*ParseItemAndAddFunc)(JSContext* aCx, JS::Handle<JSObject*> aArray,
uint32_t aIndex, const char* aStart, char** aEnd);
class ICameraControl;
class RecorderProfileManager;
class DOMCameraCapabilities MOZ_FINAL : public nsICameraCapabilities
namespace dom {
class CameraCapabilities MOZ_FINAL : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSICAMERACAPABILITIES
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraCapabilities)
DOMCameraCapabilities(ICameraControl* aCamera)
: mCamera(aCamera)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
CameraCapabilities(nsPIDOMWindow* aWindow);
~CameraCapabilities();
nsresult ParameterListToNewArray(
JSContext* cx,
JS::MutableHandle<JSObject*> aArray,
uint32_t aKey,
ParseItemAndAddFunc aParseItemAndAdd
);
nsresult StringListToNewObject(JSContext* aCx,
JS::MutableHandle<JS::Value> aArray,
uint32_t aKey);
nsresult DimensionListToNewObject(JSContext* aCx,
JS::MutableHandle<JS::Value> aArray,
uint32_t aKey);
nsresult Populate(ICameraControl* aCameraControl);
private:
DOMCameraCapabilities(const DOMCameraCapabilities&) MOZ_DELETE;
DOMCameraCapabilities& operator=(const DOMCameraCapabilities&) MOZ_DELETE;
nsPIDOMWindow* GetParentObject() const { return mWindow; }
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
void GetPreviewSizes(nsTArray<CameraSize>& aRetVal) const;
void GetPictureSizes(nsTArray<CameraSize>& aRetVal) const;
void GetThumbnailSizes(nsTArray<CameraSize>& aRetVal) const;
void GetVideoSizes(nsTArray<CameraSize>& aRetVal) const;
void GetFileFormats(nsTArray<nsString>& aRetVal) const;
void GetWhiteBalanceModes(nsTArray<nsString>& aRetVal) const;
void GetSceneModes(nsTArray<nsString>& aRetVal) const;
void GetEffects(nsTArray<nsString>& aRetVal) const;
void GetFlashModes(nsTArray<nsString>& aRetVal) const;
void GetFocusModes(nsTArray<nsString>& aRetVal) const;
void GetZoomRatios(nsTArray<double>& aRetVal) const;
uint32_t MaxFocusAreas() const;
uint32_t MaxMeteringAreas() const;
double MinExposureCompensation() const;
double MaxExposureCompensation() const;
double ExposureCompensationStep() const;
JS::Value RecorderProfiles(JSContext* cx) const;
protected:
/* additional members */
~DOMCameraCapabilities()
{
// destructor code
DOM_CAMERA_LOGT("%s:%d : this=%p, mCamera=%p\n", __func__, __LINE__, this, mCamera.get());
}
nsresult TranslateToDictionary(ICameraControl* aCameraControl,
uint32_t aKey, nsTArray<CameraSize>& aSizes);
nsRefPtr<ICameraControl> mCamera;
nsTArray<CameraSize> mPreviewSizes;
nsTArray<CameraSize> mPictureSizes;
nsTArray<CameraSize> mThumbnailSizes;
nsTArray<CameraSize> mVideoSizes;
nsTArray<nsString> mFileFormats;
nsTArray<nsString> mWhiteBalanceModes;
nsTArray<nsString> mSceneModes;
nsTArray<nsString> mEffects;
nsTArray<nsString> mFlashModes;
nsTArray<nsString> mFocusModes;
nsTArray<double> mZoomRatios;
uint32_t mMaxFocusAreas;
uint32_t mMaxMeteringAreas;
double mMinExposureCompensation;
double mMaxExposureCompensation;
double mExposureCompensationStep;
nsRefPtr<RecorderProfileManager> mRecorderProfileManager;
JS::Heap<JS::Value> mRecorderProfiles;
nsRefPtr<nsPIDOMWindow> mWindow;
};
} // namespace dom
} // namespace mozilla
#endif // DOM_CAMERA_DOMCAMERACAPABILITIES_H
#endif // mozilla_dom_CameraCapabilities_h__

File diff suppressed because it is too large Load Diff

View File

@ -8,51 +8,50 @@
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIDOMEventListener.h"
#include "DictionaryHelpers.h"
#include "mozilla/dom/CameraControlBinding.h"
#include "ICameraControl.h"
#include "DOMCameraPreview.h"
#include "nsIDOMCameraManager.h"
#include "CameraCommon.h"
#include "DOMMediaStream.h"
#include "AudioChannelAgent.h"
#include "nsProxyRelease.h"
#include "nsHashPropertyBag.h"
#include "DeviceStorage.h"
#include "DOMCameraControlListener.h"
class nsDOMDeviceStorage;
class nsPIDOMWindow;
class nsIDOMBlob;
namespace mozilla {
namespace dom {
class CameraPictureOptions;
template<typename T> class Optional;
class CameraCapabilities;
class CameraPictureOptions;
class CameraStartRecordingOptions;
template<typename T> class Optional;
}
class ErrorResult;
class StartRecordingHelper;
// Main camera control.
class nsDOMCameraControl MOZ_FINAL : public nsIDOMEventListener,
public nsWrapperCache
class nsDOMCameraControl MOZ_FINAL : public DOMMediaStream
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_NSIDOMEVENTLISTENER
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(nsDOMCameraControl)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsDOMCameraControl, DOMMediaStream)
NS_DECL_ISUPPORTS_INHERITED
nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread,
nsICameraGetCameraCallback* onSuccess,
nsICameraErrorCallback* onError, nsPIDOMWindow* aWindow);
nsresult Result(nsresult aResult,
const nsMainThreadPtrHandle<nsICameraGetCameraCallback>& onSuccess,
const nsMainThreadPtrHandle<nsICameraErrorCallback>& onError,
uint64_t aWindowId);
nsDOMCameraControl(uint32_t aCameraId,
const dom::CameraConfiguration& aInitialConfig,
dom::GetCameraCallback* aOnSuccess,
dom::CameraErrorCallback* aOnError,
nsPIDOMWindow* aWindow);
nsRefPtr<ICameraControl> GetNativeCameraControl();
void Shutdown();
nsPIDOMWindow* GetParentObject() const { return mWindow; }
// WebIDL
nsICameraCapabilities* Capabilities();
// Attributes.
void GetEffect(nsString& aEffect, ErrorResult& aRv);
void SetEffect(const nsAString& aEffect, ErrorResult& aRv);
void GetWhiteBalanceMode(nsString& aMode, ErrorResult& aRv);
@ -80,48 +79,130 @@ public:
void SetExposureCompensation(const dom::Optional<double>& aCompensation, ErrorResult& aRv);
double GetExposureCompensation(ErrorResult& aRv);
int32_t SensorAngle();
already_AddRefed<nsICameraShutterCallback> GetOnShutter(ErrorResult& aRv);
void SetOnShutter(nsICameraShutterCallback* aCb, ErrorResult& aRv);
already_AddRefed<nsICameraClosedCallback> GetOnClosed(ErrorResult& aRv);
void SetOnClosed(nsICameraClosedCallback* aCb, ErrorResult& aRv);
already_AddRefed<nsICameraRecorderStateChange> GetOnRecorderStateChange(ErrorResult& aRv);
void SetOnRecorderStateChange(nsICameraRecorderStateChange* aCb, ErrorResult& aRv);
void AutoFocus(nsICameraAutoFocusCallback* aOnSuccess, const dom::Optional<nsICameraErrorCallback*>& aOnErro, ErrorResult& aRvr);
void TakePicture(JSContext* aCx, const dom::CameraPictureOptions& aOptions,
nsICameraTakePictureCallback* onSuccess,
const dom::Optional<nsICameraErrorCallback* >& onError,
already_AddRefed<dom::CameraCapabilities> Capabilities();
// Unsolicited event handlers.
already_AddRefed<dom::CameraShutterCallback> GetOnShutter();
void SetOnShutter(dom::CameraShutterCallback* aCb);
already_AddRefed<dom::CameraClosedCallback> GetOnClosed();
void SetOnClosed(dom::CameraClosedCallback* aCb);
already_AddRefed<dom::CameraRecorderStateChange> GetOnRecorderStateChange();
void SetOnRecorderStateChange(dom::CameraRecorderStateChange* aCb);
already_AddRefed<dom::CameraPreviewStateChange> GetOnPreviewStateChange();
void SetOnPreviewStateChange(dom::CameraPreviewStateChange* aCb);
// Methods.
void SetConfiguration(const dom::CameraConfiguration& aConfiguration,
const dom::Optional<dom::OwningNonNull<dom::CameraSetConfigurationCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
void AutoFocus(dom::CameraAutoFocusCallback& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
void TakePicture(const dom::CameraPictureOptions& aOptions,
dom::CameraTakePictureCallback& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
already_AddRefed<nsICameraPreviewStateChange> GetOnPreviewStateChange() const;
void SetOnPreviewStateChange(nsICameraPreviewStateChange* aOnStateChange);
void GetPreviewStreamVideoMode(JSContext* cx, JS::Handle<JS::Value> aOptions, nsICameraPreviewStreamCallback* onSuccess, const dom::Optional<nsICameraErrorCallback* >& onError, ErrorResult& aRv);
void StartRecording(JSContext* cx, JS::Handle<JS::Value> aOptions, nsDOMDeviceStorage& storageArea, const nsAString& filename, nsICameraStartRecordingCallback* onSuccess, const dom::Optional<nsICameraErrorCallback* >& onError, ErrorResult& aRv);
void StartRecording(const dom::CameraStartRecordingOptions& aOptions,
nsDOMDeviceStorage& storageArea,
const nsAString& filename,
dom::CameraStartRecordingCallback& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
void StopRecording(ErrorResult& aRv);
void GetPreviewStream(JSContext* cx, JS::Handle<JS::Value> aOptions, nsICameraPreviewStreamCallback* onSuccess, const dom::Optional<nsICameraErrorCallback* >& onError, ErrorResult& aRv);
void ResumePreview(ErrorResult& aRv);
void ReleaseHardware(const dom::Optional<nsICameraReleaseCallback* >& onSuccess, const dom::Optional<nsICameraErrorCallback* >& onError, ErrorResult& aRv);
void ReleaseHardware(const dom::Optional<dom::OwningNonNull<dom::CameraReleaseCallback> >& aOnSuccess,
const dom::Optional<dom::OwningNonNull<dom::CameraErrorCallback> >& aOnError,
ErrorResult& aRv);
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
protected:
virtual ~nsDOMCameraControl();
class DOMCameraConfiguration : public dom::CameraConfiguration
{
public:
NS_INLINE_DECL_REFCOUNTING(DOMCameraConfiguration)
DOMCameraConfiguration();
DOMCameraConfiguration(const dom::CameraConfiguration& aConfiguration);
// Additional configuration options that aren't exposed to the DOM
uint32_t mMaxFocusAreas;
uint32_t mMaxMeteringAreas;
protected:
~DOMCameraConfiguration();
};
friend class DOMCameraControlListener;
friend class mozilla::StartRecordingHelper;
void OnCreatedFileDescriptor(bool aSucceeded);
void OnAutoFocusComplete(bool aAutoFocusSucceeded);
void OnTakePictureComplete(nsIDOMBlob* aPicture);
void OnHardwareStateChange(DOMCameraControlListener::HardwareState aState);
void OnPreviewStateChange(DOMCameraControlListener::PreviewState aState);
void OnRecorderStateChange(CameraControlListener::RecorderState aState, int32_t aStatus, int32_t aTrackNum);
void OnConfigurationChange(DOMCameraConfiguration* aConfiguration);
void OnShutter();
void OnError(CameraControlListener::CameraErrorContext aContext, const nsAString& mError);
bool IsWindowStillActive();
nsresult NotifyRecordingStatusChange(const nsString& aMsg);
nsRefPtr<ICameraControl> mCameraControl; // non-DOM camera control
// An agent used to join audio channel service.
nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit);
nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue);
nsRefPtr<DOMCameraConfiguration> mCurrentConfiguration;
nsRefPtr<dom::CameraCapabilities> mCapabilities;
// solicited camera control event handlers
nsCOMPtr<dom::GetCameraCallback> mGetCameraOnSuccessCb;
nsCOMPtr<dom::CameraErrorCallback> mGetCameraOnErrorCb;
nsCOMPtr<dom::CameraAutoFocusCallback> mAutoFocusOnSuccessCb;
nsCOMPtr<dom::CameraErrorCallback> mAutoFocusOnErrorCb;
nsCOMPtr<dom::CameraTakePictureCallback> mTakePictureOnSuccessCb;
nsCOMPtr<dom::CameraErrorCallback> mTakePictureOnErrorCb;
nsCOMPtr<dom::CameraStartRecordingCallback> mStartRecordingOnSuccessCb;
nsCOMPtr<dom::CameraErrorCallback> mStartRecordingOnErrorCb;
nsCOMPtr<dom::CameraReleaseCallback> mReleaseOnSuccessCb;
nsCOMPtr<dom::CameraErrorCallback> mReleaseOnErrorCb;
nsCOMPtr<dom::CameraSetConfigurationCallback> mSetConfigurationOnSuccessCb;
nsCOMPtr<dom::CameraErrorCallback> mSetConfigurationOnErrorCb;
// unsolicited event handlers
nsCOMPtr<dom::CameraShutterCallback> mOnShutterCb;
nsCOMPtr<dom::CameraClosedCallback> mOnClosedCb;
nsCOMPtr<dom::CameraRecorderStateChange> mOnRecorderStateChangeCb;
nsCOMPtr<dom::CameraPreviewStateChange> mOnPreviewStateChangeCb;
// Camera event listener; we only need this weak reference so that
// we can remove the listener from the camera when we're done
// with it.
DOMCameraControlListener* mListener;
// our viewfinder stream
CameraPreviewMediaStream* mInput;
// set once when this object is created
nsCOMPtr<nsPIDOMWindow> mWindow;
dom::CameraStartRecordingOptions mOptions;
nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
private:
nsDOMCameraControl(const nsDOMCameraControl&) MOZ_DELETE;
nsDOMCameraControl& operator=(const nsDOMCameraControl&) MOZ_DELETE;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
nsresult NotifyRecordingStatusChange(const nsString& aMsg);
mozilla::idl::CameraStartRecordingOptions mOptions;
nsRefPtr<DeviceStorageFileDescriptor> mDSFileDescriptor;
nsCOMPtr<nsICameraStartRecordingCallback> mOnSuccessCb;
nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
protected:
/* additional members */
nsRefPtr<ICameraControl> mCameraControl; // non-DOM camera control
nsCOMPtr<nsICameraCapabilities> mDOMCapabilities;
// An agent used to join audio channel service.
nsCOMPtr<nsIAudioChannelAgent> mAudioChannelAgent;
nsCOMPtr<nsPIDOMWindow> mWindow;
};
} // namespace mozilla

View File

@ -0,0 +1,329 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DOMCameraControlListener.h"
#include "nsThreadUtils.h"
#include "nsDOMFile.h"
#include "CameraCommon.h"
#include "DOMCameraControl.h"
#include "CameraPreviewMediaStream.h"
#include "mozilla/dom/CameraManagerBinding.h"
using namespace mozilla;
using namespace mozilla::dom;
// Boilerplate callback runnable
class DOMCameraControlListener::DOMCallback : public nsRunnable
{
public:
DOMCallback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl)
: mDOMCameraControl(aDOMCameraControl)
{ }
virtual ~DOMCallback() { }
virtual void RunCallback(nsDOMCameraControl* aDOMCameraControl) = 0;
NS_IMETHOD
Run() MOZ_OVERRIDE
{
MOZ_ASSERT(NS_IsMainThread());
nsRefPtr<nsDOMCameraControl> camera = mDOMCameraControl.get();
if (camera) {
RunCallback(camera);
}
return NS_OK;
}
protected:
nsMainThreadPtrHandle<nsDOMCameraControl> mDOMCameraControl;
};
// Specific callback handlers
void
DOMCameraControlListener::OnHardwareStateChange(HardwareState aState)
{
class Callback : public DOMCallback
{
public:
Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
HardwareState aState)
: DOMCallback(aDOMCameraControl)
, mState(aState)
{ }
void
RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
{
aDOMCameraControl->OnHardwareStateChange(mState);
}
protected:
HardwareState mState;
};
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState));
}
void
DOMCameraControlListener::OnPreviewStateChange(PreviewState aState)
{
class Callback : public DOMCallback
{
public:
Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
PreviewState aState)
: DOMCallback(aDOMCameraControl)
, mState(aState)
{ }
void
RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
{
aDOMCameraControl->OnPreviewStateChange(mState);
}
protected:
PreviewState mState;
};
switch (aState) {
case kPreviewStopped:
// Clear the current frame right away, without dispatching a
// runnable. This is an ugly coupling between the camera's
// SurfaceTextureClient and the MediaStream/ImageContainer,
// but without it, the preview can fail to start.
DOM_CAMERA_LOGI("Preview stopped, clearing current frame\n");
mStream->ClearCurrentFrame();
break;
case kPreviewPaused:
// In the paused state, we still want to reflect the change
// in preview state, but we don't want to clear the current
// frame as above, since doing so seems to cause genlock
// problems when we restart the preview. See bug 957749.
DOM_CAMERA_LOGI("Preview paused\n");
break;
case kPreviewStarted:
DOM_CAMERA_LOGI("Preview started\n");
break;
default:
DOM_CAMERA_LOGE("Unknown preview state %d\n", aState);
MOZ_ASSUME_UNREACHABLE("Invalid preview state");
return;
}
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState));
}
void
DOMCameraControlListener::OnRecorderStateChange(RecorderState aState,
int32_t aStatus, int32_t aTrackNum)
{
class Callback : public DOMCallback
{
public:
Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
RecorderState aState,
int32_t aStatus,
int32_t aTrackNum)
: DOMCallback(aDOMCameraControl)
, mState(aState)
, mStatus(aStatus)
, mTrackNum(aTrackNum)
{ }
void
RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
{
aDOMCameraControl->OnRecorderStateChange(mState, mStatus, mTrackNum);
}
protected:
RecorderState mState;
int32_t mStatus;
int32_t mTrackNum;
};
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aState, aStatus, aTrackNum));
}
void
DOMCameraControlListener::OnConfigurationChange(const CameraListenerConfiguration& aConfiguration)
{
class Callback : public DOMCallback
{
public:
Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
const CameraListenerConfiguration& aConfiguration)
: DOMCallback(aDOMCameraControl)
, mConfiguration(aConfiguration)
{ }
void
RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
{
nsRefPtr<nsDOMCameraControl::DOMCameraConfiguration> config =
new nsDOMCameraControl::DOMCameraConfiguration();
switch (mConfiguration.mMode) {
case ICameraControl::kVideoMode:
config->mMode = CameraMode::Video;
break;
case ICameraControl::kPictureMode:
config->mMode = CameraMode::Picture;
break;
default:
MOZ_ASSUME_UNREACHABLE("Unanticipated camera mode!");
}
// Map CameraControl parameters to their DOM-facing equivalents
config->mRecorderProfile = mConfiguration.mRecorderProfile;
config->mPreviewSize.mWidth = mConfiguration.mPreviewSize.width;
config->mPreviewSize.mHeight = mConfiguration.mPreviewSize.height;
config->mMaxMeteringAreas = mConfiguration.mMaxMeteringAreas;
config->mMaxFocusAreas = mConfiguration.mMaxFocusAreas;
aDOMCameraControl->OnConfigurationChange(config);
}
protected:
const CameraListenerConfiguration mConfiguration;
};
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aConfiguration));
}
void
DOMCameraControlListener::OnShutter()
{
class Callback : public DOMCallback
{
public:
Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl)
: DOMCallback(aDOMCameraControl)
{ }
void
RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
{
aDOMCameraControl->OnShutter();
}
};
NS_DispatchToMainThread(new Callback(mDOMCameraControl));
}
bool
DOMCameraControlListener::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uint32_t aHeight)
{
DOM_CAMERA_LOGI("OnNewPreviewFrame: got %d x %d frame\n", aWidth, aHeight);
mStream->SetCurrentFrame(gfxIntSize(aWidth, aHeight), aImage);
return true;
}
void
DOMCameraControlListener::OnAutoFocusComplete(bool aAutoFocusSucceeded)
{
class Callback : public DOMCallback
{
public:
Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
bool aAutoFocusSucceeded)
: DOMCallback(aDOMCameraControl)
, mAutoFocusSucceeded(aAutoFocusSucceeded)
{ }
void
RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
{
aDOMCameraControl->OnAutoFocusComplete(mAutoFocusSucceeded);
}
protected:
bool mAutoFocusSucceeded;
};
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aAutoFocusSucceeded));
}
void
DOMCameraControlListener::OnTakePictureComplete(uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
{
class Callback : public DOMCallback
{
public:
Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
uint8_t* aData, uint32_t aLength, const nsAString& aMimeType)
: DOMCallback(aDOMCameraControl)
, mData(aData)
, mLength(aLength)
, mMimeType(aMimeType)
{ }
void
RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
{
nsCOMPtr<nsIDOMBlob> picture = new nsDOMMemoryFile(static_cast<void*>(mData),
static_cast<uint64_t>(mLength),
mMimeType);
aDOMCameraControl->OnTakePictureComplete(picture);
}
protected:
uint8_t* mData;
uint32_t mLength;
nsString mMimeType;
};
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aData, aLength, aMimeType));
}
void
DOMCameraControlListener::OnError(CameraErrorContext aContext, CameraError aError)
{
class Callback : public DOMCallback
{
public:
Callback(nsMainThreadPtrHandle<nsDOMCameraControl> aDOMCameraControl,
CameraErrorContext aContext,
CameraError aError)
: DOMCallback(aDOMCameraControl)
, mContext(aContext)
, mError(aError)
{ }
void
RunCallback(nsDOMCameraControl* aDOMCameraControl) MOZ_OVERRIDE
{
nsString error;
switch (mError) {
case kErrorServiceFailed:
error = NS_LITERAL_STRING("ErrorServiceFailed");
break;
case kErrorApiFailed:
// XXXmikeh legacy error placeholder
error = NS_LITERAL_STRING("FAILURE");
break;
default:
error = NS_LITERAL_STRING("ErrorUnknown");
break;
}
aDOMCameraControl->OnError(mContext, error);
}
protected:
CameraErrorContext mContext;
CameraError mError;
};
NS_DispatchToMainThread(new Callback(mDOMCameraControl, aContext, aError));
}

View File

@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef DOM_CAMERA_DOMCAMERACONTROLLISTENER_H
#define DOM_CAMERA_DOMCAMERACONTROLLISTENER_H
#include "nsProxyRelease.h"
#include "CameraControlListener.h"
namespace mozilla {
class nsDOMCameraControl;
class CameraPreviewMediaStream;
class DOMCameraControlListener : public CameraControlListener
{
protected:
nsMainThreadPtrHandle<nsDOMCameraControl> mDOMCameraControl;
CameraPreviewMediaStream* mStream;
class DOMCallback;
public:
DOMCameraControlListener(nsDOMCameraControl* aDOMCameraControl, CameraPreviewMediaStream* aStream)
: mDOMCameraControl(new nsMainThreadPtrHolder<nsDOMCameraControl>(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;
};
} // namespace mozilla
#endif // DOM_CAMERA_DOMCAMERACONTROLLISTENER_H

View File

@ -2,16 +2,18 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DOMCameraManager.h"
#include "nsDebug.h"
#include "jsapi.h"
#include "nsPIDOMWindow.h"
#include "mozilla/Services.h"
#include "nsObserverService.h"
#include "nsIPermissionManager.h"
#include "DOMCameraControl.h"
#include "DOMCameraManager.h"
#include "nsDOMClassInfo.h"
#include "DictionaryHelpers.h"
#include "CameraCommon.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/CameraManagerBinding.h"
using namespace mozilla;
@ -22,6 +24,7 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(nsDOMCameraManager, mWindow)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMCameraManager)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_END
@ -38,37 +41,35 @@ PRLogModuleInfo*
GetCameraLog()
{
static PRLogModuleInfo *sLog;
if (!sLog)
if (!sLog) {
sLog = PR_NewLogModule("Camera");
}
return sLog;
}
/**
* nsDOMCameraManager::GetListOfCameras
* is implementation-specific, and can be found in (e.g.)
* GonkCameraManager.cpp and FallbackCameraManager.cpp.
*/
WindowTable* nsDOMCameraManager::sActiveWindows = nullptr;
nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindow* aWindow)
: mWindowId(aWindow->WindowID())
, mCameraThread(nullptr)
, mWindow(aWindow)
{
/* member initializers and constructor code */
DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%llx\n", __func__, __LINE__, this, mWindowId);
MOZ_COUNT_CTOR(nsDOMCameraManager);
SetIsDOMBinding();
}
nsDOMCameraManager::~nsDOMCameraManager()
{
/* destructor code */
MOZ_COUNT_DTOR(nsDOMCameraManager);
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
obs->RemoveObserver(this, "xpcom-shutdown");
}
}
void
nsDOMCameraManager::GetListOfCameras(nsTArray<nsString>& aList, ErrorResult& aRv)
{
aRv = ICameraControl::GetListOfCameras(aList);
}
bool
@ -106,30 +107,28 @@ nsDOMCameraManager::CreateInstance(nsPIDOMWindow* aWindow)
}
void
nsDOMCameraManager::GetCamera(const CameraSelector& aOptions,
nsICameraGetCameraCallback* onSuccess,
const Optional<nsICameraErrorCallback*>& onError,
nsDOMCameraManager::GetCamera(const nsAString& aCamera,
const CameraConfiguration& aInitialConfig,
GetCameraCallback& aOnSuccess,
const Optional<OwningNonNull<CameraErrorCallback> >& aOnError,
ErrorResult& aRv)
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
uint32_t cameraId = 0; // back (or forward-facing) camera by default
if (aOptions.mCamera.EqualsLiteral("front")) {
if (aCamera.EqualsLiteral("front")) {
cameraId = 1;
}
// reuse the same camera thread to conserve resources
if (!mCameraThread) {
aRv = NS_NewThread(getter_AddRefs(mCameraThread));
if (aRv.Failed()) {
return;
}
nsCOMPtr<CameraErrorCallback> errorCallback = nullptr;
if (aOnError.WasPassed()) {
errorCallback = &aOnError.Value();
}
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
// Creating this object will trigger the onSuccess handler
// Creating this object will trigger the aOnSuccess callback
// (or the aOnError one, if it fails).
nsRefPtr<nsDOMCameraControl> cameraControl =
new nsDOMCameraControl(cameraId, mCameraThread,
onSuccess, onError.WasPassed() ? onError.Value() : nullptr, mWindow);
new nsDOMCameraControl(cameraId, aInitialConfig, &aOnSuccess, errorCallback, mWindow);
Register(cameraControl);
}

View File

@ -10,24 +10,24 @@
#include "mozilla/dom/BindingDeclarations.h"
#include "nsCOMPtr.h"
#include "nsAutoPtr.h"
#include "nsIThread.h"
#include "nsIObserver.h"
#include "nsThreadUtils.h"
#include "nsHashKeys.h"
#include "nsWrapperCache.h"
#include "nsWeakReference.h"
#include "nsClassHashtable.h"
#include "nsIDOMCameraManager.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Attributes.h"
#include "nsPIDOMWindow.h"
class nsPIDOMWindow;
namespace mozilla {
class ErrorResult;
class nsDOMCameraControl;
namespace dom {
class CameraSelector;
}
class nsDOMCameraControl;
namespace dom {
class CameraConfiguration;
class GetCameraCallback;
class CameraErrorCallback;
}
}
typedef nsTArray<nsRefPtr<mozilla::nsDOMCameraControl> > CameraControls;
@ -52,13 +52,11 @@ public:
void Register(mozilla::nsDOMCameraControl* aDOMCameraControl);
void OnNavigation(uint64_t aWindowId);
nsresult GetNumberOfCameras(int32_t& aDeviceCount);
nsresult GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName);
// WebIDL
void GetCamera(const mozilla::dom::CameraSelector& aOptions,
nsICameraGetCameraCallback* aCallback,
const mozilla::dom::Optional<nsICameraErrorCallback*>& ErrorCallback,
void GetCamera(const nsAString& aCamera,
const mozilla::dom::CameraConfiguration& aOptions,
mozilla::dom::GetCameraCallback& aOnSuccess,
const mozilla::dom::Optional<mozilla::dom::OwningNonNull<mozilla::dom::CameraErrorCallback> >& aOnError,
mozilla::ErrorResult& aRv);
void GetListOfCameras(nsTArray<nsString>& aList, mozilla::ErrorResult& aRv);
@ -79,32 +77,12 @@ private:
protected:
uint64_t mWindowId;
nsCOMPtr<nsIThread> mCameraThread;
nsCOMPtr<nsPIDOMWindow> mWindow;
/**
* 'mActiveWindows' is only ever accessed while in the main thread,
* 'sActiveWindows' is only ever accessed while in the Main Thread,
* so it is not otherwise protected.
*/
static ::WindowTable* sActiveWindows;
};
class GetCameraTask : public nsRunnable
{
public:
GetCameraTask(uint32_t aCameraId, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, nsIThread* aCameraThread)
: mCameraId(aCameraId)
, mOnSuccessCb(onSuccess)
, mOnErrorCb(onError)
, mCameraThread(aCameraThread)
{ }
NS_IMETHOD Run() MOZ_OVERRIDE;
protected:
uint32_t mCameraId;
nsCOMPtr<nsICameraGetCameraCallback> mOnSuccessCb;
nsCOMPtr<nsICameraErrorCallback> mOnErrorCb;
nsCOMPtr<nsIThread> mCameraThread;
};
#endif // DOM_CAMERA_DOMCAMERAMANAGER_H

View File

@ -1,304 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "base/basictypes.h"
#include "Layers.h"
#include "VideoUtils.h"
#include "DOMCameraPreview.h"
#include "CameraCommon.h"
#include "nsGlobalWindow.h"
#include "nsIDocument.h"
#include "nsPIDOMWindow.h"
using namespace mozilla;
using namespace mozilla::layers;
/**
* 'PreviewControl' is a helper class that dispatches preview control
* events from the main thread.
*
* NS_NewRunnableMethod() can't be used because it AddRef()s the method's
* object, which can't be done off the main thread for cycle collection
* participants.
*
* Before using this class, 'aDOMPreview' must be appropriately AddRef()ed.
*/
class PreviewControl : public nsRunnable
{
public:
enum {
START,
STOP,
STARTED,
STOPPED
};
PreviewControl(DOMCameraPreview* aDOMPreview, uint32_t aControl)
: mDOMPreview(aDOMPreview)
, mControl(aControl)
{ }
NS_IMETHOD Run()
{
NS_ASSERTION(NS_IsMainThread(), "PreviewControl not run on main thread");
switch (mControl) {
case START:
mDOMPreview->Start();
break;
case STOP:
mDOMPreview->StopPreview();
break;
case STARTED:
mDOMPreview->SetStateStarted();
break;
case STOPPED:
mDOMPreview->SetStateStopped();
break;
default:
DOM_CAMERA_LOGE("PreviewControl: invalid control %d\n", mControl);
break;
}
return NS_OK;
}
protected:
/**
* This must be a raw pointer because this class is not created on the
* main thread, and DOMCameraPreview is not threadsafe. Prior to
* issuing a preview control event, the caller must ensure that
* mDOMPreview will not disappear.
*/
DOMCameraPreview* mDOMPreview;
uint32_t mControl;
};
class DOMCameraPreviewListener : public MediaStreamListener
{
public:
DOMCameraPreviewListener(DOMCameraPreview* aDOMPreview) :
mDOMPreview(aDOMPreview)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
~DOMCameraPreviewListener()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
void NotifyConsumptionChanged(MediaStreamGraph* aGraph, Consumption aConsuming)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
#ifdef PR_LOGGING
const char* state;
switch (aConsuming) {
case NOT_CONSUMED:
state = "not consuming";
break;
case CONSUMED:
state = "consuming";
break;
default:
state = "unknown";
break;
}
DOM_CAMERA_LOGA("camera viewfinder is %s\n", state);
#endif
nsCOMPtr<nsIRunnable> previewControl;
switch (aConsuming) {
case NOT_CONSUMED:
previewControl = new PreviewControl(mDOMPreview, PreviewControl::STOP);
break;
case CONSUMED:
previewControl = new PreviewControl(mDOMPreview, PreviewControl::START);
break;
default:
return;
}
nsresult rv = NS_DispatchToMainThread(previewControl);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Failed to dispatch preview control (%d)!\n", rv);
}
}
protected:
// Raw pointer; if we exist, 'mDOMPreview' exists as well
DOMCameraPreview* mDOMPreview;
};
DOMCameraPreview::DOMCameraPreview(nsGlobalWindow* aWindow,
ICameraControl* aCameraControl,
uint32_t aWidth, uint32_t aHeight,
uint32_t aFrameRate)
: DOMMediaStream()
, mState(STOPPED)
, mWidth(aWidth)
, mHeight(aHeight)
, mFramesPerSecond(aFrameRate)
, mFrameCount(0)
, mCameraControl(aCameraControl)
{
DOM_CAMERA_LOGT("%s:%d : this=%p : mWidth=%d, mHeight=%d, mFramesPerSecond=%d\n", __func__, __LINE__, this, mWidth, mHeight, mFramesPerSecond);
mImageContainer = LayerManager::CreateImageContainer();
mWindow = aWindow;
mInput = new CameraPreviewMediaStream(this);
mStream = mInput;
mListener = new DOMCameraPreviewListener(this);
mInput->AddListener(mListener);
if (aWindow->GetExtantDoc()) {
CombineWithPrincipal(aWindow->GetExtantDoc()->NodePrincipal());
}
}
DOMCameraPreview::~DOMCameraPreview()
{
DOM_CAMERA_LOGT("%s:%d : this=%p, mListener=%p\n", __func__, __LINE__, this, mListener);
mInput->RemoveListener(mListener);
}
bool
DOMCameraPreview::HaveEnoughBuffered()
{
return true;
}
bool
DOMCameraPreview::ReceiveFrame(void* aBuffer, ImageFormat aFormat, FrameBuilder aBuilder)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
if (!aBuffer || !aBuilder) {
return false;
}
if (mState != STARTED) {
return false;
}
nsRefPtr<Image> image = mImageContainer->CreateImage(aFormat);
aBuilder(image, aBuffer, mWidth, mHeight);
mInput->SetCurrentFrame(gfxIntSize(mWidth, mHeight), image);
return true;
}
void
DOMCameraPreview::Start()
{
NS_ASSERTION(NS_IsMainThread(), "Start() not called from main thread");
if (mState != STOPPED) {
return;
}
DOM_CAMERA_LOGI("Starting preview stream\n");
/**
* Add a reference to ourselves to make sure we stay alive while
* the preview is running, as the CameraControlImpl object holds a
* weak reference to us.
*
* This reference is removed in SetStateStopped().
*/
NS_ADDREF_THIS();
DOM_CAMERA_SETSTATE(STARTING);
mCameraControl->StartPreview(this);
}
void
DOMCameraPreview::SetStateStarted()
{
NS_ASSERTION(NS_IsMainThread(), "SetStateStarted() not called from main thread");
DOM_CAMERA_SETSTATE(STARTED);
DOM_CAMERA_LOGI("Preview stream started\n");
}
void
DOMCameraPreview::Started()
{
if (mState != STARTING) {
return;
}
DOM_CAMERA_LOGI("Dispatching preview stream started\n");
nsCOMPtr<nsIRunnable> started = new PreviewControl(this, PreviewControl::STARTED);
nsresult rv = NS_DispatchToMainThread(started);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("failed to set statrted state (%d), POTENTIAL MEMORY LEAK!\n", rv);
}
}
void
DOMCameraPreview::StopPreview()
{
NS_ASSERTION(NS_IsMainThread(), "StopPreview() not called from main thread");
if (mState != STARTED) {
return;
}
DOM_CAMERA_LOGI("Stopping preview stream\n");
DOM_CAMERA_SETSTATE(STOPPING);
mCameraControl->StopPreview();
//mInput->EndTrack(TRACK_VIDEO);
//mInput->Finish();
}
void
DOMCameraPreview::SetStateStopped()
{
NS_ASSERTION(NS_IsMainThread(), "SetStateStopped() not called from main thread");
// see bug 809259 and bug 817367.
if (mState != STOPPING) {
//mInput->EndTrack(TRACK_VIDEO);
//mInput->Finish();
}
DOM_CAMERA_SETSTATE(STOPPED);
DOM_CAMERA_LOGI("Preview stream stopped\n");
/**
* Only remove the reference added in Start() once the preview
* has stopped completely.
*/
NS_RELEASE_THIS();
}
void
DOMCameraPreview::Stopped(bool aForced)
{
if (mState != STOPPING && !aForced) {
return;
}
mInput->ClearCurrentFrame();
DOM_CAMERA_LOGI("Dispatching preview stream stopped\n");
nsCOMPtr<nsIRunnable> stopped = new PreviewControl(this, PreviewControl::STOPPED);
nsresult rv = NS_DispatchToMainThread(stopped);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("failed to decrement reference count (%d), MEMORY LEAK!\n", rv);
}
}
void
DOMCameraPreview::Error()
{
DOM_CAMERA_LOGE("Error occurred changing preview state!\n");
Stopped(true);
}

View File

@ -1,99 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef DOM_CAMERA_DOMCAMERAPREVIEW_H
#define DOM_CAMERA_DOMCAMERAPREVIEW_H
#include "nsCycleCollectionParticipant.h"
#include "MediaStreamGraph.h"
#include "StreamBuffer.h"
#include "ICameraControl.h"
#include "DOMMediaStream.h"
#include "CameraPreviewMediaStream.h"
#include "CameraCommon.h"
class nsGlobalWindow;
namespace mozilla {
typedef void (*FrameBuilder)(mozilla::layers::Image* aImage, void* aBuffer, uint32_t aWidth, uint32_t aHeight);
/**
* DOMCameraPreview is only exposed to the DOM as an nsDOMMediaStream,
* which is a cycle-collection participant already, and we don't
* add any traceable fields here, so we don't need to declare any
* more cycle-collection goop.
*/
class DOMCameraPreview : public DOMMediaStream
{
protected:
enum { TRACK_VIDEO = 1 };
public:
DOMCameraPreview(nsGlobalWindow* aWindow, ICameraControl* aCameraControl,
uint32_t aWidth, uint32_t aHeight, uint32_t aFramesPerSecond = 30);
bool ReceiveFrame(void* aBuffer, ImageFormat aFormat, mozilla::FrameBuilder aBuilder);
bool HaveEnoughBuffered();
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 Stopped(bool aForced = false);
// called by the CameraControl when preview is stopped
void Error(); // something went wrong, NS_RELEASE needed
void SetStateStarted();
void SetStateStopped();
protected:
virtual ~DOMCameraPreview();
enum {
STOPPED,
STARTING,
STARTED,
STOPPING
};
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;
CameraPreviewMediaStream* mInput;
nsRefPtr<mozilla::layers::ImageContainer> mImageContainer;
VideoSegment mVideoSegment;
uint32_t mFrameCount;
nsRefPtr<ICameraControl> mCameraControl;
// Raw pointer; AddListener() keeps the reference for us
MediaStreamListener* mListener;
private:
DOMCameraPreview(const DOMCameraPreview&) MOZ_DELETE;
DOMCameraPreview& operator=(const DOMCameraPreview&) MOZ_DELETE;
};
} // namespace mozilla
#define DOM_CAMERA_SETSTATE(newState) SetState((newState), __func__, __LINE__)
#endif // DOM_CAMERA_DOMCAMERAPREVIEW_H

View File

@ -2,9 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsDOMClassInfoID.h"
#include "DOMCameraControl.h"
#include "DOMCameraCapabilities.h"
#include "DOMCameraControl.h"
#include "CameraCommon.h"
using namespace mozilla;

View File

@ -2,215 +2,74 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DOMCameraControl.h"
#include "CameraControlImpl.h"
using namespace mozilla;
using namespace mozilla::dom;
namespace mozilla {
class RecorderProfileManager;
class RecorderProfileManager;
namespace layers {
class GraphicBufferLocked;
}
}
/**
* Fallback camera control subclass. Can be used as a template for the
* Fallback camera control subclass. Can be used as a template for the
* definition of new camera support classes.
*/
class nsFallbackCameraControl : public CameraControlImpl
class FallbackCameraControl : public CameraControlImpl
{
public:
nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
FallbackCameraControl(uint32_t aCameraId) : CameraControlImpl(aCameraId) { }
const char* GetParameter(const char* aKey);
const char* GetParameterConstChar(uint32_t aKey);
double GetParameterDouble(uint32_t aKey);
int32_t GetParameterInt32(uint32_t aKey);
void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions);
void GetParameter(uint32_t aKey, idl::CameraSize& aSize);
void SetParameter(const char* aKey, const char* aValue);
void SetParameter(uint32_t aKey, const char* aValue);
void SetParameter(uint32_t aKey, double aValue);
void SetParameter(uint32_t aKey, const nsTArray<idl::CameraRegion>& aRegions);
void SetParameter(uint32_t aKey, const idl::CameraSize& aSize);
nsresult GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes);
nsresult PushParameters();
void OnAutoFocusComplete(bool aSuccess);
void OnTakePictureComplete(uint8_t* aData, uint32_t aLength) { }
void OnTakePictureError() { }
void OnNewPreviewFrame(layers::GraphicBufferLocked* aBuffer) { }
void OnRecorderEvent(int msg, int ext1, int ext2) { }
void OnError(CameraControlListener::CameraErrorContext aWhere,
CameraControlListener::CameraError aError) { }
virtual nsresult Set(uint32_t aKey, const nsAString& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Get(uint32_t aKey, nsAString& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Set(uint32_t aKey, double aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Get(uint32_t aKey, double& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Set(uint32_t aKey, int32_t aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Get(uint32_t aKey, int32_t& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Set(uint32_t aKey, int64_t aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Get(uint32_t aKey, int64_t& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Set(uint32_t aKey, const Size& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Get(uint32_t aKey, Size& aValue) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Set(uint32_t aKey, const nsTArray<Region>& aRegions) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Get(uint32_t aKey, nsTArray<Region>& aRegions) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult SetLocation(const Position& aLocation) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Get(uint32_t aKey, nsTArray<Size>& aSizes) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) MOZ_OVERRIDE { return NS_ERROR_FAILURE; }
nsresult PushParameters() { return NS_ERROR_FAILURE; }
nsresult PullParameters() { return NS_ERROR_FAILURE; }
protected:
~nsFallbackCameraControl();
~FallbackCameraControl();
nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream);
nsresult StartPreviewImpl(StartPreviewTask* aStartPreview);
nsresult StopPreviewImpl(StopPreviewTask* aStopPreview);
nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus);
nsresult TakePictureImpl(TakePictureTask* aTakePicture);
nsresult StartRecordingImpl(StartRecordingTask* aStartRecording);
nsresult StopRecordingImpl(StopRecordingTask* aStopRecording);
nsresult PushParametersImpl();
nsresult PullParametersImpl();
already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl();
virtual nsresult StartPreviewImpl() { return NS_ERROR_FAILURE; }
virtual nsresult StopPreviewImpl() { return NS_ERROR_FAILURE; }
virtual nsresult AutoFocusImpl(bool aCancelExistingCall) { return NS_ERROR_FAILURE; }
virtual nsresult TakePictureImpl() { return NS_ERROR_FAILURE; }
virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
const StartRecordingOptions* aOptions = nullptr)
{ return NS_ERROR_FAILURE; }
virtual nsresult StopRecordingImpl() { return NS_ERROR_FAILURE; }
virtual nsresult PushParametersImpl() { return NS_ERROR_FAILURE; }
virtual nsresult PullParametersImpl() { return NS_ERROR_FAILURE; }
virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() { return nullptr; }
private:
nsFallbackCameraControl(const nsFallbackCameraControl&) MOZ_DELETE;
nsFallbackCameraControl& operator=(const nsFallbackCameraControl&) MOZ_DELETE;
FallbackCameraControl(const FallbackCameraControl&) MOZ_DELETE;
FallbackCameraControl& operator=(const FallbackCameraControl&) MOZ_DELETE;
};
/**
* Stub implementation of the DOM-facing camera control constructor.
*
* This should never get called--it exists to keep the linker happy; if
* implemented, it should construct (e.g.) nsFallbackCameraControl and
* store a reference in the 'mCameraControl' member (which is why it is
* defined here).
*/
nsDOMCameraControl::nsDOMCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, nsPIDOMWindow* aWindow) :
mWindow(aWindow)
{
MOZ_ASSERT(aWindow, "shouldn't be created with null window!");
SetIsDOMBinding();
}
/**
* Stub implemetations of the fallback camera control.
*
* None of these should ever get called--they exist to keep the linker happy,
* and may be used as templates for new camera support classes.
*/
nsFallbackCameraControl::nsFallbackCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId)
: CameraControlImpl(aCameraId, aCameraThread, aWindowId)
{
}
nsFallbackCameraControl::~nsFallbackCameraControl()
{
}
const char*
nsFallbackCameraControl::GetParameter(const char* aKey)
{
return nullptr;
}
const char*
nsFallbackCameraControl::GetParameterConstChar(uint32_t aKey)
{
return nullptr;
}
double
nsFallbackCameraControl::GetParameterDouble(uint32_t aKey)
{
return NAN;
}
int32_t
nsFallbackCameraControl::GetParameterInt32(uint32_t aKey)
{
return 0;
}
void
nsFallbackCameraControl::GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions)
{
}
void
nsFallbackCameraControl::GetParameter(uint32_t aKey, idl::CameraSize& aSize)
{
}
void
nsFallbackCameraControl::SetParameter(const char* aKey, const char* aValue)
{
}
void
nsFallbackCameraControl::SetParameter(uint32_t aKey, const char* aValue)
{
}
void
nsFallbackCameraControl::SetParameter(uint32_t aKey, double aValue)
{
}
void
nsFallbackCameraControl::SetParameter(uint32_t aKey, const nsTArray<idl::CameraRegion>& aRegions)
{
}
void
nsFallbackCameraControl::SetParameter(uint32_t aKey, const idl::CameraSize& aSize)
{
}
nsresult
nsFallbackCameraControl::PushParameters()
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsFallbackCameraControl::GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsFallbackCameraControl::StartPreviewImpl(StartPreviewTask* aGetPreviewStream)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsFallbackCameraControl::StopPreviewImpl(StopPreviewTask* aGetPreviewStream)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsFallbackCameraControl::AutoFocusImpl(AutoFocusTask* aAutoFocus)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsFallbackCameraControl::TakePictureImpl(TakePictureTask* aTakePicture)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsFallbackCameraControl::StartRecordingImpl(StartRecordingTask* aStartRecording)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsFallbackCameraControl::StopRecordingImpl(StopRecordingTask* aStopRecording)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsFallbackCameraControl::PushParametersImpl()
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsFallbackCameraControl::PullParametersImpl()
{
return NS_ERROR_NOT_IMPLEMENTED;
}
nsresult
nsFallbackCameraControl::GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
already_AddRefed<RecorderProfileManager>
nsFallbackCameraControl::GetRecorderProfileManagerImpl()
{
return nullptr;
}

View File

@ -2,27 +2,31 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "DOMCameraManager.h"
#include "mozilla/ErrorResult.h"
#include "ICameraControl.h"
using namespace mozilla;
// From nsDOMCameraManager.
// From ICameraControl.
nsresult
nsDOMCameraManager::GetNumberOfCameras(int32_t& aDeviceCount)
ICameraControl::GetNumberOfCameras(int32_t& aDeviceCount)
{
return NS_ERROR_NOT_IMPLEMENTED;
};
nsresult
nsDOMCameraManager::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName)
ICameraControl::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
void
nsDOMCameraManager::GetListOfCameras(nsTArray<nsString>& aList, ErrorResult& aRv)
nsresult
ICameraControl::GetListOfCameras(nsTArray<nsString>& aList)
{
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
return NS_ERROR_NOT_IMPLEMENTED;
}
already_AddRefed<ICameraControl>
ICameraControl::Create(uint32_t aCameraId, const Configuration* aInitialConfig)
{
return nullptr;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2012 Mozilla Foundation
* Copyright (C) 2012-2013 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -18,26 +18,26 @@
#define DOM_CAMERA_GONKCAMERACONTROL_H
#include "base/basictypes.h"
#include "prrwlock.h"
#include <media/MediaProfiles.h>
#include "mozilla/ReentrantMonitor.h"
#include "DeviceStorage.h"
#include "nsIDOMCameraManager.h"
#include "DOMCameraControl.h"
#include "CameraControlImpl.h"
#include "CameraCommon.h"
#include "GonkRecorder.h"
#include "GonkCameraHwMgr.h"
#include "GonkCameraParameters.h"
namespace android {
class GonkCameraHardware;
class MediaProfiles;
class GonkRecorder;
class GonkCameraHardware;
class MediaProfiles;
class GonkRecorder;
}
namespace mozilla {
namespace layers {
class GraphicBufferLocked;
class GraphicBufferLocked;
class ImageContainer;
}
class GonkRecorderProfile;
@ -46,89 +46,111 @@ class GonkRecorderProfileManager;
class nsGonkCameraControl : public CameraControlImpl
{
public:
nsGonkCameraControl(uint32_t aCameraId, nsIThread* aCameraThread, nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
void DispatchInit(nsDOMCameraControl* aDOMCameraControl, nsICameraGetCameraCallback* onSuccess, nsICameraErrorCallback* onError, uint64_t aWindowId);
nsresult Init();
nsGonkCameraControl(uint32_t aCameraId);
nsresult Init(const Configuration* aInitialConfig);
nsresult InitImpl();
void OnAutoFocusComplete(bool aSuccess);
void OnTakePictureComplete(uint8_t* aData, uint32_t aLength);
void OnTakePictureError();
void OnNewPreviewFrame(layers::GraphicBufferLocked* aBuffer);
void OnRecorderEvent(int msg, int ext1, int ext2);
void OnError(CameraControlListener::CameraErrorContext aWhere,
CameraControlListener::CameraError aError);
virtual nsresult Set(uint32_t aKey, const nsAString& aValue) MOZ_OVERRIDE;
virtual nsresult Get(uint32_t aKey, nsAString& aValue) MOZ_OVERRIDE;
virtual nsresult Set(uint32_t aKey, double aValue) MOZ_OVERRIDE;
virtual nsresult Get(uint32_t aKey, double& aValue) MOZ_OVERRIDE;
virtual nsresult Set(uint32_t aKey, int32_t aValue) MOZ_OVERRIDE;
virtual nsresult Get(uint32_t aKey, int32_t& aValue) MOZ_OVERRIDE;
virtual nsresult Set(uint32_t aKey, int64_t aValue) MOZ_OVERRIDE;
virtual nsresult Get(uint32_t aKey, int64_t& aValue) MOZ_OVERRIDE;
virtual nsresult Set(uint32_t aKey, const Size& aValue) MOZ_OVERRIDE;
virtual nsresult Get(uint32_t aKey, Size& aValue) MOZ_OVERRIDE;
virtual nsresult Set(uint32_t aKey, const nsTArray<Region>& aRegions) MOZ_OVERRIDE;
virtual nsresult Get(uint32_t aKey, nsTArray<Region>& aRegions) MOZ_OVERRIDE;
virtual nsresult SetLocation(const Position& aLocation) MOZ_OVERRIDE;
virtual nsresult Get(uint32_t aKey, nsTArray<Size>& aSizes) MOZ_OVERRIDE;
virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) MOZ_OVERRIDE;
virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) MOZ_OVERRIDE;
const char* GetParameter(const char* aKey);
const char* GetParameterConstChar(uint32_t aKey);
double GetParameterDouble(uint32_t aKey);
int32_t GetParameterInt32(uint32_t aKey);
void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions);
void GetParameter(uint32_t aKey, nsTArray<idl::CameraSize>& aSizes);
void GetParameter(uint32_t aKey, idl::CameraSize& aSize);
void SetParameter(const char* aKey, const char* aValue);
void SetParameter(uint32_t aKey, const char* aValue);
void SetParameter(uint32_t aKey, double aValue);
void SetParameter(uint32_t aKey, const nsTArray<idl::CameraRegion>& aRegions);
void SetParameter(uint32_t aKey, int aValue);
void SetParameter(uint32_t aKey, const idl::CameraSize& aSize);
nsresult GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes);
nsresult PushParameters();
void AutoFocusComplete(bool aSuccess);
void TakePictureComplete(uint8_t* aData, uint32_t aLength);
void TakePictureError();
void HandleRecorderEvent(int msg, int ext1, int ext2);
nsresult PullParameters();
protected:
~nsGonkCameraControl();
nsresult GetPreviewStreamImpl(GetPreviewStreamTask* aGetPreviewStream);
nsresult StartPreviewImpl(StartPreviewTask* aStartPreview);
nsresult StopPreviewImpl(StopPreviewTask* aStopPreview);
nsresult StopPreviewInternal(bool aForced = false);
nsresult AutoFocusImpl(AutoFocusTask* aAutoFocus);
nsresult TakePictureImpl(TakePictureTask* aTakePicture);
nsresult StartRecordingImpl(StartRecordingTask* aStartRecording);
nsresult StopRecordingImpl(StopRecordingTask* aStopRecording);
nsresult PushParametersImpl();
nsresult PullParametersImpl();
nsresult GetPreviewStreamVideoModeImpl(GetPreviewStreamVideoModeTask* aGetPreviewStreamVideoMode);
nsresult ReleaseHardwareImpl(ReleaseHardwareTask* aReleaseHardware);
already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl();
using CameraControlImpl::OnNewPreviewFrame;
using CameraControlImpl::OnAutoFocusComplete;
using CameraControlImpl::OnTakePictureComplete;
using CameraControlImpl::OnConfigurationChange;
using CameraControlImpl::OnError;
virtual void BeginBatchParameterSet() MOZ_OVERRIDE;
virtual void EndBatchParameterSet() MOZ_OVERRIDE;
virtual nsresult SetConfigurationImpl(const Configuration& aConfig) MOZ_OVERRIDE;
nsresult SetConfigurationInternal(const Configuration& aConfig);
nsresult SetPictureConfiguration(const Configuration& aConfig);
nsresult SetVideoConfiguration(const Configuration& aConfig);
template<class T> nsresult SetAndPush(uint32_t aKey, const T& aValue);
virtual nsresult StartPreviewImpl() MOZ_OVERRIDE;
virtual nsresult StopPreviewImpl() MOZ_OVERRIDE;
virtual nsresult AutoFocusImpl(bool aCancelExistingCall) MOZ_OVERRIDE;
virtual nsresult TakePictureImpl() MOZ_OVERRIDE;
virtual nsresult StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescriptor,
const StartRecordingOptions* aOptions = nullptr) MOZ_OVERRIDE;
virtual nsresult StopRecordingImpl() MOZ_OVERRIDE;
virtual nsresult PushParametersImpl() MOZ_OVERRIDE;
virtual nsresult PullParametersImpl() MOZ_OVERRIDE;
virtual nsresult ReleaseHardwareImpl() MOZ_OVERRIDE;
virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() MOZ_OVERRIDE;
already_AddRefed<GonkRecorderProfileManager> GetGonkRecorderProfileManager();
nsresult SetupRecording(int aFd, int aRotation, int64_t aMaxFileSizeBytes, int64_t aMaxVideoLengthMs);
nsresult SetupVideoMode(const nsAString& aProfile);
void SetPreviewSize(uint32_t aWidth, uint32_t aHeight);
void SetThumbnailSize(uint32_t aWidth, uint32_t aHeight);
void UpdateThumbnailSize();
void SetPictureSize(uint32_t aWidth, uint32_t aHeight);
nsresult SetPreviewSize(const Size& aSize);
friend class SetPictureSize;
friend class SetThumbnailSize;
nsresult SetPictureSize(const Size& aSize);
nsresult SetPictureSizeImpl(const Size& aSize);
nsresult SetThumbnailSize(const Size& aSize);
nsresult UpdateThumbnailSize();
nsresult SetThumbnailSizeImpl(const Size& aSize);
int32_t RationalizeRotation(int32_t aRotation);
android::sp<android::GonkCameraHardware> mCameraHw;
double mExposureCompensationMin;
double mExposureCompensationStep;
bool mDeferConfigUpdate;
PRRWLock* mRwLock;
android::CameraParameters mParams;
uint32_t mWidth;
uint32_t mHeight;
uint32_t mLastPictureWidth;
uint32_t mLastPictureHeight;
uint32_t mLastThumbnailWidth;
uint32_t mLastThumbnailHeight;
enum {
PREVIEW_FORMAT_UNKNOWN,
PREVIEW_FORMAT_YUV420P,
PREVIEW_FORMAT_YUV420SP
};
uint32_t mFormat;
Size mLastPictureSize;
Size mLastThumbnailSize;
Size mLastRecorderSize;
uint32_t mPreviewFps;
bool mResumePreviewAfterTakingPicture;
uint32_t mFps;
uint32_t mDiscardedFrameCount;
Atomic<uint32_t> mDeferConfigUpdate;
GonkCameraParameters mParams;
nsRefPtr<mozilla::layers::ImageContainer> mImageContainer;
android::MediaProfiles* mMediaProfiles;
nsRefPtr<android::GonkRecorder> mRecorder;
// camcorder profile settings for the desired quality level
// Camcorder profile settings for the desired quality level
nsRefPtr<GonkRecorderProfileManager> mProfileManager;
nsRefPtr<GonkRecorderProfile> mRecorderProfile;
nsRefPtr<DeviceStorageFile> mVideoFile;
nsString mFileFormat;
// Guards against calling StartPreviewImpl() while in OnTakePictureComplete().
ReentrantMonitor mReentrantMonitor;
private:
nsGonkCameraControl(const nsGonkCameraControl&) MOZ_DELETE;
@ -136,12 +158,14 @@ private:
};
// camera driver callbacks
void ReceiveImage(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength);
void ReceiveImageError(nsGonkCameraControl* gc);
void AutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess);
void ReceiveFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer);
void OnTakePictureComplete(nsGonkCameraControl* gc, uint8_t* aData, uint32_t aLength);
void OnTakePictureError(nsGonkCameraControl* gc);
void OnAutoFocusComplete(nsGonkCameraControl* gc, bool aSuccess);
void OnNewPreviewFrame(nsGonkCameraControl* gc, layers::GraphicBufferLocked* aBuffer);
void OnShutter(nsGonkCameraControl* gc);
void OnClosed(nsGonkCameraControl* gc);
void OnError(nsGonkCameraControl* gc, CameraControlListener::CameraError aError,
int32_t aArg1, int32_t aArg2);
} // namespace mozilla

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2012 Mozilla Foundation
* Copyright (C) 2012-2013 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -14,13 +14,14 @@
* limitations under the License.
*/
#include "GonkCameraHwMgr.h"
#include <binder/IPCThreadState.h>
#include <sys/system_properties.h>
#include "base/basictypes.h"
#include "nsDebug.h"
#include "GonkCameraControl.h"
#include "GonkCameraHwMgr.h"
#include "GonkNativeWindow.h"
#include "CameraCommon.h"
@ -28,39 +29,16 @@ using namespace mozilla;
using namespace mozilla::layers;
using namespace android;
#if GIHM_TIMING_RECEIVEFRAME
#define INCLUDE_TIME_H 1
#endif
#if GIHM_TIMING_OVERALL
#define INCLUDE_TIME_H 1
#endif
#if INCLUDE_TIME_H
#include <time.h>
static __inline void timespecSubtract(struct timespec* a, struct timespec* b)
{
// a = b - a
if (b->tv_nsec < a->tv_nsec) {
b->tv_nsec += 1000000000;
b->tv_sec -= 1;
}
a->tv_nsec = b->tv_nsec - a->tv_nsec;
a->tv_sec = b->tv_sec - a->tv_sec;
}
#endif
GonkCameraHardware::GonkCameraHardware(mozilla::nsGonkCameraControl* aTarget, uint32_t aCameraId, const sp<Camera>& aCamera)
: mCameraId(aCameraId)
, mClosing(false)
, mMonitor("GonkCameraHardware.Monitor")
, 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 );
DOM_CAMERA_LOGT("%s:%d : this=%p (aTarget=%p)\n", __func__, __LINE__, (void*)this, (void*)aTarget);
Init();
}
@ -75,7 +53,7 @@ GonkCameraHardware::OnNewFrame()
DOM_CAMERA_LOGW("received null frame");
return;
}
ReceiveFrame(mTarget, buffer);
OnNewPreviewFrame(mTarget, buffer);
}
// Android data callback
@ -93,9 +71,9 @@ GonkCameraHardware::postData(int32_t aMsgType, const sp<IMemory>& aDataPtr, came
case CAMERA_MSG_COMPRESSED_IMAGE:
if (aDataPtr != nullptr) {
ReceiveImage(mTarget, (uint8_t*)aDataPtr->pointer(), aDataPtr->size());
OnTakePictureComplete(mTarget, static_cast<uint8_t*>(aDataPtr->pointer()), aDataPtr->size());
} else {
ReceiveImageError(mTarget);
OnTakePictureError(mTarget);
}
break;
@ -109,27 +87,23 @@ GonkCameraHardware::postData(int32_t aMsgType, const sp<IMemory>& aDataPtr, came
void
GonkCameraHardware::notify(int32_t aMsgType, int32_t ext1, int32_t ext2)
{
bool bSuccess;
if (mClosing) {
return;
}
switch (aMsgType) {
case CAMERA_MSG_FOCUS:
if (ext1) {
DOM_CAMERA_LOGI("Autofocus complete");
bSuccess = true;
} else {
DOM_CAMERA_LOGW("Autofocus failed");
bSuccess = false;
}
AutoFocusComplete(mTarget, bSuccess);
OnAutoFocusComplete(mTarget, !!ext1);
break;
case CAMERA_MSG_SHUTTER:
OnShutter(mTarget);
break;
case CAMERA_MSG_ERROR:
OnError(mTarget, CameraControlListener::kErrorServiceFailed, ext1, ext2);
break;
default:
DOM_CAMERA_LOGE("Unhandled notify callback event %d\n", aMsgType);
break;
@ -295,6 +269,13 @@ GonkCameraHardware::CancelTakePicture()
DOM_CAMERA_LOGW("%s: android::Camera do not provide this capability\n", __func__);
}
int
GonkCameraHardware::PushParameters(const GonkCameraParameters& aParams)
{
const String8 s = aParams.Flatten();
return mCamera->setParameters(s);
}
int
GonkCameraHardware::PushParameters(const CameraParameters& aParams)
{
@ -302,6 +283,13 @@ GonkCameraHardware::PushParameters(const CameraParameters& aParams)
return mCamera->setParameters(s);
}
nsresult
GonkCameraHardware::PullParameters(GonkCameraParameters& aParams)
{
const String8 s = mCamera->getParameters();
return aParams.Unflatten(s);
}
void
GonkCameraHardware::PullParameters(CameraParameters& aParams)
{

View File

@ -27,15 +27,12 @@
#include "GonkCameraListener.h"
#include "GonkNativeWindow.h"
#include "GonkCameraParameters.h"
#include "mozilla/ReentrantMonitor.h"
// config
#define GIHM_TIMING_RECEIVEFRAME 0
#define GIHM_TIMING_OVERALL 1
namespace mozilla {
class nsGonkCameraControl;
class GonkCameraParameters;
}
namespace android {
@ -86,7 +83,9 @@ public:
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();
@ -98,15 +97,10 @@ protected:
uint32_t mCameraId;
bool mClosing;
mozilla::ReentrantMonitor mMonitor;
uint32_t mNumFrames;
sp<Camera> mCamera;
mozilla::nsGonkCameraControl* mTarget;
sp<GonkNativeWindow> mNativeWindow;
#if GIHM_TIMING_OVERALL
struct timespec mStart;
struct timespec mAutoFocusStart;
#endif
sp<GonkCameraListener> mListener;
bool mInitialized;
int mRawSensorOrientation;

View File

@ -16,35 +16,36 @@
#include <camera/Camera.h>
#include "GonkCameraControl.h"
#include "DOMCameraManager.h"
#include "CameraCommon.h"
#include "mozilla/ErrorResult.h"
#include "GonkCameraControl.h"
#include "ICameraControl.h"
using namespace mozilla;
// From nsDOMCameraManager, but gonk-specific!
// From ICameraControl, gonk-specific management functions
nsresult
nsDOMCameraManager::GetNumberOfCameras(int32_t& aDeviceCount)
ICameraControl::GetNumberOfCameras(int32_t& aDeviceCount)
{
aDeviceCount = android::Camera::getNumberOfCameras();
return NS_OK;
}
nsresult
nsDOMCameraManager::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName)
ICameraControl::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName)
{
int32_t count = android::Camera::getNumberOfCameras();
int32_t deviceNum = static_cast<int32_t>(aDeviceNum);
DOM_CAMERA_LOGI("GetCameraName : getNumberOfCameras() returned %d\n", count);
if (aDeviceNum > count) {
DOM_CAMERA_LOGE("GetCameraName : invalid device number");
if (deviceNum < 0 || deviceNum > count) {
DOM_CAMERA_LOGE("GetCameraName : invalid device number (%u)\n", aDeviceNum);
return NS_ERROR_NOT_AVAILABLE;
}
android::CameraInfo info;
int rv = android::Camera::getCameraInfo(aDeviceNum, &info);
int rv = android::Camera::getCameraInfo(deviceNum, &info);
if (rv != 0) {
DOM_CAMERA_LOGE("GetCameraName : get_camera_info(%d) failed: %d\n", aDeviceNum, rv);
DOM_CAMERA_LOGE("GetCameraName : get_camera_info(%d) failed: %d\n", deviceNum, rv);
return NS_ERROR_NOT_AVAILABLE;
}
@ -59,27 +60,27 @@ nsDOMCameraManager::GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName)
default:
aDeviceName.Assign("extra-camera-");
aDeviceName.AppendInt(aDeviceNum);
aDeviceName.AppendInt(deviceNum);
break;
}
return NS_OK;
}
void
nsDOMCameraManager::GetListOfCameras(nsTArray<nsString>& aList, ErrorResult& aRv)
nsresult
ICameraControl::GetListOfCameras(nsTArray<nsString>& aList)
{
int32_t count = android::Camera::getNumberOfCameras();
if (count <= 0) {
return;
}
DOM_CAMERA_LOGI("getListOfCameras : getNumberOfCameras() returned %d\n", count);
if (count <= 0) {
return NS_OK;
}
// Allocate 2 extra slots to reserve space for 'front' and 'back' cameras
// at the front of the array--we will collapse any empty slots below.
aList.SetLength(2);
uint32_t extraIdx = 2;
bool gotFront = false, gotBack = false;
bool gotFront = false;
bool gotBack = false;
while (count--) {
nsCString cameraName;
nsresult result = GetCameraName(count, cameraName);
@ -104,8 +105,28 @@ nsDOMCameraManager::GetListOfCameras(nsTArray<nsString>& aList, ErrorResult& aRv
if (!gotFront) {
aList.RemoveElementAt(1);
}
if (!gotBack) {
aList.RemoveElementAt(0);
}
return NS_OK;
}
// implementation-specific camera factory
already_AddRefed<ICameraControl>
ICameraControl::Create(uint32_t aCameraId, const Configuration* aInitialConfig)
{
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<nsGonkCameraControl> control = new nsGonkCameraControl(aCameraId);
nsresult rv = control->Init(aInitialConfig);
NS_ENSURE_SUCCESS(rv, nullptr);
return control.forget();
}

View File

@ -0,0 +1,620 @@
/*
* Copyright (C) 2013 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 "GonkCameraParameters.h"
#include "camera/CameraParameters.h"
#include "ICameraControl.h"
#include "CameraCommon.h"
using namespace mozilla;
using namespace android;
/* static */ const char*
GonkCameraParameters::Parameters::GetTextKey(uint32_t aKey)
{
switch (aKey) {
case CAMERA_PARAM_PREVIEWSIZE:
return KEY_PREVIEW_SIZE;
case CAMERA_PARAM_PREVIEWFORMAT:
return KEY_PREVIEW_FORMAT;
case CAMERA_PARAM_PREVIEWFRAMERATE:
return KEY_PREVIEW_FRAME_RATE;
case CAMERA_PARAM_EFFECT:
return KEY_EFFECT;
case CAMERA_PARAM_WHITEBALANCE:
return KEY_WHITE_BALANCE;
case CAMERA_PARAM_SCENEMODE:
return KEY_SCENE_MODE;
case CAMERA_PARAM_FLASHMODE:
return KEY_FLASH_MODE;
case CAMERA_PARAM_FOCUSMODE:
return KEY_FOCUS_MODE;
case CAMERA_PARAM_ZOOM:
return KEY_ZOOM;
case CAMERA_PARAM_METERINGAREAS:
return KEY_METERING_AREAS;
case CAMERA_PARAM_FOCUSAREAS:
return KEY_FOCUS_AREAS;
case CAMERA_PARAM_FOCALLENGTH:
return KEY_FOCAL_LENGTH;
case CAMERA_PARAM_FOCUSDISTANCENEAR:
return KEY_FOCUS_DISTANCES;
case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM:
return KEY_FOCUS_DISTANCES;
case CAMERA_PARAM_FOCUSDISTANCEFAR:
return KEY_FOCUS_DISTANCES;
case CAMERA_PARAM_EXPOSURECOMPENSATION:
return KEY_EXPOSURE_COMPENSATION;
case CAMERA_PARAM_PICTURESIZE:
return KEY_PICTURE_SIZE;
case CAMERA_PARAM_THUMBNAILQUALITY:
return KEY_JPEG_THUMBNAIL_QUALITY;
case CAMERA_PARAM_PICTURE_SIZE:
return KEY_PICTURE_SIZE;
case CAMERA_PARAM_PICTURE_FILEFORMAT:
return KEY_PICTURE_FORMAT;
case CAMERA_PARAM_PICTURE_ROTATION:
return KEY_ROTATION;
case CAMERA_PARAM_PICTURE_DATETIME:
// Not every platform defines a KEY_EXIF_DATETIME;
// for those that don't, we use the raw string key, and if the platform
// doesn't support it, it will be ignored.
//
// See bug 832494.
return "exif-datetime";
case CAMERA_PARAM_VIDEOSIZE:
return KEY_VIDEO_SIZE;
case CAMERA_PARAM_SUPPORTED_PREVIEWSIZES:
return KEY_SUPPORTED_PREVIEW_SIZES;
case CAMERA_PARAM_SUPPORTED_PICTURESIZES:
return KEY_SUPPORTED_PICTURE_SIZES;
case CAMERA_PARAM_SUPPORTED_VIDEOSIZES:
return KEY_SUPPORTED_VIDEO_SIZES;
case CAMERA_PARAM_SUPPORTED_PICTUREFORMATS:
return KEY_SUPPORTED_PICTURE_FORMATS;
case CAMERA_PARAM_SUPPORTED_WHITEBALANCES:
return KEY_SUPPORTED_WHITE_BALANCE;
case CAMERA_PARAM_SUPPORTED_SCENEMODES:
return KEY_SUPPORTED_SCENE_MODES;
case CAMERA_PARAM_SUPPORTED_EFFECTS:
return KEY_SUPPORTED_EFFECTS;
case CAMERA_PARAM_SUPPORTED_FLASHMODES:
return KEY_SUPPORTED_FLASH_MODES;
case CAMERA_PARAM_SUPPORTED_FOCUSMODES:
return KEY_SUPPORTED_FOCUS_MODES;
case CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS:
return KEY_MAX_NUM_FOCUS_AREAS;
case CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS:
return KEY_MAX_NUM_METERING_AREAS;
case CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION:
return KEY_MIN_EXPOSURE_COMPENSATION;
case CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION:
return KEY_MAX_EXPOSURE_COMPENSATION;
case CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP:
return KEY_EXPOSURE_COMPENSATION_STEP;
case CAMERA_PARAM_SUPPORTED_ZOOM:
return KEY_ZOOM_SUPPORTED;
case CAMERA_PARAM_SUPPORTED_ZOOMRATIOS:
return KEY_ZOOM_RATIOS;
case CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES:
return KEY_SUPPORTED_JPEG_THUMBNAIL_SIZES;
default:
DOM_CAMERA_LOGE("Unhandled camera parameter value %u\n", aKey);
return nullptr;
}
}
GonkCameraParameters::GonkCameraParameters()
: mLock(PR_NewRWLock(PR_RWLOCK_RANK_NONE, "GonkCameraParameters.Lock"))
, mDirty(false)
, mInitialized(false)
{
MOZ_COUNT_CTOR(GonkCameraParameters);
if (!mLock) {
MOZ_CRASH("OOM getting new PRRWLock");
}
}
GonkCameraParameters::~GonkCameraParameters()
{
MOZ_COUNT_DTOR(GonkCameraParameters);
if (mLock) {
PR_DestroyRWLock(mLock);
mLock = nullptr;
}
}
// Any members that need to be initialized on the first parameter pull
// need to get handled in here.
nsresult
GonkCameraParameters::Initialize()
{
nsresult rv;
rv = GetImpl(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION, mExposureCompensationMin);
if (NS_FAILED(rv)) {
return rv;
}
rv = GetImpl(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP, mExposureCompensationStep);
if (NS_FAILED(rv)) {
return rv;
}
mInitialized = true;
return NS_OK;
}
// Handle nsAStrings
nsresult
GonkCameraParameters::SetTranslated(uint32_t aKey, const nsAString& aValue)
{
return SetImpl(aKey, NS_ConvertUTF16toUTF8(aValue).get());
}
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, nsAString& aValue)
{
const char* val;
nsresult rv = GetImpl(aKey, val);
if (NS_FAILED(rv)) {
return rv;
}
aValue.AssignASCII(val);
return NS_OK;
}
// Handle ICameraControl::Sizes
nsresult
GonkCameraParameters::SetTranslated(uint32_t aKey, const ICameraControl::Size& aSize)
{
if (aSize.width > INT_MAX || aSize.height > INT_MAX) {
// AOSP can only handle signed ints.
DOM_CAMERA_LOGE("Camera parameter aKey=%d out of bounds (width=%u, height=%u)\n",
aSize.width, aSize.height);
return NS_ERROR_INVALID_ARG;
}
nsresult rv;
switch (aKey) {
case CAMERA_PARAM_THUMBNAILSIZE:
// This is a special case--for some reason the thumbnail size
// is accessed as two separate values instead of a tuple.
// XXXmikeh - make this restore the original values on error
rv = SetImpl(Parameters::KEY_JPEG_THUMBNAIL_WIDTH, static_cast<int>(aSize.width));
if (NS_SUCCEEDED(rv)) {
rv = SetImpl(Parameters::KEY_JPEG_THUMBNAIL_HEIGHT, static_cast<int>(aSize.height));
}
break;
case CAMERA_PARAM_VIDEOSIZE:
// "record-size" is probably deprecated in later ICS;
// might need to set "video-size" instead of "record-size";
// for the time being, set both. See bug 795332.
rv = SetImpl("record-size", nsPrintfCString("%ux%u", aSize.width, aSize.height).get());
if (NS_FAILED(rv)) {
break;
}
// intentional fallthrough
default:
rv = SetImpl(aKey, nsPrintfCString("%ux%u", aSize.width, aSize.height).get());
break;
}
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGE("Camera parameter aKey=%d failed to set (0x%x)\n", aKey, rv);
}
return rv;
}
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, ICameraControl::Size& aSize)
{
nsresult rv;
if (aKey == CAMERA_PARAM_THUMBNAILSIZE) {
int width;
int height;
rv = GetImpl(Parameters::KEY_JPEG_THUMBNAIL_WIDTH, width);
if (NS_FAILED(rv) || width < 0) {
return NS_ERROR_FAILURE;
}
rv = GetImpl(Parameters::KEY_JPEG_THUMBNAIL_HEIGHT, height);
if (NS_FAILED(rv) || height < 0) {
return NS_ERROR_FAILURE;
}
aSize.width = static_cast<uint32_t>(width);
aSize.height = static_cast<uint32_t>(height);
return NS_OK;
}
const char* value;
rv = GetImpl(aKey, value);
if (NS_FAILED(rv) || !value || *value == '\0') {
DOM_CAMERA_LOGW("Camera parameter aKey=%d not available (0x%x)\n", aKey, rv);
return NS_ERROR_NOT_AVAILABLE;
}
if (sscanf(value, "%ux%u", &aSize.width, &aSize.height) != 2) {
DOM_CAMERA_LOGE("Camera parameter aKey=%d size tuple '%s' is invalid\n", aKey, value);
return NS_ERROR_FAILURE;
}
return NS_OK;
}
// Handle arrays of ICameraControl::Regions
nsresult
GonkCameraParameters::SetTranslated(uint32_t aKey, const nsTArray<ICameraControl::Region>& aRegions)
{
uint32_t length = aRegions.Length();
if (!length) {
// This tells the camera driver to revert to automatic regioning.
return SetImpl(aKey, "(0,0,0,0,0)");
}
nsCString s;
for (uint32_t i = 0; i < length; ++i) {
const ICameraControl::Region* r = &aRegions[i];
s.AppendPrintf("(%d,%d,%d,%d,%d),", r->top, r->left, r->bottom, r->right, r->weight);
}
// remove the trailing comma
s.Trim(",", false, true, true);
return SetImpl(aKey, s.get());
}
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<ICameraControl::Region>& aRegions)
{
aRegions.Clear();
const char* value;
nsresult rv = GetImpl(aKey, value);
if (NS_FAILED(rv) || !value || *value == '\0') {
return NS_ERROR_FAILURE;
}
const char* p = value;
uint32_t count = 1;
// count the number of regions in the string
while ((p = strstr(p, "),("))) {
++count;
p += 3;
}
aRegions.SetCapacity(count);
ICameraControl::Region* r;
// parse all of the region sets
uint32_t i;
for (i = 0, p = value; p && i < count; ++i, p = strchr(p + 1, '(')) {
r = aRegions.AppendElement();
if (sscanf(p, "(%d,%d,%d,%d,%u)", &r->top, &r->left, &r->bottom, &r->right, &r->weight) != 5) {
DOM_CAMERA_LOGE("%s:%d : region tuple has bad format: '%s'\n", __func__, __LINE__, p);
aRegions.Clear();
return NS_ERROR_FAILURE;
}
}
return NS_OK;
}
// Handle ICameraControl::Positions
nsresult
GonkCameraParameters::SetTranslated(uint32_t aKey, const ICameraControl::Position& aPosition)
{
MOZ_ASSERT(aKey == CAMERA_PARAM_PICTURE_LOCATION);
// Add any specified location information -- we don't care if these fail.
if (!isnan(aPosition.latitude)) {
DOM_CAMERA_LOGI("setting picture latitude to %lf\n", aPosition.latitude);
SetImpl(Parameters::KEY_GPS_LATITUDE, nsPrintfCString("%lf", aPosition.latitude).get());
}
if (!isnan(aPosition.longitude)) {
DOM_CAMERA_LOGI("setting picture longitude to %lf\n", aPosition.longitude);
SetImpl(Parameters::KEY_GPS_LONGITUDE, nsPrintfCString("%lf", aPosition.longitude).get());
}
if (!isnan(aPosition.altitude)) {
DOM_CAMERA_LOGI("setting picture altitude to %lf\n", aPosition.altitude);
SetImpl(Parameters::KEY_GPS_ALTITUDE, nsPrintfCString("%lf", aPosition.altitude).get());
}
if (!isnan(aPosition.timestamp)) {
DOM_CAMERA_LOGI("setting picture timestamp to %lf\n", aPosition.timestamp);
SetImpl(Parameters::KEY_GPS_TIMESTAMP, nsPrintfCString("%lf", aPosition.timestamp).get());
}
return NS_OK;
}
// Handle int64_ts
nsresult
GonkCameraParameters::SetTranslated(uint32_t aKey, const int64_t& aValue)
{
if (aKey == CAMERA_PARAM_PICTURE_DATETIME) {
// Add the non-GPS timestamp. The EXIF date/time field is formatted as
// "YYYY:MM:DD HH:MM:SS", without room for a time-zone; as such, the time
// is meant to be stored as a local time. Since we are given seconds from
// Epoch GMT, we use localtime_r() to handle the conversion.
time_t time = aValue;
if (time != aValue) {
DOM_CAMERA_LOGE("picture date/time '%llu' is too far in the future\n", aValue);
return NS_ERROR_INVALID_ARG;
}
struct tm t;
if (!localtime_r(&time, &t)) {
DOM_CAMERA_LOGE("picture date/time couldn't be converted to local time: (%d) %s\n", errno, strerror(errno));
return NS_ERROR_FAILURE;
}
char dateTime[20];
if (!strftime(dateTime, sizeof(dateTime), "%Y:%m:%d %T", &t)) {
DOM_CAMERA_LOGE("picture date/time couldn't be converted to string\n");
return NS_ERROR_FAILURE;
}
DOM_CAMERA_LOGI("setting picture date/time to %s\n", dateTime);
return SetImpl(CAMERA_PARAM_PICTURE_DATETIME, dateTime);
}
// You can't actually pass 64-bit parameters to Gonk. :(
int32_t v = static_cast<int32_t>(aValue);
if (static_cast<int64_t>(v) != aValue) {
return NS_ERROR_INVALID_ARG;;
}
return SetImpl(aKey, v);
}
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, int64_t& aValue)
{
int val;
nsresult rv = GetImpl(aKey, val);
if (NS_FAILED(rv)) {
return rv;
}
aValue = val;
return NS_OK;
}
// Handle doubles
nsresult
GonkCameraParameters::SetTranslated(uint32_t aKey, const double& aValue)
{
if (aKey == CAMERA_PARAM_EXPOSURECOMPENSATION) {
/**
* Convert from real value to a Gonk index, round
* to the nearest step; index is 1-based.
*/
int index =
(aValue - mExposureCompensationMin + mExposureCompensationStep / 2) /
mExposureCompensationStep + 1;
DOM_CAMERA_LOGI("Exposure compensation = %f --> index = %d\n", aValue, index);
return SetImpl(CAMERA_PARAM_EXPOSURECOMPENSATION, index);
}
return SetImpl(aKey, aValue);
}
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, double& aValue)
{
double val;
int index = 0;
double focusDistance[3];
const char* s;
nsresult rv;
switch (aKey) {
case CAMERA_PARAM_ZOOM:
rv = GetImpl(CAMERA_PARAM_ZOOM, val);
if (NS_SUCCEEDED(rv)) {
val /= 100.0;
} else {
// return 1x when zooming is not supported
val = 1.0;
}
break;
/**
* The gonk camera parameters API only exposes one focus distance property
* that contains "Near,Optimum,Far" distances, in metres, where 'Far' may
* be 'Infinity'.
*/
case CAMERA_PARAM_FOCUSDISTANCEFAR:
++index;
// intentional fallthrough
case CAMERA_PARAM_FOCUSDISTANCEOPTIMUM:
++index;
// intentional fallthrough
case CAMERA_PARAM_FOCUSDISTANCENEAR:
rv = GetImpl(aKey, s);
if (NS_SUCCEEDED(rv)) {
if (sscanf(s, "%lf,%lf,%lf", &focusDistance[0], &focusDistance[1], &focusDistance[2]) == 3) {
val = focusDistance[index];
} else {
val = 0.0;
}
}
break;
case CAMERA_PARAM_EXPOSURECOMPENSATION:
rv = GetImpl(aKey, index);
if (NS_SUCCEEDED(rv)) {
if (!index) {
// NaN indicates automatic exposure compensation
val = NAN;
} else {
val = (index - 1) * mExposureCompensationStep + mExposureCompensationMin;
DOM_CAMERA_LOGI("index = %d --> compensation = %f\n", index, val);
}
}
break;
default:
rv = GetImpl(aKey, val);
break;
}
if (NS_SUCCEEDED(rv)) {
aValue = val;
}
return rv;
}
// Handle ints
nsresult
GonkCameraParameters::SetTranslated(uint32_t aKey, const int& aValue)
{
return SetImpl(aKey, aValue);
}
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, int& aValue)
{
return GetImpl(aKey, aValue);
}
// Handle uint32_ts -- Gonk only speaks int
nsresult
GonkCameraParameters::SetTranslated(uint32_t aKey, const uint32_t& aValue)
{
if (aValue > INT_MAX) {
return NS_ERROR_INVALID_ARG;
}
int val = static_cast<int>(aValue);
return SetImpl(aKey, val);
}
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, uint32_t& aValue)
{
int val;
nsresult rv = GetImpl(aKey, val);
if (NS_FAILED(rv)) {
return rv;
}
if (val < 0) {
return NS_ERROR_FAILURE;
}
aValue = val;
return NS_OK;
}
nsresult
ParseItem(const char* aStart, const char* aEnd, ICameraControl::Size* aItem)
{
if (sscanf(aStart, "%ux%u", &aItem->width, &aItem->height) == 2) {
return NS_OK;
}
DOM_CAMERA_LOGE("Size tuple has bad format: '%s'\n", __func__, __LINE__, aStart);
return NS_ERROR_FAILURE;
}
nsresult
ParseItem(const char* aStart, const char* aEnd, nsAString* aItem)
{
if (aEnd) {
aItem->AssignASCII(aStart, aEnd - aStart);
} else {
aItem->AssignASCII(aStart);
}
return NS_OK;
}
nsresult
ParseItem(const char* aStart, const char* aEnd, double* aItem)
{
if (sscanf(aStart, "%lf", aItem) == 1) {
return NS_OK;
}
return NS_ERROR_FAILURE;
}
template<class T> nsresult
GonkCameraParameters::GetListAsArray(uint32_t aKey, nsTArray<T>& aArray)
{
const char* p;
nsresult rv = GetImpl(aKey, p);
if (NS_FAILED(rv)) {
return rv;
}
if (!p) {
DOM_CAMERA_LOGW("Camera parameter %d not available (value is null)\n", aKey);
return NS_ERROR_NOT_AVAILABLE;
}
if (*p == '\0') {
DOM_CAMERA_LOGW("Camera parameter %d not available (value is empty string)\n", aKey);
return NS_ERROR_NOT_AVAILABLE;
}
aArray.Clear();
const char* comma;
while (p) {
T* v = aArray.AppendElement();
if (!v) {
aArray.Clear();
return NS_ERROR_OUT_OF_MEMORY;
}
comma = strchr(p, ',');
if (comma != p) {
rv = ParseItem(p, comma, v);
if (NS_FAILED(rv)) {
aArray.Clear();
return rv;
}
p = comma;
}
if (p) {
++p;
}
}
return NS_OK;
}
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<nsString>& aValues)
{
return GetListAsArray(aKey, aValues);
}
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<double>& aValues)
{
return GetListAsArray(aKey, aValues);
}
nsresult
GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray<ICameraControl::Size>& aSizes)
{
return GetListAsArray(aKey, aSizes);
}

View File

@ -0,0 +1,184 @@
/*
* Copyright (C) 2013 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_GONKCAMERAPARAMETERS_H
#define DOM_CAMERA_GONKCAMERAPARAMETERS_H
#include <math.h>
#include "camera/CameraParameters.h"
#include "nsTArray.h"
#include "nsString.h"
#include "AutoRwLock.h"
#include "nsPrintfCString.h"
#include "ICameraControl.h"
namespace mozilla {
class GonkCameraParameters
{
public:
GonkCameraParameters();
virtual ~GonkCameraParameters();
// IMPORTANT: This class is read and written by multiple threads --
// ALL public methods must hold mLock, for either reading or writing,
// for the life of their operation. Not doing so was the cause of
// bug 928856, which was -painful- to track down.
template<class T> nsresult
Set(uint32_t aKey, const T& aValue)
{
RwLockAutoEnterWrite lock(mLock);
nsresult rv = SetTranslated(aKey, aValue);
mDirty = mDirty || NS_SUCCEEDED(rv);
return rv;
}
template<class T> nsresult
Get(uint32_t aKey, T& aValue)
{
RwLockAutoEnterRead lock(mLock);
return GetTranslated(aKey, aValue);
}
bool
TestAndClearDirtyFlag()
{
bool dirty;
RwLockAutoEnterWrite lock(mLock);
dirty = mDirty;
mDirty = false;
return dirty;
}
android::String8
Flatten() const
{
RwLockAutoEnterRead lock(mLock);
return mParams.flatten();
}
nsresult
Unflatten(const android::String8& aFlatParameters)
{
RwLockAutoEnterWrite lock(mLock);
mParams.unflatten(aFlatParameters);
if (mInitialized) {
return NS_OK;
}
// We call Initialize() once when the parameter set is first loaded,
// to set up any constant values this class requires internally,
// e.g. the exposure compensation step and limits.
return Initialize();
}
protected:
PRRWLock* mLock;
bool mDirty;
bool mInitialized;
// Required internal properties
double mExposureCompensationMin;
double mExposureCompensationStep;
// This subclass of android::CameraParameters just gives
// all of the AOSP getters and setters the same signature.
class Parameters : public android::CameraParameters
{
public:
using android::CameraParameters::set;
void set(const char* aKey, float aValue) { setFloat(aKey, aValue); }
void set(const char* aKey, double aValue) { setFloat(aKey, aValue); }
void get(const char* aKey, float& aRet) { aRet = getFloat(aKey); }
void get(const char* aKey, double& aRet) { aRet = getFloat(aKey); }
void get(const char* aKey, const char*& aRet) { aRet = android::CameraParameters::get(aKey); }
void get(const char* aKey, int& aRet) { aRet = getInt(aKey); }
static const char* GetTextKey(uint32_t aKey);
};
Parameters mParams;
// The *Impl() templates handle converting the parameter keys from
// their enum values to string types, if necessary. These are the
// bottom layer accessors to mParams.
template<typename T> nsresult
SetImpl(uint32_t aKey, const T& aValue)
{
const char* key = Parameters::GetTextKey(aKey);
NS_ENSURE_TRUE(key, NS_ERROR_NOT_AVAILABLE);
mParams.set(key, aValue);
return NS_OK;
}
template<typename T> nsresult
GetImpl(uint32_t aKey, T& aValue)
{
const char* key = Parameters::GetTextKey(aKey);
NS_ENSURE_TRUE(key, NS_ERROR_NOT_AVAILABLE);
mParams.get(key, aValue);
return NS_OK;
}
template<class T> nsresult
SetImpl(const char* aKey, const T& aValue)
{
mParams.set(aKey, aValue);
return NS_OK;
}
template<class T> nsresult
GetImpl(const char* aKey, T& aValue)
{
mParams.get(aKey, aValue);
return NS_OK;
}
// The *Translated() functions allow us to handle special cases;
// for example, where the thumbnail size setting is exposed as an
// ICameraControl::Size object, but is handled by the AOSP layer
// as two separate parameters.
nsresult SetTranslated(uint32_t aKey, const nsAString& aValue);
nsresult GetTranslated(uint32_t aKey, nsAString& aValue);
nsresult SetTranslated(uint32_t aKey, const ICameraControl::Size& aSize);
nsresult GetTranslated(uint32_t aKey, ICameraControl::Size& aSize);
nsresult GetTranslated(uint32_t aKey, nsTArray<ICameraControl::Size>& aSizes);
nsresult SetTranslated(uint32_t aKey, const nsTArray<ICameraControl::Region>& aRegions);
nsresult GetTranslated(uint32_t aKey, nsTArray<ICameraControl::Region>& aRegions);
nsresult SetTranslated(uint32_t aKey, const ICameraControl::Position& aPosition);
nsresult SetTranslated(uint32_t aKey, const int64_t& aValue);
nsresult GetTranslated(uint32_t aKey, int64_t& aValue);
nsresult SetTranslated(uint32_t aKey, const double& aValue);
nsresult GetTranslated(uint32_t aKey, double& aValue);
nsresult SetTranslated(uint32_t aKey, const int& aValue);
nsresult GetTranslated(uint32_t aKey, int& aValue);
nsresult SetTranslated(uint32_t aKey, const uint32_t& aValue);
nsresult GetTranslated(uint32_t aKey, uint32_t& aValue);
nsresult GetTranslated(uint32_t aKey, nsTArray<nsString>& aValues);
nsresult GetTranslated(uint32_t aKey, nsTArray<double>& aValues);
template<class T> nsresult GetListAsArray(uint32_t aKey, nsTArray<T>& aArray);
nsresult Initialize();
};
} // namespace mozilla
#endif // DOM_CAMERA_GONKCAMERAPARAMETERS_H

View File

@ -158,8 +158,7 @@ GonkCameraSource::GonkCameraSource(
Size videoSize,
int32_t frameRate,
bool storeMetaDataInVideoBuffers)
: mCameraHw(aCameraHw),
mCameraFlags(0),
: mCameraFlags(0),
mNumInputBuffers(0),
mVideoFrameRate(-1),
mNumFramesReceived(0),
@ -171,7 +170,8 @@ GonkCameraSource::GonkCameraSource(
mNumFramesDropped(0),
mNumGlitches(0),
mGlitchDurationThresholdUs(200000),
mCollectStats(false) {
mCollectStats(false),
mCameraHw(aCameraHw) {
mVideoSize.width = -1;
mVideoSize.height = -1;

View File

@ -14,10 +14,10 @@
* limitations under the License.
*/
#include "GonkRecorderProfiles.h"
#include <media/MediaProfiles.h>
#include "GonkRecorder.h"
#include "CameraControlImpl.h"
#include "GonkRecorderProfiles.h"
#include "CameraCommon.h"
using namespace mozilla;

View File

@ -7,6 +7,7 @@
#include <media/MediaProfiles.h>
#include "CameraRecorderProfiles.h"
#include "ICameraControl.h"
#ifndef CHECK_SETARG
#define CHECK_SETARG(x) \
@ -86,7 +87,7 @@ public:
* supported by the camera hardware. (Just because it appears in a recorder
* profile doesn't mean the hardware can handle it.)
*/
void SetSupportedResolutions(const nsTArray<idl::CameraSize>& aSizes)
void SetSupportedResolutions(const nsTArray<ICameraControl::Size>& aSizes)
{ mSupportedSizes = aSizes; }
/**
@ -104,7 +105,7 @@ public:
protected:
virtual ~GonkRecorderProfileManager();
nsTArray<idl::CameraSize> mSupportedSizes;
nsTArray<ICameraControl::Size> mSupportedSizes;
};
}; // namespace mozilla

View File

@ -5,69 +5,185 @@
#ifndef DOM_CAMERA_ICAMERACONTROL_H
#define DOM_CAMERA_ICAMERACONTROL_H
#include "nsIFile.h"
#include "nsIDOMCameraManager.h"
#include "DictionaryHelpers.h"
#include "CameraCommon.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsAutoPtr.h"
#include "nsISupportsImpl.h"
class DeviceStorageFileDescriptor;
class nsIFile;
namespace mozilla {
class DOMCameraPreview;
class CameraControlListener;
class RecorderProfileManager;
// XXXmikeh - In some future patch this should move into the ICameraControl
// class as well, and the names updated to the 'k'-style;
// e.g. kParamPreviewSize, etc.
enum {
// current settings
CAMERA_PARAM_PREVIEWSIZE,
CAMERA_PARAM_PREVIEWFORMAT,
CAMERA_PARAM_PREVIEWFRAMERATE,
CAMERA_PARAM_VIDEOSIZE,
CAMERA_PARAM_PICTURE_SIZE,
CAMERA_PARAM_PICTURE_FILEFORMAT,
CAMERA_PARAM_PICTURE_ROTATION,
CAMERA_PARAM_PICTURE_LOCATION,
CAMERA_PARAM_PICTURE_DATETIME,
CAMERA_PARAM_EFFECT,
CAMERA_PARAM_WHITEBALANCE,
CAMERA_PARAM_SCENEMODE,
CAMERA_PARAM_FLASHMODE,
CAMERA_PARAM_FOCUSMODE,
CAMERA_PARAM_ZOOM,
CAMERA_PARAM_METERINGAREAS,
CAMERA_PARAM_FOCUSAREAS,
CAMERA_PARAM_FOCALLENGTH,
CAMERA_PARAM_FOCUSDISTANCENEAR,
CAMERA_PARAM_FOCUSDISTANCEOPTIMUM,
CAMERA_PARAM_FOCUSDISTANCEFAR,
CAMERA_PARAM_EXPOSURECOMPENSATION,
CAMERA_PARAM_PICTURESIZE,
CAMERA_PARAM_THUMBNAILSIZE,
CAMERA_PARAM_THUMBNAILQUALITY,
CAMERA_PARAM_SENSORANGLE,
// supported features
CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
CAMERA_PARAM_SUPPORTED_PICTURESIZES,
CAMERA_PARAM_SUPPORTED_VIDEOSIZES,
CAMERA_PARAM_SUPPORTED_PICTUREFORMATS,
CAMERA_PARAM_SUPPORTED_WHITEBALANCES,
CAMERA_PARAM_SUPPORTED_SCENEMODES,
CAMERA_PARAM_SUPPORTED_EFFECTS,
CAMERA_PARAM_SUPPORTED_FLASHMODES,
CAMERA_PARAM_SUPPORTED_FOCUSMODES,
CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS,
CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS,
CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION,
CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION,
CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP,
CAMERA_PARAM_SUPPORTED_ZOOM,
CAMERA_PARAM_SUPPORTED_ZOOMRATIOS,
CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES
};
class ICameraControl
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ICameraControl)
virtual nsresult GetPreviewStream(idl::CameraSize aSize, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult StartPreview(DOMCameraPreview* aDOMPreview) = 0;
virtual void StopPreview() = 0;
virtual nsresult AutoFocus(nsICameraAutoFocusCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult TakePicture(const idl::CameraSize& aSize, int32_t aRotation, const nsAString& aFileFormat, idl::CameraPosition aPosition, uint64_t aDateTime, nsICameraTakePictureCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult StartRecording(idl::CameraStartRecordingOptions* aOptions, DeviceStorageFileDescriptor *aFileDescriptor, nsICameraStartRecordingCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
static nsresult GetNumberOfCameras(int32_t& aDeviceCount);
static nsresult GetCameraName(uint32_t aDeviceNum, nsCString& aDeviceName);
static nsresult GetListOfCameras(nsTArray<nsString>& aList);
enum Mode {
kUnspecifiedMode,
kPictureMode,
kVideoMode,
};
struct Size {
uint32_t width;
uint32_t height;
};
struct Region {
int32_t top;
int32_t left;
int32_t bottom;
int32_t right;
uint32_t weight;
};
struct Position {
double latitude;
double longitude;
double altitude;
double timestamp;
};
struct StartRecordingOptions {
uint32_t rotation;
uint32_t maxFileSizeBytes;
uint32_t maxVideoLengthMs;
};
struct Configuration {
Mode mMode;
Size mPreviewSize;
nsString mRecorderProfile;
};
static already_AddRefed<ICameraControl> Create(uint32_t aCameraId,
const Configuration* aInitialConfig);
virtual nsresult SetConfiguration(const Configuration& aConfig) = 0;
virtual void AddListener(CameraControlListener* aListener) = 0;
virtual void RemoveListener(CameraControlListener* aListener) = 0;
virtual nsresult StartPreview() = 0;
virtual nsresult StopPreview() = 0;
virtual nsresult AutoFocus(bool aCancelExistingCall) = 0;
virtual nsresult TakePicture() = 0;
virtual nsresult StartRecording(DeviceStorageFileDescriptor *aFileDescriptor,
const StartRecordingOptions* aOptions = nullptr) = 0;
virtual nsresult StopRecording() = 0;
virtual nsresult GetPreviewStreamVideoMode(idl::CameraRecorderOptions* aOptions, nsICameraPreviewStreamCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult ReleaseHardware(nsICameraReleaseCallback* onSuccess, nsICameraErrorCallback* onError) = 0;
virtual nsresult ReleaseHardware() = 0;
virtual nsresult Set(uint32_t aKey, const nsAString& aValue) = 0;
virtual nsresult Get(uint32_t aKey, nsAString& aValue) = 0;
virtual nsresult Set(uint32_t aKey, double aValue) = 0;
virtual nsresult Get(uint32_t aKey, double* aValue) = 0;
virtual nsresult Set(JSContext* aCx, uint32_t aKey, const JS::Value& aValue, uint32_t aLimit) = 0;
virtual nsresult Get(JSContext* aCx, uint32_t aKey, JS::Value* aValue) = 0;
virtual nsresult Set(nsICameraShutterCallback* aOnShutter) = 0;
virtual nsresult Get(nsICameraShutterCallback** aOnShutter) = 0;
virtual nsresult Set(nsICameraClosedCallback* aOnClosed) = 0;
virtual nsresult Get(nsICameraClosedCallback** aOnClosed) = 0;
virtual nsresult Set(nsICameraRecorderStateChange* aOnRecorderStateChange) = 0;
virtual nsresult Get(nsICameraRecorderStateChange** aOnRecorderStateChange) = 0;
virtual nsresult Set(nsICameraPreviewStateChange* aOnPreviewStateChange) = 0;
virtual nsresult Get(nsICameraPreviewStateChange** aOnPreviewStateChange) = 0;
virtual nsresult Set(uint32_t aKey, const idl::CameraSize& aSize) = 0;
virtual nsresult Get(uint32_t aKey, idl::CameraSize& aSize) = 0;
virtual nsresult Get(uint32_t aKey, int32_t* aValue) = 0;
virtual nsresult SetFocusAreas(JSContext* aCx, const JS::Value& aValue) = 0;
virtual nsresult SetMeteringAreas(JSContext* aCx, const JS::Value& aValue) = 0;
virtual nsresult GetVideoSizes(nsTArray<idl::CameraSize>& aVideoSizes) = 0;
virtual nsresult Get(uint32_t aKey, double& aValue) = 0;
virtual nsresult Set(uint32_t aKey, int32_t aValue) = 0;
virtual nsresult Get(uint32_t aKey, int32_t& aValue) = 0;
virtual nsresult Set(uint32_t aKey, int64_t aValue) = 0;
virtual nsresult Get(uint32_t aKey, int64_t& aValue) = 0;
virtual nsresult Set(uint32_t aKey, const Size& aValue) = 0;
virtual nsresult Get(uint32_t aKey, Size& aValue) = 0;
virtual nsresult Set(uint32_t aKey, const nsTArray<Region>& aRegions) = 0;
virtual nsresult Get(uint32_t aKey, nsTArray<Region>& aRegions) = 0;
virtual nsresult SetLocation(const Position& aLocation) = 0;
virtual nsresult Get(uint32_t aKey, nsTArray<Size>& aSizes) = 0;
virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) = 0;
virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) = 0;
virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManager() = 0;
virtual uint32_t GetCameraId() = 0;
virtual const char* GetParameter(const char* aKey) = 0;
virtual const char* GetParameterConstChar(uint32_t aKey) = 0;
virtual double GetParameterDouble(uint32_t aKey) = 0;
virtual void GetParameter(uint32_t aKey, nsTArray<idl::CameraRegion>& aRegions) = 0;
virtual void SetParameter(const char* aKey, const char* aValue) = 0;
virtual void SetParameter(uint32_t aKey, const char* aValue) = 0;
virtual void SetParameter(uint32_t aKey, double aValue) = 0;
virtual void SetParameter(uint32_t aKey, const nsTArray<idl::CameraRegion>& aRegions) = 0;
virtual void Shutdown() = 0;
protected:
virtual ~ICameraControl() { }
friend class ICameraControlParameterSetAutoEnter;
virtual void BeginBatchParameterSet() = 0;
virtual void EndBatchParameterSet() = 0;
};
// Helper class to make it easy to update a batch of camera parameters;
// the parameters are applied atomically when this object goes out of
// scope.
class ICameraControlParameterSetAutoEnter
{
public:
ICameraControlParameterSetAutoEnter(ICameraControl* aCameraControl)
: mCameraControl(aCameraControl)
{
mCameraControl->BeginBatchParameterSet();
}
virtual ~ICameraControlParameterSetAutoEnter()
{
mCameraControl->EndBatchParameterSet();
}
protected:
nsRefPtr<ICameraControl> mCameraControl;
};
} // namespace mozilla

View File

@ -4,15 +4,9 @@
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
XPIDL_SOURCES += [
'nsIDOMCameraManager.idl',
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
TEST_DIRS += ['test']
XPIDL_MODULE = 'dom_camera'
EXPORTS += [
'CameraCommon.h',
'CameraPreviewMediaStream.h',
@ -26,8 +20,8 @@ SOURCES += [
'CameraRecorderProfiles.cpp',
'DOMCameraCapabilities.cpp',
'DOMCameraControl.cpp',
'DOMCameraControlListener.cpp',
'DOMCameraManager.cpp',
'DOMCameraPreview.cpp',
]
if CONFIG['MOZ_B2G_CAMERA']:
@ -35,6 +29,7 @@ if CONFIG['MOZ_B2G_CAMERA']:
'GonkCameraControl.cpp',
'GonkCameraHwMgr.cpp',
'GonkCameraManager.cpp',
'GonkCameraParameters.cpp',
'GonkCameraSource.cpp',
'GonkRecorder.cpp',
'GonkRecorderProfiles.cpp',

View File

@ -1,229 +0,0 @@
#include "domstubs.idl"
#include "nsIDOMMediaStream.idl"
#include "nsIDOMDOMRequest.idl"
interface nsIDOMBlob;
interface nsIDOMDeviceStorage;
/* Used to set the dimensions of a captured picture,
a preview stream, a video capture stream, etc. */
dictionary CameraSize {
unsigned long width;
unsigned long height;
};
/* Camera regions are used to set focus and metering areas;
the coordinates are referenced to the sensor:
(-1000, -1000) is the top left corner
(1000, 1000) is the bottom left corner
The weight of the region can range from 0 to 1000. */
dictionary CameraRegion {
long top;
long left;
long bottom;
long right;
unsigned long weight;
};
/* The position information to record in the image header.
'NaN' indicates the information is not available. */
dictionary CameraPosition {
double latitude;
double longitude;
double altitude;
double timestamp;
};
[scriptable, uuid(0711a4af-73c2-481d-85bc-0ba3ec36c004)]
interface nsICameraCapabilities : nsISupports
{
/* an array of objects with 'height' and 'width' properties
supported for the preview stream */
[implicit_jscontext]
readonly attribute jsval previewSizes;
/* an array of objects with 'height' and 'width' properties
supported for picture taking */
[implicit_jscontext]
readonly attribute jsval pictureSizes;
/* an array of objects with 'height' and 'width' properties
supported for thumbnail sizes in taken pictures */
[implicit_jscontext]
readonly attribute jsval thumbnailSizes;
/* an array of strings, e.g. [ "jpeg", "rgb565" ] */
[implicit_jscontext]
readonly attribute jsval fileFormats;
/* an array of strings, e.g. [ "auto", "fluorescent", etc. ] */
[implicit_jscontext]
readonly attribute jsval whiteBalanceModes;
/* an array of strings, e.g. [ "auto", "night", "beach", etc. ] */
[implicit_jscontext]
readonly attribute jsval sceneModes;
/* an array of strings, e.g. [ "normal", "sepia", "mono", etc. ] */
[implicit_jscontext]
readonly attribute jsval effects;
/* an array of strings, e.g. [ "auto", "off", "on", etc. ] */
[implicit_jscontext]
readonly attribute jsval flashModes;
/* an array of strings, e.g. [ "auto", "fixed", "macro", etc. ] */
[implicit_jscontext]
readonly attribute jsval focusModes;
/* the maximum number of focus areas supported by the camera */
[implicit_jscontext]
readonly attribute long maxFocusAreas;
/* the minimum supported exposure compensation value */
[implicit_jscontext]
readonly attribute double minExposureCompensation;
/* the maximum supported exposure compensation value */
[implicit_jscontext]
readonly attribute double maxExposureCompensation;
/* exposure compensation minimum step-size */
[implicit_jscontext]
readonly attribute double stepExposureCompensation;
/* the maximum number of metering areas supported by the camera */
[implicit_jscontext]
readonly attribute long maxMeteringAreas;
/* an array of doubles, e.g. [ 1.0, 1.2, 1.5, 2.0, 3.0, etc. ],
or null if zooming is not supported */
[implicit_jscontext]
readonly attribute jsval zoomRatios;
/* an array of objects with 'height' and 'width' properties
supported for video recording */
[implicit_jscontext]
readonly attribute jsval videoSizes;
/* an object with attributes for each of the supported recorder
profiles, e.g. recorderProfiles.cif, recorderProfiles.qvga,
etc. */
[implicit_jscontext]
readonly attribute jsval recorderProfiles;
};
/* These properties affect the video recording preview, e.g.
{
profile: "1080p",
rotation: 0
}
'profile' is one of the profiles returned by
nsICameraCapabilities.recorderProfiles'; if this profile is missing,
an arbitrary profile will be chosen.
'rotation' is the degrees clockwise to rotate the preview; if
this option is not supported, it will be ignored; if this option
is missing, the default is 0.
*/
dictionary CameraRecorderOptions
{
DOMString profile;
long rotation;
};
/* These properties affect the actual video recording, e.g.
{
rotation: 0,
maxFileSizeBytes: 1024 * 1024,
maxVideoLengthMs: 0
}
'rotation' is the degrees clockwise to rotate the recorded video; if
this options is not supported, it will be ignored; if this option is
missing, the default is 0.
'maxFileSizeBytes' is the maximum size in bytes to which the recorded
video file will be allowed to grow.
'maxVideoLengthMs' is the maximum length in milliseconds to which the
recorded video will be allowed to grow.
if either 'maxFileSizeBytes' or 'maxVideoLengthMs' is missing, zero,
or negative, that limit will be disabled.
*/
dictionary CameraStartRecordingOptions
{
long rotation;
long long maxFileSizeBytes;
long long maxVideoLengthMs;
};
[scriptable, function, uuid(0444a687-4bc9-462c-8246-5423f0fe46a4)]
interface nsICameraPreviewStreamCallback : nsISupports
{
void handleEvent(in nsIDOMMediaStream stream);
};
[scriptable, function, uuid(6baa4ac7-9c25-4c48-9bb0-5193b38b9b0a)]
interface nsICameraAutoFocusCallback : nsISupports
{
void handleEvent(in boolean success);
};
[scriptable, function, uuid(17af779e-cb6f-4ca5-890c-06468ff82e4f)]
interface nsICameraTakePictureCallback : nsISupports
{
void handleEvent(in nsIDOMBlob picture);
};
[scriptable, function, uuid(89a762f8-581b-410a-ad86-e2bd2113ad82)]
interface nsICameraStartRecordingCallback : nsISupports
{
void handleEvent();
};
[scriptable, function, uuid(fb80db71-e315-42f0-9ea9-dd3dd312ed70)]
interface nsICameraShutterCallback : nsISupports
{
void handleEvent();
};
[scriptable, function, uuid(0ef0f01e-ce74-4741-9bba-54376adfb7a2)]
interface nsICameraClosedCallback : nsISupports
{
void handleEvent();
};
[scriptable, function, uuid(550d675a-257d-4713-8b3d-0da53eba68fc)]
interface nsICameraRecorderStateChange : nsISupports
{
void handleStateChange(in DOMString newState);
};
[scriptable, function, uuid(d1634592-43fd-4117-a2b2-419aec841cc4)]
interface nsICameraPreviewStateChange : 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
{
void handleEvent(in DOMString error);
};
[scriptable, function, uuid(16de7703-dc43-4766-99c5-ff30a9ab92d7)]
interface nsICameraGetCameraCallback : nsISupports
{
void handleEvent(in nsISupports camera);
};

View File

@ -1,3 +1,4 @@
[DEFAULT]
[test_camera.html]
[test_camera_2.html]

View File

@ -1,7 +1,7 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for mozCameras</title>
<title>Test for mozCameras.getCamera() with separate .setConfiguration() call</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
@ -11,8 +11,14 @@
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var options = {
camera: navigator.mozCameras.getListOfCameras()[0]
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
var config = {
@ -24,6 +30,9 @@ var config = {
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
Camera.cameraObj.release();
Camera.cameraObj = null;
Camera.viewfinder.mozSrcObject = null;
}
var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation',
@ -96,7 +105,9 @@ var Camera = {
Camera._testsCompleted++;
if(Camera._testsCompleted == Camera._tests.length) {
ok(true, "test finishing");
Camera.cameraObj.release();
Camera.cameraObj.release(function() {
Camera.cameraObj = null;
}, onError);
Camera.viewfinder.mozSrcObject = null;
SimpleTest.finish();
} else {
@ -114,27 +125,16 @@ var Camera = {
Camera._shutter + " times");
},
streamReady: function onStreamReady (stream) {
Camera.viewfinder.mozSrcObject = stream;
Camera.viewfinder.play();
ok(true, "on Stream is ready and playing");
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
Camera.cameraObj.onPreviewStateChange = null;
Camera.onReady();
}
};
//window.setTimeout(Camera.onReady, 500);
},
onReady: function onReady() {
var camcap = Camera.cameraObj.capabilities;
var tests = {};
for (var prop in capabilities) {
prop = capabilities[prop];
ok(camcap[prop] || isFinite(camcap[prop]) || camcap[prop] == null, "Camera Capability: " +
prop + " is exposed, value = " +JSON.stringify(camcap[prop]));
}
prop + " is exposed, value = " + JSON.stringify(camcap[prop]));
}
ok(camcap.maxMeteringAreas >= 0, "maxMeteringAreas = " + camcap.maxMeteringAreas);
ok(camcap.maxFocusAreas >= 0, "maxFocusAreas = " + camcap.maxFocusAreas);
for (var prop in camcap) {
if(camcap[prop] && camcap[prop].length > 1) {
tests[prop] = camcap[prop];
@ -165,24 +165,34 @@ var Camera = {
Camera.setFlashMode(test.flashMode);
config.fileFormat = test.fileFormat;
config.pictureSize = test.pictureSize;
ok(true, JSON.stringify(config.pictureSize));
ok(true, "testing picture size " + JSON.stringify(config.pictureSize));
Camera.cameraObj.takePicture(config, this.takePictureSuccess.bind(this), onError);
},
onConfigChange: function onConfigChange(config) {
ok(config.mode === options.mode, "configuration mode = " + config.mode);
ok(config.recorderProfile === options.recorderProfile, "recorder profile = " + config.recorderProfile);
ok(config.previewSize.width === options.previewSize.width &&
config.previewSize.height === options.previewSize.height,
"preview size (w x h) = " + config.previewSize.width + " x " + config.previewSize.height);
},
setUp: function setup_tests() {
function onSuccess(camera) {
Camera.cameraObj = camera;
var size = {
profile: 'cif',
rotation: 0,
width: 352,
height: 288
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onReady();
}
};
SimpleTest.expectAssertions(34);
SimpleTest.expectAssertions(0);
ok(true, "Camera Control object has been successfully initialized");
Camera.cameraObj.getPreviewStream(size, Camera.streamReady);
Camera.cameraObj.setConfiguration(options, Camera.onConfigChange, onError);
Camera.cameraObj.onShutter = Camera.shutter;
};
navigator.mozCameras.getCamera(options, onSuccess, onError);
navigator.mozCameras.getCamera(whichCamera, null, onSuccess, onError);
}
}

View File

@ -0,0 +1,205 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test for mozCameras.getCamera() using an initial configuration</title>
<script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<video id="viewfinder" |width = "200" height = "200| autoplay></video>
<img src="#" alt="This image is going to load" id="testimage"/>
<script class="testbody" type="text/javascript;version=1.7">
var whichCamera = navigator.mozCameras.getListOfCameras()[0];
var options = {
mode: 'picture',
recorderProfile: 'cif',
previewSize: {
width: 352,
height: 288
}
};
var config = {
dateTime: Date.now() / 1000,
pictureSize: null,
fileFormat: 'jpeg',
rotation: 90
};
function onError(e) {
ok(false, "Error" + JSON.stringify(e));
Camera.cameraObj.release();
Camera.cameraObj = null;
Camera.viewfinder.mozSrcObject = null;
}
var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation',
'maxExposureCompensation', 'stepExposureCompensation', 'maxMeteringAreas', 'videoSizes',
'recorderProfiles'];
var Camera = {
cameraObj: null,
_recording: false,
_currentTest: null,
_autoFocusSupported: 0,
_manuallyFocused: false,
_flashmodes: null,
_pictureSizes: null,
_previewSizes: null,
_whiteBalanceModes: null,
_zoomRatios: null,
_sceneModes: null,
_focusModes: null,
_testsCompleted: 0,
_shutter: 0,
_config: {
dateTime: Date.now() / 1000,
pictureSize: null,
fileFormat: 'jpeg',
rotation: 90
},
_tests: null,
get viewfinder() {
return document.getElementById('viewfinder');
},
setFlashMode: function camera_setFlash(mode) {
this.cameraObj.flashMode = mode;
},
setFocus: function camera_setfocus(mode) {
this.cameraObj.focus = mode;
},
getFileFormats: function camera_formats() {
this._fileFormats = this.cameraObj.capabilities.fileFormats;
},
getFlashModes: function camera_getFlash() {
this._flashmodes = this.cameraObj.capabilities.flashModes;
},
getFocusModes: function camera_getFocus() {
this._focusModes = this.cameraObj.capabilities.focusModes;
},
getSceneModes: function camera_getScene() {
this._sceneModes = this.cameraObj.capabilities.sceneModes;
},
getZoomRatios: function camera_getZoom() {
this._zoomRatios = this.cameraObj.capabilities.zoomRatios;
},
getWhiteBalance: function camera_white() {
this._whitebalanceModes = this.cameraObj.capabilities.whiteBalanceModes;
},
getPictureSizes: function camera_sizes() {
this._pictureSizes = this.cameraObj.capabilities.pictureSizes;
},
getPreviewSizes: function camera_preview() {
this._previewSizes = this.cameraObj.capabilities.previewSizes;
},
takePictureSuccess: function taken_foto(blob) {
var img = new Image();
var test = this._currentTest;
img.onload = function Imgsize() {
ok(this.width == test.pictureSize.width, "The image taken has the width " +
this.width + " pictureSize width = " + test.pictureSize.width);
ok(this.height == test.pictureSize.height, "The image taken has the height " +
this.height + " picturesize height = " + test.pictureSize.height);
Camera._testsCompleted++;
if(Camera._testsCompleted == Camera._tests.length) {
ok(true, "test finishing");
Camera.cameraObj.release(function() {
Camera.cameraObj = null;
}, onError);
Camera.viewfinder.mozSrcObject = null;
SimpleTest.finish();
} else {
Camera.runTests();
}
}
ok(blob.size > 100 , "Blob Size Gathered = " + blob.size);
ok("image/" + test.fileFormat == blob.type, "Blob Type = " + blob.type);
img.src = window.URL.createObjectURL(blob);
},
shutter: function onShutter () {
Camera._shutter++;
ok(Camera._shutter == (Camera._testsCompleted + 1), "on Shutter has been called " +
Camera._shutter + " times");
},
onReady: function onReady() {
var camcap = Camera.cameraObj.capabilities;
var tests = {};
for (var prop in capabilities) {
prop = capabilities[prop];
ok(camcap[prop] || isFinite(camcap[prop]) || camcap[prop] == null, "Camera Capability: " +
prop + " is exposed, value = " + JSON.stringify(camcap[prop]));
}
for (var prop in camcap) {
if(camcap[prop] && camcap[prop].length > 1) {
tests[prop] = camcap[prop];
}
}
Camera.getPictureSizes();
Camera.getPreviewSizes();
Camera.getFileFormats();
Camera.getFocusModes();
ok(Camera._previewSizes.length > 0, "previewSizes length = " + Camera._previewSizes.length);
ok(Camera._pictureSizes.length > 0, "picturesizes length = " + Camera._pictureSizes.length);
ok(Camera._fileFormats.length > 0, "file formats length = " + Camera._fileFormats.length);
Camera._tests = new Array();
for (var i in Camera._pictureSizes) {
for (var l in Camera._fileFormats) {
var config = {
pictureSize: Camera._pictureSizes[i],
fileFormat: Camera._fileFormats[l]
};
Camera._tests.push(config);
}
}
Camera.runTests();
},
runTests: function run_tests() {
var test = this._tests[this._testsCompleted];
this._currentTest = test;
Camera.setFlashMode(test.flashMode);
config.fileFormat = test.fileFormat;
config.pictureSize = test.pictureSize;
ok(true, "testing picture size " + JSON.stringify(config.pictureSize));
Camera.cameraObj.takePicture(config, this.takePictureSuccess.bind(this), onError);
},
setUp: function setup_tests() {
function onSuccess(camera, config) {
ok(true, "Camera Control object has been successfully initialized");
ok(config.mode === options.mode, "configuration mode = " + config.mode);
ok(config.recorderProfile === options.recorderProfile, "recorder profile = " + config.recorderProfile);
ok(config.previewSize.width === options.previewSize.width &&
config.previewSize.height === options.previewSize.height,
"preview size (w x h) = " + config.previewSize.width + " x " + config.previewSize.height);
Camera.cameraObj = camera;
Camera.viewfinder.mozSrcObject = camera;
Camera.viewfinder.play();
Camera.cameraObj.onPreviewStateChange = function(state) {
if (state === 'started') {
ok(true, "viewfinder is ready and playing");
Camera.cameraObj.onPreviewStateChange = null;
Camera.onReady();
}
};
SimpleTest.expectAssertions(0);
Camera.cameraObj.onShutter = Camera.shutter;
};
navigator.mozCameras.getCamera(whichCamera, options, onSuccess, onError);
}
}
SimpleTest.waitForExplicitFinish();
window.addEventListener('beforeunload', function() {
Camera.viewfinder.mozSrcObject = null;
});
Camera.setUp();
</script>
</body>
</html>

View File

@ -1496,11 +1496,7 @@ MediaManager::GetBackend(uint64_t aWindowId)
MutexAutoLock lock(mMutex);
if (!mBackend) {
#if defined(MOZ_WEBRTC)
#ifndef MOZ_B2G_CAMERA
mBackend = new MediaEngineWebRTC(mPrefs);
#else
mBackend = new MediaEngineWebRTC(mCameraManager, aWindowId);
#endif
#else
mBackend = new MediaEngineDefault();
#endif

View File

@ -0,0 +1,33 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
interface CameraCapabilities
{
[Constant, Cached] readonly attribute sequence<CameraSize> previewSizes;
[Constant, Cached] readonly attribute sequence<CameraSize> pictureSizes;
[Constant, Cached] readonly attribute sequence<CameraSize> thumbnailSizes;
[Constant, Cached] readonly attribute sequence<CameraSize> videoSizes;
[Constant, Cached] readonly attribute sequence<DOMString> fileFormats;
[Constant, Cached] readonly attribute sequence<DOMString> whiteBalanceModes;
[Constant, Cached] readonly attribute sequence<DOMString> sceneModes;
[Constant, Cached] readonly attribute sequence<DOMString> effects;
[Constant, Cached] readonly attribute sequence<DOMString> flashModes;
[Constant, Cached] readonly attribute sequence<DOMString> focusModes;
[Constant, Cached] readonly attribute sequence<double> zoomRatios;
[Constant, Cached] readonly attribute unsigned long maxFocusAreas;
[Constant, Cached] readonly attribute unsigned long maxMeteringAreas;
[Constant, Cached] readonly attribute double minExposureCompensation;
[Constant, Cached] readonly attribute double maxExposureCompensation;
[Constant, Cached] readonly attribute double exposureCompensationStep;
[Constant, Cached] readonly attribute any recorderProfiles;
};

View File

@ -5,213 +5,328 @@
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
interface CameraCapabilities;
interface GetCameraCallback;
interface CameraErrorCallback;
interface CameraShutterCallback;
interface CameraClosedCallback;
interface CameraRecorderStateChange;
interface CameraAutoFocusCallback;
interface CameraTakePictureCallback;
interface CameraPreviewStateChange;
interface CameraPreviewStreamCallback;
interface CameraStartRecordingCallback;
interface CameraReleaseCallback;
/* Camera regions are used to set focus and metering areas;
the coordinates are referenced to the sensor:
(-1000, -1000) is the top-left corner
(1000, 1000) is the bottom-right corner
The weight of the region can range from 0 to 1000. */
dictionary CameraRegion
{
long top = -1000;
long left = -1000;
long bottom = 1000;
long right = 1000;
unsigned long weight = 1000;
};
/* The position information to record in the image header.
'NaN' indicates the information is not available. */
dictionary CameraPosition
{
unrestricted double latitude = NaN;
unrestricted double longitude = NaN;
unrestricted double altitude = NaN;
unrestricted double timestamp = NaN;
};
/*
Options for takePicture().
*/
dictionary CameraPictureOptions
{
/* an object with a combination of 'height' and 'width' properties
chosen from CameraCapabilities.pictureSizes */
CameraSize pictureSize = null;
/* one of the file formats chosen from
CameraCapabilities.fileFormats */
DOMString fileFormat = "";
/* the rotation of the image in degrees, from 0 to 270 in
steps of 90; this doesn't affect the image, only the
rotation recorded in the image header.*/
long rotation = 0;
/* an object containing any or all of 'latitude', 'longitude',
'altitude', and 'timestamp', used to record when and where
the image was taken. e.g.
{
latitude: 43.647118,
longitude: -79.3943,
altitude: 500
// timestamp not specified, in this case, and
// won't be included in the image header
}
can be null in the case where position information isn't
available/desired.
'altitude' is in metres; 'timestamp' is UTC, in seconds from
January 1, 1970.
*/
CameraPosition position = null;
/* the number of seconds from January 1, 1970 UTC. This can be
different from the positional timestamp (above). */
// XXXbz this should really accept a date too, no?
long long dateTime = 0;
};
/* These properties affect the video recording preview, e.g.
{
profile: "1080p",
rotation: 0
}
'profile' is one of the profiles returned by
CameraCapabilities.recorderProfiles'; if this profile is missing,
an arbitrary profile will be chosen.
'rotation' is the degrees clockwise to rotate the preview; if
this option is not supported, it will be ignored; if this option
is missing, the default is 0.
*/
dictionary CameraRecorderOptions
{
DOMString profile;
long rotation;
};
/* These properties affect the actual video recording, e.g.
{
rotation: 0,
maxFileSizeBytes: 1024 * 1024,
maxVideoLengthMs: 0
}
'rotation' is the degrees clockwise to rotate the recorded video; if
this options is not supported, it will be ignored; if this option is
missing, the default is 0.
'maxFileSizeBytes' is the maximum size in bytes to which the recorded
video file will be allowed to grow.
'maxVideoLengthMs' is the maximum length in milliseconds to which the
recorded video will be allowed to grow.
if either 'maxFileSizeBytes' or 'maxVideoLengthMs' is missing, zero,
or negative, that limit will be disabled.
*/
dictionary CameraStartRecordingOptions
{
long rotation = 0;
long long maxFileSizeBytes = 0;
long long maxVideoLengthMs = 0;
};
callback CameraSetConfigurationCallback = void (CameraConfiguration configuration);
callback CameraAutoFocusCallback = void (boolean focused);
callback CameraTakePictureCallback = void (Blob picture);
callback CameraStartRecordingCallback = void ();
callback CameraShutterCallback = void ();
callback CameraClosedCallback = void ();
callback CameraReleaseCallback = void ();
callback CameraRecorderStateChange = void (DOMString newState);
callback CameraPreviewStateChange = void (DOMString newState);
/*
attributes here affect the preview, any pictures taken, and/or
any video recorded by the camera.
*/
interface CameraControl {
readonly attribute CameraCapabilities capabilities;
interface CameraControl : MediaStream
{
[Constant, Cached]
readonly attribute CameraCapabilities capabilities;
/* one of the values chosen from capabilities.effects;
default is "none" */
[Throws]
attribute DOMString effect;
/* one of the values chosen from capabilities.effects;
default is "none" */
[Throws]
attribute DOMString effect;
/* one of the values chosen from capabilities.whiteBalanceModes;
default is "auto" */
[Throws]
attribute DOMString whiteBalanceMode;
/* one of the values chosen from capabilities.whiteBalanceModes;
default is "auto" */
[Throws]
attribute DOMString whiteBalanceMode;
/* one of the values chosen from capabilities.sceneModes;
default is "auto" */
[Throws]
attribute DOMString sceneMode;
/* one of the values chosen from capabilities.sceneModes;
default is "auto" */
[Throws]
attribute DOMString sceneMode;
/* one of the values chosen from capabilities.flashModes;
default is "auto" */
[Throws]
attribute DOMString flashMode;
/* one of the values chosen from capabilities.flashModes;
default is "auto" */
[Throws]
attribute DOMString flashMode;
/* one of the values chosen from capabilities.focusModes;
default is "auto", if supported, or "fixed" */
[Throws]
attribute DOMString focusMode;
/* one of the values chosen from capabilities.focusModes;
default is "auto", if supported, or "fixed" */
[Throws]
attribute DOMString focusMode;
/* one of the values chosen from capabilities.zoomRatios; other
values will be rounded to the nearest supported value;
default is 1.0 */
[Throws]
attribute double zoom;
/* one of the values chosen from capabilities.zoomRatios; other
values will be rounded to the nearest supported value;
default is 1.0 */
[Throws]
attribute double zoom;
/* an array of one or more objects that define where the
camera will perform light metering, each defining the properties:
{
top: -1000,
left: -1000,
bottom: 1000,
right: 1000,
weight: 1000
}
/* an array of one or more objects that define where the
camera will perform light metering, each defining the properties:
{
top: -1000,
left: -1000,
bottom: 1000,
right: 1000,
weight: 1000
}
'top', 'left', 'bottom', and 'right' all range from -1000 at
the top-/leftmost of the sensor to 1000 at the bottom-/rightmost
of the sensor.
'top', 'left', 'bottom', and 'right' all range from -1000 at
the top-/leftmost of the sensor to 1000 at the bottom-/rightmost
of the sensor.
objects missing one or more of these properties will be ignored;
if the array contains more than capabilities.maxMeteringAreas,
extra areas will be ignored.
objects missing one or more of these properties will be ignored;
if the array contains more than capabilities.maxMeteringAreas,
extra areas will be ignored.
this attribute can be set to null to allow the camera to determine
where to perform light metering. */
[Throws]
attribute any meteringAreas;
this attribute can be set to null to allow the camera to determine
where to perform light metering. */
[Throws]
attribute any meteringAreas;
/* an array of one or more objects that define where the camera will
perform auto-focusing, with the same definition as meteringAreas.
/* an array of one or more objects that define where the camera will
perform auto-focusing, with the same definition as meteringAreas.
if the array contains more than capabilities.maxFocusAreas, extra
areas will be ignored.
if the array contains more than capabilities.maxFocusAreas, extra
areas will be ignored.
this attribute can be set to null to allow the camera to determine
where to focus. */
[Throws]
attribute any focusAreas;
this attribute can be set to null to allow the camera to determine
where to focus. */
[Throws]
attribute any focusAreas;
/* focal length in millimetres */
[Throws]
readonly attribute double focalLength;
/* focal length in millimetres */
[Throws]
readonly attribute double focalLength;
/* the distances in metres to where the image subject appears to be
in focus. 'focusDistanceOptimum' is where the subject will appear
sharpest; the difference between 'focusDistanceFar' and
'focusDistanceNear' is the image's depth of field.
/* the distances in metres to where the image subject appears to be
in focus. 'focusDistanceOptimum' is where the subject will appear
sharpest; the difference between 'focusDistanceFar' and
'focusDistanceNear' is the image's depth of field.
'focusDistanceFar' may be infinity. */
[Throws]
readonly attribute double focusDistanceNear;
[Throws]
readonly attribute double focusDistanceOptimum;
[Throws]
readonly attribute unrestricted double focusDistanceFar;
'focusDistanceFar' may be infinity. */
[Throws]
readonly attribute double focusDistanceNear;
[Throws]
readonly attribute double focusDistanceOptimum;
[Throws]
readonly attribute unrestricted double focusDistanceFar;
/* 'compensation' is optional, and if missing, will
set the camera to use automatic exposure compensation.
/* 'compensation' is optional, and if missing, will
set the camera to use automatic exposure compensation.
acceptable values must range from minExposureCompensation
to maxExposureCompensation in steps of stepExposureCompensation;
invalid values will be rounded to the nearest valid value. */
[Throws]
void setExposureCompensation(optional double compensation);
[Throws]
readonly attribute unrestricted double exposureCompensation;
acceptable values must range from minExposureCompensation
to maxExposureCompensation in steps of stepExposureCompensation;
invalid values will be rounded to the nearest valid value. */
[Throws]
void setExposureCompensation(optional double compensation);
[Throws]
readonly attribute unrestricted double exposureCompensation;
/* the function to call on the camera's shutter event, to trigger
a shutter sound and/or a visual shutter indicator. */
[Throws]
attribute CameraShutterCallback? onShutter;
/* the function to call on the camera's shutter event, to trigger
a shutter sound and/or a visual shutter indicator. */
attribute CameraShutterCallback? onShutter;
/* the function to call when the camera hardware is closed
by the underlying framework, e.g. when another app makes a more
recent call to get the camera. */
[Throws]
attribute CameraClosedCallback? onClosed;
/* the function to call when the camera hardware is closed
by the underlying framework, e.g. when another app makes a more
recent call to get the camera. */
attribute CameraClosedCallback? onClosed;
/* the function to call when the recorder changes state, either because
the recording process encountered an error, or because one of the
recording limits (see CameraStartRecordingOptions) was reached. */
[Throws]
attribute CameraRecorderStateChange? onRecorderStateChange;
attribute CameraPreviewStateChange? onPreviewStateChange;
/* the function to call when the recorder changes state, either because
the recording process encountered an error, or because one of the
recording limits (see CameraStartRecordingOptions) was reached. */
attribute CameraRecorderStateChange? onRecorderStateChange;
/* the size of the picture to be returned by a call to takePicture();
an object with 'height' and 'width' properties that corresponds to
one of the options returned by capabilities.pictureSizes. */
[Throws]
attribute any pictureSize;
/* the function to call when the viewfinder stops or starts,
useful for synchronizing other UI elements. */
attribute CameraPreviewStateChange? onPreviewStateChange;
/* the size of the thumbnail to be included in the picture returned
by a call to takePicture(), assuming the chose fileFormat supports
one; an object with 'height' and 'width' properties that corresponds
to one of the options returned by capabilities.pictureSizes.
this setting should be considered a hint: the implementation will
respect it when possible, and override it if necessary. */
[Throws]
attribute any thumbnailSize;
/* the size of the picture to be returned by a call to takePicture();
an object with 'height' and 'width' properties that corresponds to
one of the options returned by capabilities.pictureSizes. */
[Throws]
attribute any pictureSize;
/* the angle, in degrees, that the image sensor is mounted relative
to the display; e.g. if 'sensorAngle' is 270 degrees (or -90 degrees),
then the preview stream needs to be rotated +90 degrees to have the
same orientation as the real world. */
readonly attribute long sensorAngle;
/* the size of the thumbnail to be included in the picture returned
by a call to takePicture(), assuming the chosen fileFormat supports
one; an object with 'height' and 'width' properties that corresponds
to one of the options returned by capabilities.pictureSizes.
/* tell the camera to attempt to focus the image */
[Throws]
void autoFocus(CameraAutoFocusCallback onSuccess, optional CameraErrorCallback onError);
this setting should be considered a hint: the implementation will
respect it when possible, and override it if necessary. */
[Throws]
attribute any thumbnailSize;
/* capture an image and return it as a blob to the 'onSuccess' callback;
if the camera supports it, this may be invoked while the camera is
already recording video.
/* the angle, in degrees, that the image sensor is mounted relative
to the display; e.g. if 'sensorAngle' is 270 degrees (or -90 degrees),
then the preview stream needs to be rotated +90 degrees to have the
same orientation as the real world. */
readonly attribute long sensorAngle;
invoking this function will stop the preview stream, which must be
manually restarted (e.g. by calling .play() on it). */
[Throws]
void takePicture(CameraPictureOptions aOptions,
CameraTakePictureCallback onSuccess,
optional CameraErrorCallback onError);
/* tell the camera to attempt to focus the image */
[Throws]
void autoFocus(CameraAutoFocusCallback onSuccess, optional CameraErrorCallback onError);
/* get a media stream to be used as a camera viewfinder in video mode;
'aOptions' is an CameraRecorderOptions object. */
[Throws]
void getPreviewStreamVideoMode(any aOptions, CameraPreviewStreamCallback onSuccess, optional CameraErrorCallback onError);
/* capture an image and return it as a blob to the 'onSuccess' callback;
if the camera supports it, this may be invoked while the camera is
already recording video.
/* start recording video; 'aOptions' is a
CameraStartRecordingOptions object. */
[Throws]
void startRecording(any aOptions, DeviceStorage storageArea, DOMString filename, CameraStartRecordingCallback onSuccess, optional CameraErrorCallback onError);
invoking this function will stop the preview stream, which must be
manually restarted (e.g. by calling .play() on it). */
[Throws]
void takePicture(CameraPictureOptions aOptions,
CameraTakePictureCallback onSuccess,
optional CameraErrorCallback onError);
/* stop precording video. */
[Throws]
void stopRecording();
/* start recording video; 'aOptions' is a
CameraStartRecordingOptions object. */
[Throws]
void startRecording(CameraStartRecordingOptions aOptions,
DeviceStorage storageArea,
DOMString filename,
CameraStartRecordingCallback onSuccess,
optional CameraErrorCallback onError);
/* get a media stream to be used as a camera viewfinder; the options
define the desired frame size of the preview, chosen from
capabilities.previewSizes, e.g.:
{
height: 640,
width: 480,
}
*/
[Throws]
void getPreviewStream(any aOptions, CameraPreviewStreamCallback onSuccess, optional CameraErrorCallback onError);
/* stop precording video. */
[Throws]
void stopRecording();
/* call in or after the takePicture() onSuccess callback to
resume the camera preview stream. */
[Throws]
void resumePreview();
/* call in or after the takePicture() onSuccess callback to
resume the camera preview stream. */
[Throws]
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).
/* 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.
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. */
[Throws]
void release(optional CameraReleaseCallback onSuccess, optional CameraErrorCallback onError);
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. */
[Throws]
void release(optional CameraReleaseCallback onSuccess,
optional CameraErrorCallback onError);
/* changes the camera configuration on the fly;
'configuration' is of type CameraConfiguration.
XXXmikeh the 'configuration' argument needs to be optional, else
the WebIDL compiler throws: "WebIDL.WebIDLError: error: Dictionary
argument or union argument containing a dictionary not followed by
a required argument must be optional"
*/
[Throws]
void setConfiguration(optional CameraConfiguration configuration,
optional CameraSetConfigurationCallback onSuccess,
optional CameraErrorCallback onError);
};

View File

@ -5,70 +5,43 @@
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
dictionary CameraPictureOptions {
enum CameraMode { "picture", "video" };
/* an object with a combination of 'height' and 'width' properties
chosen from nsICameraCapabilities.pictureSizes */
// XXXbz this should be a CameraSize dictionary, but we don't have that yet.
any pictureSize = null;
/* Used for the dimensions of a captured picture,
a preview stream, a video capture stream, etc. */
dictionary CameraSize
{
unsigned long width = 0;
unsigned long height = 0;
};
/* one of the file formats chosen from
nsICameraCapabilities.fileFormats */
DOMString fileFormat = "";
/* Pre-emptive camera configuration options. */
dictionary CameraConfiguration
{
CameraMode mode = "picture";
CameraSize previewSize = null;
DOMString recorderProfile = "cif"; // or some other recording profile
// supported by the CameraControl
};
/* the rotation of the image in degrees, from 0 to 270 in
steps of 90; this doesn't affect the image, only the
rotation recorded in the image header.*/
long rotation = 0;
callback CameraErrorCallback = void (DOMString error);
/* an object containing any or all of 'latitude', 'longitude',
'altitude', and 'timestamp', used to record when and where
the image was taken. e.g.
{
latitude: 43.647118,
longitude: -79.3943,
altitude: 500
// timestamp not specified, in this case, and
// won't be included in the image header
}
callback GetCameraCallback = void (CameraControl camera,
CameraConfiguration configuration);
can be null in the case where position information isn't
available/desired.
'altitude' is in metres; 'timestamp' is UTC, in seconds from
January 1, 1970.
interface CameraManager
{
/* get a camera instance; 'camera' is one of the camera
identifiers returned by getListOfCameras() below.
*/
any position = null;
/* the number of seconds from January 1, 1970 UTC. This can be
different from the positional timestamp (above). */
// XXXbz this should really accept a date too, no?
long long dateTime = 0;
};
// If we start using CameraPictureOptions here, remove it from DummyBinding.
interface GetCameraCallback;
interface CameraErrorCallback;
/* Select a camera to use. */
dictionary CameraSelector {
DOMString camera = "back";
};
interface CameraManager {
/* get a camera instance; options will be used to specify which
camera to get from the list returned by getListOfCameras(), e.g.:
{
camera: "front"
}
*/
[Throws]
void getCamera(CameraSelector options, GetCameraCallback callback,
void getCamera(DOMString camera,
CameraConfiguration initialConfiguration,
GetCameraCallback callback,
optional CameraErrorCallback errorCallback);
/* return an array of camera identifiers, e.g.
[ "front", "back" ]
/* return an array of camera identifiers, e.g.
[ "front", "back" ]
*/
[Throws]
sequence<DOMString> getListOfCameras();

View File

@ -41,6 +41,7 @@ WEBIDL_FILES = [
'BrowserElementDictionaries.webidl',
'CallEvent.webidl',
'CallsList.webidl',
'CameraCapabilities.webidl',
'CameraControl.webidl',
'CameraManager.webidl',
'CanvasRenderingContext2D.webidl',

View File

@ -4,11 +4,6 @@
# Dictionary interface name, interface file name
dictionaries = [
[ 'CameraSize', 'nsIDOMCameraManager.idl' ],
[ 'CameraRegion', 'nsIDOMCameraManager.idl' ],
[ 'CameraPosition', 'nsIDOMCameraManager.idl' ],
[ 'CameraSelector', 'nsIDOMCameraManager.idl' ],
[ 'CameraRecordingOptions', 'nsIDOMCameraManager.idl' ],
[ 'SmsThreadListItem', 'nsIMobileMessageCallback.idl' ],
[ 'MmsAttachment', 'nsIDOMMozMmsMessage.idl' ]
]

View File

@ -135,7 +135,6 @@
@BINPATH@/components/dom.xpt
@BINPATH@/components/dom_apps.xpt
@BINPATH@/components/dom_base.xpt
@BINPATH@/components/dom_camera.xpt
@BINPATH@/components/dom_canvas.xpt
@BINPATH@/components/dom_core.xpt
@BINPATH@/components/dom_css.xpt

View File

@ -460,7 +460,6 @@
"dom/apps/tests/test_packaged_app_update.html": "debug-only timeout",
"dom/apps/tests/test_uninstall_errors.html": "debug-only timeout",
"dom/bindings/test/test_exceptions_from_jsimplemented.html": "debug-only failure; bug 926547",
"dom/camera/test/test_camera.html": "debug-only failure; no assertions when 34 were expected",
"dom/contacts/tests/test_contacts_basics.html": "debug-only failure",
"dom/contacts/tests/test_contacts_getall.html": "debug-only failure",
"dom/datastore/tests/test_arrays.html": "debug-only failure; time out",

View File

@ -1,6 +1,6 @@
/*
* Copyright (C) 2010 The Android Open Source Project
* Copyright (C) 2012 Mozilla Foundation
* Copyright (C) 2012-2013 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -37,12 +37,12 @@ using namespace mozilla::gfx;
using namespace mozilla::layers;
GonkNativeWindow::GonkNativeWindow() :
mAbandoned(false),
mDefaultWidth(1),
mDefaultHeight(1),
mPixelFormat(PIXEL_FORMAT_RGBA_8888),
mBufferCount(MIN_BUFFER_SLOTS + 1),
mConnectedApi(NO_CONNECTED_API),
mAbandoned(false),
mFrameCounter(0),
mGeneration(0),
mNewFrameCallback(nullptr) {
@ -108,6 +108,7 @@ void GonkNativeWindow::releaseBufferFreeListUnlocked(nsTArray<SurfaceDescriptor>
void GonkNativeWindow::clearRenderingStateBuffersLocked()
{
++mGeneration;
CNW_LOGD("clearRenderingStateBuffersLocked: mGeneration=%d\n", mGeneration);
for (int i = 0; i < NUM_BUFFER_SLOTS; ++i) {
if (mSlots[i].mGraphicBuffer != NULL) {
@ -244,22 +245,33 @@ status_t GonkNativeWindow::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
renderingCount = 0;
for (int i = 0; i < mBufferCount; i++) {
const int state = mSlots[i].mBufferState;
if (state == BufferSlot::DEQUEUED) {
dequeuedCount++;
}
else if (state == BufferSlot::RENDERING) {
renderingCount++;
}
else if (state == BufferSlot::FREE) {
/* We return the oldest of the free buffers to avoid
* stalling the producer if possible. This is because
* the consumer may still have pending reads of the
* buffers in flight.
*/
if (found < 0 ||
mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) {
found = i;
}
switch (state) {
case BufferSlot::DEQUEUED:
CNW_LOGD("dequeueBuffer: slot %d is DEQUEUED\n", i);
dequeuedCount++;
break;
case BufferSlot::RENDERING:
CNW_LOGD("dequeueBuffer: slot %d is RENDERING\n", i);
renderingCount++;
break;
case BufferSlot::FREE:
CNW_LOGD("dequeueBuffer: slot %d is FREE\n", i);
/* We return the oldest of the free buffers to avoid
* stalling the producer if possible. This is because
* the consumer may still have pending reads of the
* buffers in flight.
*/
if (found < 0 ||
mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) {
found = i;
}
break;
default:
CNW_LOGD("dequeueBuffer: slot %d is %d\n", i, state);
break;
}
}
@ -330,10 +342,10 @@ status_t GonkNativeWindow::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h,
// after trying to create the surface descriptor below.
//
// So we don't need mMutex locked, which would otherwise run the risk
// of a deadlock on calling AllocSurfaceDescriptorGralloc().
// of a deadlock on calling AllocSurfaceDescriptorGralloc().
SurfaceDescriptor desc;
ImageBridgeChild* ibc;
ImageBridgeChild* ibc = nullptr;
sp<GraphicBuffer> graphicBuffer;
if (alloc) {
usage |= GraphicBuffer::USAGE_HW_TEXTURE;
@ -437,6 +449,7 @@ status_t GonkNativeWindow::queueBuffer(int buf, int64_t timestamp,
{
Mutex::Autolock lock(mMutex);
CNW_LOGD("queueBuffer: E");
CNW_LOGD("queueBuffer: buf=%d", buf);
if (mAbandoned) {
CNW_LOGE("queueBuffer: GonkNativeWindow has been abandoned!");
@ -498,6 +511,7 @@ GonkNativeWindow::getCurrentBuffer()
Fifo::iterator front(mQueue.begin());
int buf = *front;
CNW_LOGD("getCurrentBuffer: buf=%d", buf);
mSlots[buf].mBufferState = BufferSlot::RENDERING;
@ -524,7 +538,7 @@ GonkNativeWindow::returnBuffer(uint32_t aIndex, uint32_t aGeneration)
aGeneration, mGeneration);
return false;
}
if (aIndex >= mBufferCount) {
if (static_cast<int>(aIndex) >= mBufferCount) {
CNW_LOGE("returnBuffer: slot index out of range [0, %d]: %d",
mBufferCount, aIndex);
return false;