mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1128794 - Refactor EME decoders on top of GMP decoders. r=cpearce
This commit is contained in:
parent
8feb25098d
commit
56e1a18b92
@ -5,339 +5,33 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "EMEAudioDecoder.h"
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
#include "mozilla/EMELog.h"
|
||||
#include "gmp-audio-host.h"
|
||||
#include "gmp-audio-decode.h"
|
||||
#include "gmp-audio-samples.h"
|
||||
#include "GMPAudioHost.h"
|
||||
#include "GMPAudioDecoderProxy.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "prsystem.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
EMEAudioDecoder::EMEAudioDecoder(CDMProxy* aProxy,
|
||||
const AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
: mAudioRate(0)
|
||||
, mAudioBytesPerSample(0)
|
||||
, mAudioChannels(0)
|
||||
, mMustRecaptureAudioPosition(true)
|
||||
, mAudioFrameSum(0)
|
||||
, mAudioFrameOffset(0)
|
||||
, mStreamOffset(0)
|
||||
, mProxy(aProxy)
|
||||
, mGMP(nullptr)
|
||||
, mConfig(aConfig)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mCallback(aCallback)
|
||||
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, mTaskQueue, mProxy))
|
||||
, mMonitor("EMEAudioDecoder")
|
||||
, mFlushComplete(false)
|
||||
#ifdef DEBUG
|
||||
, mIsShutdown(false)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
EMEAudioDecoder::~EMEAudioDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
mMPS = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
||||
MOZ_ASSERT(mMPS);
|
||||
|
||||
nsresult rv = mMPS->GetThread(getter_AddRefs(mGMPThread));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsRefPtr<InitTask> task(new InitTask(this));
|
||||
rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(task->mResult, task->mResult);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
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);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EMEAudioDecoder::Flush()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mFlushComplete = false;
|
||||
}
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethod(this, &EMEAudioDecoder::GmpFlush);
|
||||
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
while (!mFlushComplete) {
|
||||
mon.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EMEAudioDecoder::Drain()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethod(this, &EMEAudioDecoder::GmpDrain);
|
||||
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void
|
||||
EMEAudioDecoder::Decoded(const nsTArray<int16_t>& aPCM,
|
||||
uint64_t aTimeStamp,
|
||||
uint32_t aChannels,
|
||||
uint32_t aRate)
|
||||
EMEAudioCallbackAdapter::Error(GMPErr aErr)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
if (aRate == 0 || aChannels == 0) {
|
||||
NS_WARNING("Invalid rate or num channels returned on GMP audio samples");
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
size_t numFrames = aPCM.Length() / aChannels;
|
||||
MOZ_ASSERT((aPCM.Length() % aChannels) == 0);
|
||||
nsAutoArrayPtr<AudioDataValue> audioData(new AudioDataValue[aPCM.Length()]);
|
||||
|
||||
for (size_t i = 0; i < aPCM.Length(); ++i) {
|
||||
audioData[i] = AudioSampleToFloat(aPCM[i]);
|
||||
}
|
||||
|
||||
if (mMustRecaptureAudioPosition) {
|
||||
mAudioFrameSum = 0;
|
||||
auto timestamp = UsecsToFrames(aTimeStamp, aRate);
|
||||
if (!timestamp.isValid()) {
|
||||
NS_WARNING("Invalid timestamp");
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
mAudioFrameOffset = timestamp.value();
|
||||
MOZ_ASSERT(mAudioFrameOffset >= 0);
|
||||
mMustRecaptureAudioPosition = false;
|
||||
}
|
||||
|
||||
auto timestamp = FramesToUsecs(mAudioFrameOffset + mAudioFrameSum, aRate);
|
||||
if (!timestamp.isValid()) {
|
||||
NS_WARNING("Invalid timestamp on audio samples");
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
mAudioFrameSum += numFrames;
|
||||
|
||||
auto duration = FramesToUsecs(numFrames, aRate);
|
||||
if (!duration.isValid()) {
|
||||
NS_WARNING("Invalid duration on audio samples");
|
||||
mCallback->Error();
|
||||
return;
|
||||
}
|
||||
|
||||
nsRefPtr<AudioData> audio(new AudioData(mStreamOffset,
|
||||
timestamp.value(),
|
||||
duration.value(),
|
||||
numFrames,
|
||||
audioData.forget(),
|
||||
aChannels,
|
||||
aRate));
|
||||
|
||||
#ifdef LOG_SAMPLE_DECODE
|
||||
LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u",
|
||||
timestamp, duration, currentLength);
|
||||
#endif
|
||||
|
||||
mCallback->Output(audio);
|
||||
}
|
||||
|
||||
void
|
||||
EMEAudioDecoder::InputDataExhausted()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
|
||||
void
|
||||
EMEAudioDecoder::DrainComplete()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
|
||||
void
|
||||
EMEAudioDecoder::ResetComplete()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mMustRecaptureAudioPosition = true;
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mFlushComplete = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EMEAudioDecoder::Error(GMPErr aErr)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
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
|
||||
EMEAudioDecoder::Terminated()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
GmpShutdown();
|
||||
}
|
||||
|
||||
nsresult
|
||||
EMEAudioDecoder::GmpInit()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
nsTArray<nsCString> tags;
|
||||
tags.AppendElement(NS_LITERAL_CSTRING("aac"));
|
||||
tags.AppendElement(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
|
||||
nsresult rv = mMPS->GetGMPAudioDecoder(&tags,
|
||||
mProxy->GetNodeId(),
|
||||
&mGMP);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
MOZ_ASSERT(mGMP);
|
||||
|
||||
mAudioRate = mConfig.samples_per_second;
|
||||
mAudioBytesPerSample = mConfig.bits_per_sample / 8;
|
||||
mAudioChannels = mConfig.channel_count;
|
||||
|
||||
nsTArray<uint8_t> extraData;
|
||||
extraData.AppendElements(mConfig.audio_specific_config->Elements(),
|
||||
mConfig.audio_specific_config->Length());
|
||||
mGMP->InitDecode(kGMPAudioCodecAAC,
|
||||
mAudioChannels,
|
||||
mConfig.bits_per_sample,
|
||||
mAudioRate,
|
||||
extraData,
|
||||
this);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EMEAudioDecoder::GmpInput(MP4Sample* aSample)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
nsAutoPtr<MP4Sample> sample(aSample);
|
||||
if (!mGMP) {
|
||||
mCallback->Error();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
gmp::GMPAudioSamplesImpl samples(sample, mAudioChannels, mAudioRate);
|
||||
mGMP->Decode(samples);
|
||||
|
||||
mStreamOffset = sample->byte_offset;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
EMEAudioDecoder::GmpFlush()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mGMP || NS_FAILED(mGMP->Reset())) {
|
||||
// Abort the flush...
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mFlushComplete = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EMEAudioDecoder::GmpDrain()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mGMP || NS_FAILED(mGMP->Drain())) {
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EMEAudioDecoder::GmpShutdown()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mGMP) {
|
||||
return;
|
||||
}
|
||||
mGMP->Close();
|
||||
mGMP = nullptr;
|
||||
AudioCallbackAdapter::Error(aErr);
|
||||
}
|
||||
|
||||
void
|
||||
EMEAudioDecoder::InitTags(nsTArray<nsCString>& aTags)
|
||||
{
|
||||
GMPAudioDecoder::InitTags(aTags);
|
||||
aTags.AppendElement(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
|
||||
}
|
||||
|
||||
nsCString
|
||||
EMEAudioDecoder::GetNodeId()
|
||||
{
|
||||
return mProxy->GetNodeId();
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -4,119 +4,39 @@
|
||||
* 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 EMEAACDecoder_h_
|
||||
#define EMEAACDecoder_h_
|
||||
#ifndef EMEAudioDecoder_h_
|
||||
#define EMEAudioDecoder_h_
|
||||
|
||||
#include "GMPAudioDecoder.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "GMPAudioHost.h"
|
||||
#include "GMPAudioDecoderProxy.h"
|
||||
#include "SamplesWaitingForKey.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class EMEAudioDecoder : public MediaDataDecoder
|
||||
, public GMPAudioDecoderCallbackProxy
|
||||
{
|
||||
typedef mp4_demuxer::MP4Sample MP4Sample;
|
||||
typedef mp4_demuxer::AudioDecoderConfig AudioDecoderConfig;
|
||||
class EMEAudioCallbackAdapter : public AudioCallbackAdapter {
|
||||
public:
|
||||
explicit EMEAudioCallbackAdapter(MediaDataDecoderCallbackProxy* aCallback)
|
||||
: AudioCallbackAdapter(aCallback)
|
||||
{}
|
||||
|
||||
virtual void Error(GMPErr aErr) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
class EMEAudioDecoder : public GMPAudioDecoder {
|
||||
public:
|
||||
EMEAudioDecoder(CDMProxy* aProxy,
|
||||
const AudioDecoderConfig& aConfig,
|
||||
const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
|
||||
~EMEAudioDecoder();
|
||||
|
||||
// MediaDataDecoder implementation.
|
||||
virtual nsresult Init() MOZ_OVERRIDE;
|
||||
virtual nsresult Input(MP4Sample* aSample) MOZ_OVERRIDE;
|
||||
virtual nsresult Flush() MOZ_OVERRIDE;
|
||||
virtual nsresult Drain() MOZ_OVERRIDE;
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
// GMPAudioDecoderCallbackProxy implementation.
|
||||
virtual void Decoded(const nsTArray<int16_t>& aPCM,
|
||||
uint64_t aTimeStamp,
|
||||
uint32_t aChannels,
|
||||
uint32_t aRate) MOZ_OVERRIDE;
|
||||
virtual void InputDataExhausted() MOZ_OVERRIDE;
|
||||
virtual void DrainComplete() MOZ_OVERRIDE;
|
||||
virtual void ResetComplete() MOZ_OVERRIDE;
|
||||
virtual void Error(GMPErr aErr) MOZ_OVERRIDE;
|
||||
virtual void Terminated() MOZ_OVERRIDE;
|
||||
MediaDataDecoderCallbackProxy* aCallback)
|
||||
: GMPAudioDecoder(aConfig, aTaskQueue, aCallback, new EMEAudioCallbackAdapter(aCallback))
|
||||
, mProxy(aProxy)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void InitTags(nsTArray<nsCString>& aTags) MOZ_OVERRIDE;
|
||||
virtual nsCString GetNodeId() MOZ_OVERRIDE;
|
||||
|
||||
class DeliverSample : public nsRunnable {
|
||||
public:
|
||||
DeliverSample(EMEAudioDecoder* aDecoder,
|
||||
mp4_demuxer::MP4Sample* aSample)
|
||||
: mDecoder(aDecoder)
|
||||
, mSample(aSample)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mDecoder->GmpInput(mSample.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<EMEAudioDecoder> mDecoder;
|
||||
nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
|
||||
};
|
||||
|
||||
class InitTask : public nsRunnable {
|
||||
public:
|
||||
explicit InitTask(EMEAudioDecoder* aDecoder)
|
||||
: mDecoder(aDecoder)
|
||||
{}
|
||||
NS_IMETHOD Run() {
|
||||
mResult = mDecoder->GmpInit();
|
||||
return NS_OK;
|
||||
}
|
||||
nsresult mResult;
|
||||
EMEAudioDecoder* mDecoder;
|
||||
};
|
||||
|
||||
nsresult GmpInit();
|
||||
nsresult GmpInput(MP4Sample* aSample);
|
||||
void GmpFlush();
|
||||
void GmpDrain();
|
||||
void GmpShutdown();
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsOnGMPThread() {
|
||||
return NS_GetCurrentThread() == mGMPThread;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint32_t mAudioRate;
|
||||
uint32_t mAudioBytesPerSample;
|
||||
uint32_t mAudioChannels;
|
||||
bool mMustRecaptureAudioPosition;
|
||||
int64_t mAudioFrameSum;
|
||||
int64_t mAudioFrameOffset;
|
||||
int64_t mStreamOffset;
|
||||
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
|
||||
nsCOMPtr<nsIThread> mGMPThread;
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
GMPAudioDecoderProxy* mGMP;
|
||||
|
||||
const mp4_demuxer::AudioDecoderConfig& mConfig;
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
|
||||
nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
|
||||
|
||||
Monitor mMonitor;
|
||||
bool mFlushComplete;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mIsShutdown;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
@ -5,24 +5,13 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "EMEDecoderModule.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "prsystem.h"
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "mozilla/EMELog.h"
|
||||
#include "MediaTaskQueue.h"
|
||||
#include "SharedThreadPool.h"
|
||||
#include "mozilla/EMELog.h"
|
||||
#include "EMEH264Decoder.h"
|
||||
#include "EMEAudioDecoder.h"
|
||||
#include "EMEVideoDecoder.h"
|
||||
#include "MediaDataDecoderProxy.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include "SamplesWaitingForKey.h"
|
||||
#include <string>
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@ -168,6 +157,42 @@ private:
|
||||
#endif
|
||||
};
|
||||
|
||||
class EMEMediaDataDecoderProxy : public MediaDataDecoderProxy {
|
||||
public:
|
||||
EMEMediaDataDecoderProxy(nsIThread* aProxyThread, MediaDataDecoderCallback* aCallback, CDMProxy* aProxy, MediaTaskQueue* aTaskQueue)
|
||||
: MediaDataDecoderProxy(aProxyThread, aCallback)
|
||||
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, aTaskQueue, aProxy))
|
||||
{
|
||||
}
|
||||
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
|
||||
};
|
||||
|
||||
nsresult
|
||||
EMEMediaDataDecoderProxy::Input(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return MediaDataDecoderProxy::Input(aSample);
|
||||
}
|
||||
|
||||
nsresult
|
||||
EMEMediaDataDecoderProxy::Shutdown()
|
||||
{
|
||||
nsresult rv = MediaDataDecoderProxy::Shutdown();
|
||||
|
||||
mSamplesWaitingForKey->BreakCycles();
|
||||
mSamplesWaitingForKey = nullptr;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
EMEDecoderModule::EMEDecoderModule(CDMProxy* aProxy,
|
||||
PlatformDecoderModule* aPDM,
|
||||
bool aCDMDecodesAudio,
|
||||
@ -192,6 +217,24 @@ EMEDecoderModule::Shutdown()
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static already_AddRefed<MediaDataDecoderProxy>
|
||||
CreateDecoderWrapper(MediaDataDecoderCallback* aCallback, CDMProxy* aProxy, MediaTaskQueue* aTaskQueue)
|
||||
{
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> gmpService = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
||||
if (!gmpService) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIThread> thread;
|
||||
nsresult rv = gmpService->GetThread(getter_AddRefs(thread));
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDataDecoderProxy> decoder(new EMEMediaDataDecoderProxy(thread, aCallback, aProxy, aTaskQueue));
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
EMEDecoderModule::CreateVideoDecoder(const VideoDecoderConfig& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
@ -200,13 +243,14 @@ EMEDecoderModule::CreateVideoDecoder(const VideoDecoderConfig& aConfig,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
if (mCDMDecodesVideo && aConfig.crypto.valid) {
|
||||
nsRefPtr<MediaDataDecoder> decoder(new EMEH264Decoder(mProxy,
|
||||
aConfig,
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
aVideoTaskQueue,
|
||||
aCallback));
|
||||
return decoder.forget();
|
||||
nsRefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback, mProxy, aVideoTaskQueue);
|
||||
wrapper->SetProxyTarget(new EMEVideoDecoder(mProxy,
|
||||
aConfig,
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
aVideoTaskQueue,
|
||||
wrapper->Callback()));
|
||||
return wrapper.forget();
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateVideoDecoder(aConfig,
|
||||
@ -234,11 +278,12 @@ EMEDecoderModule::CreateAudioDecoder(const AudioDecoderConfig& aConfig,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
if (mCDMDecodesAudio && aConfig.crypto.valid) {
|
||||
nsRefPtr<MediaDataDecoder> decoder(new EMEAudioDecoder(mProxy,
|
||||
aConfig,
|
||||
aAudioTaskQueue,
|
||||
aCallback));
|
||||
return decoder.forget();
|
||||
nsRefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback, mProxy, aAudioTaskQueue);
|
||||
wrapper->SetProxyTarget(new EMEAudioDecoder(mProxy,
|
||||
aConfig,
|
||||
aAudioTaskQueue,
|
||||
wrapper->Callback()));
|
||||
return wrapper.forget();
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateAudioDecoder(aConfig,
|
||||
|
@ -1,377 +0,0 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#include "EMEH264Decoder.h"
|
||||
#include "gmp-video-host.h"
|
||||
#include "gmp-video-decode.h"
|
||||
#include "gmp-video-frame-i420.h"
|
||||
#include "gmp-video-frame-encoded.h"
|
||||
#include "GMPVideoEncodedFrameImpl.h"
|
||||
#include "mp4_demuxer/AnnexB.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "prsystem.h"
|
||||
#include "gfx2DGlue.h"
|
||||
#include "mozilla/EMELog.h"
|
||||
#include "mozilla/Move.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
EMEH264Decoder::EMEH264Decoder(CDMProxy* aProxy,
|
||||
const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
: mProxy(aProxy)
|
||||
, mGMP(nullptr)
|
||||
, mHost(nullptr)
|
||||
, mConfig(aConfig)
|
||||
, mImageContainer(aImageContainer)
|
||||
, mTaskQueue(aTaskQueue)
|
||||
, mCallback(aCallback)
|
||||
, mLastStreamOffset(0)
|
||||
, mSamplesWaitingForKey(new SamplesWaitingForKey(this, mTaskQueue, mProxy))
|
||||
, mMonitor("EMEH264Decoder")
|
||||
, mFlushComplete(false)
|
||||
#ifdef DEBUG
|
||||
, mIsShutdown(false)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
EMEH264Decoder::~EMEH264Decoder() {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
nsresult rv = mMPS->GetThread(getter_AddRefs(mGMPThread));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsRefPtr<InitTask> task(new InitTask(this));
|
||||
rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(task->mResult, task->mResult);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
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);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EMEH264Decoder::Flush()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mFlushComplete = false;
|
||||
}
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpFlush);
|
||||
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
while (!mFlushComplete) {
|
||||
mon.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EMEH264Decoder::Drain()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpDrain);
|
||||
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
void
|
||||
EMEH264Decoder::Decoded(GMPVideoi420Frame* aDecodedFrame)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
VideoData::YCbCrBuffer b;
|
||||
|
||||
auto height = aDecodedFrame->Height();
|
||||
auto width = aDecodedFrame->Width();
|
||||
|
||||
// Y (Y') plane
|
||||
b.mPlanes[0].mData = aDecodedFrame->Buffer(kGMPYPlane);
|
||||
b.mPlanes[0].mStride = aDecodedFrame->Stride(kGMPYPlane);
|
||||
b.mPlanes[0].mHeight = height;
|
||||
b.mPlanes[0].mWidth = width;
|
||||
b.mPlanes[0].mOffset = 0;
|
||||
b.mPlanes[0].mSkip = 0;
|
||||
|
||||
// U plane (Cb)
|
||||
b.mPlanes[1].mData = aDecodedFrame->Buffer(kGMPUPlane);
|
||||
b.mPlanes[1].mStride = aDecodedFrame->Stride(kGMPUPlane);
|
||||
b.mPlanes[1].mHeight = height / 2;
|
||||
b.mPlanes[1].mWidth = width / 2;
|
||||
b.mPlanes[1].mOffset = 0;
|
||||
b.mPlanes[1].mSkip = 0;
|
||||
|
||||
// V plane (Cr)
|
||||
b.mPlanes[2].mData = aDecodedFrame->Buffer(kGMPVPlane);
|
||||
b.mPlanes[2].mStride = aDecodedFrame->Stride(kGMPVPlane);
|
||||
b.mPlanes[2].mHeight = height / 2;
|
||||
b.mPlanes[2].mWidth = width / 2;
|
||||
b.mPlanes[2].mOffset = 0;
|
||||
b.mPlanes[2].mSkip = 0;
|
||||
|
||||
gfx::IntRect pictureRegion(0, 0, width, height);
|
||||
nsRefPtr<VideoData> v = VideoData::Create(mVideoInfo,
|
||||
mImageContainer,
|
||||
mLastStreamOffset,
|
||||
aDecodedFrame->Timestamp(),
|
||||
aDecodedFrame->Duration(),
|
||||
b,
|
||||
false,
|
||||
-1,
|
||||
pictureRegion);
|
||||
aDecodedFrame->Destroy();
|
||||
mCallback->Output(v);
|
||||
}
|
||||
|
||||
void
|
||||
EMEH264Decoder::ReceivedDecodedReferenceFrame(const uint64_t aPictureId)
|
||||
{
|
||||
// Ignore.
|
||||
}
|
||||
|
||||
void
|
||||
EMEH264Decoder::ReceivedDecodedFrame(const uint64_t aPictureId)
|
||||
{
|
||||
// Ignore.
|
||||
}
|
||||
|
||||
void
|
||||
EMEH264Decoder::InputDataExhausted()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
|
||||
void
|
||||
EMEH264Decoder::DrainComplete()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
|
||||
void
|
||||
EMEH264Decoder::ResetComplete()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
{
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mFlushComplete = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EMEH264Decoder::Error(GMPErr aErr)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
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
|
||||
EMEH264Decoder::Terminated()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
NS_WARNING("H.264 GMP decoder terminated.");
|
||||
GmpShutdown();
|
||||
}
|
||||
|
||||
nsresult
|
||||
EMEH264Decoder::GmpInit()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
nsTArray<nsCString> tags;
|
||||
tags.AppendElement(NS_LITERAL_CSTRING("h264"));
|
||||
tags.AppendElement(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
|
||||
nsresult rv = mMPS->GetGMPVideoDecoder(&tags,
|
||||
mProxy->GetNodeId(),
|
||||
&mHost,
|
||||
&mGMP);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
MOZ_ASSERT(mHost && mGMP);
|
||||
|
||||
GMPVideoCodec codec;
|
||||
memset(&codec, 0, sizeof(codec));
|
||||
|
||||
codec.mGMPApiVersion = kGMPVersion33;
|
||||
|
||||
codec.mCodecType = kGMPVideoCodecH264;
|
||||
codec.mWidth = mConfig.display_width;
|
||||
codec.mHeight = mConfig.display_height;
|
||||
|
||||
nsTArray<uint8_t> codecSpecific;
|
||||
codecSpecific.AppendElement(0); // mPacketizationMode.
|
||||
codecSpecific.AppendElements(mConfig.extra_data->Elements(),
|
||||
mConfig.extra_data->Length());
|
||||
|
||||
rv = mGMP->InitDecode(codec,
|
||||
codecSpecific,
|
||||
this,
|
||||
PR_GetNumberOfProcessors());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mVideoInfo.mDisplay = nsIntSize(mConfig.display_width, mConfig.display_height);
|
||||
mVideoInfo.mHasVideo = true;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
EMEH264Decoder::GmpInput(MP4Sample* aSample)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
nsAutoPtr<MP4Sample> sample(aSample);
|
||||
if (!mGMP) {
|
||||
mCallback->Error();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mLastStreamOffset = sample->byte_offset;
|
||||
|
||||
GMPVideoFrame* ftmp = nullptr;
|
||||
GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
|
||||
if (GMP_FAILED(err)) {
|
||||
mCallback->Error();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
GMPUniquePtr<gmp::GMPVideoEncodedFrameImpl> frame(static_cast<gmp::GMPVideoEncodedFrameImpl*>(ftmp));
|
||||
err = frame->CreateEmptyFrame(sample->size);
|
||||
if (GMP_FAILED(err)) {
|
||||
mCallback->Error();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
memcpy(frame->Buffer(), sample->data, frame->Size());
|
||||
|
||||
frame->SetEncodedWidth(mConfig.display_width);
|
||||
frame->SetEncodedHeight(mConfig.display_height);
|
||||
frame->SetTimeStamp(sample->composition_timestamp);
|
||||
frame->SetCompleteFrame(true);
|
||||
frame->SetDuration(sample->duration);
|
||||
if (sample->crypto.valid) {
|
||||
frame->InitCrypto(sample->crypto);
|
||||
}
|
||||
frame->SetFrameType(sample->is_sync_point ? kGMPKeyFrame : kGMPDeltaFrame);
|
||||
frame->SetBufferType(GMP_BufferLength32);
|
||||
|
||||
nsTArray<uint8_t> info; // No codec specific per-frame info to pass.
|
||||
nsresult rv = mGMP->Decode(GMPUniquePtr<GMPVideoEncodedFrame>(frame.release()), false, info, 0);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCallback->Error();
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
EMEH264Decoder::GmpFlush()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mGMP || NS_FAILED(mGMP->Reset())) {
|
||||
// Abort the flush...
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mFlushComplete = true;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EMEH264Decoder::GmpDrain()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mGMP || NS_FAILED(mGMP->Drain())) {
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EMEH264Decoder::GmpShutdown()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
if (!mGMP) {
|
||||
return;
|
||||
}
|
||||
mGMP->Close();
|
||||
mGMP = nullptr;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
@ -1,121 +0,0 @@
|
||||
/* -*- 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 EMEH264Decoder_h_
|
||||
#define EMEH264Decoder_h_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "GMPVideoDecoderProxy.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "SamplesWaitingForKey.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class CDMProxy;
|
||||
class MediaTaskQueue;
|
||||
|
||||
class EMEH264Decoder : public MediaDataDecoder
|
||||
, public GMPVideoDecoderCallbackProxy
|
||||
{
|
||||
typedef mp4_demuxer::MP4Sample MP4Sample;
|
||||
public:
|
||||
EMEH264Decoder(CDMProxy* aProxy,
|
||||
const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback);
|
||||
~EMEH264Decoder();
|
||||
|
||||
// MediaDataDecoder
|
||||
virtual nsresult Init() MOZ_OVERRIDE;
|
||||
virtual nsresult Input(MP4Sample* aSample) MOZ_OVERRIDE;
|
||||
virtual nsresult Flush() MOZ_OVERRIDE;
|
||||
virtual nsresult Drain() MOZ_OVERRIDE;
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
// GMPVideoDecoderProxyCallback
|
||||
virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) MOZ_OVERRIDE;
|
||||
virtual void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) MOZ_OVERRIDE;
|
||||
virtual void ReceivedDecodedFrame(const uint64_t aPictureId) MOZ_OVERRIDE;
|
||||
virtual void InputDataExhausted() MOZ_OVERRIDE;
|
||||
virtual void DrainComplete() MOZ_OVERRIDE;
|
||||
virtual void ResetComplete() MOZ_OVERRIDE;
|
||||
virtual void Error(GMPErr aErr) MOZ_OVERRIDE;
|
||||
virtual void Terminated() MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
|
||||
nsresult GmpInit();
|
||||
nsresult GmpInput(MP4Sample* aSample);
|
||||
void GmpFlush();
|
||||
void GmpDrain();
|
||||
void GmpShutdown();
|
||||
|
||||
#ifdef DEBUG
|
||||
bool IsOnGMPThread() {
|
||||
return NS_GetCurrentThread() == mGMPThread;
|
||||
}
|
||||
#endif
|
||||
|
||||
class DeliverSample : public nsRunnable {
|
||||
public:
|
||||
DeliverSample(EMEH264Decoder* aDecoder,
|
||||
mp4_demuxer::MP4Sample* aSample)
|
||||
: mDecoder(aDecoder)
|
||||
, mSample(aSample)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mDecoder->GmpInput(mSample.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
private:
|
||||
nsRefPtr<EMEH264Decoder> mDecoder;
|
||||
nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
|
||||
};
|
||||
|
||||
class InitTask : public nsRunnable {
|
||||
public:
|
||||
explicit InitTask(EMEH264Decoder* aDecoder)
|
||||
: mDecoder(aDecoder)
|
||||
{}
|
||||
NS_IMETHOD Run() {
|
||||
mResult = mDecoder->GmpInit();
|
||||
return NS_OK;
|
||||
}
|
||||
nsresult mResult;
|
||||
EMEH264Decoder* mDecoder;
|
||||
};
|
||||
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
|
||||
nsCOMPtr<nsIThread> mGMPThread;
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
GMPVideoDecoderProxy* mGMP;
|
||||
GMPVideoHost* mHost;
|
||||
|
||||
VideoInfo mVideoInfo;
|
||||
const mp4_demuxer::VideoDecoderConfig& mConfig;
|
||||
nsRefPtr<layers::ImageContainer> mImageContainer;
|
||||
nsRefPtr<MediaTaskQueue> mTaskQueue;
|
||||
MediaDataDecoderCallback* mCallback;
|
||||
int64_t mLastStreamOffset;
|
||||
|
||||
nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
|
||||
|
||||
Monitor mMonitor;
|
||||
bool mFlushComplete;
|
||||
|
||||
#ifdef DEBUG
|
||||
bool mIsShutdown;
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // EMEH264Decoder_h_
|
48
dom/media/fmp4/eme/EMEVideoDecoder.cpp
Normal file
48
dom/media/fmp4/eme/EMEVideoDecoder.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#include "EMEVideoDecoder.h"
|
||||
#include "GMPVideoEncodedFrameImpl.h"
|
||||
#include "mozilla/CDMProxy.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
EMEVideoCallbackAdapter::Error(GMPErr 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");
|
||||
return;
|
||||
}
|
||||
VideoCallbackAdapter::Error(aErr);
|
||||
}
|
||||
|
||||
void
|
||||
EMEVideoDecoder::InitTags(nsTArray<nsCString>& aTags)
|
||||
{
|
||||
GMPVideoDecoder::InitTags(aTags);
|
||||
aTags.AppendElement(NS_ConvertUTF16toUTF8(mProxy->KeySystem()));
|
||||
}
|
||||
|
||||
nsCString
|
||||
EMEVideoDecoder::GetNodeId()
|
||||
{
|
||||
return mProxy->GetNodeId();
|
||||
}
|
||||
|
||||
GMPUniquePtr<GMPVideoEncodedFrame>
|
||||
EMEVideoDecoder::CreateFrame(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
GMPUniquePtr<GMPVideoEncodedFrame> frame = GMPVideoDecoder::CreateFrame(aSample);
|
||||
if (frame && aSample->crypto.valid) {
|
||||
static_cast<gmp::GMPVideoEncodedFrameImpl*>(frame.get())->InitCrypto(aSample->crypto);
|
||||
}
|
||||
return frame;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
54
dom/media/fmp4/eme/EMEVideoDecoder.h
Normal file
54
dom/media/fmp4/eme/EMEVideoDecoder.h
Normal file
@ -0,0 +1,54 @@
|
||||
/* -*- 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 EMEVideoDecoder_h_
|
||||
#define EMEVideoDecoder_h_
|
||||
|
||||
#include "GMPVideoDecoder.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class CDMProxy;
|
||||
class MediaTaskQueue;
|
||||
|
||||
class EMEVideoCallbackAdapter : public VideoCallbackAdapter {
|
||||
public:
|
||||
EMEVideoCallbackAdapter(MediaDataDecoderCallbackProxy* aCallback,
|
||||
VideoInfo aVideoInfo,
|
||||
layers::ImageContainer* aImageContainer)
|
||||
: VideoCallbackAdapter(aCallback, aVideoInfo, aImageContainer)
|
||||
{}
|
||||
|
||||
virtual void Error(GMPErr aErr) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
class EMEVideoDecoder : public GMPVideoDecoder {
|
||||
public:
|
||||
EMEVideoDecoder(CDMProxy* aProxy,
|
||||
const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallbackProxy* aCallback)
|
||||
: GMPVideoDecoder(aConfig, aLayersBackend, aImageContainer, aTaskQueue, aCallback,
|
||||
new EMEVideoCallbackAdapter(aCallback, VideoInfo(aConfig.display_width,
|
||||
aConfig.display_height), aImageContainer))
|
||||
, mProxy(aProxy)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void InitTags(nsTArray<nsCString>& aTags) MOZ_OVERRIDE;
|
||||
virtual nsCString GetNodeId() MOZ_OVERRIDE;
|
||||
virtual GMPUniquePtr<GMPVideoEncodedFrame> CreateFrame(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
|
||||
|
||||
nsRefPtr<CDMProxy> mProxy;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // EMEVideoDecoder_h_
|
@ -7,14 +7,14 @@
|
||||
EXPORTS += [
|
||||
'EMEAudioDecoder.h',
|
||||
'EMEDecoderModule.h',
|
||||
'EMEH264Decoder.h',
|
||||
'EMEVideoDecoder.h',
|
||||
'SamplesWaitingForKey.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'EMEAudioDecoder.cpp',
|
||||
'EMEDecoderModule.cpp',
|
||||
'EMEH264Decoder.cpp',
|
||||
'EMEVideoDecoder.cpp',
|
||||
'SamplesWaitingForKey.cpp',
|
||||
]
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user