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