Merge m-c to b2g-inbound. a=merge

This commit is contained in:
Ryan VanderMeulen 2014-10-31 19:16:12 -04:00
commit 909d536477
41 changed files with 1237 additions and 1361 deletions

View File

@ -208,22 +208,6 @@ DOMInterfaces = {
'headerFile': 'DOMCameraManager.h'
},
'CameraRecorderAudioProfile': {
'headerFile': 'DOMCameraCapabilities.h'
},
'CameraRecorderProfile': {
'headerFile': 'DOMCameraCapabilities.h'
},
'CameraRecorderProfiles': {
'headerFile': 'DOMCameraCapabilities.h'
},
'CameraRecorderVideoProfile': {
'headerFile': 'DOMCameraCapabilities.h'
},
'CanvasRenderingContext2D': {
'implicitJSContext': [
'createImageData', 'getImageData'

View File

@ -8,6 +8,7 @@
#include "mozilla/unused.h"
#include "nsPrintfCString.h"
#include "nsIWeakReferenceUtils.h"
#include "CameraRecorderProfiles.h"
#include "CameraCommon.h"
#include "nsGlobalWindow.h"
#include "DeviceStorageFileDescriptor.h"
@ -64,6 +65,12 @@ CameraControlImpl::~CameraControlImpl()
}
}
already_AddRefed<RecorderProfileManager>
CameraControlImpl::GetRecorderProfileManager()
{
return GetRecorderProfileManagerImpl();
}
void
CameraControlImpl::Shutdown()
{
@ -107,7 +114,7 @@ CameraControlImpl::OnConfigurationChange()
MOZ_ASSERT(NS_GetCurrentThread() == mCameraThread);
RwLockAutoEnterRead lock(mListenerLock);
DOM_CAMERA_LOGI("OnConfigurationChange : %zu listeners\n", mListeners.Length());
DOM_CAMERA_LOGI("OnConfigurationChange : %d listeners\n", mListeners.Length());
for (uint32_t i = 0; i < mListeners.Length(); ++i) {
CameraControlListener* l = mListeners[i];
@ -262,7 +269,7 @@ CameraControlImpl::OnNewPreviewFrame(layers::Image* aImage, uint32_t aWidth, uin
// On Gonk, it is called from the camera driver's preview thread.
RwLockAutoEnterRead lock(mListenerLock);
DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %zu preview frame listener(s)\n",
DOM_CAMERA_LOGI("OnNewPreviewFrame: we have %d preview frame listener(s)\n",
mListeners.Length());
bool consumed = false;

View File

@ -25,6 +25,8 @@ namespace layers {
class Image;
}
class RecorderProfileManager;
class CameraControlImpl : public ICameraControl
{
public:
@ -47,6 +49,7 @@ public:
virtual nsresult StopRecording() MOZ_OVERRIDE;
virtual nsresult ResumeContinuousFocus() MOZ_OVERRIDE;
already_AddRefed<RecorderProfileManager> GetRecorderProfileManager();
uint32_t GetCameraId() { return mCameraId; }
virtual void Shutdown() MOZ_OVERRIDE;
@ -125,6 +128,8 @@ protected:
virtual nsresult PushParametersImpl() = 0;
virtual nsresult PullParametersImpl() = 0;
virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() = 0;
void OnShutterInternal();
void OnClosedInternal();

View File

@ -34,8 +34,8 @@ public:
enum HardwareState
{
kHardwareClosed,
kHardwareOpen
kHardwareOpen,
kHardwareClosed
};
virtual void OnHardwareStateChange(HardwareState aState) { }

View File

@ -0,0 +1,195 @@
/* 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 "CameraRecorderProfiles.h"
#include "jsapi.h"
#include "CameraCommon.h"
using namespace mozilla;
/**
* Video profile implementation.
*/
RecorderVideoProfile::RecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex)
: mCameraId(aCameraId)
, mQualityIndex(aQualityIndex)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
RecorderVideoProfile::~RecorderVideoProfile()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
nsresult
RecorderVideoProfile::GetJsObject(JSContext* aCx, JSObject** aObject)
{
NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG);
JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
NS_ENSURE_TRUE(o, NS_ERROR_OUT_OF_MEMORY);
const char* codec = GetCodecName();
NS_ENSURE_TRUE(codec, NS_ERROR_FAILURE);
JS::Rooted<JSString*> s(aCx, JS_NewStringCopyZ(aCx, codec));
JS::Rooted<JS::Value> v(aCx, STRING_TO_JSVAL(s));
if (!JS_SetProperty(aCx, o, "codec", v)) {
return NS_ERROR_FAILURE;
}
if (mBitrate != -1) {
v = INT_TO_JSVAL(mBitrate);
if (!JS_SetProperty(aCx, o, "bitrate", v)) {
return NS_ERROR_FAILURE;
}
}
if (mFramerate != -1) {
v = INT_TO_JSVAL(mFramerate);
if (!JS_SetProperty(aCx, o, "framerate", v)) {
return NS_ERROR_FAILURE;
}
}
if (mWidth != -1) {
v = INT_TO_JSVAL(mWidth);
if (!JS_SetProperty(aCx, o, "width", v)) {
return NS_ERROR_FAILURE;
}
}
if (mHeight != -1) {
v = INT_TO_JSVAL(mHeight);
if (!JS_SetProperty(aCx, o, "height", v)) {
return NS_ERROR_FAILURE;
}
}
*aObject = o;
return NS_OK;
}
/**
* Audio profile implementation.
*/
RecorderAudioProfile::RecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex)
: mCameraId(aCameraId)
, mQualityIndex(aQualityIndex)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
RecorderAudioProfile::~RecorderAudioProfile()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
nsresult
RecorderAudioProfile::GetJsObject(JSContext* aCx, JSObject** aObject)
{
NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG);
JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
NS_ENSURE_TRUE(o, NS_ERROR_OUT_OF_MEMORY);
const char* codec = GetCodecName();
NS_ENSURE_TRUE(codec, NS_ERROR_FAILURE);
JS::Rooted<JSString*> s(aCx, JS_NewStringCopyZ(aCx, codec));
JS::Rooted<JS::Value> v(aCx, STRING_TO_JSVAL(s));
if (!JS_SetProperty(aCx, o, "codec", v)) {
return NS_ERROR_FAILURE;
}
if (mBitrate != -1) {
v = INT_TO_JSVAL(mBitrate);
if (!JS_SetProperty(aCx, o, "bitrate", v)) {
return NS_ERROR_FAILURE;
}
}
if (mSamplerate != -1) {
v = INT_TO_JSVAL(mSamplerate);
if (!JS_SetProperty(aCx, o, "samplerate", v)) {
return NS_ERROR_FAILURE;
}
}
if (mChannels != -1) {
v = INT_TO_JSVAL(mChannels);
if (!JS_SetProperty(aCx, o, "channels", v)) {
return NS_ERROR_FAILURE;
}
}
*aObject = o;
return NS_OK;
}
/**
* Recorder Profile
*/
RecorderProfile::RecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex)
: mCameraId(aCameraId)
, mQualityIndex(aQualityIndex)
, mName(nullptr)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
RecorderProfile::~RecorderProfile()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
/**
* Recorder profile manager implementation.
*/
RecorderProfileManager::RecorderProfileManager(uint32_t aCameraId)
: mCameraId(aCameraId)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
RecorderProfileManager::~RecorderProfileManager()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
nsresult
RecorderProfileManager::GetJsObject(JSContext* aCx, JSObject** aObject) const
{
NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG);
JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
if (!o) {
return NS_ERROR_OUT_OF_MEMORY;
}
for (uint32_t q = 0; q < GetMaxQualityIndex(); ++q) {
if (!IsSupported(q)) {
continue;
}
nsRefPtr<RecorderProfile> profile = Get(q);
if (!profile) {
return NS_ERROR_OUT_OF_MEMORY;
}
const char* profileName = profile->GetName();
if (!profileName) {
// don't allow anonymous recorder profiles
continue;
}
JS::Rooted<JSObject*> p(aCx);
nsresult rv = profile->GetJsObject(aCx, p.address());
NS_ENSURE_SUCCESS(rv, rv);
JS::Rooted<JS::Value> v(aCx, OBJECT_TO_JSVAL(p));
if (!JS_SetProperty(aCx, o, profileName, v)) {
return NS_ERROR_FAILURE;
}
}
*aObject = o;
return NS_OK;
}

View File

@ -0,0 +1,274 @@
/* 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_CAMERA_RECORDER_PROFILES_H
#define DOM_CAMERA_CAMERA_RECORDER_PROFILES_H
#include "nsISupportsImpl.h"
#include "nsMimeTypes.h"
#include "nsAutoPtr.h"
#include "nsTArray.h"
#include "jsapi.h"
#include "CameraCommon.h"
namespace mozilla {
class CameraControlImpl;
class RecorderVideoProfile
{
public:
RecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex);
virtual ~RecorderVideoProfile();
int GetBitrate() const { return mBitrate; }
int GetFramerate() const { return mFramerate; }
int GetWidth() const { return mWidth; }
int GetHeight() const { return mHeight; }
enum Codec {
H263,
H264,
MPEG4SP,
UNKNOWN
};
Codec GetCodec() const { return mCodec; }
const char* GetCodecName() const
{
switch (mCodec) {
case H263: return "h263";
case H264: return "h264";
case MPEG4SP: return "mpeg4sp";
default: return nullptr;
}
}
// Get a representation of this video profile that can be returned
// to JS, possibly as a child member of another object.
//
// Return values:
// - NS_OK on success;
// - NS_ERROR_INVALID_ARG if 'aObject' is null;
// - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated;
// - NS_ERROR_FAILURE if construction of the JS object fails.
nsresult GetJsObject(JSContext* aCx, JSObject** aObject);
protected:
uint32_t mCameraId;
uint32_t mQualityIndex;
Codec mCodec;
int mBitrate;
int mFramerate;
int mWidth;
int mHeight;
};
class RecorderAudioProfile
{
public:
RecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex);
virtual ~RecorderAudioProfile();
int GetBitrate() const { return mBitrate; }
int GetSamplerate() const { return mSamplerate; }
int GetChannels() const { return mChannels; }
enum Codec {
AMRNB,
AMRWB,
AAC,
UNKNOWN
};
Codec GetCodec() const { return mCodec; }
const char* GetCodecName() const
{
switch (mCodec) {
case AMRNB: return "amrnb";
case AMRWB: return "amrwb";
case AAC: return "aac";
default: return nullptr;
}
}
// Get a representation of this audio profile that can be returned
// to JS, possibly as a child member of another object.
//
// Return values:
// - NS_OK on success;
// - NS_ERROR_INVALID_ARG if 'aObject' is null;
// - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated;
// - NS_ERROR_FAILURE if construction of the JS object fails.
nsresult GetJsObject(JSContext* aCx, JSObject** aObject);
protected:
uint32_t mCameraId;
uint32_t mQualityIndex;
Codec mCodec;
int mBitrate;
int mSamplerate;
int mChannels;
};
class RecorderProfile
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecorderProfile)
RecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex);
virtual const RecorderVideoProfile* GetVideoProfile() const = 0;
virtual const RecorderAudioProfile* GetAudioProfile() const = 0;
const char* GetName() const { return mName; }
enum FileFormat {
THREE_GPP,
MPEG4,
UNKNOWN
};
FileFormat GetFileFormat() const { return mFileFormat; }
const char* GetFileFormatName() const
{
switch (mFileFormat) {
case THREE_GPP: return "3gp";
case MPEG4: return "mp4";
default: return nullptr;
}
}
const char* GetFileMimeType() const
{
switch (mFileFormat) {
case THREE_GPP: return VIDEO_3GPP;
case MPEG4: return VIDEO_MP4;
default: return nullptr;
}
}
// Get a representation of this recorder profile that can be returned
// to JS, possibly as a child member of another object.
//
// Return values:
// - NS_OK on success;
// - NS_ERROR_INVALID_ARG if 'aObject' is null;
// - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated;
// - NS_ERROR_FAILURE if construction of the JS object fails.
virtual nsresult GetJsObject(JSContext* aCx, JSObject** aObject) = 0;
protected:
virtual ~RecorderProfile();
uint32_t mCameraId;
uint32_t mQualityIndex;
const char* mName;
FileFormat mFileFormat;
};
template <class Audio, class Video>
class RecorderProfileBase : public RecorderProfile
{
public:
RecorderProfileBase(uint32_t aCameraId, uint32_t aQualityIndex)
: RecorderProfile(aCameraId, aQualityIndex)
, mVideo(aCameraId, aQualityIndex)
, mAudio(aCameraId, aQualityIndex)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
virtual ~RecorderProfileBase()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
const RecorderVideoProfile* GetVideoProfile() const { return &mVideo; }
const RecorderAudioProfile* GetAudioProfile() const { return &mAudio; }
// Get a representation of this recorder profile that can be returned
// to JS, possibly as a child member of another object.
//
// Return values:
// - NS_OK on success;
// - NS_ERROR_INVALID_ARG if 'aObject' is null;
// - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated;
// - NS_ERROR_NOT_AVAILABLE if the profile has no file format name;
// - NS_ERROR_FAILURE if construction of the JS object fails.
nsresult
GetJsObject(JSContext* aCx, JSObject** aObject)
{
NS_ENSURE_TRUE(aObject, NS_ERROR_INVALID_ARG);
const char* format = GetFileFormatName();
if (!format) {
// the profile must have a file format
return NS_ERROR_NOT_AVAILABLE;
}
JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, JS::NullPtr(), JS::NullPtr()));
if (!o) {
return NS_ERROR_OUT_OF_MEMORY;
}
JS::Rooted<JSString*> s(aCx, JS_NewStringCopyZ(aCx, format));
JS::Rooted<JS::Value> v(aCx, STRING_TO_JSVAL(s));
if (!JS_SetProperty(aCx, o, "format", v)) {
return NS_ERROR_FAILURE;
}
JS::Rooted<JSObject*> video(aCx);
nsresult rv = mVideo.GetJsObject(aCx, video.address());
NS_ENSURE_SUCCESS(rv, rv);
v = OBJECT_TO_JSVAL(video);
if (!JS_SetProperty(aCx, o, "video", v)) {
return NS_ERROR_FAILURE;
}
JS::Rooted<JSObject*> audio(aCx);
rv = mAudio.GetJsObject(aCx, audio.address());
NS_ENSURE_SUCCESS(rv, rv);
v = OBJECT_TO_JSVAL(audio);
if (!JS_SetProperty(aCx, o, "audio", v)) {
return NS_ERROR_FAILURE;
}
*aObject = o;
return NS_OK;
}
protected:
Video mVideo;
Audio mAudio;
};
class RecorderProfileManager
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecorderProfileManager)
virtual bool IsSupported(uint32_t aQualityIndex) const { return true; }
virtual already_AddRefed<RecorderProfile> Get(uint32_t aQualityIndex) const = 0;
uint32_t GetMaxQualityIndex() const { return mMaxQualityIndex; }
// Get a representation of all supported recorder profiles that can be
// returned to JS.
//
// Return values:
// - NS_OK on success;
// - NS_ERROR_INVALID_ARG if 'aObject' is null;
// - NS_ERROR_OUT_OF_MEMORY if a new object could not be allocated;
// - NS_ERROR_NOT_AVAILABLE if the profile has no file format name;
// - NS_ERROR_FAILURE if construction of the JS object fails.
nsresult GetJsObject(JSContext* aCx, JSObject** aObject) const;
protected:
explicit RecorderProfileManager(uint32_t aCameraId);
virtual ~RecorderProfileManager();
uint32_t mCameraId;
uint32_t mMaxQualityIndex;
};
} // namespace mozilla
#endif // DOM_CAMERA_CAMERA_RECORDER_PROFILES_H

View File

@ -12,202 +12,28 @@
#include "Navigator.h"
#include "CameraCommon.h"
#include "ICameraControl.h"
#include "CameraRecorderProfiles.h"
namespace mozilla {
namespace dom {
/**
* CameraRecorderVideoProfile
*/
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderVideoProfile, mParent)
NS_IMPL_CYCLE_COLLECTION_CLASS(CameraCapabilities)
NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderVideoProfile)
NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderVideoProfile)
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_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderVideoProfile)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_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
JSObject*
CameraRecorderVideoProfile::WrapObject(JSContext* aCx)
{
return CameraRecorderVideoProfileBinding::Wrap(aCx, this);
}
CameraRecorderVideoProfile::CameraRecorderVideoProfile(nsISupports* aParent,
const ICameraControl::RecorderProfile::Video& aProfile)
: mParent(aParent)
, mCodec(aProfile.GetCodec())
, mBitrate(aProfile.GetBitsPerSecond())
, mFramerate(aProfile.GetFramesPerSecond())
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
mSize.mWidth = aProfile.GetSize().width;
mSize.mHeight = aProfile.GetSize().height;
DOM_CAMERA_LOGI(" video: '%s' %ux%u bps=%u fps=%u\n",
NS_ConvertUTF16toUTF8(mCodec).get(), mSize.mWidth, mSize.mHeight, mBitrate, mFramerate);
}
CameraRecorderVideoProfile::~CameraRecorderVideoProfile()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
/**
* CameraRecorderAudioProfile
*/
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderAudioProfile, mParent)
NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderAudioProfile)
NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderAudioProfile)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderAudioProfile)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
JSObject*
CameraRecorderAudioProfile::WrapObject(JSContext* aCx)
{
return CameraRecorderAudioProfileBinding::Wrap(aCx, this);
}
CameraRecorderAudioProfile::CameraRecorderAudioProfile(nsISupports* aParent,
const ICameraControl::RecorderProfile::Audio& aProfile)
: mParent(aParent)
, mCodec(aProfile.GetCodec())
, mBitrate(aProfile.GetBitsPerSecond())
, mSamplerate(aProfile.GetSamplesPerSecond())
, mChannels(aProfile.GetChannels())
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
DOM_CAMERA_LOGI(" audio: '%s' bps=%u samples/s=%u channels=%u\n",
NS_ConvertUTF16toUTF8(mCodec).get(), mBitrate, mSamplerate, mChannels);
}
CameraRecorderAudioProfile::~CameraRecorderAudioProfile()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
/**
* CameraRecorderProfile
*/
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderProfile, mParent)
NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderProfile)
NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderProfile)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderProfile)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
JSObject*
CameraRecorderProfile::WrapObject(JSContext* aCx)
{
return CameraRecorderProfileBinding::Wrap(aCx, this);
}
CameraRecorderProfile::CameraRecorderProfile(nsISupports* aParent,
const ICameraControl::RecorderProfile& aProfile)
: mParent(aParent)
, mName(aProfile.GetName())
, mContainerFormat(aProfile.GetContainer())
, mMimeType(aProfile.GetMimeType())
, mVideo(new CameraRecorderVideoProfile(this, aProfile.GetVideo()))
, mAudio(new CameraRecorderAudioProfile(this, aProfile.GetAudio()))
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
DOM_CAMERA_LOGI("profile: '%s' container=%s mime-type=%s\n",
NS_ConvertUTF16toUTF8(mName).get(),
NS_ConvertUTF16toUTF8(mContainerFormat).get(),
NS_ConvertUTF16toUTF8(mMimeType).get());
}
CameraRecorderProfile::~CameraRecorderProfile()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
/**
* CameraRecorderProfiles
*/
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraRecorderProfiles, mParent)
NS_IMPL_CYCLE_COLLECTING_ADDREF(CameraRecorderProfiles)
NS_IMPL_CYCLE_COLLECTING_RELEASE(CameraRecorderProfiles)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CameraRecorderProfiles)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
JSObject*
CameraRecorderProfiles::WrapObject(JSContext* aCx)
{
return CameraRecorderProfilesBinding::Wrap(aCx, this);
}
CameraRecorderProfiles::CameraRecorderProfiles(nsISupports* aParent,
ICameraControl* aCameraControl)
: mParent(aParent)
, mCameraControl(aCameraControl)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
CameraRecorderProfiles::~CameraRecorderProfiles()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
void
CameraRecorderProfiles::GetSupportedNames(unsigned aFlags, nsTArray<nsString>& aNames)
{
DOM_CAMERA_LOGT("%s:%d : this=%p, flags=0x%x\n",
__func__, __LINE__, this, aFlags);
nsresult rv = mCameraControl->GetRecorderProfiles(aNames);
if (NS_WARN_IF(NS_FAILED(rv))) {
aNames.Clear();
}
}
CameraRecorderProfile*
CameraRecorderProfiles::NamedGetter(const nsAString& aName, bool& aFound)
{
DOM_CAMERA_LOGT("%s:%d : this=%p, name='%s'\n", __func__, __LINE__, this,
NS_ConvertUTF16toUTF8(aName).get());
CameraRecorderProfile* profile = mProfiles.GetWeak(aName, &aFound);
if (!aFound || !profile) {
nsRefPtr<ICameraControl::RecorderProfile> p = mCameraControl->GetProfileInfo(aName);
if (p) {
profile = new CameraRecorderProfile(this, *p);
mProfiles.Put(aName, profile);
aFound = true;
}
}
return profile;
}
bool
CameraRecorderProfiles::NameIsEnumerable(const nsAString& aName)
{
DOM_CAMERA_LOGT("%s:%d : this=%p, name='%s' (always returns true)\n",
__func__, __LINE__, this, NS_ConvertUTF16toUTF8(aName).get());
return true;
}
/**
* CameraCapabilities
*/
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CameraCapabilities, mWindow)
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)
@ -224,24 +50,20 @@ CameraCapabilities::HasSupport(JSContext* aCx, JSObject* aGlobal)
return Navigator::HasCameraSupport(aCx, aGlobal);
}
CameraCapabilities::CameraCapabilities(nsPIDOMWindow* aWindow,
ICameraControl* aCameraControl)
: mMaxFocusAreas(0)
, mMaxMeteringAreas(0)
, mMaxDetectedFaces(0)
, mMinExposureCompensation(0.0)
, mMaxExposureCompensation(0.0)
, mExposureCompensationStep(0.0)
CameraCapabilities::CameraCapabilities(nsPIDOMWindow* aWindow)
: mRecorderProfiles(JS::UndefinedValue())
, mWindow(aWindow)
, mCameraControl(aCameraControl)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
MOZ_COUNT_CTOR(CameraCapabilities);
mozilla::HoldJSObjects(this);
}
CameraCapabilities::~CameraCapabilities()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
mRecorderProfiles = JS::UndefinedValue();
mozilla::DropJSObjects(this);
MOZ_COUNT_DTOR(CameraCapabilities);
}
@ -260,12 +82,13 @@ CameraCapabilities::WrapObject(JSContext* aCx)
} while(0)
nsresult
CameraCapabilities::TranslateToDictionary(uint32_t aKey, nsTArray<CameraSize>& aSizes)
CameraCapabilities::TranslateToDictionary(ICameraControl* aCameraControl,
uint32_t aKey, nsTArray<CameraSize>& aSizes)
{
nsresult rv;
nsTArray<ICameraControl::Size> sizes;
rv = mCameraControl->Get(aKey, sizes);
rv = aCameraControl->Get(aKey, sizes);
if (NS_FAILED(rv)) {
return rv;
}
@ -281,218 +104,205 @@ CameraCapabilities::TranslateToDictionary(uint32_t aKey, nsTArray<CameraSize>& a
return NS_OK;
}
void
CameraCapabilities::GetPreviewSizes(nsTArray<dom::CameraSize>& retval)
nsresult
CameraCapabilities::Populate(ICameraControl* aCameraControl)
{
if (mPreviewSizes.Length() == 0) {
nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_PREVIEWSIZES,
mPreviewSizes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PREVIEWSIZES);
NS_ENSURE_TRUE(aCameraControl, NS_ERROR_INVALID_ARG);
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_ISOMODES, mIsoModes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ISOMODES);
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;
int32_t faces;
rv = aCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES, faces);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES);
mMaxDetectedFaces = faces < 0 ? 0 : faces;
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 (0x%x)\n", rv);
} else {
mRecorderProfiles = JS::ObjectValue(*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)
CameraCapabilities::GetPictureSizes(nsTArray<dom::CameraSize>& retval) const
{
if (mPictureSizes.Length() == 0) {
nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_PICTURESIZES,
mPictureSizes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTURESIZES);
}
retval = mPictureSizes;
}
void
CameraCapabilities::GetThumbnailSizes(nsTArray<dom::CameraSize>& retval)
CameraCapabilities::GetThumbnailSizes(nsTArray<dom::CameraSize>& retval) const
{
if (mThumbnailSizes.Length() == 0) {
nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES,
mThumbnailSizes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_JPEG_THUMBNAIL_SIZES);
}
retval = mThumbnailSizes;
}
void
CameraCapabilities::GetVideoSizes(nsTArray<dom::CameraSize>& retval)
CameraCapabilities::GetVideoSizes(nsTArray<dom::CameraSize>& retval) const
{
if (mVideoSizes.Length() == 0) {
nsresult rv = TranslateToDictionary(CAMERA_PARAM_SUPPORTED_VIDEOSIZES,
mVideoSizes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_VIDEOSIZES);
}
retval = mVideoSizes;
}
void
CameraCapabilities::GetFileFormats(nsTArray<nsString>& retval)
CameraCapabilities::GetFileFormats(nsTArray<nsString>& retval) const
{
if (mFileFormats.Length() == 0) {
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_PICTUREFORMATS,
mFileFormats);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_PICTUREFORMATS);
}
retval = mFileFormats;
}
void
CameraCapabilities::GetWhiteBalanceModes(nsTArray<nsString>& retval)
CameraCapabilities::GetWhiteBalanceModes(nsTArray<nsString>& retval) const
{
if (mWhiteBalanceModes.Length() == 0) {
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_WHITEBALANCES,
mWhiteBalanceModes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_WHITEBALANCES);
}
retval = mWhiteBalanceModes;
}
void
CameraCapabilities::GetSceneModes(nsTArray<nsString>& retval)
CameraCapabilities::GetSceneModes(nsTArray<nsString>& retval) const
{
if (mSceneModes.Length() == 0) {
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_SCENEMODES,
mSceneModes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_SCENEMODES);
}
retval = mSceneModes;
}
void
CameraCapabilities::GetEffects(nsTArray<nsString>& retval)
CameraCapabilities::GetEffects(nsTArray<nsString>& retval) const
{
if (mEffects.Length() == 0) {
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_EFFECTS,
mEffects);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EFFECTS);
}
retval = mEffects;
}
void
CameraCapabilities::GetFlashModes(nsTArray<nsString>& retval)
CameraCapabilities::GetFlashModes(nsTArray<nsString>& retval) const
{
if (mFlashModes.Length() == 0) {
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_FLASHMODES,
mFlashModes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FLASHMODES);
}
retval = mFlashModes;
}
void
CameraCapabilities::GetFocusModes(nsTArray<nsString>& retval)
CameraCapabilities::GetFocusModes(nsTArray<nsString>& retval) const
{
if (mFocusModes.Length() == 0) {
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_FOCUSMODES,
mFocusModes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_FOCUSMODES);
}
retval = mFocusModes;
}
void
CameraCapabilities::GetZoomRatios(nsTArray<double>& retval)
CameraCapabilities::GetZoomRatios(nsTArray<double>& retval) const
{
if (mZoomRatios.Length() == 0) {
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS,
mZoomRatios);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS);
}
retval = mZoomRatios;
}
uint32_t
CameraCapabilities::MaxFocusAreas()
CameraCapabilities::MaxFocusAreas() const
{
if (mMaxFocusAreas == 0) {
int32_t areas;
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS,
areas);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXFOCUSAREAS);
mMaxFocusAreas = areas < 0 ? 0 : areas;
}
return mMaxFocusAreas;
}
uint32_t
CameraCapabilities::MaxMeteringAreas()
CameraCapabilities::MaxMeteringAreas() const
{
if (mMaxMeteringAreas == 0) {
int32_t areas;
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS,
areas);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXMETERINGAREAS);
mMaxMeteringAreas = areas < 0 ? 0 : areas;
}
return mMaxMeteringAreas;
}
uint32_t
CameraCapabilities::MaxDetectedFaces()
CameraCapabilities::MaxDetectedFaces() const
{
if (mMaxDetectedFaces == 0) {
int32_t faces;
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES,
faces);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXDETECTEDFACES);
mMaxDetectedFaces = faces < 0 ? 0 : faces;
}
return mMaxDetectedFaces;
}
double
CameraCapabilities::MinExposureCompensation()
CameraCapabilities::MinExposureCompensation() const
{
if (mMinExposureCompensation == 0.0) {
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION,
mMinExposureCompensation);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MINEXPOSURECOMPENSATION);
}
return mMinExposureCompensation;
}
double
CameraCapabilities::MaxExposureCompensation()
CameraCapabilities::MaxExposureCompensation() const
{
if (mMaxExposureCompensation == 0.0) {
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION,
mMaxExposureCompensation);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_MAXEXPOSURECOMPENSATION);
}
return mMaxExposureCompensation;
}
double
CameraCapabilities::ExposureCompensationStep()
CameraCapabilities::ExposureCompensationStep() const
{
if (mExposureCompensationStep == 0.0) {
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP,
mExposureCompensationStep);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_EXPOSURECOMPENSATIONSTEP);
}
return mExposureCompensationStep;
}
CameraRecorderProfiles*
CameraCapabilities::RecorderProfiles()
void
CameraCapabilities::GetRecorderProfiles(JSContext* aCx,
JS::MutableHandle<JS::Value> aRetval) const
{
nsRefPtr<CameraRecorderProfiles> profiles = mRecorderProfiles;
if (!mRecorderProfiles) {
profiles = new CameraRecorderProfiles(this, mCameraControl);
mRecorderProfiles = profiles;
}
return profiles;
JS::ExposeValueToActiveJS(mRecorderProfiles);
aRetval.set(mRecorderProfiles);
}
void
CameraCapabilities::GetIsoModes(nsTArray<nsString>& retval)
CameraCapabilities::GetIsoModes(nsTArray<nsString>& retval) const
{
if (mIsoModes.Length() == 0) {
nsresult rv = mCameraControl->Get(CAMERA_PARAM_SUPPORTED_ISOMODES,
mIsoModes);
LOG_IF_ERROR(rv, CAMERA_PARAM_SUPPORTED_ISOMODES);
}
retval = mIsoModes;
}

View File

@ -9,173 +9,23 @@
#include "nsString.h"
#include "nsAutoPtr.h"
#include "base/basictypes.h"
#include "mozilla/Attributes.h"
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/CameraManagerBinding.h"
#include "nsCycleCollectionParticipant.h"
#include "nsWrapperCache.h"
#include "nsPIDOMWindow.h"
#include "nsHashKeys.h"
#include "nsRefPtrHashtable.h"
#include "nsDataHashtable.h"
#include "ICameraControl.h"
struct JSContext;
class nsPIDOMWindow;
namespace mozilla {
class ICameraControl;
class RecorderProfileManager;
namespace dom {
/**
* CameraRecorderVideoProfile
*/
class CameraRecorderVideoProfile MOZ_FINAL : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderVideoProfile)
explicit CameraRecorderVideoProfile(nsISupports* aParent,
const ICameraControl::RecorderProfile::Video& aProfile);
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
uint32_t BitsPerSecond() const { return mBitrate; }
uint32_t FramesPerSecond() const { return mFramerate; }
void GetCodec(nsAString& aCodec) const { aCodec = mCodec; }
void GetSize(dom::CameraSize& aSize) const { aSize = mSize; }
// XXXmikeh - legacy, remove these when the Camera app is updated
uint32_t Width() const { return mSize.mWidth; }
uint32_t Height() const { return mSize.mHeight; }
protected:
virtual ~CameraRecorderVideoProfile();
nsCOMPtr<nsISupports> mParent;
const nsString mCodec;
uint32_t mBitrate;
uint32_t mFramerate;
dom::CameraSize mSize;
private:
DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderVideoProfile);
};
/**
* CameraRecorderAudioProfile
*/
class CameraRecorderAudioProfile MOZ_FINAL : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderAudioProfile)
explicit CameraRecorderAudioProfile(nsISupports* aParent,
const ICameraControl::RecorderProfile::Audio& aProfile);
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
uint32_t BitsPerSecond() const { return mBitrate; }
uint32_t SamplesPerSecond() const { return mSamplerate; }
uint32_t Channels() const { return mChannels; }
void GetCodec(nsAString& aCodec) const { aCodec = mCodec; }
protected:
virtual ~CameraRecorderAudioProfile();
nsCOMPtr<nsISupports> mParent;
const nsString mCodec;
uint32_t mBitrate;
uint32_t mSamplerate;
uint32_t mChannels;
private:
DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderAudioProfile);
};
/**
* CameraRecorderProfile
*/
class CameraRecorderProfile MOZ_FINAL : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderProfile)
explicit CameraRecorderProfile(nsISupports* aParent,
const ICameraControl::RecorderProfile& aProfile);
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
void GetMimeType(nsAString& aMimeType) const { aMimeType = mMimeType; }
CameraRecorderVideoProfile* Video() { return mVideo; }
CameraRecorderAudioProfile* Audio() { return mAudio; }
void GetName(nsAString& aName) const { aName = mName; }
void
GetContainerFormat(nsAString& aContainerFormat) const
{
aContainerFormat = mContainerFormat;
}
protected:
virtual ~CameraRecorderProfile();
nsCOMPtr<nsISupports> mParent;
const nsString mName;
const nsString mContainerFormat;
const nsString mMimeType;
nsRefPtr<CameraRecorderVideoProfile> mVideo;
nsRefPtr<CameraRecorderAudioProfile> mAudio;
private:
DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderProfile);
};
/**
* CameraRecorderProfiles
*/
class CameraRecorderProfiles MOZ_FINAL : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CameraRecorderProfiles)
explicit CameraRecorderProfiles(nsISupports* aParent,
ICameraControl* aCameraControl);
nsISupports* GetParentObject() const { return mParent; }
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
CameraRecorderProfile* NamedGetter(const nsAString& aName, bool& aFound);
bool NameIsEnumerable(const nsAString& aName);
void GetSupportedNames(unsigned aFlags, nsTArray<nsString>& aNames);
protected:
virtual ~CameraRecorderProfiles();
nsCOMPtr<nsISupports> mParent;
nsRefPtr<ICameraControl> mCameraControl;
nsRefPtrHashtable<nsStringHashKey, CameraRecorderProfile> mProfiles;
private:
DISALLOW_EVIL_CONSTRUCTORS(CameraRecorderProfiles);
};
/**
* CameraCapabilities
*/
class CameraCapabilities MOZ_FINAL : public nsISupports
, public nsWrapperCache
{
@ -190,38 +40,45 @@ public:
// Great Renaming proposed in bug 983177.
static bool HasSupport(JSContext* aCx, JSObject* aGlobal);
explicit CameraCapabilities(nsPIDOMWindow* aWindow,
ICameraControl* aCameraControl);
explicit CameraCapabilities(nsPIDOMWindow* aWindow);
// Populate the camera capabilities interface from the specific
// camera control object.
//
// Return values:
// - NS_OK on success;
// - NS_ERROR_INVALID_ARG if 'aCameraControl' is null.
nsresult Populate(ICameraControl* aCameraControl);
nsPIDOMWindow* GetParentObject() const { return mWindow; }
virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
void GetPreviewSizes(nsTArray<CameraSize>& aRetVal);
void GetPictureSizes(nsTArray<CameraSize>& aRetVal);
void GetThumbnailSizes(nsTArray<CameraSize>& aRetVal);
void GetVideoSizes(nsTArray<CameraSize>& aRetVal);
void GetFileFormats(nsTArray<nsString>& aRetVal);
void GetWhiteBalanceModes(nsTArray<nsString>& aRetVal);
void GetSceneModes(nsTArray<nsString>& aRetVal);
void GetEffects(nsTArray<nsString>& aRetVal);
void GetFlashModes(nsTArray<nsString>& aRetVal);
void GetFocusModes(nsTArray<nsString>& aRetVal);
void GetZoomRatios(nsTArray<double>& aRetVal);
uint32_t MaxFocusAreas();
uint32_t MaxMeteringAreas();
uint32_t MaxDetectedFaces();
double MinExposureCompensation();
double MaxExposureCompensation();
double ExposureCompensationStep();
void GetIsoModes(nsTArray<nsString>& aRetVal);
CameraRecorderProfiles* RecorderProfiles();
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;
uint32_t MaxDetectedFaces() const;
double MinExposureCompensation() const;
double MaxExposureCompensation() const;
double ExposureCompensationStep() const;
void GetRecorderProfiles(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval) const;
void GetIsoModes(nsTArray<nsString>& aRetVal) const;
protected:
~CameraCapabilities();
nsresult TranslateToDictionary(uint32_t aKey, nsTArray<CameraSize>& aSizes);
nsresult TranslateToDictionary(ICameraControl* aCameraControl,
uint32_t aKey, nsTArray<CameraSize>& aSizes);
nsTArray<CameraSize> mPreviewSizes;
nsTArray<CameraSize> mPictureSizes;
@ -246,12 +103,10 @@ protected:
double mMaxExposureCompensation;
double mExposureCompensationStep;
nsRefPtr<nsPIDOMWindow> mWindow;
nsRefPtr<ICameraControl> mCameraControl;
nsRefPtr<CameraRecorderProfiles> mRecorderProfiles;
nsRefPtr<RecorderProfileManager> mRecorderProfileManager;
JS::Heap<JS::Value> mRecorderProfiles;
private:
DISALLOW_EVIL_CONSTRUCTORS(CameraCapabilities);
nsRefPtr<nsPIDOMWindow> mWindow;
};
} // namespace dom

View File

@ -664,7 +664,12 @@ nsDOMCameraControl::Capabilities()
nsRefPtr<CameraCapabilities> caps = mCapabilities;
if (!caps) {
caps = new CameraCapabilities(mWindow, mCameraControl);
caps = new CameraCapabilities(mWindow);
nsresult rv = caps->Populate(mCameraControl);
if (NS_FAILED(rv)) {
DOM_CAMERA_LOGW("Failed to populate camera capabilities (%d)\n", rv);
return nullptr;
}
mCapabilities = caps;
}
@ -1135,9 +1140,10 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
MOZ_ASSERT(NS_IsMainThread());
ErrorResult ignored;
DOM_CAMERA_LOGI("DOM OnHardwareStateChange(%d)\n", aState);
switch (aState) {
case CameraControlListener::kHardwareOpen:
DOM_CAMERA_LOGI("DOM OnHardwareStateChange: open\n");
{
// The hardware is open, so we can return a camera to JS, even if
// the preview hasn't started yet.
@ -1158,7 +1164,6 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
break;
case CameraControlListener::kHardwareClosed:
DOM_CAMERA_LOGI("DOM OnHardwareStateChange: closed\n");
{
nsRefPtr<Promise> promise = mReleasePromise.forget();
if (promise || mReleaseOnSuccessCb) {
@ -1183,7 +1188,6 @@ nsDOMCameraControl::OnHardwareStateChange(CameraControlListener::HardwareState a
break;
default:
DOM_CAMERA_LOGE("DOM OnHardwareStateChange: UNKNOWN=%d\n", aState);
MOZ_ASSERT_UNREACHABLE("Unanticipated camera hardware state");
}
}
@ -1398,7 +1402,7 @@ nsDOMCameraControl::OnAutoFocusMoving(bool aIsMoving)
void
nsDOMCameraControl::OnFacesDetected(const nsTArray<ICameraControl::Face>& aFaces)
{
DOM_CAMERA_LOGI("DOM OnFacesDetected %zu face(s)\n", aFaces.Length());
DOM_CAMERA_LOGI("DOM OnFacesDetected %u face(s)\n", aFaces.Length());
MOZ_ASSERT(NS_IsMainThread());
Sequence<OwningNonNull<DOMCameraDetectedFace> > faces;

View File

@ -59,7 +59,7 @@ nsDOMCameraManager::nsDOMCameraManager(nsPIDOMWindow* aWindow)
, mWindow(aWindow)
{
/* member initializers and constructor code */
DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%" PRIx64 "\n", __func__, __LINE__, this, mWindowId);
DOM_CAMERA_LOGT("%s:%d : this=%p, windowId=%llx\n", __func__, __LINE__, this, mWindowId);
MOZ_COUNT_CTOR(nsDOMCameraManager);
}
@ -364,7 +364,7 @@ nsDOMCameraManager::PermissionCancelled(uint32_t aCameraId,
void
nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl)
{
DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%" PRIx64 "\n", aDOMCameraControl, mWindowId);
DOM_CAMERA_LOGI(">>> Register( aDOMCameraControl = %p ) mWindowId = 0x%llx\n", aDOMCameraControl, mWindowId);
MOZ_ASSERT(NS_IsMainThread());
// Put the camera control into the hash table
@ -379,7 +379,7 @@ nsDOMCameraManager::Register(nsDOMCameraControl* aDOMCameraControl)
void
nsDOMCameraManager::Shutdown(uint64_t aWindowId)
{
DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%" PRIx64 " )\n", aWindowId);
DOM_CAMERA_LOGI(">>> Shutdown( aWindowId = 0x%llx )\n", aWindowId);
MOZ_ASSERT(NS_IsMainThread());
CameraControls* controls = sActiveWindows->Get(aWindowId);

View File

@ -45,9 +45,6 @@ public:
virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
virtual nsresult GetRecorderProfiles(nsTArray<nsString>& aProfiles) MOZ_OVERRIDE { return NS_ERROR_NOT_IMPLEMENTED; }
virtual RecorderProfile* GetProfileInfo(const nsAString& aProfile) MOZ_OVERRIDE { return nullptr; }
nsresult PushParameters() { return NS_ERROR_NOT_INITIALIZED; }
nsresult PullParameters() { return NS_ERROR_NOT_INITIALIZED; }
@ -66,6 +63,7 @@ protected:
virtual nsresult StopRecordingImpl() { return NS_ERROR_NOT_INITIALIZED; }
virtual nsresult PushParametersImpl() { return NS_ERROR_NOT_INITIALIZED; }
virtual nsresult PullParametersImpl() { return NS_ERROR_NOT_INITIALIZED; }
virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() MOZ_OVERRIDE { return nullptr; }
private:
FallbackCameraControl(const FallbackCameraControl&) MOZ_DELETE;

View File

@ -71,8 +71,11 @@ nsGonkCameraControl::nsGonkCameraControl(uint32_t aCameraId)
, mAutoFlashModeOverridden(false)
, mSeparateVideoAndPreviewSizesSupported(false)
, mDeferConfigUpdate(0)
, mMediaProfiles(nullptr)
, mRecorder(nullptr)
, mRecorderMonitor("GonkCameraControl::mRecorder.Monitor")
, mProfileManager(nullptr)
, mRecorderProfile(nullptr)
, mVideoFile(nullptr)
, mReentrantMonitor("GonkCameraControl::OnTakePicture.Monitor")
{
@ -142,7 +145,6 @@ nsGonkCameraControl::Initialize()
}
DOM_CAMERA_LOGI("Initializing camera %d (this=%p, mCameraHw=%p)\n", mCameraId, this, mCameraHw.get());
mCurrentConfiguration.mRecorderProfile.Truncate();
// Initialize our camera configuration database.
PullParametersImpl();
@ -303,6 +305,9 @@ nsGonkCameraControl::SetPictureConfiguration(const Configuration& aConfig)
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
// remove any existing recorder profile
mRecorderProfile = nullptr;
nsresult rv = SetPreviewSize(aConfig.mPreviewSize);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
@ -955,7 +960,7 @@ nsGonkCameraControl::StartRecordingImpl(DeviceStorageFileDescriptor* aFileDescri
ReentrantMonitorAutoEnter mon(mRecorderMonitor);
NS_ENSURE_TRUE(!mCurrentConfiguration.mRecorderProfile.IsEmpty(), NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_TRUE(mRecorderProfile, NS_ERROR_NOT_INITIALIZED);
NS_ENSURE_FALSE(mRecorder, NS_ERROR_FAILURE);
/**
@ -1363,25 +1368,32 @@ nsGonkCameraControl::SetVideoConfiguration(const Configuration& aConfig)
{
DOM_CAMERA_LOGT("%s:%d\n", __func__, __LINE__);
RecorderProfile* profile;
if (!mRecorderProfiles.Get(aConfig.mRecorderProfile, &profile)) {
DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n",
NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile).get());
// read preferences for camcorder
mMediaProfiles = MediaProfiles::getInstance();
nsAutoCString profile = NS_ConvertUTF16toUTF8(aConfig.mRecorderProfile);
mRecorderProfile = GetGonkRecorderProfileManager().take()->Get(profile.get());
if (!mRecorderProfile) {
DOM_CAMERA_LOGE("Recorder profile '%s' is not supported\n", profile.get());
return NS_ERROR_INVALID_ARG;
}
mCurrentConfiguration.mRecorderProfile = aConfig.mRecorderProfile;
const RecorderProfile::Video& video(profile->GetVideo());
const Size& size = video.GetSize();
int fps = video.GetFramesPerSecond();
if (fps <= 0 || size.width <= 0 || size.height <= 0) {
DOM_CAMERA_LOGE("Can't configure video with fps=%d, width=%d, height=%d\n",
fps, size.width, size.height);
const GonkRecorderVideoProfile* video = mRecorderProfile->GetGonkVideoProfile();
int width = video->GetWidth();
int height = video->GetHeight();
int fps = video->GetFramerate();
if (fps == -1 || width < 0 || height < 0) {
DOM_CAMERA_LOGE("Can't configure preview with fps=%d, width=%d, height=%d\n",
fps, width, height);
return NS_ERROR_FAILURE;
}
PullParametersImpl();
Size size;
size.width = static_cast<uint32_t>(width);
size.height = static_cast<uint32_t>(height);
{
ICameraControlParameterSetAutoEnter set(this);
nsresult rv;
@ -1587,12 +1599,8 @@ nsGonkCameraControl::SetupRecording(int aFd, int aRotation,
mRecorder = new GonkRecorder();
CHECK_SETARG_RETURN(mRecorder->init(), NS_ERROR_FAILURE);
nsresult rv =
GonkRecorderProfile::ConfigureRecorder(*mRecorder, mCameraId,
mCurrentConfiguration.mRecorderProfile);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsresult rv = mRecorderProfile->ConfigureRecorder(mRecorder);
NS_ENSURE_SUCCESS(rv, rv);
CHECK_SETARG_RETURN(mRecorder->setCamera(mCameraHw), NS_ERROR_FAILURE);
@ -1663,76 +1671,27 @@ nsGonkCameraControl::StopImpl()
return NS_OK;
}
nsresult
nsGonkCameraControl::LoadRecorderProfiles()
already_AddRefed<GonkRecorderProfileManager>
nsGonkCameraControl::GetGonkRecorderProfileManager()
{
if (mRecorderProfiles.Count() == 0) {
nsTArray<nsRefPtr<RecorderProfile>> profiles;
nsresult rv = GonkRecorderProfile::GetAll(mCameraId, profiles);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (!mProfileManager) {
nsTArray<Size> sizes;
rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes);
if (NS_WARN_IF(NS_FAILED(rv))) {
return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv = Get(CAMERA_PARAM_SUPPORTED_VIDEOSIZES, sizes);
NS_ENSURE_SUCCESS(rv, nullptr);
// Limit profiles to those video sizes supported by the camera hardware...
for (nsTArray<RecorderProfile>::size_type i = 0; i < profiles.Length(); ++i) {
int width = profiles[i]->GetVideo().GetSize().width;
int height = profiles[i]->GetVideo().GetSize().height;
if (width < 0 || height < 0) {
DOM_CAMERA_LOGW("Ignoring weird profile '%s' with width and/or height < 0\n",
NS_ConvertUTF16toUTF8(profiles[i]->GetName()).get());
continue;
}
for (nsTArray<Size>::size_type n = 0; n < sizes.Length(); ++n) {
if (static_cast<uint32_t>(width) == sizes[n].width &&
static_cast<uint32_t>(height) == sizes[n].height) {
mRecorderProfiles.Put(profiles[i]->GetName(), profiles[i]);
break;
}
}
}
mProfileManager = new GonkRecorderProfileManager(mCameraId);
mProfileManager->SetSupportedResolutions(sizes);
}
return NS_OK;
nsRefPtr<GonkRecorderProfileManager> profileMgr = mProfileManager;
return profileMgr.forget();
}
/* static */ PLDHashOperator
nsGonkCameraControl::Enumerate(const nsAString& aProfileName,
RecorderProfile* aProfile,
void* aUserArg)
already_AddRefed<RecorderProfileManager>
nsGonkCameraControl::GetRecorderProfileManagerImpl()
{
nsTArray<nsString>* profiles = static_cast<nsTArray<nsString>*>(aUserArg);
MOZ_ASSERT(profiles);
profiles->AppendElement(aProfileName);
return PL_DHASH_NEXT;
}
nsresult
nsGonkCameraControl::GetRecorderProfiles(nsTArray<nsString>& aProfiles)
{
nsresult rv = LoadRecorderProfiles();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
aProfiles.Clear();
mRecorderProfiles.EnumerateRead(Enumerate, static_cast<void*>(&aProfiles));
return NS_OK;
}
ICameraControl::RecorderProfile*
nsGonkCameraControl::GetProfileInfo(const nsAString& aProfile)
{
RecorderProfile* profile;
if (!mRecorderProfiles.Get(aProfile, &profile)) {
return nullptr;
}
return profile;
nsRefPtr<RecorderProfileManager> profileMgr = GetGonkRecorderProfileManager();
return profileMgr.forget();
}
void

View File

@ -18,7 +18,6 @@
#define DOM_CAMERA_GONKCAMERACONTROL_H
#include "base/basictypes.h"
#include "nsRefPtrHashtable.h"
#include <media/MediaProfiles.h>
#include "mozilla/ReentrantMonitor.h"
#include "DeviceStorage.h"
@ -80,10 +79,6 @@ public:
virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) MOZ_OVERRIDE;
virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) MOZ_OVERRIDE;
virtual nsresult GetRecorderProfiles(nsTArray<nsString>& aProfiles) MOZ_OVERRIDE;
virtual ICameraControl::RecorderProfile*
GetProfileInfo(const nsAString& aProfile) MOZ_OVERRIDE;
nsresult PushParameters();
nsresult PullParameters();
@ -125,6 +120,8 @@ protected:
virtual nsresult ResumeContinuousFocusImpl() MOZ_OVERRIDE;
virtual nsresult PushParametersImpl() MOZ_OVERRIDE;
virtual nsresult PullParametersImpl() MOZ_OVERRIDE;
virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManagerImpl() MOZ_OVERRIDE;
already_AddRefed<GonkRecorderProfileManager> GetGonkRecorderProfileManager();
nsresult SetupRecording(int aFd, int aRotation, uint64_t aMaxFileSizeBytes,
uint64_t aMaxVideoLengthMs);
@ -134,11 +131,6 @@ protected:
nsresult PausePreview();
nsresult GetSupportedSize(const Size& aSize, const nsTArray<Size>& supportedSizes, Size& best);
nsresult LoadRecorderProfiles();
static PLDHashOperator Enumerate(const nsAString& aProfileName,
RecorderProfile* aProfile,
void* aUserArg);
friend class SetPictureSize;
friend class SetThumbnailSize;
nsresult SetPictureSize(const Size& aSize);
@ -165,14 +157,16 @@ protected:
nsRefPtr<mozilla::layers::ImageContainer> mImageContainer;
android::MediaProfiles* mMediaProfiles;
nsRefPtr<android::GonkRecorder> mRecorder;
// Touching mRecorder happens inside this monitor because the destructor
// can run on any thread, and we need to be able to clean up properly if
// GonkCameraControl goes away.
ReentrantMonitor mRecorderMonitor;
// Supported recorder profiles
nsRefPtrHashtable<nsStringHashKey, RecorderProfile> mRecorderProfiles;
// Camcorder profile settings for the desired quality level
nsRefPtr<GonkRecorderProfileManager> mProfileManager;
nsRefPtr<GonkRecorderProfile> mRecorderProfile;
nsRefPtr<DeviceStorageFile> mVideoFile;
nsString mFileFormat;

View File

@ -1,5 +1,5 @@
/*
* Copyright (C) 2012-2014 Mozilla Foundation
* Copyright (C) 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,7 +16,6 @@
#include "GonkRecorderProfiles.h"
#include <media/MediaProfiles.h>
#include "nsMimeTypes.h"
#include "GonkRecorder.h"
#include "CameraControlImpl.h"
#include "CameraCommon.h"
@ -39,318 +38,216 @@ static struct {
{ nullptr, 0 }
};
/* static */ nsClassHashtable<nsUint32HashKey, ProfileHashtable> GonkRecorderProfile::sProfiles;
/* static */ android::MediaProfiles* sMediaProfiles = nullptr;
static MediaProfiles* sMediaProfiles = nullptr;
static MediaProfiles*
GetMediaProfiles()
static bool
IsQualitySupported(uint32_t aCameraId, uint32_t aQualityIndex)
{
if (!sMediaProfiles) {
sMediaProfiles = MediaProfiles::getInstance();
}
MOZ_ASSERT(sMediaProfiles);
return sMediaProfiles;
}
static bool
IsProfileSupported(uint32_t aCameraId, uint32_t aProfileIndex)
{
MediaProfiles* profiles = GetMediaProfiles();
camcorder_quality q = static_cast<camcorder_quality>(ProfileList[aProfileIndex].quality);
return profiles->hasCamcorderProfile(static_cast<int>(aCameraId), q);
camcorder_quality q = static_cast<camcorder_quality>(ProfileList[aQualityIndex].quality);
return sMediaProfiles->hasCamcorderProfile(static_cast<int>(aCameraId), q);
}
static int
GetProfileParameter(uint32_t aCameraId, uint32_t aProfileIndex, const char* aParameter)
GetProfileParam(uint32_t aCameraId, uint32_t aQualityIndex, const char* aParam)
{
MediaProfiles* profiles = GetMediaProfiles();
camcorder_quality q = static_cast<camcorder_quality>(ProfileList[aProfileIndex].quality);
return profiles->getCamcorderProfileParamByName(aParameter, static_cast<int>(aCameraId), q);
if (!sMediaProfiles) {
sMediaProfiles = MediaProfiles::getInstance();
}
camcorder_quality q = static_cast<camcorder_quality>(ProfileList[aQualityIndex].quality);
return sMediaProfiles->getCamcorderProfileParamByName(aParam, static_cast<int>(aCameraId), q);
}
/* static */ bool
GonkRecorderVideo::Translate(video_encoder aCodec, nsAString& aCodecName)
/**
* Recorder profile.
*/
static RecorderProfile::FileFormat
TranslateFileFormat(output_format aFileFormat)
{
switch (aCodec) {
case VIDEO_ENCODER_H263:
aCodecName.AssignASCII("h263");
break;
case VIDEO_ENCODER_H264:
aCodecName.AssignASCII("h264");
break;
case VIDEO_ENCODER_MPEG_4_SP:
aCodecName.AssignASCII("mpeg4sp");
break;
default:
return false;
switch (aFileFormat) {
case OUTPUT_FORMAT_THREE_GPP: return RecorderProfile::THREE_GPP;
case OUTPUT_FORMAT_MPEG_4: return RecorderProfile::MPEG4;
default: return RecorderProfile::UNKNOWN;
}
return true;
}
int
GonkRecorderVideo::GetProfileParameter(const char* aParameter)
GonkRecorderProfile::GonkRecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex)
: RecorderProfileBase<GonkRecorderAudioProfile, GonkRecorderVideoProfile>(aCameraId, aQualityIndex)
{
return ::GetProfileParameter(mCameraId, mProfileIndex, aParameter);
DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraId=%d, mQualityIndex=%d\n", __func__, __LINE__, this, mCameraId, mQualityIndex);
mPlatformOutputFormat = static_cast<output_format>(GetProfileParam(mCameraId, mQualityIndex, "file.format"));
mFileFormat = TranslateFileFormat(mPlatformOutputFormat);
if (aQualityIndex < PROFILE_COUNT) {
mName = ProfileList[aQualityIndex].name;
DOM_CAMERA_LOGI("Created camera %d profile index %d: '%s'\n", mCameraId, mQualityIndex, mName);
}
}
GonkRecorderVideo::GonkRecorderVideo(uint32_t aCameraId, uint32_t aProfileIndex)
: mCameraId(aCameraId)
, mProfileIndex(aProfileIndex)
, mIsValid(false)
GonkRecorderProfile::~GonkRecorderProfile()
{
mPlatformEncoder = static_cast<video_encoder>(GetProfileParameter("vid.codec"));
bool isValid = Translate(mPlatformEncoder, mCodec);
int v = GetProfileParameter("vid.width");
if (v >= 0) {
mSize.width = v;
} else {
isValid = false;
}
v = GetProfileParameter("vid.height");
if (v >= 0) {
mSize.height = v;
} else {
isValid = false;
}
v = GetProfileParameter("vid.bps");
if (v >= 0) {
mBitsPerSecond = v;
} else {
isValid = false;
}
v = GetProfileParameter("vid.fps");
if (v >= 0) {
mFramesPerSecond = v;
} else {
isValid = false;
}
mIsValid = isValid;
}
/* static */ bool
GonkRecorderAudio::Translate(audio_encoder aCodec, nsAString& aCodecName)
{
switch (aCodec) {
case AUDIO_ENCODER_AMR_NB:
aCodecName.AssignASCII("amrnb");
break;
case AUDIO_ENCODER_AMR_WB:
aCodecName.AssignASCII("amrwb");
break;
case AUDIO_ENCODER_AAC:
aCodecName.AssignASCII("aac");
break;
default:
return false;
}
return true;
}
int
GonkRecorderAudio::GetProfileParameter(const char* aParameter)
{
return ::GetProfileParameter(mCameraId, mProfileIndex, aParameter);
}
GonkRecorderAudio::GonkRecorderAudio(uint32_t aCameraId, uint32_t aProfileIndex)
: mCameraId(aCameraId)
, mProfileIndex(aProfileIndex)
, mIsValid(false)
{
mPlatformEncoder = static_cast<audio_encoder>(GetProfileParameter("aud.codec"));
bool isValid = Translate(mPlatformEncoder, mCodec);
int v = GetProfileParameter("aud.ch");
if (v >= 0) {
mChannels = v;
} else {
isValid = false;
}
v = GetProfileParameter("aud.bps");
if (v >= 0) {
mBitsPerSecond = v;
} else {
isValid = false;
}
v = GetProfileParameter("aud.hz");
if (v >= 0) {
mSamplesPerSecond = v;
} else {
isValid = false;
}
mIsValid = isValid;
}
/* static */ bool
GonkRecorderProfile::Translate(output_format aContainer, nsAString& aContainerName)
{
switch (aContainer) {
case OUTPUT_FORMAT_THREE_GPP:
aContainerName.AssignASCII("3gp");
break;
case OUTPUT_FORMAT_MPEG_4:
aContainerName.AssignASCII("mp4");
break;
default:
return false;
}
return true;
}
/* static */ bool
GonkRecorderProfile::GetMimeType(output_format aContainer, nsAString& aMimeType)
{
switch (aContainer) {
case OUTPUT_FORMAT_THREE_GPP:
aMimeType.AssignASCII(VIDEO_3GPP);
break;
case OUTPUT_FORMAT_MPEG_4:
aMimeType.AssignASCII(VIDEO_MP4);
break;
default:
return false;
}
return true;
}
int
GonkRecorderProfile::GetProfileParameter(const char* aParameter)
{
return ::GetProfileParameter(mCameraId, mProfileIndex, aParameter);
}
GonkRecorderProfile::GonkRecorderProfile(uint32_t aCameraId,
uint32_t aProfileIndex,
const nsAString& aName)
: GonkRecorderProfileBase<GonkRecorderAudio, GonkRecorderVideo>(aCameraId,
aProfileIndex, aName)
, mCameraId(aCameraId)
, mProfileIndex(aProfileIndex)
, mIsValid(false)
{
mOutputFormat = static_cast<output_format>(GetProfileParameter("file.format"));
bool isValid = Translate(mOutputFormat, mContainer);
isValid = GetMimeType(mOutputFormat, mMimeType) ? isValid : false;
mIsValid = isValid && mAudio.IsValid() && mVideo.IsValid();
}
/* static */ PLDHashOperator
GonkRecorderProfile::Enumerate(const nsAString& aProfileName,
GonkRecorderProfile* aProfile,
void* aUserArg)
{
nsTArray<nsRefPtr<ICameraControl::RecorderProfile>>* profiles =
static_cast<nsTArray<nsRefPtr<ICameraControl::RecorderProfile>>*>(aUserArg);
MOZ_ASSERT(profiles);
profiles->AppendElement(aProfile);
return PL_DHASH_NEXT;
}
/* static */
ProfileHashtable*
GonkRecorderProfile::GetProfileHashtable(uint32_t aCameraId)
{
ProfileHashtable* profiles = sProfiles.Get(aCameraId);
if (!profiles) {
profiles = new ProfileHashtable;
sProfiles.Put(aCameraId, profiles);
for (uint32_t i = 0; ProfileList[i].name; ++i) {
if (IsProfileSupported(aCameraId, i)) {
DOM_CAMERA_LOGI("Profile %d '%s' supported by platform\n", i, ProfileList[i].name);
nsAutoString name;
name.AssignASCII(ProfileList[i].name);
nsRefPtr<GonkRecorderProfile> profile = new GonkRecorderProfile(aCameraId, i, name);
if (!profile->IsValid()) {
DOM_CAMERA_LOGE("Profile %d '%s' is not valid\n", i, ProfileList[i].name);
continue;
}
profiles->Put(name, profile);
} else {
DOM_CAMERA_LOGI("Profile %d '%s' not supported by platform\n", i, ProfileList[i].name);
}
}
}
return profiles;
}
/* static */ nsresult
GonkRecorderProfile::GetAll(uint32_t aCameraId,
nsTArray<nsRefPtr<ICameraControl::RecorderProfile>>& aProfiles)
{
ProfileHashtable* profiles = GetProfileHashtable(aCameraId);
if (!profiles) {
return NS_ERROR_FAILURE;
}
aProfiles.Clear();
profiles->EnumerateRead(Enumerate, static_cast<void*>(&aProfiles));
return NS_OK;
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
nsresult
GonkRecorderProfile::ConfigureRecorder(GonkRecorder& aRecorder)
GonkRecorderProfile::ConfigureRecorder(GonkRecorder* aRecorder)
{
if (!aRecorder) {
DOM_CAMERA_LOGW("ConfigureRecorder() called with null aRecorder\n");
return NS_ERROR_INVALID_ARG;
}
static const size_t SIZE = 256;
char buffer[SIZE];
// set all the params
CHECK_SETARG(aRecorder.setAudioSource(AUDIO_SOURCE_CAMCORDER));
CHECK_SETARG(aRecorder.setVideoSource(VIDEO_SOURCE_CAMERA));
CHECK_SETARG(aRecorder.setOutputFormat(mOutputFormat));
CHECK_SETARG(aRecorder.setVideoFrameRate(mVideo.GetFramesPerSecond()));
CHECK_SETARG(aRecorder.setVideoSize(mVideo.GetSize().width, mVideo.GetSize().height));
CHECK_SETARG(aRecorder.setVideoEncoder(mVideo.GetPlatformEncoder()));
CHECK_SETARG(aRecorder.setAudioEncoder(mAudio.GetPlatformEncoder()));
CHECK_SETARG(aRecorder->setAudioSource(AUDIO_SOURCE_CAMCORDER));
CHECK_SETARG(aRecorder->setVideoSource(VIDEO_SOURCE_CAMERA));
CHECK_SETARG(aRecorder->setOutputFormat(GetOutputFormat()));
CHECK_SETARG(aRecorder->setVideoFrameRate(mVideo.GetFramerate()));
CHECK_SETARG(aRecorder->setVideoSize(mVideo.GetWidth(), mVideo.GetHeight()));
CHECK_SETARG(aRecorder->setVideoEncoder(mVideo.GetPlatformCodec()));
CHECK_SETARG(aRecorder->setAudioEncoder(mAudio.GetPlatformCodec()));
snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideo.GetBitsPerSecond());
CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
snprintf(buffer, SIZE, "video-param-encoding-bitrate=%d", mVideo.GetBitrate());
CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
snprintf(buffer, SIZE, "audio-param-encoding-bitrate=%d", mAudio.GetBitsPerSecond());
CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
snprintf(buffer, SIZE, "audio-param-encoding-bitrate=%d", mAudio.GetBitrate());
CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
snprintf(buffer, SIZE, "audio-param-number-of-channels=%d", mAudio.GetChannels());
CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudio.GetSamplesPerSecond());
CHECK_SETARG(aRecorder.setParameters(String8(buffer)));
snprintf(buffer, SIZE, "audio-param-sampling-rate=%d", mAudio.GetSamplerate());
CHECK_SETARG(aRecorder->setParameters(String8(buffer)));
return NS_OK;
}
/* static */ nsresult
GonkRecorderProfile::ConfigureRecorder(android::GonkRecorder& aRecorder,
uint32_t aCameraId,
const nsAString& aProfileName)
/**
* Recorder audio profile.
*/
static RecorderAudioProfile::Codec
TranslateAudioCodec(audio_encoder aCodec)
{
ProfileHashtable* profiles = GetProfileHashtable(aCameraId);
if (!profiles) {
return NS_ERROR_FAILURE;
switch (aCodec) {
case AUDIO_ENCODER_AMR_NB: return RecorderAudioProfile::AMRNB;
case AUDIO_ENCODER_AMR_WB: return RecorderAudioProfile::AMRWB;
case AUDIO_ENCODER_AAC: return RecorderAudioProfile::AAC;
default: return RecorderAudioProfile::UNKNOWN;
}
GonkRecorderProfile* profile;
if (!profiles->Get(aProfileName, &profile)) {
return NS_ERROR_INVALID_ARG;
}
return profile->ConfigureRecorder(aRecorder);
}
GonkRecorderAudioProfile::GonkRecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex)
: RecorderAudioProfile(aCameraId, aQualityIndex)
{
DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraId=%d, mQualityIndex=%d\n", __func__, __LINE__, this, mCameraId, mQualityIndex);
mPlatformCodec = static_cast<audio_encoder>(GetProfileParam(mCameraId, mQualityIndex, "aud.codec"));
mCodec = TranslateAudioCodec(mPlatformCodec);
mBitrate = GetProfileParam(mCameraId, mQualityIndex, "aud.bps");
mSamplerate = GetProfileParam(mCameraId, mQualityIndex, "aud.hz");
mChannels = GetProfileParam(mCameraId, mQualityIndex, "aud.ch");
}
GonkRecorderAudioProfile::~GonkRecorderAudioProfile()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
/**
* Recorder video profile.
*/
static RecorderVideoProfile::Codec
TranslateVideoCodec(video_encoder aCodec)
{
switch (aCodec) {
case VIDEO_ENCODER_H263: return RecorderVideoProfile::H263;
case VIDEO_ENCODER_H264: return RecorderVideoProfile::H264;
case VIDEO_ENCODER_MPEG_4_SP: return RecorderVideoProfile::MPEG4SP;
default: return RecorderVideoProfile::UNKNOWN;
}
}
GonkRecorderVideoProfile::GonkRecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex)
: RecorderVideoProfile(aCameraId, aQualityIndex)
{
DOM_CAMERA_LOGT("%s:%d : this=%p, mCameraId=%d, mQualityIndex=%d\n", __func__, __LINE__, this, mCameraId, mQualityIndex);
mPlatformCodec = static_cast<video_encoder>(GetProfileParam(mCameraId, mQualityIndex, "vid.codec"));
mCodec = TranslateVideoCodec(mPlatformCodec);
mBitrate = GetProfileParam(mCameraId, mQualityIndex, "vid.bps");
mFramerate = GetProfileParam(mCameraId, mQualityIndex, "vid.fps");
mWidth = GetProfileParam(mCameraId, mQualityIndex, "vid.width");
mHeight = GetProfileParam(mCameraId, mQualityIndex, "vid.height");
}
GonkRecorderVideoProfile::~GonkRecorderVideoProfile()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
GonkRecorderProfileManager::GonkRecorderProfileManager(uint32_t aCameraId)
: RecorderProfileManager(aCameraId)
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
mMaxQualityIndex = sizeof(ProfileList) / sizeof(ProfileList[0]) - 1;
}
GonkRecorderProfileManager::~GonkRecorderProfileManager()
{
DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
}
bool
GonkRecorderProfileManager::IsSupported(uint32_t aQualityIndex) const
{
if (!IsQualitySupported(mCameraId, aQualityIndex)) {
// This profile is not supported
return false;
}
int width = GetProfileParam(mCameraId, aQualityIndex, "vid.width");
int height = GetProfileParam(mCameraId, aQualityIndex, "vid.height");
if (width == -1 || height == -1) {
// This would be unexpected, but we handle it just in case
DOM_CAMERA_LOGE("Camera %d recorder profile %d has width=%d, height=%d\n", mCameraId, aQualityIndex, width, height);
return false;
}
for (uint32_t i = 0; i < mSupportedSizes.Length(); ++i) {
if (static_cast<uint32_t>(width) == mSupportedSizes[i].width &&
static_cast<uint32_t>(height) == mSupportedSizes[i].height)
{
return true;
}
}
return false;
}
already_AddRefed<RecorderProfile>
GonkRecorderProfileManager::Get(uint32_t aQualityIndex) const
{
// This overrides virtual RecorderProfileManager::Get(...)
DOM_CAMERA_LOGT("%s:%d : aQualityIndex=%d\n", __func__, __LINE__, aQualityIndex);
nsRefPtr<RecorderProfile> profile = new GonkRecorderProfile(mCameraId, aQualityIndex);
return profile.forget();
}
already_AddRefed<GonkRecorderProfile>
GonkRecorderProfileManager::Get(const char* aProfileName) const
{
DOM_CAMERA_LOGT("%s:%d : aProfileName='%s'\n", __func__, __LINE__, aProfileName);
for (uint32_t i = 0; i < mMaxQualityIndex; ++i) {
if (strcmp(ProfileList[i].name, aProfileName) == 0) {
nsRefPtr<GonkRecorderProfile> profile = nullptr;
if (IsSupported(i)) {
profile = new GonkRecorderProfile(mCameraId, i);
return profile.forget();
}
return nullptr;
}
}
DOM_CAMERA_LOGW("Couldn't file recorder profile named '%s'\n", aProfileName);
return nullptr;
}

View File

@ -6,6 +6,7 @@
#define DOM_CAMERA_GONK_RECORDER_PROFILES_H
#include <media/MediaProfiles.h>
#include "CameraRecorderProfiles.h"
#include "ICameraControl.h"
#ifndef CHECK_SETARG_RETURN
@ -23,125 +24,97 @@
#endif
namespace android {
class GonkRecorder;
class GonkRecorder;
};
namespace mozilla {
/**
* class GonkRecorderProfileBase
* Gonk-specific video profile.
*/
template<class A, class V>
class GonkRecorderProfileBase : public ICameraControl::RecorderProfile
class GonkRecorderVideoProfile : public RecorderVideoProfile
{
public:
GonkRecorderProfileBase(uint32_t aCameraId, uint32_t aProfileIndex, const nsAString& aName)
: RecorderProfile(aName)
, mAudio(aCameraId, aProfileIndex)
, mVideo(aCameraId, aProfileIndex)
{ }
virtual const Audio& GetAudio() const MOZ_OVERRIDE { return mAudio; }
virtual const Video& GetVideo() const MOZ_OVERRIDE { return mVideo; }
GonkRecorderVideoProfile(uint32_t aCameraId, uint32_t aQualityIndex);
~GonkRecorderVideoProfile();
android::video_encoder GetPlatformCodec() const { return mPlatformCodec; }
protected:
virtual ~GonkRecorderProfileBase() { }
A mAudio;
V mVideo;
android::video_encoder mPlatformCodec;
};
/**
* class GonkRecorderVideo
* Gonk-specific audio profile.
*/
class GonkRecorderVideo : public ICameraControl::RecorderProfile::Video
class GonkRecorderAudioProfile : public RecorderAudioProfile
{
public:
GonkRecorderVideo(uint32_t aCameraId, uint32_t aProfileIndex);
virtual ~GonkRecorderVideo() { }
android::video_encoder GetPlatformEncoder() const { return mPlatformEncoder; }
bool IsValid() const { return mIsValid; }
GonkRecorderAudioProfile(uint32_t aCameraId, uint32_t aQualityIndex);
~GonkRecorderAudioProfile();
android::audio_encoder GetPlatformCodec() const { return mPlatformCodec; }
protected:
int GetProfileParameter(const char* aParameter);
static bool Translate(android::video_encoder aCodec, nsAString& aCodecName);
uint32_t mCameraId;
uint32_t mProfileIndex;
bool mIsValid;
android::video_encoder mPlatformEncoder;
android::audio_encoder mPlatformCodec;
};
/**
* class GonkRecorderAudio
* Gonk-specific recorder profile.
*/
class GonkRecorderAudio : public ICameraControl::RecorderProfile::Audio
class GonkRecorderProfile : public RecorderProfileBase<GonkRecorderAudioProfile, GonkRecorderVideoProfile>
{
public:
GonkRecorderAudio(uint32_t aCameraId, uint32_t aProfileIndex);
virtual ~GonkRecorderAudio() { }
GonkRecorderProfile(uint32_t aCameraId, uint32_t aQualityIndex);
android::audio_encoder GetPlatformEncoder() const { return mPlatformEncoder; }
bool IsValid() const { return mIsValid; }
GonkRecorderAudioProfile* GetGonkAudioProfile() { return &mAudio; }
GonkRecorderVideoProfile* GetGonkVideoProfile() { return &mVideo; }
protected:
int GetProfileParameter(const char* aParameter);
static bool Translate(android::audio_encoder aCodec, nsAString& aCodecName);
android::output_format GetOutputFormat() const { return mPlatformOutputFormat; }
uint32_t mCameraId;
uint32_t mProfileIndex;
bool mIsValid;
android::audio_encoder mPlatformEncoder;
};
/**
* class GonkRecorderProfile
*/
typedef nsRefPtrHashtable<nsStringHashKey, GonkRecorderProfile> ProfileHashtable;
class GonkRecorderProfile
: public GonkRecorderProfileBase<GonkRecorderAudio, GonkRecorderVideo>
{
public:
static nsresult GetAll(uint32_t aCameraId,
nsTArray<nsRefPtr<ICameraControl::RecorderProfile>>& aProfiles);
// Configures the specified recorder using the specified profile.
// Configures the specified recorder using this profile.
//
// Return values:
// - NS_OK on success;
// - NS_ERROR_INVALID_ARG if the profile isn't supported;
// - NS_ERROR_NOT_AVAILABLE if the recorder rejected the profile.
static nsresult ConfigureRecorder(android::GonkRecorder& aRecorder,
uint32_t aCameraId,
const nsAString& aProfileName);
// - NS_ERROR_INVALID_ARG if 'aRecorder' is null;
// - NS_ERROR_NOT_AVAILABLE if the recorder rejected this profile.
nsresult ConfigureRecorder(android::GonkRecorder* aRecorder);
protected:
GonkRecorderProfile(uint32_t aCameraId,
uint32_t aProfileIndex,
const nsAString& aName);
virtual ~GonkRecorderProfile();
int GetProfileParameter(const char* aParameter);
android::output_format mPlatformOutputFormat;
};
bool Translate(android::output_format aContainer, nsAString& aContainerName);
bool GetMimeType(android::output_format aContainer, nsAString& aMimeType);
bool IsValid() const { return mIsValid; };
/**
* Gonk-specific profile manager.
*/
class GonkRecorderProfileManager : public RecorderProfileManager
{
public:
GonkRecorderProfileManager(uint32_t aCameraId);
nsresult ConfigureRecorder(android::GonkRecorder& aRecorder);
static ProfileHashtable* GetProfileHashtable(uint32_t aCameraId);
static PLDHashOperator Enumerate(const nsAString& aProfileName,
GonkRecorderProfile* aProfile,
void* aUserArg);
/**
* Call this function to indicate that the specified resolutions are in fact
* 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<ICameraControl::Size>& aSizes)
{ mSupportedSizes = aSizes; }
uint32_t mCameraId;
uint32_t mProfileIndex;
bool mIsValid;
android::output_format mOutputFormat;
/**
* Call this function to remove all resolutions set by calling
* SetSupportedResolutions().
*/
void ClearSupportedResolutions() { mSupportedSizes.Clear(); }
static nsClassHashtable<nsUint32HashKey, ProfileHashtable> sProfiles;
bool IsSupported(uint32_t aQualityIndex) const;
private:
DISALLOW_EVIL_CONSTRUCTORS(GonkRecorderProfile);
already_AddRefed<RecorderProfile> Get(uint32_t aQualityIndex) const;
already_AddRefed<GonkRecorderProfile> Get(const char* aProfileName) const;
protected:
virtual ~GonkRecorderProfileManager();
nsTArray<ICameraControl::Size> mSupportedSizes;
};
}; // namespace mozilla

View File

@ -9,7 +9,6 @@
#include "nsString.h"
#include "nsAutoPtr.h"
#include "nsISupportsImpl.h"
#include "base/basictypes.h"
struct DeviceStorageFileDescriptor;
@ -18,6 +17,7 @@ class nsIFile;
namespace mozilla {
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;
@ -162,76 +162,6 @@ public:
Point mouth;
};
class RecorderProfile
{
public:
class Video
{
public:
Video() { }
virtual ~Video() { }
const nsString& GetCodec() const { return mCodec; }
const Size& GetSize() const { return mSize; }
uint32_t GetBitsPerSecond() const { return mBitsPerSecond; }
uint32_t GetFramesPerSecond() const { return mFramesPerSecond; }
protected:
nsString mCodec;
Size mSize;
uint32_t mBitsPerSecond;
uint32_t mFramesPerSecond;
private:
DISALLOW_EVIL_CONSTRUCTORS(Video);
};
class Audio
{
public:
Audio() { }
virtual ~Audio() { }
const nsString& GetCodec() const { return mCodec; }
uint32_t GetChannels() const { return mChannels; }
uint32_t GetBitsPerSecond() const { return mBitsPerSecond; }
uint32_t GetSamplesPerSecond() const { return mSamplesPerSecond; }
protected:
nsString mCodec;
uint32_t mChannels;
uint32_t mBitsPerSecond;
uint32_t mSamplesPerSecond;
private:
DISALLOW_EVIL_CONSTRUCTORS(Audio);
};
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecorderProfile)
RecorderProfile(const nsAString& aName)
: mName(aName)
{ }
const nsString& GetName() const { return mName; }
const nsString& GetContainer() const { return mContainer; }
const nsString& GetMimeType() const { return mMimeType; }
virtual const Video& GetVideo() const = 0;
virtual const Audio& GetAudio() const = 0;
protected:
virtual ~RecorderProfile() { }
nsString mName;
nsString mContainer;
nsString mMimeType;
private:
DISALLOW_EVIL_CONSTRUCTORS(RecorderProfile);
};
static already_AddRefed<ICameraControl> Create(uint32_t aCameraId);
virtual void AddListener(CameraControlListener* aListener) = 0;
@ -290,9 +220,7 @@ public:
virtual nsresult Get(uint32_t aKey, nsTArray<nsString>& aValues) = 0;
virtual nsresult Get(uint32_t aKey, nsTArray<double>& aValues) = 0;
virtual nsresult GetRecorderProfiles(nsTArray<nsString>& aProfiles) = 0;
virtual RecorderProfile* GetProfileInfo(const nsAString& aProfile) = 0;
virtual already_AddRefed<RecorderProfileManager> GetRecorderProfileManager() = 0;
virtual uint32_t GetCameraId() = 0;
virtual void Shutdown() = 0;

View File

@ -17,6 +17,7 @@ UNIFIED_SOURCES += [
'CameraControlImpl.cpp',
'CameraPreferences.cpp',
'CameraPreviewMediaStream.cpp',
'CameraRecorderProfiles.cpp',
'DOMCameraCapabilities.cpp',
'DOMCameraControl.cpp',
'DOMCameraControlListener.cpp',

View File

@ -211,14 +211,6 @@ var interfaceNamesInGlobalScope =
{name: "CameraFacesDetectedEvent", b2g: true, pref: "camera.control.face_detection.enabled"},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraManager", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraRecorderAudioProfile", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraRecorderProfile", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraRecorderProfiles", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraRecorderVideoProfile", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
{name: "CameraStateChangeEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -5,55 +5,6 @@
* You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/* The capabilities of the video recorder. These are guaranteed not to change
over the lifetime of that partcicular instance.
*/
[Func="CameraCapabilities::HasSupport"]
interface CameraRecorderAudioProfile
{
[Constant, Cached] readonly attribute DOMString codec;
[Constant, Cached] readonly attribute unsigned long bitsPerSecond;
[Constant, Cached] readonly attribute unsigned long samplesPerSecond;
[Constant, Cached] readonly attribute unsigned long channels;
jsonifier;
};
[Func="CameraCapabilities::HasSupport"]
interface CameraRecorderVideoProfile
{
[Constant, Cached] readonly attribute DOMString codec;
[Constant, Cached] readonly attribute unsigned long bitsPerSecond;
[Constant, Cached] readonly attribute unsigned long framesPerSecond;
[Constant, Cached] readonly attribute CameraSize size;
[Constant, Cached] readonly attribute unsigned long width;
[Constant, Cached] readonly attribute unsigned long height;
jsonifier;
};
[Func="CameraCapabilities::HasSupport"]
interface CameraRecorderProfile
{
[Constant, Cached] readonly attribute DOMString name;
[Constant, Cached] readonly attribute DOMString containerFormat;
[Constant, Cached] readonly attribute DOMString mimeType;
[Constant, Cached] readonly attribute CameraRecorderAudioProfile audio;
[Constant, Cached] readonly attribute CameraRecorderVideoProfile video;
jsonifier;
};
[Func="CameraCapabilities::HasSupport"]
interface CameraRecorderProfiles
{
getter CameraRecorderProfile(DOMString profile);
jsonifier;
};
/* The capabilities of a CameraControl instance. These are guaranteed
not to change over the lifetime of that particular instance.
*/
@ -83,9 +34,7 @@ interface CameraCapabilities
[Constant, Cached] readonly attribute double maxExposureCompensation;
[Constant, Cached] readonly attribute double exposureCompensationStep;
[Constant, Cached] readonly attribute CameraRecorderProfiles recorderProfiles;
[Constant, Cached] readonly attribute any recorderProfiles;
[Constant, Cached] readonly attribute sequence<DOMString> isoModes;
jsonifier;
};

View File

@ -500,7 +500,7 @@ AsmJSHandleExecutionInterrupt()
{
AsmJSActivation *act = PerThreadData::innermostAsmJSActivation();
act->module().setInterrupted(true);
bool ret = CheckForInterrupt(act->cx());
bool ret = HandleExecutionInterrupt(act->cx());
act->module().setInterrupted(false);
return ret;
}
@ -672,8 +672,8 @@ AddressOf(AsmJSImmKind kind, ExclusiveContext *cx)
switch (kind) {
case AsmJSImm_Runtime:
return cx->runtimeAddressForJit();
case AsmJSImm_RuntimeInterruptUint32:
return cx->runtimeAddressOfInterruptUint32();
case AsmJSImm_RuntimeInterrupt:
return cx->runtimeAddressOfInterrupt();
case AsmJSImm_StackLimit:
return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
case AsmJSImm_ReportOverRecursed:

View File

@ -152,7 +152,7 @@ NativeRegExpMacroAssembler::GenerateCode(JSContext *cx, bool match_only)
// Check if we have space on the stack.
Label stack_ok;
void *stack_limit = runtime->mainThread.addressofJitStackLimit();
void *stack_limit = &runtime->mainThread.jitStackLimit;
masm.branchPtr(Assembler::Below, AbsoluteAddress(stack_limit), StackPointer, &stack_ok);
// Exit with an exception. There is not enough space on the stack
@ -502,7 +502,7 @@ NativeRegExpMacroAssembler::Backtrack()
// Check for an interrupt.
Label noInterrupt;
masm.branch32(Assembler::Equal,
AbsoluteAddress(runtime->addressOfInterruptUint32()), Imm32(0),
AbsoluteAddress(&runtime->interrupt), Imm32(0),
&noInterrupt);
masm.movePtr(ImmWord(RegExpRunStatus_Error), temp0);
masm.jump(&exit_label_);

View File

@ -496,7 +496,7 @@ bool
BaselineCompiler::emitStackCheck(bool earlyCheck)
{
Label skipCall;
void *limitAddr = cx->runtime()->mainThread.addressofJitStackLimit();
uintptr_t *limitAddr = &cx->runtime()->mainThread.jitStackLimit;
uint32_t slotsSize = script->nslots() * sizeof(Value);
uint32_t tolerance = earlyCheck ? slotsSize : 0;
@ -646,7 +646,7 @@ BaselineCompiler::emitInterruptCheck()
frame.syncStack(0);
Label done;
void *interrupt = cx->runtimeAddressOfInterruptUint32();
void *interrupt = (void *)&cx->runtime()->interrupt;
masm.branch32(Assembler::Equal, AbsoluteAddress(interrupt), Imm32(0), &done);
prepareVMCall();

View File

@ -3747,7 +3747,7 @@ CodeGenerator::visitCheckOverRecursedPar(LCheckOverRecursedPar *lir)
Register tempReg = ToRegister(lir->getTempReg());
masm.loadPtr(Address(cxReg, offsetof(ForkJoinContext, perThreadData)), tempReg);
masm.loadPtr(Address(tempReg, PerThreadData::offsetOfJitStackLimit()), tempReg);
masm.loadPtr(Address(tempReg, offsetof(PerThreadData, jitStackLimit)), tempReg);
// Conditional forward (unlikely) branch to failure.
CheckOverRecursedFailure *ool = new(alloc()) CheckOverRecursedFailure(lir);
@ -9789,7 +9789,7 @@ CodeGenerator::visitInterruptCheck(LInterruptCheck *lir)
if (!ool)
return false;
AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterruptUint32());
AbsoluteAddress interruptAddr(GetIonContext()->runtime->addressOfInterrupt());
masm.branch32(Assembler::NotEqual, interruptAddr, Imm32(0), ool->entry());
masm.bind(ool->rejoin());
return true;
@ -9799,8 +9799,8 @@ bool
CodeGenerator::visitAsmJSInterruptCheck(LAsmJSInterruptCheck *lir)
{
Register scratch = ToRegister(lir->scratch());
masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterruptUint32), scratch);
masm.load32(Address(scratch, 0), scratch);
masm.movePtr(AsmJSImmPtr(AsmJSImm_RuntimeInterrupt), scratch);
masm.load8ZeroExtend(Address(scratch, 0), scratch);
Label rejoin;
masm.branch32(Assembler::Equal, scratch, Imm32(0), &rejoin);
{

View File

@ -43,7 +43,7 @@ CompileRuntime::addressOfJitTop()
const void *
CompileRuntime::addressOfJitStackLimit()
{
return runtime()->mainThread.addressofJitStackLimit();
return &runtime()->mainThread.jitStackLimit;
}
const void *
@ -73,15 +73,15 @@ CompileRuntime::addressOfGCZeal()
#endif
const void *
CompileRuntime::addressOfInterruptUint32()
CompileRuntime::addressOfInterrupt()
{
return runtime()->addressOfInterruptUint32();
return &runtime()->interrupt;
}
const void *
CompileRuntime::addressOfInterruptParUint32()
CompileRuntime::addressOfInterruptPar()
{
return runtime()->addressOfInterruptParUint32();
return &runtime()->interruptPar;
}
const void *

View File

@ -50,8 +50,8 @@ class CompileRuntime
const void *addressOfGCZeal();
#endif
const void *addressOfInterruptUint32();
const void *addressOfInterruptParUint32();
const void *addressOfInterrupt();
const void *addressOfInterruptPar();
const void *addressOfThreadPool();

View File

@ -423,7 +423,7 @@ JitRuntime::ensureIonCodeAccessible(JSRuntime *rt)
ionCodeProtected_ = false;
}
if (rt->hasPendingInterrupt()) {
if (rt->interrupt) {
// The interrupt handler needs to be invoked by this thread, but we may
// be inside a signal handler and have no idea what is above us on the
// stack (probably we are executing Ion code at an arbitrary point, but
@ -1157,7 +1157,7 @@ IonScript::copyPatchableBackedges(JSContext *cx, JitCode *code,
// whether an interrupt is currently desired, matching the targets
// established by ensureIonCodeAccessible() above. We don't handle the
// interrupt immediately as the interrupt lock is held here.
if (cx->runtime()->hasPendingInterrupt())
if (cx->runtime()->interrupt)
PatchBackedge(backedge, interruptCheck, JitRuntime::BackedgeInterruptCheck);
else
PatchBackedge(backedge, loopHeader, JitRuntime::BackedgeLoopHeader);

View File

@ -1168,7 +1168,7 @@ MacroAssembler::loadStringChar(Register str, Register index, Register output)
void
MacroAssembler::checkInterruptFlagPar(Register tempReg, Label *fail)
{
movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptParUint32()), tempReg);
movePtr(ImmPtr(GetIonContext()->runtime->addressOfInterruptPar()), tempReg);
branch32(Assembler::NonZero, Address(tempReg, 0), Imm32(0), fail);
}

View File

@ -147,7 +147,7 @@ jit::CheckOverRecursedPar(ForkJoinContext *cx)
}
#endif
if (!JS_CHECK_STACK_SIZE(cx->perThreadData->jitStackLimit(), &stackDummy_)) {
if (!JS_CHECK_STACK_SIZE(cx->perThreadData->jitStackLimit, &stackDummy_)) {
cx->bailoutRecord->joinCause(ParallelBailoutOverRecursed);
return false;
}

View File

@ -113,17 +113,28 @@ NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap)
bool
CheckOverRecursed(JSContext *cx)
{
// We just failed the jitStackLimit check. There are two possible reasons:
// - jitStackLimit was the real stack limit and we're over-recursed
// - jitStackLimit was set to UINTPTR_MAX by JSRuntime::requestInterrupt
// and we need to call JSRuntime::handleInterrupt.
// IonMonkey's stackLimit is equal to nativeStackLimit by default. When we
// request an interrupt, we set the jitStackLimit to nullptr, which causes
// the stack limit check to fail.
//
// There are two states we're concerned about here:
// (1) The interrupt bit is set, and we need to fire the interrupt callback.
// (2) The stack limit has been exceeded, and we need to throw an error.
//
// Note that we can reach here if jitStackLimit is MAXADDR, but interrupt
// has not yet been set to 1. That's okay; it will be set to 1 very shortly,
// and in the interim we might just fire a few useless calls to
// CheckOverRecursed.
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, 0, return false);
#else
JS_CHECK_RECURSION(cx, return false);
#endif
gc::MaybeVerifyBarriers(cx);
return cx->runtime()->handleInterrupt(cx);
if (cx->runtime()->interrupt)
return InterruptCheck(cx);
return true;
}
// This function can get called in two contexts. In the usual context, it's
@ -167,8 +178,10 @@ CheckOverRecursedWithExtra(JSContext *cx, BaselineFrame *frame,
JS_CHECK_RECURSION_WITH_SP(cx, checkSp, return false);
#endif
gc::MaybeVerifyBarriers(cx);
return cx->runtime()->handleInterrupt(cx);
if (cx->runtime()->interrupt)
return InterruptCheck(cx);
return true;
}
bool

View File

@ -783,7 +783,7 @@ enum AsmJSImmKind
AsmJSImm_PowD = AsmJSExit::Builtin_PowD,
AsmJSImm_ATan2D = AsmJSExit::Builtin_ATan2D,
AsmJSImm_Runtime,
AsmJSImm_RuntimeInterruptUint32,
AsmJSImm_RuntimeInterrupt,
AsmJSImm_StackLimit,
AsmJSImm_ReportOverRecursed,
AsmJSImm_OnDetached,

View File

@ -2029,48 +2029,68 @@ JS_GetExternalStringFinalizer(JSString *str)
}
static void
SetNativeStackQuotaAndLimit(JSRuntime *rt, StackKind kind, size_t stackSize)
SetNativeStackQuota(JSRuntime *rt, StackKind kind, size_t stackSize)
{
rt->nativeStackQuota[kind] = stackSize;
if (rt->nativeStackBase)
RecomputeStackLimit(rt, kind);
}
void
js::RecomputeStackLimit(JSRuntime *rt, StackKind kind)
{
size_t stackSize = rt->nativeStackQuota[kind];
#if JS_STACK_GROWTH_DIRECTION > 0
if (stackSize == 0) {
rt->mainThread.nativeStackLimit[kind] = UINTPTR_MAX;
} else {
MOZ_ASSERT(rt->nativeStackBase <= size_t(-1) - stackSize);
rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase + stackSize - 1;
rt->mainThread.nativeStackLimit[kind] =
rt->nativeStackBase + stackSize - 1;
}
#else
if (stackSize == 0) {
rt->mainThread.nativeStackLimit[kind] = 0;
} else {
MOZ_ASSERT(rt->nativeStackBase >= stackSize);
rt->mainThread.nativeStackLimit[kind] = rt->nativeStackBase - (stackSize - 1);
rt->mainThread.nativeStackLimit[kind] =
rt->nativeStackBase - (stackSize - 1);
}
#endif
// If there's no pending interrupt request set on the runtime's main thread's
// jitStackLimit, then update it so that it reflects the new nativeStacklimit.
//
// Note that, for now, we use the untrusted limit for ion. This is fine,
// because it's the most conservative limit, and if we hit it, we'll bail
// out of ion into the interpeter, which will do a proper recursion check.
if (kind == StackForUntrustedScript) {
JSRuntime::AutoLockForInterrupt lock(rt);
if (rt->mainThread.jitStackLimit != uintptr_t(-1)) {
rt->mainThread.jitStackLimit = rt->mainThread.nativeStackLimit[kind];
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
rt->mainThread.jitStackLimit = jit::Simulator::StackLimit();
#endif
}
}
}
JS_PUBLIC_API(void)
JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize, size_t trustedScriptStackSize,
JS_SetNativeStackQuota(JSRuntime *rt, size_t systemCodeStackSize,
size_t trustedScriptStackSize,
size_t untrustedScriptStackSize)
{
MOZ_ASSERT(rt->requestDepth == 0);
MOZ_ASSERT_IF(trustedScriptStackSize,
trustedScriptStackSize < systemCodeStackSize);
if (!trustedScriptStackSize)
trustedScriptStackSize = systemCodeStackSize;
else
MOZ_ASSERT(trustedScriptStackSize < systemCodeStackSize);
MOZ_ASSERT_IF(untrustedScriptStackSize,
untrustedScriptStackSize < trustedScriptStackSize);
if (!untrustedScriptStackSize)
untrustedScriptStackSize = trustedScriptStackSize;
else
MOZ_ASSERT(untrustedScriptStackSize < trustedScriptStackSize);
SetNativeStackQuotaAndLimit(rt, StackForSystemCode, systemCodeStackSize);
SetNativeStackQuotaAndLimit(rt, StackForTrustedScript, trustedScriptStackSize);
SetNativeStackQuotaAndLimit(rt, StackForUntrustedScript, untrustedScriptStackSize);
rt->mainThread.initJitStackLimit();
SetNativeStackQuota(rt, StackForSystemCode, systemCodeStackSize);
SetNativeStackQuota(rt, StackForTrustedScript, trustedScriptStackSize);
SetNativeStackQuota(rt, StackForUntrustedScript, untrustedScriptStackSize);
}
/************************************************************************/

View File

@ -2272,9 +2272,6 @@ JS_GetExternalStringFinalizer(JSString *str);
* The stack quotas for each kind of code should be monotonically descending,
* and may be specified with this function. If 0 is passed for a given kind
* of code, it defaults to the value of the next-highest-priority kind.
*
* This function may only be called immediately after the runtime is initialized
* and before any code is executed and/or interrupts requested.
*/
extern JS_PUBLIC_API(void)
JS_SetNativeStackQuota(JSRuntime *cx, size_t systemCodeStackSize,

View File

@ -41,6 +41,7 @@
#include "gc/Marking.h"
#include "jit/Ion.h"
#include "js/CharacterEncoding.h"
#include "vm/Debugger.h"
#include "vm/HelperThreads.h"
#include "vm/Shape.h"
@ -970,6 +971,90 @@ js_GetErrorMessage(void *userRef, const unsigned errorNumber)
return nullptr;
}
bool
js::InvokeInterruptCallback(JSContext *cx)
{
MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
JSRuntime *rt = cx->runtime();
MOZ_ASSERT(rt->interrupt);
// Reset the callback counter first, then run GC and yield. If another
// thread is racing us here we will accumulate another callback request
// which will be serviced at the next opportunity.
rt->interrupt = false;
// IonMonkey sets its stack limit to UINTPTR_MAX to trigger interrupt
// callbacks.
rt->resetJitStackLimit();
cx->gcIfNeeded();
rt->interruptPar = false;
// A worker thread may have requested an interrupt after finishing an Ion
// compilation.
jit::AttachFinishedCompilations(cx);
// Important: Additional callbacks can occur inside the callback handler
// if it re-enters the JS engine. The embedding must ensure that the
// callback is disconnected before attempting such re-entry.
JSInterruptCallback cb = cx->runtime()->interruptCallback;
if (!cb)
return true;
if (cb(cx)) {
// Debugger treats invoking the interrupt callback as a "step", so
// invoke the onStep handler.
if (cx->compartment()->debugMode()) {
ScriptFrameIter iter(cx);
if (iter.script()->stepModeEnabled()) {
RootedValue rval(cx);
switch (Debugger::onSingleStep(cx, &rval)) {
case JSTRAP_ERROR:
return false;
case JSTRAP_CONTINUE:
return true;
case JSTRAP_RETURN:
// See note in Debugger::propagateForcedReturn.
Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
return false;
case JSTRAP_THROW:
cx->setPendingException(rval);
return false;
default:;
}
}
}
return true;
}
// No need to set aside any pending exception here: ComputeStackString
// already does that.
JSString *stack = ComputeStackString(cx);
JSFlatString *flat = stack ? stack->ensureFlat(cx) : nullptr;
const char16_t *chars;
AutoStableStringChars stableChars(cx);
if (flat && stableChars.initTwoByte(cx, flat))
chars = stableChars.twoByteRange().start().get();
else
chars = MOZ_UTF16("(stack not available)");
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr,
JSMSG_TERMINATED, chars);
return false;
}
bool
js::HandleExecutionInterrupt(JSContext *cx)
{
if (cx->runtime()->interrupt)
return InvokeInterruptCallback(cx);
return true;
}
ThreadSafeContext::ThreadSafeContext(JSRuntime *rt, PerThreadData *pt, ContextKind kind)
: ContextFriendFields(rt),
contextKind_(kind),

View File

@ -289,7 +289,7 @@ struct ThreadSafeContext : ContextFriendFields,
PropertyName *emptyString() { return runtime_->emptyString; }
FreeOp *defaultFreeOp() { return runtime_->defaultFreeOp(); }
void *runtimeAddressForJit() { return runtime_; }
void *runtimeAddressOfInterruptUint32() { return runtime_->addressOfInterruptUint32(); }
void *runtimeAddressOfInterrupt() { return &runtime_->interrupt; }
void *stackLimitAddress(StackKind kind) { return &runtime_->mainThread.nativeStackLimit[kind]; }
void *stackLimitAddressForJitCode(StackKind kind);
size_t gcSystemPageSize() { return gc::SystemPageSize(); }
@ -782,15 +782,33 @@ extern const JSErrorFormatString js_ErrorFormatString[JSErr_Limit];
namespace js {
/*
* Invoke the interrupt callback and return false if the current execution
* is to be terminated.
*/
bool
InvokeInterruptCallback(JSContext *cx);
bool
HandleExecutionInterrupt(JSContext *cx);
/*
* Process any pending interrupt requests. Long-running inner loops in C++ must
* call this periodically to make sure they are interruptible --- that is, to
* make sure they do not prevent the slow script dialog from appearing.
*
* This can run a full GC or call the interrupt callback, which could do
* anything. In the browser, it displays the slow script dialog.
*
* If this returns true, the caller can continue; if false, the caller must
* break out of its loop. This happens if, for example, the user clicks "Stop
* script" on the slow script dialog; treat it as an uncatchable error.
*/
MOZ_ALWAYS_INLINE bool
CheckForInterrupt(JSContext *cx)
{
// Add an inline fast-path since we have to check for interrupts in some hot
// C++ loops of library builtins.
JSRuntime *rt = cx->runtime();
if (rt->hasPendingInterrupt())
return rt->handleInterrupt(cx);
return true;
MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
return !cx->runtime()->interrupt || InvokeInterruptCallback(cx);
}
/************************************************************************/

View File

@ -526,7 +526,7 @@ CheckAllocatorState(ThreadSafeContext *cx, AllocKind kind)
rt->gc.runDebugGC();
#endif
if (rt->hasPendingInterrupt()) {
if (rt->interrupt) {
// Invoking the interrupt callback can fail and we can't usefully
// handle that here. Just check in case we need to collect instead.
ncx->gcIfNeeded();

View File

@ -18,7 +18,6 @@ inline uintptr_t
GetNativeStackBase()
{
uintptr_t stackBase = reinterpret_cast<uintptr_t>(GetNativeStackBaseImpl());
MOZ_ASSERT(stackBase != 0);
MOZ_ASSERT(stackBase % sizeof(void *) == 0);
return stackBase;
}

View File

@ -1440,7 +1440,7 @@ ForkJoinShared::execute()
// Sometimes a GC request occurs *just before* we enter into the
// parallel section. Rather than enter into the parallel section
// and then abort, we just check here and abort early.
if (cx_->runtime()->hasPendingInterruptPar())
if (cx_->runtime()->interruptPar)
return TP_RETRY_SEQUENTIALLY;
AutoLockMonitor lock(*this);
@ -1518,7 +1518,7 @@ ForkJoinShared::executeFromWorker(ThreadPoolWorker *worker, uintptr_t stackLimit
// Don't use setIonStackLimit() because that acquires the ionStackLimitLock, and the
// lock has not been initialized in these cases.
thisThread.initJitStackLimitPar(stackLimit);
thisThread.jitStackLimit = stackLimit;
executePortion(&thisThread, worker);
TlsPerThreadData.set(nullptr);
@ -1551,7 +1551,7 @@ ForkJoinShared::executeFromMainThread(ThreadPoolWorker *worker)
//
// Thus, use GetNativeStackLimit instead of just propagating the
// main thread's.
thisThread.initJitStackLimitPar(GetNativeStackLimit(cx_));
thisThread.jitStackLimit = GetNativeStackLimit(cx_);
executePortion(&thisThread, worker);
TlsPerThreadData.set(oldData);
@ -1647,7 +1647,7 @@ ForkJoinShared::executePortion(PerThreadData *perThread, ThreadPoolWorker *worke
void
ForkJoinShared::setAbortFlagDueToInterrupt(ForkJoinContext &cx)
{
MOZ_ASSERT(cx_->runtime()->hasPendingInterruptPar());
MOZ_ASSERT(cx_->runtime()->interruptPar);
// The GC Needed flag should not be set during parallel
// execution. Instead, one of the requestGC() or
// requestZoneGC() methods should be invoked.
@ -1826,7 +1826,7 @@ ForkJoinContext::hasAcquiredJSContext() const
bool
ForkJoinContext::check()
{
if (runtime()->hasPendingInterruptPar()) {
if (runtime()->interruptPar) {
shared_->setAbortFlagDueToInterrupt(*this);
return false;
}
@ -2273,6 +2273,13 @@ js::ParallelTestsShouldPass(JSContext *cx)
cx->runtime()->gcZeal() == 0;
}
void
js::RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode)
{
if (mode != JSRuntime::RequestInterruptAnyThreadDontStopIon)
rt->interruptPar = true;
}
bool
js::intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp)
{

View File

@ -546,6 +546,8 @@ bool InExclusiveParallelSection();
bool ParallelTestsShouldPass(JSContext *cx);
void RequestInterruptForForkJoin(JSRuntime *rt, JSRuntime::InterruptMode mode);
bool intrinsic_SetForkJoinTargetRegion(JSContext *cx, unsigned argc, Value *vp);
extern const JSJitInfo intrinsic_SetForkJoinTargetRegionInfo;

View File

@ -621,8 +621,14 @@ RegExpShared::execute(JSContext *cx, HandleLinearString input, size_t start,
// in the bytecode interpreter, which can execute while tolerating
// future interrupts. Otherwise, if we keep getting interrupted we
// will never finish executing the regexp.
if (cx->runtime()->hasPendingInterrupt()) {
if (!cx->runtime()->handleInterrupt(cx))
bool interrupted;
{
JSRuntime::AutoLockForInterrupt lock(cx->runtime());
interrupted = cx->runtime()->interrupt;
}
if (interrupted) {
if (!InvokeInterruptCallback(cx))
return RegExpRunStatus_Error;
break;
}

View File

@ -36,7 +36,6 @@
#include "jit/PcScriptCache.h"
#include "js/MemoryMetrics.h"
#include "js/SliceBudget.h"
#include "vm/Debugger.h"
#include "jscntxtinlines.h"
#include "jsgcinlines.h"
@ -74,7 +73,7 @@ PerThreadData::PerThreadData(JSRuntime *runtime)
runtime_(runtime),
jitTop(nullptr),
jitJSContext(nullptr),
jitStackLimit_(0xbad),
jitStackLimit(0),
#ifdef JS_TRACE_LOGGING
traceLogger(nullptr),
#endif
@ -136,8 +135,8 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime)
),
mainThread(this),
parentRuntime(parentRuntime),
interrupt_(false),
interruptPar_(false),
interrupt(false),
interruptPar(false),
handlingSignal(false),
interruptCallback(nullptr),
interruptLock(nullptr),
@ -157,7 +156,7 @@ JSRuntime::JSRuntime(JSRuntime *parentRuntime)
execAlloc_(nullptr),
jitRuntime_(nullptr),
selfHostingGlobal_(nullptr),
nativeStackBase(GetNativeStackBase()),
nativeStackBase(0),
cxCallback(nullptr),
destroyCompartmentCallback(nullptr),
destroyZoneCallback(nullptr),
@ -323,6 +322,8 @@ JSRuntime::init(uint32_t maxbytes, uint32_t maxNurseryBytes)
return false;
#endif
nativeStackBase = GetNativeStackBase();
jitSupportsFloatingPoint = js::jit::JitSupportsFloatingPoint();
jitSupportsSimd = js::jit::JitSupportsSimd();
@ -464,6 +465,17 @@ NewObjectCache::clearNurseryObjects(JSRuntime *rt)
#endif
}
void
JSRuntime::resetJitStackLimit()
{
AutoLockForInterrupt lock(this);
mainThread.setJitStackLimit(mainThread.nativeStackLimit[js::StackForUntrustedScript]);
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
mainThread.setJitStackLimit(js::jit::Simulator::StackLimit());
#endif
}
void
JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *rtSizes)
{
@ -518,120 +530,33 @@ JSRuntime::addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::Runtim
#endif
}
static bool
InvokeInterruptCallback(JSContext *cx)
{
MOZ_ASSERT(cx->runtime()->requestDepth >= 1);
cx->gcIfNeeded();
// A worker thread may have requested an interrupt after finishing an Ion
// compilation.
jit::AttachFinishedCompilations(cx);
// Important: Additional callbacks can occur inside the callback handler
// if it re-enters the JS engine. The embedding must ensure that the
// callback is disconnected before attempting such re-entry.
JSInterruptCallback cb = cx->runtime()->interruptCallback;
if (!cb)
return true;
if (cb(cx)) {
// Debugger treats invoking the interrupt callback as a "step", so
// invoke the onStep handler.
if (cx->compartment()->debugMode()) {
ScriptFrameIter iter(cx);
if (iter.script()->stepModeEnabled()) {
RootedValue rval(cx);
switch (Debugger::onSingleStep(cx, &rval)) {
case JSTRAP_ERROR:
return false;
case JSTRAP_CONTINUE:
return true;
case JSTRAP_RETURN:
// See note in Debugger::propagateForcedReturn.
Debugger::propagateForcedReturn(cx, iter.abstractFramePtr(), rval);
return false;
case JSTRAP_THROW:
cx->setPendingException(rval);
return false;
default:;
}
}
}
return true;
}
// No need to set aside any pending exception here: ComputeStackString
// already does that.
JSString *stack = ComputeStackString(cx);
JSFlatString *flat = stack ? stack->ensureFlat(cx) : nullptr;
const char16_t *chars;
AutoStableStringChars stableChars(cx);
if (flat && stableChars.initTwoByte(cx, flat))
chars = stableChars.twoByteRange().start().get();
else
chars = MOZ_UTF16("(stack not available)");
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_WARNING, js_GetErrorMessage, nullptr,
JSMSG_TERMINATED, chars);
return false;
}
void
PerThreadData::resetJitStackLimit()
{
// Note that, for now, we use the untrusted limit for ion. This is fine,
// because it's the most conservative limit, and if we hit it, we'll bail
// out of ion into the interpeter, which will do a proper recursion check.
#if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
jitStackLimit_ = jit::Simulator::StackLimit();
#else
jitStackLimit_ = nativeStackLimit[StackForUntrustedScript];
#endif
}
void
PerThreadData::initJitStackLimit()
{
resetJitStackLimit();
}
void
PerThreadData::initJitStackLimitPar(uintptr_t limit)
{
jitStackLimit_ = limit;
}
void
JSRuntime::requestInterrupt(InterruptMode mode)
{
interrupt_ = true;
interruptPar_ = true;
mainThread.jitStackLimit_ = UINTPTR_MAX;
AutoLockForInterrupt lock(this);
/*
* Invalidate ionTop to trigger its over-recursion check. Note this must be
* set before interrupt, to avoid racing with js::InvokeInterruptCallback,
* into a weird state where interrupt is stuck at 0 but jitStackLimit is
* MAXADDR.
*/
mainThread.setJitStackLimit(-1);
interrupt = true;
RequestInterruptForForkJoin(this, mode);
/*
* asm.js and normal Ion code optionally use memory protection and signal
* handlers to halt running code.
*/
if (canUseSignalHandlers()) {
AutoLockForInterrupt lock(this);
RequestInterruptForAsmJSCode(this, mode);
jit::RequestInterruptForIonCode(this, mode);
}
}
bool
JSRuntime::handleInterrupt(JSContext *cx)
{
MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
if (interrupt_ || mainThread.jitStackLimit_ == UINTPTR_MAX) {
interrupt_ = false;
interruptPar_ = false;
mainThread.resetJitStackLimit();
return InvokeInterruptCallback(cx);
}
return true;
}
jit::ExecutableAllocator *
JSRuntime::createExecutableAllocator(JSContext *cx)
{

View File

@ -519,20 +519,13 @@ class PerThreadData : public PerThreadDataFriendFields
*/
JSContext *jitJSContext;
/* See comment for JSRuntime::interrupt_. */
private:
mozilla::Atomic<uintptr_t, mozilla::Relaxed> jitStackLimit_;
void resetJitStackLimit();
friend struct ::JSRuntime;
public:
void initJitStackLimit();
void initJitStackLimitPar(uintptr_t limit);
/*
* The stack limit checked by JIT code. This stack limit may be temporarily
* set to null to force JIT code to exit (e.g., for the operation callback).
*/
uintptr_t jitStackLimit;
uintptr_t jitStackLimit() const { return jitStackLimit_; }
// For read-only JIT use:
void *addressofJitStackLimit() { return &jitStackLimit_; }
static size_t offsetOfJitStackLimit() { return offsetof(PerThreadData, jitStackLimit_); }
inline void setJitStackLimit(uintptr_t limit);
// Information about the heap allocated backtrack stack used by RegExp JIT code.
irregexp::RegExpStack regexpStack;
@ -685,6 +678,8 @@ class PerThreadData : public PerThreadDataFriendFields
class AutoLockForExclusiveAccess;
void RecomputeStackLimit(JSRuntime *rt, StackKind kind);
} // namespace js
struct JSRuntime : public JS::shadow::Runtime,
@ -708,56 +703,18 @@ struct JSRuntime : public JS::shadow::Runtime,
*/
JSRuntime *parentRuntime;
private:
mozilla::Atomic<uint32_t, mozilla::Relaxed> interrupt_;
mozilla::Atomic<uint32_t, mozilla::Relaxed> interruptPar_;
public:
/*
* If true, we've been asked to call the interrupt callback as soon as
* possible.
*/
mozilla::Atomic<bool, mozilla::Relaxed> interrupt;
enum InterruptMode {
RequestInterruptMainThread,
RequestInterruptAnyThread,
RequestInterruptAnyThreadDontStopIon,
RequestInterruptAnyThreadForkJoin
};
// Any thread can call requestInterrupt() to request that the main JS thread
// stop running and call the interrupt callback (allowing the interrupt
// callback to halt execution). To stop the main JS thread, requestInterrupt
// sets two fields: interrupt_ (set to true) and jitStackLimit_ (set to
// UINTPTR_MAX). The JS engine must continually poll one of these fields
// and call handleInterrupt if either field has the interrupt value. (The
// point of setting jitStackLimit_ to UINTPTR_MAX is that JIT code already
// needs to guard on jitStackLimit_ in every function prologue to avoid
// stack overflow, so we avoid a second branch on interrupt_ by setting
// jitStackLimit_ to a value that is guaranteed to fail the guard.)
//
// Note that the writes to interrupt_ and jitStackLimit_ use a Relaxed
// Atomic so, while the writes are guaranteed to eventually be visible to
// the main thread, it can happen in any order. handleInterrupt calls the
// interrupt callback if either is set, so it really doesn't matter as long
// as the JS engine is continually polling at least one field. In corner
// cases, this relaxed ordering could lead to an interrupt handler being
// called twice in succession after a single requestInterrupt call, but
// that's fine.
void requestInterrupt(InterruptMode mode);
bool handleInterrupt(JSContext *cx);
MOZ_ALWAYS_INLINE bool hasPendingInterrupt() const {
return interrupt_;
}
MOZ_ALWAYS_INLINE bool hasPendingInterruptPar() const {
return interruptPar_;
}
// For read-only JIT use:
void *addressOfInterruptUint32() {
static_assert(sizeof(interrupt_) == sizeof(uint32_t), "Assumed by JIT callers");
return &interrupt_;
}
void *addressOfInterruptParUint32() {
static_assert(sizeof(interruptPar_) == sizeof(uint32_t), "Assumed by JIT callers");
return &interruptPar_;
}
/*
* If non-zero, ForkJoin should service an interrupt. This is a separate
* flag from |interrupt| because we cannot use the mprotect trick with PJS
* code and ignore the TriggerCallbackAnyThreadDontStopIon trigger.
*/
mozilla::Atomic<bool, mozilla::Relaxed> interruptPar;
/* Set when handling a signal for a thread associated with this runtime. */
bool handlingSignal;
@ -949,7 +906,7 @@ struct JSRuntime : public JS::shadow::Runtime,
void setDefaultVersion(JSVersion v) { defaultVersion_ = v; }
/* Base address of the native stack for the current thread. */
const uintptr_t nativeStackBase;
uintptr_t nativeStackBase;
/* The native stack size limit that runtime should not exceed. */
size_t nativeStackQuota[js::StackKindCount];
@ -1315,6 +1272,10 @@ struct JSRuntime : public JS::shadow::Runtime,
bool jitSupportsFloatingPoint;
bool jitSupportsSimd;
// Used to reset stack limit after a signaled interrupt (i.e. jitStackLimit_ = -1)
// has been noticed by Ion/Baseline.
void resetJitStackLimit();
// Cache for jit::GetPcScript().
js::jit::PcScriptCache *ionPcScriptCache;
@ -1375,6 +1336,17 @@ struct JSRuntime : public JS::shadow::Runtime,
/* onOutOfMemory but can call the largeAllocationFailureCallback. */
JS_FRIEND_API(void *) onOutOfMemoryCanGC(void *p, size_t bytes);
// Ways in which the interrupt callback on the runtime can be triggered,
// varying based on which thread is triggering the callback.
enum InterruptMode {
RequestInterruptMainThread,
RequestInterruptAnyThread,
RequestInterruptAnyThreadDontStopIon,
RequestInterruptAnyThreadForkJoin
};
void requestInterrupt(InterruptMode mode);
void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::RuntimeSizes *runtime);
private:
@ -1598,6 +1570,13 @@ class MOZ_STACK_CLASS AutoKeepAtoms
}
};
inline void
PerThreadData::setJitStackLimit(uintptr_t limit)
{
MOZ_ASSERT(runtime_->currentThreadOwnsInterruptLock());
jitStackLimit = limit;
}
inline JSRuntime *
PerThreadData::runtimeFromMainThread()
{