• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 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 "media/audio/audio_output_controller.h"
6 
7 #include "base/bind.h"
8 #include "base/debug/trace_event.h"
9 #include "base/metrics/histogram.h"
10 #include "base/task_runner_util.h"
11 #include "base/threading/platform_thread.h"
12 #include "base/time/time.h"
13 #include "build/build_config.h"
14 #include "media/base/scoped_histogram_timer.h"
15 
16 using base::TimeDelta;
17 
18 namespace media {
19 
20 #if defined(AUDIO_POWER_MONITORING)
21 // Time constant for AudioPowerMonitor.  See AudioPowerMonitor ctor comments for
22 // semantics.  This value was arbitrarily chosen, but seems to work well.
23 static const int kPowerMeasurementTimeConstantMillis = 10;
24 #endif
25 
AudioOutputController(AudioManager * audio_manager,EventHandler * handler,const AudioParameters & params,const std::string & output_device_id,SyncReader * sync_reader)26 AudioOutputController::AudioOutputController(
27     AudioManager* audio_manager,
28     EventHandler* handler,
29     const AudioParameters& params,
30     const std::string& output_device_id,
31     SyncReader* sync_reader)
32     : audio_manager_(audio_manager),
33       params_(params),
34       handler_(handler),
35       output_device_id_(output_device_id),
36       stream_(NULL),
37       diverting_to_stream_(NULL),
38       volume_(1.0),
39       state_(kEmpty),
40       sync_reader_(sync_reader),
41       message_loop_(audio_manager->GetTaskRunner()),
42 #if defined(AUDIO_POWER_MONITORING)
43       power_monitor_(
44           params.sample_rate(),
45           TimeDelta::FromMilliseconds(kPowerMeasurementTimeConstantMillis)),
46 #endif
47       on_more_io_data_called_(0) {
48   DCHECK(audio_manager);
49   DCHECK(handler_);
50   DCHECK(sync_reader_);
51   DCHECK(message_loop_.get());
52 }
53 
~AudioOutputController()54 AudioOutputController::~AudioOutputController() {
55   DCHECK_EQ(kClosed, state_);
56 }
57 
58 // static
Create(AudioManager * audio_manager,EventHandler * event_handler,const AudioParameters & params,const std::string & output_device_id,SyncReader * sync_reader)59 scoped_refptr<AudioOutputController> AudioOutputController::Create(
60     AudioManager* audio_manager,
61     EventHandler* event_handler,
62     const AudioParameters& params,
63     const std::string& output_device_id,
64     SyncReader* sync_reader) {
65   DCHECK(audio_manager);
66   DCHECK(sync_reader);
67 
68   if (!params.IsValid() || !audio_manager)
69     return NULL;
70 
71   scoped_refptr<AudioOutputController> controller(new AudioOutputController(
72       audio_manager, event_handler, params, output_device_id, sync_reader));
73   controller->message_loop_->PostTask(FROM_HERE, base::Bind(
74       &AudioOutputController::DoCreate, controller, false));
75   return controller;
76 }
77 
Play()78 void AudioOutputController::Play() {
79   message_loop_->PostTask(FROM_HERE, base::Bind(
80       &AudioOutputController::DoPlay, this));
81 }
82 
Pause()83 void AudioOutputController::Pause() {
84   message_loop_->PostTask(FROM_HERE, base::Bind(
85       &AudioOutputController::DoPause, this));
86 }
87 
Close(const base::Closure & closed_task)88 void AudioOutputController::Close(const base::Closure& closed_task) {
89   DCHECK(!closed_task.is_null());
90   message_loop_->PostTaskAndReply(FROM_HERE, base::Bind(
91       &AudioOutputController::DoClose, this), closed_task);
92 }
93 
SetVolume(double volume)94 void AudioOutputController::SetVolume(double volume) {
95   message_loop_->PostTask(FROM_HERE, base::Bind(
96       &AudioOutputController::DoSetVolume, this, volume));
97 }
98 
GetOutputDeviceId(base::Callback<void (const std::string &)> callback) const99 void AudioOutputController::GetOutputDeviceId(
100     base::Callback<void(const std::string&)> callback) const {
101   base::PostTaskAndReplyWithResult(
102       message_loop_.get(),
103       FROM_HERE,
104       base::Bind(&AudioOutputController::DoGetOutputDeviceId, this),
105       callback);
106 }
107 
SwitchOutputDevice(const std::string & output_device_id,const base::Closure & callback)108 void AudioOutputController::SwitchOutputDevice(
109     const std::string& output_device_id, const base::Closure& callback) {
110   message_loop_->PostTaskAndReply(
111       FROM_HERE,
112       base::Bind(&AudioOutputController::DoSwitchOutputDevice, this,
113                  output_device_id),
114       callback);
115 }
116 
DoCreate(bool is_for_device_change)117 void AudioOutputController::DoCreate(bool is_for_device_change) {
118   DCHECK(message_loop_->BelongsToCurrentThread());
119   SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CreateTime");
120   TRACE_EVENT0("audio", "AudioOutputController::DoCreate");
121 
122   // Close() can be called before DoCreate() is executed.
123   if (state_ == kClosed)
124     return;
125 
126   DoStopCloseAndClearStream();  // Calls RemoveOutputDeviceChangeListener().
127   DCHECK_EQ(kEmpty, state_);
128 
129   stream_ = diverting_to_stream_ ?
130       diverting_to_stream_ :
131       audio_manager_->MakeAudioOutputStreamProxy(params_, output_device_id_);
132   if (!stream_) {
133     state_ = kError;
134     handler_->OnError();
135     return;
136   }
137 
138   if (!stream_->Open()) {
139     DoStopCloseAndClearStream();
140     state_ = kError;
141     handler_->OnError();
142     return;
143   }
144 
145   // Everything started okay, so re-register for state change callbacks if
146   // stream_ was created via AudioManager.
147   if (stream_ != diverting_to_stream_)
148     audio_manager_->AddOutputDeviceChangeListener(this);
149 
150   // We have successfully opened the stream. Set the initial volume.
151   stream_->SetVolume(volume_);
152 
153   // Finally set the state to kCreated.
154   state_ = kCreated;
155 
156   // And then report we have been created if we haven't done so already.
157   if (!is_for_device_change)
158     handler_->OnCreated();
159 }
160 
DoPlay()161 void AudioOutputController::DoPlay() {
162   DCHECK(message_loop_->BelongsToCurrentThread());
163   SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PlayTime");
164   TRACE_EVENT0("audio", "AudioOutputController::DoPlay");
165 
166   // We can start from created or paused state.
167   if (state_ != kCreated && state_ != kPaused)
168     return;
169 
170   // Ask for first packet.
171   sync_reader_->UpdatePendingBytes(0);
172 
173   state_ = kPlaying;
174 
175   stream_->Start(this);
176 
177   // For UMA tracking purposes, start the wedge detection timer.  This allows us
178   // to record statistics about the number of wedged playbacks in the field.
179   //
180   // WedgeCheck() will look to see if |on_more_io_data_called_| is true after
181   // the timeout expires.  Care must be taken to ensure the wedge check delay is
182   // large enough that the value isn't queried while OnMoreDataIO() is setting
183   // it.
184   //
185   // Timer self-manages its lifetime and WedgeCheck() will only record the UMA
186   // statistic if state is still kPlaying.  Additional Start() calls will
187   // invalidate the previous timer.
188   wedge_timer_.reset(new base::OneShotTimer<AudioOutputController>());
189   wedge_timer_->Start(
190       FROM_HERE, TimeDelta::FromSeconds(5), this,
191       &AudioOutputController::WedgeCheck);
192 
193   handler_->OnPlaying();
194 }
195 
StopStream()196 void AudioOutputController::StopStream() {
197   DCHECK(message_loop_->BelongsToCurrentThread());
198 
199   if (state_ == kPlaying) {
200     wedge_timer_.reset();
201     stream_->Stop();
202 
203 #if defined(AUDIO_POWER_MONITORING)
204     // A stopped stream is silent, and power_montior_.Scan() is no longer being
205     // called; so we must reset the power monitor.
206     power_monitor_.Reset();
207 #endif
208 
209     state_ = kPaused;
210   }
211 }
212 
DoPause()213 void AudioOutputController::DoPause() {
214   DCHECK(message_loop_->BelongsToCurrentThread());
215   SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.PauseTime");
216   TRACE_EVENT0("audio", "AudioOutputController::DoPause");
217 
218   StopStream();
219 
220   if (state_ != kPaused)
221     return;
222 
223   // Let the renderer know we've stopped.  Necessary to let PPAPI clients know
224   // audio has been shutdown.  TODO(dalecurtis): This stinks.  PPAPI should have
225   // a better way to know when it should exit PPB_Audio_Shared::Run().
226   sync_reader_->UpdatePendingBytes(-1);
227 
228   handler_->OnPaused();
229 }
230 
DoClose()231 void AudioOutputController::DoClose() {
232   DCHECK(message_loop_->BelongsToCurrentThread());
233   SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.CloseTime");
234   TRACE_EVENT0("audio", "AudioOutputController::DoClose");
235 
236   if (state_ != kClosed) {
237     DoStopCloseAndClearStream();
238     sync_reader_->Close();
239     state_ = kClosed;
240   }
241 }
242 
DoSetVolume(double volume)243 void AudioOutputController::DoSetVolume(double volume) {
244   DCHECK(message_loop_->BelongsToCurrentThread());
245 
246   // Saves the volume to a member first. We may not be able to set the volume
247   // right away but when the stream is created we'll set the volume.
248   volume_ = volume;
249 
250   switch (state_) {
251     case kCreated:
252     case kPlaying:
253     case kPaused:
254       stream_->SetVolume(volume_);
255       break;
256     default:
257       return;
258   }
259 }
260 
DoGetOutputDeviceId() const261 std::string AudioOutputController::DoGetOutputDeviceId() const {
262   DCHECK(message_loop_->BelongsToCurrentThread());
263   return output_device_id_;
264 }
265 
DoSwitchOutputDevice(const std::string & output_device_id)266 void AudioOutputController::DoSwitchOutputDevice(
267     const std::string& output_device_id) {
268   DCHECK(message_loop_->BelongsToCurrentThread());
269 
270   if (state_ == kClosed)
271     return;
272 
273   if (output_device_id == output_device_id_)
274     return;
275 
276   output_device_id_ = output_device_id;
277 
278   // If output is currently diverted, we must not call OnDeviceChange
279   // since it would break the diverted setup. Once diversion is
280   // finished using StopDiverting() the output will switch to the new
281   // device ID.
282   if (stream_ != diverting_to_stream_)
283     OnDeviceChange();
284 }
285 
DoReportError()286 void AudioOutputController::DoReportError() {
287   DCHECK(message_loop_->BelongsToCurrentThread());
288   if (state_ != kClosed)
289     handler_->OnError();
290 }
291 
OnMoreData(AudioBus * dest,AudioBuffersState buffers_state)292 int AudioOutputController::OnMoreData(AudioBus* dest,
293                                       AudioBuffersState buffers_state) {
294   TRACE_EVENT0("audio", "AudioOutputController::OnMoreData");
295 
296   // Indicate that we haven't wedged (at least not indefinitely, WedgeCheck()
297   // may have already fired if OnMoreIOData() took an abnormal amount of time).
298   // Since this thread is the only writer of |on_more_io_data_called_| once the
299   // thread starts, its safe to compare and then increment.
300   if (base::AtomicRefCountIsZero(&on_more_io_data_called_))
301     base::AtomicRefCountInc(&on_more_io_data_called_);
302 
303   sync_reader_->Read(dest);
304 
305   const int frames = dest->frames();
306   sync_reader_->UpdatePendingBytes(
307       buffers_state.total_bytes() + frames * params_.GetBytesPerFrame());
308 
309 #if defined(AUDIO_POWER_MONITORING)
310   power_monitor_.Scan(*dest, frames);
311 #endif
312 
313   return frames;
314 }
315 
OnError(AudioOutputStream * stream)316 void AudioOutputController::OnError(AudioOutputStream* stream) {
317   // Handle error on the audio controller thread.
318   message_loop_->PostTask(FROM_HERE, base::Bind(
319       &AudioOutputController::DoReportError, this));
320 }
321 
DoStopCloseAndClearStream()322 void AudioOutputController::DoStopCloseAndClearStream() {
323   DCHECK(message_loop_->BelongsToCurrentThread());
324 
325   // Allow calling unconditionally and bail if we don't have a stream_ to close.
326   if (stream_) {
327     // De-register from state change callbacks if stream_ was created via
328     // AudioManager.
329     if (stream_ != diverting_to_stream_)
330       audio_manager_->RemoveOutputDeviceChangeListener(this);
331 
332     StopStream();
333     stream_->Close();
334     if (stream_ == diverting_to_stream_)
335       diverting_to_stream_ = NULL;
336     stream_ = NULL;
337   }
338 
339   state_ = kEmpty;
340 }
341 
OnDeviceChange()342 void AudioOutputController::OnDeviceChange() {
343   DCHECK(message_loop_->BelongsToCurrentThread());
344   SCOPED_UMA_HISTOGRAM_TIMER("Media.AudioOutputController.DeviceChangeTime");
345   TRACE_EVENT0("audio", "AudioOutputController::OnDeviceChange");
346 
347   // TODO(dalecurtis): Notify the renderer side that a device change has
348   // occurred.  Currently querying the hardware information here will lead to
349   // crashes on OSX.  See http://crbug.com/158170.
350 
351   // Recreate the stream (DoCreate() will first shut down an existing stream).
352   // Exit if we ran into an error.
353   const State original_state = state_;
354   DoCreate(true);
355   if (!stream_ || state_ == kError)
356     return;
357 
358   // Get us back to the original state or an equivalent state.
359   switch (original_state) {
360     case kPlaying:
361       DoPlay();
362       return;
363     case kCreated:
364     case kPaused:
365       // From the outside these two states are equivalent.
366       return;
367     default:
368       NOTREACHED() << "Invalid original state.";
369   }
370 }
371 
GetAudioParameters()372 const AudioParameters& AudioOutputController::GetAudioParameters() {
373   return params_;
374 }
375 
StartDiverting(AudioOutputStream * to_stream)376 void AudioOutputController::StartDiverting(AudioOutputStream* to_stream) {
377   message_loop_->PostTask(
378       FROM_HERE,
379       base::Bind(&AudioOutputController::DoStartDiverting, this, to_stream));
380 }
381 
StopDiverting()382 void AudioOutputController::StopDiverting() {
383   message_loop_->PostTask(
384       FROM_HERE, base::Bind(&AudioOutputController::DoStopDiverting, this));
385 }
386 
DoStartDiverting(AudioOutputStream * to_stream)387 void AudioOutputController::DoStartDiverting(AudioOutputStream* to_stream) {
388   DCHECK(message_loop_->BelongsToCurrentThread());
389 
390   if (state_ == kClosed)
391     return;
392 
393   DCHECK(!diverting_to_stream_);
394   diverting_to_stream_ = to_stream;
395   // Note: OnDeviceChange() will engage the "re-create" process, which will
396   // detect and use the alternate AudioOutputStream rather than create a new one
397   // via AudioManager.
398   OnDeviceChange();
399 }
400 
DoStopDiverting()401 void AudioOutputController::DoStopDiverting() {
402   DCHECK(message_loop_->BelongsToCurrentThread());
403 
404   if (state_ == kClosed)
405     return;
406 
407   // Note: OnDeviceChange() will cause the existing stream (the consumer of the
408   // diverted audio data) to be closed, and diverting_to_stream_ will be set
409   // back to NULL.
410   OnDeviceChange();
411   DCHECK(!diverting_to_stream_);
412 }
413 
ReadCurrentPowerAndClip()414 std::pair<float, bool> AudioOutputController::ReadCurrentPowerAndClip() {
415 #if defined(AUDIO_POWER_MONITORING)
416   return power_monitor_.ReadCurrentPowerAndClip();
417 #else
418   NOTREACHED();
419   return std::make_pair(AudioPowerMonitor::zero_power(), false);
420 #endif
421 }
422 
WedgeCheck()423 void AudioOutputController::WedgeCheck() {
424   DCHECK(message_loop_->BelongsToCurrentThread());
425 
426   // If we should be playing and we haven't, that's a wedge.
427   if (state_ == kPlaying) {
428     UMA_HISTOGRAM_BOOLEAN("Media.AudioOutputControllerPlaybackStartupSuccess",
429                           base::AtomicRefCountIsOne(&on_more_io_data_called_));
430   }
431 }
432 
433 }  // namespace media
434