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