Bug 882145: gUM constraints converted to webidl + gUMDevices() takes constraints. r=bz r=jesup

This commit is contained in:
Jan-Ivar Bruaroey 2013-09-14 00:48:39 -04:00
parent 8a78d90df8
commit 251d4428cf
18 changed files with 398 additions and 229 deletions

View File

@ -65,18 +65,18 @@ function getBrowserForWindow(aContentWindow) {
}
function handleRequest(aSubject, aTopic, aData) {
let {windowID: windowID, callID: callID} = JSON.parse(aData);
let params = aSubject.QueryInterface(Ci.nsIMediaStreamOptions);
let constraints = aSubject.getConstraints();
Services.wm.getMostRecentWindow(null).navigator.mozGetUserMediaDevices(
constraints,
function (devices) {
prompt(windowID, callID, params.audio, params.video || params.picture, devices);
prompt(aSubject.windowID, aSubject.callID, constraints.audio,
constraints.video || constraints.picture, devices);
},
function (error) {
// bug 827146 -- In the future, the UI should catch NO_DEVICES_FOUND
// and allow the user to plug in a device, instead of immediately failing.
denyRequest(callID, error);
denyRequest(aSubject.callID, error);
}
);
}

View File

@ -10,6 +10,7 @@
#include "nsCycleCollectionParticipant.h"
#include "mozilla/dom/SpeechRecognitionBinding.h"
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "mozilla/MediaManager.h"
#include "mozilla/Services.h"
@ -711,11 +712,16 @@ SpeechRecognition::Start(ErrorResult& aRv)
rv = mRecognitionService->Initialize(this->asWeakPtr());
NS_ENSURE_SUCCESS_VOID(rv);
AutoSafeJSContext cx;
MediaStreamConstraintsInitializer constraints;
constraints.mAudio.SetAsBoolean() = true;
if (!mTestConfig.mFakeFSMEvents) {
MediaManager* manager = MediaManager::Get();
manager->GetUserMedia(false,
manager->GetUserMedia(cx,
false,
GetOwner(),
new GetUserMediaStreamOptions(),
constraints,
new GetUserMediaSuccessCallback(this),
new GetUserMediaErrorCallback(this));
}
@ -922,56 +928,6 @@ SpeechRecognition::GetName(SpeechEvent* aEvent)
return names[aEvent->mType];
}
NS_IMPL_ISUPPORTS1(SpeechRecognition::GetUserMediaStreamOptions, nsIMediaStreamOptions)
NS_IMETHODIMP
SpeechRecognition::GetUserMediaStreamOptions::GetFake(bool* aFake)
{
*aFake = false;
return NS_OK;
}
NS_IMETHODIMP
SpeechRecognition::GetUserMediaStreamOptions::GetAudio(bool* aAudio)
{
*aAudio = true;
return NS_OK;
}
NS_IMETHODIMP
SpeechRecognition::GetUserMediaStreamOptions::GetVideo(bool* aVideo)
{
*aVideo = false;
return NS_OK;
}
NS_IMETHODIMP
SpeechRecognition::GetUserMediaStreamOptions::GetPicture(bool* aPicture)
{
*aPicture = false;
return NS_OK;
}
NS_IMETHODIMP
SpeechRecognition::GetUserMediaStreamOptions::GetCamera(nsAString& aCamera)
{
return NS_OK;
}
NS_IMETHODIMP
SpeechRecognition::GetUserMediaStreamOptions::GetAudioDevice(nsIMediaDevice** aAudioDevice)
{
*aAudioDevice = nullptr;
return NS_OK;
}
NS_IMETHODIMP
SpeechRecognition::GetUserMediaStreamOptions::GetVideoDevice(nsIMediaDevice** aVideoDevice)
{
*aVideoDevice = nullptr;
return NS_OK;
}
SpeechEvent::~SpeechEvent()
{
delete mAudioSegment;

View File

@ -176,16 +176,6 @@ private:
void SetState(FSMState state);
bool StateBetween(FSMState begin, FSMState end);
class GetUserMediaStreamOptions : public nsIMediaStreamOptions
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIMEDIASTREAMOPTIONS
GetUserMediaStreamOptions() {}
virtual ~GetUserMediaStreamOptions() {}
};
class GetUserMediaSuccessCallback : public nsIDOMGetUserMediaSuccessCallback
{
public:

View File

@ -986,18 +986,19 @@ Navigator::GetGeolocation(ErrorResult& aRv)
#ifdef MOZ_MEDIA_NAVIGATOR
void
Navigator::MozGetUserMedia(nsIMediaStreamOptions* aParams,
MozDOMGetUserMediaSuccessCallback* aOnSuccess,
MozDOMGetUserMediaErrorCallback* aOnError,
Navigator::MozGetUserMedia(JSContext* aCx,
const MediaStreamConstraints& aConstraints,
NavigatorUserMediaSuccessCallback& aOnSuccess,
NavigatorUserMediaErrorCallback& aOnError,
ErrorResult& aRv)
{
CallbackObjectHolder<MozDOMGetUserMediaSuccessCallback,
nsIDOMGetUserMediaSuccessCallback> holder1(aOnSuccess);
CallbackObjectHolder<NavigatorUserMediaSuccessCallback,
nsIDOMGetUserMediaSuccessCallback> holder1(&aOnSuccess);
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onsuccess =
holder1.ToXPCOMCallback();
CallbackObjectHolder<MozDOMGetUserMediaErrorCallback,
nsIDOMGetUserMediaErrorCallback> holder2(aOnError);
CallbackObjectHolder<NavigatorUserMediaErrorCallback,
nsIDOMGetUserMediaErrorCallback> holder2(&aOnError);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onerror = holder2.ToXPCOMCallback();
if (!mWindow || !mWindow->GetOuterWindow() ||
@ -1009,21 +1010,23 @@ Navigator::MozGetUserMedia(nsIMediaStreamOptions* aParams,
bool privileged = nsContentUtils::IsChromeDoc(mWindow->GetExtantDoc());
MediaManager* manager = MediaManager::Get();
aRv = manager->GetUserMedia(privileged, mWindow, aParams, onsuccess, onerror);
aRv = manager->GetUserMedia(aCx, privileged, mWindow, aConstraints,
onsuccess, onerror);
}
void
Navigator::MozGetUserMediaDevices(MozGetUserMediaDevicesSuccessCallback* aOnSuccess,
MozDOMGetUserMediaErrorCallback* aOnError,
Navigator::MozGetUserMediaDevices(const MediaStreamConstraintsInternal& aConstraints,
MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
NavigatorUserMediaErrorCallback& aOnError,
ErrorResult& aRv)
{
CallbackObjectHolder<MozGetUserMediaDevicesSuccessCallback,
nsIGetUserMediaDevicesSuccessCallback> holder1(aOnSuccess);
nsIGetUserMediaDevicesSuccessCallback> holder1(&aOnSuccess);
nsCOMPtr<nsIGetUserMediaDevicesSuccessCallback> onsuccess =
holder1.ToXPCOMCallback();
CallbackObjectHolder<MozDOMGetUserMediaErrorCallback,
nsIDOMGetUserMediaErrorCallback> holder2(aOnError);
CallbackObjectHolder<NavigatorUserMediaErrorCallback,
nsIDOMGetUserMediaErrorCallback> holder2(&aOnError);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onerror = holder2.ToXPCOMCallback();
if (!mWindow || !mWindow->GetOuterWindow() ||
@ -1033,7 +1036,7 @@ Navigator::MozGetUserMediaDevices(MozGetUserMediaDevicesSuccessCallback* aOnSucc
}
MediaManager* manager = MediaManager::Get();
aRv = manager->GetUserMediaDevices(mWindow, onsuccess, onerror);
aRv = manager->GetUserMediaDevices(mWindow, aConstraints, onsuccess, onerror);
}
#endif

View File

@ -22,7 +22,6 @@ class nsPIDOMWindow;
class nsIDOMMozConnection;
class nsIDOMMozMobileMessageManager;
class nsIDOMNavigatorSystemMessages;
class nsIMediaStreamOptions;
class nsDOMCameraManager;
class nsDOMDeviceStorage;
@ -30,6 +29,8 @@ namespace mozilla {
namespace dom {
class Geolocation;
class systemMessageCallback;
class MediaStreamConstraints;
class MediaStreamConstraintsInternal;
}
}
@ -62,8 +63,8 @@ class MozIdleObserver;
class Gamepad;
#endif // MOZ_GAMEPAD
#ifdef MOZ_MEDIA_NAVIGATOR
class MozDOMGetUserMediaSuccessCallback;
class MozDOMGetUserMediaErrorCallback;
class NavigatorUserMediaSuccessCallback;
class NavigatorUserMediaErrorCallback;
class MozGetUserMediaDevicesSuccessCallback;
#endif // MOZ_MEDIA_NAVIGATOR
@ -240,12 +241,14 @@ public:
system::AudioChannelManager* GetMozAudioChannelManager(ErrorResult& aRv);
#endif // MOZ_AUDIO_CHANNEL_MANAGER
#ifdef MOZ_MEDIA_NAVIGATOR
void MozGetUserMedia(nsIMediaStreamOptions* aParams,
MozDOMGetUserMediaSuccessCallback* aOnSuccess,
MozDOMGetUserMediaErrorCallback* aOnError,
void MozGetUserMedia(JSContext* aCx,
const MediaStreamConstraints& aConstraints,
NavigatorUserMediaSuccessCallback& aOnSuccess,
NavigatorUserMediaErrorCallback& aOnError,
ErrorResult& aRv);
void MozGetUserMediaDevices(MozGetUserMediaDevicesSuccessCallback* aOnSuccess,
MozDOMGetUserMediaErrorCallback* aOnError,
void MozGetUserMediaDevices(const MediaStreamConstraintsInternal& aConstraints,
MozGetUserMediaDevicesSuccessCallback& aOnSuccess,
NavigatorUserMediaErrorCallback& aOnError,
ErrorResult& aRv);
#endif // MOZ_MEDIA_NAVIGATOR
bool DoNewResolve(JSContext* aCx, JS::Handle<JSObject*> aObject,

View File

@ -1785,8 +1785,6 @@ addExternalIface('MozConnection', headerFile='nsIDOMConnection.h')
addExternalIface('MozControllers', nativeType='nsIControllers')
addExternalIface('MozFrameLoader', nativeType='nsIFrameLoader', notflattened=True)
addExternalIface('MozIccManager', headerFile='nsIDOMIccManager.h')
addExternalIface('MozMediaStreamOptions', nativeType='nsIMediaStreamOptions',
headerFile='nsIDOMNavigatorUserMedia.h')
addExternalIface('MozMobileConnection', headerFile='nsIDOMMobileConnection.h')
addExternalIface('MozMobileMessageManager', headerFile='nsIDOMMobileMessageManager.h')
addExternalIface('MozObserver', nativeType='nsIObserver', notflattened=True)

View File

@ -635,6 +635,10 @@ class CGHeaders(CGWrapper):
if unrolled.isUnion():
# UnionConversions.h includes UnionTypes.h
bindingHeaders.add("mozilla/dom/UnionConversions.h")
if dictionary:
# Our dictionary definition is in the header and
# needs the union type.
declareIncludes.add("mozilla/dom/UnionTypes.h")
elif unrolled.isDate():
if dictionary or jsImplementedDescriptors:
headerSet = declareIncludes

View File

@ -0,0 +1,64 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "base/basictypes.h"
#include "GetUserMediaRequest.h"
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "mozilla/dom/GetUserMediaRequestBinding.h"
#include "nsIScriptGlobalObject.h"
#include "nsPIDOMWindow.h"
#include "nsCxPusher.h"
namespace mozilla {
namespace dom {
GetUserMediaRequest::GetUserMediaRequest(
nsPIDOMWindow* aInnerWindow,
const nsAString& aCallID,
const MediaStreamConstraintsInternal& aConstraints)
: mInnerWindow(aInnerWindow)
, mWindowID(aInnerWindow->GetOuterWindow()->WindowID())
, mCallID(aCallID)
, mConstraints(aConstraints)
{
SetIsDOMBinding();
}
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(GetUserMediaRequest, mInnerWindow)
NS_IMPL_CYCLE_COLLECTING_ADDREF(GetUserMediaRequest)
NS_IMPL_CYCLE_COLLECTING_RELEASE(GetUserMediaRequest)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(GetUserMediaRequest)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
JSObject*
GetUserMediaRequest::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
return GetUserMediaRequestBinding::Wrap(aCx, aScope, this);
}
nsISupports* GetUserMediaRequest::GetParentObject()
{
return mInnerWindow;
}
void GetUserMediaRequest::GetCallID(nsString& retval)
{
retval = mCallID;
}
uint64_t GetUserMediaRequest::WindowID()
{
return mWindowID;
}
void
GetUserMediaRequest::GetConstraints(MediaStreamConstraintsInternal &result)
{
result = mConstraints;
}
} // namespace dom
} // namespace mozilla

View File

@ -0,0 +1,48 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef GetUserMediaRequest_h__
#define GetUserMediaRequest_h__
#include "mozilla/ErrorResult.h"
#include "nsISupportsImpl.h"
#include "nsAutoPtr.h"
#include "nsWrapperCache.h"
#include "mozilla/dom/BindingUtils.h"
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "nsPIDOMWindow.h"
namespace mozilla {
namespace dom {
class MediaStreamConstraintsInternal;
class GetUserMediaRequest : public nsISupports, public nsWrapperCache
{
public:
GetUserMediaRequest(nsPIDOMWindow* aInnerWindow, const nsAString& aCallID,
const MediaStreamConstraintsInternal& aConstraints);
virtual ~GetUserMediaRequest() {};
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(GetUserMediaRequest)
virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> scope)
MOZ_OVERRIDE;
nsISupports* GetParentObject();
uint64_t WindowID();
void GetCallID(nsString& retval);
void GetConstraints(MediaStreamConstraintsInternal &result);
private:
nsCOMPtr<nsPIDOMWindow> mInnerWindow;
uint64_t mWindowID;
const nsString mCallID;
MediaStreamConstraintsInternal mConstraints;
};
} // namespace dom
} // namespace mozilla
#endif // GetUserMediaRequest_h__

View File

@ -5,6 +5,7 @@
#include "MediaManager.h"
#include "MediaStreamGraph.h"
#include "GetUserMediaRequest.h"
#ifdef MOZ_WIDGET_GONK
#include "nsIAudioManager.h"
#endif
@ -19,6 +20,8 @@
#include "nsISupportsPrimitives.h"
#include "nsIInterfaceRequestorUtils.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "mozilla/dom/GetUserMediaRequestBinding.h"
// For PR_snprintf
#include "prprf.h"
@ -61,6 +64,75 @@ GetMediaManagerLog()
#define LOG(msg)
#endif
using dom::MediaStreamConstraints; // Outside API (contains JSObject)
using dom::MediaStreamConstraintsInternal; // Storable supported constraints
using dom::MediaTrackConstraintsInternal; // Video or audio constraints
using dom::MediaTrackConstraintSet; // Mandatory or optional constraints
using dom::MediaTrackConstraints; // Raw mMandatory (as JSObject)
using dom::GetUserMediaRequest;
// Used to compare raw MediaTrackConstraintSet against normalized dictionary
// version to detect member differences, e.g. unsupported constraints.
static nsresult CompareDictionaries(JSContext* aCx, JSObject *aA,
const MediaTrackConstraintSet &aB,
nsString *aDifference)
{
JS::Rooted<JSObject*> a(aCx, aA);
JSAutoCompartment ac(aCx, aA);
JS::Rooted<JS::Value> bval(aCx);
aB.ToObject(aCx, JS::NullPtr(), &bval);
JS::Rooted<JSObject*> b(aCx, &bval.toObject());
// Iterate over each property in A, and check if it is in B
JS::AutoIdArray props(aCx, JS_Enumerate(aCx, a));
for (size_t i = 0; i < props.length(); i++) {
JS::Rooted<JS::Value> bprop(aCx);
if (!JS_GetPropertyById(aCx, b, props[i], &bprop)) {
LOG(("Error parsing dictionary!\n"));
return NS_ERROR_UNEXPECTED;
}
if (bprop.isUndefined()) {
// Unknown property found in A. Bail with name
JS::Rooted<JS::Value> nameval(aCx);
bool success = JS_IdToValue(aCx, props[i], nameval.address());
NS_ENSURE_TRUE(success, NS_ERROR_UNEXPECTED);
JS::Rooted<JSString*> namestr(aCx, JS_ValueToString(aCx, nameval));
NS_ENSURE_TRUE(namestr, NS_ERROR_UNEXPECTED);
aDifference->Assign(JS_GetStringCharsZ(aCx, namestr));
return NS_OK;
}
}
aDifference->Truncate();
return NS_OK;
}
// Look for and return any unknown mandatory constraint. Done by comparing
// a raw MediaTrackConstraints against a normalized copy, both passed in.
static nsresult ValidateTrackConstraints(
JSContext *aCx, JSObject *aRaw,
const MediaTrackConstraintsInternal &aNormalized,
nsString *aOutUnknownConstraint)
{
// First find raw mMandatory member (use MediaTrackConstraints as helper)
dom::RootedDictionary<MediaTrackConstraints> track(aCx);
JS::Rooted<JS::Value> rawval(aCx, JS::ObjectValue(*aRaw));
bool success = track.Init(aCx, rawval);
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
if (track.mMandatory.WasPassed()) {
nsresult rv = CompareDictionaries(aCx, track.mMandatory.Value(),
aNormalized.mMandatory,
aOutUnknownConstraint);
NS_ENSURE_SUCCESS(rv, rv);
}
return NS_OK;
}
/**
* Send an error back to content. The error is the form a string.
* Do this only on the main thread. The success callback is also passed here
@ -520,36 +592,6 @@ private:
class GetUserMediaRunnable : public nsRunnable
{
public:
/**
* The caller can choose to provide a MediaDevice as the last argument,
* if one is not provided, a default device is automatically chosen.
*/
GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
MediaEnginePrefs &aPrefs,
MediaDevice* aAudioDevice, MediaDevice* aVideoDevice)
: mAudio(aAudio)
, mVideo(aVideo)
, mPicture(aPicture)
, mSuccess(aSuccess)
, mError(aError)
, mWindowID(aWindowID)
, mListener(aListener)
, mPrefs(aPrefs)
, mDeviceChosen(true)
, mBackendChosen(false)
, mManager(MediaManager::GetInstance())
{
if (mAudio) {
mAudioDevice = aAudioDevice;
}
if (mVideo) {
mVideoDevice = aVideoDevice;
}
}
GetUserMediaRunnable(bool aAudio, bool aVideo, bool aPicture,
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
@ -847,14 +889,15 @@ class GetUserMediaDevicesRunnable : public nsRunnable
{
public:
GetUserMediaDevicesRunnable(
const MediaStreamConstraintsInternal& aConstraints,
already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
uint64_t aWindowId)
: mSuccess(aSuccess)
: mConstraints(aConstraints)
, mSuccess(aSuccess)
, mError(aError)
, mManager(MediaManager::GetInstance())
, mWindowId(aWindowId)
{}
, mWindowId(aWindowId) {}
NS_IMETHOD
Run()
@ -896,6 +939,7 @@ public:
}
private:
MediaStreamConstraintsInternal mConstraints;
already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
nsRefPtr<MediaManager> mManager;
@ -973,14 +1017,13 @@ MediaManager::GetInstance()
* for handling all incoming getUserMedia calls from every window.
*/
nsresult
MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
nsIMediaStreamOptions* aParams,
MediaManager::GetUserMedia(JSContext* aCx, bool aPrivileged,
nsPIDOMWindow* aWindow, const MediaStreamConstraints& aRawConstraints,
nsIDOMGetUserMediaSuccessCallback* aOnSuccess,
nsIDOMGetUserMediaErrorCallback* aOnError)
{
NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
NS_ENSURE_TRUE(aParams, NS_ERROR_NULL_POINTER);
NS_ENSURE_TRUE(aWindow, NS_ERROR_NULL_POINTER);
NS_ENSURE_TRUE(aOnError, NS_ERROR_NULL_POINTER);
NS_ENSURE_TRUE(aOnSuccess, NS_ERROR_NULL_POINTER);
@ -988,50 +1031,53 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
nsCOMPtr<nsIDOMGetUserMediaSuccessCallback> onSuccess(aOnSuccess);
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError(aOnError);
/* Get options */
Maybe<JSAutoCompartment> ac;
if (aRawConstraints.mAudio.IsObject() || aRawConstraints.mVideo.IsObject()) {
ac.construct(aCx, (aRawConstraints.mVideo.IsObject()?
aRawConstraints.mVideo.GetAsObject() :
aRawConstraints.mAudio.GetAsObject()));
}
// aRawConstraints has JSObjects in it, so process it by copying it into
// MediaStreamConstraintsInternal which does not.
dom::RootedDictionary<MediaStreamConstraintsInternal> c(aCx);
// TODO: Simplify this part once Bug 767924 is fixed.
// Since we cannot yet use unions on non-objects, we process the raw object
// into discrete members for internal use until Bug 767924 is fixed
nsresult rv;
bool fake, audio, video, picture;
nsString unknownConstraintFound;
rv = aParams->GetFake(&fake);
NS_ENSURE_SUCCESS(rv, rv);
if (aRawConstraints.mAudio.IsObject()) {
JS::Rooted<JS::Value> temp(aCx,
JS::ObjectValue(*aRawConstraints.mAudio.GetAsObject()));
bool success = c.mAudiom.Init(aCx, temp);
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
rv = aParams->GetPicture(&picture);
NS_ENSURE_SUCCESS(rv, rv);
rv = aParams->GetAudio(&audio);
NS_ENSURE_SUCCESS(rv, rv);
rv = aParams->GetVideo(&video);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMediaDevice> audiodevice;
rv = aParams->GetAudioDevice(getter_AddRefs(audiodevice));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIMediaDevice> videodevice;
rv = aParams->GetVideoDevice(getter_AddRefs(videodevice));
NS_ENSURE_SUCCESS(rv, rv);
// If a device was provided, make sure it support the type of stream requested.
if (audiodevice) {
nsString type;
audiodevice->GetType(type);
if (audio && !type.EqualsLiteral("audio")) {
return NS_ERROR_FAILURE;
}
}
if (videodevice) {
nsString type;
videodevice->GetType(type);
if ((picture || video) && !type.EqualsLiteral("video")) {
return NS_ERROR_FAILURE;
}
rv = ValidateTrackConstraints(aCx, aRawConstraints.mAudio.GetAsObject(),
c.mAudiom, &unknownConstraintFound);
NS_ENSURE_SUCCESS(rv, rv);
c.mAudio = true;
} else {
c.mAudio = aRawConstraints.mAudio.GetAsBoolean();
}
if (aRawConstraints.mVideo.IsObject()) {
JS::Rooted<JS::Value> temp(aCx,
JS::ObjectValue(*aRawConstraints.mVideo.GetAsObject()));
bool success = c.mVideom.Init(aCx, temp);
NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
// We only support "front" or "back". TBD: Send to GetUserMediaRunnable.
nsString cameraType;
rv = aParams->GetCamera(cameraType);
NS_ENSURE_SUCCESS(rv, rv);
rv = ValidateTrackConstraints(aCx, aRawConstraints.mVideo.GetAsObject(),
c.mVideom, &unknownConstraintFound);
NS_ENSURE_SUCCESS(rv, rv);
c.mVideo = true;
} else {
c.mVideo = aRawConstraints.mVideo.GetAsBoolean();
}
c.mPicture = aRawConstraints.mPicture;
c.mFake = aRawConstraints.mFake;
/**
* If we were asked to get a picture, before getting a snapshot, we check if
@ -1041,7 +1087,7 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
* may point we can decide whether to extend this test there as well.
*/
#if !defined(MOZ_WEBRTC)
if (picture && !aPrivileged) {
if (c.mPicture && !aPrivileged) {
if (aWindow->GetPopupControlState() > openControlled) {
nsCOMPtr<nsIPopupWindowManager> pm =
do_GetService(NS_POPUPWINDOWMANAGER_CONTRACTID);
@ -1079,6 +1125,22 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
listeners = new StreamListeners;
GetActiveWindows()->Put(windowID, listeners);
}
if (!unknownConstraintFound.IsEmpty()) {
// An unsupported mandatory constraint was found.
// Things are set up enough here that we can fire Error callback.
LOG(("Unsupported mandatory constraint: %s\n",
NS_ConvertUTF16toUTF8(unknownConstraintFound).get()));
nsString errormsg(NS_LITERAL_STRING("NOT_SUPPORTED_ERR: "));
errormsg.Append(unknownConstraintFound);
NS_DispatchToMainThread(new ErrorCallbackRunnable(onSuccess.forget(),
onError.forget(),
errormsg, windowID));
return NS_OK;
}
// Ensure there's a thread for gum to proxy to off main thread
nsIThread *mediaThread = MediaManager::GetThread();
@ -1096,32 +1158,18 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
/**
* Pass runnables along to GetUserMediaRunnable so it can add the
* MediaStreamListener to the runnable list. The last argument can
* optionally be a MediaDevice object, which should provided if one was
* selected by the user via the UI, or was provided by privileged code
* via the device: attribute via nsIMediaStreamOptions.
*
* If a fake stream was requested, we force the use of the default backend.
* MediaStreamListener to the runnable list.
*/
// XXX take options from constraints instead of prefs
if (fake) {
if (c.mFake) {
// Fake stream from default backend.
gUMRunnable = new GetUserMediaRunnable(
audio, video, onSuccess.forget(), onError.forget(), windowID, listener, mPrefs,
new MediaEngineDefault()
);
} else if (audiodevice || videodevice) {
// Stream from provided device.
gUMRunnable = new GetUserMediaRunnable(
audio, video, picture, onSuccess.forget(), onError.forget(), windowID, listener, mPrefs,
static_cast<MediaDevice*>(audiodevice.get()),
static_cast<MediaDevice*>(videodevice.get())
);
c.mAudio, c.mVideo, onSuccess.forget(),
onError.forget(), windowID, listener, mPrefs, new MediaEngineDefault());
} else {
// Stream from default device from WebRTC backend.
gUMRunnable = new GetUserMediaRunnable(
audio, video, picture, onSuccess.forget(), onError.forget(), windowID, listener, mPrefs
);
c.mAudio, c.mVideo, c.mPicture,
onSuccess.forget(), onError.forget(), windowID, listener, mPrefs);
}
#ifdef MOZ_B2G_CAMERA
@ -1134,14 +1182,14 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
#endif
#if defined(ANDROID) && !defined(MOZ_WIDGET_GONK)
if (picture) {
if (c.mPicture) {
// ShowFilePickerForMimeType() must run on the Main Thread! (on Android)
NS_DispatchToMainThread(gUMRunnable);
return NS_OK;
}
#endif
// XXX No full support for picture in Desktop yet (needs proper UI)
if (aPrivileged || fake) {
if (aPrivileged || c.mFake) {
mMediaThread->Dispatch(gUMRunnable, NS_DISPATCH_NORMAL);
} else {
// Ask for user permission, and dispatch runnable (or not) when a response
@ -1164,22 +1212,10 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
// Store the current callback.
mActiveCallbacks.Put(callID, gUMRunnable);
// Construct JSON structure with both the windowID and the callID.
nsAutoString data;
data.Append(NS_LITERAL_STRING("{\"windowID\":"));
// Convert window ID to string.
char windowBuffer[32];
PR_snprintf(windowBuffer, sizeof(windowBuffer), "%llu",
aWindow->GetOuterWindow()->WindowID());
data.Append(NS_ConvertUTF8toUTF16(windowBuffer));
data.Append(NS_LITERAL_STRING(", \"callID\":\""));
data.Append(callID);
data.Append(NS_LITERAL_STRING("\"}"));
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
obs->NotifyObservers(aParams, "getUserMedia:request", data.get());
nsRefPtr<GetUserMediaRequest> req = new GetUserMediaRequest(aWindow,
callID, c);
obs->NotifyObservers(req, "getUserMedia:request", nullptr);
}
return NS_OK;
@ -1187,6 +1223,7 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
nsresult
MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
const MediaStreamConstraintsInternal& aConstraints,
nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
nsIDOMGetUserMediaErrorCallback* aOnError)
{
@ -1199,7 +1236,7 @@ MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
nsCOMPtr<nsIDOMGetUserMediaErrorCallback> onError(aOnError);
nsCOMPtr<nsIRunnable> gUMDRunnable = new GetUserMediaDevicesRunnable(
onSuccess.forget(), onError.forget(), aWindow->WindowID()
aConstraints, onSuccess.forget(), onError.forget(), aWindow->WindowID()
);
nsCOMPtr<nsIThread> deviceThread;

View File

@ -20,6 +20,7 @@
#include "nsXULAppAPI.h"
#include "mozilla/Attributes.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/dom/MediaStreamTrackBinding.h"
#include "prlog.h"
#include "DOMMediaStream.h"
@ -32,6 +33,11 @@
#endif
namespace mozilla {
namespace dom {
class MediaStreamConstraints;
class NavigatorUserMediaSuccessCallback;
class NavigatorUserMediaErrorCallback;
}
#ifdef PR_LOGGING
extern PRLogModuleInfo* GetMediaManagerLog();
@ -407,11 +413,14 @@ public:
void RemoveFromWindowList(uint64_t aWindowID,
GetUserMediaCallbackMediaStreamListener *aListener);
nsresult GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
nsIMediaStreamOptions* aParams,
nsresult GetUserMedia(JSContext* aCx, bool aPrivileged,
nsPIDOMWindow* aWindow,
const dom::MediaStreamConstraints& aRawConstraints,
nsIDOMGetUserMediaSuccessCallback* onSuccess,
nsIDOMGetUserMediaErrorCallback* onError);
nsresult GetUserMediaDevices(nsPIDOMWindow* aWindow,
const dom::MediaStreamConstraintsInternal& aConstraints,
nsIGetUserMediaDevicesSuccessCallback* onSuccess,
nsIDOMGetUserMediaErrorCallback* onError);
void OnNavigation(uint64_t aWindowID);

View File

@ -19,11 +19,16 @@ XPIDL_MODULE = 'dom_media'
MODULE = 'dom'
EXPORTS.mozilla.dom += [
'GetUserMediaRequest.h',
]
EXPORTS.mozilla += [
'MediaManager.h',
]
CPP_SOURCES += [
'GetUserMediaRequest.cpp',
'MediaManager.cpp',
]

View File

@ -35,15 +35,3 @@ interface nsIDOMGetUserMediaErrorCallback : nsISupports
{
void onError(in DOMString error);
};
[scriptable, uuid(f34a3616-395a-43cd-b275-bf81750ac8b9)]
interface nsIMediaStreamOptions : nsISupports
{
readonly attribute boolean fake;
readonly attribute boolean audio;
readonly attribute boolean video;
readonly attribute boolean picture;
readonly attribute DOMString camera;
readonly attribute nsIMediaDevice audioDevice;
readonly attribute nsIMediaDevice videoDevice;
};

View File

@ -39,7 +39,7 @@ var exceptionTests = [
// type to any mozGetUserMedia parameter should throw
// the correct exception specified
{ params: [1, unexpectedCall, unexpectedCall],
error: "Argument 1 of Navigator.mozGetUserMedia is not an object.",
error: "Argument 1 of Navigator.mozGetUserMedia can't be converted to a dictionary.",
message: "wrong object type as first parameter" },
{ params: [{video: true, fake: true}, 1, unexpectedCall],
error: "Argument 2 of Navigator.mozGetUserMedia is not an object.",

View File

@ -0,0 +1,14 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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/.
*
* This is an internal IDL file
*/
[NoInterfaceObject]
interface GetUserMediaRequest {
readonly attribute unsigned long long windowID;
readonly attribute DOMString callID;
MediaStreamConstraintsInternal getConstraints();
};

View File

@ -10,6 +10,55 @@
* liability, trademark and document use rules apply.
*/
// Important! Do not ever add members that might need tracing (e.g. object)
// to MediaTrackConstraintSet or any dictionary marked XxxInternal here
enum VideoFacingModeEnum {
"user",
"environment",
"left",
"right"
};
dictionary MediaTrackConstraintSet {
VideoFacingModeEnum facingMode;
};
// MediaTrackConstraint = single-property-subset of MediaTrackConstraintSet
// Implemented as full set. Test Object.keys(pair).length == 1
// typedef MediaTrackConstraintSet MediaTrackConstraint; // TODO: Bug 913053
dictionary MediaStreamConstraints {
(boolean or object) audio = false; // TODO: Once Bug 767924 fixed change to
(boolean or object) video = false; // (boolean or MediaTrackConstraints)
boolean picture = false;
boolean fake = false;
};
// Internal dictionary to process the raw objects in (boolean or object)
// workaround above. Since we cannot yet use unions on non-objects, we process
// the data into discrete members for internal use until Bug 767924 is fixed:
dictionary MediaStreamConstraintsInternal {
boolean audio = false;
boolean video = false;
boolean picture = false;
boolean fake = false;
MediaTrackConstraintsInternal audiom;
MediaTrackConstraintsInternal videom;
};
dictionary MediaTrackConstraints {
object mandatory; // so we can see unknown + unsupported constraints
sequence<MediaTrackConstraintSet> _optional; // a.k.a. MediaTrackConstraint
};
dictionary MediaTrackConstraintsInternal {
MediaTrackConstraintSet mandatory; // holds only supported constraints
sequence<MediaTrackConstraintSet> _optional; // a.k.a. MediaTrackConstraint
};
interface MediaStreamTrack {
readonly attribute DOMString kind;
readonly attribute DOMString id;

View File

@ -320,22 +320,22 @@ partial interface Navigator {
#endif // MOZ_AUDIO_CHANNEL_MANAGER
#ifdef MOZ_MEDIA_NAVIGATOR
// nsIDOMNavigatorUserMedia
callback MozDOMGetUserMediaSuccessCallback = void (nsISupports? value);
callback MozDOMGetUserMediaErrorCallback = void (DOMString error);
interface MozMediaStreamOptions;
callback NavigatorUserMediaSuccessCallback = void (MediaStream stream);
callback NavigatorUserMediaErrorCallback = void (DOMString error);
partial interface Navigator {
[Throws, Func="Navigator::HasUserMediaSupport"]
void mozGetUserMedia(MozMediaStreamOptions? params,
MozDOMGetUserMediaSuccessCallback? onsuccess,
MozDOMGetUserMediaErrorCallback? onerror);
void mozGetUserMedia(MediaStreamConstraints constraints,
NavigatorUserMediaSuccessCallback successCallback,
NavigatorUserMediaErrorCallback errorCallback);
};
// nsINavigatorUserMedia
callback MozGetUserMediaDevicesSuccessCallback = void (nsIVariant? devices);
partial interface Navigator {
[Throws, ChromeOnly]
void mozGetUserMediaDevices(MozGetUserMediaDevicesSuccessCallback? onsuccess,
MozDOMGetUserMediaErrorCallback? onerror);
void mozGetUserMediaDevices(MediaStreamConstraintsInternal constraints,
MozGetUserMediaDevicesSuccessCallback onsuccess,
NavigatorUserMediaErrorCallback onerror);
};
#endif // MOZ_MEDIA_NAVIGATOR

View File

@ -101,6 +101,7 @@ WEBIDL_FILES = [
'Function.webidl',
'GainNode.webidl',
'Geolocation.webidl',
'GetUserMediaRequest.webidl',
'History.webidl',
'HTMLAnchorElement.webidl',
'HTMLAppletElement.webidl',