diff --git a/dom/media/AudioSink.cpp b/dom/media/AudioSink.cpp index 1fee1c59d0..6120d46839 100644 --- a/dom/media/AudioSink.cpp +++ b/dom/media/AudioSink.cpp @@ -10,6 +10,7 @@ #include "VideoUtils.h" #include "mozilla/CheckedInt.h" +#include "mozilla/DebugOnly.h" namespace mozilla { @@ -28,22 +29,60 @@ AudioSink::AudioSink(MediaQueue& aAudioQueue, dom::AudioChannel aChannel) : mAudioQueue(aAudioQueue) , mMonitor("AudioSink::mMonitor") + , mState(AUDIOSINK_STATE_INIT) + , mAudioLoopScheduled(false) , mStartTime(aStartTime) , mWritten(0) , mLastGoodPosition(0) , mInfo(aInfo) , mChannel(aChannel) - , mVolume(1.0) - , mPlaybackRate(1.0) - , mPreservesPitch(false) , mStopAudioThread(false) - , mSetVolume(false) - , mSetPlaybackRate(false) - , mSetPreservesPitch(false) , mPlaying(true) { } +void +AudioSink::SetState(State aState) +{ + AssertOnAudioThread(); + mPendingState = Some(aState); +} + +void +AudioSink::DispatchTask(already_AddRefed&& event) +{ + DebugOnly rv = mThread->Dispatch(Move(event), NS_DISPATCH_NORMAL); + // There isn't much we can do if Dispatch() fails. + // Just assert it to keep things simple. + MOZ_ASSERT(NS_SUCCEEDED(rv)); +} + +void +AudioSink::ScheduleNextLoop() +{ + AssertOnAudioThread(); + if (mAudioLoopScheduled) { + return; + } + mAudioLoopScheduled = true; + nsCOMPtr r = NS_NewRunnableMethod(this, &AudioSink::AudioLoop); + DispatchTask(r.forget()); +} + +void +AudioSink::ScheduleNextLoopCrossThread() +{ + AssertNotOnAudioThread(); + nsRefPtr self = this; + nsCOMPtr r = NS_NewRunnableFunction([self] () { + // Do nothing if there is already a pending task waiting for its turn. + if (!self->mAudioLoopScheduled) { + self->AudioLoop(); + } + }); + DispatchTask(r.forget()); +} + nsRefPtr AudioSink::Init() { @@ -57,13 +96,7 @@ AudioSink::Init() return p; } - nsCOMPtr event = NS_NewRunnableMethod(this, &AudioSink::AudioLoop); - rv = mThread->Dispatch(event, NS_DISPATCH_NORMAL); - if (NS_FAILED(rv)) { - mEndPromise.Reject(rv, __func__); - return p; - } - + ScheduleNextLoopCrossThread(); return p; } @@ -94,120 +127,104 @@ AudioSink::HasUnplayedFrames() void AudioSink::Shutdown() { - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - mStopAudioThread = true; - if (mAudioStream) { - mAudioStream->Cancel(); + { + ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); + if (mAudioStream) { + mAudioStream->Cancel(); + } } - GetReentrantMonitor().NotifyAll(); + nsRefPtr self = this; + nsCOMPtr r = NS_NewRunnableFunction([=] () { + self->mStopAudioThread = true; + if (!self->mAudioLoopScheduled) { + self->AudioLoop(); + } + }); + DispatchTask(r.forget()); - // Exit the monitor so audio loop can enter the monitor and finish its job. - ReentrantMonitorAutoExit exit(GetReentrantMonitor()); mThread->Shutdown(); mThread = nullptr; if (mAudioStream) { mAudioStream->Shutdown(); mAudioStream = nullptr; } + + // Should've reached the final state after shutdown. + MOZ_ASSERT(mState == AUDIOSINK_STATE_SHUTDOWN || + mState == AUDIOSINK_STATE_ERROR); + // Should have no pending state change. + MOZ_ASSERT(mPendingState.isNothing()); } void AudioSink::SetVolume(double aVolume) { - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - mVolume = aVolume; - mSetVolume = true; + AssertNotOnAudioThread(); + nsRefPtr self = this; + nsCOMPtr r = NS_NewRunnableFunction([=] () { + if (self->mState == AUDIOSINK_STATE_PLAYING) { + self->mAudioStream->SetVolume(aVolume); + } + }); + DispatchTask(r.forget()); } void AudioSink::SetPlaybackRate(double aPlaybackRate) { - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - NS_ASSERTION(mPlaybackRate != 0, "Don't set the playbackRate to 0 on AudioStream"); - mPlaybackRate = aPlaybackRate; - mSetPlaybackRate = true; + AssertNotOnAudioThread(); + MOZ_ASSERT(aPlaybackRate != 0, "Don't set the playbackRate to 0 on AudioStream"); + nsRefPtr self = this; + nsCOMPtr r = NS_NewRunnableFunction([=] () { + if (self->mState == AUDIOSINK_STATE_PLAYING) { + self->mAudioStream->SetPlaybackRate(aPlaybackRate); + } + }); + DispatchTask(r.forget()); } void AudioSink::SetPreservesPitch(bool aPreservesPitch) { - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - mPreservesPitch = aPreservesPitch; - mSetPreservesPitch = true; + AssertNotOnAudioThread(); + nsRefPtr self = this; + nsCOMPtr r = NS_NewRunnableFunction([=] () { + if (self->mState == AUDIOSINK_STATE_PLAYING) { + self->mAudioStream->SetPreservesPitch(aPreservesPitch); + } + }); + DispatchTask(r.forget()); } void AudioSink::SetPlaying(bool aPlaying) { - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - mPlaying = aPlaying; - GetReentrantMonitor().NotifyAll(); + AssertNotOnAudioThread(); + nsRefPtr self = this; + nsCOMPtr r = NS_NewRunnableFunction([=] () { + if (self->mState != AUDIOSINK_STATE_PLAYING || + self->mPlaying == aPlaying) { + return; + } + self->mPlaying = aPlaying; + // pause/resume AudioStream as necessary. + if (!aPlaying && !self->mAudioStream->IsPaused()) { + self->mAudioStream->Pause(); + } else if (aPlaying && self->mAudioStream->IsPaused()) { + self->mAudioStream->Resume(); + } + // Wake up the audio loop to play next sample. + if (aPlaying && !self->mAudioLoopScheduled) { + self->AudioLoop(); + } + }); + DispatchTask(r.forget()); } void AudioSink::NotifyData() { - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - GetReentrantMonitor().NotifyAll(); -} - -void -AudioSink::AudioLoop() -{ - AssertOnAudioThread(); - SINK_LOG("AudioLoop started"); - - nsresult rv = InitializeAudioStream(); - if (NS_FAILED(rv)) { - NS_WARNING("Initializing AudioStream failed."); - mEndPromise.Reject(rv, __func__); - return; - } - - while (1) { - { - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - WaitForAudioToPlay(); - if (!IsPlaybackContinuing()) { - break; - } - } - // See if there's a gap in the audio. If there is, push silence into the - // audio hardware, so we can play across the gap. - // Calculate the timestamp of the next chunk of audio in numbers of - // samples. - NS_ASSERTION(AudioQueue().GetSize() > 0, "Should have data to play"); - CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate); - - // Calculate the number of frames that have been pushed onto the audio hardware. - CheckedInt64 playedFrames = UsecsToFrames(mStartTime, mInfo.mRate) + - static_cast(mWritten); - - CheckedInt64 missingFrames = sampleTime - playedFrames; - if (!missingFrames.isValid() || !sampleTime.isValid()) { - NS_WARNING("Int overflow adding in AudioLoop"); - break; - } - - if (missingFrames.value() > AUDIO_FUZZ_FRAMES) { - // The next audio chunk begins some time after the end of the last chunk - // we pushed to the audio hardware. We must push silence into the audio - // hardware so that the next audio chunk begins playback at the correct - // time. - missingFrames = std::min(UINT32_MAX, missingFrames.value()); - mWritten += PlaySilence(static_cast(missingFrames.value())); - } else { - mWritten += PlayFromAudioQueue(); - } - } - ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); - MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream()); - if (!mStopAudioThread && mPlaying) { - Drain(); - } - SINK_LOG("AudioLoop complete"); - Cleanup(); - SINK_LOG("AudioLoop exit"); + ScheduleNextLoopCrossThread(); } nsresult @@ -226,7 +243,6 @@ AudioSink::InitializeAudioStream() ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); mAudioStream = audioStream; - UpdateStreamSettings(); return NS_OK; } @@ -234,21 +250,18 @@ AudioSink::InitializeAudioStream() void AudioSink::Drain() { + AssertOnAudioThread(); MOZ_ASSERT(mPlaying && !mAudioStream->IsPaused()); - AssertCurrentThreadInMonitor(); // If the media was too short to trigger the start of the audio stream, // start it now. mAudioStream->Start(); - { - ReentrantMonitorAutoExit exit(GetReentrantMonitor()); - mAudioStream->Drain(); - } + mAudioStream->Drain(); } void AudioSink::Cleanup() { - AssertCurrentThreadInMonitor(); + AssertOnAudioThread(); mEndPromise.Resolve(true, __func__); // Since the promise if resolved asynchronously, we don't shutdown // AudioStream here so MDSM::ResyncAudioClock can get the correct @@ -261,39 +274,141 @@ AudioSink::ExpectMoreAudioData() return AudioQueue().GetSize() == 0 && !AudioQueue().IsFinished(); } -void -AudioSink::WaitForAudioToPlay() +bool +AudioSink::WaitingForAudioToPlay() { - // Wait while we're not playing, and we're not shutting down, or we're + AssertOnAudioThread(); + // Return true if we're not playing, and we're not shutting down, or we're // playing and we've got no audio to play. - AssertCurrentThreadInMonitor(); - while (!mStopAudioThread && (!mPlaying || ExpectMoreAudioData())) { - if (!mPlaying && !mAudioStream->IsPaused()) { - mAudioStream->Pause(); - } - GetReentrantMonitor().Wait(); + if (!mStopAudioThread && (!mPlaying || ExpectMoreAudioData())) { + return true; } + return false; } bool AudioSink::IsPlaybackContinuing() { - AssertCurrentThreadInMonitor(); - if (mPlaying && mAudioStream->IsPaused()) { - mAudioStream->Resume(); - } - + AssertOnAudioThread(); // If we're shutting down, captured, or at EOS, break out and exit the audio // thread. if (mStopAudioThread || AudioQueue().AtEndOfStream()) { return false; } - UpdateStreamSettings(); + return true; +} + +void +AudioSink::AudioLoop() +{ + AssertOnAudioThread(); + mAudioLoopScheduled = false; + + switch (mState) { + case AUDIOSINK_STATE_INIT: { + SINK_LOG("AudioLoop started"); + nsresult rv = InitializeAudioStream(); + if (NS_FAILED(rv)) { + NS_WARNING("Initializing AudioStream failed."); + mEndPromise.Reject(rv, __func__); + SetState(AUDIOSINK_STATE_ERROR); + break; + } + SetState(AUDIOSINK_STATE_PLAYING); + break; + } + + case AUDIOSINK_STATE_PLAYING: { + if (WaitingForAudioToPlay()) { + // NotifyData() will schedule next loop. + break; + } + if (!IsPlaybackContinuing()) { + SetState(AUDIOSINK_STATE_COMPLETE); + break; + } + if (!PlayAudio()) { + SetState(AUDIOSINK_STATE_COMPLETE); + break; + } + // Schedule next loop to play next sample. + ScheduleNextLoop(); + break; + } + + case AUDIOSINK_STATE_COMPLETE: { + FinishAudioLoop(); + SetState(AUDIOSINK_STATE_SHUTDOWN); + break; + } + + case AUDIOSINK_STATE_SHUTDOWN: + break; + + case AUDIOSINK_STATE_ERROR: + break; + } // end of switch + + // We want mState to stay stable during AudioLoop to keep things simple. + // Therefore, we only do state transition at the end of AudioLoop. + if (mPendingState.isSome()) { + MOZ_ASSERT(mState != mPendingState.ref()); + SINK_LOG("change mState, %d -> %d", mState, mPendingState.ref()); + mState = mPendingState.ref(); + mPendingState.reset(); + // Schedule next loop when state changes. + ScheduleNextLoop(); + } +} + +bool +AudioSink::PlayAudio() +{ + // See if there's a gap in the audio. If there is, push silence into the + // audio hardware, so we can play across the gap. + // Calculate the timestamp of the next chunk of audio in numbers of + // samples. + NS_ASSERTION(AudioQueue().GetSize() > 0, "Should have data to play"); + CheckedInt64 sampleTime = UsecsToFrames(AudioQueue().PeekFront()->mTime, mInfo.mRate); + + // Calculate the number of frames that have been pushed onto the audio hardware. + CheckedInt64 playedFrames = UsecsToFrames(mStartTime, mInfo.mRate) + + static_cast(mWritten); + + CheckedInt64 missingFrames = sampleTime - playedFrames; + if (!missingFrames.isValid() || !sampleTime.isValid()) { + NS_WARNING("Int overflow adding in AudioLoop"); + return false; + } + + if (missingFrames.value() > AUDIO_FUZZ_FRAMES) { + // The next audio chunk begins some time after the end of the last chunk + // we pushed to the audio hardware. We must push silence into the audio + // hardware so that the next audio chunk begins playback at the correct + // time. + missingFrames = std::min(UINT32_MAX, missingFrames.value()); + mWritten += PlaySilence(static_cast(missingFrames.value())); + } else { + mWritten += PlayFromAudioQueue(); + } return true; } +void +AudioSink::FinishAudioLoop() +{ + AssertOnAudioThread(); + MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream()); + if (!mStopAudioThread && mPlaying) { + Drain(); + } + SINK_LOG("AudioLoop complete"); + Cleanup(); + SINK_LOG("AudioLoop exit"); +} + uint32_t AudioSink::PlaySilence(uint32_t aFrames) { @@ -335,40 +450,6 @@ AudioSink::PlayFromAudioQueue() return audio->mFrames; } -void -AudioSink::UpdateStreamSettings() -{ - AssertCurrentThreadInMonitor(); - - bool setVolume = mSetVolume; - bool setPlaybackRate = mSetPlaybackRate; - bool setPreservesPitch = mSetPreservesPitch; - double volume = mVolume; - double playbackRate = mPlaybackRate; - bool preservesPitch = mPreservesPitch; - - mSetVolume = false; - mSetPlaybackRate = false; - mSetPreservesPitch = false; - - { - ReentrantMonitorAutoExit exit(GetReentrantMonitor()); - if (setVolume) { - mAudioStream->SetVolume(volume); - } - - if (setPlaybackRate && - NS_FAILED(mAudioStream->SetPlaybackRate(playbackRate))) { - NS_WARNING("Setting the playback rate failed in AudioSink."); - } - - if (setPreservesPitch && - NS_FAILED(mAudioStream->SetPreservesPitch(preservesPitch))) { - NS_WARNING("Setting the pitch preservation failed in AudioSink."); - } - } -} - void AudioSink::StartAudioStreamPlaybackIfNeeded() { @@ -411,4 +492,10 @@ AudioSink::AssertOnAudioThread() MOZ_ASSERT(NS_GetCurrentThread() == mThread); } +void +AudioSink::AssertNotOnAudioThread() +{ + MOZ_ASSERT(NS_GetCurrentThread() != mThread); +} + } // namespace mozilla diff --git a/dom/media/AudioSink.h b/dom/media/AudioSink.h index 9f0d6ae33e..050038feea 100644 --- a/dom/media/AudioSink.h +++ b/dom/media/AudioSink.h @@ -12,6 +12,7 @@ #include "mozilla/dom/AudioChannelBinding.h" #include "mozilla/Atomics.h" +#include "mozilla/Maybe.h" #include "mozilla/MozPromise.h" #include "mozilla/ReentrantMonitor.h" @@ -57,8 +58,21 @@ public: void NotifyData(); private: + enum State { + AUDIOSINK_STATE_INIT, + AUDIOSINK_STATE_PLAYING, + AUDIOSINK_STATE_COMPLETE, + AUDIOSINK_STATE_SHUTDOWN, + AUDIOSINK_STATE_ERROR + }; + ~AudioSink() {} + void DispatchTask(already_AddRefed&& event); + void SetState(State aState); + void ScheduleNextLoop(); + void ScheduleNextLoopCrossThread(); + // The main loop for the audio thread. Sent to the thread as // an nsRunnableMethod. This continually does blocking writes to // to audio stream to play audio data. @@ -73,14 +87,20 @@ private: bool ExpectMoreAudioData(); - // Wait on the decoder monitor until playback is ready or the sink is told to shut down. - void WaitForAudioToPlay(); + // Return true if playback is not ready and the sink is not told to shut down. + bool WaitingForAudioToPlay(); // Check if the sink has been told to shut down, resuming mAudioStream if // not. Returns true if processing should continue, false if AudioLoop // should shutdown. bool IsPlaybackContinuing(); + // Write audio samples or silence to the audio hardware. + // Return false if any error. Called on the audio thread. + bool PlayAudio(); + + void FinishAudioLoop(); + // Write aFrames of audio frames of silence to the audio hardware. Returns // the number of frames actually written. The write size is capped at // SILENCE_BYTES_CHUNK (32kB), so must be called in a loop to write the @@ -94,8 +114,6 @@ private: // audio data to the audio hardware. Called on the audio thread. uint32_t PlayFromAudioQueue(); - void UpdateStreamSettings(); - // If we have already written enough frames to the AudioStream, start the // playback. void StartAudioStreamPlaybackIfNeeded(); @@ -114,10 +132,16 @@ private: } void AssertOnAudioThread(); + void AssertNotOnAudioThread(); MediaQueue& mAudioQueue; mutable ReentrantMonitor mMonitor; + // There members are accessed on the audio thread only. + State mState; + Maybe mPendingState; + bool mAudioLoopScheduled; + // Thread for pushing audio onto the audio hardware. // The "audio push thread". nsCOMPtr mThread; @@ -146,16 +170,8 @@ private: dom::AudioChannel mChannel; - double mVolume; - double mPlaybackRate; - bool mPreservesPitch; - bool mStopAudioThread; - bool mSetVolume; - bool mSetPlaybackRate; - bool mSetPreservesPitch; - bool mPlaying; MozPromiseHolder mEndPromise; diff --git a/dom/media/FileBlockCache.h b/dom/media/FileBlockCache.h index afcadb2e15..507d9dd60a 100644 --- a/dom/media/FileBlockCache.h +++ b/dom/media/FileBlockCache.h @@ -13,7 +13,7 @@ #include "MediaCache.h" #include "nsDeque.h" #include "nsThreadUtils.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" struct PRFileDesc; diff --git a/dom/media/MediaDecoderStateMachine.cpp b/dom/media/MediaDecoderStateMachine.cpp index ecdb43fec3..cf6a915022 100644 --- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -30,7 +30,7 @@ #include "nsITimer.h" #include "nsContentUtils.h" #include "MediaShutdownManager.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include "mozilla/TaskQueue.h" #include "nsIEventTarget.h" #include "prenv.h" diff --git a/dom/media/MediaFormatReader.cpp b/dom/media/MediaFormatReader.cpp index 7f6f934f07..1ef5e5343f 100644 --- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -15,7 +15,7 @@ #include "MediaFormatReader.h" #include "MediaResource.h" #include "SharedDecoderManager.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include "VideoUtils.h" #include @@ -257,10 +257,7 @@ MediaFormatReader::OnDemuxerInitDone(nsresult) if (videoActive) { // We currently only handle the first video track. mVideo.mTrackDemuxer = mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0); - if (!mVideo.mTrackDemuxer) { - mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__); - return; - } + MOZ_ASSERT(mVideo.mTrackDemuxer); mInfo.mVideo = *mVideo.mTrackDemuxer->GetInfo()->GetAsVideoInfo(); mVideo.mCallback = new DecoderCallback(this, TrackInfo::kVideoTrack); mVideo.mTimeRanges = mVideo.mTrackDemuxer->GetBuffered(); @@ -270,10 +267,7 @@ MediaFormatReader::OnDemuxerInitDone(nsresult) bool audioActive = !!mDemuxer->GetNumberTracks(TrackInfo::kAudioTrack); if (audioActive) { mAudio.mTrackDemuxer = mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0); - if (!mAudio.mTrackDemuxer) { - mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__); - return; - } + MOZ_ASSERT(mAudio.mTrackDemuxer); mInfo.mAudio = *mAudio.mTrackDemuxer->GetInfo()->GetAsAudioInfo(); mAudio.mCallback = new DecoderCallback(this, TrackInfo::kAudioTrack); mAudio.mTimeRanges = mAudio.mTrackDemuxer->GetBuffered(); @@ -317,18 +311,12 @@ MediaFormatReader::OnDemuxerInitDone(nsresult) if (videoActive) { mVideoTrackDemuxer = mMainThreadDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0); - if (!mVideoTrackDemuxer) { - mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__); - return; - } + MOZ_ASSERT(mVideoTrackDemuxer); } if (audioActive) { mAudioTrackDemuxer = mMainThreadDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0); - if (!mAudioTrackDemuxer) { - mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__); - return; - } + MOZ_ASSERT(mAudioTrackDemuxer); } mInitDone = true; @@ -374,7 +362,7 @@ MediaFormatReader::EnsureDecodersSetup() if (HasAudio() && !mAudio.mDecoder) { NS_ENSURE_TRUE(IsSupportedAudioMimeType(mInfo.mAudio.mMimeType), - false); + false); mAudio.mDecoder = mPlatform->CreateDecoder(mAudio.mInfo ? @@ -441,11 +429,15 @@ void MediaFormatReader::DisableHardwareAcceleration() { MOZ_ASSERT(OnTaskQueue()); - if (HasVideo() && mSharedDecoderManager) { - mSharedDecoderManager->DisableHardwareAcceleration(); - - if (!mSharedDecoderManager->Recreate(mInfo.mVideo)) { - mVideo.mError = true; + if (HasVideo()) { + mPlatform->DisableHardwareAcceleration(); + Flush(TrackInfo::kVideoTrack); + mVideo.mDecoder->Shutdown(); + mVideo.mDecoder = nullptr; + if (!EnsureDecodersSetup()) { + LOG("Unable to re-create decoder, aborting"); + NotifyError(TrackInfo::kVideoTrack); + return; } ScheduleUpdate(TrackInfo::kVideoTrack); } diff --git a/dom/media/MediaShutdownManager.cpp b/dom/media/MediaShutdownManager.cpp index 22833b2e82..9780ab74dd 100644 --- a/dom/media/MediaShutdownManager.cpp +++ b/dom/media/MediaShutdownManager.cpp @@ -8,7 +8,7 @@ #include "nsContentUtils.h" #include "mozilla/StaticPtr.h" #include "MediaDecoder.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include "mozilla/Logging.h" namespace mozilla { diff --git a/dom/media/MediaTimer.cpp b/dom/media/MediaTimer.cpp index ec342d5603..eacb3b8f65 100644 --- a/dom/media/MediaTimer.cpp +++ b/dom/media/MediaTimer.cpp @@ -12,7 +12,7 @@ #include "nsThreadUtils.h" #include "mozilla/DebugOnly.h" #include "mozilla/RefPtr.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" namespace mozilla { diff --git a/dom/media/TaskQueue.cpp b/dom/media/TaskQueue.cpp index db92462e1e..d35da725e0 100644 --- a/dom/media/TaskQueue.cpp +++ b/dom/media/TaskQueue.cpp @@ -7,7 +7,7 @@ #include "mozilla/TaskQueue.h" #include "nsThreadUtils.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" namespace mozilla { diff --git a/dom/media/TaskQueue.h b/dom/media/TaskQueue.h index 89533c8f65..2d77b27467 100644 --- a/dom/media/TaskQueue.h +++ b/dom/media/TaskQueue.h @@ -15,7 +15,7 @@ #include -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include "nsThreadUtils.h" class nsIRunnable; diff --git a/dom/media/VideoUtils.cpp b/dom/media/VideoUtils.cpp index f38d19243a..3df1483603 100644 --- a/dom/media/VideoUtils.cpp +++ b/dom/media/VideoUtils.cpp @@ -8,6 +8,7 @@ #include "mozilla/Base64.h" #include "mozilla/TaskQueue.h" #include "mozilla/Telemetry.h" +#include "mozilla/Function.h" #include "MediaResource.h" #include "TimeUnits.h" @@ -15,8 +16,7 @@ #include "nsSize.h" #include "VorbisUtils.h" #include "ImageContainer.h" -#include "SharedThreadPool.h" -#include "mozilla/Function.h" +#include "mozilla/SharedThreadPool.h" #include "nsIRandomGenerator.h" #include "nsIServiceManager.h" #include "nsCharSeparatedTokenizer.h" diff --git a/dom/media/gtest/TestMP4Demuxer.cpp b/dom/media/gtest/TestMP4Demuxer.cpp index 0f6b6e3da7..9db57df756 100644 --- a/dom/media/gtest/TestMP4Demuxer.cpp +++ b/dom/media/gtest/TestMP4Demuxer.cpp @@ -4,32 +4,151 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "gtest/gtest.h" -#include "mp4_demuxer/mp4_demuxer.h" +#include "MP4Demuxer.h" #include "MP4Stream.h" +#include "MozPromise.h" +#include "MediaDataDemuxer.h" +#include "mozilla/SharedThreadPool.h" +#include "TaskQueue.h" #include "mozilla/ArrayUtils.h" #include "MockMediaResource.h" using namespace mozilla; using namespace mp4_demuxer; +class AutoTaskQueue; + +#define DO_FAIL []()->void { EXPECT_TRUE(false); } + class MP4DemuxerBinding { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MP4DemuxerBinding); nsRefPtr resource; - Monitor mMonitor; - nsRefPtr demuxer; + nsRefPtr mDemuxer; + nsRefPtr mTaskQueue; + nsRefPtr mAudioTrack; + nsRefPtr mVideoTrack; + uint32_t mIndex; + nsTArray> mSamples; + nsTArray mKeyFrameTimecodes; + MozPromiseHolder mCheckTrackKeyFramePromise; + MozPromiseHolder mCheckTrackSamples; explicit MP4DemuxerBinding(const char* aFileName = "dash_dashinit.mp4") : resource(new MockMediaResource(aFileName)) - , mMonitor("TestMP4Demuxer monitor") - , demuxer(new MP4Demuxer(new MP4Stream(resource), &mMonitor)) + , mDemuxer(new MP4Demuxer(resource)) + , mTaskQueue(new TaskQueue(GetMediaThreadPool(MediaThreadType::PLAYBACK))) + , mIndex(0) { EXPECT_EQ(NS_OK, resource->Open(nullptr)); } + template + void RunTestAndWait(const Function& aFunction) + { + Function func(aFunction); + mDemuxer->Init()->Then(mTaskQueue, __func__, Move(func), DO_FAIL); + mTaskQueue->AwaitShutdownAndIdle(); + } + + nsRefPtr + CheckTrackKeyFrame(MediaTrackDemuxer* aTrackDemuxer) + { + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + + nsRefPtr track = aTrackDemuxer; + nsRefPtr self = this; + + int64_t time = -1; + while (mIndex < mSamples.Length()) { + uint32_t i = mIndex++; + if (mSamples[i]->mKeyframe) { + time = mSamples[i]->mTime; + break; + } + } + + nsRefPtr p = mCheckTrackKeyFramePromise.Ensure(__func__); + + if (time == -1) { + mCheckTrackKeyFramePromise.Resolve(true, __func__); + return p; + } + + + DispatchTask( + [track, time, self] () { + track->Seek(media::TimeUnit::FromMicroseconds(time))->Then(self->mTaskQueue, __func__, + [track, time, self] () { + track->GetSamples()->Then(self->mTaskQueue, __func__, + [track, time, self] (nsRefPtr aSamples) { + EXPECT_EQ(time, aSamples->mSamples[0]->mTime); + self->CheckTrackKeyFrame(track); + }, + DO_FAIL + ); + }, + DO_FAIL + ); + } + ); + + return p; + } + + nsRefPtr + CheckTrackSamples(MediaTrackDemuxer* aTrackDemuxer) + { + MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn()); + + nsRefPtr track = aTrackDemuxer; + nsRefPtr self = this; + + nsRefPtr p = mCheckTrackSamples.Ensure(__func__); + + DispatchTask( + [track, self] () { + track->GetSamples()->Then(self->mTaskQueue, __func__, + [track, self] (nsRefPtr aSamples) { + if (aSamples->mSamples.Length()) { + self->mSamples.AppendElements(aSamples->mSamples); + self->CheckTrackSamples(track); + } + }, + [self] (DemuxerFailureReason aReason) { + if (aReason == DemuxerFailureReason::DEMUXER_ERROR) { + EXPECT_TRUE(false); + self->mCheckTrackSamples.Reject(NS_ERROR_FAILURE, __func__); + } else if (aReason == DemuxerFailureReason::END_OF_STREAM) { + EXPECT_TRUE(self->mSamples.Length() > 1); + for (uint32_t i = 0; i < (self->mSamples.Length() - 1); i++) { + EXPECT_LT(self->mSamples[i]->mTimecode, self->mSamples[i + 1]->mTimecode); + if (self->mSamples[i]->mKeyframe) { + self->mKeyFrameTimecodes.AppendElement(self->mSamples[i]->mTimecode); + } + } + self->mCheckTrackSamples.Resolve(true, __func__); + } + } + ); + } + ); + + return p; + } + private: + + template + void + DispatchTask(FunctionType aFun) + { + nsRefPtr r = NS_NewRunnableFunction(aFun); + mTaskQueue->Dispatch(r.forget()); + } + virtual ~MP4DemuxerBinding() { } @@ -37,30 +156,20 @@ private: TEST(MP4Demuxer, Seek) { - nsRefPtr b = new MP4DemuxerBinding(); - MonitorAutoLock mon(b->mMonitor); - MP4Demuxer* d = b->demuxer; + nsRefPtr binding = new MP4DemuxerBinding(); - EXPECT_TRUE(d->Init()); - - nsTArray> samples; - nsRefPtr sample; - while (!!(sample = d->DemuxVideoSample())) { - samples.AppendElement(sample); - if (samples.Length() >= 2) { - EXPECT_LT(samples[samples.Length() - 2]->mTimecode, - samples[samples.Length() - 1]->mTimecode); - } - } - Microseconds keyFrame = 0; - for (size_t i = 0; i < samples.Length(); i++) { - if (samples[i]->mKeyframe) { - keyFrame = samples[i]->mTimecode; - } - d->SeekVideo(samples[i]->mTime); - sample = d->DemuxVideoSample(); - EXPECT_EQ(keyFrame, sample->mTimecode); - } + binding->RunTestAndWait([binding] () { + binding->mVideoTrack = binding->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0); + binding->CheckTrackSamples(binding->mVideoTrack) + ->Then(binding->mTaskQueue, __func__, + [binding] () { + binding->CheckTrackKeyFrame(binding->mVideoTrack) + ->Then(binding->mTaskQueue, __func__, + [binding] () { + binding->mTaskQueue->BeginShutdown(); + }, DO_FAIL); + }, DO_FAIL); + }); } static nsCString @@ -87,14 +196,10 @@ ToCryptoString(const CryptoSample& aCrypto) return res; } +#ifndef XP_WIN // VC2013 doesn't support C++11 array initialization. + TEST(MP4Demuxer, CENCFrag) { - nsRefPtr b = new MP4DemuxerBinding("gizmo-frag.mp4"); - MonitorAutoLock mon(b->mMonitor); - MP4Demuxer* d = b->demuxer; - - EXPECT_TRUE(d->Init()); - const char* video[] = { "1 16 7e571d037e571d037e571d037e571d03 00000000000000000000000000000000 5,684 5,16980", "1 16 7e571d037e571d037e571d037e571d03 00000000000000000000000000000450 5,1826", @@ -158,13 +263,22 @@ TEST(MP4Demuxer, CENCFrag) "1 16 7e571d037e571d037e571d037e571d03 000000000000000000000000000019cd 5,2392", }; - nsRefPtr sample; - size_t i = 0; - while (!!(sample = d->DemuxVideoSample())) { - nsCString text = ToCryptoString(sample->mCrypto); - EXPECT_STREQ(video[i++], text.get()); - } - EXPECT_EQ(ArrayLength(video), i); + nsRefPtr binding = new MP4DemuxerBinding("gizmo-frag.mp4"); + + binding->RunTestAndWait([binding, video] () { + // grab all video samples. + binding->mVideoTrack = binding->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0); + binding->CheckTrackSamples(binding->mVideoTrack) + ->Then(binding->mTaskQueue, __func__, + [binding, video] () { + for (uint32_t i = 0; i < binding->mSamples.Length(); i++) { + nsCString text = ToCryptoString(binding->mSamples[i]->mCrypto); + EXPECT_STREQ(video[i++], text.get()); + } + EXPECT_EQ(ArrayLength(video), binding->mSamples.Length()); + binding->mTaskQueue->BeginShutdown(); + }, DO_FAIL); + }); const char* audio[] = { "1 16 7e571d047e571d047e571d047e571d04 00000000000000000000000000000000 0,281", @@ -262,42 +376,52 @@ TEST(MP4Demuxer, CENCFrag) "1 16 7e571d047e571d047e571d047e571d04 000000000000000000000000000008cd 0,433", "1 16 7e571d047e571d047e571d047e571d04 000000000000000000000000000008e9 0,481", }; + nsRefPtr audiobinding = new MP4DemuxerBinding("gizmo-frag.mp4"); - i = 0; - while (!!(sample = d->DemuxAudioSample())) { - nsCString text = ToCryptoString(sample->mCrypto); - EXPECT_STREQ(audio[i++], text.get()); - } - EXPECT_EQ(ArrayLength(audio), i); + audiobinding->RunTestAndWait([audiobinding, audio] () { + // grab all audio samples. + audiobinding->mAudioTrack = audiobinding->mDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0); + audiobinding->CheckTrackSamples(audiobinding->mAudioTrack) + ->Then(audiobinding->mTaskQueue, __func__, + [audiobinding, audio] () { + EXPECT_TRUE(audiobinding->mSamples.Length() > 1); + for (uint32_t i = 0; i < audiobinding->mSamples.Length(); i++) { + nsCString text = ToCryptoString(audiobinding->mSamples[i]->mCrypto); + EXPECT_STREQ(audio[i++], text.get()); + } + EXPECT_EQ(ArrayLength(audio), audiobinding->mSamples.Length()); + audiobinding->mTaskQueue->BeginShutdown(); + }, DO_FAIL); + }); } +#endif + TEST(MP4Demuxer, GetNextKeyframe) { - nsRefPtr b = new MP4DemuxerBinding("gizmo-frag.mp4"); - MonitorAutoLock mon(b->mMonitor); - MP4Demuxer* d = b->demuxer; + nsRefPtr binding = new MP4DemuxerBinding("gizmo-frag.mp4"); - EXPECT_TRUE(d->Init()); + binding->RunTestAndWait([binding] () { + // Insert a [0,end] buffered range, to simulate Moof's being buffered + // via MSE. + auto len = binding->resource->GetLength(); + binding->resource->MockAddBufferedRange(0, len); - // Insert a [0,end] buffered range, to simulate Moof's being buffered - // via MSE. - auto len = b->resource->GetLength(); - b->resource->MockAddBufferedRange(0, len); - - // Rebuild the index so that it can be used to find the keyframes. - nsTArray ranges; - EXPECT_TRUE(NS_SUCCEEDED(b->resource->GetCachedRanges(ranges))); - d->UpdateIndex(ranges); - - // gizmp-frag has two keyframes; one at dts=cts=0, and another at - // dts=cts=1000000. Verify we get expected results. - - nsRefPtr sample; - size_t i = 0; - const int64_t keyframe = 1000000; - while (!!(sample = d->DemuxVideoSample())) { - int64_t expected = (sample->mTimecode < keyframe) ? keyframe : -1; - EXPECT_EQ(d->GetNextKeyframeTime(), expected); - i++; - } + // gizmp-frag has two keyframes; one at dts=cts=0, and another at + // dts=cts=1000000. Verify we get expected results. + media::TimeUnit time; + binding->mVideoTrack = binding->mDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0); + binding->mVideoTrack->Reset(); + binding->mVideoTrack->GetNextRandomAccessPoint(&time); + EXPECT_EQ(time.ToMicroseconds(), 0); + binding->mVideoTrack->GetSamples()->Then(binding->mTaskQueue, __func__, + [binding] () { + media::TimeUnit time; + binding->mVideoTrack->GetNextRandomAccessPoint(&time); + EXPECT_EQ(time.ToMicroseconds(), 1000000); + binding->mTaskQueue->BeginShutdown(); + }, + DO_FAIL + ); + }); } diff --git a/dom/media/gtest/TestMP4Reader.cpp b/dom/media/gtest/TestMP4Reader.cpp index 6069ea3637..4f2742bb05 100644 --- a/dom/media/gtest/TestMP4Reader.cpp +++ b/dom/media/gtest/TestMP4Reader.cpp @@ -6,7 +6,7 @@ #include "gtest/gtest.h" #include "MP4Reader.h" #include "MP4Decoder.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include "MockMediaResource.h" #include "MockMediaDecoderOwner.h" #include "mozilla/Preferences.h" diff --git a/dom/media/gtest/TestMozPromise.cpp b/dom/media/gtest/TestMozPromise.cpp index 9c04e33888..19816136a9 100644 --- a/dom/media/gtest/TestMozPromise.cpp +++ b/dom/media/gtest/TestMozPromise.cpp @@ -9,7 +9,7 @@ #include "mozilla/MozPromise.h" #include "nsISupportsImpl.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include "VideoUtils.h" using namespace mozilla; diff --git a/dom/media/gtest/moz.build b/dom/media/gtest/moz.build index 20da5c7768..39878a3ad3 100644 --- a/dom/media/gtest/moz.build +++ b/dom/media/gtest/moz.build @@ -11,6 +11,7 @@ SOURCES += [ 'TestIntervalSet.cpp', 'TestMozPromise.cpp', 'TestMP3Demuxer.cpp', + 'TestMP4Demuxer.cpp', 'TestMP4Reader.cpp', 'TestTrackEncoder.cpp', 'TestVideoSegment.cpp', diff --git a/dom/media/mediasource/TrackBuffer.cpp b/dom/media/mediasource/TrackBuffer.cpp index 4d23129a87..c35262881e 100644 --- a/dom/media/mediasource/TrackBuffer.cpp +++ b/dom/media/mediasource/TrackBuffer.cpp @@ -9,7 +9,7 @@ #include "ContainerParser.h" #include "MediaData.h" #include "MediaSourceDecoder.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include "mozilla/TaskQueue.h" #include "SourceBufferDecoder.h" #include "SourceBufferResource.h" diff --git a/dom/media/moz.build b/dom/media/moz.build index 8b71cc6b67..8164e56e8d 100644 --- a/dom/media/moz.build +++ b/dom/media/moz.build @@ -133,7 +133,6 @@ EXPORTS += [ 'RtspMediaResource.h', 'SelfRef.h', 'SharedBuffer.h', - 'SharedThreadPool.h', 'StreamBuffer.h', 'ThreadPoolCOMListener.h', 'TimeUnits.h', @@ -146,12 +145,10 @@ EXPORTS += [ ] EXPORTS.mozilla += [ - 'AbstractThread.h', 'MediaManager.h', 'MozPromise.h', 'StateMirroring.h', 'StateWatching.h', - 'TaskDispatcher.h', 'TaskQueue.h', ] @@ -190,7 +187,6 @@ EXPORTS.mozilla.dom += [ ] UNIFIED_SOURCES += [ - 'AbstractThread.cpp', 'ADTSDecoder.cpp', 'ADTSDemuxer.cpp', 'AudioCaptureStream.cpp', @@ -233,7 +229,6 @@ UNIFIED_SOURCES += [ 'MP3Demuxer.cpp', 'MP3FrameParser.cpp', 'RtspMediaResource.cpp', - 'SharedThreadPool.cpp', 'StreamBuffer.cpp', 'TaskQueue.cpp', 'TextTrack.cpp', diff --git a/dom/media/omx/MediaCodecReader.cpp b/dom/media/omx/MediaCodecReader.cpp index de6e725246..ca5b4b3d22 100644 --- a/dom/media/omx/MediaCodecReader.cpp +++ b/dom/media/omx/MediaCodecReader.cpp @@ -36,7 +36,7 @@ #include "nsMimeTypes.h" #include "nsThreadUtils.h" #include "ImageContainer.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include "VideoFrameContainer.h" #include "VideoUtils.h" diff --git a/dom/media/platforms/PlatformDecoderModule.cpp b/dom/media/platforms/PlatformDecoderModule.cpp index 541d94497b..0c68ec26e5 100644 --- a/dom/media/platforms/PlatformDecoderModule.cpp +++ b/dom/media/platforms/PlatformDecoderModule.cpp @@ -27,7 +27,7 @@ #include "mozilla/TaskQueue.h" #include "mozilla/WindowsVersion.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include "MediaInfo.h" #include "H264Converter.h" diff --git a/dom/media/platforms/SharedDecoderManager.cpp b/dom/media/platforms/SharedDecoderManager.cpp index 39158280c5..5dafe6fa90 100644 --- a/dom/media/platforms/SharedDecoderManager.cpp +++ b/dom/media/platforms/SharedDecoderManager.cpp @@ -55,7 +55,7 @@ SharedDecoderManager::SharedDecoderManager() , mActiveProxy(nullptr) , mActiveCallback(nullptr) , mWaitForInternalDrain(false) - , mMonitor("SharedDecoderProxy") + , mMonitor("SharedDecoderManager") , mDecoderReleasedResources(false) { MOZ_ASSERT(NS_IsMainThread()); // taskqueue must be created on main thread. diff --git a/dom/media/platforms/wmf/DXVA2Manager.cpp b/dom/media/platforms/wmf/DXVA2Manager.cpp index 43b8597dab..a9bbcfcb79 100644 --- a/dom/media/platforms/wmf/DXVA2Manager.cpp +++ b/dom/media/platforms/wmf/DXVA2Manager.cpp @@ -245,6 +245,8 @@ public: virtual HRESULT ConfigureForSize(uint32_t aWidth, uint32_t aHeight) override; + virtual bool IsD3D11() override { return true; } + private: HRESULT CreateFormatConverter(); diff --git a/dom/media/platforms/wmf/DXVA2Manager.h b/dom/media/platforms/wmf/DXVA2Manager.h index 5770e4d338..9908827650 100644 --- a/dom/media/platforms/wmf/DXVA2Manager.h +++ b/dom/media/platforms/wmf/DXVA2Manager.h @@ -40,6 +40,8 @@ public: virtual HRESULT ConfigureForSize(uint32_t aWidth, uint32_t aHeight) { return S_OK; } + virtual bool IsD3D11() { return false; } + virtual ~DXVA2Manager(); protected: diff --git a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp index 6c56cbe8c6..d5f2c9c706 100644 --- a/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp +++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.cpp @@ -23,7 +23,6 @@ WMFMediaDataDecoder::WMFMediaDataDecoder(MFTManager* aMFTManager, , mCallback(aCallback) , mMFTManager(aMFTManager) , mMonitor("WMFMediaDataDecoder") - , mIsDecodeTaskDispatched(false) , mIsFlushing(false) , mIsShutDown(false) { @@ -71,18 +70,6 @@ WMFMediaDataDecoder::ProcessShutdown() mDecoder = nullptr; } -void -WMFMediaDataDecoder::EnsureDecodeTaskDispatched() -{ - mMonitor.AssertCurrentThreadOwns(); - if (!mIsDecodeTaskDispatched) { - nsCOMPtr runnable = - NS_NewRunnableMethod(this, &WMFMediaDataDecoder::Decode); - mTaskQueue->Dispatch(runnable.forget()); - mIsDecodeTaskDispatched = true; - } -} - // Inserts data into the decoder's pipeline. nsresult WMFMediaDataDecoder::Input(MediaRawData* aSample) @@ -90,50 +77,36 @@ WMFMediaDataDecoder::Input(MediaRawData* aSample) MOZ_ASSERT(mCallback->OnReaderTaskQueue()); MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown); - MonitorAutoLock mon(mMonitor); - mInput.push(aSample); - EnsureDecodeTaskDispatched(); + nsCOMPtr runnable = + NS_NewRunnableMethodWithArg>( + this, + &WMFMediaDataDecoder::ProcessDecode, + nsRefPtr(aSample)); + mTaskQueue->Dispatch(runnable.forget()); return NS_OK; } void -WMFMediaDataDecoder::Decode() +WMFMediaDataDecoder::ProcessDecode(MediaRawData* aSample) { - while (true) { - nsRefPtr input; - { - MonitorAutoLock mon(mMonitor); - MOZ_ASSERT(mIsDecodeTaskDispatched); - if (mInput.empty()) { - if (mIsFlushing) { - if (mDecoder) { - mDecoder->Flush(); - } - mIsFlushing = false; - } - mIsDecodeTaskDispatched = false; - mon.NotifyAll(); - return; - } - input = mInput.front(); - mInput.pop(); + { + MonitorAutoLock mon(mMonitor); + if (mIsFlushing) { + // Skip sample, to be released by runnable. + return; } - - HRESULT hr = mMFTManager->Input(input); - if (FAILED(hr)) { - NS_WARNING("MFTManager rejected sample"); - { - MonitorAutoLock mon(mMonitor); - PurgeInputQueue(); - } - mCallback->Error(); - continue; // complete flush if flushing - } - - mLastStreamOffset = input->mOffset; - - ProcessOutput(); } + + HRESULT hr = mMFTManager->Input(aSample); + if (FAILED(hr)) { + NS_WARNING("MFTManager rejected sample"); + mCallback->Error(); + return; + } + + mLastStreamOffset = aSample->mOffset; + + ProcessOutput(); } void @@ -151,21 +124,19 @@ WMFMediaDataDecoder::ProcessOutput() } } else if (FAILED(hr)) { NS_WARNING("WMFMediaDataDecoder failed to output data"); - { - MonitorAutoLock mon(mMonitor); - PurgeInputQueue(); - } mCallback->Error(); } } void -WMFMediaDataDecoder::PurgeInputQueue() +WMFMediaDataDecoder::ProcessFlush() { - mMonitor.AssertCurrentThreadOwns(); - while (!mInput.empty()) { - mInput.pop(); + if (mDecoder) { + mDecoder->Flush(); } + MonitorAutoLock mon(mMonitor); + mIsFlushing = false; + mon.NotifyAll(); } nsresult @@ -174,11 +145,12 @@ WMFMediaDataDecoder::Flush() MOZ_ASSERT(mCallback->OnReaderTaskQueue()); MOZ_DIAGNOSTIC_ASSERT(!mIsShutDown); + nsCOMPtr runnable = + NS_NewRunnableMethod(this, &WMFMediaDataDecoder::ProcessFlush); MonitorAutoLock mon(mMonitor); - PurgeInputQueue(); mIsFlushing = true; - EnsureDecodeTaskDispatched(); - while (mIsDecodeTaskDispatched || mIsFlushing) { + mTaskQueue->Dispatch(runnable.forget()); + while (mIsFlushing) { mon.Wait(); } return NS_OK; @@ -187,7 +159,12 @@ WMFMediaDataDecoder::Flush() void WMFMediaDataDecoder::ProcessDrain() { - if (mDecoder) { + bool isFlushing; + { + MonitorAutoLock mon(mMonitor); + isFlushing = mIsFlushing; + } + if (!isFlushing && mDecoder) { // Order the decoder to drain... if (FAILED(mDecoder->SendMFTMessage(MFT_MESSAGE_COMMAND_DRAIN, 0))) { NS_WARNING("Failed to send DRAIN command to MFT"); diff --git a/dom/media/platforms/wmf/WMFMediaDataDecoder.h b/dom/media/platforms/wmf/WMFMediaDataDecoder.h index df2a8aae81..b4528d0fff 100644 --- a/dom/media/platforms/wmf/WMFMediaDataDecoder.h +++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.h @@ -75,14 +75,16 @@ private: // Called on the task queue. Inserts the sample into the decoder, and // extracts output if available. - void Decode(); - void EnsureDecodeTaskDispatched(); - void PurgeInputQueue(); + void ProcessDecode(MediaRawData* 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 flush. There is no output to + // extract. + void ProcessFlush(); + // Called on the task queue. Orders the MFT to drain, and then extracts // all available output. void ProcessDrain(); @@ -99,10 +101,13 @@ private: // This is used to approximate the decoder's position in the media resource. int64_t mLastStreamOffset; + // For access to and waiting on mIsFlushing Monitor mMonitor; - std::queue> mInput; - bool mIsDecodeTaskDispatched; + // Set on reader/decode thread calling Flush() to indicate that output is + // not required and so input samples on mTaskQueue need not be processed. + // Cleared on mTaskQueue. bool mIsFlushing; + bool mIsShutDown; }; diff --git a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp index 7830aff65d..c7779e7cb2 100644 --- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp +++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp @@ -145,7 +145,7 @@ public: }; bool -WMFVideoMFTManager::InitializeDXVA() +WMFVideoMFTManager::InitializeDXVA(bool aForceD3D9) { MOZ_ASSERT(!mDXVA2Manager); @@ -159,7 +159,8 @@ WMFVideoMFTManager::InitializeDXVA() } // The DXVA manager must be created on the main thread. - nsRefPtr event(new CreateDXVAManagerEvent(mLayersBackend)); + nsRefPtr event = + new CreateDXVAManagerEvent(aForceD3D9 ? LayersBackend::LAYERS_D3D9 : mLayersBackend); if (NS_IsMainThread()) { event->Run(); @@ -173,9 +174,24 @@ WMFVideoMFTManager::InitializeDXVA() already_AddRefed WMFVideoMFTManager::Init() +{ + RefPtr decoder = InitInternal(/* aForceD3D9 = */ false); + + // If initialization failed with d3d11 DXVA then try falling back + // to d3d9. + if (!decoder && mDXVA2Manager && mDXVA2Manager->IsD3D11()) { + mDXVA2Manager = nullptr; + decoder = InitInternal(true); + } + + return decoder.forget(); +} + +already_AddRefed +WMFVideoMFTManager::InitInternal(bool aForceD3D9) { mUseHwAccel = false; // default value; changed if D3D setup succeeds. - bool useDxva = InitializeDXVA(); + bool useDxva = InitializeDXVA(aForceD3D9); RefPtr decoder(new MFTDecoder()); diff --git a/dom/media/platforms/wmf/WMFVideoMFTManager.h b/dom/media/platforms/wmf/WMFVideoMFTManager.h index 9c0959f8d1..aad1fa3c9b 100644 --- a/dom/media/platforms/wmf/WMFVideoMFTManager.h +++ b/dom/media/platforms/wmf/WMFVideoMFTManager.h @@ -38,7 +38,9 @@ public: private: - bool InitializeDXVA(); + bool InitializeDXVA(bool aForceD3D9); + + already_AddRefed InitInternal(bool aForceD3D9); HRESULT ConfigureVideoFrameGeometry(); diff --git a/dom/media/webm/IntelWebMVideoDecoder.cpp b/dom/media/webm/IntelWebMVideoDecoder.cpp index ad022c4299..72a0fe1cdc 100644 --- a/dom/media/webm/IntelWebMVideoDecoder.cpp +++ b/dom/media/webm/IntelWebMVideoDecoder.cpp @@ -12,7 +12,7 @@ #include "MediaResource.h" #include "mozilla/dom/HTMLMediaElement.h" #include "nsError.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include "VorbisUtils.h" #include "nestegg/nestegg.h" diff --git a/dom/media/webm/WebMDemuxer.cpp b/dom/media/webm/WebMDemuxer.cpp index d4036f2796..b34de66bbe 100644 --- a/dom/media/webm/WebMDemuxer.cpp +++ b/dom/media/webm/WebMDemuxer.cpp @@ -12,7 +12,7 @@ #include "WebMBufferedParser.h" #include "gfx2DGlue.h" #include "mozilla/Preferences.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include "MediaDataDemuxer.h" #include "nsAutoRef.h" #include "NesteggPacketHolder.h" diff --git a/dom/media/webm/WebMReader.cpp b/dom/media/webm/WebMReader.cpp index e3587f2489..1eef3c3374 100644 --- a/dom/media/webm/WebMReader.cpp +++ b/dom/media/webm/WebMReader.cpp @@ -13,7 +13,7 @@ #include "gfx2DGlue.h" #include "Layers.h" #include "mozilla/Preferences.h" -#include "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include @@ -280,7 +280,8 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) { // We can't use OnTaskQueue() here because of the wacky initialization task - // queue that TrackBuffer uses. + // queue that TrackBuffer uses. We should be able to fix this when we do + // bug 1148234. MOZ_ASSERT(mDecoder->OnDecodeTaskQueue()); nestegg_io io; diff --git a/dom/media/AbstractThread.cpp b/xpcom/threads/AbstractThread.cpp similarity index 100% rename from dom/media/AbstractThread.cpp rename to xpcom/threads/AbstractThread.cpp diff --git a/dom/media/AbstractThread.h b/xpcom/threads/AbstractThread.h similarity index 100% rename from dom/media/AbstractThread.h rename to xpcom/threads/AbstractThread.h diff --git a/dom/media/SharedThreadPool.cpp b/xpcom/threads/SharedThreadPool.cpp similarity index 99% rename from dom/media/SharedThreadPool.cpp rename to xpcom/threads/SharedThreadPool.cpp index cde6eca41b..d48bc6677f 100644 --- a/dom/media/SharedThreadPool.cpp +++ b/xpcom/threads/SharedThreadPool.cpp @@ -4,7 +4,7 @@ * 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 "SharedThreadPool.h" +#include "mozilla/SharedThreadPool.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/Monitor.h" #include "mozilla/ReentrantMonitor.h" diff --git a/dom/media/SharedThreadPool.h b/xpcom/threads/SharedThreadPool.h similarity index 100% rename from dom/media/SharedThreadPool.h rename to xpcom/threads/SharedThreadPool.h diff --git a/dom/media/TaskDispatcher.h b/xpcom/threads/TaskDispatcher.h similarity index 100% rename from dom/media/TaskDispatcher.h rename to xpcom/threads/TaskDispatcher.h diff --git a/xpcom/threads/moz.build b/xpcom/threads/moz.build index d7b9385c0e..5d68c45618 100644 --- a/xpcom/threads/moz.build +++ b/xpcom/threads/moz.build @@ -28,14 +28,18 @@ EXPORTS += [ ] EXPORTS.mozilla += [ + 'AbstractThread.h', 'BackgroundHangMonitor.h', 'HangAnnotations.h', 'HangMonitor.h', 'LazyIdleThread.h', + 'SharedThreadPool.h', 'SyncRunnable.h', + 'TaskDispatcher.h', ] UNIFIED_SOURCES += [ + 'AbstractThread.cpp', 'BackgroundHangMonitor.cpp', 'HangAnnotations.cpp', 'HangMonitor.cpp', @@ -48,6 +52,7 @@ UNIFIED_SOURCES += [ 'nsThreadManager.cpp', 'nsThreadPool.cpp', 'nsTimerImpl.cpp', + 'SharedThreadPool.cpp', 'ThreadStackHelper.cpp', 'TimerThread.cpp', ]