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