Bug 1154133 - Remove sync dispatches in EMEDecryptor, and mark MediaTaskQueue::SyncDispatch as deprecated. r=edwin

This commit is contained in:
Chris Pearce 2015-04-15 12:14:49 +12:00
parent 2fa42acf5e
commit d6025eed65
5 changed files with 129 additions and 109 deletions

View File

@ -124,6 +124,7 @@ private:
nsresult
MediaTaskQueue::SyncDispatch(TemporaryRef<nsIRunnable> aRunnable) {
NS_WARNING("MediaTaskQueue::SyncDispatch is dangerous and deprecated. Stop using this!");
RefPtr<MediaTaskQueueSyncRunnable> task(new MediaTaskQueueSyncRunnable(aRunnable));
nsresult rv = Dispatch(task);
NS_ENSURE_SUCCESS(rv, rv);

View File

@ -84,6 +84,8 @@ public:
// flushed. Normal operations should use Dispatch.
nsresult ForceDispatch(TemporaryRef<nsIRunnable> aRunnable);
// DEPRECATED! Do not us, if a flush happens at the same time, this function
// can hang and block forever!
nsresult SyncDispatch(TemporaryRef<nsIRunnable> aRunnable);
// Puts the queue in a shutdown state and returns immediately. The queue will

View File

@ -440,7 +440,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(GMPAbortedErr, nullptr);
job->PostResult(GMPAbortedErr);
}
mDecryptionJobs.Clear();
@ -625,23 +625,22 @@ CDMProxy::Capabilites() {
void
CDMProxy::Decrypt(MediaRawData* aSample,
DecryptionClient* aClient)
DecryptionClient* aClient,
MediaTaskQueue* aTaskQueue)
{
nsAutoPtr<DecryptJob> job(new DecryptJob(aSample, aClient));
nsRefPtr<DecryptJob> job(new DecryptJob(aSample, aClient, aTaskQueue));
nsCOMPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<nsAutoPtr<DecryptJob>>(this, &CDMProxy::gmp_Decrypt, job));
NS_NewRunnableMethodWithArg<nsRefPtr<DecryptJob>>(this, &CDMProxy::gmp_Decrypt, job));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
void
CDMProxy::gmp_Decrypt(nsAutoPtr<DecryptJob> aJob)
CDMProxy::gmp_Decrypt(nsRefPtr<DecryptJob> aJob)
{
MOZ_ASSERT(IsOnGMPThread());
MOZ_ASSERT(aJob->mClient);
MOZ_ASSERT(aJob->mSample);
if (!mCDM) {
aJob->mClient->Decrypted(GMPAbortedErr, nullptr);
aJob->PostResult(GMPAbortedErr);
return;
}
@ -658,34 +657,64 @@ CDMProxy::gmp_Decrypted(uint32_t aId,
const nsTArray<uint8_t>& aDecryptedData)
{
MOZ_ASSERT(IsOnGMPThread());
#ifdef DEBUG
bool jobIdFound = false;
#endif
for (size_t i = 0; i < mDecryptionJobs.Length(); i++) {
DecryptJob* job = mDecryptionJobs[i];
if (job->mId == aId) {
if (aDecryptedData.Length() != job->mSample->mSize) {
NS_WARNING("CDM returned incorrect number of decrypted bytes");
}
if (GMP_SUCCEEDED(aResult)) {
nsAutoPtr<MediaRawDataWriter> writer(job->mSample->CreateWriter());
PodCopy(writer->mData,
aDecryptedData.Elements(),
std::min<size_t>(aDecryptedData.Length(), job->mSample->mSize));
job->mClient->Decrypted(GMPNoErr, job->mSample);
} 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);
} else {
nsAutoCString str("CDM returned decode failure GMPErr=");
str.AppendInt(aResult);
NS_WARNING(str.get());
job->mClient->Decrypted(aResult, nullptr);
}
#ifdef DEBUG
jobIdFound = true;
#endif
job->PostResult(aResult, aDecryptedData);
mDecryptionJobs.RemoveElementAt(i);
return;
}
}
NS_WARNING("GMPDecryptorChild returned incorrect job ID");
#ifdef DEBUG
if (!jobIdFound) {
NS_WARNING("GMPDecryptorChild returned incorrect job ID");
}
#endif
}
void
CDMProxy::DecryptJob::PostResult(GMPErr aResult)
{
nsTArray<uint8_t> empty;
PostResult(aResult, empty);
}
void
CDMProxy::DecryptJob::PostResult(GMPErr aResult, const nsTArray<uint8_t>& aDecryptedData)
{
if (aDecryptedData.Length() != mSample->mSize) {
NS_WARNING("CDM returned incorrect number of decrypted bytes");
}
mResult = aResult;
if (GMP_SUCCEEDED(aResult)) {
nsAutoPtr<MediaRawDataWriter> writer(mSample->CreateWriter());
PodCopy(writer->mData,
aDecryptedData.Elements(),
std::min<size_t>(aDecryptedData.Length(), mSample->mSize));
} 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.
} else {
nsAutoCString str("CDM returned decode failure GMPErr=");
str.AppendInt(aResult);
NS_WARNING(str.get());
mSample = nullptr;
}
mTaskQueue->Dispatch(RefPtr<nsIRunnable>(this).forget());
}
nsresult
CDMProxy::DecryptJob::Run()
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
mClient->Decrypted(mResult, mSample);
return NS_OK;
}
void

View File

@ -141,7 +141,9 @@ public:
const nsAString& aMsg);
// Threadsafe.
void Decrypt(MediaRawData* aSample, DecryptionClient* aSink);
void Decrypt(MediaRawData* aSample,
DecryptionClient* aSink,
MediaTaskQueue* aTaskQueue);
// Reject promise with DOMException corresponding to aExceptionCode.
// Can be called from any thread.
@ -234,18 +236,35 @@ private:
// GMP thread only.
void gmp_RemoveSession(nsAutoPtr<SessionOpData> aData);
struct DecryptJob {
DecryptJob(MediaRawData* aSample, DecryptionClient* aClient)
class DecryptJob : public nsRunnable {
public:
explicit DecryptJob(MediaRawData* aSample,
DecryptionClient* aClient,
MediaTaskQueue* aTaskQueue)
: mId(0)
, mSample(aSample)
, mClient(aClient)
{}
, mResult(GMPGenericErr)
, mTaskQueue(aTaskQueue)
{
MOZ_ASSERT(mClient);
MOZ_ASSERT(mSample);
}
NS_METHOD Run() override;
void PostResult(GMPErr aResult, const nsTArray<uint8_t>& aDecryptedData);
void PostResult(GMPErr aResult);
uint32_t mId;
nsRefPtr<MediaRawData> mSample;
private:
~DecryptJob() {}
nsAutoPtr<DecryptionClient> mClient;
GMPErr mResult;
nsRefPtr<MediaTaskQueue> mTaskQueue;
};
// GMP thread only.
void gmp_Decrypt(nsAutoPtr<DecryptJob> aJob);
void gmp_Decrypt(nsRefPtr<DecryptJob> aJob);
class RejectPromiseTask : public nsRunnable {
public:
@ -316,7 +335,7 @@ private:
// Decryption jobs sent to CDM, awaiting result.
// GMP thread only.
nsTArray<nsAutoPtr<DecryptJob>> mDecryptionJobs;
nsTArray<nsRefPtr<DecryptJob>> mDecryptionJobs;
// Number of buffers we've decrypted. Used to uniquely identify
// decryption jobs sent to CDM. Note we can't just use the length of

View File

@ -22,71 +22,38 @@ public:
EMEDecryptor(MediaDataDecoder* aDecoder,
MediaDataDecoderCallback* aCallback,
CDMProxy* aProxy)
CDMProxy* aProxy,
MediaTaskQueue* aDecodeTaskQueue)
: mDecoder(aDecoder)
, mCallback(aCallback)
, mTaskQueue(CreateFlushableMediaDecodeTaskQueue())
, mTaskQueue(aDecodeTaskQueue)
, mProxy(aProxy)
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, mTaskQueue, mProxy))
#ifdef DEBUG
, mIsShutdown(false)
#endif
{
}
virtual nsresult Init() override {
MOZ_ASSERT(!mIsShutdown);
nsresult rv = mTaskQueue->SyncDispatch(
NS_NewRunnableMethod(mDecoder, &MediaDataDecoder::Init));
unused << NS_WARN_IF(NS_FAILED(rv));
return rv;
return mDecoder->Init();
}
class DeliverDecrypted : public DecryptionClient {
public:
DeliverDecrypted(EMEDecryptor* aDecryptor, FlushableMediaTaskQueue* aTaskQueue)
explicit DeliverDecrypted(EMEDecryptor* aDecryptor)
: mDecryptor(aDecryptor)
, mTaskQueue(aTaskQueue)
{}
virtual void Decrypted(GMPErr aResult,
MediaRawData* aSample) override {
if (aResult == GMPNoKeyErr) {
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
mDecryptor,
&EMEDecryptor::Input,
nsRefPtr<MediaRawData>(aSample));
mTaskQueue->Dispatch(task.forget());
} else if (GMP_FAILED(aResult)) {
if (mDecryptor->mCallback) {
mDecryptor->mCallback->Error();
}
MOZ_ASSERT(!aSample);
} else {
RefPtr<nsIRunnable> task;
task = NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
mDecryptor,
&EMEDecryptor::Decrypted,
nsRefPtr<MediaRawData>(aSample));
mTaskQueue->Dispatch(task.forget());
}
mTaskQueue = nullptr;
{ }
virtual void Decrypted(GMPErr aResult, MediaRawData* aSample) override {
mDecryptor->Decrypted(aResult, aSample);
mDecryptor = nullptr;
}
private:
nsRefPtr<EMEDecryptor> mDecryptor;
nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
};
virtual nsresult Input(MediaRawData* aSample) override {
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
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
// for keys to become usable, and once they do we need to dispatch an event
// 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.
if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
return NS_OK;
}
@ -95,57 +62,59 @@ public:
mProxy->GetSessionIdsForKeyId(aSample->mCrypto.mKeyId,
writer->mCrypto.mSessionIds);
mProxy->Decrypt(aSample, new DeliverDecrypted(this, mTaskQueue));
mProxy->Decrypt(aSample, new DeliverDecrypted(this), mTaskQueue);
return NS_OK;
}
void Decrypted(MediaRawData* aSample) {
MOZ_ASSERT(!mIsShutdown);
nsresult rv = mTaskQueue->Dispatch(
NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
mDecoder,
&MediaDataDecoder::Input,
nsRefPtr<MediaRawData>(aSample)));
unused << NS_WARN_IF(NS_FAILED(rv));
void Decrypted(GMPErr aResult, MediaRawData* aSample) {
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
if (mIsShutdown) {
NS_WARNING("EME decrypted sample arrived after shutdown");
return;
}
if (aResult == GMPNoKeyErr) {
// Key became unusable after we sent the sample to CDM to decrypt.
// Call Input() again, so that the sample is enqueued for decryption
// if the key becomes usable again.
Input(aSample);
} else if (GMP_FAILED(aResult)) {
if (mCallback) {
mCallback->Error();
}
MOZ_ASSERT(!aSample);
} else {
MOZ_ASSERT(!mIsShutdown);
nsresult rv = mDecoder->Input(aSample);
unused << NS_WARN_IF(NS_FAILED(rv));
}
}
virtual nsresult Flush() override {
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
MOZ_ASSERT(!mIsShutdown);
nsresult rv = mTaskQueue->SyncDispatch(
NS_NewRunnableMethod(
mDecoder,
&MediaDataDecoder::Flush));
nsresult rv = mDecoder->Flush();
unused << NS_WARN_IF(NS_FAILED(rv));
mSamplesWaitingForKey->Flush();
return rv;
}
virtual nsresult Drain() override {
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
MOZ_ASSERT(!mIsShutdown);
nsresult rv = mTaskQueue->Dispatch(
NS_NewRunnableMethod(
mDecoder,
&MediaDataDecoder::Drain));
nsresult rv = mDecoder->Drain();
unused << NS_WARN_IF(NS_FAILED(rv));
return rv;
}
virtual nsresult Shutdown() override {
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
MOZ_ASSERT(!mIsShutdown);
#ifdef DEBUG
mIsShutdown = true;
#endif
nsresult rv = mTaskQueue->SyncDispatch(
NS_NewRunnableMethod(
mDecoder,
&MediaDataDecoder::Shutdown));
nsresult rv = mDecoder->Shutdown();
unused << NS_WARN_IF(NS_FAILED(rv));
mSamplesWaitingForKey->BreakCycles();
mSamplesWaitingForKey = nullptr;
mDecoder = nullptr;
mTaskQueue->BeginShutdown();
mTaskQueue->AwaitShutdownAndIdle();
mTaskQueue = nullptr;
mProxy = nullptr;
mCallback = nullptr;
return rv;
@ -155,12 +124,10 @@ private:
nsRefPtr<MediaDataDecoder> mDecoder;
MediaDataDecoderCallback* mCallback;
nsRefPtr<FlushableMediaTaskQueue> mTaskQueue;
nsRefPtr<MediaTaskQueue> mTaskQueue;
nsRefPtr<CDMProxy> mProxy;
nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
#ifdef DEBUG
bool mIsShutdown;
#endif
};
class EMEMediaDataDecoderProxy : public MediaDataDecoderProxy {
@ -273,7 +240,8 @@ EMEDecoderModule::CreateVideoDecoder(const VideoInfo& aConfig,
nsRefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder,
aCallback,
mProxy));
mProxy,
MediaTaskQueue::GetCurrentQueue()));
return emeDecoder.forget();
}
@ -303,7 +271,8 @@ EMEDecoderModule::CreateAudioDecoder(const AudioInfo& aConfig,
nsRefPtr<MediaDataDecoder> emeDecoder(new EMEDecryptor(decoder,
aCallback,
mProxy));
mProxy,
MediaTaskQueue::GetCurrentQueue()));
return emeDecoder.forget();
}