Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 : /* This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : #include "AudioSink.h"
8 : #include "AudioSinkWrapper.h"
9 :
10 : namespace mozilla {
11 : namespace media {
12 :
13 0 : AudioSinkWrapper::~AudioSinkWrapper()
14 : {
15 0 : }
16 :
17 : void
18 0 : AudioSinkWrapper::Shutdown()
19 : {
20 0 : AssertOwnerThread();
21 0 : MOZ_ASSERT(!mIsStarted, "Must be called after playback stopped.");
22 0 : mCreator = nullptr;
23 0 : }
24 :
25 : const MediaSink::PlaybackParams&
26 0 : AudioSinkWrapper::GetPlaybackParams() const
27 : {
28 0 : AssertOwnerThread();
29 0 : return mParams;
30 : }
31 :
32 : void
33 0 : AudioSinkWrapper::SetPlaybackParams(const PlaybackParams& aParams)
34 : {
35 0 : AssertOwnerThread();
36 0 : if (mAudioSink) {
37 0 : mAudioSink->SetVolume(aParams.mVolume);
38 0 : mAudioSink->SetPlaybackRate(aParams.mPlaybackRate);
39 0 : mAudioSink->SetPreservesPitch(aParams.mPreservesPitch);
40 : }
41 0 : mParams = aParams;
42 0 : }
43 :
44 : RefPtr<GenericPromise>
45 0 : AudioSinkWrapper::OnEnded(TrackType aType)
46 : {
47 0 : AssertOwnerThread();
48 0 : MOZ_ASSERT(mIsStarted, "Must be called after playback starts.");
49 0 : if (aType == TrackInfo::kAudioTrack) {
50 0 : return mEndPromise;
51 : }
52 0 : return nullptr;
53 : }
54 :
55 : TimeUnit
56 0 : AudioSinkWrapper::GetEndTime(TrackType aType) const
57 : {
58 0 : AssertOwnerThread();
59 0 : MOZ_ASSERT(mIsStarted, "Must be called after playback starts.");
60 0 : if (aType == TrackInfo::kAudioTrack && mAudioSink) {
61 0 : return mAudioSink->GetEndTime();
62 : }
63 0 : return TimeUnit::Zero();
64 : }
65 :
66 : TimeUnit
67 0 : AudioSinkWrapper::GetVideoPosition(TimeStamp aNow) const
68 : {
69 0 : AssertOwnerThread();
70 0 : MOZ_ASSERT(!mPlayStartTime.IsNull());
71 : // Time elapsed since we started playing.
72 0 : double delta = (aNow - mPlayStartTime).ToSeconds();
73 : // Take playback rate into account.
74 0 : return mPlayDuration + TimeUnit::FromSeconds(delta * mParams.mPlaybackRate);
75 : }
76 :
77 : TimeUnit
78 0 : AudioSinkWrapper::GetPosition(TimeStamp* aTimeStamp) const
79 : {
80 0 : AssertOwnerThread();
81 0 : MOZ_ASSERT(mIsStarted, "Must be called after playback starts.");
82 :
83 0 : TimeUnit pos;
84 0 : TimeStamp t = TimeStamp::Now();
85 :
86 0 : if (!mAudioEnded) {
87 : // Rely on the audio sink to report playback position when it is not ended.
88 0 : pos = mAudioSink->GetPosition();
89 0 : } else if (!mPlayStartTime.IsNull()) {
90 : // Calculate playback position using system clock if we are still playing.
91 0 : pos = GetVideoPosition(t);
92 : } else {
93 : // Return how long we've played if we are not playing.
94 0 : pos = mPlayDuration;
95 : }
96 :
97 0 : if (aTimeStamp) {
98 0 : *aTimeStamp = t;
99 : }
100 :
101 0 : return pos;
102 : }
103 :
104 : bool
105 0 : AudioSinkWrapper::HasUnplayedFrames(TrackType aType) const
106 : {
107 0 : AssertOwnerThread();
108 0 : return mAudioSink ? mAudioSink->HasUnplayedFrames() : false;
109 : }
110 :
111 : void
112 0 : AudioSinkWrapper::SetVolume(double aVolume)
113 : {
114 0 : AssertOwnerThread();
115 0 : mParams.mVolume = aVolume;
116 0 : if (mAudioSink) {
117 0 : mAudioSink->SetVolume(aVolume);
118 : }
119 0 : }
120 :
121 : void
122 0 : AudioSinkWrapper::SetPlaybackRate(double aPlaybackRate)
123 : {
124 0 : AssertOwnerThread();
125 0 : if (!mAudioEnded) {
126 : // Pass the playback rate to the audio sink. The underlying AudioStream
127 : // will handle playback rate changes and report correct audio position.
128 0 : mAudioSink->SetPlaybackRate(aPlaybackRate);
129 0 : } else if (!mPlayStartTime.IsNull()) {
130 : // Adjust playback duration and start time when we are still playing.
131 0 : TimeStamp now = TimeStamp::Now();
132 0 : mPlayDuration = GetVideoPosition(now);
133 0 : mPlayStartTime = now;
134 : }
135 : // mParams.mPlaybackRate affects GetVideoPosition(). It should be updated
136 : // after the calls to GetVideoPosition();
137 0 : mParams.mPlaybackRate = aPlaybackRate;
138 :
139 : // Do nothing when not playing. Changes in playback rate will be taken into
140 : // account by GetVideoPosition().
141 0 : }
142 :
143 : void
144 0 : AudioSinkWrapper::SetPreservesPitch(bool aPreservesPitch)
145 : {
146 0 : AssertOwnerThread();
147 0 : mParams.mPreservesPitch = aPreservesPitch;
148 0 : if (mAudioSink) {
149 0 : mAudioSink->SetPreservesPitch(aPreservesPitch);
150 : }
151 0 : }
152 :
153 : void
154 0 : AudioSinkWrapper::SetPlaying(bool aPlaying)
155 : {
156 0 : AssertOwnerThread();
157 :
158 : // Resume/pause matters only when playback started.
159 0 : if (!mIsStarted) {
160 0 : return;
161 : }
162 :
163 0 : if (mAudioSink) {
164 0 : mAudioSink->SetPlaying(aPlaying);
165 : }
166 :
167 0 : if (aPlaying) {
168 0 : MOZ_ASSERT(mPlayStartTime.IsNull());
169 0 : mPlayStartTime = TimeStamp::Now();
170 : } else {
171 : // Remember how long we've played.
172 0 : mPlayDuration = GetPosition();
173 : // mPlayStartTime must be updated later since GetPosition()
174 : // depends on the value of mPlayStartTime.
175 0 : mPlayStartTime = TimeStamp();
176 : }
177 : }
178 :
179 : void
180 0 : AudioSinkWrapper::Start(const TimeUnit& aStartTime, const MediaInfo& aInfo)
181 : {
182 0 : AssertOwnerThread();
183 0 : MOZ_ASSERT(!mIsStarted, "playback already started.");
184 :
185 0 : mIsStarted = true;
186 0 : mPlayDuration = aStartTime;
187 0 : mPlayStartTime = TimeStamp::Now();
188 :
189 : // no audio is equivalent to audio ended before video starts.
190 0 : mAudioEnded = !aInfo.HasAudio();
191 :
192 0 : if (aInfo.HasAudio()) {
193 0 : mAudioSink.reset(mCreator->Create());
194 0 : mEndPromise = mAudioSink->Init(mParams);
195 :
196 0 : mEndPromise->Then(
197 0 : mOwnerThread.get(), __func__, this,
198 : &AudioSinkWrapper::OnAudioEnded,
199 : &AudioSinkWrapper::OnAudioEnded
200 0 : )->Track(mAudioSinkPromise);
201 : }
202 0 : }
203 :
204 : void
205 0 : AudioSinkWrapper::Stop()
206 : {
207 0 : AssertOwnerThread();
208 0 : MOZ_ASSERT(mIsStarted, "playback not started.");
209 :
210 0 : mIsStarted = false;
211 0 : mAudioEnded = true;
212 :
213 0 : if (mAudioSink) {
214 0 : mAudioSinkPromise.DisconnectIfExists();
215 0 : mAudioSink->Shutdown();
216 0 : mAudioSink = nullptr;
217 0 : mEndPromise = nullptr;
218 : }
219 0 : }
220 :
221 : bool
222 0 : AudioSinkWrapper::IsStarted() const
223 : {
224 0 : AssertOwnerThread();
225 0 : return mIsStarted;
226 : }
227 :
228 : bool
229 0 : AudioSinkWrapper::IsPlaying() const
230 : {
231 0 : AssertOwnerThread();
232 0 : return IsStarted() && !mPlayStartTime.IsNull();
233 : }
234 :
235 : void
236 0 : AudioSinkWrapper::OnAudioEnded()
237 : {
238 0 : AssertOwnerThread();
239 0 : mAudioSinkPromise.Complete();
240 0 : mPlayDuration = GetPosition();
241 0 : if (!mPlayStartTime.IsNull()) {
242 0 : mPlayStartTime = TimeStamp::Now();
243 : }
244 0 : mAudioEnded = true;
245 0 : }
246 :
247 : } // namespace media
248 : } // namespace mozilla
249 :
|