Bug 1057908 - GeckoMediaPluginService needs to be proxied from Content processes to parent process. Part 2 - support asynchronous GMP API getters. r=jwwang,rjesup.

This commit is contained in:
Peter Van der Beken 2015-02-10 11:48:29 +01:00
parent e98292c191
commit ace406a462
12 changed files with 1171 additions and 373 deletions

View File

@ -25,6 +25,7 @@ CDMProxy::CDMProxy(dom::MediaKeys* aKeys, const nsAString& aKeySystem)
, mKeySystem(aKeySystem)
, mCDM(nullptr)
, mDecryptionJobCount(0)
, mShutdownCalled(false)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_COUNT_CTOR(CDMProxy);
@ -70,7 +71,7 @@ CDMProxy::Init(PromiseId aPromiseId,
nsCOMPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<nsAutoPtr<InitData>>(this,
&CDMProxy::gmp_Init,
data));
Move(data)));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
}
@ -83,14 +84,57 @@ CDMProxy::IsOnGMPThread()
#endif
void
CDMProxy::gmp_Init(nsAutoPtr<InitData> aData)
CDMProxy::gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData)
{
EME_LOG("CDMProxy::gmp_InitDone");
if (!aCDM || mShutdownCalled) {
if (aCDM) {
aCDM->Close();
}
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
mCDM = aCDM;
mCallback = new CDMCallbackProxy(this);
mCDM->Init(mCallback);
nsCOMPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<uint32_t>(this,
&CDMProxy::OnCDMCreated,
aData->mPromiseId));
NS_DispatchToMainThread(task);
}
class gmp_InitDoneCallback : public GetGMPDecryptorCallback
{
public:
gmp_InitDoneCallback(CDMProxy* aCDMProxy,
nsAutoPtr<CDMProxy::InitData>&& aData)
: mCDMProxy(aCDMProxy),
mData(Move(aData))
{
}
void Done(GMPDecryptorProxy* aCDM)
{
mCDMProxy->gmp_InitDone(aCDM, Move(mData));
}
private:
nsRefPtr<CDMProxy> mCDMProxy;
nsAutoPtr<CDMProxy::InitData> mData;
};
void
CDMProxy::gmp_Init(nsAutoPtr<InitData>&& aData)
{
MOZ_ASSERT(IsOnGMPThread());
uint32_t promiseID = aData->mPromiseId;
nsCOMPtr<mozIGeckoMediaPluginService> mps =
do_GetService("@mozilla.org/gecko-media-plugin-service;1");
if (!mps) {
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
@ -100,7 +144,7 @@ CDMProxy::gmp_Init(nsAutoPtr<InitData> aData)
mNodeId);
MOZ_ASSERT(!GetNodeId().IsEmpty());
if (NS_FAILED(rv)) {
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
@ -112,17 +156,12 @@ CDMProxy::gmp_Init(nsAutoPtr<InitData> aData)
nsTArray<nsCString> tags;
tags.AppendElement(NS_ConvertUTF16toUTF8(mKeySystem));
rv = mps->GetGMPDecryptor(&tags, GetNodeId(), &mCDM);
if (NS_FAILED(rv) || !mCDM) {
RejectPromise(aData->mPromiseId, NS_ERROR_DOM_INVALID_STATE_ERR);
} else {
mCallback = new CDMCallbackProxy(this);
mCDM->Init(mCallback);
nsCOMPtr<nsIRunnable> task(
NS_NewRunnableMethodWithArg<uint32_t>(this,
&CDMProxy::OnCDMCreated,
aData->mPromiseId));
NS_DispatchToMainThread(task);
UniquePtr<GetGMPDecryptorCallback> callback(new gmp_InitDoneCallback(this,
Move(aData)));
rv = mps->GetGMPDecryptor(&tags, GetNodeId(), Move(callback));
if (NS_FAILED(rv)) {
RejectPromise(promiseID, NS_ERROR_DOM_INVALID_STATE_ERR);
}
}
@ -341,6 +380,8 @@ CDMProxy::gmp_Shutdown()
{
MOZ_ASSERT(IsOnGMPThread());
mShutdownCalled = true;
// 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];

View File

@ -174,6 +174,7 @@ public:
#endif
private:
friend class gmp_InitDoneCallback;
struct InitData {
uint32_t mPromiseId;
@ -183,7 +184,8 @@ private:
};
// GMP thread only.
void gmp_Init(nsAutoPtr<InitData> aData);
void gmp_Init(nsAutoPtr<InitData>&& aData);
void gmp_InitDone(GMPDecryptorProxy* aCDM, nsAutoPtr<InitData>&& aData);
// GMP thread only.
void gmp_Shutdown();
@ -320,6 +322,10 @@ private:
// from it.
// GMP thread only.
uint32_t mDecryptionJobCount;
// True if CDMProxy::gmp_Shutdown was called.
// GMP thread only.
bool mShutdownCalled;
};

View File

@ -133,6 +133,39 @@ GMPAudioDecoder::GetNodeId()
return NS_LITERAL_CSTRING("");
}
void
GMPAudioDecoder::GetGMPAPI(GMPInitDoneRunnable* aInitDone)
{
MOZ_ASSERT(IsOnGMPThread());
nsTArray<nsCString> tags;
InitTags(tags);
UniquePtr<GetGMPAudioDecoderCallback> callback(
new GMPInitDoneCallback(this, aInitDone));
if (NS_FAILED(mMPS->GetGMPAudioDecoder(&tags, GetNodeId(), Move(callback)))) {
aInitDone->Dispatch();
}
}
void
GMPAudioDecoder::GMPInitDone(GMPAudioDecoderProxy* aGMP)
{
MOZ_ASSERT(aGMP);
nsTArray<uint8_t> codecSpecific;
codecSpecific.AppendElements(mConfig.audio_specific_config->Elements(),
mConfig.audio_specific_config->Length());
nsresult rv = aGMP->InitDecode(kGMPAudioCodecAAC,
mConfig.channel_count,
mConfig.bits_per_sample,
mConfig.samples_per_second,
codecSpecific,
mAdapter);
if (NS_SUCCEEDED(rv)) {
mGMP = aGMP;
}
}
nsresult
GMPAudioDecoder::Init()
{
@ -141,25 +174,20 @@ GMPAudioDecoder::Init()
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
MOZ_ASSERT(mMPS);
nsTArray<nsCString> tags;
InitTags(tags);
nsresult rv = mMPS->GetGMPAudioDecoder(&tags, GetNodeId(), &mGMP);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(mGMP);
nsCOMPtr<nsIThread> gmpThread = NS_GetCurrentThread();
nsTArray<uint8_t> codecSpecific;
codecSpecific.AppendElements(mConfig.audio_specific_config->Elements(),
mConfig.audio_specific_config->Length());
nsRefPtr<GMPInitDoneRunnable> initDone(new GMPInitDoneRunnable());
gmpThread->Dispatch(
NS_NewRunnableMethodWithArg<GMPInitDoneRunnable*>(this,
&GMPAudioDecoder::GetGMPAPI,
initDone),
NS_DISPATCH_NORMAL);
rv = mGMP->InitDecode(kGMPAudioCodecAAC,
mConfig.channel_count,
mConfig.bits_per_sample,
mConfig.samples_per_second,
codecSpecific,
mAdapter);
NS_ENSURE_SUCCESS(rv, rv);
while (!initDone->IsDone()) {
NS_ProcessNextEvent(gmpThread, true);
}
return NS_OK;
return mGMP ? NS_OK : NS_ERROR_FAILURE;
}
nsresult

View File

@ -80,6 +80,61 @@ protected:
virtual nsCString GetNodeId();
private:
class GMPInitDoneRunnable : public nsRunnable
{
public:
GMPInitDoneRunnable()
: mInitDone(false),
mThread(do_GetCurrentThread())
{
}
NS_IMETHOD Run()
{
mInitDone = true;
return NS_OK;
}
void Dispatch()
{
mThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
bool IsDone()
{
MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
return mInitDone;
}
private:
bool mInitDone;
nsCOMPtr<nsIThread> mThread;
};
void GetGMPAPI(GMPInitDoneRunnable* aInitDone);
class GMPInitDoneCallback : public GetGMPAudioDecoderCallback
{
public:
GMPInitDoneCallback(GMPAudioDecoder* aDecoder,
GMPInitDoneRunnable* aGMPInitDone)
: mDecoder(aDecoder)
, mGMPInitDone(aGMPInitDone)
{
}
virtual void Done(GMPAudioDecoderProxy* aGMP)
{
if (aGMP) {
mDecoder->GMPInitDone(aGMP);
}
mGMPInitDone->Dispatch();
}
private:
nsRefPtr<GMPAudioDecoder> mDecoder;
nsRefPtr<GMPInitDoneRunnable> mGMPInitDone;
};
void GMPInitDone(GMPAudioDecoderProxy* aGMP);
const mp4_demuxer::AudioDecoderConfig& mConfig;
MediaDataDecoderCallbackProxy* mCallback;
nsCOMPtr<mozIGeckoMediaPluginService> mMPS;

View File

@ -157,29 +157,24 @@ GMPVideoDecoder::CreateFrame(mp4_demuxer::MP4Sample* aSample)
return frame;
}
nsresult
GMPVideoDecoder::Init()
void
GMPVideoDecoder::GetGMPAPI(GMPInitDoneRunnable* aInitDone)
{
MOZ_ASSERT(IsOnGMPThread());
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
MOZ_ASSERT(mMPS);
nsTArray<nsCString> tags;
InitTags(tags);
nsresult rv = mMPS->GetGMPVideoDecoder(&tags, GetNodeId(), &mHost, &mGMP);
NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(mHost && mGMP);
UniquePtr<GetGMPVideoDecoderCallback> callback(
new GMPInitDoneCallback(this, aInitDone));
if (NS_FAILED(mMPS->GetGMPVideoDecoder(&tags, GetNodeId(), Move(callback)))) {
aInitDone->Dispatch();
}
}
// GMP implementations have interpreted the meaning of GMP_BufferLength32
// differently. The OpenH264 GMP expects GMP_BufferLength32 to behave as
// specified in the GMP API, where each buffer is prefixed by a 32-bit
// host-endian buffer length that includes the size of the buffer length
// field. Other existing GMPs currently expect GMP_BufferLength32 (when
// combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
// 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
// and do not include the length of the buffer length field.
mConvertNALUnitLengths = mGMP->GetDisplayName().EqualsLiteral("gmpopenh264");
void
GMPVideoDecoder::GMPInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
{
MOZ_ASSERT(aHost && aGMP);
GMPVideoCodec codec;
memset(&codec, 0, sizeof(codec));
@ -195,13 +190,48 @@ GMPVideoDecoder::Init()
codecSpecific.AppendElements(mConfig.extra_data->Elements(),
mConfig.extra_data->Length());
rv = mGMP->InitDecode(codec,
nsresult rv = aGMP->InitDecode(codec,
codecSpecific,
mAdapter,
PR_GetNumberOfProcessors());
NS_ENSURE_SUCCESS(rv, rv);
if (NS_SUCCEEDED(rv)) {
mGMP = aGMP;
mHost = aHost;
return NS_OK;
// GMP implementations have interpreted the meaning of GMP_BufferLength32
// differently. The OpenH264 GMP expects GMP_BufferLength32 to behave as
// specified in the GMP API, where each buffer is prefixed by a 32-bit
// host-endian buffer length that includes the size of the buffer length
// field. Other existing GMPs currently expect GMP_BufferLength32 (when
// combined with kGMPVideoCodecH264) to mean "like AVCC but restricted to
// 4-byte NAL lengths" (i.e. buffer lengths are specified in big-endian
// and do not include the length of the buffer length field.
mConvertNALUnitLengths = mGMP->GetDisplayName().EqualsLiteral("gmpopenh264");
}
}
nsresult
GMPVideoDecoder::Init()
{
MOZ_ASSERT(IsOnGMPThread());
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
MOZ_ASSERT(mMPS);
nsCOMPtr<nsIThread> gmpThread = NS_GetCurrentThread();
nsRefPtr<GMPInitDoneRunnable> initDone(new GMPInitDoneRunnable());
gmpThread->Dispatch(
NS_NewRunnableMethodWithArg<GMPInitDoneRunnable*>(this,
&GMPVideoDecoder::GetGMPAPI,
initDone),
NS_DISPATCH_NORMAL);
while (!initDone->IsDone()) {
NS_ProcessNextEvent(gmpThread, true);
}
return mGMP ? NS_OK : NS_ERROR_FAILURE;
}
nsresult

View File

@ -96,6 +96,61 @@ protected:
virtual GMPUnique<GMPVideoEncodedFrame>::Ptr CreateFrame(mp4_demuxer::MP4Sample* aSample);
private:
class GMPInitDoneRunnable : public nsRunnable
{
public:
GMPInitDoneRunnable()
: mInitDone(false),
mThread(do_GetCurrentThread())
{
}
NS_IMETHOD Run()
{
mInitDone = true;
return NS_OK;
}
void Dispatch()
{
mThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
bool IsDone()
{
MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
return mInitDone;
}
private:
bool mInitDone;
nsCOMPtr<nsIThread> mThread;
};
void GetGMPAPI(GMPInitDoneRunnable* aInitDone);
class GMPInitDoneCallback : public GetGMPVideoDecoderCallback
{
public:
GMPInitDoneCallback(GMPVideoDecoder* aDecoder,
GMPInitDoneRunnable* aGMPInitDone)
: mDecoder(aDecoder)
, mGMPInitDone(aGMPInitDone)
{
}
virtual void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
{
if (aGMP) {
mDecoder->GMPInitDone(aGMP, aHost);
}
mGMPInitDone->Dispatch();
}
private:
nsRefPtr<GMPVideoDecoder> mDecoder;
nsRefPtr<GMPInitDoneRunnable> mGMPInitDone;
};
void GMPInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost);
const mp4_demuxer::VideoDecoderConfig& mConfig;
MediaDataDecoderCallbackProxy* mCallback;
nsCOMPtr<mozIGeckoMediaPluginService> mMPS;

View File

@ -281,14 +281,33 @@ GeckoMediaPluginService::GetThread(nsIThread** aThread)
return NS_OK;
}
class GetGMPParentForAudioDecoderDone
{
public:
explicit GetGMPParentForAudioDecoderDone(UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
: mCallback(Move(aCallback))
{
}
void Done(GMPParent* aGMPParent)
{
GMPAudioDecoderParent* gmpADP = nullptr;
aGMPParent->GetGMPAudioDecoder(&gmpADP);
mCallback->Done(gmpADP);
}
private:
UniquePtr<GetGMPAudioDecoderCallback> mCallback;
};
NS_IMETHODIMP
GeckoMediaPluginService::GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
const nsACString& aNodeId,
GMPAudioDecoderProxy** aGMPAD)
UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aGMPAD);
NS_ENSURE_ARG(aCallback);
if (mShuttingDownOnGMPThread) {
return NS_ERROR_FAILURE;
@ -301,27 +320,42 @@ GeckoMediaPluginService::GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
return NS_ERROR_FAILURE;
}
GMPAudioDecoderParent* gmpADP;
nsresult rv = gmp->GetGMPAudioDecoder(&gmpADP);
if (NS_FAILED(rv)) {
return rv;
}
*aGMPAD = gmpADP;
GetGMPParentForAudioDecoderDone(Move(aCallback)).Done(gmp);
return NS_OK;
}
class GetGMPParentForVideoDecoderDone
{
public:
explicit GetGMPParentForVideoDecoderDone(UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
: mCallback(Move(aCallback))
{
}
void Done(GMPParent* aGMPParent)
{
GMPVideoDecoderParent* gmpVDP = nullptr;
GMPVideoHostImpl* videoHost = nullptr;
nsresult rv = aGMPParent->GetGMPVideoDecoder(&gmpVDP);
if (NS_SUCCEEDED(rv)) {
videoHost = &gmpVDP->Host();
}
mCallback->Done(gmpVDP, videoHost);
}
private:
UniquePtr<GetGMPVideoDecoderCallback> mCallback;
};
NS_IMETHODIMP
GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
const nsACString& aNodeId,
GMPVideoHost** aOutVideoHost,
GMPVideoDecoderProxy** aGMPVD)
UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aOutVideoHost);
NS_ENSURE_ARG(aGMPVD);
NS_ENSURE_ARG(aCallback);
if (mShuttingDownOnGMPThread) {
return NS_ERROR_FAILURE;
@ -339,28 +373,42 @@ GeckoMediaPluginService::GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
}
GMPVideoDecoderParent* gmpVDP;
nsresult rv = gmp->GetGMPVideoDecoder(&gmpVDP);
if (NS_FAILED(rv)) {
return rv;
}
*aGMPVD = gmpVDP;
*aOutVideoHost = &gmpVDP->Host();
GetGMPParentForVideoDecoderDone(Move(aCallback)).Done(gmp);
return NS_OK;
}
class GetGMPParentForVideoEncoderDone
{
public:
explicit GetGMPParentForVideoEncoderDone(UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
: mCallback(Move(aCallback))
{
}
void Done(GMPParent* aGMPParent)
{
GMPVideoEncoderParent* gmpVEP = nullptr;
GMPVideoHostImpl* videoHost = nullptr;
nsresult rv = aGMPParent->GetGMPVideoEncoder(&gmpVEP);
if (NS_SUCCEEDED(rv)) {
videoHost = &gmpVEP->Host();
}
mCallback->Done(gmpVEP, videoHost);
}
private:
UniquePtr<GetGMPVideoEncoderCallback> mCallback;
};
NS_IMETHODIMP
GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
const nsACString& aNodeId,
GMPVideoHost** aOutVideoHost,
GMPVideoEncoderProxy** aGMPVE)
UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aOutVideoHost);
NS_ENSURE_ARG(aGMPVE);
NS_ENSURE_ARG(aCallback);
if (mShuttingDownOnGMPThread) {
return NS_ERROR_FAILURE;
@ -377,22 +425,34 @@ GeckoMediaPluginService::GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
return NS_ERROR_FAILURE;
}
GMPVideoEncoderParent* gmpVEP;
nsresult rv = gmp->GetGMPVideoEncoder(&gmpVEP);
if (NS_FAILED(rv)) {
return rv;
}
*aGMPVE = gmpVEP;
*aOutVideoHost = &gmpVEP->Host();
GetGMPParentForVideoEncoderDone(Move(aCallback)).Done(gmp);
return NS_OK;
}
class GetGMPParentForDecryptorDone
{
public:
explicit GetGMPParentForDecryptorDone(UniquePtr<GetGMPDecryptorCallback>&& aCallback)
: mCallback(Move(aCallback))
{
}
void Done(GMPParent* aGMPParent)
{
GMPDecryptorParent* ksp = nullptr;
aGMPParent->GetGMPDecryptor(&ksp);
mCallback->Done(ksp);
}
private:
UniquePtr<GetGMPDecryptorCallback> mCallback;
};
NS_IMETHODIMP
GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
const nsACString& aNodeId,
GMPDecryptorProxy** aDecryptor)
UniquePtr<GetGMPDecryptorCallback>&& aCallback)
{
#if defined(XP_LINUX) && defined(MOZ_GMP_SANDBOX)
if (!SandboxInfo::Get().CanSandboxMedia()) {
@ -404,7 +464,7 @@ GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
NS_ENSURE_ARG(aTags && aTags->Length() > 0);
NS_ENSURE_ARG(aDecryptor);
NS_ENSURE_ARG(aCallback);
if (mShuttingDownOnGMPThread) {
return NS_ERROR_FAILURE;
@ -417,13 +477,7 @@ GeckoMediaPluginService::GetGMPDecryptor(nsTArray<nsCString>* aTags,
return NS_ERROR_FAILURE;
}
GMPDecryptorParent* ksp;
nsresult rv = gmp->GetGMPDecryptor(&ksp);
if (NS_FAILED(rv)) {
return rv;
}
*aDecryptor = static_cast<GMPDecryptorProxy*>(ksp);
GetGMPParentForDecryptorDone(Move(aCallback)).Done(gmp);
return NS_OK;
}

View File

@ -44,18 +44,20 @@ public:
bool *aRetVal) override;
NS_IMETHOD GetGMPVideoDecoder(nsTArray<nsCString>* aTags,
const nsACString& aNodeId,
GMPVideoHost** aOutVideoHost,
GMPVideoDecoderProxy** aGMPVD) override;
UniquePtr<GetGMPVideoDecoderCallback>&& aCallback)
override;
NS_IMETHOD GetGMPVideoEncoder(nsTArray<nsCString>* aTags,
const nsACString& aNodeId,
GMPVideoHost **aOutVideoHost,
GMPVideoEncoderProxy** aGMPVE) override;
UniquePtr<GetGMPVideoEncoderCallback>&& aCallback)
override;
NS_IMETHOD GetGMPAudioDecoder(nsTArray<nsCString>* aTags,
const nsACString& aNodeId,
GMPAudioDecoderProxy **aGMPAD) override;
UniquePtr<GetGMPAudioDecoderCallback>&& aCallback)
override;
NS_IMETHOD GetGMPDecryptor(nsTArray<nsCString>* aTags,
const nsACString& aNodeId,
GMPDecryptorProxy** aDecryptor) override;
UniquePtr<GetGMPDecryptorCallback>&& aCallback)
override;
int32_t AsyncShutdownTimeoutMs();

View File

@ -7,6 +7,7 @@
#include "nsIThread.idl"
%{C++
#include "mozilla/UniquePtr.h"
#include "nsTArray.h"
#include "nsStringGlue.h"
class GMPAudioDecoderProxy;
@ -14,16 +15,36 @@ class GMPDecryptorProxy;
class GMPVideoDecoderProxy;
class GMPVideoEncoderProxy;
class GMPVideoHost;
template<class T>
class GMPGetterCallback
{
public:
GMPGetterCallback() { MOZ_COUNT_CTOR(GMPGetterCallback<T>); }
virtual ~GMPGetterCallback() { MOZ_COUNT_DTOR(GMPGetterCallback<T>); }
virtual void Done(T*) = 0;
};
template<class T>
class GMPVideoGetterCallback
{
public:
GMPVideoGetterCallback() { MOZ_COUNT_CTOR(GMPVideoGetterCallback<T>); }
virtual ~GMPVideoGetterCallback() { MOZ_COUNT_DTOR(GMPVideoGetterCallback<T>); }
virtual void Done(T*, GMPVideoHost*) = 0;
};
typedef GMPGetterCallback<GMPDecryptorProxy> GetGMPDecryptorCallback;
typedef GMPGetterCallback<GMPAudioDecoderProxy> GetGMPAudioDecoderCallback;
typedef GMPVideoGetterCallback<GMPVideoDecoderProxy> GetGMPVideoDecoderCallback;
typedef GMPVideoGetterCallback<GMPVideoEncoderProxy> GetGMPVideoEncoderCallback;
%}
[ptr] native GMPVideoDecoderProxy(GMPVideoDecoderProxy);
[ptr] native GMPVideoEncoderProxy(GMPVideoEncoderProxy);
[ptr] native GMPVideoHost(GMPVideoHost);
[ptr] native TagArray(nsTArray<nsCString>);
[ptr] native GMPDecryptorProxy(GMPDecryptorProxy);
[ptr] native GMPAudioDecoderProxy(GMPAudioDecoderProxy);
native GetGMPDecryptorCallback(mozilla::UniquePtr<GetGMPDecryptorCallback>&&);
native GetGMPAudioDecoderCallback(mozilla::UniquePtr<GetGMPAudioDecoderCallback>&&);
native GetGMPVideoDecoderCallback(mozilla::UniquePtr<GetGMPVideoDecoderCallback>&&);
native GetGMPVideoEncoderCallback(mozilla::UniquePtr<GetGMPVideoEncoderCallback>&&);
[scriptable, uuid(fed4d2d8-87d8-42fe-a141-98c15b5f7a1e)]
[scriptable, uuid(11bb248b-7a4b-4f62-bebb-8211551d8c20)]
interface mozIGeckoMediaPluginService : nsISupports
{
@ -52,35 +73,63 @@ interface mozIGeckoMediaPluginService : nsISupports
* The array of tags should at least contain a codec tag, and optionally
* other tags such as for EME keysystem.
* Callable only on GMP thread.
* This is an asynchronous operation, the Done method of the callback object
* will be called on the GMP thread with the result (which might be null in
* the case of failure). This method always takes ownership of the callback
* object, but if this method returns an error then the Done method of the
* callback object will not be called at all.
*/
[noscript]
GMPVideoDecoderProxy getGMPVideoDecoder(in TagArray tags,
void getGMPVideoDecoder(in TagArray tags,
[optional] in ACString nodeId,
out GMPVideoHost outVideoHost);
in GetGMPVideoDecoderCallback callback);
/**
* Get a video encoder that supports the specified tags.
* The array of tags should at least contain a codec tag, and optionally
* other tags.
* Callable only on GMP thread.
* This is an asynchronous operation, the Done method of the callback object
* will be called on the GMP thread with the result (which might be null in
* the case of failure). This method always takes ownership of the callback
* object, but if this method returns an error then the Done method of the
* callback object will not be called at all.
*/
[noscript]
GMPVideoEncoderProxy getGMPVideoEncoder(in TagArray tags,
void getGMPVideoEncoder(in TagArray tags,
[optional] in ACString nodeId,
out GMPVideoHost outVideoHost);
in GetGMPVideoEncoderCallback callback);
// Returns an audio decoder that supports the specified tags.
// The array of tags should at least contain a codec tag, and optionally
// other tags such as for EME keysystem.
// Callable only on GMP thread.
GMPAudioDecoderProxy getGMPAudioDecoder(in TagArray tags,
[optional] in ACString nodeId);
/**
* Returns an audio decoder that supports the specified tags.
* The array of tags should at least contain a codec tag, and optionally
* other tags such as for EME keysystem.
* Callable only on GMP thread.
* This is an asynchronous operation, the Done method of the callback object
* will be called on the GMP thread with the result (which might be null in
* the case of failure). This method always takes ownership of the callback
* object, but if this method returns an error then the Done method of the
* callback object will not be called at all.
*/
[noscript]
void getGMPAudioDecoder(in TagArray tags,
[optional] in ACString nodeId,
in GetGMPAudioDecoderCallback callback);
// Returns a decryption session manager that supports the specified tags.
// The array of tags should at least contain a key system tag, and optionally
// other tags.
// Callable only on GMP thread.
GMPDecryptorProxy getGMPDecryptor(in TagArray tags, in ACString nodeId);
/**
* Returns a decryption session manager that supports the specified tags.
* The array of tags should at least contain a key system tag, and optionally
* other tags.
* Callable only on GMP thread.
* This is an asynchronous operation, the Done method of the callback object
* will be called on the GMP thread with the result (which might be null in
* the case of failure). This method always takes ownership of the callback
* object, but if this method returns an error then the Done method of the
* callback object will not be called at all.
*/
[noscript]
void getGMPDecryptor(in TagArray tags, in ACString nodeId,
in GetGMPDecryptorCallback callback);
/**
* Gets the NodeId for a (origin, urlbarOrigin, isInprivateBrowsing) tuple.

View File

@ -28,87 +28,234 @@ using namespace std;
using namespace mozilla;
using namespace mozilla::gmp;
class GMPTestMonitor
{
public:
GMPTestMonitor()
: mFinished(false)
{
}
void AwaitFinished()
{
MOZ_ASSERT(NS_IsMainThread());
while (!mFinished) {
NS_ProcessNextEvent(nullptr, true);
}
mFinished = false;
}
private:
void MarkFinished()
{
mFinished = true;
}
public:
void SetFinished()
{
NS_DispatchToMainThread(NS_NewNonOwningRunnableMethod(this,
&GMPTestMonitor::MarkFinished));
}
private:
bool mFinished;
};
struct GMPTestRunner
{
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GMPTestRunner)
void DoTest(void (GMPTestRunner::*aTestMethod)());
void RunTestGMPTestCodec();
void RunTestGMPCrossOrigin();
void DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&));
void RunTestGMPTestCodec1(GMPTestMonitor& aMonitor);
void RunTestGMPTestCodec2(GMPTestMonitor& aMonitor);
void RunTestGMPTestCodec3(GMPTestMonitor& aMonitor);
void RunTestGMPCrossOrigin1(GMPTestMonitor& aMonitor);
void RunTestGMPCrossOrigin2(GMPTestMonitor& aMonitor);
void RunTestGMPCrossOrigin3(GMPTestMonitor& aMonitor);
void RunTestGMPCrossOrigin4(GMPTestMonitor& aMonitor);
private:
~GMPTestRunner() { }
};
void
GMPTestRunner::RunTestGMPTestCodec()
template<class T, class Base,
nsresult (NS_STDCALL GeckoMediaPluginService::*Getter)(nsTArray<nsCString>*,
const nsACString&,
UniquePtr<Base>&&)>
class RunTestGMPVideoCodec : public Base
{
nsRefPtr<GeckoMediaPluginService> service =
GeckoMediaPluginService::GetGeckoMediaPluginService();
public:
virtual void Done(T* aGMP, GMPVideoHost* aHost)
{
EXPECT_TRUE(aGMP);
EXPECT_TRUE(aHost);
if (aGMP) {
aGMP->Close();
}
mMonitor.SetFinished();
}
GMPVideoHost* host = nullptr;
GMPVideoDecoderProxy* decoder = nullptr;
GMPVideoDecoderProxy* decoder2 = nullptr;
GMPVideoEncoderProxy* encoder = nullptr;
static void Run(GMPTestMonitor& aMonitor, const nsCString& aOrigin)
{
UniquePtr<GMPCallbackType> callback(new RunTestGMPVideoCodec(aMonitor));
Get(aOrigin, Move(callback));
}
protected:
typedef T GMPCodecType;
typedef Base GMPCallbackType;
explicit RunTestGMPVideoCodec(GMPTestMonitor& aMonitor)
: mMonitor(aMonitor)
{
}
static nsresult Get(const nsACString& aNodeId, UniquePtr<Base>&& aCallback)
{
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("o"), &host, &decoder2);
service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING(""), &host, &decoder);
nsRefPtr<GeckoMediaPluginService> service =
GeckoMediaPluginService::GetGeckoMediaPluginService();
return ((*service).*Getter)(&tags, aNodeId, Move(aCallback));
}
service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING(""), &host, &encoder);
protected:
GMPTestMonitor& mMonitor;
};
EXPECT_TRUE(host);
EXPECT_TRUE(decoder);
EXPECT_TRUE(decoder2);
EXPECT_TRUE(encoder);
typedef RunTestGMPVideoCodec<GMPVideoDecoderProxy,
GetGMPVideoDecoderCallback,
&GeckoMediaPluginService::GetGMPVideoDecoder>
RunTestGMPVideoDecoder;
typedef RunTestGMPVideoCodec<GMPVideoEncoderProxy,
GetGMPVideoEncoderCallback,
&GeckoMediaPluginService::GetGMPVideoEncoder>
RunTestGMPVideoEncoder;
if (decoder) decoder->Close();
if (decoder2) decoder2->Close();
if (encoder) encoder->Close();
void
GMPTestRunner::RunTestGMPTestCodec1(GMPTestMonitor& aMonitor)
{
RunTestGMPVideoDecoder::Run(aMonitor, NS_LITERAL_CSTRING("o"));
}
void
GMPTestRunner::RunTestGMPCrossOrigin()
GMPTestRunner::RunTestGMPTestCodec2(GMPTestMonitor& aMonitor)
{
nsRefPtr<GeckoMediaPluginService> service =
GeckoMediaPluginService::GetGeckoMediaPluginService();
RunTestGMPVideoDecoder::Run(aMonitor, NS_LITERAL_CSTRING(""));
}
GMPVideoHost* host = nullptr;
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
void
GMPTestRunner::RunTestGMPTestCodec3(GMPTestMonitor& aMonitor)
{
RunTestGMPVideoEncoder::Run(aMonitor, NS_LITERAL_CSTRING(""));
}
GMPVideoDecoderProxy* decoder1 = nullptr;
GMPVideoDecoderProxy* decoder2 = nullptr;
GMPVideoEncoderProxy* encoder1 = nullptr;
GMPVideoEncoderProxy* encoder2 = nullptr;
template<class Base>
class RunTestGMPCrossOrigin : public Base
{
public:
virtual void Done(typename Base::GMPCodecType* aGMP, GMPVideoHost* aHost)
{
EXPECT_TRUE(aGMP);
service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &decoder1);
service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("origin2"), &host, &decoder2);
EXPECT_TRUE(!!decoder1 && !!decoder2 &&
decoder1->ParentID() != decoder2->ParentID());
UniquePtr<typename Base::GMPCallbackType> callback(
new Step2(Base::mMonitor, aGMP, mShouldBeEqual));
nsresult rv = Base::Get(mOrigin2, Move(callback));
EXPECT_TRUE(NS_SUCCEEDED(rv));
if (NS_FAILED(rv)) {
Base::mMonitor.SetFinished();
}
}
service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &encoder1);
service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING("origin2"), &host, &encoder2);
EXPECT_TRUE(!!encoder1 && !!encoder2 &&
encoder1->ParentID() != encoder2->ParentID());
static void Run(GMPTestMonitor& aMonitor, const nsCString& aOrigin1,
const nsCString& aOrigin2)
{
UniquePtr<typename Base::GMPCallbackType> callback(
new RunTestGMPCrossOrigin<Base>(aMonitor, aOrigin1, aOrigin2));
nsresult rv = Base::Get(aOrigin1, Move(callback));
EXPECT_TRUE(NS_SUCCEEDED(rv));
if (NS_FAILED(rv)) {
aMonitor.SetFinished();
}
}
if (decoder2) decoder2->Close();
if (encoder2) encoder2->Close();
private:
RunTestGMPCrossOrigin(GMPTestMonitor& aMonitor, const nsCString& aOrigin1,
const nsCString& aOrigin2)
: Base(aMonitor),
mGMP(nullptr),
mOrigin2(aOrigin2),
mShouldBeEqual(aOrigin1.Equals(aOrigin2))
{
}
service->GetGMPVideoDecoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &decoder2);
EXPECT_TRUE(!!decoder1 && !!decoder2 &&
decoder1->ParentID() == decoder2->ParentID());
class Step2 : public Base
{
public:
Step2(GMPTestMonitor& aMonitor,
typename Base::GMPCodecType* aGMP,
bool aShouldBeEqual)
: Base(aMonitor),
mGMP(aGMP),
mShouldBeEqual(aShouldBeEqual)
{
}
virtual void Done(typename Base::GMPCodecType* aGMP, GMPVideoHost* aHost)
{
EXPECT_TRUE(aGMP);
if (aGMP) {
EXPECT_TRUE(mGMP &&
(mGMP->ParentID() == aGMP->ParentID()) == mShouldBeEqual);
}
if (mGMP) {
mGMP->Close();
}
Base::Done(aGMP, aHost);
}
service->GetGMPVideoEncoder(&tags, NS_LITERAL_CSTRING("origin1"), &host, &encoder2);
EXPECT_TRUE(!!encoder1 && !!encoder2 &&
encoder1->ParentID() == encoder2->ParentID());
private:
typename Base::GMPCodecType* mGMP;
bool mShouldBeEqual;
};
if (decoder1) decoder1->Close();
if (decoder2) decoder2->Close();
if (encoder1) encoder1->Close();
if (encoder2) encoder2->Close();
typename Base::GMPCodecType* mGMP;
nsCString mOrigin2;
bool mShouldBeEqual;
};
typedef RunTestGMPCrossOrigin<RunTestGMPVideoDecoder>
RunTestGMPVideoDecoderCrossOrigin;
typedef RunTestGMPCrossOrigin<RunTestGMPVideoEncoder>
RunTestGMPVideoEncoderCrossOrigin;
void
GMPTestRunner::RunTestGMPCrossOrigin1(GMPTestMonitor& aMonitor)
{
RunTestGMPVideoDecoderCrossOrigin::Run(
aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin2"));
}
void
GMPTestRunner::RunTestGMPCrossOrigin2(GMPTestMonitor& aMonitor)
{
RunTestGMPVideoEncoderCrossOrigin::Run(
aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin2"));
}
void
GMPTestRunner::RunTestGMPCrossOrigin3(GMPTestMonitor& aMonitor)
{
RunTestGMPVideoDecoderCrossOrigin::Run(
aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin1"));
}
void
GMPTestRunner::RunTestGMPCrossOrigin4(GMPTestMonitor& aMonitor)
{
RunTestGMPVideoEncoderCrossOrigin::Run(
aMonitor, NS_LITERAL_CSTRING("origin1"), NS_LITERAL_CSTRING("origin1"));
}
static already_AddRefed<nsIThread>
@ -438,9 +585,73 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
SetFinished();
}
class CreateDecryptorDone : public GetGMPDecryptorCallback
{
public:
CreateDecryptorDone(GMPStorageTest* aRunner, nsIRunnable* aContinuation)
: mRunner(aRunner),
mContinuation(aContinuation)
{
}
virtual void Done(GMPDecryptorProxy* aDecryptor) override
{
mRunner->mDecryptor = aDecryptor;
EXPECT_TRUE(!!mRunner->mDecryptor);
if (mRunner->mDecryptor) {
mRunner->mDecryptor->Init(mRunner);
}
nsCOMPtr<nsIThread> thread(GetGMPThread());
thread->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
}
private:
nsRefPtr<GMPStorageTest> mRunner;
nsCOMPtr<nsIRunnable> mContinuation;
};
void CreateDecryptor(const nsAString& aOrigin,
const nsAString& aTopLevelOrigin,
bool aInPBMode) {
bool aInPBMode,
const nsCString& aUpdate)
{
nsTArray<nsCString> updates;
updates.AppendElement(aUpdate);
CreateDecryptor(aOrigin, aTopLevelOrigin, aInPBMode, Move(updates));
}
class Updates : public nsRunnable
{
public:
Updates(GMPStorageTest* aRunner, nsTArray<nsCString>&& aUpdates)
: mRunner(aRunner),
mUpdates(aUpdates)
{
}
NS_IMETHOD Run()
{
for (auto& update : mUpdates) {
mRunner->Update(update);
}
return NS_OK;
}
private:
nsRefPtr<GMPStorageTest> mRunner;
nsTArray<nsCString> mUpdates;
};
void CreateDecryptor(const nsAString& aOrigin,
const nsAString& aTopLevelOrigin,
bool aInPBMode,
nsTArray<nsCString>&& aUpdates) {
nsCOMPtr<nsIRunnable> updates(new Updates(this, Move(aUpdates)));
CreateDecryptor(aOrigin, aTopLevelOrigin, aInPBMode, updates);
}
void CreateDecryptor(const nsAString& aOrigin,
const nsAString& aTopLevelOrigin,
bool aInPBMode,
nsIRunnable* aContinuation) {
nsRefPtr<GeckoMediaPluginService> service =
GeckoMediaPluginService::GetGeckoMediaPluginService();
EXPECT_TRUE(service);
@ -451,13 +662,11 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("fake"));
nsresult rv = service->GetGMPDecryptor(&tags, mNodeId, &mDecryptor);
UniquePtr<GetGMPDecryptorCallback> callback(
new CreateDecryptorDone(this, aContinuation));
nsresult rv =
service->GetGMPDecryptor(&tags, mNodeId, Move(callback));
EXPECT_TRUE(NS_SUCCEEDED(rv));
EXPECT_TRUE(!!mDecryptor);
if (mDecryptor) {
mDecryptor->Init(this);
}
}
void TestBasicStorage() {
@ -467,16 +676,16 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
nsRefPtr<GeckoMediaPluginService> service =
GeckoMediaPluginService::GetGeckoMediaPluginService();
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false);
// Send a message to the fake GMP for it to run its own tests internally.
// It sends us a "test-storage complete" message when its passed, or
// some other message if its tests fail.
Expect(NS_LITERAL_CSTRING("test-storage complete"),
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
Update(NS_LITERAL_CSTRING("test-storage"));
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
}
/**
@ -490,28 +699,28 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
EXPECT_TRUE(IsGMPStorageIsEmpty());
// Generate storage data for some site.
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &GMPStorageTest::TestForgetThisSite_AnotherSite);
Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
Update(NS_LITERAL_CSTRING("test-storage"));
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
}
void TestForgetThisSite_AnotherSite() {
Shutdown();
// Generate storage data for another site.
CreateDecryptor(NS_LITERAL_STRING("example3.com"),
NS_LITERAL_STRING("example4.com"),
false);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &GMPStorageTest::TestForgetThisSite_CollectSiteInfo);
Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
Update(NS_LITERAL_CSTRING("test-storage"));
CreateDecryptor(NS_LITERAL_STRING("example3.com"),
NS_LITERAL_STRING("example4.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
}
struct NodeInfo {
@ -623,16 +832,15 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
EXPECT_TRUE(IsGMPStorageIsEmpty());
// Generate storage data for some site.
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &GMPStorageTest::TestClearRecentHistory1_Clear);
Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
Update(NS_LITERAL_CSTRING("test-storage"));
}
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
}
/**
* 1. Generate some storage data.
@ -646,15 +854,14 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
EXPECT_TRUE(IsGMPStorageIsEmpty());
// Generate storage data for some site.
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &GMPStorageTest::TestClearRecentHistory2_Clear);
Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
Update(NS_LITERAL_CSTRING("test-storage"));
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
}
/**
@ -669,15 +876,14 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
EXPECT_TRUE(IsGMPStorageIsEmpty());
// Generate storage data for some site.
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false);
nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
this, &GMPStorageTest::TestClearRecentHistory3_Clear);
Expect(NS_LITERAL_CSTRING("test-storage complete"), r);
Update(NS_LITERAL_CSTRING("test-storage"));
CreateDecryptor(NS_LITERAL_STRING("example1.com"),
NS_LITERAL_STRING("example2.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
}
class MaxMTimeFinder {
@ -775,12 +981,6 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
void TestCrossOriginStorage() {
EXPECT_TRUE(!mDecryptor);
// Open decryptor on one, origin, write a record, and test that that
// record can't be read on another origin.
CreateDecryptor(NS_LITERAL_STRING("example3.com"),
NS_LITERAL_STRING("example4.com"),
false);
// Send the decryptor the message "store recordid $time"
// Wait for the decrytor to send us "stored recordid $time"
auto t = time(0);
@ -791,7 +991,13 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
nsCString update("store crossOriginTestRecordId ");
update.AppendInt((int64_t)t);
Update(update);
// Open decryptor on one, origin, write a record, and test that that
// record can't be read on another origin.
CreateDecryptor(NS_LITERAL_STRING("example3.com"),
NS_LITERAL_STRING("example4.com"),
false,
update);
}
void TestCrossOriginStorage_RecordStoredContinuation() {
@ -799,87 +1005,102 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
// and try to read the record.
Shutdown();
CreateDecryptor(NS_LITERAL_STRING("example5.com"),
NS_LITERAL_STRING("example6.com"),
false);
Expect(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId succeeded (length 0 bytes)"),
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
Update(NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId"));
CreateDecryptor(NS_LITERAL_STRING("example5.com"),
NS_LITERAL_STRING("example6.com"),
false,
NS_LITERAL_CSTRING("retrieve crossOriginTestRecordId"));
}
void TestPBStorage() {
// Open decryptor on one, origin, write a record, close decryptor,
// open another, and test that record can be read, close decryptor,
// then send pb-last-context-closed notification, then open decryptor
// and check that it can't read that data; it should have been purged.
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
NS_LITERAL_STRING("pb2.com"),
true);
// Send the decryptor the message "store recordid $time"
// Wait for the decrytor to send us "stored recordid $time"
nsCString response("stored pbdata test-pb-data");
Expect(response, NS_NewRunnableMethod(this,
&GMPStorageTest::TestPBStorage_RecordStoredContinuation));
nsCString update("store pbdata test-pb-data");
Update(update);
// Open decryptor on one, origin, write a record, close decryptor,
// open another, and test that record can be read, close decryptor,
// then send pb-last-context-closed notification, then open decryptor
// and check that it can't read that data; it should have been purged.
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
NS_LITERAL_STRING("pb2.com"),
true,
NS_LITERAL_CSTRING("store pbdata test-pb-data"));
}
void TestPBStorage_RecordStoredContinuation() {
Shutdown();
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
NS_LITERAL_STRING("pb2.com"),
true);
Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 12 bytes)"),
NS_NewRunnableMethod(this,
&GMPStorageTest::TestPBStorage_RecordRetrievedContinuation));
Update(NS_LITERAL_CSTRING("retrieve pbdata"));
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
NS_LITERAL_STRING("pb2.com"),
true,
NS_LITERAL_CSTRING("retrieve pbdata"));
}
void TestPBStorage_RecordRetrievedContinuation() {
Shutdown();
SimulatePBModeExit();
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
NS_LITERAL_STRING("pb2.com"),
true);
Expect(NS_LITERAL_CSTRING("retrieve pbdata succeeded (length 0 bytes)"),
NS_NewRunnableMethod(this,
&GMPStorageTest::SetFinished));
Update(NS_LITERAL_CSTRING("retrieve pbdata"));
CreateDecryptor(NS_LITERAL_STRING("pb1.com"),
NS_LITERAL_STRING("pb2.com"),
true,
NS_LITERAL_CSTRING("retrieve pbdata"));
}
void NextAsyncShutdownTimeoutTest(nsIRunnable* aContinuation)
{
if (mDecryptor) {
Update(NS_LITERAL_CSTRING("shutdown-mode timeout"));
Shutdown();
}
nsCOMPtr<nsIThread> thread(GetGMPThread());
thread->Dispatch(aContinuation, NS_DISPATCH_NORMAL);
}
void CreateAsyncShutdownTimeoutGMP(const nsAString& aOrigin1,
const nsAString& aOrigin2) {
CreateDecryptor(aOrigin1, aOrigin2, false);
Update(NS_LITERAL_CSTRING("shutdown-mode timeout"));
Shutdown();
const nsAString& aOrigin2,
void (GMPStorageTest::*aCallback)()) {
nsCOMPtr<nsIRunnable> continuation(
NS_NewRunnableMethodWithArg<nsCOMPtr<nsIRunnable>>(
this,
&GMPStorageTest::NextAsyncShutdownTimeoutTest,
NS_NewRunnableMethod(this, aCallback)));
CreateDecryptor(aOrigin1, aOrigin2, false, continuation);
}
void TestAsyncShutdownTimeout() {
// Create decryptors that timeout in their async shutdown.
// If the gtest hangs on shutdown, test fails!
CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example7.com"),
NS_LITERAL_STRING("example8.com"));
NS_LITERAL_STRING("example8.com"),
&GMPStorageTest::TestAsyncShutdownTimeout2);
};
void TestAsyncShutdownTimeout2() {
CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example9.com"),
NS_LITERAL_STRING("example10.com"));
NS_LITERAL_STRING("example10.com"),
&GMPStorageTest::TestAsyncShutdownTimeout3);
};
void TestAsyncShutdownTimeout3() {
CreateAsyncShutdownTimeoutGMP(NS_LITERAL_STRING("example11.com"),
NS_LITERAL_STRING("example12.com"));
SetFinished();
NS_LITERAL_STRING("example12.com"),
&GMPStorageTest::SetFinished);
};
void TestAsyncShutdownStorage() {
// Test that a GMP can write to storage during shutdown, and retrieve
// that written data in a subsequent session.
CreateDecryptor(NS_LITERAL_STRING("example13.com"),
NS_LITERAL_STRING("example14.com"),
false);
// Instruct the GMP to write a token (the current timestamp, so it's
// unique) during async shutdown, then shutdown the plugin, re-create
// it, and check that the token was successfully stored.
@ -896,7 +1117,12 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
Expect(response, NS_NewRunnableMethodWithArg<nsCString>(this,
&GMPStorageTest::TestAsyncShutdownStorage_ReceivedShutdownToken, token));
Update(update);
// Test that a GMP can write to storage during shutdown, and retrieve
// that written data in a subsequent session.
CreateDecryptor(NS_LITERAL_STRING("example13.com"),
NS_LITERAL_STRING("example14.com"),
false,
update);
}
void TestAsyncShutdownStorage_ReceivedShutdownToken(const nsCString& aToken) {
@ -907,37 +1133,39 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
void TestAsyncShutdownStorage_AsyncShutdownComplete(const nsCString& aToken) {
// Create a new instance of the plugin, retrieve the token written
// during shutdown and verify it is correct.
CreateDecryptor(NS_LITERAL_STRING("example13.com"),
NS_LITERAL_STRING("example14.com"),
false);
nsCString response("retrieved shutdown-token ");
response.Append(aToken);
Expect(response,
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
Update(NS_LITERAL_CSTRING("retrieve-shutdown-token"));
CreateDecryptor(NS_LITERAL_STRING("example13.com"),
NS_LITERAL_STRING("example14.com"),
false,
NS_LITERAL_CSTRING("retrieve-shutdown-token"));
}
#if defined(XP_WIN)
void TestOutputProtection() {
Shutdown();
CreateDecryptor(NS_LITERAL_STRING("example15.com"),
NS_LITERAL_STRING("example16.com"),
false);
Expect(NS_LITERAL_CSTRING("OP tests completed"),
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
Update(NS_LITERAL_CSTRING("test-op-apis"));
CreateDecryptor(NS_LITERAL_STRING("example15.com"),
NS_LITERAL_STRING("example16.com"),
false,
NS_LITERAL_CSTRING("test-op-apis"));
}
#endif
void TestPluginVoucher() {
CreateDecryptor(NS_LITERAL_STRING("example17.com"),
NS_LITERAL_STRING("example18.com"),
false);
Expect(NS_LITERAL_CSTRING("retrieved plugin-voucher: gmp-fake placeholder voucher"),
NS_NewRunnableMethod(this, &GMPStorageTest::SetFinished));
Update(NS_LITERAL_CSTRING("retrieve-plugin-voucher"));
CreateDecryptor(NS_LITERAL_STRING("example17.com"),
NS_LITERAL_STRING("example18.com"),
false,
NS_LITERAL_CSTRING("retrieve-plugin-voucher"));
}
void TestGetRecordNamesInMemoryStorage() {
@ -954,12 +1182,9 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
}
void TestGetRecordNames(bool aPrivateBrowsing) {
CreateDecryptor(NS_LITERAL_STRING("foo.com"),
NS_LITERAL_STRING("bar.com"),
aPrivateBrowsing);
// Create a number of records of different names.
const uint32_t num = 100;
nsTArray<nsCString> updates(num);
for (uint32_t i = 0; i < num; i++) {
nsAutoCString response;
response.AppendLiteral("stored data");
@ -973,7 +1198,7 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
mRecordNames.AppendLiteral("data");
AppendIntPadded(mRecordNames, i);
nsAutoCString update;
nsCString& update = *updates.AppendElement();
update.AppendLiteral("store data");
AppendIntPadded(update, i);
update.AppendLiteral(" test-data");
@ -985,8 +1210,12 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
NS_NewRunnableMethod(this, &GMPStorageTest::TestGetRecordNames_QueryNames);
}
Expect(response, continuation);
Update(update);
}
CreateDecryptor(NS_LITERAL_STRING("foo.com"),
NS_LITERAL_STRING("bar.com"),
aPrivateBrowsing,
Move(updates));
}
void TestGetRecordNames_QueryNames() {
@ -1026,10 +1255,6 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
MOZ_ASSERT(longRecordName.Length() < GMP_MAX_RECORD_NAME_SIZE);
MOZ_ASSERT(longRecordName.Length() > 260); // Windows MAX_PATH
CreateDecryptor(NS_LITERAL_STRING("fuz.com"),
NS_LITERAL_STRING("baz.com"),
false);
nsCString response("stored ");
response.Append(longRecordName);
response.AppendLiteral(" ");
@ -1040,7 +1265,10 @@ class GMPStorageTest : public GMPDecryptorProxyCallback
update.Append(longRecordName);
update.AppendLiteral(" ");
update.Append(data);
Update(update);
CreateDecryptor(NS_LITERAL_STRING("fuz.com"),
NS_LITERAL_STRING("baz.com"),
false,
update);
}
void Expect(const nsCString& aMessage, nsIRunnable* aContinuation) {
@ -1147,24 +1375,31 @@ private:
};
void
GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)())
GMPTestRunner::DoTest(void (GMPTestRunner::*aTestMethod)(GMPTestMonitor&))
{
nsRefPtr<GeckoMediaPluginService> service =
GeckoMediaPluginService::GetGeckoMediaPluginService();
nsCOMPtr<nsIThread> thread;
nsCOMPtr<nsIThread> thread(GetGMPThread());
service->GetThread(getter_AddRefs(thread));
thread->Dispatch(NS_NewRunnableMethod(this, aTestMethod), NS_DISPATCH_SYNC);
GMPTestMonitor monitor;
thread->Dispatch(NS_NewRunnableMethodWithArg<GMPTestMonitor&>(this,
aTestMethod,
monitor),
NS_DISPATCH_NORMAL);
monitor.AwaitFinished();
}
TEST(GeckoMediaPlugins, GMPTestCodec) {
nsRefPtr<GMPTestRunner> runner = new GMPTestRunner();
runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec);
runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec1);
runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec2);
runner->DoTest(&GMPTestRunner::RunTestGMPTestCodec3);
}
TEST(GeckoMediaPlugins, GMPCrossOrigin) {
nsRefPtr<GMPTestRunner> runner = new GMPTestRunner();
runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin);
runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin1);
runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin2);
runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin3);
runner->DoTest(&GMPTestRunner::RunTestGMPCrossOrigin4);
}
TEST(GeckoMediaPlugins, GMPStorageGetNodeId) {

View File

@ -151,36 +151,57 @@ WebrtcGmpVideoEncoder::InitEncode(const webrtc::VideoCodec* aCodecSettings,
}
}
int32_t ret;
mGMPThread->Dispatch(WrapRunnableRet(this,
nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
MOZ_ASSERT(currentThread != mGMPThread);
nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
mGMPThread->Dispatch(WrapRunnable(this,
&WebrtcGmpVideoEncoder::InitEncode_g,
aCodecSettings,
aNumberOfCores,
aMaxPayloadSize,
&ret),
NS_DISPATCH_SYNC);
initDone),
NS_DISPATCH_NORMAL);
return ret;
while (!initDone->IsDone()) {
NS_ProcessNextEvent(currentThread, true);
}
return initDone->Result();
}
int32_t
void
WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize)
uint32_t aMaxPayloadSize,
InitDoneRunnable* aInitDone)
{
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoEncoder(&tags,
UniquePtr<GetGMPVideoEncoderCallback> callback(
new InitDoneCallback(this, aInitDone, aCodecSettings, aMaxPayloadSize));
nsresult rv = mMPS->GetGMPVideoEncoder(&tags,
NS_LITERAL_CSTRING(""),
&mHost,
&mGMP)))) {
Move(callback));
if (NS_WARN_IF(NS_FAILED(rv))) {
mMPS = nullptr;
mGMP = nullptr;
mGMPThread = nullptr;
mHost = nullptr;
return WEBRTC_VIDEO_CODEC_ERROR;
aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
return;
}
}
int32_t
WebrtcGmpVideoEncoder::GmpInitDone(GMPVideoEncoderProxy* aGMP,
GMPVideoHost* aHost,
const webrtc::VideoCodec* aCodecSettings,
uint32_t aMaxPayloadSize)
{
mGMP = aGMP;
mHost = aHost;
if (!mGMP || !mHost) {
return WEBRTC_VIDEO_CODEC_ERROR;
}
@ -189,8 +210,6 @@ WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
memset(&mCodecParams, 0, sizeof(mCodecParams));
mCodecParams.mGMPApiVersion = 33;
mCodecParams.mWidth = aCodecSettings->width;
mCodecParams.mHeight = aCodecSettings->height;
mCodecParams.mStartBitrate = aCodecSettings->startBitrate;
mCodecParams.mMinBitrate = aCodecSettings->minBitrate;
mCodecParams.mMaxBitrate = aCodecSettings->maxBitrate;
@ -206,6 +225,14 @@ WebrtcGmpVideoEncoder::InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
mCodecParams.mMode = kGMPRealtimeVideo;
}
return InitEncoderForSize(aCodecSettings->width, aCodecSettings->height);
}
int32_t
WebrtcGmpVideoEncoder::InitEncoderForSize(unsigned short aWidth, unsigned short aHeight)
{
mCodecParams.mWidth = aWidth;
mCodecParams.mHeight = aHeight;
// Pass dummy codecSpecific data for now...
nsTArray<uint8_t> codecSpecific;
@ -223,8 +250,37 @@ WebrtcGmpVideoEncoder::Encode(const webrtc::I420VideoFrame& aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes)
{
MOZ_ASSERT(mHost);
if (!mGMP) {
// destroyed via Terminate()
return WEBRTC_VIDEO_CODEC_ERROR;
}
MOZ_ASSERT(aInputImage.width() >= 0 && aInputImage.height() >= 0);
if (static_cast<uint32_t>(aInputImage.width()) != mCodecParams.mWidth ||
static_cast<uint32_t>(aInputImage.height()) != mCodecParams.mHeight) {
LOGD(("GMP Encode: resolution change from %ux%u to %dx%d",
mCodecParams.mWidth, mCodecParams.mHeight, aInputImage.width(), aInputImage.height()));
nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
nsCOMPtr<nsIRunnable> task(
WrapRunnable(this,
&WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange,
&aInputImage,
initDone));
mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
while (!initDone->IsDone()) {
NS_ProcessNextEvent(currentThread, true);
}
if (initDone->Result() != WEBRTC_VIDEO_CODEC_OK) {
return initDone->Result();
}
}
int32_t ret;
MOZ_ASSERT(mGMPThread);
mozilla::SyncRunnable::DispatchToThread(mGMPThread,
WrapRunnableRet(this,
&WebrtcGmpVideoEncoder::Encode_g,
@ -236,25 +292,17 @@ WebrtcGmpVideoEncoder::Encode(const webrtc::I420VideoFrame& aInputImage,
return ret;
}
int32_t
WebrtcGmpVideoEncoder::Encode_g(const webrtc::I420VideoFrame* aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes)
void
WebrtcGmpVideoEncoder::RegetEncoderForResolutionChange(const webrtc::I420VideoFrame* aInputImage,
InitDoneRunnable* aInitDone)
{
MOZ_ASSERT(mHost);
if (!mGMP) {
// destroyed via Terminate()
return WEBRTC_VIDEO_CODEC_ERROR;
}
MOZ_ASSERT(aInputImage->width() >= 0 && aInputImage->height() >= 0);
if (static_cast<uint32_t>(aInputImage->width()) != mCodecParams.mWidth ||
static_cast<uint32_t>(aInputImage->height()) != mCodecParams.mHeight) {
LOGD(("GMP Encode: resolution change from %ux%u to %dx%d",
mCodecParams.mWidth, mCodecParams.mHeight, aInputImage->width(), aInputImage->height()));
mGMP->Close();
UniquePtr<GetGMPVideoEncoderCallback> callback(
new InitDoneForResolutionChangeCallback(this, aInitDone,
aInputImage->width(),
aInputImage->height()));
// OpenH264 codec (at least) can't handle dynamic input resolution changes
// re-init the plugin when the resolution changes
// XXX allow codec to indicate it doesn't need re-init!
@ -262,23 +310,20 @@ WebrtcGmpVideoEncoder::Encode_g(const webrtc::I420VideoFrame* aInputImage,
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoEncoder(&tags,
NS_LITERAL_CSTRING(""),
&mHost,
&mGMP)))) {
Move(callback))))) {
mGMP = nullptr;
mHost = nullptr;
return WEBRTC_VIDEO_CODEC_ERROR;
aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
}
}
mCodecParams.mWidth = aInputImage->width();
mCodecParams.mHeight = aInputImage->height();
// Pass dummy codecSpecific data for now...
nsTArray<uint8_t> codecSpecific;
GMPErr err = mGMP->InitEncode(mCodecParams, codecSpecific, this, 1, mMaxPayloadSize);
if (err != GMPNoErr) {
return WEBRTC_VIDEO_CODEC_ERROR;
}
}
int32_t
WebrtcGmpVideoEncoder::Encode_g(const webrtc::I420VideoFrame* aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes)
{
MOZ_ASSERT(mHost);
MOZ_ASSERT(mGMP);
GMPVideoFrame* ftmp = nullptr;
GMPErr err = mHost->CreateFrame(kGMPI420VideoFrame, &ftmp);
@ -566,33 +611,48 @@ WebrtcGmpVideoDecoder::InitDecode(const webrtc::VideoCodec* aCodecSettings,
}
}
int32_t ret;
mGMPThread->Dispatch(WrapRunnableRet(this,
nsRefPtr<InitDoneRunnable> initDone(new InitDoneRunnable());
mGMPThread->Dispatch(WrapRunnable(this,
&WebrtcGmpVideoDecoder::InitDecode_g,
aCodecSettings,
aNumberOfCores,
&ret),
NS_DISPATCH_SYNC);
initDone.get()),
NS_DISPATCH_NORMAL);
return ret;
nsCOMPtr<nsIThread> currentThread(do_GetCurrentThread());
while (!initDone->IsDone()) {
NS_ProcessNextEvent(currentThread, true);
}
return initDone->Result();
}
int32_t
void
WebrtcGmpVideoDecoder::InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores)
int32_t aNumberOfCores,
InitDoneRunnable* aInitDone)
{
nsTArray<nsCString> tags;
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
UniquePtr<GetGMPVideoDecoderCallback> callback(
new InitDoneCallback(this, aInitDone));
if (NS_WARN_IF(NS_FAILED(mMPS->GetGMPVideoDecoder(&tags,
NS_LITERAL_CSTRING(""),
&mHost,
&mGMP)))) {
Move(callback))))) {
mMPS = nullptr;
mGMP = nullptr;
mGMPThread = nullptr;
mHost = nullptr;
return WEBRTC_VIDEO_CODEC_ERROR;
aInitDone->Dispatch(WEBRTC_VIDEO_CODEC_ERROR);
}
}
int32_t
WebrtcGmpVideoDecoder::GmpInitDone(GMPVideoDecoderProxy* aGMP,
GMPVideoHost* aHost)
{
mGMP = aGMP;
mHost = aHost;
mMPS = nullptr;
if (!mGMP || !mHost) {

View File

@ -21,6 +21,7 @@
#include <queue>
#include "nsThreadUtils.h"
#include "mozilla/Monitor.h"
#include "mozilla/Mutex.h"
#include "mozIGeckoMediaPluginService.h"
@ -77,13 +78,131 @@ public:
}
private:
virtual int32_t InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize);
class InitDoneRunnable : public nsRunnable
{
public:
InitDoneRunnable()
: mInitDone(false),
mResult(WEBRTC_VIDEO_CODEC_OK),
mThread(do_GetCurrentThread())
{
}
virtual int32_t Encode_g(const webrtc::I420VideoFrame* aInputImage,
NS_IMETHOD Run()
{
MOZ_ASSERT(mThread == nsCOMPtr<nsIThread>(do_GetCurrentThread()));
mInitDone = true;
return NS_OK;
}
void Dispatch(int32_t aResult)
{
mResult = aResult;
mThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
bool IsDone()
{
MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
return mInitDone;
}
int32_t Result()
{
return mResult;
}
private:
bool mInitDone;
int32_t mResult;
nsCOMPtr<nsIThread> mThread;
};
void InitEncode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
uint32_t aMaxPayloadSize,
InitDoneRunnable* aInitDone);
int32_t GmpInitDone(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost,
const webrtc::VideoCodec* aCodecSettings,
uint32_t aMaxPayloadSize);
int32_t InitEncoderForSize(unsigned short aWidth, unsigned short aHeight);
class InitDoneCallback : public GetGMPVideoEncoderCallback
{
public:
InitDoneCallback(WebrtcGmpVideoEncoder* aEncoder,
InitDoneRunnable* aInitDone,
const webrtc::VideoCodec* aCodecSettings,
uint32_t aMaxPayloadSize)
: mEncoder(aEncoder),
mInitDone(aInitDone),
mCodecSettings(aCodecSettings),
mMaxPayloadSize(aMaxPayloadSize)
{
}
virtual void Done(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost) override
{
mEncoder->mGMP = aGMP;
mEncoder->mHost = aHost;
int32_t result;
if (aGMP || aHost) {
result = mEncoder->GmpInitDone(aGMP, aHost, mCodecSettings,
mMaxPayloadSize);
} else {
result = WEBRTC_VIDEO_CODEC_ERROR;
}
mInitDone->Dispatch(result);
}
private:
WebrtcGmpVideoEncoder* mEncoder;
nsRefPtr<InitDoneRunnable> mInitDone;
const webrtc::VideoCodec* mCodecSettings;
uint32_t mMaxPayloadSize;
};
int32_t Encode_g(const webrtc::I420VideoFrame* aInputImage,
const webrtc::CodecSpecificInfo* aCodecSpecificInfo,
const std::vector<webrtc::VideoFrameType>* aFrameTypes);
void RegetEncoderForResolutionChange(const webrtc::I420VideoFrame* aInputImage,
InitDoneRunnable* aInitDone);
class InitDoneForResolutionChangeCallback : public GetGMPVideoEncoderCallback
{
public:
InitDoneForResolutionChangeCallback(WebrtcGmpVideoEncoder* aEncoder,
InitDoneRunnable* aInitDone,
uint32_t aWidth,
uint32_t aHeight)
: mEncoder(aEncoder),
mInitDone(aInitDone),
mWidth(aWidth),
mHeight(aHeight)
{
}
virtual void Done(GMPVideoEncoderProxy* aGMP, GMPVideoHost* aHost) override
{
mEncoder->mGMP = aGMP;
mEncoder->mHost = aHost;
int32_t result;
if (aGMP && aHost) {
result = mEncoder->InitEncoderForSize(mWidth, mHeight);
} else {
result = WEBRTC_VIDEO_CODEC_ERROR;
}
mInitDone->Dispatch(result);
}
private:
WebrtcGmpVideoEncoder* mEncoder;
nsRefPtr<InitDoneRunnable> mInitDone;
uint32_t mWidth;
uint32_t mHeight;
};
virtual int32_t SetRates_g(uint32_t aNewBitRate,
uint32_t aFrameRate);
@ -151,8 +270,72 @@ public:
}
private:
virtual int32_t InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores);
class InitDoneRunnable : public nsRunnable
{
public:
InitDoneRunnable()
: mInitDone(false),
mResult(WEBRTC_VIDEO_CODEC_OK),
mThread(do_GetCurrentThread())
{
}
NS_IMETHOD Run()
{
MOZ_ASSERT(mThread == nsCOMPtr<nsIThread>(do_GetCurrentThread()));
mInitDone = true;
return NS_OK;
}
void Dispatch(int32_t aResult)
{
mResult = aResult;
mThread->Dispatch(this, NS_DISPATCH_NORMAL);
}
bool IsDone()
{
MOZ_ASSERT(nsCOMPtr<nsIThread>(do_GetCurrentThread()) == mThread);
return mInitDone;
}
int32_t Result()
{
return mResult;
}
private:
bool mInitDone;
int32_t mResult;
nsCOMPtr<nsIThread> mThread;
};
void InitDecode_g(const webrtc::VideoCodec* aCodecSettings,
int32_t aNumberOfCores,
InitDoneRunnable* aInitDone);
int32_t GmpInitDone(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost);
class InitDoneCallback : public GetGMPVideoDecoderCallback
{
public:
explicit InitDoneCallback(WebrtcGmpVideoDecoder* aDecoder,
InitDoneRunnable* aInitDone)
: mDecoder(aDecoder),
mInitDone(aInitDone)
{
}
virtual void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
{
int32_t result = mDecoder->GmpInitDone(aGMP, aHost);
mInitDone->Dispatch(result);
}
private:
WebrtcGmpVideoDecoder* mDecoder;
nsRefPtr<InitDoneRunnable> mInitDone;
};
virtual int32_t Decode_g(const webrtc::EncodedImage& aInputImage,
bool aMissingFrames,