Coverage Report

Created: 2018-09-25 14:53

/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