mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-15 04:39:31 +00:00
Bug 1121258 - Add a GMP PDM to allow MP4 playback via OpenH264. r=cpearce
This commit is contained in:
parent
a15ce311b8
commit
6e2bccf8bb
@ -36,12 +36,18 @@ struct TrackInfo {
|
||||
|
||||
// Stores info relevant to presenting media frames.
|
||||
class VideoInfo {
|
||||
private:
|
||||
VideoInfo(int32_t aWidth, int32_t aHeight, bool aHasVideo)
|
||||
: mDisplay(aWidth, aHeight)
|
||||
, mStereoMode(StereoMode::MONO)
|
||||
, mHasVideo(aHasVideo)
|
||||
, mIsHardwareAccelerated(false)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
VideoInfo()
|
||||
: mDisplay(0,0)
|
||||
, mStereoMode(StereoMode::MONO)
|
||||
, mHasVideo(false)
|
||||
, mIsHardwareAccelerated(false)
|
||||
: VideoInfo(0, 0, false)
|
||||
{
|
||||
// TODO: TrackInfo should be initialized by its specific codec decoder.
|
||||
// This following call should be removed once we have that implemented.
|
||||
@ -49,6 +55,11 @@ public:
|
||||
EmptyString(), EmptyString(), true);
|
||||
}
|
||||
|
||||
VideoInfo(int32_t aWidth, int32_t aHeight)
|
||||
: VideoInfo(aWidth, aHeight, true)
|
||||
{
|
||||
}
|
||||
|
||||
// Size in pixels at which the video is rendered. This is after it has
|
||||
// been scaled by its aspect ratio.
|
||||
nsIntSize mDisplay;
|
||||
|
@ -212,6 +212,12 @@ IsGonkMP4DecoderAvailable()
|
||||
return Preferences::GetBool("media.fragmented-mp4.gonk.enabled", false);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsGMPDecoderAvailable()
|
||||
{
|
||||
return Preferences::GetBool("media.fragmented-mp4.gmp.enabled", false);
|
||||
}
|
||||
|
||||
static bool
|
||||
HavePlatformMPEGDecoders()
|
||||
{
|
||||
@ -224,6 +230,7 @@ HavePlatformMPEGDecoders()
|
||||
IsFFmpegAvailable() ||
|
||||
IsAppleAvailable() ||
|
||||
IsGonkMP4DecoderAvailable() ||
|
||||
IsGMPDecoderAvailable() ||
|
||||
// TODO: Other platforms...
|
||||
false;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#include "AndroidDecoderModule.h"
|
||||
#endif
|
||||
#include "GMPDecoderModule.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#ifdef MOZ_EME
|
||||
@ -40,6 +41,7 @@ bool PlatformDecoderModule::sFFmpegDecoderEnabled = false;
|
||||
bool PlatformDecoderModule::sGonkDecoderEnabled = false;
|
||||
bool PlatformDecoderModule::sAndroidMCDecoderEnabled = false;
|
||||
bool PlatformDecoderModule::sAndroidMCDecoderPreferred = false;
|
||||
bool PlatformDecoderModule::sGMPDecoderEnabled = false;
|
||||
|
||||
/* static */
|
||||
void
|
||||
@ -68,6 +70,9 @@ PlatformDecoderModule::Init()
|
||||
"media.fragmented-mp4.android-media-codec.preferred", false);
|
||||
#endif
|
||||
|
||||
Preferences::AddBoolVarCache(&sGMPDecoderEnabled,
|
||||
"media.fragmented-mp4.gmp.enabled", false);
|
||||
|
||||
#ifdef XP_WIN
|
||||
WMFDecoderModule::Init();
|
||||
#endif
|
||||
@ -167,6 +172,10 @@ PlatformDecoderModule::CreatePDM()
|
||||
return m.forget();
|
||||
}
|
||||
#endif
|
||||
if (sGMPDecoderEnabled) {
|
||||
nsRefPtr<PlatformDecoderModule> m(new AVCCDecoderModule(new GMPDecoderModule()));
|
||||
return m.forget();
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -145,6 +145,7 @@ protected:
|
||||
static bool sGonkDecoderEnabled;
|
||||
static bool sAndroidMCDecoderPreferred;
|
||||
static bool sAndroidMCDecoderEnabled;
|
||||
static bool sGMPDecoderEnabled;
|
||||
};
|
||||
|
||||
// A callback used by MediaDataDecoder to return output/errors to the
|
||||
|
224
dom/media/fmp4/gmp/GMPAudioDecoder.cpp
Normal file
224
dom/media/fmp4/gmp/GMPAudioDecoder.cpp
Normal file
@ -0,0 +1,224 @@
|
||||
/* -*- 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 "GMPAudioDecoder.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#if defined(DEBUG)
|
||||
static bool IsOnGMPThread()
|
||||
{
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mps = do_GetService("@mozilla.org/gecko-media-plugin-service;1");
|
||||
MOZ_ASSERT(mps);
|
||||
|
||||
nsCOMPtr<nsIThread> gmpThread;
|
||||
nsresult rv = mps->GetThread(getter_AddRefs(gmpThread));
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv) && gmpThread);
|
||||
return NS_GetCurrentThread() == gmpThread;
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
AudioCallbackAdapter::Decoded(const nsTArray<int16_t>& aPCM, uint64_t aTimeStamp, uint32_t aChannels, uint32_t aRate)
|
||||
{
|
||||
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(mLastStreamOffset,
|
||||
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
|
||||
AudioCallbackAdapter::InputDataExhausted()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
|
||||
void
|
||||
AudioCallbackAdapter::DrainComplete()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
|
||||
void
|
||||
AudioCallbackAdapter::ResetComplete()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mMustRecaptureAudioPosition = true;
|
||||
mCallback->FlushComplete();
|
||||
}
|
||||
|
||||
void
|
||||
AudioCallbackAdapter::Error(GMPErr aErr)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mCallback->Error();
|
||||
}
|
||||
|
||||
void
|
||||
AudioCallbackAdapter::Terminated()
|
||||
{
|
||||
NS_WARNING("AAC GMP decoder terminated.");
|
||||
mCallback->Error();
|
||||
}
|
||||
|
||||
void
|
||||
GMPAudioDecoder::InitTags(nsTArray<nsCString>& aTags)
|
||||
{
|
||||
aTags.AppendElement(NS_LITERAL_CSTRING("aac"));
|
||||
}
|
||||
|
||||
nsCString
|
||||
GMPAudioDecoder::GetNodeId()
|
||||
{
|
||||
return NS_LITERAL_CSTRING("");
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPAudioDecoder::Init()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
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);
|
||||
|
||||
nsTArray<uint8_t> codecSpecific;
|
||||
codecSpecific.AppendElements(mConfig.audio_specific_config->Elements(),
|
||||
mConfig.audio_specific_config->Length());
|
||||
|
||||
rv = mGMP->InitDecode(kGMPAudioCodecAAC,
|
||||
mConfig.channel_count,
|
||||
mConfig.bits_per_sample,
|
||||
mConfig.samples_per_second,
|
||||
codecSpecific,
|
||||
mAdapter);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPAudioDecoder::Input(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
nsAutoPtr<mp4_demuxer::MP4Sample> sample(aSample);
|
||||
if (!mGMP) {
|
||||
mCallback->Error();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mAdapter->SetLastStreamOffset(sample->byte_offset);
|
||||
|
||||
gmp::GMPAudioSamplesImpl samples(sample, mConfig.channel_count, mConfig.samples_per_second);
|
||||
nsresult rv = mGMP->Decode(samples);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCallback->Error();
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPAudioDecoder::Flush()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
if (!mGMP || NS_FAILED(mGMP->Reset())) {
|
||||
// Abort the flush.
|
||||
mCallback->FlushComplete();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPAudioDecoder::Drain()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
if (!mGMP || NS_FAILED(mGMP->Drain())) {
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPAudioDecoder::Shutdown()
|
||||
{
|
||||
if (!mGMP) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mGMP->Close();
|
||||
mGMP = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
89
dom/media/fmp4/gmp/GMPAudioDecoder.h
Normal file
89
dom/media/fmp4/gmp/GMPAudioDecoder.h
Normal file
@ -0,0 +1,89 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#if !defined(GMPAudioDecoder_h_)
|
||||
#define GMPAudioDecoder_h_
|
||||
|
||||
#include "GMPAudioDecoderProxy.h"
|
||||
#include "MediaDataDecoderProxy.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class AudioCallbackAdapter : public GMPAudioDecoderCallbackProxy {
|
||||
public:
|
||||
explicit AudioCallbackAdapter(MediaDataDecoderCallbackProxy* aCallback)
|
||||
: mCallback(aCallback)
|
||||
, mLastStreamOffset(0)
|
||||
, mAudioFrameSum(0)
|
||||
, mAudioFrameOffset(0)
|
||||
, mMustRecaptureAudioPosition(true)
|
||||
{}
|
||||
|
||||
// GMPAudioDecoderCallbackProxy
|
||||
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;
|
||||
|
||||
void SetLastStreamOffset(int64_t aStreamOffset) {
|
||||
mLastStreamOffset = aStreamOffset;
|
||||
}
|
||||
|
||||
private:
|
||||
MediaDataDecoderCallbackProxy* mCallback;
|
||||
int64_t mLastStreamOffset;
|
||||
|
||||
int64_t mAudioFrameSum;
|
||||
int64_t mAudioFrameOffset;
|
||||
bool mMustRecaptureAudioPosition;
|
||||
};
|
||||
|
||||
class GMPAudioDecoder : public MediaDataDecoder {
|
||||
protected:
|
||||
GMPAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallbackProxy* aCallback,
|
||||
AudioCallbackAdapter* aAdapter)
|
||||
: mConfig(aConfig)
|
||||
, mCallback(aCallback)
|
||||
, mGMP(nullptr)
|
||||
, mAdapter(aAdapter)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
GMPAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallbackProxy* aCallback)
|
||||
: GMPAudioDecoder(aConfig, aTaskQueue, aCallback, new AudioCallbackAdapter(aCallback))
|
||||
{
|
||||
}
|
||||
|
||||
virtual nsresult Init() MOZ_OVERRIDE;
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
|
||||
virtual nsresult Flush() MOZ_OVERRIDE;
|
||||
virtual nsresult Drain() MOZ_OVERRIDE;
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
virtual void InitTags(nsTArray<nsCString>& aTags);
|
||||
virtual nsCString GetNodeId();
|
||||
|
||||
private:
|
||||
const mp4_demuxer::AudioDecoderConfig& mConfig;
|
||||
MediaDataDecoderCallbackProxy* mCallback;
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
|
||||
GMPAudioDecoderProxy* mGMP;
|
||||
nsAutoPtr<AudioCallbackAdapter> mAdapter;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GMPAudioDecoder_h_
|
91
dom/media/fmp4/gmp/GMPDecoderModule.cpp
Normal file
91
dom/media/fmp4/gmp/GMPDecoderModule.cpp
Normal file
@ -0,0 +1,91 @@
|
||||
/* -*- 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 "GMPDecoderModule.h"
|
||||
#include "GMPAudioDecoder.h"
|
||||
#include "GMPVideoDecoder.h"
|
||||
#include "MediaDataDecoderProxy.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
GMPDecoderModule::GMPDecoderModule()
|
||||
{
|
||||
}
|
||||
|
||||
GMPDecoderModule::~GMPDecoderModule()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPDecoderModule::Shutdown()
|
||||
{
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static already_AddRefed<MediaDataDecoderProxy>
|
||||
CreateDecoderWrapper(MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
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 MediaDataDecoderProxy(thread, aCallback));
|
||||
return decoder.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
GMPDecoderModule::CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
if (strcmp(aConfig.mime_type, "video/avc") != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback);
|
||||
wrapper->SetProxyTarget(new GMPVideoDecoder(aConfig,
|
||||
aLayersBackend,
|
||||
aImageContainer,
|
||||
aVideoTaskQueue,
|
||||
wrapper->Callback()));
|
||||
return wrapper.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<MediaDataDecoder>
|
||||
GMPDecoderModule::CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback)
|
||||
{
|
||||
if (strcmp(aConfig.mime_type, "audio/mp4a-latm") != 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<MediaDataDecoderProxy> wrapper = CreateDecoderWrapper(aCallback);
|
||||
wrapper->SetProxyTarget(new GMPAudioDecoder(aConfig,
|
||||
aAudioTaskQueue,
|
||||
wrapper->Callback()));
|
||||
return wrapper.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
GMPDecoderModule::DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig)
|
||||
{
|
||||
// GMPVideoCodecType::kGMPVideoCodecH264 specifies that encoded frames must be in AVCC format.
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
42
dom/media/fmp4/gmp/GMPDecoderModule.h
Normal file
42
dom/media/fmp4/gmp/GMPDecoderModule.h
Normal file
@ -0,0 +1,42 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#if !defined(GMPDecoderModule_h_)
|
||||
#define GMPDecoderModule_h_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class GMPDecoderModule : public PlatformDecoderModule {
|
||||
public:
|
||||
GMPDecoderModule();
|
||||
|
||||
virtual ~GMPDecoderModule();
|
||||
|
||||
// Called when the decoders have shutdown. Main thread only.
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
// Decode thread.
|
||||
virtual already_AddRefed<MediaDataDecoder>
|
||||
CreateVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aVideoTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
|
||||
|
||||
// Decode thread.
|
||||
virtual already_AddRefed<MediaDataDecoder>
|
||||
CreateAudioDecoder(const mp4_demuxer::AudioDecoderConfig& aConfig,
|
||||
MediaTaskQueue* aAudioTaskQueue,
|
||||
MediaDataDecoderCallback* aCallback) MOZ_OVERRIDE;
|
||||
|
||||
virtual bool DecoderNeedsAVCC(const mp4_demuxer::VideoDecoderConfig& aConfig) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GMPDecoderModule_h_
|
245
dom/media/fmp4/gmp/GMPVideoDecoder.cpp
Normal file
245
dom/media/fmp4/gmp/GMPVideoDecoder.cpp
Normal file
@ -0,0 +1,245 @@
|
||||
/* -*- 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 "GMPVideoDecoder.h"
|
||||
#include "GMPVideoHost.h"
|
||||
#include "prsystem.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
#if defined(DEBUG)
|
||||
static bool IsOnGMPThread();
|
||||
#endif
|
||||
|
||||
void
|
||||
VideoCallbackAdapter::Decoded(GMPVideoi420Frame* aDecodedFrame)
|
||||
{
|
||||
GMPUniquePtr<GMPVideoi420Frame> decodedFrame(aDecodedFrame);
|
||||
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
VideoData::YCbCrBuffer b;
|
||||
for (int i = 0; i < kGMPNumOfPlanes; ++i) {
|
||||
b.mPlanes[i].mData = decodedFrame->Buffer(GMPPlaneType(i));
|
||||
b.mPlanes[i].mStride = decodedFrame->Stride(GMPPlaneType(i));
|
||||
if (i == kGMPYPlane) {
|
||||
b.mPlanes[i].mWidth = decodedFrame->Width();
|
||||
b.mPlanes[i].mHeight = decodedFrame->Height();
|
||||
} else {
|
||||
b.mPlanes[i].mWidth = (decodedFrame->Width() + 1) / 2;
|
||||
b.mPlanes[i].mHeight = (decodedFrame->Height() + 1) / 2;
|
||||
}
|
||||
b.mPlanes[i].mOffset = 0;
|
||||
b.mPlanes[i].mSkip = 0;
|
||||
}
|
||||
|
||||
gfx::IntRect pictureRegion(0, 0, decodedFrame->Width(), decodedFrame->Height());
|
||||
nsRefPtr<VideoData> v = VideoData::Create(mVideoInfo,
|
||||
mImageContainer,
|
||||
mLastStreamOffset,
|
||||
decodedFrame->Timestamp(),
|
||||
decodedFrame->Duration(),
|
||||
b,
|
||||
false,
|
||||
-1,
|
||||
pictureRegion);
|
||||
if (v) {
|
||||
mCallback->Output(v);
|
||||
} else {
|
||||
mCallback->Error();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
VideoCallbackAdapter::ReceivedDecodedReferenceFrame(const uint64_t aPictureId)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
}
|
||||
|
||||
void
|
||||
VideoCallbackAdapter::ReceivedDecodedFrame(const uint64_t aPictureId)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
}
|
||||
|
||||
void
|
||||
VideoCallbackAdapter::InputDataExhausted()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mCallback->InputExhausted();
|
||||
}
|
||||
|
||||
void
|
||||
VideoCallbackAdapter::DrainComplete()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
|
||||
void
|
||||
VideoCallbackAdapter::ResetComplete()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mCallback->FlushComplete();
|
||||
}
|
||||
|
||||
void
|
||||
VideoCallbackAdapter::Error(GMPErr aErr)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
mCallback->Error();
|
||||
}
|
||||
|
||||
void
|
||||
VideoCallbackAdapter::Terminated()
|
||||
{
|
||||
// Note that this *may* be called from the proxy thread also.
|
||||
NS_WARNING("H.264 GMP decoder terminated.");
|
||||
mCallback->Error();
|
||||
}
|
||||
|
||||
void
|
||||
GMPVideoDecoder::InitTags(nsTArray<nsCString>& aTags)
|
||||
{
|
||||
aTags.AppendElement(NS_LITERAL_CSTRING("h264"));
|
||||
}
|
||||
|
||||
nsCString
|
||||
GMPVideoDecoder::GetNodeId()
|
||||
{
|
||||
return NS_LITERAL_CSTRING("");
|
||||
}
|
||||
|
||||
GMPUniquePtr<GMPVideoEncodedFrame>
|
||||
GMPVideoDecoder::CreateFrame(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
GMPVideoFrame* ftmp = nullptr;
|
||||
GMPErr err = mHost->CreateFrame(kGMPEncodedVideoFrame, &ftmp);
|
||||
if (GMP_FAILED(err)) {
|
||||
mCallback->Error();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
GMPUniquePtr<GMPVideoEncodedFrame> frame(static_cast<GMPVideoEncodedFrame*>(ftmp));
|
||||
err = frame->CreateEmptyFrame(aSample->size);
|
||||
if (GMP_FAILED(err)) {
|
||||
mCallback->Error();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memcpy(frame->Buffer(), aSample->data, frame->Size());
|
||||
|
||||
frame->SetEncodedWidth(mConfig.display_width);
|
||||
frame->SetEncodedHeight(mConfig.display_height);
|
||||
frame->SetTimeStamp(aSample->composition_timestamp);
|
||||
frame->SetCompleteFrame(true);
|
||||
frame->SetDuration(aSample->duration);
|
||||
frame->SetFrameType(aSample->is_sync_point ? kGMPKeyFrame : kGMPDeltaFrame);
|
||||
frame->SetBufferType(GMP_BufferLength32);
|
||||
|
||||
return frame;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPVideoDecoder::Init()
|
||||
{
|
||||
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);
|
||||
|
||||
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,
|
||||
mAdapter,
|
||||
PR_GetNumberOfProcessors());
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPVideoDecoder::Input(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
nsAutoPtr<mp4_demuxer::MP4Sample> sample(aSample);
|
||||
if (!mGMP) {
|
||||
mCallback->Error();
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mAdapter->SetLastStreamOffset(sample->byte_offset);
|
||||
|
||||
GMPUniquePtr<GMPVideoEncodedFrame> frame = CreateFrame(sample);
|
||||
nsTArray<uint8_t> info; // No codec specific per-frame info to pass.
|
||||
nsresult rv = mGMP->Decode(Move(frame), false, info, 0);
|
||||
if (NS_FAILED(rv)) {
|
||||
mCallback->Error();
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPVideoDecoder::Flush()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
if (!mGMP || NS_FAILED(mGMP->Reset())) {
|
||||
// Abort the flush.
|
||||
mCallback->FlushComplete();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPVideoDecoder::Drain()
|
||||
{
|
||||
MOZ_ASSERT(IsOnGMPThread());
|
||||
|
||||
if (!mGMP || NS_FAILED(mGMP->Drain())) {
|
||||
mCallback->DrainComplete();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
GMPVideoDecoder::Shutdown()
|
||||
{
|
||||
// Note that this *may* be called from the proxy thread also.
|
||||
if (!mGMP) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
mGMP->Close();
|
||||
mGMP = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
105
dom/media/fmp4/gmp/GMPVideoDecoder.h
Normal file
105
dom/media/fmp4/gmp/GMPVideoDecoder.h
Normal file
@ -0,0 +1,105 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#if !defined(GMPVideoDecoder_h_)
|
||||
#define GMPVideoDecoder_h_
|
||||
|
||||
#include "GMPVideoDecoderProxy.h"
|
||||
#include "ImageContainer.h"
|
||||
#include "MediaDataDecoderProxy.h"
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mozIGeckoMediaPluginService.h"
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class VideoCallbackAdapter : public GMPVideoDecoderCallbackProxy {
|
||||
public:
|
||||
VideoCallbackAdapter(MediaDataDecoderCallbackProxy* aCallback,
|
||||
VideoInfo aVideoInfo,
|
||||
layers::ImageContainer* aImageContainer)
|
||||
: mCallback(aCallback)
|
||||
, mLastStreamOffset(0)
|
||||
, mVideoInfo(aVideoInfo)
|
||||
, mImageContainer(aImageContainer)
|
||||
{}
|
||||
|
||||
// GMPVideoDecoderCallbackProxy
|
||||
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;
|
||||
|
||||
void SetLastStreamOffset(int64_t aStreamOffset) {
|
||||
mLastStreamOffset = aStreamOffset;
|
||||
}
|
||||
|
||||
private:
|
||||
MediaDataDecoderCallbackProxy* mCallback;
|
||||
int64_t mLastStreamOffset;
|
||||
|
||||
VideoInfo mVideoInfo;
|
||||
nsRefPtr<layers::ImageContainer> mImageContainer;
|
||||
};
|
||||
|
||||
class GMPVideoDecoder : public MediaDataDecoder {
|
||||
protected:
|
||||
GMPVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallbackProxy* aCallback,
|
||||
VideoCallbackAdapter* aAdapter)
|
||||
: mConfig(aConfig)
|
||||
, mCallback(aCallback)
|
||||
, mGMP(nullptr)
|
||||
, mHost(nullptr)
|
||||
, mAdapter(aAdapter)
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
GMPVideoDecoder(const mp4_demuxer::VideoDecoderConfig& aConfig,
|
||||
layers::LayersBackend aLayersBackend,
|
||||
layers::ImageContainer* aImageContainer,
|
||||
MediaTaskQueue* aTaskQueue,
|
||||
MediaDataDecoderCallbackProxy* aCallback)
|
||||
: GMPVideoDecoder(aConfig, aLayersBackend, aImageContainer, aTaskQueue, aCallback,
|
||||
new VideoCallbackAdapter(aCallback,
|
||||
VideoInfo(aConfig.display_width,
|
||||
aConfig.display_height),
|
||||
aImageContainer))
|
||||
{
|
||||
}
|
||||
|
||||
virtual nsresult Init() MOZ_OVERRIDE;
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
|
||||
virtual nsresult Flush() MOZ_OVERRIDE;
|
||||
virtual nsresult Drain() MOZ_OVERRIDE;
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
virtual void InitTags(nsTArray<nsCString>& aTags);
|
||||
virtual nsCString GetNodeId();
|
||||
virtual GMPUniquePtr<GMPVideoEncodedFrame> CreateFrame(mp4_demuxer::MP4Sample* aSample);
|
||||
|
||||
private:
|
||||
const mp4_demuxer::VideoDecoderConfig& mConfig;
|
||||
MediaDataDecoderCallbackProxy* mCallback;
|
||||
nsCOMPtr<mozIGeckoMediaPluginService> mMPS;
|
||||
GMPVideoDecoderProxy* mGMP;
|
||||
GMPVideoHost* mHost;
|
||||
nsAutoPtr<VideoCallbackAdapter> mAdapter;
|
||||
};
|
||||
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // GMPVideoDecoder_h_
|
101
dom/media/fmp4/gmp/MediaDataDecoderProxy.cpp
Normal file
101
dom/media/fmp4/gmp/MediaDataDecoderProxy.cpp
Normal file
@ -0,0 +1,101 @@
|
||||
/* -*- 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 "MediaDataDecoderProxy.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
void
|
||||
MediaDataDecoderCallbackProxy::Error()
|
||||
{
|
||||
mProxyCallback->Error();
|
||||
mProxyDecoder->Shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
MediaDataDecoderCallbackProxy::FlushComplete()
|
||||
{
|
||||
mProxyDecoder->FlushComplete();
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaDataDecoderProxy::Init()
|
||||
{
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
nsRefPtr<InitTask> task(new InitTask(mProxyDecoder));
|
||||
nsresult rv = mProxyThread->Dispatch(task, NS_DISPATCH_SYNC);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
NS_ENSURE_SUCCESS(task->Result(), task->Result());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaDataDecoderProxy::Input(mp4_demuxer::MP4Sample* aSample)
|
||||
{
|
||||
MOZ_ASSERT(!IsOnProxyThread());
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
nsRefPtr<nsIRunnable> task(new InputTask(mProxyDecoder, aSample));
|
||||
nsresult rv = mProxyThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaDataDecoderProxy::Flush()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnProxyThread());
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
mFlushComplete.Set(false);
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethod(mProxyDecoder, &MediaDataDecoder::Flush);
|
||||
nsresult rv = mProxyThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mFlushComplete.WaitUntil(true);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaDataDecoderProxy::Drain()
|
||||
{
|
||||
MOZ_ASSERT(!IsOnProxyThread());
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethod(mProxyDecoder, &MediaDataDecoder::Drain);
|
||||
nsresult rv = mProxyThread->Dispatch(task, NS_DISPATCH_NORMAL);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
MediaDataDecoderProxy::Shutdown()
|
||||
{
|
||||
// Note that this *may* be called from the proxy thread also.
|
||||
MOZ_ASSERT(!mIsShutdown);
|
||||
#if defined(DEBUG)
|
||||
mIsShutdown = true;
|
||||
#endif
|
||||
nsRefPtr<nsIRunnable> task;
|
||||
task = NS_NewRunnableMethod(mProxyDecoder, &MediaDataDecoder::Shutdown);
|
||||
nsresult rv = mProxyThread->Dispatch(task, NS_DISPATCH_SYNC);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
MediaDataDecoderProxy::FlushComplete()
|
||||
{
|
||||
mFlushComplete.Set(true);
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
191
dom/media/fmp4/gmp/MediaDataDecoderProxy.h
Normal file
191
dom/media/fmp4/gmp/MediaDataDecoderProxy.h
Normal file
@ -0,0 +1,191 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
#if !defined(MediaDataDecoderProxy_h_)
|
||||
#define MediaDataDecoderProxy_h_
|
||||
|
||||
#include "PlatformDecoderModule.h"
|
||||
#include "mp4_demuxer/DecoderData.h"
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsRefPtr.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nscore.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class InputTask : public nsRunnable {
|
||||
public:
|
||||
InputTask(MediaDataDecoder* aDecoder,
|
||||
mp4_demuxer::MP4Sample* aSample)
|
||||
: mDecoder(aDecoder)
|
||||
, mSample(aSample)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mDecoder->Input(mSample.forget());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsRefPtr<MediaDataDecoder> mDecoder;
|
||||
nsAutoPtr<mp4_demuxer::MP4Sample> mSample;
|
||||
};
|
||||
|
||||
class InitTask : public nsRunnable {
|
||||
public:
|
||||
explicit InitTask(MediaDataDecoder* aDecoder)
|
||||
: mDecoder(aDecoder)
|
||||
, mResultValid(false)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Run() {
|
||||
mResult = mDecoder->Init();
|
||||
mResultValid = true;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult Result() {
|
||||
MOZ_ASSERT(mResultValid);
|
||||
return mResult;
|
||||
}
|
||||
|
||||
private:
|
||||
MediaDataDecoder* mDecoder;
|
||||
nsresult mResult;
|
||||
bool mResultValid;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Condition {
|
||||
public:
|
||||
explicit Condition(T aValue)
|
||||
: mMonitor("Condition")
|
||||
, mCondition(aValue)
|
||||
{}
|
||||
|
||||
void Set(T aValue) {
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
mCondition = aValue;
|
||||
mon.NotifyAll();
|
||||
}
|
||||
|
||||
void WaitUntil(T aValue) {
|
||||
MonitorAutoLock mon(mMonitor);
|
||||
while (mCondition != aValue) {
|
||||
mon.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
Monitor mMonitor;
|
||||
T mCondition;
|
||||
};
|
||||
|
||||
class MediaDataDecoderProxy;
|
||||
|
||||
class MediaDataDecoderCallbackProxy : public MediaDataDecoderCallback {
|
||||
public:
|
||||
explicit MediaDataDecoderCallbackProxy(MediaDataDecoderProxy* aProxyDecoder, MediaDataDecoderCallback* aCallback)
|
||||
: mProxyDecoder(aProxyDecoder)
|
||||
, mProxyCallback(aCallback)
|
||||
{
|
||||
}
|
||||
|
||||
virtual void Output(MediaData* aData) MOZ_OVERRIDE {
|
||||
mProxyCallback->Output(aData);
|
||||
}
|
||||
|
||||
virtual void Error() MOZ_OVERRIDE;
|
||||
|
||||
virtual void InputExhausted() MOZ_OVERRIDE {
|
||||
mProxyCallback->InputExhausted();
|
||||
}
|
||||
|
||||
virtual void DrainComplete() MOZ_OVERRIDE {
|
||||
mProxyCallback->DrainComplete();
|
||||
}
|
||||
|
||||
virtual void NotifyResourcesStatusChanged() MOZ_OVERRIDE {
|
||||
mProxyCallback->NotifyResourcesStatusChanged();
|
||||
}
|
||||
|
||||
virtual void ReleaseMediaResources() MOZ_OVERRIDE {
|
||||
mProxyCallback->ReleaseMediaResources();
|
||||
}
|
||||
|
||||
virtual void FlushComplete();
|
||||
|
||||
private:
|
||||
MediaDataDecoderProxy* mProxyDecoder;
|
||||
MediaDataDecoderCallback* mProxyCallback;
|
||||
};
|
||||
|
||||
class MediaDataDecoderProxy : public MediaDataDecoder {
|
||||
public:
|
||||
MediaDataDecoderProxy(nsIThread* aProxyThread, MediaDataDecoderCallback* aCallback)
|
||||
: mProxyThread(aProxyThread)
|
||||
, mProxyCallback(this, aCallback)
|
||||
, mFlushComplete(false)
|
||||
#if defined(DEBUG)
|
||||
, mIsShutdown(false)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
// Ideally, this would return a regular MediaDataDecoderCallback pointer
|
||||
// to retain the clean abstraction, but until MediaDataDecoderCallback
|
||||
// supports the FlushComplete interface, this will have to do. When MDDC
|
||||
// supports FlushComplete, this, the GMP*Decoders, and the
|
||||
// *CallbackAdapters can be reverted to accepting a regular
|
||||
// MediaDataDecoderCallback pointer.
|
||||
MediaDataDecoderCallbackProxy* Callback()
|
||||
{
|
||||
return &mProxyCallback;
|
||||
}
|
||||
|
||||
void SetProxyTarget(MediaDataDecoder* aProxyDecoder)
|
||||
{
|
||||
MOZ_ASSERT(aProxyDecoder);
|
||||
mProxyDecoder = aProxyDecoder;
|
||||
}
|
||||
|
||||
// These are called from the decoder thread pool.
|
||||
// Init and Shutdown run synchronously on the proxy thread, all others are
|
||||
// asynchronously and responded to via the MediaDataDecoderCallback.
|
||||
// Note: the nsresults returned by the proxied decoder are lost.
|
||||
virtual nsresult Init() MOZ_OVERRIDE;
|
||||
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample) MOZ_OVERRIDE;
|
||||
virtual nsresult Flush() MOZ_OVERRIDE;
|
||||
virtual nsresult Drain() MOZ_OVERRIDE;
|
||||
virtual nsresult Shutdown() MOZ_OVERRIDE;
|
||||
|
||||
// Called by MediaDataDecoderCallbackProxy.
|
||||
void FlushComplete();
|
||||
|
||||
private:
|
||||
#ifdef DEBUG
|
||||
bool IsOnProxyThread() {
|
||||
return NS_GetCurrentThread() == mProxyThread;
|
||||
}
|
||||
#endif
|
||||
|
||||
friend class InputTask;
|
||||
friend class InitTask;
|
||||
|
||||
nsRefPtr<MediaDataDecoder> mProxyDecoder;
|
||||
nsCOMPtr<nsIThread> mProxyThread;
|
||||
|
||||
MediaDataDecoderCallbackProxy mProxyCallback;
|
||||
|
||||
Condition<bool> mFlushComplete;
|
||||
#if defined(DEBUG)
|
||||
bool mIsShutdown;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MediaDataDecoderProxy_h_
|
26
dom/media/fmp4/gmp/moz.build
Normal file
26
dom/media/fmp4/gmp/moz.build
Normal file
@ -0,0 +1,26 @@
|
||||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
EXPORTS += [
|
||||
'GMPAudioDecoder.h',
|
||||
'GMPDecoderModule.h',
|
||||
'GMPVideoDecoder.h',
|
||||
'MediaDataDecoderProxy.h',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'GMPAudioDecoder.cpp',
|
||||
'GMPDecoderModule.cpp',
|
||||
'GMPVideoDecoder.cpp',
|
||||
'MediaDataDecoderProxy.cpp',
|
||||
]
|
||||
|
||||
# GMPVideoEncodedFrameImpl.h needs IPC
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
FAIL_ON_WARNINGS = True
|
@ -26,6 +26,8 @@ SOURCES += [
|
||||
'MP4Reader.cpp',
|
||||
]
|
||||
|
||||
DIRS += ['gmp']
|
||||
|
||||
if CONFIG['MOZ_WMF']:
|
||||
DIRS += [ 'wmf' ];
|
||||
|
||||
|
@ -280,6 +280,7 @@ pref("media.directshow.enabled", true);
|
||||
#ifdef MOZ_FMP4
|
||||
pref("media.fragmented-mp4.enabled", true);
|
||||
pref("media.fragmented-mp4.ffmpeg.enabled", false);
|
||||
pref("media.fragmented-mp4.gmp.enabled", false);
|
||||
#if defined(XP_WIN) && defined(MOZ_WMF) || defined(XP_MACOSX) || defined(MOZ_WIDGET_GONK)
|
||||
// Denotes that the fragmented MP4 parser can be created by <video> elements.
|
||||
pref("media.fragmented-mp4.exposed", true);
|
||||
|
Loading…
x
Reference in New Issue
Block a user