Bug 1754495 - Don't use the audio clock to determine if an AudioStream is low on decoded audio. r=alwu

The old technique was slightly misleading. It was counting the audio already
sent to the device, in the decoded audio duration. But in fact, there's nothing
that can be done about this, the audio is going to be played regardless of what
happens. If the audio latency is high enough, this would return a number that's
high, but not a lot of audio would be sitting in Firefox's buffer, ready to
service the audio callbacks.

It's best to only consider the decoded audio (processed + unprocessed) here,
since the goal of this is to determine whether more audio needs to be decoded.
It also saves a `cubeb_stream_get_position` IPC call if using AudioIPC (soon to
be all platforms).

Differential Revision: https://phabricator.services.mozilla.com/D138318
This commit is contained in:
Paul Adenot 2022-02-14 16:42:25 +00:00
parent a0a0df247c
commit b37e1d497b
9 changed files with 36 additions and 7 deletions

View File

@ -2904,10 +2904,8 @@ already_AddRefed<MediaSink> MediaDecoderStateMachine::CreateMediaSink() {
TimeUnit MediaDecoderStateMachine::GetDecodedAudioDuration() const {
MOZ_ASSERT(OnTaskQueue());
if (mMediaSink->IsStarted()) {
// mDecodedAudioEndTime might be smaller than GetClock() when there is
// overlap between 2 adjacent audio samples or when we are playing
// a chained ogg file.
return std::max(mDecodedAudioEndTime - GetClock(), TimeUnit::Zero());
return mMediaSink->UnplayedDuration(TrackInfo::kAudioTrack) +
TimeUnit::FromMicroseconds(AudioQueue().Duration());
}
// MediaSink not started. All audio samples are in the queue.
return TimeUnit::FromMicroseconds(AudioQueue().Duration());

View File

@ -115,6 +115,10 @@ bool AudioSink::HasUnplayedFrames() {
(mAudioStream && mAudioStream->GetPositionInFrames() + 1 < mWritten);
}
TimeUnit AudioSink::UnplayedDuration() const {
return TimeUnit::FromMicroseconds(AudioQueuedInRingBufferMS());
}
void AudioSink::Shutdown() {
MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn());

View File

@ -54,10 +54,13 @@ class AudioSink : private AudioStream::DataSource {
media::TimeUnit GetPosition();
media::TimeUnit GetEndTime() const;
// Check whether we've pushed more frames to the audio hardware than it has
// played.
// Check whether we've pushed more frames to the audio stream than it
// has played.
bool HasUnplayedFrames();
// The duration of the buffered frames.
media::TimeUnit UnplayedDuration() const;
// Shut down the AudioSink's resources.
void Shutdown();

View File

@ -80,6 +80,11 @@ bool AudioSinkWrapper::HasUnplayedFrames(TrackType aType) const {
return mAudioSink ? mAudioSink->HasUnplayedFrames() : false;
}
media::TimeUnit AudioSinkWrapper::UnplayedDuration(TrackType aType) const {
AssertOwnerThread();
return mAudioSink ? mAudioSink->UnplayedDuration() : media::TimeUnit::Zero();
}
void AudioSinkWrapper::SetVolume(double aVolume) {
AssertOwnerThread();
mParams.mVolume = aVolume;

View File

@ -66,6 +66,7 @@ class AudioSinkWrapper : public MediaSink {
media::TimeUnit GetEndTime(TrackType aType) const override;
media::TimeUnit GetPosition(TimeStamp* aTimeStamp = nullptr) const override;
bool HasUnplayedFrames(TrackType aType) const override;
media::TimeUnit UnplayedDuration(TrackType aType) const override;
void SetVolume(double aVolume) override;
void SetStreamName(const nsAString& aStreamName) override;

View File

@ -46,10 +46,15 @@ class DecodedStream : public MediaSink {
media::TimeUnit GetEndTime(TrackType aType) const override;
media::TimeUnit GetPosition(TimeStamp* aTimeStamp = nullptr) const override;
bool HasUnplayedFrames(TrackType aType) const override {
// TODO: implement this.
// TODO: bug 1755026
return false;
}
media::TimeUnit UnplayedDuration(TrackType aType) const override {
// TODO: bug 1755026
return media::TimeUnit::Zero();
}
void SetVolume(double aVolume) override;
void SetPlaybackRate(double aPlaybackRate) override;
void SetPreservesPitch(bool aPreservesPitch) override;

View File

@ -67,6 +67,10 @@ class MediaSink {
// Can be called in any state.
virtual bool HasUnplayedFrames(TrackType aType) const = 0;
// Return the duration of data consumed but not played yet.
// Can be called in any state.
virtual media::TimeUnit UnplayedDuration(TrackType aType) const = 0;
// Set volume of the audio track.
// Do nothing if this sink has no audio track.
// Can be called in any state.

View File

@ -128,6 +128,14 @@ bool VideoSink::HasUnplayedFrames(TrackType aType) const {
return mAudioSink->HasUnplayedFrames(aType);
}
media::TimeUnit VideoSink::UnplayedDuration(TrackType aType) const {
AssertOwnerThread();
MOZ_ASSERT(aType == TrackInfo::kAudioTrack,
"Not implemented for non audio tracks.");
return mAudioSink->UnplayedDuration(aType);
}
void VideoSink::SetPlaybackRate(double aPlaybackRate) {
AssertOwnerThread();

View File

@ -39,6 +39,7 @@ class VideoSink : public MediaSink {
media::TimeUnit GetPosition(TimeStamp* aTimeStamp = nullptr) const override;
bool HasUnplayedFrames(TrackType aType) const override;
media::TimeUnit UnplayedDuration(TrackType aType) const override;
void SetPlaybackRate(double aPlaybackRate) override;