Bug 882145: reland changesets bc4ee7cbd7bd and cf373e408a6b r=bz

This commit is contained in:
Randell Jesup
2013-09-16 02:34:57 -04:00
parent f8628e4a22
commit 7ca83ce649
18 changed files with 580 additions and 340 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,76 @@ 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;
using dom::Sequence;
// 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
@@ -237,6 +310,28 @@ protected:
*/
NS_IMPL_ISUPPORTS1(MediaDevice, nsIMediaDevice)
MediaDevice::MediaDevice(MediaEngineVideoSource* aSource)
: mHasFacingMode(false)
, mSource(aSource) {
mType.Assign(NS_LITERAL_STRING("video"));
mSource->GetName(mName);
mSource->GetUUID(mID);
// Kludge to test user-facing cameras on OSX.
if (mName.Find(NS_LITERAL_STRING("Face")) != -1) {
mHasFacingMode = true;
mFacingMode = dom::VideoFacingModeEnum::User;
}
}
MediaDevice::MediaDevice(MediaEngineAudioSource* aSource)
: mHasFacingMode(false)
, mSource(aSource) {
mType.Assign(NS_LITERAL_STRING("audio"));
mSource->GetName(mName);
mSource->GetUUID(mID);
}
NS_IMETHODIMP
MediaDevice::GetName(nsAString& aName)
{
@@ -258,6 +353,18 @@ MediaDevice::GetId(nsAString& aID)
return NS_OK;
}
NS_IMETHODIMP
MediaDevice::GetFacingMode(nsAString& aFacingMode)
{
if (mHasFacingMode) {
aFacingMode.Assign(NS_ConvertUTF8toUTF16(
dom::VideoFacingModeEnumValues::strings[uint32_t(mFacingMode)].value));
} else {
aFacingMode.Truncate(0);
}
return NS_OK;
}
MediaEngineSource*
MediaDevice::GetSource()
{
@@ -508,6 +615,112 @@ private:
nsRefPtr<MediaManager> mManager; // get ref to this when creating the runnable
};
/**
* Helper functions that implement the constraints algorithm from
* http://dev.w3.org/2011/webrtc/editor/getusermedia.html#methods-5
*/
static bool SatisfyConstraint(const MediaEngineVideoSource *,
const MediaTrackConstraintSet &aConstraints,
nsIMediaDevice &aCandidate)
{
if (aConstraints.mFacingMode.WasPassed()) {
nsString s;
aCandidate.GetFacingMode(s);
if (!s.EqualsASCII(dom::VideoFacingModeEnumValues::strings[
uint32_t(aConstraints.mFacingMode.Value())].value)) {
return false;
}
}
// TODO: Add more video-specific constraints
return true;
}
static bool SatisfyConstraint(const MediaEngineAudioSource *,
const MediaTrackConstraintSet &aConstraints,
nsIMediaDevice &aCandidate)
{
// TODO: Add audio-specific constraints
return true;
}
typedef nsTArray<nsCOMPtr<nsIMediaDevice> > SourceSet;
// Source getter that constrains list returned
template<class SourceType>
static SourceSet *
GetSources(MediaEngine *engine,
const MediaTrackConstraintsInternal &aConstraints,
void (MediaEngine::* aEnumerate)(nsTArray<nsRefPtr<SourceType> >*))
{
const SourceType * const type = nullptr;
// First collect sources
SourceSet candidateSet;
{
nsTArray<nsRefPtr<SourceType> > sources;
(engine->*aEnumerate)(&sources);
/**
* We're allowing multiple tabs to access the same camera for parity
* with Chrome. See bug 811757 for some of the issues surrounding
* this decision. To disallow, we'd filter by IsAvailable() as we used
* to.
*/
for (uint32_t len = sources.Length(), i = 0; i < len; i++) {
candidateSet.AppendElement(new MediaDevice(sources[i]));
}
}
// Then apply mandatory constraints
// Note: Iterator must be signed as it can dip below zero
for (int i = 0; i < int(candidateSet.Length()); i++) {
// Overloading instead of template specialization keeps things local
if (!SatisfyConstraint(type, aConstraints.mMandatory, *candidateSet[i])) {
candidateSet.RemoveElementAt(i--);
}
}
// Then apply optional constraints.
//
// These are only effective when there are multiple sources to pick from.
// Spec as-of-this-writing says to run algorithm on "all possible tracks
// of media type T that the browser COULD RETURN" (emphasis added).
//
// We think users ultimately control which devices we could return, so after
// determining the webpage's preferred list, we add the remaining choices
// to the tail, reasoning that they would all have passed individually,
// i.e. if the user had any one of them as their sole device (enabled).
//
// This avoids users having to unplug/disable devices should a webpage pick
// the wrong one (UX-fail). Webpage-preferred devices will be listed first.
SourceSet tailSet;
if (aConstraints.mOptional.WasPassed()) {
const Sequence<MediaTrackConstraintSet> &array = aConstraints.mOptional.Value();
for (int i = 0; i < int(array.Length()); i++) {
SourceSet rejects;
// Note: Iterator must be signed as it can dip below zero
for (int j = 0; j < int(candidateSet.Length()); j++) {
if (!SatisfyConstraint(type, array[i], *candidateSet[j])) {
rejects.AppendElement(candidateSet[j]);
candidateSet.RemoveElementAt(j--);
}
}
(candidateSet.Length()? tailSet : candidateSet).MoveElementsFrom(rejects);
}
}
SourceSet *result = new SourceSet;
result->MoveElementsFrom(candidateSet);
result->MoveElementsFrom(tailSet);
return result;
}
/**
* Runs on a seperate thread and is responsible for enumerating devices.
* Depending on whether a picture or stream was asked for, either
@@ -520,44 +733,13 @@ 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,
GetUserMediaRunnable(
const MediaStreamConstraintsInternal& aConstraints,
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
MediaEnginePrefs &aPrefs)
: mAudio(aAudio)
, mVideo(aVideo)
, mPicture(aPicture)
: mConstraints(aConstraints)
, mSuccess(aSuccess)
, mError(aError)
, mWindowID(aWindowID)
@@ -572,15 +754,14 @@ public:
* The caller can also choose to provide their own backend instead of
* using the one provided by MediaManager::GetBackend.
*/
GetUserMediaRunnable(bool aAudio, bool aVideo,
GetUserMediaRunnable(
const MediaStreamConstraintsInternal& aConstraints,
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> aSuccess,
already_AddRefed<nsIDOMGetUserMediaErrorCallback> aError,
uint64_t aWindowID, GetUserMediaCallbackMediaStreamListener *aListener,
MediaEnginePrefs &aPrefs,
MediaEngine* aBackend)
: mAudio(aAudio)
, mVideo(aVideo)
, mPicture(false)
: mConstraints(aConstraints)
, mSuccess(aSuccess)
, mError(aError)
, mWindowID(aWindowID)
@@ -617,21 +798,23 @@ public:
}
// It is an error if audio or video are requested along with picture.
if (mPicture && (mAudio || mVideo)) {
if (mConstraints.mPicture && (mConstraints.mAudio || mConstraints.mVideo)) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mSuccess, mError, NS_LITERAL_STRING("NOT_SUPPORTED_ERR"), mWindowID
));
return NS_OK;
}
if (mPicture) {
if (mConstraints.mPicture) {
ProcessGetUserMediaSnapshot(mVideoDevice->GetSource(), 0);
return NS_OK;
}
// There's a bug in the permission code that can leave us with mAudio but no audio device
ProcessGetUserMedia((mAudio && mAudioDevice) ? mAudioDevice->GetSource() : nullptr,
(mVideo && mVideoDevice) ? mVideoDevice->GetSource() : nullptr);
ProcessGetUserMedia(((mConstraints.mAudio && mAudioDevice) ?
mAudioDevice->GetSource() : nullptr),
((mConstraints.mVideo && mVideoDevice) ?
mVideoDevice->GetSource() : nullptr));
return NS_OK;
}
@@ -682,69 +865,31 @@ public:
nsresult
SelectDevice()
{
bool found = false;
uint32_t count;
if (mPicture || mVideo) {
nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
mBackend->EnumerateVideoDevices(&videoSources);
if (mConstraints.mPicture || mConstraints.mVideo) {
ScopedDeletePtr<SourceSet> sources (GetSources(mBackend,
mConstraints.mVideom, &MediaEngine::EnumerateVideoDevices));
count = videoSources.Length();
if (count <= 0) {
if (!sources->Length()) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mSuccess, mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
));
mSuccess, mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID));
return NS_ERROR_FAILURE;
}
/**
* We're allowing multiple tabs to access the same camera for parity
* with Chrome. See bug 811757 for some of the issues surrounding
* this decision. To disallow, we'd filter by IsAvailable() as we used
* to.
*/
// Pick the first available device.
for (uint32_t i = 0; i < count; i++) {
nsRefPtr<MediaEngineVideoSource> vSource = videoSources[i];
found = true;
mVideoDevice = new MediaDevice(videoSources[i]);
break;
}
if (!found) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mSuccess, mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
));
return NS_ERROR_FAILURE;
}
mVideoDevice = do_QueryObject((*sources)[0]);
LOG(("Selected video device"));
}
found = false;
if (mAudio) {
nsTArray<nsRefPtr<MediaEngineAudioSource> > audioSources;
mBackend->EnumerateAudioDevices(&audioSources);
if (mConstraints.mAudio) {
ScopedDeletePtr<SourceSet> sources (GetSources(mBackend,
mConstraints.mAudiom, &MediaEngine::EnumerateAudioDevices));
count = audioSources.Length();
if (count <= 0) {
if (!sources->Length()) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mSuccess, mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID
));
return NS_ERROR_FAILURE;
}
for (uint32_t i = 0; i < count; i++) {
nsRefPtr<MediaEngineAudioSource> aSource = audioSources[i];
found = true;
mAudioDevice = new MediaDevice(audioSources[i]);
break;
}
if (!found) {
NS_DispatchToMainThread(new ErrorCallbackRunnable(
mSuccess, mError, NS_LITERAL_STRING("HARDWARE_UNAVAILABLE"), mWindowID
));
mSuccess, mError, NS_LITERAL_STRING("NO_DEVICES_FOUND"), mWindowID));
return NS_ERROR_FAILURE;
}
// Pick the first available device.
mAudioDevice = do_QueryObject((*sources)[0]);
LOG(("Selected audio device"));
}
@@ -818,9 +963,7 @@ public:
}
private:
bool mAudio;
bool mVideo;
bool mPicture;
MediaStreamConstraintsInternal mConstraints;
already_AddRefed<nsIDOMGetUserMediaSuccessCallback> mSuccess;
already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
@@ -847,55 +990,36 @@ 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()
{
NS_ASSERTION(!NS_IsMainThread(), "Don't call on main thread");
MediaEngine *backend = mManager->GetBackend(mWindowId);
uint32_t audioCount, videoCount, i;
nsTArray<nsRefPtr<MediaEngineVideoSource> > videoSources;
mManager->GetBackend(mWindowId)->EnumerateVideoDevices(&videoSources);
videoCount = videoSources.Length();
nsTArray<nsRefPtr<MediaEngineAudioSource> > audioSources;
mManager->GetBackend(mWindowId)->EnumerateAudioDevices(&audioSources);
audioCount = audioSources.Length();
nsTArray<nsCOMPtr<nsIMediaDevice> > *devices =
new nsTArray<nsCOMPtr<nsIMediaDevice> >;
/**
* We're allowing multiple tabs to access the same camera for parity
* with Chrome. See bug 811757 for some of the issues surrounding
* this decision. To disallow, we'd filter by IsAvailable() as we used
* to.
*/
for (i = 0; i < videoCount; i++) {
MediaEngineVideoSource *vSource = videoSources[i];
devices->AppendElement(new MediaDevice(vSource));
ScopedDeletePtr<SourceSet> final (GetSources(backend, mConstraints.mVideom,
&MediaEngine::EnumerateVideoDevices));
{
ScopedDeletePtr<SourceSet> s (GetSources(backend, mConstraints.mAudiom,
&MediaEngine::EnumerateAudioDevices));
final->MoveElementsFrom(*s);
}
for (i = 0; i < audioCount; i++) {
MediaEngineAudioSource *aSource = audioSources[i];
devices->AppendElement(new MediaDevice(aSource));
}
NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(
mSuccess, mError, devices // give ownership of the nsTArray to the runnable
));
NS_DispatchToMainThread(new DeviceSuccessCallbackRunnable(mSuccess, mError,
final.forget()));
return NS_OK;
}
private:
MediaStreamConstraintsInternal mConstraints;
already_AddRefed<nsIGetUserMediaDevicesSuccessCallback> mSuccess;
already_AddRefed<nsIDOMGetUserMediaErrorCallback> mError;
nsRefPtr<MediaManager> mManager;
@@ -973,14 +1097,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 +1111,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 +1167,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 +1205,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 +1238,16 @@ 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())
);
gUMRunnable = new GetUserMediaRunnable(c, 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
);
gUMRunnable = new GetUserMediaRunnable(c, onSuccess.forget(),
onError.forget(), windowID, listener, mPrefs);
}
#ifdef MOZ_B2G_CAMERA
@@ -1134,14 +1260,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 +1290,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 +1301,7 @@ MediaManager::GetUserMedia(bool aPrivileged, nsPIDOMWindow* aWindow,
nsresult
MediaManager::GetUserMediaDevices(nsPIDOMWindow* aWindow,
const MediaStreamConstraintsInternal& aConstraints,
nsIGetUserMediaDevicesSuccessCallback* aOnSuccess,
nsIDOMGetUserMediaErrorCallback* aOnError)
{
@@ -1199,7 +1314,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();
@@ -350,18 +356,8 @@ public:
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIMEDIADEVICE
MediaDevice(MediaEngineVideoSource* aSource) {
mSource = aSource;
mType.Assign(NS_LITERAL_STRING("video"));
mSource->GetName(mName);
mSource->GetUUID(mID);
}
MediaDevice(MediaEngineAudioSource* aSource) {
mSource = aSource;
mType.Assign(NS_LITERAL_STRING("audio"));
mSource->GetName(mName);
mSource->GetUUID(mID);
}
MediaDevice(MediaEngineVideoSource* aSource);
MediaDevice(MediaEngineAudioSource* aSource);
virtual ~MediaDevice() {}
MediaEngineSource* GetSource();
@@ -369,6 +365,8 @@ private:
nsString mName;
nsString mType;
nsString mID;
bool mHasFacingMode;
dom::VideoFacingModeEnum mFacingMode;
nsRefPtr<MediaEngineSource> mSource;
};
@@ -407,11 +405,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

@@ -6,12 +6,13 @@
#include "nsIVariant.idl"
#include "nsIDOMMediaStream.idl"
[scriptable, builtinclass, uuid(6de854f9-acf8-4383-b464-4803631ef309)]
[scriptable, builtinclass, uuid(4af2bdb7-1547-4d10-8886-02a78c3c0b83)]
interface nsIMediaDevice : nsISupports
{
readonly attribute DOMString type;
readonly attribute DOMString name;
readonly attribute DOMString id;
readonly attribute DOMString facingMode;
};
[scriptable, function, uuid(24544878-d35e-4962-8c5f-fb84e97bdfee)]
@@ -35,15 +36,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',