Bug 1674597 - part1 : let audio stream to manage the ended promise for playback. r=padenot

Audio stream is the class which really know about whether the audio playback is completed or not. So it makes more sense to let it return and manage the promise for playback end, not audio sink.

Differential Revision: https://phabricator.services.mozilla.com/D96468
This commit is contained in:
alwu 2020-11-10 19:05:10 +00:00
parent 1c99940712
commit fed2b07c52
5 changed files with 53 additions and 50 deletions

View File

@ -282,6 +282,9 @@ nsresult AudioStream::Init(uint32_t aNumChannels,
mSinkInfo = aSinkInfo;
// Hasn't started playing audio yet.
mPlaybackComplete = false;
cubeb_stream_params params;
params.rate = aRate;
params.channels = mOutChannels;
@ -359,22 +362,23 @@ void AudioStream::SetVolume(double aVolume) {
}
}
nsresult AudioStream::Start() {
Result<already_AddRefed<MediaSink::EndedPromise>, nsresult>
AudioStream::Start() {
TRACE();
MonitorAutoLock mon(mMonitor);
MOZ_ASSERT(mState == INITIALIZED);
mState = STARTED;
auto r = InvokeCubeb(cubeb_stream_start);
if (r != CUBEB_OK) {
if (InvokeCubeb(cubeb_stream_start) != CUBEB_OK) {
mState = ERRORED;
}
LOG("started, state %s", mState == STARTED
? "STARTED"
: mState == DRAINED ? "DRAINED" : "ERRORED");
if (mState == STARTED || mState == DRAINED) {
return NS_OK;
if (mState != STARTED && mState != DRAINED) {
return Err(NS_ERROR_FAILURE);
}
return NS_ERROR_FAILURE;
mPlaybackComplete = false;
return mEndedPromise.Ensure(__func__);
}
void AudioStream::Pause() {
@ -434,6 +438,7 @@ void AudioStream::Shutdown() {
}
mState = SHUTDOWN;
mEndedPromise.ResolveIfExists(true, __func__);
}
#if defined(XP_WIN)
@ -661,15 +666,20 @@ void AudioStream::StateCallback(cubeb_state aState) {
MOZ_ASSERT(mState != SHUTDOWN, "No state callback after shutdown");
LOG("StateCallback, mState=%d cubeb_state=%d", mState, aState);
if (aState == CUBEB_STATE_DRAINED) {
LOG("Drained");
mState = DRAINED;
mDataSource.Drained();
mPlaybackComplete = true;
mEndedPromise.ResolveIfExists(true, __func__);
} else if (aState == CUBEB_STATE_ERROR) {
LOGE("StateCallback() state %d cubeb error", mState);
mState = ERRORED;
mDataSource.Errored();
mPlaybackComplete = true;
mEndedPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
}
}
bool AudioStream::IsPlaybackCompleted() const { return mPlaybackComplete; }
AudioClock::AudioClock()
: mOutRate(0),
mInRate(0),

View File

@ -9,8 +9,12 @@
# include "AudioSampleFormat.h"
# include "CubebUtils.h"
# include "MediaInfo.h"
# include "MediaSink.h"
# include "mozilla/Atomics.h"
# include "mozilla/Monitor.h"
# include "mozilla/MozPromise.h"
# include "mozilla/RefPtr.h"
# include "mozilla/Result.h"
# include "mozilla/TimeStamp.h"
# include "mozilla/UniquePtr.h"
# include "nsCOMPtr.h"
@ -201,10 +205,6 @@ class AudioStream final
virtual UniquePtr<Chunk> PopFrames(uint32_t aFrames) = 0;
// Return true if no more data will be added to the source.
virtual bool Ended() const = 0;
// Notify that all data is drained by the AudioStream.
virtual void Drained() = 0;
// Notify that a fatal error has occured during playback.
virtual void Errored() = 0;
protected:
virtual ~DataSource() = default;
@ -229,8 +229,9 @@ class AudioStream final
// 0 (meaning muted) to 1 (meaning full volume). Thread-safe.
void SetVolume(double aVolume);
// Start the stream.
nsresult Start();
// Start the stream and return a promise that will be resolve when the
// playback completes.
Result<already_AddRefed<MediaSink::EndedPromise>, nsresult> Start();
// Pause audio playback.
void Pause();
@ -266,6 +267,8 @@ class AudioStream final
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
bool IsPlaybackCompleted() const;
protected:
friend class AudioClock;
@ -341,6 +344,9 @@ class AudioStream final
/* Contains the id of the audio thread, from profiler_get_thread_id. */
std::atomic<int> mAudioThreadId;
const bool mSandboxed = false;
MozPromiseHolder<MediaSink::EndedPromise> mEndedPromise;
Atomic<bool> mPlaybackComplete;
};
} // namespace mozilla

View File

@ -63,7 +63,6 @@ AudioSink::AudioSink(AbstractThread* aThread,
mMonitor("AudioSink"),
mWritten(0),
mErrored(false),
mPlaybackComplete(false),
mOwnerThread(aThread),
mProcessedQueueLength(0),
mFramesParsed(0),
@ -77,8 +76,8 @@ AudioSink::AudioSink(AbstractThread* aThread,
AudioSink::~AudioSink() = default;
nsresult AudioSink::Init(const PlaybackParams& aParams,
RefPtr<MediaSink::EndedPromise>& aEndedPromise) {
Result<already_AddRefed<MediaSink::EndedPromise>, nsresult> AudioSink::Start(
const PlaybackParams& aParams) {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());
mAudioQueueListener = mAudioQueue.PushEvent().Connect(
@ -91,12 +90,11 @@ nsresult AudioSink::Init(const PlaybackParams& aParams,
// To ensure at least one audio packet will be popped from AudioQueue and
// ready to be played.
NotifyAudioNeeded();
aEndedPromise = mEndedPromise.Ensure(__func__);
nsresult rv = InitializeAudioStream(aParams);
if (NS_FAILED(rv)) {
mEndedPromise.Reject(rv, __func__);
return Err(rv);
}
return rv;
return mAudioStream->Start();
}
TimeUnit AudioSink::GetPosition() {
@ -144,7 +142,6 @@ void AudioSink::Shutdown() {
}
mProcessedQueue.Reset();
mProcessedQueue.Finish();
mEndedPromise.ResolveIfExists(true, __func__);
}
void AudioSink::SetVolume(double aVolume) {
@ -168,7 +165,8 @@ void AudioSink::SetPreservesPitch(bool aPreservesPitch) {
}
void AudioSink::SetPlaying(bool aPlaying) {
if (!mAudioStream || mPlaying == aPlaying || mPlaybackComplete) {
if (!mAudioStream || mAudioStream->IsPlaybackCompleted() ||
mPlaying == aPlaying) {
return;
}
// pause/resume AudioStream as necessary.
@ -204,7 +202,7 @@ nsresult AudioSink::InitializeAudioStream(const PlaybackParams& aParams) {
mAudioStream->SetVolume(aParams.mVolume);
mAudioStream->SetPlaybackRate(aParams.mPlaybackRate);
mAudioStream->SetPreservesPitch(aParams.mPreservesPitch);
return mAudioStream->Start();
return NS_OK;
}
TimeUnit AudioSink::GetEndTime() const {
@ -305,18 +303,6 @@ bool AudioSink::Ended() const {
return mProcessedQueue.IsFinished() || mErrored;
}
void AudioSink::Drained() {
SINK_LOG("Drained");
mPlaybackComplete = true;
mEndedPromise.ResolveIfExists(true, __func__);
}
void AudioSink::Errored() {
SINK_LOG("Errored");
mPlaybackComplete = true;
mEndedPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
}
void AudioSink::CheckIsAudible(const AudioData* aData) {
MOZ_ASSERT(aData);
@ -532,7 +518,7 @@ void AudioSink::GetDebugInfo(dom::MediaSinkDebugInfo& aInfo) {
aInfo.mAudioSinkWrapper.mAudioSink.mWritten = mWritten;
aInfo.mAudioSinkWrapper.mAudioSink.mHasErrored = bool(mErrored);
aInfo.mAudioSinkWrapper.mAudioSink.mPlaybackComplete =
bool(mPlaybackComplete);
mAudioStream ? mAudioStream->IsPlaybackCompleted() : false;
}
} // namespace mozilla

View File

@ -17,6 +17,7 @@
#include "mozilla/Monitor.h"
#include "mozilla/MozPromise.h"
#include "mozilla/RefPtr.h"
#include "mozilla/Result.h"
#include "nsISupportsImpl.h"
namespace mozilla {
@ -41,10 +42,10 @@ class AudioSink : private AudioStream::DataSource {
~AudioSink();
// Return a promise which will be resolved when AudioSink
// finishes playing, or rejected if any error.
nsresult Init(const PlaybackParams& aParams,
RefPtr<MediaSink::EndedPromise>& aEndedPromise);
// Start audio playback and return a promise which will be resolved when the
// playback finishes, or return an error result if any error occurs.
Result<already_AddRefed<MediaSink::EndedPromise>, nsresult> Start(
const PlaybackParams& aParams);
/*
* All public functions are not thread-safe.
@ -79,8 +80,6 @@ class AudioSink : private AudioStream::DataSource {
// Called on the callback thread of cubeb.
UniquePtr<AudioStream::Chunk> PopFrames(uint32_t aFrames) override;
bool Ended() const override;
void Drained() override;
void Errored() override;
void CheckIsAudible(const AudioData* aData);
@ -106,8 +105,6 @@ class AudioSink : private AudioStream::DataSource {
// Used on the task queue of MDSM only.
bool mPlaying;
MozPromiseHolder<MediaSink::EndedPromise> mEndedPromise;
/*
* Members to implement AudioStream::DataSource.
* Used on the callback thread of cubeb.
@ -129,9 +126,6 @@ class AudioSink : private AudioStream::DataSource {
// True if there is any error in processing audio data like overflow.
Atomic<bool> mErrored;
// Set on the callback thread of cubeb once the stream has drained.
Atomic<bool> mPlaybackComplete;
const RefPtr<AbstractThread> mOwnerThread;
// Audio Processing objects and methods

View File

@ -7,6 +7,7 @@
#include "AudioSinkWrapper.h"
#include "AudioSink.h"
#include "VideoUtils.h"
#include "mozilla/Result.h"
#include "nsPrintfCString.h"
namespace mozilla {
@ -154,10 +155,16 @@ nsresult AudioSinkWrapper::Start(const TimeUnit& aStartTime,
mPlayStartTime = TimeStamp::Now();
mAudioEnded = IsAudioSourceEnded(aInfo);
nsresult rv = NS_OK;
if (!mAudioEnded) {
mAudioSink.reset(mCreator->Create());
rv = mAudioSink->Init(mParams, mEndedPromise);
Result<already_AddRefed<MediaSink::EndedPromise>, nsresult> rv =
mAudioSink->Start(mParams);
if (rv.isErr()) {
mEndedPromise =
MediaSink::EndedPromise::CreateAndReject(rv.unwrapErr(), __func__);
return rv.unwrapErr();
}
mEndedPromise = rv.unwrap();
mEndedPromise
->Then(mOwnerThread.get(), __func__, this,
&AudioSinkWrapper::OnAudioEnded, &AudioSinkWrapper::OnAudioEnded)
@ -167,7 +174,7 @@ nsresult AudioSinkWrapper::Start(const TimeUnit& aStartTime,
mEndedPromise = MediaSink::EndedPromise::CreateAndResolve(true, __func__);
}
}
return rv;
return NS_OK;
}
bool AudioSinkWrapper::IsAudioSourceEnded(const MediaInfo& aInfo) const {