Bug 1268984 - Ensure GMPs are re-inserted in GMPServiceParent::mPlugins in the same order in ReAddOnGMPThread. r=gerald,jesup,a=ritu

The GMP which GeckoMediaPluginServiceParent::FindPluginForAPIFrom() returns
depends on the order in which GMPs lie in GMPServiceParent::mPlugins. However
when we shutdown a GMPParent we remove and then re-append the GMPParent to
mPlugins. This means the order in which GMPs lie in the list changes.

So when WebRTC requests an H.264 decoder, the first time it will get OpenH264,
since that's first in the list. But once we dispose of that decoder, its
GMPParent will be cloned and the clone will be appended to the end of the
list. This means the next time WebRTC requests a decoder, it'll get whatever
was next in the list.

This could be the Adobe GMP, which seems to be able to handle whatever WebRTC
is putting into it. However, if you do this enough times, you'll get the
Widevine CDM, which can't handle whatever WebRTC is putting into it.

So a quick hack to fix this is in ReAddOnGMPThread is to re-insert the clone
of the GMP into the slot in mPlugins that the original occupied. Then WebRTC
will always get OpenH264 whenever it requests for an H.264 decoder, as the
order of the GMPParents in mPlugins won't change.




MozReview-Commit-ID: Ii4AMqDqAo9
This commit is contained in:
Chris Pearce 2016-05-04 13:57:20 +12:00
parent 4eab9fb575
commit ed8e6a58be
2 changed files with 40 additions and 30 deletions

View File

@ -908,7 +908,7 @@ GeckoMediaPluginServiceParent::GetPluginVersionForAPI(const nsACString& aAPI,
double maxParsedVersion = -1.;
*aHasPlugin = false;
while (GMPParent* gmp = FindPluginForAPIFrom(index, api, *aTags, &index)) {
while (RefPtr<GMPParent> gmp = FindPluginForAPIFrom(index, api, *aTags, &index)) {
*aHasPlugin = true;
double parsedVersion = atof(gmp->GetVersion().get());
if (maxParsedVersion < 0 || parsedVersion > maxParsedVersion) {
@ -941,7 +941,7 @@ GeckoMediaPluginServiceParent::EnsurePluginsOnDiskScanned()
return NS_OK;
}
GMPParent*
already_AddRefed<GMPParent>
GeckoMediaPluginServiceParent::FindPluginForAPIFrom(size_t aSearchStartIndex,
const nsCString& aAPI,
const nsTArray<nsCString>& aTags,
@ -949,7 +949,7 @@ GeckoMediaPluginServiceParent::FindPluginForAPIFrom(size_t aSearchStartIndex,
{
mMutex.AssertCurrentThreadOwns();
for (size_t i = aSearchStartIndex; i < mPlugins.Length(); i++) {
GMPParent* gmp = mPlugins[i];
RefPtr<GMPParent> gmp = mPlugins[i];
bool supportsAllTags = true;
for (size_t t = 0; t < aTags.Length(); t++) {
const nsCString& tag = aTags.ElementAt(t);
@ -964,12 +964,12 @@ GeckoMediaPluginServiceParent::FindPluginForAPIFrom(size_t aSearchStartIndex,
if (aOutPluginIndex) {
*aOutPluginIndex = i;
}
return gmp;
return gmp.forget();
}
return nullptr;
}
GMPParent*
already_AddRefed<GMPParent>
GeckoMediaPluginServiceParent::SelectPluginForAPI(const nsACString& aNodeId,
const nsCString& aAPI,
const nsTArray<nsCString>& aTags)
@ -981,16 +981,16 @@ GeckoMediaPluginServiceParent::SelectPluginForAPI(const nsACString& aNodeId,
{
MutexAutoLock lock(mMutex);
size_t index = 0;
GMPParent* gmp = nullptr;
RefPtr<GMPParent> gmp;
while ((gmp = FindPluginForAPIFrom(index, aAPI, aTags, &index))) {
if (aNodeId.IsEmpty()) {
if (gmp->CanBeSharedCrossNodeIds()) {
return gmp;
return gmp.forget();
}
} else if (gmp->CanBeUsedFrom(aNodeId)) {
MOZ_ASSERT(!aNodeId.IsEmpty());
gmp->SetNodeId(aNodeId);
return gmp;
return gmp.forget();
}
if (!gmpToClone ||
@ -1012,11 +1012,15 @@ GeckoMediaPluginServiceParent::SelectPluginForAPI(const nsACString& aNodeId,
// Plugin exists, but we can't use it due to cross-origin separation. Create a
// new one.
if (gmpToClone) {
GMPParent* clone = ClonePlugin(gmpToClone);
RefPtr<GMPParent> clone = ClonePlugin(gmpToClone);
{
MutexAutoLock lock(mMutex);
mPlugins.AppendElement(clone);
}
if (!aNodeId.IsEmpty()) {
clone->SetNodeId(aNodeId);
}
return clone;
return clone.forget();
}
return nullptr;
@ -1037,7 +1041,7 @@ CreateGMPParent()
return new GMPParent();
}
GMPParent*
already_AddRefed<GMPParent>
GeckoMediaPluginServiceParent::ClonePlugin(const GMPParent* aOriginal)
{
MOZ_ASSERT(aOriginal);
@ -1050,10 +1054,7 @@ GeckoMediaPluginServiceParent::ClonePlugin(const GMPParent* aOriginal)
return nullptr;
}
MutexAutoLock lock(mMutex);
mPlugins.AppendElement(gmp);
return gmp.get();
return gmp.forget();
}
RefPtr<GenericPromise>
@ -1196,15 +1197,21 @@ GeckoMediaPluginServiceParent::ReAddOnGMPThread(const RefPtr<GMPParent>& aOld)
RefPtr<GMPParent> gmp;
if (!mShuttingDownOnGMPThread) {
// Don't re-add plugin if we're shutting down. Let the old plugin die.
// We're not shutting down, so replace the old plugin in the list with a
// clone which is in a pristine state. Note: We place the plugin in
// the same slot in the array as a hack to ensure if we re-request with
// the same capabilities we get an instance of the same plugin.
gmp = ClonePlugin(aOld);
MutexAutoLock lock(mMutex);
MOZ_ASSERT(mPlugins.Contains(aOld));
if (mPlugins.Contains(aOld)) {
mPlugins[mPlugins.IndexOf(aOld)] = gmp;
}
} else {
// We're shutting down; don't re-add plugin, let the old plugin die.
MutexAutoLock lock(mMutex);
mPlugins.RemoveElement(aOld);
}
// Note: both are now in the list
// Until we give up the GMPThread, we're safe even if we unlock temporarily
// since off-main-thread users just test for existance; they don't modify the list.
MutexAutoLock lock(mMutex);
mPlugins.RemoveElement(aOld);
// Schedule aOld to be destroyed. We can't destroy it from here since we
// may be inside ActorDestroyed() for it.
NS_DispatchToCurrentThread(WrapRunnableNM(&Dummy, aOld));

View File

@ -66,13 +66,14 @@ private:
void ClearStorage();
GMPParent* SelectPluginForAPI(const nsACString& aNodeId,
const nsCString& aAPI,
const nsTArray<nsCString>& aTags);
GMPParent* FindPluginForAPIFrom(size_t aSearchStartIndex,
const nsCString& aAPI,
const nsTArray<nsCString>& aTags,
size_t* aOutPluginIndex);
already_AddRefed<GMPParent> SelectPluginForAPI(const nsACString& aNodeId,
const nsCString& aAPI,
const nsTArray<nsCString>& aTags);
already_AddRefed<GMPParent> FindPluginForAPIFrom(size_t aSearchStartIndex,
const nsCString& aAPI,
const nsTArray<nsCString>& aTags,
size_t* aOutPluginIndex);
nsresult GetNodeId(const nsAString& aOrigin, const nsAString& aTopLevelOrigin,
const nsAString& aGMPName,
@ -114,7 +115,9 @@ protected:
UniquePtr<GetGMPContentParentCallback>&& aCallback)
override;
private:
GMPParent* ClonePlugin(const GMPParent* aOriginal);
// Creates a copy of aOriginal. Note that the caller is responsible for
// adding this to GeckoMediaPluginServiceParent::mPlugins.
already_AddRefed<GMPParent> ClonePlugin(const GMPParent* aOriginal);
nsresult EnsurePluginsOnDiskScanned();
nsresult InitStorage();