Bug 1109861 - Add delegate to manage waiting for the CDM to mark key usable. r=kinetik

This commit is contained in:
Chris Pearce 2014-12-11 15:59:37 +13:00
parent d65355fd96
commit 515d668bfb
13 changed files with 303 additions and 131 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View 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_

View File

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

View File

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