import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1187817. Part 1 - Move Set{Volume,PlaybackRate,PreservesPitch} to the audio thread. r=kinetik. (a234a7080)
- Bug 1187817. Part 2 - remove unused code. r=kinetik. (836c52a19)
- Bug 1187763. Part 1 - move while loop out of WaitingForAudioToPlay(). r=kinetik. (a80d70f87)
- Bug 1187763. Part 2 - extract some code of AudioLoop() into its own function. r=kinetik. (275b8bfb6)
- Bug 1187763. Part 3 - refactor AudioSink::AudioLoop into a series of events. r=kinetik (7eb3f506f)
- Bug 1187817. Part 3 - move SetPlaying to the audio thread. r=kinetik. (0fef85968)
- Bug 1187817. Part 4 - move some code in Shutdown to the audio thread. r=kinetik. (8c73fbe6d)
- Bug 1187817. Part 5 - assert some code in the audio thread and don't enter the monitor. r=kinetik. (64de4616f)
- move include (f842b4b1e)
- Bug 1163486 - Update test to use new MP4Demuxer. r=bholley (b853be477)
- Bug 1190496 - Namespace the SharedThreadPool.h include. r=cpearce (196d25d42)
- Bug 1190496 - Hoist SharedThreadPool into xpcom. r=froydnj (3dad8176f)
- Bug 1190492 - Hoist AbstractThread and TaskDispatcher to xpcom. r=froydnj (c3329fa29)
- bug 1166107 documentation of mWaitForInternalDrain thread access r=gerald (f762764b1)
- correct comment (b623b2959)
- revert demuxer check from promise reject to assert like in 1156708 (f540b270c)
- more chekcs back to asserts (7e82a0f99)
- space (733bd85a3)
- Bug 1188220: Allow disabling HW acceleration even when SharedDecoderManager isn't used. r=cpearce (909a86682)
- bug 1161903 ensure pending DrainComplete is not run after Flush() r=cpearce (648cabbb7)
- Bug 1158089 - Fall back to d3d9 DXVA if d3d11 initialization fails. r=cpearce (84b3a8e6d)
This commit is contained in:
2021-08-20 10:57:59 +08:00
parent 3b90b72ef4
commit d1af43433c
34 changed files with 577 additions and 352 deletions
+237 -150
View File
@@ -10,6 +10,7 @@
#include "VideoUtils.h"
#include "mozilla/CheckedInt.h"
#include "mozilla/DebugOnly.h"
namespace mozilla {
@@ -28,22 +29,60 @@ AudioSink::AudioSink(MediaQueue<AudioData>& 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<nsIRunnable>&& event)
{
DebugOnly<nsresult> 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<nsIRunnable> r = NS_NewRunnableMethod(this, &AudioSink::AudioLoop);
DispatchTask(r.forget());
}
void
AudioSink::ScheduleNextLoopCrossThread()
{
AssertNotOnAudioThread();
nsRefPtr<AudioSink> self = this;
nsCOMPtr<nsIRunnable> 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<GenericPromise>
AudioSink::Init()
{
@@ -57,13 +96,7 @@ AudioSink::Init()
return p;
}
nsCOMPtr<nsIRunnable> 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<AudioSink> self = this;
nsCOMPtr<nsIRunnable> 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<AudioSink> self = this;
nsCOMPtr<nsIRunnable> 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<AudioSink> self = this;
nsCOMPtr<nsIRunnable> 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<AudioSink> self = this;
nsCOMPtr<nsIRunnable> 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<AudioSink> self = this;
nsCOMPtr<nsIRunnable> 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<int64_t>(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<int64_t>(UINT32_MAX, missingFrames.value());
mWritten += PlaySilence(static_cast<uint32_t>(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<int64_t>(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<int64_t>(UINT32_MAX, missingFrames.value());
mWritten += PlaySilence(static_cast<uint32_t>(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
+28 -12
View File
@@ -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<nsIRunnable>&& 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<AudioData>& mAudioQueue;
mutable ReentrantMonitor mMonitor;
// There members are accessed on the audio thread only.
State mState;
Maybe<State> mPendingState;
bool mAudioLoopScheduled;
// Thread for pushing audio onto the audio hardware.
// The "audio push thread".
nsCOMPtr<nsIThread> 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<GenericPromise> mEndPromise;
+1 -1
View File
@@ -13,7 +13,7 @@
#include "MediaCache.h"
#include "nsDeque.h"
#include "nsThreadUtils.h"
#include "SharedThreadPool.h"
#include "mozilla/SharedThreadPool.h"
struct PRFileDesc;
+1 -1
View File
@@ -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"
+15 -23
View File
@@ -15,7 +15,7 @@
#include "MediaFormatReader.h"
#include "MediaResource.h"
#include "SharedDecoderManager.h"
#include "SharedThreadPool.h"
#include "mozilla/SharedThreadPool.h"
#include "VideoUtils.h"
#include <algorithm>
@@ -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);
}
+1 -1
View File
@@ -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 {
+1 -1
View File
@@ -12,7 +12,7 @@
#include "nsThreadUtils.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/RefPtr.h"
#include "SharedThreadPool.h"
#include "mozilla/SharedThreadPool.h"
namespace mozilla {
+1 -1
View File
@@ -7,7 +7,7 @@
#include "mozilla/TaskQueue.h"
#include "nsThreadUtils.h"
#include "SharedThreadPool.h"
#include "mozilla/SharedThreadPool.h"
namespace mozilla {
+1 -1
View File
@@ -15,7 +15,7 @@
#include <queue>
#include "SharedThreadPool.h"
#include "mozilla/SharedThreadPool.h"
#include "nsThreadUtils.h"
class nsIRunnable;
+2 -2
View File
@@ -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"
+196 -72
View File
@@ -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<MockMediaResource> resource;
Monitor mMonitor;
nsRefPtr<MP4Demuxer> demuxer;
nsRefPtr<MP4Demuxer> mDemuxer;
nsRefPtr<TaskQueue> mTaskQueue;
nsRefPtr<MediaTrackDemuxer> mAudioTrack;
nsRefPtr<MediaTrackDemuxer> mVideoTrack;
uint32_t mIndex;
nsTArray<nsRefPtr<MediaRawData>> mSamples;
nsTArray<int64_t> mKeyFrameTimecodes;
MozPromiseHolder<GenericPromise> mCheckTrackKeyFramePromise;
MozPromiseHolder<GenericPromise> 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<typename Function>
void RunTestAndWait(const Function& aFunction)
{
Function func(aFunction);
mDemuxer->Init()->Then(mTaskQueue, __func__, Move(func), DO_FAIL);
mTaskQueue->AwaitShutdownAndIdle();
}
nsRefPtr<GenericPromise>
CheckTrackKeyFrame(MediaTrackDemuxer* aTrackDemuxer)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
nsRefPtr<MediaTrackDemuxer> track = aTrackDemuxer;
nsRefPtr<MP4DemuxerBinding> self = this;
int64_t time = -1;
while (mIndex < mSamples.Length()) {
uint32_t i = mIndex++;
if (mSamples[i]->mKeyframe) {
time = mSamples[i]->mTime;
break;
}
}
nsRefPtr<GenericPromise> 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<MediaTrackDemuxer::SamplesHolder> aSamples) {
EXPECT_EQ(time, aSamples->mSamples[0]->mTime);
self->CheckTrackKeyFrame(track);
},
DO_FAIL
);
},
DO_FAIL
);
}
);
return p;
}
nsRefPtr<GenericPromise>
CheckTrackSamples(MediaTrackDemuxer* aTrackDemuxer)
{
MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
nsRefPtr<MediaTrackDemuxer> track = aTrackDemuxer;
nsRefPtr<MP4DemuxerBinding> self = this;
nsRefPtr<GenericPromise> p = mCheckTrackSamples.Ensure(__func__);
DispatchTask(
[track, self] () {
track->GetSamples()->Then(self->mTaskQueue, __func__,
[track, self] (nsRefPtr<MediaTrackDemuxer::SamplesHolder> 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<typename FunctionType>
void
DispatchTask(FunctionType aFun)
{
nsRefPtr<nsRunnable> r = NS_NewRunnableFunction(aFun);
mTaskQueue->Dispatch(r.forget());
}
virtual ~MP4DemuxerBinding()
{
}
@@ -37,30 +156,20 @@ private:
TEST(MP4Demuxer, Seek)
{
nsRefPtr<MP4DemuxerBinding> b = new MP4DemuxerBinding();
MonitorAutoLock mon(b->mMonitor);
MP4Demuxer* d = b->demuxer;
nsRefPtr<MP4DemuxerBinding> binding = new MP4DemuxerBinding();
EXPECT_TRUE(d->Init());
nsTArray<nsRefPtr<MediaRawData>> samples;
nsRefPtr<MediaRawData> 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<MP4DemuxerBinding> 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<MediaRawData> 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<MP4DemuxerBinding> 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<MP4DemuxerBinding> 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<MP4DemuxerBinding> b = new MP4DemuxerBinding("gizmo-frag.mp4");
MonitorAutoLock mon(b->mMonitor);
MP4Demuxer* d = b->demuxer;
nsRefPtr<MP4DemuxerBinding> 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<MediaByteRange> 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<MediaRawData> 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
);
});
}
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -9,7 +9,7 @@
#include "mozilla/MozPromise.h"
#include "nsISupportsImpl.h"
#include "SharedThreadPool.h"
#include "mozilla/SharedThreadPool.h"
#include "VideoUtils.h"
using namespace mozilla;
+1
View File
@@ -11,6 +11,7 @@ SOURCES += [
'TestIntervalSet.cpp',
'TestMozPromise.cpp',
'TestMP3Demuxer.cpp',
'TestMP4Demuxer.cpp',
'TestMP4Reader.cpp',
'TestTrackEncoder.cpp',
'TestVideoSegment.cpp',
+1 -1
View File
@@ -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"
-5
View File
@@ -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',
+1 -1
View File
@@ -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"
@@ -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"
+1 -1
View File
@@ -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.
+2
View File
@@ -245,6 +245,8 @@ public:
virtual HRESULT ConfigureForSize(uint32_t aWidth, uint32_t aHeight) override;
virtual bool IsD3D11() override { return true; }
private:
HRESULT CreateFormatConverter();
+2
View File
@@ -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:
+39 -62
View File
@@ -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<nsIRunnable> 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<nsIRunnable> runnable =
NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
this,
&WMFMediaDataDecoder::ProcessDecode,
nsRefPtr<MediaRawData>(aSample));
mTaskQueue->Dispatch(runnable.forget());
return NS_OK;
}
void
WMFMediaDataDecoder::Decode()
WMFMediaDataDecoder::ProcessDecode(MediaRawData* aSample)
{
while (true) {
nsRefPtr<MediaRawData> 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<nsIRunnable> 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");
+10 -5
View File
@@ -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<nsRefPtr<MediaRawData>> 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;
};
+19 -3
View File
@@ -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<CreateDXVAManagerEvent> event(new CreateDXVAManagerEvent(mLayersBackend));
nsRefPtr<CreateDXVAManagerEvent> event =
new CreateDXVAManagerEvent(aForceD3D9 ? LayersBackend::LAYERS_D3D9 : mLayersBackend);
if (NS_IsMainThread()) {
event->Run();
@@ -173,9 +174,24 @@ WMFVideoMFTManager::InitializeDXVA()
already_AddRefed<MFTDecoder>
WMFVideoMFTManager::Init()
{
RefPtr<MFTDecoder> 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<MFTDecoder>
WMFVideoMFTManager::InitInternal(bool aForceD3D9)
{
mUseHwAccel = false; // default value; changed if D3D setup succeeds.
bool useDxva = InitializeDXVA();
bool useDxva = InitializeDXVA(aForceD3D9);
RefPtr<MFTDecoder> decoder(new MFTDecoder());
+3 -1
View File
@@ -38,7 +38,9 @@ public:
private:
bool InitializeDXVA();
bool InitializeDXVA(bool aForceD3D9);
already_AddRefed<MFTDecoder> InitInternal(bool aForceD3D9);
HRESULT ConfigureVideoFrameGeometry();
+1 -1
View File
@@ -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"
+1 -1
View File
@@ -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"
+3 -2
View File
@@ -13,7 +13,7 @@
#include "gfx2DGlue.h"
#include "Layers.h"
#include "mozilla/Preferences.h"
#include "SharedThreadPool.h"
#include "mozilla/SharedThreadPool.h"
#include <algorithm>
@@ -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;
@@ -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"
+5
View File
@@ -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',
]