зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to fx-team. a=merge
This commit is contained in:
Коммит
ccd8895aa4
|
@ -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'
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -34,8 +34,8 @@ public:
|
|||
|
||||
enum HardwareState
|
||||
{
|
||||
kHardwareClosed,
|
||||
kHardwareOpen
|
||||
kHardwareOpen,
|
||||
kHardwareClosed
|
||||
};
|
||||
virtual void OnHardwareStateChange(HardwareState aState) { }
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -17,6 +17,7 @@ UNIFIED_SOURCES += [
|
|||
'CameraControlImpl.cpp',
|
||||
'CameraPreferences.cpp',
|
||||
'CameraPreviewMediaStream.cpp',
|
||||
'CameraRecorderProfiles.cpp',
|
||||
'DOMCameraCapabilities.cpp',
|
||||
'DOMCameraControl.cpp',
|
||||
'DOMCameraControlListener.cpp',
|
||||
|
|
|
@ -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!
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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_);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
{
|
||||
|
|
|
@ -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 *
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/************************************************************************/
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче