Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(263)

Side by Side Diff: content/renderer/media/audio_renderer_impl.cc

Issue 7253003: Change audio renderer to communicate with host using low latency codepath. (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 9 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 "base/command_line.h"
10 #include "content/common/content_switches.h"
9 #include "content/common/media/audio_messages.h" 11 #include "content/common/media/audio_messages.h"
10 #include "content/renderer/render_thread.h" 12 #include "content/renderer/render_thread.h"
11 #include "content/renderer/render_view.h" 13 #include "content/renderer/render_view.h"
14 #include "media/audio/audio_output_controller.h"
12 #include "media/base/filter_host.h" 15 #include "media/base/filter_host.h"
13 16
14 namespace { 17 // Static variable that says what code path we are using -- low or high
15 18 // latency. Made separate variable so we don't have to go to command line
16 // We will try to fill 200 ms worth of audio samples in each packet. A round 19 // for every DCHECK().
17 // trip latency for IPC messages are typically 10 ms, this should give us 20 AudioRendererImpl::LatencyType AudioRendererImpl::latency_type_ =
18 // plenty of time to avoid clicks. 21 AudioRendererImpl::kUninitializedLatency;
19 const int kMillisecondsPerPacket = 200;
20
21 // We have at most 3 packets in browser, i.e. 600 ms. This is a reasonable
22 // amount to avoid clicks.
23 const int kPacketsInBuffer = 3;
24
25 } // namespace
26 22
27 AudioRendererImpl::AudioRendererImpl(AudioMessageFilter* filter) 23 AudioRendererImpl::AudioRendererImpl(AudioMessageFilter* filter)
28 : AudioRendererBase(), 24 : AudioRendererBase(),
29 bytes_per_second_(0), 25 bytes_per_second_(0),
30 filter_(filter), 26 filter_(filter),
31 stream_id_(0), 27 stream_id_(0),
32 shared_memory_(NULL), 28 shared_memory_(NULL),
33 shared_memory_size_(0), 29 shared_memory_size_(0),
34 io_loop_(filter->message_loop()), 30 io_loop_(filter->message_loop()),
35 stopped_(false), 31 stopped_(false),
36 pending_request_(false), 32 pending_request_(false),
37 prerolling_(false), 33 prerolling_(false),
38 preroll_bytes_(0) { 34 preroll_bytes_(0) {
39 DCHECK(io_loop_); 35 DCHECK(io_loop_);
36 // Figure out if we are planning to use high or low latency code path.
37 // We are initializing only one variable and double initialization is Ok,
38 // so there would not be any issues caused by CPU memory model.
39 if (latency_type_ == kUninitializedLatency) {
40 if (CommandLine::ForCurrentProcess()->HasSwitch(
41 switches::kLowLatencyAudio)) {
42 latency_type_ = kLowLatency;
43 } else {
44 latency_type_ = kHighLatency;
45 }
46 }
40 } 47 }
41 48
42 AudioRendererImpl::~AudioRendererImpl() { 49 AudioRendererImpl::~AudioRendererImpl() {
43 } 50 }
44 51
52 // static
53 void AudioRendererImpl::set_latency_type(LatencyType latency_type) {
54 DCHECK_EQ(kUninitializedLatency, latency_type_);
55 latency_type_ = latency_type;
56 }
57
45 base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) { 58 base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) {
46 if (bytes_per_second_) { 59 if (bytes_per_second_) {
47 return base::TimeDelta::FromMicroseconds( 60 return base::TimeDelta::FromMicroseconds(
48 base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second_); 61 base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second_);
49 } 62 }
50 return base::TimeDelta(); 63 return base::TimeDelta();
51 } 64 }
52 65
53 bool AudioRendererImpl::OnInitialize(const media::AudioDecoderConfig& config) { 66 bool AudioRendererImpl::OnInitialize(const media::AudioDecoderConfig& config) {
54 AudioParameters params(config); 67 AudioParameters params(config);
55 params.format = AudioParameters::AUDIO_PCM_LINEAR; 68 params.format = AudioParameters::AUDIO_PCM_LINEAR;
56 69
57 bytes_per_second_ = params.GetBytesPerSecond(); 70 bytes_per_second_ = params.GetBytesPerSecond();
58 71
59 io_loop_->PostTask(FROM_HERE, 72 io_loop_->PostTask(FROM_HERE,
60 NewRunnableMethod(this, &AudioRendererImpl::CreateStreamTask, params)); 73 NewRunnableMethod(this, &AudioRendererImpl::CreateStreamTask, params));
61 return true; 74 return true;
62 } 75 }
63 76
64 void AudioRendererImpl::OnStop() { 77 void AudioRendererImpl::OnStop() {
65 base::AutoLock auto_lock(lock_); 78 base::AutoLock auto_lock(lock_);
66 if (stopped_) 79 if (stopped_)
67 return; 80 return;
68 stopped_ = true; 81 stopped_ = true;
69 82
70 // We should never touch |io_loop_| after being stopped, so post our final 83 // We should never touch |io_loop_| after being stopped, so post our final
71 // task to clean up. 84 // task to clean up.
72 io_loop_->PostTask(FROM_HERE, 85 io_loop_->PostTask(FROM_HERE,
73 NewRunnableMethod(this, &AudioRendererImpl::DestroyTask)); 86 NewRunnableMethod(this, &AudioRendererImpl::DestroyTask));
87
88 if (audio_thread_.get()) {
89 socket_->Close();
90 audio_thread_->Join();
91 }
92 }
93
94 void AudioRendererImpl::NotifyDataAvailableIfNecessary() {
95 if (latency_type_ == kHighLatency) {
96 // Post a task to render thread to notify a packet reception.
97 io_loop_->PostTask(FROM_HERE,
98 NewRunnableMethod(this, &AudioRendererImpl::NotifyPacketReadyTask));
99 }
74 } 100 }
75 101
76 void AudioRendererImpl::ConsumeAudioSamples( 102 void AudioRendererImpl::ConsumeAudioSamples(
77 scoped_refptr<media::Buffer> buffer_in) { 103 scoped_refptr<media::Buffer> buffer_in) {
78 base::AutoLock auto_lock(lock_); 104 base::AutoLock auto_lock(lock_);
79 if (stopped_) 105 if (stopped_)
80 return; 106 return;
81 107
82 // TODO(hclam): handle end of stream here. 108 // TODO(hclam): handle end of stream here.
83 109
84 // Use the base class to queue the buffer. 110 // Use the base class to queue the buffer.
85 AudioRendererBase::ConsumeAudioSamples(buffer_in); 111 AudioRendererBase::ConsumeAudioSamples(buffer_in);
86 112
87 // Post a task to render thread to notify a packet reception. 113 NotifyDataAvailableIfNecessary();
88 io_loop_->PostTask(FROM_HERE,
89 NewRunnableMethod(this, &AudioRendererImpl::NotifyPacketReadyTask));
90 } 114 }
91 115
92 void AudioRendererImpl::SetPlaybackRate(float rate) { 116 void AudioRendererImpl::SetPlaybackRate(float rate) {
93 DCHECK(rate >= 0.0f); 117 DCHECK_LE(0.0f, rate);
94 118
95 base::AutoLock auto_lock(lock_); 119 base::AutoLock auto_lock(lock_);
96 // Handle the case where we stopped due to |io_loop_| dying. 120 // Handle the case where we stopped due to |io_loop_| dying.
97 if (stopped_) { 121 if (stopped_) {
98 AudioRendererBase::SetPlaybackRate(rate); 122 AudioRendererBase::SetPlaybackRate(rate);
99 return; 123 return;
100 } 124 }
101 125
102 // We have two cases here: 126 // We have two cases here:
103 // Play: GetPlaybackRate() == 0.0 && rate != 0.0 127 // Play: GetPlaybackRate() == 0.0 && rate != 0.0
104 // Pause: GetPlaybackRate() != 0.0 && rate == 0.0 128 // Pause: GetPlaybackRate() != 0.0 && rate == 0.0
105 if (GetPlaybackRate() == 0.0f && rate != 0.0f) { 129 if (GetPlaybackRate() == 0.0f && rate != 0.0f) {
106 io_loop_->PostTask(FROM_HERE, 130 io_loop_->PostTask(FROM_HERE,
107 NewRunnableMethod(this, &AudioRendererImpl::PlayTask)); 131 NewRunnableMethod(this, &AudioRendererImpl::PlayTask));
108 } else if (GetPlaybackRate() != 0.0f && rate == 0.0f) { 132 } else if (GetPlaybackRate() != 0.0f && rate == 0.0f) {
109 // Pause is easy, we can always pause. 133 // Pause is easy, we can always pause.
110 io_loop_->PostTask(FROM_HERE, 134 io_loop_->PostTask(FROM_HERE,
111 NewRunnableMethod(this, &AudioRendererImpl::PauseTask)); 135 NewRunnableMethod(this, &AudioRendererImpl::PauseTask));
112 } 136 }
113 AudioRendererBase::SetPlaybackRate(rate); 137 AudioRendererBase::SetPlaybackRate(rate);
114 138
115 // If we are playing, give a kick to try fulfilling the packet request as 139 // If we are playing, give a kick to try fulfilling the packet request as
116 // the previous packet request may be stalled by a pause. 140 // the previous packet request may be stalled by a pause.
117 if (rate > 0.0f) { 141 if (rate > 0.0f) {
118 io_loop_->PostTask( 142 NotifyDataAvailableIfNecessary();
119 FROM_HERE,
120 NewRunnableMethod(this, &AudioRendererImpl::NotifyPacketReadyTask));
121 } 143 }
122 } 144 }
123 145
124 void AudioRendererImpl::Pause(media::FilterCallback* callback) { 146 void AudioRendererImpl::Pause(media::FilterCallback* callback) {
125 AudioRendererBase::Pause(callback); 147 AudioRendererBase::Pause(callback);
126 base::AutoLock auto_lock(lock_); 148 base::AutoLock auto_lock(lock_);
127 if (stopped_) 149 if (stopped_)
128 return; 150 return;
129 151
130 io_loop_->PostTask(FROM_HERE, 152 io_loop_->PostTask(FROM_HERE,
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
163 if (stopped_) 185 if (stopped_)
164 return; 186 return;
165 io_loop_->PostTask(FROM_HERE, 187 io_loop_->PostTask(FROM_HERE,
166 NewRunnableMethod( 188 NewRunnableMethod(
167 this, &AudioRendererImpl::SetVolumeTask, volume)); 189 this, &AudioRendererImpl::SetVolumeTask, volume));
168 } 190 }
169 191
170 void AudioRendererImpl::OnCreated(base::SharedMemoryHandle handle, 192 void AudioRendererImpl::OnCreated(base::SharedMemoryHandle handle,
171 uint32 length) { 193 uint32 length) {
172 DCHECK(MessageLoop::current() == io_loop_); 194 DCHECK(MessageLoop::current() == io_loop_);
195 DCHECK_EQ(kHighLatency, latency_type_);
173 196
174 base::AutoLock auto_lock(lock_); 197 base::AutoLock auto_lock(lock_);
175 if (stopped_) 198 if (stopped_)
176 return; 199 return;
177 200
178 shared_memory_.reset(new base::SharedMemory(handle, false)); 201 shared_memory_.reset(new base::SharedMemory(handle, false));
179 shared_memory_->Map(length); 202 shared_memory_->Map(length);
180 shared_memory_size_ = length; 203 shared_memory_size_ = length;
181 } 204 }
182 205
183 void AudioRendererImpl::OnLowLatencyCreated(base::SharedMemoryHandle, 206 void AudioRendererImpl::CreateSocket(base::SyncSocket::Handle socket_handle) {
184 base::SyncSocket::Handle, uint32) { 207 DCHECK_EQ(kLowLatency, latency_type_);
185 // AudioRenderer should not have a low-latency audio channel. 208 #if defined(OS_WIN)
186 NOTREACHED(); 209 DCHECK(socket_handle);
210 #else
211 DCHECK_GE(socket_handle, 0);
212 #endif
213 socket_.reset(new base::SyncSocket(socket_handle));
214 }
215
216 void AudioRendererImpl::CreateAudioThread() {
217 DCHECK_EQ(kLowLatency, latency_type_);
218 audio_thread_.reset(
219 new base::DelegateSimpleThread(this, "renderer_audio_thread"));
220 audio_thread_->Start();
221 }
222
223 void AudioRendererImpl::OnLowLatencyCreated(
224 base::SharedMemoryHandle handle,
225 base::SyncSocket::Handle socket_handle,
226 uint32 length) {
227 DCHECK(MessageLoop::current() == io_loop_);
228 DCHECK_EQ(kLowLatency, latency_type_);
229 #if defined(OS_WIN)
230 DCHECK(handle);
231 #else
232 DCHECK_GE(handle.fd, 0);
233 #endif
234 DCHECK_NE(0u, length);
235
236 base::AutoLock auto_lock(lock_);
237 if (stopped_)
238 return;
239
240 shared_memory_.reset(new base::SharedMemory(handle, false));
241 shared_memory_->Map(length);
242 shared_memory_size_ = length;
243
244 CreateSocket(socket_handle);
245 CreateAudioThread();
187 } 246 }
188 247
189 void AudioRendererImpl::OnRequestPacket(AudioBuffersState buffers_state) { 248 void AudioRendererImpl::OnRequestPacket(AudioBuffersState buffers_state) {
190 DCHECK(MessageLoop::current() == io_loop_); 249 DCHECK(MessageLoop::current() == io_loop_);
191 250 DCHECK_EQ(kHighLatency, latency_type_);
192 { 251 {
193 base::AutoLock auto_lock(lock_); 252 base::AutoLock auto_lock(lock_);
194 DCHECK(!pending_request_); 253 DCHECK(!pending_request_);
195 pending_request_ = true; 254 pending_request_ = true;
196 request_buffers_state_ = buffers_state; 255 request_buffers_state_ = buffers_state;
197 } 256 }
198 257
199 // Try to fill in the fulfill the packet request. 258 // Try to fill in the fulfill the packet request.
200 NotifyPacketReadyTask(); 259 NotifyPacketReadyTask();
201 } 260 }
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 299
241 // Make sure we don't call create more than once. 300 // Make sure we don't call create more than once.
242 DCHECK_EQ(0, stream_id_); 301 DCHECK_EQ(0, stream_id_);
243 stream_id_ = filter_->AddDelegate(this); 302 stream_id_ = filter_->AddDelegate(this);
244 io_loop_->AddDestructionObserver(this); 303 io_loop_->AddDestructionObserver(this);
245 304
246 AudioParameters params_to_send(audio_params); 305 AudioParameters params_to_send(audio_params);
247 // Let the browser choose packet size. 306 // Let the browser choose packet size.
248 params_to_send.samples_per_packet = 0; 307 params_to_send.samples_per_packet = 0;
249 308
250 filter_->Send(new AudioHostMsg_CreateStream( 309 filter_->Send(new AudioHostMsg_CreateStream(0,
251 0, stream_id_, params_to_send, false)); 310 stream_id_,
311 params_to_send,
312 latency_type_ == kLowLatency));
252 } 313 }
253 314
254 void AudioRendererImpl::PlayTask() { 315 void AudioRendererImpl::PlayTask() {
255 DCHECK(MessageLoop::current() == io_loop_); 316 DCHECK(MessageLoop::current() == io_loop_);
256 317
257 filter_->Send(new AudioHostMsg_PlayStream(0, stream_id_)); 318 filter_->Send(new AudioHostMsg_PlayStream(0, stream_id_));
258 } 319 }
259 320
260 void AudioRendererImpl::PauseTask() { 321 void AudioRendererImpl::PauseTask() {
261 DCHECK(MessageLoop::current() == io_loop_); 322 DCHECK(MessageLoop::current() == io_loop_);
(...skipping 24 matching lines...) Expand all
286 DCHECK(MessageLoop::current() == io_loop_); 347 DCHECK(MessageLoop::current() == io_loop_);
287 348
288 base::AutoLock auto_lock(lock_); 349 base::AutoLock auto_lock(lock_);
289 if (stopped_) 350 if (stopped_)
290 return; 351 return;
291 filter_->Send(new AudioHostMsg_SetVolume(0, stream_id_, volume)); 352 filter_->Send(new AudioHostMsg_SetVolume(0, stream_id_, volume));
292 } 353 }
293 354
294 void AudioRendererImpl::NotifyPacketReadyTask() { 355 void AudioRendererImpl::NotifyPacketReadyTask() {
295 DCHECK(MessageLoop::current() == io_loop_); 356 DCHECK(MessageLoop::current() == io_loop_);
357 DCHECK_EQ(kHighLatency, latency_type_);
296 358
297 base::AutoLock auto_lock(lock_); 359 base::AutoLock auto_lock(lock_);
298 if (stopped_) 360 if (stopped_)
299 return; 361 return;
300 if (pending_request_ && GetPlaybackRate() > 0.0f) { 362 if (pending_request_ && GetPlaybackRate() > 0.0f) {
301 DCHECK(shared_memory_.get()); 363 DCHECK(shared_memory_.get());
302 364
303 // Adjust the playback delay. 365 // Adjust the playback delay.
304 base::Time current_time = base::Time::Now(); 366 base::Time current_time = base::Time::Now();
305 367
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
339 DCHECK(MessageLoop::current() == io_loop_); 401 DCHECK(MessageLoop::current() == io_loop_);
340 402
341 // We treat the IO loop going away the same as stopping. 403 // We treat the IO loop going away the same as stopping.
342 base::AutoLock auto_lock(lock_); 404 base::AutoLock auto_lock(lock_);
343 if (stopped_) 405 if (stopped_)
344 return; 406 return;
345 407
346 stopped_ = true; 408 stopped_ = true;
347 DestroyTask(); 409 DestroyTask();
348 } 410 }
411
412 // Our audio thread runs here. We receive requests for more data and send it
413 // on this thread.
414 void AudioRendererImpl::Run() {
415 audio_thread_->SetThreadPriority(base::kThreadPriority_RealtimeAudio);
416
417 int bytes;
418 while (sizeof(bytes) == socket_->Receive(&bytes, sizeof(bytes))) {
419 LOG(ERROR) << "+++ bytes: " << bytes;
420 if (bytes == media::AudioOutputController::kPauseMark)
421 continue;
422 else if (bytes < 0)
423 break;
424 base::AutoLock auto_lock(lock_);
425 if (stopped_)
426 break;
427 float playback_rate = GetPlaybackRate();
428 if (playback_rate <= 0.0f)
429 continue;
430 DCHECK(shared_memory_.get());
431 base::TimeDelta request_delay = ConvertToDuration(bytes);
432 // We need to adjust the delay according to playback rate.
433 if (playback_rate != 1.0f) {
434 request_delay = base::TimeDelta::FromMicroseconds(
435 static_cast<int64>(ceil(request_delay.InMicroseconds() *
436 playback_rate)));
437 }
438 FillBuffer(static_cast<uint8*>(shared_memory_->memory()),
439 shared_memory_size_,
440 request_delay,
441 true /* buffers empty */);
442 }
443 }
OLDNEW
« no previous file with comments | « content/renderer/media/audio_renderer_impl.h ('k') | content/renderer/media/audio_renderer_impl_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698