mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 15:55:36 +00:00
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:
parent
1c99940712
commit
fed2b07c52
@ -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),
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user