Bug 1491889 - Update chromium CDM interface to accommodate async init. r=cpearce

Starting at the Widevine CDM10 interface, the CDM is expected to make a callback
to an `OnInititalized` function to signal initialization has taken place. Prior
to this, it was sufficient to call the init function on the CDM, with no waiting
for a callback.

This changeset puts in place the IPDL to support async init, as well as the
handling for the ChromiumCDMParent and ChromiumCDMProxy. The code is not fully
updated to handle CDM10, so CDM9 is the only compatible CDM. Because CDM9 does
not perform the init callback, we immediately call our IPDL to signal init has
taken place. This also accommodates the clearkey case, which uses the CDM9
interface.

Further changesets will put in place more elaborate handling to accommodate the
possible failure of init, as well as implementing the handling `OnInitialized`
function explicitly.

Differential Revision: https://phabricator.services.mozilla.com/D6061

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Bryce Van Dyk 2018-09-25 02:42:26 +00:00
Родитель 03ab113d02
Коммит 2fe058b38b
7 изменённых файлов: 121 добавлений и 71 удалений

Просмотреть файл

@ -454,7 +454,8 @@ ChromiumCDMChild::RecvPurgeShmems()
mozilla::ipc::IPCResult
ChromiumCDMChild::RecvInit(const bool& aAllowDistinctiveIdentifier,
const bool& aAllowPersistentState)
const bool& aAllowPersistentState,
InitResolver&& aResolver)
{
MOZ_ASSERT(IsOnMessageLoopThread());
GMP_LOG("ChromiumCDMChild::RecvInit(distinctiveId=%s, persistentState=%s)",
@ -467,6 +468,7 @@ ChromiumCDMChild::RecvInit(const bool& aAllowDistinctiveIdentifier,
// We do not yet support hardware secure codecs
false);
}
aResolver(true /* unused */);
return IPC_OK();
}

Просмотреть файл

@ -89,7 +89,8 @@ protected:
ipc::IPCResult RecvPurgeShmems() override;
void PurgeShmems();
ipc::IPCResult RecvInit(const bool& aAllowDistinctiveIdentifier,
const bool& aAllowPersistentState) override;
const bool& aAllowPersistentState,
InitResolver&& aResolver) override;
ipc::IPCResult RecvSetServerCertificate(
const uint32_t& aPromiseId,
nsTArray<uint8_t>&& aServerCert) override;

Просмотреть файл

@ -41,47 +41,75 @@ ChromiumCDMParent::ChromiumCDMParent(GMPContentParent* aContentParent,
aPluginId);
}
bool
RefPtr<ChromiumCDMParent::InitPromise>
ChromiumCDMParent::Init(ChromiumCDMCallback* aCDMCallback,
bool aAllowDistinctiveIdentifier,
bool aAllowPersistentState,
nsIEventTarget* aMainThread,
nsCString& aOutFailureReason)
nsIEventTarget* aMainThread)
{
GMP_LOG("ChromiumCDMParent::Init(this=%p) shutdown=%d abormalShutdown=%d "
"actorDestroyed=%d",
GMP_LOG("ChromiumCDMParent::Init(this=%p) shutdown=%s abormalShutdown=%s "
"actorDestroyed=%s",
this,
mIsShutdown,
mAbnormalShutdown,
mActorDestroyed);
mIsShutdown ? "true" : "false",
mAbnormalShutdown ? "true" : "false",
mActorDestroyed ? "true" : "false");
if (!aCDMCallback || !aMainThread) {
aOutFailureReason = nsPrintfCString("ChromiumCDMParent::Init() failed "
"nullCallback=%d nullMainThread=%d",
!aCDMCallback,
!aMainThread);
GMP_LOG("ChromiumCDMParent::Init(this=%p) failure since aCDMCallback(%p) or"
" aMainThread(%p) is nullptr", this, aCDMCallback, aMainThread);
return false;
GMP_LOG("ChromiumCDMParent::Init(this=%p) failed "
"nullCallback=%s nullMainThread=%s",
this,
!aCDMCallback ? "true" : "false",
!aMainThread ? "true" : "false");
return ChromiumCDMParent::InitPromise::CreateAndReject(
MediaResult(NS_ERROR_FAILURE,
nsPrintfCString("ChromiumCDMParent::Init() failed "
"nullCallback=%s nullMainThread=%s",
!aCDMCallback ? "true" : "false",
!aMainThread ? "true" : "false")),
__func__);
}
mCDMCallback = aCDMCallback;
mMainThread = aMainThread;
if (SendInit(aAllowDistinctiveIdentifier,
aAllowPersistentState)) {
return true;
}
RefPtr<gmp::GeckoMediaPluginService> service =
gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
bool xpcomWillShutdown = service && service->XPCOMWillShutdownReceived();
aOutFailureReason = nsPrintfCString(
"ChromiumCDMParent::Init() failed "
"shutdown=%d cdmCrash=%d actorDestroyed=%d browserShutdown=%d",
mIsShutdown,
mAbnormalShutdown,
mActorDestroyed,
xpcomWillShutdown);
return false;
RefPtr<ChromiumCDMParent::InitPromise> promise =
mInitPromise.Ensure(__func__);
RefPtr<ChromiumCDMParent> self = this;
SendInit(aAllowDistinctiveIdentifier, aAllowPersistentState)
->Then(AbstractThread::GetCurrent(),
__func__,
[self](bool /* unused */) {
GMP_LOG(
"ChromiumCDMParent::Init() succeeded with callback from child");
self->mInitPromise.ResolveIfExists(true /* unused */, __func__);
},
[self](ResponseRejectReason aReason) {
RefPtr<gmp::GeckoMediaPluginService> service =
gmp::GeckoMediaPluginService::GetGeckoMediaPluginService();
bool xpcomWillShutdown =
service && service->XPCOMWillShutdownReceived();
GMP_LOG("ChromiumCDMParent::Init(this=%p) failed "
"shutdown=%s cdmCrash=%s actorDestroyed=%s "
"browserShutdown=%s promiseRejectReason=%d",
self.get(),
self->mIsShutdown ? "true" : "false",
self->mAbnormalShutdown ? "true" : "false",
self->mActorDestroyed ? "true" : "false",
xpcomWillShutdown ? "true" : "false",
static_cast<int>(aReason));
self->mInitPromise.RejectIfExists(
MediaResult(
NS_ERROR_FAILURE,
nsPrintfCString("ChromiumCDMParent::Init() failed "
"shutdown=%s cdmCrash=%s actorDestroyed=%s "
"browserShutdown=%s promiseRejectReason=%d",
self->mIsShutdown ? "true" : "false",
self->mAbnormalShutdown ? "true" : "false",
self->mActorDestroyed ? "true" : "false",
xpcomWillShutdown ? "true" : "false",
static_cast<int>(aReason))),
__func__);
});
return promise;
}
void
@ -1139,6 +1167,11 @@ ChromiumCDMParent::Shutdown()
// Note: MediaKeys rejects all outstanding promises when it initiates shutdown.
mPromiseToCreateSessionToken.Clear();
mInitPromise.RejectIfExists(
MediaResult(NS_ERROR_DOM_ABORT_ERR,
RESULT_DETAIL("ChromiumCDMParent is shutdown")),
__func__);
mInitVideoDecoderPromise.RejectIfExists(
MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
RESULT_DETAIL("ChromiumCDMParent is shutdown")),

Просмотреть файл

@ -34,17 +34,18 @@ class ChromiumCDMParent final
, public GMPCrashHelperHolder
{
public:
typedef MozPromise<bool, MediaResult, /* IsExclusive = */ true> InitPromise;
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ChromiumCDMParent)
ChromiumCDMParent(GMPContentParent* aContentParent, uint32_t aPluginId);
uint32_t PluginId() const { return mPluginId; }
bool Init(ChromiumCDMCallback* aCDMCallback,
bool aAllowDistinctiveIdentifier,
bool aAllowPersistentState,
nsIEventTarget* aMainThread,
nsCString& aOutFailureReason);
RefPtr<InitPromise> Init(ChromiumCDMCallback* aCDMCallback,
bool aAllowDistinctiveIdentifier,
bool aAllowPersistentState,
nsIEventTarget* aMainThread);
void CreateSession(uint32_t aCreateSessionToken,
uint32_t aSessionType,
@ -159,6 +160,8 @@ protected:
nsDataHashtable<nsUint32HashKey, uint32_t> mPromiseToCreateSessionToken;
nsTArray<RefPtr<DecryptJob>> mDecrypts;
MozPromiseHolder<InitPromise> mInitPromise;
MozPromiseHolder<MediaDataDecoder::InitPromise> mInitVideoDecoderPromise;
MozPromiseHolder<MediaDataDecoder::DecodePromise> mDecodePromise;

Просмотреть файл

@ -98,25 +98,33 @@ ChromiumCDMProxy::Init(PromiseId aPromiseId,
promise->Then(
thread,
__func__,
[self, aPromiseId](RefPtr<gmp::ChromiumCDMParent> cdm) {
[self, aPromiseId, thread](RefPtr<gmp::ChromiumCDMParent> cdm) {
// service->GetCDM succeeded
self->mCallback =
MakeUnique<ChromiumCDMCallbackProxy>(self, self->mMainThread);
nsCString failureReason;
if (!cdm->Init(self->mCallback.get(),
self->mDistinctiveIdentifierRequired,
self->mPersistentStateRequired,
self->mMainThread,
failureReason)) {
self->RejectPromise(aPromiseId, NS_ERROR_FAILURE, failureReason);
return;
}
{
MutexAutoLock lock(self->mCDMMutex);
self->mCDM = cdm;
}
self->OnCDMCreated(aPromiseId);
cdm
->Init(self->mCallback.get(),
self->mDistinctiveIdentifierRequired,
self->mPersistentStateRequired,
self->mMainThread)
->Then(thread,
__func__,
[self, aPromiseId, cdm](bool /* unused */) {
// CDM init succeeded
{
MutexAutoLock lock(self->mCDMMutex);
self->mCDM = cdm;
}
self->OnCDMCreated(aPromiseId);
},
[self, aPromiseId](MediaResult aResult) {
// CDM init failed
self->RejectPromise(
aPromiseId, aResult.Code(), aResult.Message());
});
},
[self, aPromiseId](MediaResult rv) {
// service->GetCDM failed
self->RejectPromise(
aPromiseId, rv.Code(), rv.Description());
});

Просмотреть файл

@ -16,7 +16,7 @@ child:
// cdm::ContentDecryptionModule9+10
async Init(bool aAllowDistinctiveIdentifier,
bool aAllowPersistentState);
bool aAllowPersistentState) returns (bool unused);
async GetStatusForPolicy(uint32_t aPromiseId,
nsCString aMinHdcpVersion);

Просмотреть файл

@ -448,24 +448,27 @@ class CDMStorageTest
RefPtr<gmp::GetCDMParentPromise> promise =
service->GetCDM(aNodeId, std::move(tags), nullptr);
auto thread = GetAbstractGMPThread();
promise->Then(thread,
__func__,
[self, aUpdates](RefPtr<gmp::ChromiumCDMParent> cdm) {
self->mCDM = cdm;
EXPECT_TRUE(!!self->mCDM);
self->mCallback.reset(new CallbackProxy(self));
nsCString failureReason;
self->mCDM->Init(self->mCallback.get(),
false,
true,
GetMainThreadEventTarget(),
failureReason);
promise->Then(
thread,
__func__,
[self, aUpdates, thread](RefPtr<gmp::ChromiumCDMParent> cdm) {
self->mCDM = cdm;
EXPECT_TRUE(!!self->mCDM);
self->mCallback.reset(new CallbackProxy(self));
nsCString failureReason;
self->mCDM
->Init(self->mCallback.get(), false, true, GetMainThreadEventTarget())
->Then(thread,
__func__,
[self, aUpdates] {
for (auto& update : aUpdates) {
self->Update(update);
}
},
[](MediaResult rv) { EXPECT_TRUE(false); });
for (auto& update : aUpdates) {
self->Update(update);
}
},
[](MediaResult rv) { EXPECT_TRUE(false); });
},
[](MediaResult rv) { EXPECT_TRUE(false); });
}
void TestBasicStorage() {