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/message_loop/message_loop_proxy.h" | 11 #include "base/message_loop/message_loop_proxy.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 |
25 // Delay between end of sound and call to AudioOutputStream::Stop(). | |
26 const int kStopStreamDelaySec = 3; | |
27 | |
22 // The number of frames each OnMoreData() call will request. | 28 // The number of frames each OnMoreData() call will request. |
23 const int kDefaultFrameCount = 1024; | 29 const int kDefaultFrameCount = 1024; |
24 | 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 replay_scheduled_(false), |
39 } | 44 is_stream_stopped_(true), |
45 schedule_replay_request_for_testing_(0) {} | |
40 | 46 |
41 virtual ~AudioStreamContainer() { | 47 virtual ~AudioStreamContainer() { |
42 DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread()); | 48 DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread()); |
49 stop_stream_closure_.Cancel(); | |
50 | |
51 if (stream_) { | |
52 stream_->Stop(); | |
53 stream_->Close(); | |
54 } | |
43 } | 55 } |
44 | 56 |
45 void Play() { | 57 void Play() { |
46 DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread()); | 58 DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread()); |
47 | 59 |
48 if (!stream_) { | 60 { |
49 stream_ = AudioManager::Get()->MakeAudioOutputStreamProxy(params_, | 61 base::AutoLock sl(state_lock_); |
50 std::string(), | 62 |
51 std::string()); | 63 if (!stream_) { |
52 if (!stream_ || !stream_->Open()) { | 64 const AudioParameters& p = wav_audio_.params(); |
53 LOG(ERROR) << "Failed to open an output stream."; | 65 const AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, |
66 p.channel_layout(), | |
67 p.sample_rate(), | |
68 p.bits_per_sample(), | |
69 kDefaultFrameCount); | |
70 stream_ = AudioManager::Get()->MakeAudioOutputStreamProxy( | |
71 params, std::string(), std::string()); | |
72 if (!stream_ || !stream_->Open()) { | |
73 LOG(ERROR) << "Failed to open an output stream."; | |
74 return; | |
75 } | |
76 cursor_ = 0; | |
77 stream_->SetVolume(kOutputVolumePercent); | |
78 } else if (is_stream_stopped_) { | |
79 // If stream exists and idle, just stop the stream and start again. | |
80 cursor_ = 0; | |
81 StopStreamWithoutLock(); | |
DaleCurtis
2014/01/02 20:11:17
Method says WithoutLock but in under state_lock_.
ygorshenin1
2014/01/09 19:26:57
Removed.
| |
82 } else { | |
83 // Stream exists and not idle, so let the current sound to | |
84 // finish playing. If less than |wav_audio_.duration()| / 2 | |
85 // until the end (including all replay requests), then replay is | |
86 // sheduled. The main purpose of this strategy is to smooth | |
87 // sound reproduction when multiple requests are made, for | |
88 // instance, when user presses volume(up|down) button for a long | |
89 // time. | |
90 if (ReplayRequestCouldBeScheduled()) | |
91 replay_scheduled_ = true; | |
54 return; | 92 return; |
55 } | 93 } |
56 stream_->SetVolume(kOutputVolumePercent); | 94 |
57 } else { | 95 last_play_start_time_ = base::TimeTicks::Now(); |
58 // TODO (ygorshenin@): implement smart stream rewind. | 96 replay_scheduled_ = false; |
59 stream_->Stop(); | 97 is_stream_stopped_ = false; |
60 } | 98 } |
61 | 99 |
62 cursor_ = 0; | |
63 if (g_audio_source_for_testing) | 100 if (g_audio_source_for_testing) |
64 stream_->Start(g_audio_source_for_testing); | 101 stream_->Start(g_audio_source_for_testing); |
65 else | 102 else |
66 stream_->Start(this); | 103 stream_->Start(this); |
67 | 104 |
68 if (g_observer_for_testing) | 105 if (g_observer_for_testing) |
69 g_observer_for_testing->OnPlay(); | 106 g_observer_for_testing->OnPlay(); |
70 } | 107 } |
71 | 108 |
72 void Stop() { | 109 void Stop() { |
73 DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread()); | 110 DCHECK(AudioManager::Get()->GetMessageLoop()->BelongsToCurrentThread()); |
74 if (!stream_) | 111 StopStream(); |
75 return; | |
76 stream_->Stop(); | |
77 stream_->Close(); | |
78 stream_ = NULL; | |
79 | |
80 if (g_observer_for_testing) | |
81 g_observer_for_testing->OnStop(cursor_); | |
82 } | 112 } |
83 | 113 |
84 private: | 114 private: |
115 friend class AudioStreamHandler; | |
116 | |
85 // AudioOutputStream::AudioSourceCallback overrides: | 117 // AudioOutputStream::AudioSourceCallback overrides: |
86 // Following methods could be called from *ANY* thread. | 118 // Following methods could be called from *ANY* thread. |
87 virtual int OnMoreData(AudioBus* dest, | 119 virtual int OnMoreData(AudioBus* dest, |
88 AudioBuffersState /* state */) OVERRIDE { | 120 AudioBuffersState /* state */) OVERRIDE { |
121 base::AutoLock sl(state_lock_); | |
122 | |
123 if (is_stream_stopped_) | |
124 return 0; | |
125 | |
89 size_t bytes_written = 0; | 126 size_t bytes_written = 0; |
90 if (wav_audio_.AtEnd(cursor_) || | 127 if (wav_audio_.AtEnd(cursor_) || |
91 !wav_audio_.CopyTo(dest, cursor_, &bytes_written)) { | 128 !wav_audio_.CopyTo(dest, cursor_, &bytes_written)) { |
92 AudioManager::Get()->GetMessageLoop()->PostTask( | 129 // If there are replay requests, |cursor_| is reset and will |
93 FROM_HERE, | 130 // continue to play from the start. Otherwise, stream will be |
94 base::Bind(&AudioStreamContainer::Stop, base::Unretained(this))); | 131 // stopped (not closed!) in |kStopStreamDelaySec| seconds, |
132 // unless new Play() requests arrive during this time period. | |
133 if (replay_scheduled_) { | |
134 cursor_ = 0; | |
135 last_play_start_time_ = base::TimeTicks::Now(); | |
136 replay_scheduled_ = false; | |
137 if (g_observer_for_testing) | |
138 g_observer_for_testing->OnReplay(); | |
139 } else { | |
140 is_stream_stopped_ = true; | |
141 stop_stream_closure_.Reset(base::Bind(&AudioStreamContainer::StopStream, | |
142 base::Unretained(this))); | |
143 AudioManager::Get()->GetMessageLoop()->PostDelayedTask( | |
144 FROM_HERE, | |
145 stop_stream_closure_.callback(), | |
146 base::TimeDelta::FromSeconds(kStopStreamDelaySec)); | |
147 } | |
95 return 0; | 148 return 0; |
96 } | 149 } |
97 cursor_ += bytes_written; | 150 cursor_ += bytes_written; |
98 | 151 |
99 return dest->frames(); | 152 return dest->frames(); |
100 } | 153 } |
101 | 154 |
102 virtual int OnMoreIOData(AudioBus* /* source */, | 155 virtual int OnMoreIOData(AudioBus* /* source */, |
103 AudioBus* dest, | 156 AudioBus* dest, |
104 AudioBuffersState state) OVERRIDE { | 157 AudioBuffersState state) OVERRIDE { |
105 return OnMoreData(dest, state); | 158 return OnMoreData(dest, state); |
106 } | 159 } |
107 | 160 |
108 virtual void OnError(AudioOutputStream* /* stream */) OVERRIDE { | 161 virtual void OnError(AudioOutputStream* /* stream */) OVERRIDE { |
109 LOG(ERROR) << "Error during system sound reproduction."; | 162 LOG(ERROR) << "Error during system sound reproduction."; |
110 } | 163 } |
111 | 164 |
165 void StopStreamWithoutLock() { | |
166 stop_stream_closure_.Cancel(); | |
167 replay_scheduled_ = false; | |
168 if (stream_) | |
169 stream_->Stop(); | |
170 if (g_observer_for_testing) | |
171 g_observer_for_testing->OnStop(cursor_); | |
172 } | |
173 | |
174 void StopStream() { | |
175 base::AutoLock sl(state_lock_); | |
176 StopStreamWithoutLock(); | |
177 } | |
178 | |
179 bool ReplayRequestCouldBeScheduled() { | |
180 if (schedule_replay_request_for_testing_) { | |
181 --schedule_replay_request_for_testing_; | |
182 return true; | |
183 } | |
184 const base::TimeDelta delta = | |
185 base::TimeTicks::Now() - last_play_start_time_; | |
186 return 2 * delta > wav_audio_.params().GetBufferDuration() && | |
187 !replay_scheduled_; | |
188 } | |
189 | |
190 void AllowReplayOnceForTesting() { | |
191 base::AutoLock st(state_lock_); | |
192 ++schedule_replay_request_for_testing_; | |
193 } | |
194 | |
112 AudioOutputStream* stream_; | 195 AudioOutputStream* stream_; |
113 | 196 |
114 const WavAudioHandler wav_audio_; | 197 WavAudioHandler wav_audio_; |
115 const AudioParameters params_; | |
116 | 198 |
117 size_t cursor_; | 199 size_t cursor_; |
118 | 200 |
201 // True if replay of the current sound is scheduled. | |
202 bool replay_scheduled_; | |
203 | |
204 base::CancelableClosure stop_stream_closure_; | |
205 bool is_stream_stopped_; | |
206 | |
207 // Last time sound started to play from the start. | |
208 base::TimeTicks last_play_start_time_; | |
209 | |
210 int schedule_replay_request_for_testing_; | |
211 | |
212 // State lock for all fields except stream_ and wav_audio_. | |
213 base::Lock state_lock_; | |
214 | |
119 DISALLOW_COPY_AND_ASSIGN(AudioStreamContainer); | 215 DISALLOW_COPY_AND_ASSIGN(AudioStreamContainer); |
120 }; | 216 }; |
121 | 217 |
122 AudioStreamHandler::AudioStreamHandler(const base::StringPiece& wav_data) | 218 AudioStreamHandler::AudioStreamHandler(const base::StringPiece& wav_data) |
123 : wav_audio_(wav_data), | 219 : wav_audio_(wav_data), |
124 initialized_(false) { | 220 initialized_(false) { |
125 AudioManager* manager = AudioManager::Get(); | 221 AudioManager* manager = AudioManager::Get(); |
126 if (!manager) { | 222 if (!manager) { |
127 LOG(ERROR) << "Can't get access to audio manager."; | 223 LOG(ERROR) << "Can't get access to audio manager."; |
128 return; | 224 return; |
129 } | 225 } |
130 AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY, | 226 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."; | 227 LOG(ERROR) << "Audio params are invalid."; |
137 return; | 228 return; |
138 } | 229 } |
139 stream_.reset(new AudioStreamContainer(wav_audio_, params)); | 230 stream_.reset(new AudioStreamContainer(wav_audio_)); |
140 initialized_ = true; | 231 initialized_ = true; |
141 } | 232 } |
142 | 233 |
143 AudioStreamHandler::~AudioStreamHandler() { | 234 AudioStreamHandler::~AudioStreamHandler() { |
144 DCHECK(CalledOnValidThread()); | 235 DCHECK(CalledOnValidThread()); |
145 AudioManager::Get()->GetMessageLoop()->PostTask( | 236 AudioManager::Get()->GetMessageLoop()->PostTask( |
146 FROM_HERE, | 237 FROM_HERE, |
147 base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get()))); | 238 base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get()))); |
148 AudioManager::Get()->GetMessageLoop()->DeleteSoon(FROM_HERE, | 239 AudioManager::Get()->GetMessageLoop()->DeleteSoon(FROM_HERE, |
149 stream_.release()); | 240 stream_.release()); |
(...skipping 28 matching lines...) Expand all Loading... | |
178 void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) { | 269 void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) { |
179 g_observer_for_testing = observer; | 270 g_observer_for_testing = observer; |
180 } | 271 } |
181 | 272 |
182 // static | 273 // static |
183 void AudioStreamHandler::SetAudioSourceForTesting( | 274 void AudioStreamHandler::SetAudioSourceForTesting( |
184 AudioOutputStream::AudioSourceCallback* source) { | 275 AudioOutputStream::AudioSourceCallback* source) { |
185 g_audio_source_for_testing = source; | 276 g_audio_source_for_testing = source; |
186 } | 277 } |
187 | 278 |
279 void AudioStreamHandler::AllowReplayOnceForTesting() { | |
280 AudioManager::Get()->GetMessageLoop()->PostTask( | |
281 FROM_HERE, | |
282 base::Bind(&AudioStreamContainer::AllowReplayOnceForTesting, | |
283 base::Unretained(stream_.get()))); | |
284 } | |
285 | |
188 } // namespace media | 286 } // namespace media |
OLD | NEW |