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