Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(137)

Side by Side Diff: chromecast/media/audio/cast_audio_mixer.cc

Issue 2879703003: [chromecast] Moves CastAudioOutputStream::Backend to CMA thread. (Closed)
Patch Set: addressed comments Created 3 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chromecast/media/audio/cast_audio_mixer.h" 5 #include "chromecast/media/audio/cast_audio_mixer.h"
6 6
7 #include "base/bind.h"
7 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/memory/ptr_util.h"
8 #include "chromecast/media/audio/cast_audio_manager.h" 10 #include "chromecast/media/audio/cast_audio_manager.h"
9 #include "chromecast/media/audio/cast_audio_output_stream.h" 11 #include "chromecast/media/audio/cast_audio_output_stream.h"
10 #include "media/base/audio_timestamp_helper.h" 12 #include "media/base/audio_timestamp_helper.h"
11 #include "media/base/channel_layout.h" 13 #include "media/base/channel_layout.h"
12 14
13 namespace { 15 namespace {
14 const int kBitsPerSample = 16; 16 const int kBitsPerSample = 16;
15 const int kFramesPerBuffer = 1024; 17 const int kFramesPerBuffer = 1024;
16 const int kSampleRate = 48000; 18 const int kSampleRate = 48000;
17 } // namespace 19 } // namespace
18 20
19 namespace chromecast { 21 namespace chromecast {
20 namespace media { 22 namespace media {
21 23
22 class CastAudioMixer::MixerProxyStream 24 class CastAudioMixer::MixerProxyStream
23 : public ::media::AudioOutputStream, 25 : public ::media::AudioOutputStream,
24 private ::media::AudioConverter::InputCallback { 26 public ::media::AudioConverter::InputCallback {
25 public: 27 public:
26 MixerProxyStream(const ::media::AudioParameters& input_params, 28 MixerProxyStream(const ::media::AudioParameters& input_params,
27 const ::media::AudioParameters& output_params, 29 const ::media::AudioParameters& output_params,
28 CastAudioManager* audio_manager,
29 CastAudioMixer* audio_mixer) 30 CastAudioMixer* audio_mixer)
30 : audio_manager_(audio_manager), 31 : audio_mixer_(audio_mixer),
31 audio_mixer_(audio_mixer),
32 source_callback_(nullptr),
33 input_params_(input_params), 32 input_params_(input_params),
34 output_params_(output_params), 33 output_params_(output_params),
35 opened_(false), 34 opened_(false),
36 volume_(1.0) {} 35 volume_(1.0),
36 source_callback_(nullptr) {
37 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
38 }
37 39
38 ~MixerProxyStream() override {} 40 ~MixerProxyStream() override {
41 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
42 }
39 43
40 void OnError() { 44 void OnError() {
41 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); 45 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
42
43 if (source_callback_) 46 if (source_callback_)
44 source_callback_->OnError(); 47 source_callback_->OnError();
45 } 48 }
46 49
47 private: 50 private:
51 // ResamplerProxy is an intermediate filter between MixerProxyStream and
52 // CastAudioMixer::output_stream_ whose only responsibility is to resample
53 // audio to the sample rate expected by CastAudioMixer::output_stream_.
48 class ResamplerProxy : public ::media::AudioConverter::InputCallback { 54 class ResamplerProxy : public ::media::AudioConverter::InputCallback {
49 public: 55 public:
50 ResamplerProxy(::media::AudioConverter::InputCallback* input_callback, 56 ResamplerProxy(::media::AudioConverter::InputCallback* input_callback,
51 const ::media::AudioParameters& input_params, 57 const ::media::AudioParameters& input_params,
52 const ::media::AudioParameters& output_params) { 58 const ::media::AudioParameters& output_params) {
53 resampler_.reset( 59 resampler_.reset(
54 new ::media::AudioConverter(input_params, output_params, false)); 60 new ::media::AudioConverter(input_params, output_params, false));
55 resampler_->AddInput(input_callback); 61 resampler_->AddInput(input_callback);
62 DETACH_FROM_THREAD(backend_thread_checker_);
56 } 63 }
57 64
58 ~ResamplerProxy() override {} 65 ~ResamplerProxy() override {}
59 66
60 private: 67 private:
61 // ::media::AudioConverter::InputCallback implementation 68 // ::media::AudioConverter::InputCallback implementation
62 double ProvideInput(::media::AudioBus* audio_bus, 69 double ProvideInput(::media::AudioBus* audio_bus,
63 uint32_t frames_delayed) override { 70 uint32_t frames_delayed) override {
71 DCHECK_CALLED_ON_VALID_THREAD(backend_thread_checker_);
64 resampler_->ConvertWithDelay(frames_delayed, audio_bus); 72 resampler_->ConvertWithDelay(frames_delayed, audio_bus);
65
66 // Volume multiplier has already been applied by |resampler_|. 73 // Volume multiplier has already been applied by |resampler_|.
67 return 1.0; 74 return 1.0;
68 } 75 }
69 76
70 std::unique_ptr<::media::AudioConverter> resampler_; 77 std::unique_ptr<::media::AudioConverter> resampler_;
71 78
79 THREAD_CHECKER(backend_thread_checker_);
72 DISALLOW_COPY_AND_ASSIGN(ResamplerProxy); 80 DISALLOW_COPY_AND_ASSIGN(ResamplerProxy);
73 }; 81 };
74 82
75 // ::media::AudioOutputStream implementation 83 // ::media::AudioOutputStream implementation
76 bool Open() override { 84 bool Open() override {
77 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); 85 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
78 86
79 ::media::AudioParameters::Format format = input_params_.format(); 87 ::media::AudioParameters::Format format = input_params_.format();
80 DCHECK((format == ::media::AudioParameters::AUDIO_PCM_LINEAR) || 88 DCHECK((format == ::media::AudioParameters::AUDIO_PCM_LINEAR) ||
81 (format == ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY)); 89 (format == ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY));
82 90
83 ::media::ChannelLayout channel_layout = input_params_.channel_layout(); 91 ::media::ChannelLayout channel_layout = input_params_.channel_layout();
84 if ((channel_layout != ::media::CHANNEL_LAYOUT_MONO) && 92 if ((channel_layout != ::media::CHANNEL_LAYOUT_MONO) &&
85 (channel_layout != ::media::CHANNEL_LAYOUT_STEREO)) { 93 (channel_layout != ::media::CHANNEL_LAYOUT_STEREO)) {
86 LOG(WARNING) << "Unsupported channel layout: " << channel_layout; 94 LOG(WARNING) << "Unsupported channel layout: " << channel_layout;
87 return false; 95 return false;
88 } 96 }
89 DCHECK_GE(input_params_.channels(), 1); 97 DCHECK_GE(input_params_.channels(), 1);
90 DCHECK_LE(input_params_.channels(), 2); 98 DCHECK_LE(input_params_.channels(), 2);
91 99
92 return opened_ = audio_mixer_->Register(this); 100 return opened_ = audio_mixer_->Register(this);
93 } 101 }
94 102
95 void Close() override { 103 void Close() override {
96 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); 104 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
97 105
98 if (proxy_) 106 if (proxy_)
99 Stop(); 107 Stop();
100 if (opened_) 108 if (opened_)
101 audio_mixer_->Unregister(this); 109 audio_mixer_->Unregister(this);
110
102 // Signal to the manager that we're closed and can be removed. 111 // Signal to the manager that we're closed and can be removed.
103 // This should be the last call in the function as it deletes "this". 112 // This should be the last call in the function as it deletes "this".
104 audio_manager_->ReleaseOutputStream(this); 113 audio_mixer_->audio_manager_->ReleaseOutputStream(this);
105 } 114 }
106 115
107 void Start(AudioSourceCallback* source_callback) override { 116 void Start(AudioSourceCallback* source_callback) override {
108 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); 117 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
109 DCHECK(source_callback); 118 DCHECK(source_callback);
110 119
111 if (!opened_ || proxy_) 120 if (!opened_ || proxy_)
112 return; 121 return;
113 source_callback_ = source_callback; 122 source_callback_ = source_callback;
114 proxy_.reset(new ResamplerProxy(this, input_params_, output_params_)); 123 proxy_ =
124 base::MakeUnique<ResamplerProxy>(this, input_params_, output_params_);
115 audio_mixer_->AddInput(proxy_.get()); 125 audio_mixer_->AddInput(proxy_.get());
116 } 126 }
117 127
118 void Stop() override { 128 void Stop() override {
119 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); 129 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
120 130
121 if (!proxy_) 131 if (!proxy_)
122 return; 132 return;
123 audio_mixer_->RemoveInput(proxy_.get()); 133 audio_mixer_->RemoveInput(proxy_.get());
134 // Once the above function returns it is guaranteed that proxy_ or
135 // source_callback_ would not be used on the backend thread, so it is safe
136 // to reset them.
124 proxy_.reset(); 137 proxy_.reset();
125 source_callback_ = nullptr; 138 source_callback_ = nullptr;
126 } 139 }
127 140
128 void SetVolume(double volume) override { 141 void SetVolume(double volume) override {
129 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); 142 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
130 143
144 base::AutoLock auto_lock(volume_lock_);
131 volume_ = volume; 145 volume_ = volume;
132 } 146 }
133 147
134 void GetVolume(double* volume) override { 148 void GetVolume(double* volume) override {
135 DCHECK(audio_manager_->GetTaskRunner()->BelongsToCurrentThread()); 149 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
136 150
137 *volume = volume_; 151 *volume = volume_;
138 } 152 }
139 153
140 // ::media::AudioConverter::InputCallback implementation 154 // ::media::AudioConverter::InputCallback implementation
141 double ProvideInput(::media::AudioBus* audio_bus, 155 double ProvideInput(::media::AudioBus* audio_bus,
142 uint32_t frames_delayed) override { 156 uint32_t frames_delayed) override {
157 // Called on backend thread. Member variables accessed from both backend
158 // and audio thread must be thread-safe.
143 DCHECK(source_callback_); 159 DCHECK(source_callback_);
144 160
145 const base::TimeDelta delay = ::media::AudioTimestampHelper::FramesToTime( 161 const base::TimeDelta delay = ::media::AudioTimestampHelper::FramesToTime(
146 frames_delayed, input_params_.sample_rate()); 162 frames_delayed, input_params_.sample_rate());
147 source_callback_->OnMoreData(delay, base::TimeTicks::Now(), 0, audio_bus); 163 source_callback_->OnMoreData(delay, base::TimeTicks::Now(), 0, audio_bus);
164
165 base::AutoLock auto_lock(volume_lock_);
148 return volume_; 166 return volume_;
149 } 167 }
150 168
151 CastAudioManager* const audio_manager_;
152 CastAudioMixer* const audio_mixer_; 169 CastAudioMixer* const audio_mixer_;
153
154 std::unique_ptr<ResamplerProxy> proxy_;
155 AudioSourceCallback* source_callback_;
156 const ::media::AudioParameters input_params_; 170 const ::media::AudioParameters input_params_;
157 const ::media::AudioParameters output_params_; 171 const ::media::AudioParameters output_params_;
172
158 bool opened_; 173 bool opened_;
159 double volume_; 174 double volume_;
175 base::Lock volume_lock_;
176 AudioSourceCallback* source_callback_;
177 std::unique_ptr<ResamplerProxy> proxy_;
160 178
179 THREAD_CHECKER(audio_thread_checker_);
161 DISALLOW_COPY_AND_ASSIGN(MixerProxyStream); 180 DISALLOW_COPY_AND_ASSIGN(MixerProxyStream);
162 }; 181 };
163 182
164 CastAudioMixer::CastAudioMixer(const RealStreamFactory& real_stream_factory) 183 CastAudioMixer::CastAudioMixer(CastAudioManager* audio_manager)
165 : error_(false), 184 : audio_manager_(audio_manager), error_(false), output_stream_(nullptr) {
166 real_stream_factory_(real_stream_factory),
167 output_stream_(nullptr) {
168 output_params_ = ::media::AudioParameters( 185 output_params_ = ::media::AudioParameters(
169 ::media::AudioParameters::Format::AUDIO_PCM_LOW_LATENCY, 186 ::media::AudioParameters::Format::AUDIO_PCM_LOW_LATENCY,
170 ::media::CHANNEL_LAYOUT_STEREO, kSampleRate, kBitsPerSample, 187 ::media::CHANNEL_LAYOUT_STEREO, kSampleRate, kBitsPerSample,
171 kFramesPerBuffer); 188 kFramesPerBuffer);
172 mixer_.reset( 189 mixer_.reset(
173 new ::media::AudioConverter(output_params_, output_params_, false)); 190 new ::media::AudioConverter(output_params_, output_params_, false));
174 thread_checker_.DetachFromThread(); 191 DETACH_FROM_THREAD(audio_thread_checker_);
175 } 192 }
176 193
177 CastAudioMixer::~CastAudioMixer() {} 194 CastAudioMixer::~CastAudioMixer() {}
178 195
179 ::media::AudioOutputStream* CastAudioMixer::MakeStream( 196 ::media::AudioOutputStream* CastAudioMixer::MakeStream(
180 const ::media::AudioParameters& params, 197 const ::media::AudioParameters& params) {
181 CastAudioManager* audio_manager) { 198 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
182 return new MixerProxyStream(params, output_params_, audio_manager, this); 199 return new MixerProxyStream(params, output_params_, this);
183 } 200 }
184 201
185 bool CastAudioMixer::Register(MixerProxyStream* proxy_stream) { 202 bool CastAudioMixer::Register(MixerProxyStream* proxy_stream) {
186 DCHECK(thread_checker_.CalledOnValidThread()); 203 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
187 DCHECK(std::find(proxy_streams_.begin(), proxy_streams_.end(), 204 DCHECK(std::find(proxy_streams_.begin(), proxy_streams_.end(),
188 proxy_stream) == proxy_streams_.end()); 205 proxy_stream) == proxy_streams_.end());
189 206
190 // If the mixer is in an error state, do not allow any new 207 // Do not allow opening new streams while in error state.
191 // registrations until every active stream has been
192 // unregistered.
193 if (error_) 208 if (error_)
194 return false; 209 return false;
195 210
196 // Initialize a backend instance if there are no output streams. 211 // Initialize a backend instance if there are no output streams.
197 // The stream will fail to register if the CastAudioOutputStream 212 // The stream will fail to register if the CastAudioOutputStream
198 // is not opened properly. 213 // is not opened properly.
199 if (proxy_streams_.empty()) { 214 if (proxy_streams_.empty()) {
200 DCHECK(!output_stream_); 215 DCHECK(!output_stream_);
201 output_stream_ = real_stream_factory_.Run(output_params_); 216 output_stream_ = audio_manager_->MakeMixerOutputStream(output_params_);
202 if (!output_stream_->Open()) { 217 if (!output_stream_->Open()) {
203 output_stream_->Close(); 218 output_stream_->Close();
204 output_stream_ = nullptr; 219 output_stream_ = nullptr;
205 return false; 220 return false;
206 } 221 }
207 } 222 }
208 223
209 proxy_streams_.push_back(proxy_stream); 224 proxy_streams_.insert(proxy_stream);
210 return true; 225 return true;
211 } 226 }
212 227
213 void CastAudioMixer::Unregister(MixerProxyStream* proxy_stream) { 228 void CastAudioMixer::Unregister(MixerProxyStream* proxy_stream) {
214 DCHECK(thread_checker_.CalledOnValidThread()); 229 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
215 auto it = 230 DCHECK(std::find(proxy_streams_.begin(), proxy_streams_.end(),
216 std::find(proxy_streams_.begin(), proxy_streams_.end(), proxy_stream); 231 proxy_stream) != proxy_streams_.end());
217 DCHECK(it != proxy_streams_.end());
218 232
219 proxy_streams_.erase(it); 233 proxy_streams_.erase(proxy_stream);
220 234
221 // Reset the state once all streams have been unregistered. 235 // Reset the state once all streams have been unregistered.
222 if (proxy_streams_.empty()) { 236 if (proxy_streams_.empty()) {
223 DCHECK(mixer_->empty()); 237 DCHECK(mixer_->empty());
224 if (output_stream_) 238 if (output_stream_)
225 output_stream_->Close(); 239 output_stream_->Close();
226 output_stream_ = nullptr; 240 output_stream_ = nullptr;
227 error_ = false; 241 error_ = false;
228 } 242 }
229 } 243 }
230 244
231 void CastAudioMixer::AddInput( 245 void CastAudioMixer::AddInput(
232 ::media::AudioConverter::InputCallback* input_callback) { 246 ::media::AudioConverter::InputCallback* input_callback) {
233 DCHECK(thread_checker_.CalledOnValidThread()); 247 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
234 248
235 // Start the backend if there are no current inputs. 249 // Start the backend if there are no current inputs.
236 if (mixer_->empty() && output_stream_) 250 if (mixer_->empty() && output_stream_)
237 output_stream_->Start(this); 251 output_stream_->Start(this);
252
253 base::AutoLock auto_lock(mixer_lock_);
238 mixer_->AddInput(input_callback); 254 mixer_->AddInput(input_callback);
239 } 255 }
240 256
241 void CastAudioMixer::RemoveInput( 257 void CastAudioMixer::RemoveInput(
242 ::media::AudioConverter::InputCallback* input_callback) { 258 ::media::AudioConverter::InputCallback* input_callback) {
243 DCHECK(thread_checker_.CalledOnValidThread()); 259 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
244 260
245 mixer_->RemoveInput(input_callback); 261 {
262 base::AutoLock auto_lock(mixer_lock_);
263 mixer_->RemoveInput(input_callback);
264 }
265
246 // Stop |output_stream_| if there are no inputs and the stream is running. 266 // Stop |output_stream_| if there are no inputs and the stream is running.
247 if (mixer_->empty() && output_stream_) 267 if (mixer_->empty() && output_stream_)
248 output_stream_->Stop(); 268 output_stream_->Stop();
249 } 269 }
250 270
251 int CastAudioMixer::OnMoreData(base::TimeDelta delay, 271 int CastAudioMixer::OnMoreData(base::TimeDelta delay,
252 base::TimeTicks /* delay_timestamp */, 272 base::TimeTicks /* delay_timestamp */,
253 int /* prior_frames_skipped */, 273 int /* prior_frames_skipped */,
254 ::media::AudioBus* dest) { 274 ::media::AudioBus* dest) {
255 DCHECK(thread_checker_.CalledOnValidThread()); 275 // Called on backend thread.
256
257 uint32_t frames_delayed = ::media::AudioTimestampHelper::TimeToFrames( 276 uint32_t frames_delayed = ::media::AudioTimestampHelper::TimeToFrames(
258 delay, output_params_.sample_rate()); 277 delay, output_params_.sample_rate());
278
279 base::AutoLock auto_lock(mixer_lock_);
259 mixer_->ConvertWithDelay(frames_delayed, dest); 280 mixer_->ConvertWithDelay(frames_delayed, dest);
260 return dest->frames(); 281 return dest->frames();
261 } 282 }
262 283
263 void CastAudioMixer::OnError() { 284 void CastAudioMixer::OnError() {
264 DCHECK(thread_checker_.CalledOnValidThread()); 285 // Called on backend thread.
286 audio_manager_->GetTaskRunner()->PostTask(
287 FROM_HERE,
288 base::BindOnce(&CastAudioMixer::HandleError, base::Unretained(this)));
289 }
265 290
266 // TODO(ameyak): Add rate limiting. If errors are seen to occur 291 void CastAudioMixer::HandleError() {
267 // above some arbitrary value in a specified amount 292 DCHECK_CALLED_ON_VALID_THREAD(audio_thread_checker_);
268 // of time, signal errors to all currently active
269 // streams and close.
270 293
271 output_stream_->Stop(); 294 error_ = true;
272 output_stream_->Close(); 295 for (auto it = proxy_streams_.begin(); it != proxy_streams_.end(); ++it)
273 output_stream_ = real_stream_factory_.Run(output_params_); 296 (*it)->OnError();
274
275 if (output_stream_->Open()) {
276 output_stream_->Start(this);
277 } else {
278 // Assume error state
279 output_stream_->Close();
280 output_stream_ = nullptr;
281 error_ = true;
282 for (auto it = proxy_streams_.begin(); it != proxy_streams_.end(); it++)
283 (*it)->OnError();
284 }
285 } 297 }
286 298
287 } // namespace media 299 } // namespace media
288 } // namespace chromecast 300 } // namespace chromecast
OLDNEW
« no previous file with comments | « chromecast/media/audio/cast_audio_mixer.h ('k') | chromecast/media/audio/cast_audio_mixer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698