audio_stream_handler.cc revision 116680a4aac90f2aa7413d9095a592090648e557
1// Copyright 2013 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/sounds/audio_stream_handler.h" 6 7#include <string> 8 9#include "base/cancelable_callback.h" 10#include "base/logging.h" 11#include "base/single_thread_task_runner.h" 12#include "base/synchronization/lock.h" 13#include "base/time/time.h" 14#include "media/audio/audio_manager.h" 15#include "media/audio/audio_manager_base.h" 16#include "media/base/channel_layout.h" 17 18namespace media { 19 20namespace { 21 22// Volume percent. 23const double kOutputVolumePercent = 0.8; 24 25// The number of frames each OnMoreData() call will request. 26const int kDefaultFrameCount = 1024; 27 28// Keep alive timeout for audio stream. 29const int kKeepAliveMs = 1500; 30 31AudioStreamHandler::TestObserver* g_observer_for_testing = NULL; 32AudioOutputStream::AudioSourceCallback* g_audio_source_for_testing = NULL; 33 34} // namespace 35 36class AudioStreamHandler::AudioStreamContainer 37 : public AudioOutputStream::AudioSourceCallback { 38 public: 39 AudioStreamContainer(const WavAudioHandler& wav_audio) 40 : started_(false), 41 stream_(NULL), 42 cursor_(0), 43 delayed_stop_posted_(false), 44 wav_audio_(wav_audio) {} 45 46 virtual ~AudioStreamContainer() { 47 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); 48 } 49 50 void Play() { 51 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); 52 53 if (!stream_) { 54 const AudioParameters& p = wav_audio_.params(); 55 const AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, 56 p.channel_layout(), 57 p.sample_rate(), 58 p.bits_per_sample(), 59 kDefaultFrameCount); 60 stream_ = AudioManager::Get()->MakeAudioOutputStreamProxy(params, 61 std::string()); 62 if (!stream_ || !stream_->Open()) { 63 LOG(ERROR) << "Failed to open an output stream."; 64 return; 65 } 66 stream_->SetVolume(kOutputVolumePercent); 67 } 68 69 { 70 base::AutoLock al(state_lock_); 71 72 delayed_stop_posted_ = false; 73 stop_closure_.Reset(base::Bind(&AudioStreamContainer::StopStream, 74 base::Unretained(this))); 75 76 if (started_) { 77 if (wav_audio_.AtEnd(cursor_)) 78 cursor_ = 0; 79 return; 80 } 81 82 cursor_ = 0; 83 } 84 85 started_ = true; 86 if (g_audio_source_for_testing) 87 stream_->Start(g_audio_source_for_testing); 88 else 89 stream_->Start(this); 90 91 if (g_observer_for_testing) 92 g_observer_for_testing->OnPlay(); 93 } 94 95 void Stop() { 96 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); 97 StopStream(); 98 if (stream_) 99 stream_->Close(); 100 stream_ = NULL; 101 stop_closure_.Cancel(); 102 } 103 104 private: 105 // AudioOutputStream::AudioSourceCallback overrides: 106 // Following methods could be called from *ANY* thread. 107 virtual int OnMoreData(AudioBus* dest, 108 AudioBuffersState /* state */) OVERRIDE { 109 base::AutoLock al(state_lock_); 110 size_t bytes_written = 0; 111 112 if (wav_audio_.AtEnd(cursor_) || 113 !wav_audio_.CopyTo(dest, cursor_, &bytes_written)) { 114 if (delayed_stop_posted_) 115 return 0; 116 delayed_stop_posted_ = true; 117 AudioManager::Get()->GetTaskRunner()->PostDelayedTask( 118 FROM_HERE, 119 stop_closure_.callback(), 120 base::TimeDelta::FromMilliseconds(kKeepAliveMs)); 121 return 0; 122 } 123 cursor_ += bytes_written; 124 return dest->frames(); 125 } 126 127 virtual void OnError(AudioOutputStream* /* stream */) OVERRIDE { 128 LOG(ERROR) << "Error during system sound reproduction."; 129 } 130 131 void StopStream() { 132 DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); 133 134 if (stream_ && started_) { 135 // Do not hold the |state_lock_| while stopping the output stream. 136 stream_->Stop(); 137 if (g_observer_for_testing) 138 g_observer_for_testing->OnStop(cursor_); 139 } 140 141 started_ = false; 142 } 143 144 // Must only be accessed on the AudioManager::GetTaskRunner() thread. 145 bool started_; 146 AudioOutputStream* stream_; 147 148 // All variables below must be accessed under |state_lock_| when |started_|. 149 base::Lock state_lock_; 150 size_t cursor_; 151 bool delayed_stop_posted_; 152 const WavAudioHandler wav_audio_; 153 base::CancelableClosure stop_closure_; 154 155 DISALLOW_COPY_AND_ASSIGN(AudioStreamContainer); 156}; 157 158AudioStreamHandler::AudioStreamHandler(const base::StringPiece& wav_data) 159 : wav_audio_(wav_data), 160 initialized_(false) { 161 AudioManager* manager = AudioManager::Get(); 162 if (!manager) { 163 LOG(ERROR) << "Can't get access to audio manager."; 164 return; 165 } 166 if (!wav_audio_.params().IsValid()) { 167 LOG(ERROR) << "Audio params are invalid."; 168 return; 169 } 170 stream_.reset(new AudioStreamContainer(wav_audio_)); 171 initialized_ = true; 172} 173 174AudioStreamHandler::~AudioStreamHandler() { 175 DCHECK(CalledOnValidThread()); 176 AudioManager::Get()->GetTaskRunner()->PostTask( 177 FROM_HERE, 178 base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get()))); 179 AudioManager::Get()->GetTaskRunner()->DeleteSoon(FROM_HERE, 180 stream_.release()); 181} 182 183bool AudioStreamHandler::IsInitialized() const { 184 DCHECK(CalledOnValidThread()); 185 return initialized_; 186} 187 188bool AudioStreamHandler::Play() { 189 DCHECK(CalledOnValidThread()); 190 191 if (!IsInitialized()) 192 return false; 193 194 AudioManager::Get()->GetTaskRunner()->PostTask( 195 FROM_HERE, 196 base::Bind(base::IgnoreResult(&AudioStreamContainer::Play), 197 base::Unretained(stream_.get()))); 198 return true; 199} 200 201void AudioStreamHandler::Stop() { 202 DCHECK(CalledOnValidThread()); 203 AudioManager::Get()->GetTaskRunner()->PostTask( 204 FROM_HERE, 205 base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get()))); 206} 207 208// static 209void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) { 210 g_observer_for_testing = observer; 211} 212 213// static 214void AudioStreamHandler::SetAudioSourceForTesting( 215 AudioOutputStream::AudioSourceCallback* source) { 216 g_audio_source_for_testing = source; 217} 218 219} // namespace media 220