Bug 1047214 - Add MediaDataDecoders that uses a GMP for decoding as well as EME decryption. r=edwin

This commit is contained in:
Chris Pearce 2014-08-05 19:56:04 +12:00
parent 87c36f4878
commit e181a50d63
7 changed files with 932 additions and 7 deletions

View File

@ -93,9 +93,6 @@ PlatformDecoderModule::CreateCDMWrapper(CDMProxy* aProxy,
if (!pdm) {
return nullptr;
}
} else {
NS_WARNING("CDM that decodes not yet supported!");
return nullptr;
}
return new EMEDecoderModule(aProxy,

View File

@ -0,0 +1,319 @@
/* -*- 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 "EMEAACDecoder.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 {
EMEAACDecoder::EMEAACDecoder(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)
, mMonitor("EMEAACDecoder")
, mFlushComplete(false)
{
}
EMEAACDecoder::~EMEAACDecoder()
{
}
nsresult
EMEAACDecoder::Init()
{
// Note: this runs on the decode task queue.
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
EMEAACDecoder::Input(MP4Sample* aSample)
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
nsRefPtr<nsIRunnable> task(new DeliverSample(this, aSample));
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
EMEAACDecoder::Flush()
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
{
MonitorAutoLock mon(mMonitor);
mFlushComplete = false;
}
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEAACDecoder::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
EMEAACDecoder::Drain()
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEAACDecoder::GmpDrain);
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_NORMAL);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult
EMEAACDecoder::Shutdown()
{
MOZ_ASSERT(!IsOnGMPThread()); // Runs on the decode task queue.
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEAACDecoder::GmpShutdown);
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
void
EMEAACDecoder::Decoded(const nsTArray<int16_t>& aPCM,
uint64_t aTimeStamp)
{
MOZ_ASSERT(IsOnGMPThread());
size_t numFrames = aPCM.Length() / mAudioChannels;
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, mAudioRate);
if (!timestamp.isValid()) {
NS_WARNING("Invalid timestamp");
mCallback->Error();
return;
}
mAudioFrameOffset = timestamp.value();
MOZ_ASSERT(mAudioFrameOffset >= 0);
mMustRecaptureAudioPosition = false;
}
auto timestamp = FramesToUsecs(mAudioFrameOffset + mAudioFrameSum, mAudioRate);
if (!timestamp.isValid()) {
NS_WARNING("Invalid timestamp on audio samples");
mCallback->Error();
return;
}
mAudioFrameSum += numFrames;
auto duration = FramesToUsecs(numFrames, mAudioRate);
if (!duration.isValid()) {
NS_WARNING("Invalid duration on audio samples");
mCallback->Error();
return;
}
nsAutoPtr<AudioData> audio(new AudioData(mStreamOffset,
timestamp.value(),
duration.value(),
numFrames,
audioData.forget(),
mAudioChannels));
#ifdef LOG_SAMPLE_DECODE
LOG("Decoded audio sample! timestamp=%lld duration=%lld currentLength=%u",
timestamp, duration, currentLength);
#endif
mCallback->Output(audio.forget());
}
void
EMEAACDecoder::InputDataExhausted()
{
MOZ_ASSERT(IsOnGMPThread());
mCallback->InputExhausted();
}
void
EMEAACDecoder::DrainComplete()
{
MOZ_ASSERT(IsOnGMPThread());
mCallback->DrainComplete();
}
void
EMEAACDecoder::ResetComplete()
{
MOZ_ASSERT(IsOnGMPThread());
mMustRecaptureAudioPosition = true;
{
MonitorAutoLock mon(mMonitor);
mFlushComplete = true;
mon.NotifyAll();
}
}
void
EMEAACDecoder::Error(GMPErr aErr)
{
MOZ_ASSERT(IsOnGMPThread());
mCallback->Error();
GmpShutdown();
}
void
EMEAACDecoder::Terminated()
{
MOZ_ASSERT(IsOnGMPThread());
GmpShutdown();
}
nsresult
EMEAACDecoder::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->GetOrigin(),
&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[0],
mConfig.audio_specific_config.length());
mGMP->InitDecode(kGMPAudioCodecAAC,
mAudioChannels,
mConfig.bits_per_sample,
mAudioRate,
extraData,
this);
return NS_OK;
}
nsresult
EMEAACDecoder::GmpInput(MP4Sample* aSample)
{
MOZ_ASSERT(IsOnGMPThread());
nsAutoPtr<MP4Sample> sample(aSample);
if (!mGMP) {
mCallback->Error();
return NS_ERROR_FAILURE;
}
if (sample->crypto.valid) {
CDMCaps::AutoLock caps(mProxy->Capabilites());
MOZ_ASSERT(caps.CanDecryptAndDecodeAudio());
const auto& keyid = sample->crypto.key;
if (!caps.IsKeyUsable(keyid)) {
// DeliverSample assumes responsibility for deleting aSample.
nsRefPtr<nsIRunnable> task(new DeliverSample(this, sample.forget()));
caps.CallWhenKeyUsable(keyid, task, mGMPThread);
return NS_OK;
}
}
gmp::GMPAudioSamplesImpl samples(sample);
mGMP->Decode(samples);
mStreamOffset = sample->byte_offset;
return NS_OK;
}
void
EMEAACDecoder::GmpFlush()
{
MOZ_ASSERT(IsOnGMPThread());
if (!mGMP || NS_FAILED(mGMP->Reset())) {
// Abort the flush...
MonitorAutoLock mon(mMonitor);
mFlushComplete = true;
mon.NotifyAll();
}
}
void
EMEAACDecoder::GmpDrain()
{
MOZ_ASSERT(IsOnGMPThread());
if (!mGMP || NS_FAILED(mGMP->Drain())) {
mCallback->DrainComplete();
}
}
void
EMEAACDecoder::GmpShutdown()
{
MOZ_ASSERT(IsOnGMPThread());
if (!mGMP) {
return;
}
mGMP->Close();
mGMP = nullptr;
}
} // namespace mozilla

View File

@ -0,0 +1,115 @@
/* -*- 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 EMEAACDecoder_h_
#define EMEAACDecoder_h_
#include "PlatformDecoderModule.h"
#include "mp4_demuxer/DecoderData.h"
#include "mozIGeckoMediaPluginService.h"
#include "nsServiceManagerUtils.h"
#include "GMPAudioHost.h"
#include "GMPAudioDecoderProxy.h"
namespace mozilla {
class EMEAACDecoder : public MediaDataDecoder
, public GMPAudioDecoderProxyCallback
{
typedef mp4_demuxer::MP4Sample MP4Sample;
typedef mp4_demuxer::AudioDecoderConfig AudioDecoderConfig;
public:
EMEAACDecoder(CDMProxy* aProxy,
const AudioDecoderConfig& aConfig,
MediaTaskQueue* aTaskQueue,
MediaDataDecoderCallback* aCallback);
~EMEAACDecoder();
// 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;
// GMPAudioDecoderProxyCallback implementation.
virtual void Decoded(const nsTArray<int16_t>& aPCM,
uint64_t aTimeStamp) 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:
class DeliverSample : public nsRunnable {
public:
DeliverSample(EMEAACDecoder* aDecoder,
mp4_demuxer::MP4Sample* aSample)
: mDecoder(aDecoder)
, mSample(aSample)
{}
NS_IMETHOD Run() {
mDecoder->GmpInput(mSample.forget());
return NS_OK;
}
private:
nsRefPtr<EMEAACDecoder> mDecoder;
nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
};
class InitTask : public nsRunnable {
public:
InitTask(EMEAACDecoder* aDecoder)
: mDecoder(aDecoder)
{}
NS_IMETHOD Run() {
mResult = mDecoder->GmpInit();
return NS_OK;
}
nsresult mResult;
EMEAACDecoder* 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;
Monitor mMonitor;
bool mFlushComplete;
};
} // namespace mozilla
#endif

View File

@ -18,6 +18,8 @@
#include "MediaTaskQueue.h"
#include "SharedThreadPool.h"
#include "mozilla/EMELog.h"
#include "EMEH264Decoder.h"
#include "EMEAACDecoder.h"
#include <string>
namespace mozilla {
@ -199,8 +201,13 @@ EMEDecoderModule::CreateH264Decoder(const VideoDecoderConfig& aConfig,
MediaDataDecoderCallback* aCallback)
{
if (mCDMDecodesVideo) {
NS_WARNING("Support for CDM that decodes video not yet supported");
return nullptr;
nsRefPtr<MediaDataDecoder> decoder(new EMEH264Decoder(mProxy,
aConfig,
aLayersBackend,
aImageContainer,
aVideoTaskQueue,
aCallback));
return decoder.forget();
}
nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateH264Decoder(aConfig,
@ -225,8 +232,11 @@ EMEDecoderModule::CreateAACDecoder(const AudioDecoderConfig& aConfig,
MediaDataDecoderCallback* aCallback)
{
if (mCDMDecodesAudio) {
NS_WARNING("Support for CDM that decodes audio not yet supported");
return nullptr;
nsRefPtr<MediaDataDecoder> decoder(new EMEAACDecoder(mProxy,
aConfig,
aAudioTaskQueue,
aCallback));
return decoder.forget();
}
nsRefPtr<MediaDataDecoder> decoder(mPDM->CreateAACDecoder(aConfig,

View File

@ -0,0 +1,360 @@
/* -*- 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"
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)
, mMonitor("EMEH264Decoder")
, mFlushComplete(false)
{
}
EMEH264Decoder::~EMEH264Decoder() {
}
nsresult
EMEH264Decoder::Init()
{
// Note: this runs on the decode task queue.
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.
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.
{
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.
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.
nsRefPtr<nsIRunnable> task;
task = NS_NewRunnableMethod(this, &EMEH264Decoder::GmpShutdown);
nsresult rv = mGMPThread->Dispatch(task, NS_DISPATCH_SYNC);
NS_ENSURE_SUCCESS(rv, rv);
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;
VideoData *v = VideoData::Create(mVideoInfo,
mImageContainer,
mLastStreamOffset,
aDecodedFrame->Timestamp(),
aDecodedFrame->Duration(),
b,
false,
-1,
ToIntRect(mPictureRegion));
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());
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->GetOrigin(),
&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.begin(),
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;
mPictureRegion = nsIntRect(0, 0, mConfig.display_width, mConfig.display_height);
return NS_OK;
}
nsresult
EMEH264Decoder::GmpInput(MP4Sample* aSample)
{
MOZ_ASSERT(IsOnGMPThread());
nsAutoPtr<MP4Sample> sample(aSample);
if (!mGMP) {
mCallback->Error();
return NS_ERROR_FAILURE;
}
if (sample->crypto.valid) {
CDMCaps::AutoLock caps(mProxy->Capabilites());
MOZ_ASSERT(caps.CanDecryptAndDecodeVideo());
const auto& keyid = sample->crypto.key;
if (!caps.IsKeyUsable(keyid)) {
nsRefPtr<nsIRunnable> task(new DeliverSample(this, sample.forget()));
caps.CallWhenKeyUsable(keyid, task, mGMPThread);
return NS_OK;
}
}
mLastStreamOffset = sample->byte_offset;
GMPVideoFrame* ftmp = nullptr;
GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
if (GMP_FAILED(err)) {
mCallback->Error();
return NS_ERROR_FAILURE;
}
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(frame, 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

View File

@ -0,0 +1,114 @@
/* -*- 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"
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:
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;
nsIntRect mPictureRegion;
const mp4_demuxer::VideoDecoderConfig& mConfig;
nsRefPtr<layers::ImageContainer> mImageContainer;
nsRefPtr<MediaTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
int64_t mLastStreamOffset;
Monitor mMonitor;
bool mFlushComplete;
};
}
#endif // EMEH264Decoder_h_

View File

@ -5,7 +5,9 @@
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
EXPORTS += [
'eme/EMEAACDecoder.h',
'eme/EMEDecoderModule.h',
'eme/EMEH264Decoder.h',
'MP4Decoder.h',
'MP4Reader.h',
'PlatformDecoderModule.h',
@ -13,7 +15,9 @@ EXPORTS += [
UNIFIED_SOURCES += [
'BlankDecoderModule.cpp',
'eme/EMEAACDecoder.cpp',
'eme/EMEDecoderModule.cpp',
'eme/EMEH264Decoder.cpp',
'PlatformDecoderModule.cpp',
]
@ -58,6 +62,12 @@ if CONFIG['MOZ_APPLEMEDIA']:
'-framework AudioToolbox',
]
include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [
'../base',
]
FINAL_LIBRARY = 'xul'
FAIL_ON_WARNINGS = True