Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 "content/renderer/media/audio_renderer_impl.h" | 5 #include "content/renderer/media/audio_renderer_impl.h" |
| 6 | 6 |
| 7 #include <math.h> | 7 #include <math.h> |
| 8 | 8 |
| 9 #include <algorithm> | 9 #include <algorithm> |
| 10 | 10 |
| 11 #include "base/bind.h" | 11 #include "base/bind.h" |
| 12 #include "content/common/child_process.h" | 12 #include "content/common/child_process.h" |
| 13 #include "content/common/media/audio_messages.h" | 13 #include "content/common/media/audio_messages.h" |
| 14 #include "content/renderer/render_thread_impl.h" | 14 #include "content/renderer/render_thread_impl.h" |
| 15 #include "media/audio/audio_buffers_state.h" | 15 #include "media/audio/audio_buffers_state.h" |
| 16 #include "media/audio/audio_util.h" | 16 #include "media/audio/audio_util.h" |
| 17 #include "media/base/filter_host.h" | 17 #include "media/base/filter_host.h" |
| 18 | 18 |
| 19 using base::subtle::AtomicWord; | |
| 20 | |
| 19 // We define GetBufferSizeForSampleRate() instead of using | 21 // We define GetBufferSizeForSampleRate() instead of using |
| 20 // GetAudioHardwareBufferSize() in audio_util because we're using | 22 // GetAudioHardwareBufferSize() in audio_util because we're using |
| 21 // the AUDIO_PCM_LINEAR flag, instead of AUDIO_PCM_LOW_LATENCY, | 23 // the AUDIO_PCM_LINEAR flag, instead of AUDIO_PCM_LOW_LATENCY, |
| 22 // which the audio_util functions assume. | 24 // which the audio_util functions assume. |
| 23 // | 25 // |
| 24 // See: http://code.google.com/p/chromium/issues/detail?id=103627 | 26 // See: http://code.google.com/p/chromium/issues/detail?id=103627 |
| 25 // for a more detailed description of the subtleties. | 27 // for a more detailed description of the subtleties. |
| 26 static size_t GetBufferSizeForSampleRate(int sample_rate) { | 28 static size_t GetBufferSizeForSampleRate(int sample_rate) { |
| 27 // kNominalBufferSize has been tested on Windows, Mac OS X, and Linux | 29 // kNominalBufferSize has been tested on Windows, Mac OS X, and Linux |
| 28 // using the low-latency audio codepath (SyncSocket implementation) | 30 // using the low-latency audio codepath (SyncSocket implementation) |
| 29 // with the AUDIO_PCM_LINEAR flag. | 31 // with the AUDIO_PCM_LINEAR flag. |
| 30 const size_t kNominalBufferSize = 2048; | 32 const size_t kNominalBufferSize = 2048; |
| 31 | 33 |
| 32 if (sample_rate <= 48000) | 34 if (sample_rate <= 48000) |
| 33 return kNominalBufferSize; | 35 return kNominalBufferSize; |
| 34 else if (sample_rate <= 96000) | 36 else if (sample_rate <= 96000) |
| 35 return kNominalBufferSize * 2; | 37 return kNominalBufferSize * 2; |
| 36 return kNominalBufferSize * 4; | 38 return kNominalBufferSize * 4; |
| 37 } | 39 } |
| 38 | 40 |
| 39 AudioRendererImpl::AudioRendererImpl(media::AudioRendererSink* sink) | 41 AudioRendererImpl::AudioRendererImpl(media::AudioRendererSink* sink) |
| 40 : AudioRendererBase(), | 42 : AudioRendererBase(), |
| 41 bytes_per_second_(0), | 43 bytes_per_second_(0), |
| 42 stopped_(false), | 44 stopped_(false), |
| 43 sink_(sink), | 45 sink_(sink), |
| 44 is_initialized_(false) { | 46 is_initialized_(false), |
| 47 ended_event_scheduled_(false), | |
| 48 io_message_loop_proxy_(ChildProcess::current()->io_message_loop_proxy()), | |
| 49 stream_id_(0) { | |
| 45 } | 50 } |
| 46 | 51 |
| 47 AudioRendererImpl::~AudioRendererImpl() { | 52 AudioRendererImpl::~AudioRendererImpl() { |
| 48 } | 53 } |
| 49 | 54 |
| 50 base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) { | 55 base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) { |
| 51 if (bytes_per_second_) { | 56 if (bytes_per_second_) { |
| 52 return base::TimeDelta::FromMicroseconds( | 57 return base::TimeDelta::FromMicroseconds( |
| 53 base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second_); | 58 base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second_); |
| 54 } | 59 } |
| (...skipping 112 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 167 | 172 |
| 168 void AudioRendererImpl::SetVolume(float volume) { | 173 void AudioRendererImpl::SetVolume(float volume) { |
| 169 if (stopped_) | 174 if (stopped_) |
| 170 return; | 175 return; |
| 171 DCHECK(sink_.get()); | 176 DCHECK(sink_.get()); |
| 172 sink_->SetVolume(volume); | 177 sink_->SetVolume(volume); |
| 173 } | 178 } |
| 174 | 179 |
| 175 void AudioRendererImpl::DoPlay() { | 180 void AudioRendererImpl::DoPlay() { |
| 176 earliest_end_time_ = base::Time::Now(); | 181 earliest_end_time_ = base::Time::Now(); |
| 182 ended_event_scheduled_ = false; | |
| 177 DCHECK(sink_.get()); | 183 DCHECK(sink_.get()); |
| 178 sink_->Play(); | 184 sink_->Play(); |
| 179 } | 185 } |
| 180 | 186 |
| 181 void AudioRendererImpl::DoPause() { | 187 void AudioRendererImpl::DoPause() { |
| 188 ++stream_id_; | |
| 182 DCHECK(sink_.get()); | 189 DCHECK(sink_.get()); |
| 183 sink_->Pause(false); | 190 sink_->Pause(false); |
| 184 } | 191 } |
| 185 | 192 |
| 186 void AudioRendererImpl::DoSeek() { | 193 void AudioRendererImpl::DoSeek() { |
| 187 earliest_end_time_ = base::Time::Now(); | 194 earliest_end_time_ = base::Time::Now(); |
| 195 ended_event_scheduled_ = false; | |
| 196 ++stream_id_; | |
| 188 | 197 |
| 189 // Pause and flush the stream when we seek to a new location. | 198 // Pause and flush the stream when we seek to a new location. |
| 190 DCHECK(sink_.get()); | 199 DCHECK(sink_.get()); |
| 191 sink_->Pause(true); | 200 sink_->Pause(true); |
| 192 } | 201 } |
| 193 | 202 |
| 194 size_t AudioRendererImpl::Render(const std::vector<float*>& audio_data, | 203 size_t AudioRendererImpl::Render(const std::vector<float*>& audio_data, |
| 195 size_t number_of_frames, | 204 size_t number_of_frames, |
| 196 size_t audio_delay_milliseconds) { | 205 size_t audio_delay_milliseconds) { |
| 197 if (stopped_ || GetPlaybackRate() == 0.0f) { | 206 if (stopped_ || GetPlaybackRate() == 0.0f) { |
| 198 // Output silence if stopped. | 207 // Output silence if stopped. |
| 199 for (size_t i = 0; i < audio_data.size(); ++i) | 208 for (size_t i = 0; i < audio_data.size(); ++i) |
| 200 memset(audio_data[i], 0, sizeof(float) * number_of_frames); | 209 memset(audio_data[i], 0, sizeof(audio_data[i][0]) * number_of_frames); |
| 201 return 0; | 210 return 0; |
| 202 } | 211 } |
| 203 | 212 |
| 204 // Adjust the playback delay. | 213 // Adjust the playback delay. |
| 205 base::TimeDelta request_delay = | 214 base::TimeDelta request_delay = |
| 206 base::TimeDelta::FromMilliseconds(audio_delay_milliseconds); | 215 base::TimeDelta::FromMilliseconds(audio_delay_milliseconds); |
| 207 | 216 |
| 208 // Finally we need to adjust the delay according to playback rate. | 217 // Finally we need to adjust the delay according to playback rate. |
| 209 if (GetPlaybackRate() != 1.0f) { | 218 if (GetPlaybackRate() != 1.0f) { |
| 210 request_delay = base::TimeDelta::FromMicroseconds( | 219 request_delay = base::TimeDelta::FromMicroseconds( |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 232 channels, | 241 channels, |
| 233 channel_index, | 242 channel_index, |
| 234 bytes_per_frame / channels, | 243 bytes_per_frame / channels, |
| 235 filled_frames); | 244 filled_frames); |
| 236 | 245 |
| 237 // If FillBuffer() didn't give us enough data then zero out the remainder. | 246 // If FillBuffer() didn't give us enough data then zero out the remainder. |
| 238 if (filled_frames < number_of_frames) { | 247 if (filled_frames < number_of_frames) { |
| 239 int frames_to_zero = number_of_frames - filled_frames; | 248 int frames_to_zero = number_of_frames - filled_frames; |
| 240 memset(audio_data[channel_index] + filled_frames, | 249 memset(audio_data[channel_index] + filled_frames, |
| 241 0, | 250 0, |
| 242 sizeof(float) * frames_to_zero); | 251 sizeof(audio_data[channel_index][0]) * frames_to_zero); |
| 243 } | 252 } |
| 244 } | 253 } |
| 245 return filled_frames; | 254 return filled_frames; |
| 246 } | 255 } |
| 247 | 256 |
| 248 void AudioRendererImpl::OnError() { | 257 void AudioRendererImpl::OnError() { |
| 249 host()->DisableAudioRenderer(); | 258 host()->DisableAudioRenderer(); |
| 250 } | 259 } |
| 251 | 260 |
| 261 int64 AudioRendererImpl::OnRenderEndOfStreamDelay() { | |
| 262 return (earliest_end_time_ - base::Time::Now()).InMilliseconds(); | |
| 263 } | |
| 264 | |
| 252 void AudioRendererImpl::OnRenderEndOfStream() { | 265 void AudioRendererImpl::OnRenderEndOfStream() { |
| 253 // TODO(enal): schedule callback instead of polling. | 266 if (!ended_event_scheduled_) { |
| 254 if (base::Time::Now() >= earliest_end_time_) | 267 ended_event_scheduled_ = true; |
| 268 int64 delay_ms = OnRenderEndOfStreamDelay(); | |
| 269 if (delay_ms <= 0) { | |
| 270 DoSignalEndOfStream(stream_id_); | |
| 271 } else { | |
| 272 io_message_loop_proxy_->PostDelayedTask( | |
| 273 FROM_HERE, | |
| 274 base::Bind(&AudioRendererImpl::DoSignalEndOfStream, this, stream_id_), | |
| 275 delay_ms); | |
|
scherkus (not reviewing)
2012/02/08 02:10:40
PostDelayedTask() now supports base::TimeDelta and
enal1
2012/02/08 02:43:22
Not MessageLoopProxy::PostDelayedTask(). It is for
scherkus (not reviewing)
2012/02/09 04:52:50
Looks like MLProxy slipped through the cracks!
htt
| |
| 276 } | |
| 277 } | |
| 278 } | |
| 279 | |
| 280 void AudioRendererImpl::DoSignalEndOfStream(AtomicWord stream_id) { | |
| 281 // There is no way to cancel delayed task if we paused or seeked after task | |
| 282 // was scheduled, use stream id to catch such case. | |
| 283 if (stream_id == stream_id_) | |
| 255 SignalEndOfStream(); | 284 SignalEndOfStream(); |
| 256 } | 285 } |
| OLD | NEW |