summaryrefslogtreecommitdiffstats
path: root/dom/media/mediasink/AudioSinkWrapper.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/mediasink/AudioSinkWrapper.cpp')
-rw-r--r--dom/media/mediasink/AudioSinkWrapper.cpp248
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
+