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 18 namespace media { 19 20 namespace { 21 22 // Volume percent. 23 const double kOutputVolumePercent = 0.8; 24 25 // The number of frames each OnMoreData() call will request. 26 const int kDefaultFrameCount = 1024; 27 28 // Keep alive timeout for audio stream. 29 const int kKeepAliveMs = 1500; 30 31 AudioStreamHandler::TestObserver* g_observer_for_testing = NULL; 32 AudioOutputStream::AudioSourceCallback* g_audio_source_for_testing = NULL; 33 34 } // namespace 35 36 class 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 158 AudioStreamHandler::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 174 AudioStreamHandler::~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 183 bool AudioStreamHandler::IsInitialized() const { 184 DCHECK(CalledOnValidThread()); 185 return initialized_; 186 } 187 188 bool 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 201 void 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 209 void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) { 210 g_observer_for_testing = observer; 211 } 212 213 // static 214 void AudioStreamHandler::SetAudioSourceForTesting( 215 AudioOutputStream::AudioSourceCallback* source) { 216 g_audio_source_for_testing = source; 217 } 218 219 } // namespace media 220