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:
Jan-Ivar Bruaroey 2015-10-03 20:42:26 -04:00
parent 8ba5de483c
commit 61cd22ad23
3 changed files with 172 additions and 97 deletions

View File

@ -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);
});

View File

@ -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;

View File

@ -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