diff options
Diffstat (limited to 'dom/media/mediasink/AudioSinkWrapper.cpp')
-rw-r--r-- | dom/media/mediasink/AudioSinkWrapper.cpp | 248 |
1 files changed, 248 insertions, 0 deletions
diff --git a/dom/media/mediasink/AudioSinkWrapper.cpp b/dom/media/mediasink/AudioSinkWrapper.cpp new file mode 100644 index 000000000..a2dfcd8fb --- /dev/null +++ b/dom/media/mediasink/AudioSinkWrapper.cpp @@ -0,0 +1,248 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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 "AudioSink.h" +#include "AudioSinkWrapper.h" + +namespace mozilla { +namespace media { + +AudioSinkWrapper::~AudioSinkWrapper() +{ +} + +void +AudioSinkWrapper::Shutdown() +{ + AssertOwnerThread(); + MOZ_ASSERT(!mIsStarted, "Must be called after playback stopped."); + mCreator = nullptr; +} + +const MediaSink::PlaybackParams& +AudioSinkWrapper::GetPlaybackParams() const +{ + AssertOwnerThread(); + return mParams; +} + +void +AudioSinkWrapper::SetPlaybackParams(const PlaybackParams& aParams) +{ + AssertOwnerThread(); + if (mAudioSink) { + mAudioSink->SetVolume(aParams.mVolume); + mAudioSink->SetPlaybackRate(aParams.mPlaybackRate); + mAudioSink->SetPreservesPitch(aParams.mPreservesPitch); + } + mParams = aParams; +} + +RefPtr<GenericPromise> +AudioSinkWrapper::OnEnded(TrackType aType) +{ + AssertOwnerThread(); + MOZ_ASSERT(mIsStarted, "Must be called after playback starts."); + if (aType == TrackInfo::kAudioTrack) { + return mEndPromise; + } + return nullptr; +} + +int64_t +AudioSinkWrapper::GetEndTime(TrackType aType) const +{ + AssertOwnerThread(); + MOZ_ASSERT(mIsStarted, "Must be called after playback starts."); + if (aType == TrackInfo::kAudioTrack && mAudioSink) { + return mAudioSink->GetEndTime(); + } + return -1; +} + +int64_t +AudioSinkWrapper::GetVideoPosition(TimeStamp aNow) const +{ + AssertOwnerThread(); + MOZ_ASSERT(!mPlayStartTime.IsNull()); + // Time elapsed since we started playing. + int64_t delta = (aNow - mPlayStartTime).ToMicroseconds(); + // Take playback rate into account. + return mPlayDuration + delta * mParams.mPlaybackRate; +} + +int64_t +AudioSinkWrapper::GetPosition(TimeStamp* aTimeStamp) const +{ + AssertOwnerThread(); + MOZ_ASSERT(mIsStarted, "Must be called after playback starts."); + + int64_t pos = -1; + TimeStamp t = TimeStamp::Now(); + + if (!mAudioEnded) { + // Rely on the audio sink to report playback position when it is not ended. + pos = mAudioSink->GetPosition(); + } else if (!mPlayStartTime.IsNull()) { + // Calculate playback position using system clock if we are still playing. + pos = GetVideoPosition(t); + } else { + // Return how long we've played if we are not playing. + pos = mPlayDuration; + } + + if (aTimeStamp) { + *aTimeStamp = t; + } + + return pos; +} + +bool +AudioSinkWrapper::HasUnplayedFrames(TrackType aType) const +{ + AssertOwnerThread(); + return mAudioSink ? mAudioSink->HasUnplayedFrames() : false; +} + +void +AudioSinkWrapper::SetVolume(double aVolume) +{ + AssertOwnerThread(); + mParams.mVolume = aVolume; + if (mAudioSink) { + mAudioSink->SetVolume(aVolume); + } +} + +void +AudioSinkWrapper::SetPlaybackRate(double aPlaybackRate) +{ + AssertOwnerThread(); + if (!mAudioEnded) { + // Pass the playback rate to the audio sink. The underlying AudioStream + // will handle playback rate changes and report correct audio position. + mAudioSink->SetPlaybackRate(aPlaybackRate); + } else if (!mPlayStartTime.IsNull()) { + // Adjust playback duration and start time when we are still playing. + TimeStamp now = TimeStamp::Now(); + mPlayDuration = GetVideoPosition(now); + mPlayStartTime = now; + } + // mParams.mPlaybackRate affects GetVideoPosition(). It should be updated + // after the calls to GetVideoPosition(); + mParams.mPlaybackRate = aPlaybackRate; + + // Do nothing when not playing. Changes in playback rate will be taken into + // account by GetVideoPosition(). +} + +void +AudioSinkWrapper::SetPreservesPitch(bool aPreservesPitch) +{ + AssertOwnerThread(); + mParams.mPreservesPitch = aPreservesPitch; + if (mAudioSink) { + mAudioSink->SetPreservesPitch(aPreservesPitch); + } +} + +void +AudioSinkWrapper::SetPlaying(bool aPlaying) +{ + AssertOwnerThread(); + + // Resume/pause matters only when playback started. + if (!mIsStarted) { + return; + } + + if (mAudioSink) { + mAudioSink->SetPlaying(aPlaying); + } + + if (aPlaying) { + MOZ_ASSERT(mPlayStartTime.IsNull()); + mPlayStartTime = TimeStamp::Now(); + } else { + // Remember how long we've played. + mPlayDuration = GetPosition(); + // mPlayStartTime must be updated later since GetPosition() + // depends on the value of mPlayStartTime. + mPlayStartTime = TimeStamp(); + } +} + +void +AudioSinkWrapper::Start(int64_t aStartTime, const MediaInfo& aInfo) +{ + AssertOwnerThread(); + MOZ_ASSERT(!mIsStarted, "playback already started."); + + mIsStarted = true; + mPlayDuration = aStartTime; + mPlayStartTime = TimeStamp::Now(); + + // no audio is equivalent to audio ended before video starts. + mAudioEnded = !aInfo.HasAudio(); + + if (aInfo.HasAudio()) { + mAudioSink = mCreator->Create(); + mEndPromise = mAudioSink->Init(mParams); + + mAudioSinkPromise.Begin(mEndPromise->Then( + mOwnerThread.get(), __func__, this, + &AudioSinkWrapper::OnAudioEnded, + &AudioSinkWrapper::OnAudioEnded)); + } +} + +void +AudioSinkWrapper::Stop() +{ + AssertOwnerThread(); + MOZ_ASSERT(mIsStarted, "playback not started."); + + mIsStarted = false; + mAudioEnded = true; + + if (mAudioSink) { + mAudioSinkPromise.DisconnectIfExists(); + mAudioSink->Shutdown(); + mAudioSink = nullptr; + mEndPromise = nullptr; + } +} + +bool +AudioSinkWrapper::IsStarted() const +{ + AssertOwnerThread(); + return mIsStarted; +} + +bool +AudioSinkWrapper::IsPlaying() const +{ + AssertOwnerThread(); + return IsStarted() && !mPlayStartTime.IsNull(); +} + +void +AudioSinkWrapper::OnAudioEnded() +{ + AssertOwnerThread(); + mAudioSinkPromise.Complete(); + mPlayDuration = GetPosition(); + if (!mPlayStartTime.IsNull()) { + mPlayStartTime = TimeStamp::Now(); + } + mAudioEnded = true; +} + +} // namespace media +} // namespace mozilla + |