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