Bug 1123535 - Make MP4Reader and WMFMediaDataDecoder support going dormant. r=kentuckyfriedtakahe
--- a/dom/media/fmp4/MP4Reader.cpp
+++ b/dom/media/fmp4/MP4Reader.cpp
@@ -112,16 +112,19 @@ MP4Reader::MP4Reader(AbstractMediaDecode
, mAudio(MediaData::AUDIO_DATA, Preferences::GetUint("media.mp4-audio-decode-ahead", 2))
, mVideo(MediaData::VIDEO_DATA, Preferences::GetUint("media.mp4-video-decode-ahead", 2))
, mLastReportedNumDecodedFrames(0)
, mLayersBackendType(layers::LayersBackend::LAYERS_NONE)
, mDemuxerInitialized(false)
, mIsEncrypted(false)
, mIndexReady(false)
, mDemuxerMonitor("MP4 Demuxer")
+#if defined(XP_WIN)
+ , mDormantEnabled(Preferences::GetBool("media.decoder.heuristic.dormant.enabled", false))
+#endif
{
MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
MOZ_COUNT_CTOR(MP4Reader);
}
MP4Reader::~MP4Reader()
{
MOZ_COUNT_DTOR(MP4Reader);
@@ -247,25 +250,25 @@ public:
private:
nsRefPtr<AbstractMediaDecoder> mDecoder;
nsTArray<uint8_t> mInitData;
nsString mInitDataType;
};
#endif
void MP4Reader::RequestCodecResource() {
-#ifdef MOZ_GONK_MEDIACODEC
- if(mVideo.mDecoder) {
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
+ if (mVideo.mDecoder) {
mVideo.mDecoder->AllocateMediaResources();
}
#endif
}
bool MP4Reader::IsWaitingOnCodecResource() {
-#ifdef MOZ_GONK_MEDIACODEC
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
return mVideo.mDecoder && mVideo.mDecoder->IsWaitingMediaResources();
#endif
return false;
}
bool MP4Reader::IsWaitingOnCDMResource() {
#ifdef MOZ_EME
nsRefPtr<CDMProxy> proxy;
@@ -441,17 +444,18 @@ MP4Reader::ReadMetadata(MediaInfo* aInfo
if (mInfo.mVideo.mHasVideo && !IsSupportedVideoMimeType(video.mime_type)) {
return NS_ERROR_FAILURE;
}
mInfo.mVideo.mDisplay =
nsIntSize(video.display_width, video.display_height);
mVideo.mCallback = new DecoderCallback(this, kVideo);
if (mSharedDecoderManager) {
mVideo.mDecoder =
- mSharedDecoderManager->CreateVideoDecoder(video,
+ mSharedDecoderManager->CreateVideoDecoder(mPlatform,
+ video,
mLayersBackendType,
mDecoder->GetImageContainer(),
mVideo.mTaskQueue,
mVideo.mCallback);
} else {
mVideo.mDecoder = mPlatform->CreateVideoDecoder(video,
mLayersBackendType,
mDecoder->GetImageContainer(),
@@ -996,40 +1000,45 @@ MP4Reader::GetBuffered(dom::TimeRanges*
}
}
return NS_OK;
}
bool MP4Reader::IsDormantNeeded()
{
-#ifdef MOZ_GONK_MEDIACODEC
- return mVideo.mDecoder && mVideo.mDecoder->IsDormantNeeded();
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
+ return
+#if defined(XP_WIN)
+ mDormantEnabled &&
+#endif
+ mVideo.mDecoder &&
+ mVideo.mDecoder->IsDormantNeeded();
#endif
return false;
}
void MP4Reader::ReleaseMediaResources()
{
-#ifdef MOZ_GONK_MEDIACODEC
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
// Before freeing a video codec, all video buffers needed to be released
// even from graphics pipeline.
VideoFrameContainer* container = mDecoder->GetVideoFrameContainer();
if (container) {
container->ClearCurrentFrame();
}
if (mVideo.mDecoder) {
mVideo.mDecoder->ReleaseMediaResources();
}
#endif
}
void MP4Reader::NotifyResourcesStatusChanged()
{
-#ifdef MOZ_GONK_MEDIACODEC
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
if (mDecoder) {
mDecoder->NotifyWaitingForResourcesStatusChanged();
}
#endif
}
void
MP4Reader::SetIdle()
@@ -1038,14 +1047,14 @@ MP4Reader::SetIdle()
mSharedDecoderManager->SetIdle(mVideo.mDecoder);
NotifyResourcesStatusChanged();
}
}
void
MP4Reader::SetSharedDecoderManager(SharedDecoderManager* aManager)
{
-#ifdef MOZ_GONK_MEDIACODEC
+#if defined(MOZ_GONK_MEDIACODEC) || defined(XP_WIN)
mSharedDecoderManager = aManager;
#endif
}
} // namespace mozilla
--- a/dom/media/fmp4/MP4Reader.h
+++ b/dom/media/fmp4/MP4Reader.h
@@ -262,13 +262,17 @@ private:
bool mDemuxerInitialized;
// Synchronized by decoder monitor.
bool mIsEncrypted;
bool mIndexReady;
Monitor mDemuxerMonitor;
nsRefPtr<SharedDecoderManager> mSharedDecoderManager;
+
+#if defined(XP_WIN)
+ const bool mDormantEnabled;
+#endif
};
} // namespace mozilla
#endif
--- a/dom/media/fmp4/SharedDecoderManager.cpp
+++ b/dom/media/fmp4/SharedDecoderManager.cpp
@@ -50,36 +50,43 @@ public:
mManager->mActiveCallback->ReleaseMediaResources();
}
}
SharedDecoderManager* mManager;
};
SharedDecoderManager::SharedDecoderManager()
- : mActiveProxy(nullptr)
+ : mTaskQueue(new MediaTaskQueue(GetMediaDecodeThreadPool()))
+ , mActiveProxy(nullptr)
, mActiveCallback(nullptr)
, mWaitForInternalDrain(false)
, mMonitor("SharedDecoderProxy")
+ , mDecoderReleasedResources(false)
{
+ MOZ_ASSERT(NS_IsMainThread()); // taskqueue must be created on main thread.
mCallback = new SharedDecoderCallback(this);
}
SharedDecoderManager::~SharedDecoderManager() {}
already_AddRefed<MediaDataDecoder>
SharedDecoderManager::CreateVideoDecoder(
+ PlatformDecoderModule* aPDM,
const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend, layers::ImageContainer* aImageContainer,
MediaTaskQueue* aVideoTaskQueue, MediaDataDecoderCallback* aCallback)
{
if (!mDecoder) {
- nsRefPtr<PlatformDecoderModule> platform(PlatformDecoderModule::Create());
- mDecoder = platform->CreateVideoDecoder(
- aConfig, aLayersBackend, aImageContainer, aVideoTaskQueue, mCallback);
+ // We use the manager's task queue for the decoder, rather than the one
+ // passed in, so that none of the objects sharing the decoder can shutdown
+ // the task queue while we're potentially still using it for a *different*
+ // object also sharing the decoder.
+ mDecoder = aPDM->CreateVideoDecoder(
+ aConfig, aLayersBackend, aImageContainer, mTaskQueue, mCallback);
if (!mDecoder) {
return nullptr;
}
nsresult rv = mDecoder->Init();
NS_ENSURE_SUCCESS(rv, nullptr);
}
nsRefPtr<SharedDecoderProxy> proxy(new SharedDecoderProxy(this, aCallback));
@@ -91,16 +98,21 @@ SharedDecoderManager::Select(SharedDecod
{
if (mActiveProxy == aProxy) {
return;
}
SetIdle(mActiveProxy);
mActiveProxy = aProxy;
mActiveCallback = aProxy->mCallback;
+
+ if (mDecoderReleasedResources) {
+ mDecoder->AllocateMediaResources();
+ mDecoderReleasedResources = false;
+ }
}
void
SharedDecoderManager::SetIdle(MediaDataDecoder* aProxy)
{
if (aProxy && mActiveProxy == aProxy) {
mWaitForInternalDrain = true;
mActiveProxy->Drain();
@@ -120,16 +132,38 @@ SharedDecoderManager::DrainComplete()
MonitorAutoLock mon(mMonitor);
mWaitForInternalDrain = false;
mon.NotifyAll();
} else {
mActiveCallback->DrainComplete();
}
}
+void
+SharedDecoderManager::ReleaseMediaResources()
+{
+ mDecoderReleasedResources = true;
+ mDecoder->ReleaseMediaResources();
+ mActiveProxy = nullptr;
+}
+
+void
+SharedDecoderManager::Shutdown()
+{
+ if (mDecoder) {
+ mDecoder->Shutdown();
+ mDecoder = nullptr;
+ }
+ if (mTaskQueue) {
+ mTaskQueue->BeginShutdown();
+ mTaskQueue->AwaitShutdownAndIdle();
+ mTaskQueue = nullptr;
+ }
+}
+
SharedDecoderProxy::SharedDecoderProxy(
SharedDecoderManager* aManager, MediaDataDecoderCallback* aCallback)
: mManager(aManager), mCallback(aCallback)
{
}
SharedDecoderProxy::~SharedDecoderProxy() { Shutdown(); }
@@ -141,17 +175,16 @@ SharedDecoderProxy::Init()
nsresult
SharedDecoderProxy::Input(mp4_demuxer::MP4Sample* aSample)
{
if (mManager->mActiveProxy != this) {
mManager->Select(this);
}
return mManager->mDecoder->Input(aSample);
- return NS_OK;
}
nsresult
SharedDecoderProxy::Flush()
{
if (mManager->mActiveProxy == this) {
return mManager->mDecoder->Flush();
}
@@ -188,17 +221,17 @@ SharedDecoderProxy::IsDormantNeeded()
{
return mManager->mDecoder->IsDormantNeeded();
}
void
SharedDecoderProxy::ReleaseMediaResources()
{
if (mManager->mActiveProxy == this) {
- mManager->mDecoder->ReleaseMediaResources();
+ mManager->ReleaseMediaResources();
}
}
void
SharedDecoderProxy::ReleaseDecoder()
{
if (mManager->mActiveProxy == this) {
mManager->mDecoder->ReleaseMediaResources();
--- a/dom/media/fmp4/SharedDecoderManager.h
+++ b/dom/media/fmp4/SharedDecoderManager.h
@@ -20,38 +20,43 @@ class SharedDecoderCallback;
class SharedDecoderManager
{
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedDecoderManager)
SharedDecoderManager();
already_AddRefed<MediaDataDecoder> CreateVideoDecoder(
+ PlatformDecoderModule* aPDM,
const mp4_demuxer::VideoDecoderConfig& aConfig,
layers::LayersBackend aLayersBackend,
layers::ImageContainer* aImageContainer, MediaTaskQueue* aVideoTaskQueue,
MediaDataDecoderCallback* aCallback);
void SetReader(MediaDecoderReader* aReader);
void Select(SharedDecoderProxy* aProxy);
void SetIdle(MediaDataDecoder* aProxy);
+ void ReleaseMediaResources();
+ void Shutdown();
friend class SharedDecoderProxy;
friend class SharedDecoderCallback;
private:
virtual ~SharedDecoderManager();
void DrainComplete();
nsRefPtr<MediaDataDecoder> mDecoder;
+ nsRefPtr<MediaTaskQueue> mTaskQueue;
SharedDecoderProxy* mActiveProxy;
MediaDataDecoderCallback* mActiveCallback;
nsAutoPtr<MediaDataDecoderCallback> mCallback;
bool mWaitForInternalDrain;
Monitor mMonitor;
+ bool mDecoderReleasedResources;
};
class SharedDecoderProxy : public MediaDataDecoder
{
public:
SharedDecoderProxy(SharedDecoderManager* aManager,
MediaDataDecoderCallback* aCallback);
virtual ~SharedDecoderProxy();
--- a/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp
+++ b/dom/media/fmp4/wmf/WMFMediaDataDecoder.cpp
@@ -43,28 +43,41 @@ WMFMediaDataDecoder::Init()
NS_ENSURE_TRUE(mDecoder, NS_ERROR_FAILURE);
return NS_OK;
}
nsresult
WMFMediaDataDecoder::Shutdown()
{
- mTaskQueue->FlushAndDispatch(NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessShutdown));
+ DebugOnly<nsresult> rv = mTaskQueue->FlushAndDispatch(
+ NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessShutdown));
+#ifdef DEBUG
+ if (NS_FAILED(rv)) {
+ NS_WARNING("WMFMediaDataDecoder::Shutdown() dispatch of task failed!");
+ }
+#endif
return NS_OK;
}
void
WMFMediaDataDecoder::ProcessShutdown()
{
mMFTManager->Shutdown();
mMFTManager = nullptr;
mDecoder = nullptr;
}
+void
+WMFMediaDataDecoder::ProcessReleaseDecoder()
+{
+ mMFTManager->Shutdown();
+ mDecoder = nullptr;
+}
+
// Inserts data into the decoder's pipeline.
nsresult
WMFMediaDataDecoder::Input(mp4_demuxer::MP4Sample* aSample)
{
mTaskQueue->Dispatch(
NS_NewRunnableMethodWithArg<nsAutoPtr<mp4_demuxer::MP4Sample>>(
this,
&WMFMediaDataDecoder::ProcessDecode,
@@ -137,9 +150,33 @@ WMFMediaDataDecoder::ProcessDrain()
nsresult
WMFMediaDataDecoder::Drain()
{
mTaskQueue->Dispatch(NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessDrain));
return NS_OK;
}
+void
+WMFMediaDataDecoder::AllocateMediaResources()
+{
+ mDecoder = mMFTManager->Init();
+}
+
+void
+WMFMediaDataDecoder::ReleaseMediaResources()
+{
+ DebugOnly<nsresult> rv = mTaskQueue->FlushAndDispatch(
+ NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessReleaseDecoder));
+#ifdef DEBUG
+ if (NS_FAILED(rv)) {
+ NS_WARNING("WMFMediaDataDecoder::ReleaseMediaResources() dispatch of task failed!");
+ }
+#endif
+}
+
+void
+WMFMediaDataDecoder::ReleaseDecoder()
+{
+ ReleaseMediaResources();
+}
+
} // namespace mozilla
--- a/dom/media/fmp4/wmf/WMFMediaDataDecoder.h
+++ b/dom/media/fmp4/wmf/WMFMediaDataDecoder.h
@@ -65,31 +65,38 @@ public:
virtual nsresult Input(mp4_demuxer::MP4Sample* aSample);
virtual nsresult Flush() MOZ_OVERRIDE;
virtual nsresult Drain() MOZ_OVERRIDE;
virtual nsresult Shutdown() MOZ_OVERRIDE;
+ virtual bool IsWaitingMediaResources() { return false; };
+ virtual bool IsDormantNeeded() { return true; };
+ virtual void AllocateMediaResources() MOZ_OVERRIDE;
+ virtual void ReleaseMediaResources() MOZ_OVERRIDE;
+ virtual void ReleaseDecoder() MOZ_OVERRIDE;
+
private:
// Called on the task queue. Inserts the sample into the decoder, and
// extracts output if available.
void ProcessDecode(mp4_demuxer::MP4Sample* aSample);
// Called on the task queue. Extracts output if available, and delivers
// it to the reader. Called after ProcessDecode() and ProcessDrain().
void ProcessOutput();
// Called on the task queue. Orders the MFT to drain, and then extracts
// all available output.
void ProcessDrain();
void ProcessShutdown();
+ void ProcessReleaseDecoder();
RefPtr<MediaTaskQueue> mTaskQueue;
MediaDataDecoderCallback* mCallback;
RefPtr<MFTDecoder> mDecoder;
nsAutoPtr<MFTManager> mMFTManager;
// The last offset into the media resource that was passed into Input().
--- a/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/fmp4/wmf/WMFVideoMFTManager.cpp
@@ -95,17 +95,19 @@ WMFVideoMFTManager::WMFVideoMFTManager(
mStreamType = Unknown;
}
}
WMFVideoMFTManager::~WMFVideoMFTManager()
{
MOZ_COUNT_DTOR(WMFVideoMFTManager);
// Ensure DXVA/D3D9 related objects are released on the main thread.
- DeleteOnMainThread(mDXVA2Manager);
+ if (mDXVA2Manager) {
+ DeleteOnMainThread(mDXVA2Manager);
+ }
}
const GUID&
WMFVideoMFTManager::GetMFTGUID()
{
MOZ_ASSERT(mStreamType != Unknown);
switch (mStreamType) {
case H264: return CLSID_CMSH264DecoderMFT;
@@ -135,16 +137,18 @@ public:
return NS_OK;
}
nsAutoPtr<DXVA2Manager> mDXVA2Manager;
};
bool
WMFVideoMFTManager::InitializeDXVA()
{
+ MOZ_ASSERT(!mDXVA2Manager);
+
// If we use DXVA but aren't running with a D3D layer manager then the
// readback of decoded video frames from GPU to CPU memory grinds painting
// to a halt, and makes playback performance *worse*.
if (!mDXVAEnabled ||
(mLayersBackend != LayersBackend::LAYERS_D3D9 &&
mLayersBackend != LayersBackend::LAYERS_D3D10 &&
mLayersBackend != LayersBackend::LAYERS_D3D11)) {
return false;
@@ -482,11 +486,12 @@ WMFVideoMFTManager::Output(int64_t aStre
return S_OK;
}
void
WMFVideoMFTManager::Shutdown()
{
mDecoder = nullptr;
+ DeleteOnMainThread(mDXVA2Manager);
}
} // namespace mozilla
--- a/dom/media/mediasource/MediaSourceReader.cpp
+++ b/dom/media/mediasource/MediaSourceReader.cpp
@@ -406,16 +406,21 @@ MediaSourceReader::ContinueShutdown()
return;
}
mAudioTrack = nullptr;
mAudioReader = nullptr;
mVideoTrack = nullptr;
mVideoReader = nullptr;
+ if (mSharedDecoderManager) {
+ mSharedDecoderManager->Shutdown();
+ mSharedDecoderManager = nullptr;
+ }
+
MOZ_ASSERT(mAudioPromise.IsEmpty());
MOZ_ASSERT(mVideoPromise.IsEmpty());
mAudioWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::AUDIO_DATA, WaitForDataRejectValue::SHUTDOWN), __func__);
mVideoWaitPromise.RejectIfExists(WaitForDataRejectValue(MediaData::VIDEO_DATA, WaitForDataRejectValue::SHUTDOWN), __func__);
MediaDecoderReader::Shutdown()->ChainTo(mMediaSourceShutdownPromise.Steal(), __func__);
}