mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-16 23:05:42 +00:00
Bug 1210852 - do SelectSettings of device capabilities on media thread. r=jesup
--HG-- extra : transplant_source : %8E%BB%7B%90MSt%0F%40s%8A%0C/%16y%15Ne%2A%1E
This commit is contained in:
parent
8ba5de483c
commit
61cd22ad23
@ -1133,46 +1133,72 @@ GetSources(MediaEngine *engine, dom::MediaSourceEnum aSrcType,
|
||||
}
|
||||
}
|
||||
|
||||
static const char*
|
||||
SelectSettings(MediaStreamConstraints &aConstraints,
|
||||
nsTArray<nsRefPtr<MediaDevice>>& aSources)
|
||||
// TODO: Remove once upgraded to GCC 4.8+ on linux. Bogus error on static func:
|
||||
// error: 'this' was not captured for this lambda function
|
||||
|
||||
static auto& MediaManager_GetInstance = MediaManager::GetInstance;
|
||||
static auto& MediaManager_ToJSArray = MediaManager::ToJSArray;
|
||||
static auto& MediaManager_AnonymizeDevices = MediaManager::AnonymizeDevices;
|
||||
|
||||
already_AddRefed<MediaManager::PledgeChar>
|
||||
MediaManager::SelectSettings(
|
||||
MediaStreamConstraints& aConstraints,
|
||||
nsRefPtr<Refcountable<ScopedDeletePtr<SourceSet>>>& aSources)
|
||||
{
|
||||
// Since the advanced part of the constraints algorithm needs to know when
|
||||
// a candidate set is overconstrained (zero members), we must split up the
|
||||
// list into videos and audios, and put it back together again at the end.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
nsRefPtr<PledgeChar> p = new PledgeChar();
|
||||
uint32_t id = mOutstandingCharPledges.Append(*p);
|
||||
|
||||
nsTArray<nsRefPtr<VideoDevice>> videos;
|
||||
nsTArray<nsRefPtr<AudioDevice>> audios;
|
||||
// Algorithm accesses device capabilities code and must run on media thread.
|
||||
// Modifies passed-in aSources.
|
||||
|
||||
for (auto& source : aSources) {
|
||||
if (source->mIsVideo) {
|
||||
nsRefPtr<VideoDevice> video = static_cast<VideoDevice*>(source.get());
|
||||
videos.AppendElement(video);
|
||||
} else {
|
||||
nsRefPtr<AudioDevice> audio = static_cast<AudioDevice*>(source.get());
|
||||
audios.AppendElement(audio);
|
||||
MediaManager::PostTask(FROM_HERE, NewTaskFrom([id, aConstraints,
|
||||
aSources]() mutable {
|
||||
auto& sources = **aSources;
|
||||
|
||||
// Since the advanced part of the constraints algorithm needs to know when
|
||||
// a candidate set is overconstrained (zero members), we must split up the
|
||||
// list into videos and audios, and put it back together again at the end.
|
||||
|
||||
nsTArray<nsRefPtr<VideoDevice>> videos;
|
||||
nsTArray<nsRefPtr<AudioDevice>> audios;
|
||||
|
||||
for (auto& source : sources) {
|
||||
if (source->mIsVideo) {
|
||||
nsRefPtr<VideoDevice> video = static_cast<VideoDevice*>(source.get());
|
||||
videos.AppendElement(video);
|
||||
} else {
|
||||
nsRefPtr<AudioDevice> audio = static_cast<AudioDevice*>(source.get());
|
||||
audios.AppendElement(audio);
|
||||
}
|
||||
}
|
||||
}
|
||||
aSources.Clear();
|
||||
MOZ_ASSERT(!aSources.Length());
|
||||
sources.Clear();
|
||||
const char* badConstraint = nullptr;
|
||||
|
||||
const char* badConstraint = nullptr;
|
||||
|
||||
if (IsOn(aConstraints.mVideo)) {
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||
GetInvariant(aConstraints.mVideo), videos);
|
||||
for (auto& video : videos) {
|
||||
aSources.AppendElement(video);
|
||||
if (IsOn(aConstraints.mVideo)) {
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||
GetInvariant(aConstraints.mVideo), videos);
|
||||
for (auto& video : videos) {
|
||||
sources.AppendElement(video);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (audios.Length() && IsOn(aConstraints.mAudio)) {
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||
GetInvariant(aConstraints.mAudio), audios);
|
||||
for (auto& audio : audios) {
|
||||
aSources.AppendElement(audio);
|
||||
if (audios.Length() && IsOn(aConstraints.mAudio)) {
|
||||
badConstraint = MediaConstraintsHelper::SelectSettings(
|
||||
GetInvariant(aConstraints.mAudio), audios);
|
||||
for (auto& audio : audios) {
|
||||
sources.AppendElement(audio);
|
||||
}
|
||||
}
|
||||
}
|
||||
return badConstraint;
|
||||
NS_DispatchToMainThread(do_AddRef(NewRunnableFrom([id, badConstraint]() mutable {
|
||||
nsRefPtr<MediaManager> mgr = MediaManager_GetInstance();
|
||||
nsRefPtr<PledgeChar> p = mgr->mOutstandingCharPledges.Remove(id);
|
||||
if (p) {
|
||||
p->Resolve(badConstraint);
|
||||
}
|
||||
return NS_OK;
|
||||
})));
|
||||
}));
|
||||
return p.forget();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1373,13 +1399,6 @@ private:
|
||||
};
|
||||
#endif
|
||||
|
||||
// TODO: Remove once upgraded to GCC 4.8+ on linux. Bogus error on static func:
|
||||
// error: 'this' was not captured for this lambda function
|
||||
|
||||
static auto& MediaManager_GetInstance = MediaManager::GetInstance;
|
||||
static auto& MediaManager_ToJSArray = MediaManager::ToJSArray;
|
||||
static auto& MediaManager_AnonymizeDevices = MediaManager::AnonymizeDevices;
|
||||
|
||||
/**
|
||||
* EnumerateRawDevices - Enumerate a list of audio & video devices that
|
||||
* satisfy passed-in constraints. List contains raw id's.
|
||||
@ -2026,80 +2045,95 @@ MediaManager::GetUserMedia(nsPIDOMWindow* aWindow,
|
||||
fakeTracks);
|
||||
p->Then([this, onSuccess, onFailure, windowID, c, listener, askPermission,
|
||||
prefs, isHTTPS, callID, origin](SourceSet*& aDevices) mutable {
|
||||
ScopedDeletePtr<SourceSet> devices(aDevices); // grab result
|
||||
|
||||
// Ensure this pointer is still valid, and window is still alive.
|
||||
nsRefPtr<MediaManager> mgr = MediaManager::GetInstance();
|
||||
nsRefPtr<nsPIDOMWindow> window = static_cast<nsPIDOMWindow*>
|
||||
(nsGlobalWindow::GetInnerWindowWithId(windowID));
|
||||
if (!mgr || !window) {
|
||||
nsRefPtr<Refcountable<ScopedDeletePtr<SourceSet>>> devices(
|
||||
new Refcountable<ScopedDeletePtr<SourceSet>>(aDevices)); // grab result
|
||||
|
||||
// Ensure that the captured 'this' pointer and our windowID are still good.
|
||||
if (!MediaManager::Exists() ||
|
||||
!nsGlobalWindow::GetInnerWindowWithId(windowID)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Apply any constraints. This modifies the list.
|
||||
const char* badConstraint = SelectSettings(c, *devices);
|
||||
if (badConstraint) {
|
||||
nsString constraint;
|
||||
constraint.AssignASCII(badConstraint);
|
||||
nsRefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(window,
|
||||
NS_LITERAL_STRING("OverconstrainedError"),
|
||||
NS_LITERAL_STRING(""),
|
||||
constraint);
|
||||
onFailure->OnError(error);
|
||||
return;
|
||||
}
|
||||
if (!devices->Length()) {
|
||||
nsRefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(window, NS_LITERAL_STRING("NotFoundError"));
|
||||
onFailure->OnError(error);
|
||||
return;
|
||||
}
|
||||
// Apply any constraints. This modifies the passed-in list.
|
||||
nsRefPtr<PledgeChar> p2 = SelectSettings(c, devices);
|
||||
|
||||
nsCOMPtr<nsISupportsArray> devicesCopy; // before we give up devices below
|
||||
if (!askPermission) {
|
||||
nsresult rv = NS_NewISupportsArray(getter_AddRefs(devicesCopy));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
p2->Then([this, onSuccess, onFailure, windowID, c,
|
||||
listener, askPermission, prefs, isHTTPS,
|
||||
callID, origin, devices](const char*& badConstraint) mutable {
|
||||
|
||||
// Ensure that the captured 'this' pointer and our windowID are still good.
|
||||
nsRefPtr<nsPIDOMWindow> window = static_cast<nsPIDOMWindow*>
|
||||
(nsGlobalWindow::GetInnerWindowWithId(windowID));
|
||||
if (!MediaManager::Exists() || !window) {
|
||||
return;
|
||||
}
|
||||
for (auto& device : *devices) {
|
||||
rv = devicesCopy->AppendElement(device);
|
||||
|
||||
if (badConstraint) {
|
||||
nsString constraint;
|
||||
constraint.AssignASCII(badConstraint);
|
||||
nsRefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(window,
|
||||
NS_LITERAL_STRING("OverconstrainedError"),
|
||||
NS_LITERAL_STRING(""),
|
||||
constraint);
|
||||
onFailure->OnError(error);
|
||||
return;
|
||||
}
|
||||
if (!(*devices)->Length()) {
|
||||
nsRefPtr<MediaStreamError> error =
|
||||
new MediaStreamError(window, NS_LITERAL_STRING("NotFoundError"));
|
||||
onFailure->OnError(error);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupportsArray> devicesCopy; // before we give up devices below
|
||||
if (!askPermission) {
|
||||
nsresult rv = NS_NewISupportsArray(getter_AddRefs(devicesCopy));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
for (auto& device : **devices) {
|
||||
rv = devicesCopy->AppendElement(device);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pass callbacks and MediaStreamListener along to GetUserMediaTask.
|
||||
nsAutoPtr<GetUserMediaTask> task (new GetUserMediaTask(c, onSuccess.forget(),
|
||||
onFailure.forget(),
|
||||
windowID, listener,
|
||||
prefs, origin,
|
||||
devices.forget()));
|
||||
// Store the task w/callbacks.
|
||||
mActiveCallbacks.Put(callID, task.forget());
|
||||
// Pass callbacks and MediaStreamListener along to GetUserMediaTask.
|
||||
nsAutoPtr<GetUserMediaTask> task (new GetUserMediaTask(c, onSuccess.forget(),
|
||||
onFailure.forget(),
|
||||
windowID, listener,
|
||||
prefs, origin,
|
||||
devices->forget()));
|
||||
// Store the task w/callbacks.
|
||||
mActiveCallbacks.Put(callID, task.forget());
|
||||
|
||||
// Add a WindowID cross-reference so OnNavigation can tear things down
|
||||
nsTArray<nsString>* array;
|
||||
if (!mCallIds.Get(windowID, &array)) {
|
||||
array = new nsTArray<nsString>();
|
||||
mCallIds.Put(windowID, array);
|
||||
}
|
||||
array->AppendElement(callID);
|
||||
// Add a WindowID cross-reference so OnNavigation can tear things down
|
||||
nsTArray<nsString>* array;
|
||||
if (!mCallIds.Get(windowID, &array)) {
|
||||
array = new nsTArray<nsString>();
|
||||
mCallIds.Put(windowID, array);
|
||||
}
|
||||
array->AppendElement(callID);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (!askPermission) {
|
||||
obs->NotifyObservers(devicesCopy, "getUserMedia:privileged:allow",
|
||||
callID.BeginReading());
|
||||
} else {
|
||||
nsRefPtr<GetUserMediaRequest> req =
|
||||
new GetUserMediaRequest(window, callID, c, isHTTPS);
|
||||
obs->NotifyObservers(req, "getUserMedia:request", nullptr);
|
||||
}
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
if (!askPermission) {
|
||||
obs->NotifyObservers(devicesCopy, "getUserMedia:privileged:allow",
|
||||
callID.BeginReading());
|
||||
} else {
|
||||
nsRefPtr<GetUserMediaRequest> req =
|
||||
new GetUserMediaRequest(window, callID, c, isHTTPS);
|
||||
obs->NotifyObservers(req, "getUserMedia:request", nullptr);
|
||||
}
|
||||
|
||||
#ifdef MOZ_WEBRTC
|
||||
EnableWebRtcLog();
|
||||
EnableWebRtcLog();
|
||||
#endif
|
||||
}, [onFailure](MediaStreamError*& reason) mutable {
|
||||
onFailure->OnError(reason);
|
||||
});
|
||||
}, [onFailure](MediaStreamError*& reason) mutable {
|
||||
onFailure->OnError(reason);
|
||||
});
|
||||
|
@ -474,6 +474,7 @@ public:
|
||||
static bool IsPrivateBrowsing(nsPIDOMWindow *window);
|
||||
private:
|
||||
typedef media::Pledge<SourceSet*, dom::MediaStreamError*> PledgeSourceSet;
|
||||
typedef media::Pledge<const char*, dom::MediaStreamError*> PledgeChar;
|
||||
|
||||
static bool IsPrivileged();
|
||||
static bool IsLoop(nsIURI* aDocURI);
|
||||
@ -493,6 +494,10 @@ private:
|
||||
dom::MediaSourceEnum aVideoSrcType,
|
||||
dom::MediaSourceEnum aAudioSrcType,
|
||||
bool aFake = false, bool aFakeTracks = false);
|
||||
already_AddRefed<PledgeChar>
|
||||
SelectSettings(
|
||||
dom::MediaStreamConstraints& aConstraints,
|
||||
nsRefPtr<media::Refcountable<ScopedDeletePtr<SourceSet>>>& aSources);
|
||||
|
||||
StreamListeners* AddWindowID(uint64_t aWindowId);
|
||||
WindowTable *GetActiveWindows() {
|
||||
@ -533,6 +538,7 @@ private:
|
||||
static StaticRefPtr<MediaManager> sSingleton;
|
||||
|
||||
media::CoatCheck<PledgeSourceSet> mOutstandingPledges;
|
||||
media::CoatCheck<PledgeChar> mOutstandingCharPledges;
|
||||
media::CoatCheck<GetUserMediaCallbackMediaStreamListener::PledgeVoid> mOutstandingVoidPledges;
|
||||
#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)
|
||||
nsRefPtr<nsDOMCameraManager> mCameraManager;
|
||||
|
@ -322,6 +322,41 @@ private:
|
||||
nsAutoTArray<Element, 3> mElements;
|
||||
};
|
||||
|
||||
/* media::Refcountable - Add threadsafe ref-counting to something that isn't.
|
||||
*
|
||||
* Often, reference counting is the most practical way to share an object with
|
||||
* another thread without imposing lifetime restrictions, even if there's
|
||||
* otherwise no concurrent access happening on the object. For instance, an
|
||||
* algorithm on another thread may find it more expedient to modify a passed-in
|
||||
* object, rather than pass expensive copies back and forth.
|
||||
*
|
||||
* Lists in particular often aren't ref-countable, yet are expensive to copy,
|
||||
* e.g. nsTArray<nsRefPtr<Foo>>. Refcountable can be used to make such objects
|
||||
* (or owning smart-pointers to such objects) refcountable.
|
||||
*
|
||||
* Technical limitation: A template specialization is needed for types that take
|
||||
* a constructor. Please add below (ScopedDeletePtr covers a lot of ground though).
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
class Refcountable : public T
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
|
||||
private:
|
||||
~Refcountable<T>() {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Refcountable<ScopedDeletePtr<T>> : public ScopedDeletePtr<T>
|
||||
{
|
||||
public:
|
||||
explicit Refcountable<ScopedDeletePtr<T>>(T* aPtr) : ScopedDeletePtr<T>(aPtr) {}
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Refcountable<T>)
|
||||
private:
|
||||
~Refcountable<ScopedDeletePtr<T>>() {}
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
} // namespace mozilla
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user