mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-30 00:01:50 +00:00
Bug 1109861 - Add delegate to manage waiting for the CDM to mark key usable. r=kinetik
This commit is contained in:
parent
d65355fd96
commit
515d668bfb
@ -8,6 +8,7 @@
|
||||
#include "gmp-decryption.h"
|
||||
#include "EMELog.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "SamplesWaitingForKey.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -103,12 +104,7 @@ CDMCaps::AutoLock::SetKeyUsable(const CencKeyId& aKeyId,
|
||||
while (i < waiters.Length()) {
|
||||
auto& w = waiters[i];
|
||||
if (w.mKeyId == aKeyId) {
|
||||
if (waiters[i].mTarget) {
|
||||
EME_LOG("SetKeyUsable() notified waiter.");
|
||||
w.mTarget->Dispatch(w.mContinuation, NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
w.mContinuation->Run();
|
||||
}
|
||||
w.mListener->NotifyUsable(aKeyId);
|
||||
waiters.RemoveElementAt(i);
|
||||
} else {
|
||||
i++;
|
||||
@ -138,14 +134,13 @@ CDMCaps::AutoLock::SetKeyUnusable(const CencKeyId& aKeyId,
|
||||
}
|
||||
|
||||
void
|
||||
CDMCaps::AutoLock::CallWhenKeyUsable(const CencKeyId& aKey,
|
||||
nsIRunnable* aContinuation,
|
||||
nsIThread* aTarget)
|
||||
CDMCaps::AutoLock::NotifyWhenKeyIdUsable(const CencKeyId& aKey,
|
||||
SamplesWaitingForKey* aListener)
|
||||
{
|
||||
mData.mMonitor.AssertCurrentThreadOwns();
|
||||
MOZ_ASSERT(!IsKeyUsable(aKey));
|
||||
MOZ_ASSERT(aContinuation);
|
||||
mData.mWaitForKeys.AppendElement(WaitForKeys(aKey, aContinuation, aTarget));
|
||||
MOZ_ASSERT(aListener);
|
||||
mData.mWaitForKeys.AppendElement(WaitForKeys(aKey, aListener));
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -13,11 +13,10 @@
|
||||
#include "nsIThread.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "SamplesWaitingForKey.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
typedef nsTArray<uint8_t> CencKeyId;
|
||||
|
||||
// CDM capabilities; what keys a CDMProxy can use, and whether it can decrypt, or
|
||||
// decrypt-and-decode on a per stream basis. Must be locked to access state.
|
||||
class CDMCaps {
|
||||
@ -61,13 +60,9 @@ public:
|
||||
|
||||
void CallOnMainThreadWhenCapsAvailable(nsIRunnable* aContinuation);
|
||||
|
||||
// Calls aContinuation on aTarget thread when key become usable.
|
||||
// Pass aTarget=nullptr and runnable will be called on the GMP thread
|
||||
// when key becomes usable.
|
||||
void CallWhenKeyUsable(const CencKeyId& aKey,
|
||||
nsIRunnable* aContinuation,
|
||||
nsIThread* aTarget = nullptr);
|
||||
|
||||
// Notifies the SamplesWaitingForKey when key become usable.
|
||||
void NotifyWhenKeyIdUsable(const CencKeyId& aKey,
|
||||
SamplesWaitingForKey* aSamplesWaiting);
|
||||
private:
|
||||
// Not taking a strong ref, since this should be allocated on the stack.
|
||||
CDMCaps& mData;
|
||||
@ -80,15 +75,12 @@ private:
|
||||
|
||||
struct WaitForKeys {
|
||||
WaitForKeys(const CencKeyId& aKeyId,
|
||||
nsIRunnable* aContinuation,
|
||||
nsIThread* aTarget)
|
||||
SamplesWaitingForKey* aListener)
|
||||
: mKeyId(aKeyId)
|
||||
, mContinuation(aContinuation)
|
||||
, mTarget(aTarget)
|
||||
, mListener(aListener)
|
||||
{}
|
||||
CencKeyId mKeyId;
|
||||
nsRefPtr<nsIRunnable> mContinuation;
|
||||
nsCOMPtr<nsIThread> mTarget;
|
||||
nsRefPtr<SamplesWaitingForKey> mListener;
|
||||
};
|
||||
|
||||
Monitor mMonitor;
|
||||
|
@ -336,7 +336,7 @@ CDMProxy::gmp_Shutdown()
|
||||
// Abort any pending decrypt jobs, to awaken any clients waiting on a job.
|
||||
for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
|
||||
DecryptJob* job = mDecryptionJobs[i];
|
||||
job->mClient->Decrypted(NS_ERROR_ABORT, nullptr);
|
||||
job->mClient->Decrypted(GMPAbortedErr, nullptr);
|
||||
}
|
||||
mDecryptionJobs.Clear();
|
||||
|
||||
@ -522,7 +522,7 @@ CDMProxy::gmp_Decrypt(nsAutoPtr<DecryptJob> aJob)
|
||||
MOZ_ASSERT(aJob->mSample);
|
||||
|
||||
if (!mCDM) {
|
||||
aJob->mClient->Decrypted(NS_ERROR_FAILURE, nullptr);
|
||||
aJob->mClient->Decrypted(GMPAbortedErr, nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -549,23 +549,30 @@ CDMProxy::gmp_Decrypted(uint32_t aId,
|
||||
PodCopy(job->mSample->data,
|
||||
aDecryptedData.Elements(),
|
||||
std::min<size_t>(aDecryptedData.Length(), job->mSample->size));
|
||||
job->mClient->Decrypted(NS_OK, job->mSample.forget());
|
||||
job->mClient->Decrypted(GMPNoErr, job->mSample.forget());
|
||||
} else if (aResult == GMPNoKeyErr) {
|
||||
NS_WARNING("CDM returned GMPNoKeyErr");
|
||||
// We still have the encrypted sample, so we can re-enqueue it to be
|
||||
// decrypted again once the key is usable again.
|
||||
job->mClient->Decrypted(GMPNoKeyErr, job->mSample.forget());
|
||||
} else {
|
||||
job->mClient->Decrypted(NS_ERROR_FAILURE, nullptr);
|
||||
nsAutoCString str("CDM returned decode failure GMPErr=");
|
||||
str.AppendInt(aResult);
|
||||
NS_WARNING(str.get());
|
||||
job->mClient->Decrypted(aResult, nullptr);
|
||||
}
|
||||
mDecryptionJobs.RemoveElementAt(i);
|
||||
return;
|
||||
} else {
|
||||
NS_WARNING("GMPDecryptorChild returned incorrect job ID");
|
||||
}
|
||||
}
|
||||
NS_WARNING("GMPDecryptorChild returned incorrect job ID");
|
||||
}
|
||||
|
||||
void
|
||||
CDMProxy::gmp_Terminated()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
EME_LOG("CDM terminated");
|
||||
NS_WARNING("CDM terminated");
|
||||
gmp_Shutdown();
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ class MediaKeySession;
|
||||
class DecryptionClient {
|
||||
public:
|
||||
virtual ~DecryptionClient() {}
|
||||
virtual void Decrypted(nsresult aResult,
|
||||
virtual void Decrypted(GMPErr aResult,
|
||||
mp4_demuxer::MP4Sample* aSample) = 0;
|
||||
};
|
||||
|
||||
|
@ -34,8 +34,12 @@ EMEAudioDecoder::EMEAudioDecoder(CDMProxy* aProxy,
|
||||
, mConfig(aConfig)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mCallback(aCallback)
|
||||
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, mTaskQueue, mProxy))
|
||||
, mMonitor("EMEAudioDecoder")
|
||||
, mFlushComplete(false)
|
||||
#ifdef DEBUG
|
||||
, mIsShutdown(false)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@ -47,6 +51,7 @@ nsresult
|
||||
EMEAudioDecoder::Init()
|
||||
{
|
||||
// Note: this runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
MOZ_ASSERT((mConfig.bits_per_sample / 8) == 2); // Demuxer guarantees this.
|
||||
|
||||
@ -68,6 +73,11 @@ nsresult
|
||||
EMEAudioDecoder::Input(MP4Sample* aSample)
|
||||
{
|
||||
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<nsIRunnable> task(new DeliverSample(this, aSample));
|
||||
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
@ -80,6 +90,7 @@ nsresult
|
||||
EMEAudioDecoder::Flush()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
@ -105,6 +116,7 @@ nsresult
|
||||
EMEAudioDecoder::Drain()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethod(this, &EMEAudioDecoder::GmpDrain);
|
||||
@ -117,11 +129,19 @@ nsresult
|
||||
EMEAudioDecoder::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
#ifdef DEBUG
|
||||
mIsShutdown = true;
|
||||
#endif
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethod(this, &EMEAudioDecoder::GmpShutdown);
|
||||
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mSamplesWaitingForKey->BreakCycles();
|
||||
mSamplesWaitingForKey = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -221,9 +241,15 @@ void
|
||||
EMEAudioDecoder::Error(GMPErr aErr)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
EME_LOG("EMEAudioDecoder::Error");
|
||||
mCallback->Error();
|
||||
GmpShutdown();
|
||||
EME_LOG("EMEAudioDecoder::Error %d", aErr);
|
||||
if (aErr == GMPNoKeyErr) {
|
||||
// The GMP failed to decrypt a frame due to not having a key. This can
|
||||
// happen if a key expires or a session is closed during playback.
|
||||
NS_WARNING("GMP failed to decrypt due to lack of key");
|
||||
} else {
|
||||
mCallback->Error();
|
||||
GmpShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -275,18 +301,6 @@ EMEAudioDecoder::GmpInput(MP4Sample* aSample)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (sample->crypto.valid) {
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
MOZ_ASSERT(caps.CanDecryptAndDecodeAudio());
|
||||
const auto& keyid = sample->crypto.key;
|
||||
if (!caps.IsKeyUsable(keyid)) {
|
||||
// DeliverSample assumes responsibility for deleting aSample.
|
||||
nsRefPtr<nsIRunnable> task(new DeliverSample(this, sample.forget()));
|
||||
caps.CallWhenKeyUsable(keyid, task, mGMPThread);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
gmp::GMPAudioSamplesImpl samples(sample, mAudioChannels, mAudioRate);
|
||||
mGMP->Decode(samples);
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "GMPAudioHost.h"
|
||||
#include "GMPAudioDecoderProxy.h"
|
||||
#include "SamplesWaitingForKey.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -108,8 +109,14 @@ private:
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
|
||||
nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
|
||||
|
||||
Monitor mMonitor;
|
||||
bool mFlushComplete;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mIsShutdown;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include "EMEH264Decoder.h"
|
||||
#include "EMEAudioDecoder.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "SamplesWaitingForKey.h"
|
||||
#include <string>
|
||||
|
||||
namespace mozilla {
|
||||
@ -37,52 +38,36 @@ public:
|
||||
, mCallback(aCallback)
|
||||
, mTaskQueue(CreateMediaDecodeTaskQueue())
|
||||
, mProxy(aProxy)
|
||||
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, mTaskQueue, mProxy))
|
||||
#ifdef DEBUG
|
||||
, mIsShutdown(false)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
virtual nsresult Init() MOZ_OVERRIDE {
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
nsresult rv = mTaskQueue->SyncDispatch(
|
||||
NS_NewRunnableMethod(mDecoder, &MediaDataDecoder::Init));
|
||||
unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
return rv;
|
||||
}
|
||||
|
||||
class RedeliverEncryptedInput : public nsRunnable {
|
||||
public:
|
||||
RedeliverEncryptedInput(EMEDecryptor* aDecryptor,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MP4Sample* aSample)
|
||||
: mDecryptor(aDecryptor)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mSample(aSample)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
RefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
|
||||
&EMEDecryptor::Input,
|
||||
mSample.forget());
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
mTaskQueue = nullptr;
|
||||
mDecryptor = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<EMEDecryptor> mDecryptor;
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
nsAutoPtr<MP4Sample> mSample;
|
||||
};
|
||||
|
||||
class DeliverDecrypted : public DecryptionClient {
|
||||
public:
|
||||
DeliverDecrypted(EMEDecryptor* aDecryptor, MediaTaskQueue* aTaskQueue)
|
||||
: mDecryptor(aDecryptor)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
{}
|
||||
virtual void Decrypted(nsresult aResult,
|
||||
virtual void Decrypted(GMPErr aResult,
|
||||
mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE {
|
||||
if (NS_FAILED(aResult)) {
|
||||
if (aResult == GMPNoKeyErr) {
|
||||
RefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecryptor,
|
||||
&EMEDecryptor::Input,
|
||||
aSample);
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
} else if (GMP_FAILED(aResult)) {
|
||||
mDecryptor->mCallback->Error();
|
||||
MOZ_ASSERT(!aSample);
|
||||
} else {
|
||||
@ -91,9 +76,9 @@ public:
|
||||
&EMEDecryptor::Decrypted,
|
||||
aSample);
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
mTaskQueue = nullptr;
|
||||
mDecryptor = nullptr;
|
||||
}
|
||||
mTaskQueue = nullptr;
|
||||
mDecryptor = nullptr;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<EMEDecryptor> mDecryptor;
|
||||
@ -101,6 +86,7 @@ public:
|
||||
};
|
||||
|
||||
virtual nsresult Input(MP4Sample* aSample) MOZ_OVERRIDE {
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
// We run the PDM on its own task queue. We can't run it on the decode
|
||||
// task queue, because that calls into Input() in a loop and waits until
|
||||
// output is delivered. We need to defer some Input() calls while we wait
|
||||
@ -108,22 +94,16 @@ public:
|
||||
// to run the PDM on the same task queue, but since the decode task queue
|
||||
// is waiting in MP4Reader::Decode() for output our task would never run.
|
||||
// So we dispatch tasks to make all calls into the wrapped decoder.
|
||||
{
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
if (!caps.IsKeyUsable(aSample->crypto.key)) {
|
||||
EME_LOG("Encountered a non-usable key, waiting");
|
||||
nsRefPtr<nsIRunnable> task(new RedeliverEncryptedInput(this,
|
||||
mTaskQueue,
|
||||
aSample));
|
||||
caps.CallWhenKeyUsable(aSample->crypto.key, task);
|
||||
return NS_OK;
|
||||
}
|
||||
if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mProxy->Decrypt(aSample, new DeliverDecrypted(this, mTaskQueue));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void Decrypted(mp4_demuxer::MP4Sample* aSample) {
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
nsresult rv = mTaskQueue->Dispatch(
|
||||
NS_NewRunnableMethodWithArg<mp4_demuxer::MP4Sample*>(
|
||||
mDecoder,
|
||||
@ -133,15 +113,18 @@ public:
|
||||
}
|
||||
|
||||
virtual nsresult Flush() MOZ_OVERRIDE {
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
nsresult rv = mTaskQueue->SyncDispatch(
|
||||
NS_NewRunnableMethod(
|
||||
mDecoder,
|
||||
&MediaDataDecoder::Flush));
|
||||
unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
mSamplesWaitingForKey->Flush();
|
||||
return rv;
|
||||
}
|
||||
|
||||
virtual nsresult Drain() MOZ_OVERRIDE {
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
nsresult rv = mTaskQueue->Dispatch(
|
||||
NS_NewRunnableMethod(
|
||||
mDecoder,
|
||||
@ -151,11 +134,17 @@ public:
|
||||
}
|
||||
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE {
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
#ifdef DEBUG
|
||||
mIsShutdown = true;
|
||||
#endif
|
||||
nsresult rv = mTaskQueue->SyncDispatch(
|
||||
NS_NewRunnableMethod(
|
||||
mDecoder,
|
||||
&MediaDataDecoder::Shutdown));
|
||||
unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
mSamplesWaitingForKey->BreakCycles();
|
||||
mSamplesWaitingForKey = nullptr;
|
||||
mDecoder = nullptr;
|
||||
mTaskQueue->BeginShutdown();
|
||||
mTaskQueue->AwaitShutdownAndIdle();
|
||||
@ -170,6 +159,10 @@ private:
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
|
||||
#ifdef DEBUG
|
||||
bool mIsShutdown;
|
||||
#endif
|
||||
};
|
||||
|
||||
EMEDecoderModule::EMEDecoderModule(CDMProxy* aProxy,
|
||||
|
@ -34,8 +34,12 @@ EMEH264Decoder::EMEH264Decoder(CDMProxy* aProxy,
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mCallback(aCallback)
|
||||
, mLastStreamOffset(0)
|
||||
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, mTaskQueue, mProxy))
|
||||
, mMonitor("EMEH264Decoder")
|
||||
, mFlushComplete(false)
|
||||
#ifdef DEBUG
|
||||
, mIsShutdown(false)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
@ -46,6 +50,7 @@ nsresult
|
||||
EMEH264Decoder::Init()
|
||||
{
|
||||
// Note: this runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
||||
MOZ_ASSERT(mMPS);
|
||||
@ -65,6 +70,11 @@ nsresult
|
||||
EMEH264Decoder::Input(MP4Sample* aSample)
|
||||
{
|
||||
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsRefPtr<nsIRunnable> task(new DeliverSample(this, aSample));
|
||||
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
@ -77,6 +87,7 @@ nsresult
|
||||
EMEH264Decoder::Flush()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
@ -102,6 +113,7 @@ nsresult
|
||||
EMEH264Decoder::Drain()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpDrain);
|
||||
@ -114,11 +126,19 @@ nsresult
|
||||
EMEH264Decoder::Shutdown()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
#ifdef DEBUG
|
||||
mIsShutdown = true;
|
||||
#endif
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpShutdown);
|
||||
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mSamplesWaitingForKey->BreakCycles();
|
||||
mSamplesWaitingForKey = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -211,9 +231,15 @@ void
|
||||
EMEH264Decoder::Error(GMPErr aErr)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
EME_LOG("EMEH264Decoder::Error");
|
||||
mCallback->Error();
|
||||
GmpShutdown();
|
||||
EME_LOG("EMEH264Decoder::Error %d", aErr);
|
||||
if (aErr == GMPNoKeyErr) {
|
||||
// The GMP failed to decrypt a frame due to not having a key. This can
|
||||
// happen if a key expires or a session is closed during playback.
|
||||
NS_WARNING("GMP failed to decrypt due to lack of key");
|
||||
} else {
|
||||
mCallback->Error();
|
||||
GmpShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
@ -277,18 +303,6 @@ EMEH264Decoder::GmpInput(MP4Sample* aSample)
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
if (sample->crypto.valid) {
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
MOZ_ASSERT(caps.CanDecryptAndDecodeVideo());
|
||||
const auto& keyid = sample->crypto.key;
|
||||
if (!caps.IsKeyUsable(keyid)) {
|
||||
nsRefPtr<nsIRunnable> task(new DeliverSample(this, sample.forget()));
|
||||
caps.CallWhenKeyUsable(keyid, task, mGMPThread);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mLastStreamOffset = sample->byte_offset;
|
||||
|
||||
GMPVideoFrame* ftmp = nullptr;
|
||||
|
@ -12,6 +12,7 @@
|
||||
#include "ImageContainer.h"
|
||||
#include "GMPVideoDecoderProxy.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "SamplesWaitingForKey.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -104,8 +105,15 @@ private:
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
int64_t mLastStreamOffset;
|
||||
|
||||
nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
|
||||
|
||||
Monitor mMonitor;
|
||||
bool mFlushComplete;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mIsShutdown;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
82
dom/media/fmp4/eme/SamplesWaitingForKey.cpp
Normal file
82
dom/media/fmp4/eme/SamplesWaitingForKey.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "SamplesWaitingForKey.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "mozilla/CDMCaps.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
SamplesWaitingForKey::SamplesWaitingForKey(MediaDataDecoder* aDecoder,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
CDMProxy* aProxy)
|
||||
: mMutex("SamplesWaitingForKey")
|
||||
, mDecoder(aDecoder)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mProxy(aProxy)
|
||||
{
|
||||
}
|
||||
|
||||
SamplesWaitingForKey::~SamplesWaitingForKey()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
SamplesWaitingForKey::WaitIfKeyNotUsable(MP4Sample* aSample)
|
||||
{
|
||||
if (!aSample || !aSample->crypto.valid || !mProxy) {
|
||||
return false;
|
||||
}
|
||||
CDMCaps::AutoLock caps(mProxy->Capabilites());
|
||||
const auto& keyid = aSample->crypto.key;
|
||||
if (!caps.IsKeyUsable(keyid)) {
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mSamples.AppendElement(aSample);
|
||||
}
|
||||
caps.NotifyWhenKeyIdUsable(aSample->crypto.key, this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SamplesWaitingForKey::NotifyUsable(const CencKeyId& aKeyId)
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
size_t i = 0;
|
||||
while (i < mSamples.Length()) {
|
||||
if (aKeyId == mSamples[i]->crypto.key) {
|
||||
RefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethodWithArg<MP4Sample*>(mDecoder,
|
||||
&MediaDataDecoder::Input,
|
||||
mSamples[i].forget());
|
||||
mSamples.RemoveElementAt(i);
|
||||
mTaskQueue->Dispatch(task.forget());
|
||||
} else {
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SamplesWaitingForKey::Flush()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mSamples.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
SamplesWaitingForKey::BreakCycles()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mDecoder = nullptr;
|
||||
mTaskQueue = nullptr;
|
||||
mProxy = nullptr;
|
||||
mSamples.Clear();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
57
dom/media/fmp4/eme/SamplesWaitingForKey.h
Normal file
57
dom/media/fmp4/eme/SamplesWaitingForKey.h
Normal file
@ -0,0 +1,57 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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 SamplesWaitingForKey_h_
|
||||
#define SamplesWaitingForKey_h_
|
||||
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
#include "MediaTaskQueue.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
typedef nsTArray<uint8_t> CencKeyId;
|
||||
|
||||
class CDMProxy;
|
||||
|
||||
// Encapsulates the task of waiting for the CDMProxy to have the necessary
|
||||
// keys to decypt a given sample.
|
||||
class SamplesWaitingForKey {
|
||||
typedef mp4_demuxer::MP4Sample MP4Sample;
|
||||
public:
|
||||
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SamplesWaitingForKey)
|
||||
|
||||
explicit SamplesWaitingForKey(MediaDataDecoder* aDecoder,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
CDMProxy* aProxy);
|
||||
|
||||
// Returns true if we need to wait for a key to become usable.
|
||||
// Will callback MediaDataDecoder::Input(aSample) on mDecoder once the
|
||||
// sample is ready to be decrypted. The order of input samples is
|
||||
// preserved.
|
||||
bool WaitIfKeyNotUsable(MP4Sample* aSample);
|
||||
|
||||
void NotifyUsable(const CencKeyId& aKeyId);
|
||||
|
||||
void Flush();
|
||||
|
||||
void BreakCycles();
|
||||
|
||||
protected:
|
||||
~SamplesWaitingForKey();
|
||||
|
||||
private:
|
||||
Mutex mMutex;
|
||||
nsRefPtr<MediaDataDecoder> mDecoder;
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
nsTArray<nsAutoPtr<MP4Sample>> mSamples;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // SamplesWaitingForKey_h_
|
@ -1,26 +1,28 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
EXPORTS += [
|
||||
'EMEAudioDecoder.h',
|
||||
'EMEDecoderModule.h',
|
||||
'EMEH264Decoder.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'EMEAudioDecoder.cpp',
|
||||
'EMEDecoderModule.cpp',
|
||||
'EMEH264Decoder.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
DEFINES['NOMINMAX'] = True
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
EXPORTS += [
|
||||
'EMEAudioDecoder.h',
|
||||
'EMEDecoderModule.h',
|
||||
'EMEH264Decoder.h',
|
||||
'SamplesWaitingForKey.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'EMEAudioDecoder.cpp',
|
||||
'EMEDecoderModule.cpp',
|
||||
'EMEH264Decoder.cpp',
|
||||
'SamplesWaitingForKey.cpp',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
||||
|
||||
if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
DEFINES['NOMINMAX'] = True
|
||||
|
@ -47,6 +47,7 @@ typedef enum {
|
||||
GMPCryptoErr = 10,
|
||||
GMPEndOfEnumeration = 11,
|
||||
GMPInvalidArgErr = 12,
|
||||
GMPAbortedErr = 13,
|
||||
GMPLastErr // Placeholder, must be last. This enum's values must remain consecutive!
|
||||
} GMPErr;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user