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

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, 6 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(
scherkus (not reviewing) 2011/06/24 21:09:26 instead of checking the command line here, do the
enal1 2011/06/24 22:01:09 (1) I thought about moving check up the call tree,
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
45 base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) { 52 base::TimeDelta AudioRendererImpl::ConvertToDuration(int bytes) {
46 if (bytes_per_second_) { 53 if (bytes_per_second_) {
47 return base::TimeDelta::FromMicroseconds( 54 return base::TimeDelta::FromMicroseconds(
48 base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second_); 55 base::Time::kMicrosecondsPerSecond * bytes / bytes_per_second_);
49 } 56 }
(...skipping 14 matching lines...) Expand all
64 void AudioRendererImpl::OnStop() { 71 void AudioRendererImpl::OnStop() {
65 base::AutoLock auto_lock(lock_); 72 base::AutoLock auto_lock(lock_);
66 if (stopped_) 73 if (stopped_)
67 return; 74 return;
68 stopped_ = true; 75 stopped_ = true;
69 76
70 // We should never touch |io_loop_| after being stopped, so post our final 77 // We should never touch |io_loop_| after being stopped, so post our final
71 // task to clean up. 78 // task to clean up.
72 io_loop_->PostTask(FROM_HERE, 79 io_loop_->PostTask(FROM_HERE,
73 NewRunnableMethod(this, &AudioRendererImpl::DestroyTask)); 80 NewRunnableMethod(this, &AudioRendererImpl::DestroyTask));
81
82 if (audio_thread_.get()) {
83 socket_->Close();
84 audio_thread_->Join();
85 }
86 }
87
88 void AudioRendererImpl::NotifyDataAvailableIfNecessary() {
89 if (latency_type_ == kHighLatency) {
90 // Post a task to render thread to notify a packet reception.
91 io_loop_->PostTask(FROM_HERE,
92 NewRunnableMethod(this, &AudioRendererImpl::NotifyPacketReadyTask));
93 }
74 } 94 }
75 95
76 void AudioRendererImpl::ConsumeAudioSamples( 96 void AudioRendererImpl::ConsumeAudioSamples(
77 scoped_refptr<media::Buffer> buffer_in) { 97 scoped_refptr<media::Buffer> buffer_in) {
78 base::AutoLock auto_lock(lock_); 98 base::AutoLock auto_lock(lock_);
79 if (stopped_) 99 if (stopped_)
80 return; 100 return;
81 101
82 // TODO(hclam): handle end of stream here. 102 // TODO(hclam): handle end of stream here.
83 103
84 // Use the base class to queue the buffer. 104 // Use the base class to queue the buffer.
85 AudioRendererBase::ConsumeAudioSamples(buffer_in); 105 AudioRendererBase::ConsumeAudioSamples(buffer_in);
86 106
87 // Post a task to render thread to notify a packet reception. 107 NotifyDataAvailableIfNecessary();
88 io_loop_->PostTask(FROM_HERE,
89 NewRunnableMethod(this, &AudioRendererImpl::NotifyPacketReadyTask));
90 } 108 }
91 109
92 void AudioRendererImpl::SetPlaybackRate(float rate) { 110 void AudioRendererImpl::SetPlaybackRate(float rate) {
93 DCHECK(rate >= 0.0f); 111 DCHECK(rate >= 0.0f);
94 112
95 base::AutoLock auto_lock(lock_); 113 base::AutoLock auto_lock(lock_);
96 // Handle the case where we stopped due to |io_loop_| dying. 114 // Handle the case where we stopped due to |io_loop_| dying.
97 if (stopped_) { 115 if (stopped_) {
98 AudioRendererBase::SetPlaybackRate(rate); 116 AudioRendererBase::SetPlaybackRate(rate);
99 return; 117 return;
100 } 118 }
101 119
102 // We have two cases here: 120 // We have two cases here:
103 // Play: GetPlaybackRate() == 0.0 && rate != 0.0 121 // Play: GetPlaybackRate() == 0.0 && rate != 0.0
104 // Pause: GetPlaybackRate() != 0.0 && rate == 0.0 122 // Pause: GetPlaybackRate() != 0.0 && rate == 0.0
105 if (GetPlaybackRate() == 0.0f && rate != 0.0f) { 123 if (GetPlaybackRate() == 0.0f && rate != 0.0f) {
106 io_loop_->PostTask(FROM_HERE, 124 io_loop_->PostTask(FROM_HERE,
107 NewRunnableMethod(this, &AudioRendererImpl::PlayTask)); 125 NewRunnableMethod(this, &AudioRendererImpl::PlayTask));
108 } else if (GetPlaybackRate() != 0.0f && rate == 0.0f) { 126 } else if (GetPlaybackRate() != 0.0f && rate == 0.0f) {
109 // Pause is easy, we can always pause. 127 // Pause is easy, we can always pause.
110 io_loop_->PostTask(FROM_HERE, 128 io_loop_->PostTask(FROM_HERE,
111 NewRunnableMethod(this, &AudioRendererImpl::PauseTask)); 129 NewRunnableMethod(this, &AudioRendererImpl::PauseTask));
112 } 130 }
113 AudioRendererBase::SetPlaybackRate(rate); 131 AudioRendererBase::SetPlaybackRate(rate);
114 132
115 // If we are playing, give a kick to try fulfilling the packet request as 133 // 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. 134 // the previous packet request may be stalled by a pause.
117 if (rate > 0.0f) { 135 if (rate > 0.0f) {
118 io_loop_->PostTask( 136 NotifyDataAvailableIfNecessary();
119 FROM_HERE,
120 NewRunnableMethod(this, &AudioRendererImpl::NotifyPacketReadyTask));
121 } 137 }
122 } 138 }
123 139
124 void AudioRendererImpl::Pause(media::FilterCallback* callback) { 140 void AudioRendererImpl::Pause(media::FilterCallback* callback) {
125 AudioRendererBase::Pause(callback); 141 AudioRendererBase::Pause(callback);
126 base::AutoLock auto_lock(lock_); 142 base::AutoLock auto_lock(lock_);
127 if (stopped_) 143 if (stopped_)
128 return; 144 return;
129 145
130 io_loop_->PostTask(FROM_HERE, 146 io_loop_->PostTask(FROM_HERE,
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
163 if (stopped_) 179 if (stopped_)
164 return; 180 return;
165 io_loop_->PostTask(FROM_HERE, 181 io_loop_->PostTask(FROM_HERE,
166 NewRunnableMethod( 182 NewRunnableMethod(
167 this, &AudioRendererImpl::SetVolumeTask, volume)); 183 this, &AudioRendererImpl::SetVolumeTask, volume));
168 } 184 }
169 185
170 void AudioRendererImpl::OnCreated(base::SharedMemoryHandle handle, 186 void AudioRendererImpl::OnCreated(base::SharedMemoryHandle handle,
171 uint32 length) { 187 uint32 length) {
172 DCHECK(MessageLoop::current() == io_loop_); 188 DCHECK(MessageLoop::current() == io_loop_);
189 DCHECK(latency_type_ == kHighLatency);
scherkus (not reviewing) 2011/06/24 21:09:26 try to use DCHECK_EQ when possible for pointers i
enal1 2011/06/24 22:01:09 Done.
173 190
174 base::AutoLock auto_lock(lock_); 191 base::AutoLock auto_lock(lock_);
175 if (stopped_) 192 if (stopped_)
176 return; 193 return;
177 194
178 shared_memory_.reset(new base::SharedMemory(handle, false)); 195 shared_memory_.reset(new base::SharedMemory(handle, false));
179 shared_memory_->Map(length); 196 shared_memory_->Map(length);
180 shared_memory_size_ = length; 197 shared_memory_size_ = length;
181 } 198 }
182 199
183 void AudioRendererImpl::OnLowLatencyCreated(base::SharedMemoryHandle, 200 void AudioRendererImpl::CreateSocket(base::SyncSocket::Handle socket_handle) {
184 base::SyncSocket::Handle, uint32) { 201 DCHECK(latency_type_ == kLowLatency);
scherkus (not reviewing) 2011/06/24 21:09:26 DCHECK_EQ
enal1 2011/06/24 22:01:09 Done.
185 // AudioRenderer should not have a low-latency audio channel. 202 #if defined(OS_WIN)
186 NOTREACHED(); 203 DCHECK(socket_handle);
204 #else
205 DCHECK_GE(socket_handle, 0);
206 #endif
207 socket_.reset(new base::SyncSocket(socket_handle));
208 }
209
210 void AudioRendererImpl::CreateAudioThread() {
211 DCHECK(latency_type_ == kLowLatency);
scherkus (not reviewing) 2011/06/24 21:09:26 DCHECK_EQ
enal1 2011/06/24 22:01:09 Done.
212 audio_thread_.reset(
213 new base::DelegateSimpleThread(this, "renderer_audio_thread"));
214 audio_thread_->Start();
215 }
216
217 void AudioRendererImpl::OnLowLatencyCreated(
218 base::SharedMemoryHandle handle,
219 base::SyncSocket::Handle socket_handle,
220 uint32 length) {
221
scherkus (not reviewing) 2011/06/24 21:09:26 nit: remove blank line
enal1 2011/06/24 22:01:09 Done.
222 DCHECK(MessageLoop::current() == io_loop_);
223 DCHECK(latency_type_ == kLowLatency);
scherkus (not reviewing) 2011/06/24 21:09:26 DCHECK_EQ
enal1 2011/06/24 22:01:09 Done.
224 #if defined(OS_WIN)
225 DCHECK(handle);
226 DCHECK(socket_handle);
227 #else
228 DCHECK_GE(handle.fd, 0);
229 DCHECK_GE(socket_handle, 0);
230 #endif
231 DCHECK(length);
232
233 base::AutoLock auto_lock(lock_);
234 if (stopped_)
235 return;
236
237 shared_memory_.reset(new base::SharedMemory(handle, false));
238 shared_memory_->Map(length);
239 shared_memory_size_ = length;
240
241 CreateSocket(socket_handle);
242 CreateAudioThread();
187 } 243 }
188 244
189 void AudioRendererImpl::OnRequestPacket(AudioBuffersState buffers_state) { 245 void AudioRendererImpl::OnRequestPacket(AudioBuffersState buffers_state) {
190 DCHECK(MessageLoop::current() == io_loop_); 246 DCHECK(MessageLoop::current() == io_loop_);
191 247 DCHECK(latency_type_ == kHighLatency);
scherkus (not reviewing) 2011/06/24 21:09:26 DCHECK_EQ
enal1 2011/06/24 22:01:09 Done.
192 { 248 {
193 base::AutoLock auto_lock(lock_); 249 base::AutoLock auto_lock(lock_);
194 DCHECK(!pending_request_); 250 DCHECK(!pending_request_);
195 pending_request_ = true; 251 pending_request_ = true;
196 request_buffers_state_ = buffers_state; 252 request_buffers_state_ = buffers_state;
197 } 253 }
198 254
199 // Try to fill in the fulfill the packet request. 255 // Try to fill in the fulfill the packet request.
200 NotifyPacketReadyTask(); 256 NotifyPacketReadyTask();
201 } 257 }
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
240 296
241 // Make sure we don't call create more than once. 297 // Make sure we don't call create more than once.
242 DCHECK_EQ(0, stream_id_); 298 DCHECK_EQ(0, stream_id_);
243 stream_id_ = filter_->AddDelegate(this); 299 stream_id_ = filter_->AddDelegate(this);
244 io_loop_->AddDestructionObserver(this); 300 io_loop_->AddDestructionObserver(this);
245 301
246 AudioParameters params_to_send(audio_params); 302 AudioParameters params_to_send(audio_params);
247 // Let the browser choose packet size. 303 // Let the browser choose packet size.
248 params_to_send.samples_per_packet = 0; 304 params_to_send.samples_per_packet = 0;
249 305
250 filter_->Send(new AudioHostMsg_CreateStream( 306 filter_->Send(new AudioHostMsg_CreateStream(0,
251 0, stream_id_, params_to_send, false)); 307 stream_id_,
308 params_to_send,
309 latency_type_ == kLowLatency));
252 } 310 }
253 311
254 void AudioRendererImpl::PlayTask() { 312 void AudioRendererImpl::PlayTask() {
255 DCHECK(MessageLoop::current() == io_loop_); 313 DCHECK(MessageLoop::current() == io_loop_);
256 314
257 filter_->Send(new AudioHostMsg_PlayStream(0, stream_id_)); 315 filter_->Send(new AudioHostMsg_PlayStream(0, stream_id_));
258 } 316 }
259 317
260 void AudioRendererImpl::PauseTask() { 318 void AudioRendererImpl::PauseTask() {
261 DCHECK(MessageLoop::current() == io_loop_); 319 DCHECK(MessageLoop::current() == io_loop_);
(...skipping 24 matching lines...) Expand all
286 DCHECK(MessageLoop::current() == io_loop_); 344 DCHECK(MessageLoop::current() == io_loop_);
287 345
288 base::AutoLock auto_lock(lock_); 346 base::AutoLock auto_lock(lock_);
289 if (stopped_) 347 if (stopped_)
290 return; 348 return;
291 filter_->Send(new AudioHostMsg_SetVolume(0, stream_id_, volume)); 349 filter_->Send(new AudioHostMsg_SetVolume(0, stream_id_, volume));
292 } 350 }
293 351
294 void AudioRendererImpl::NotifyPacketReadyTask() { 352 void AudioRendererImpl::NotifyPacketReadyTask() {
295 DCHECK(MessageLoop::current() == io_loop_); 353 DCHECK(MessageLoop::current() == io_loop_);
354 DCHECK(latency_type_ == kHighLatency);
scherkus (not reviewing) 2011/06/24 21:09:26 DCHECK_EQ
enal1 2011/06/24 22:01:09 Done.
296 355
297 base::AutoLock auto_lock(lock_); 356 base::AutoLock auto_lock(lock_);
298 if (stopped_) 357 if (stopped_)
299 return; 358 return;
300 if (pending_request_ && GetPlaybackRate() > 0.0f) { 359 if (pending_request_ && GetPlaybackRate() > 0.0f) {
301 DCHECK(shared_memory_.get()); 360 DCHECK(shared_memory_.get());
302 361
303 // Adjust the playback delay. 362 // Adjust the playback delay.
304 base::Time current_time = base::Time::Now(); 363 base::Time current_time = base::Time::Now();
305 364
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
339 DCHECK(MessageLoop::current() == io_loop_); 398 DCHECK(MessageLoop::current() == io_loop_);
340 399
341 // We treat the IO loop going away the same as stopping. 400 // We treat the IO loop going away the same as stopping.
342 base::AutoLock auto_lock(lock_); 401 base::AutoLock auto_lock(lock_);
343 if (stopped_) 402 if (stopped_)
344 return; 403 return;
345 404
346 stopped_ = true; 405 stopped_ = true;
347 DestroyTask(); 406 DestroyTask();
348 } 407 }
408
409 // Our audio thread runs here. We receive requests for more data and send it
410 // on this thread.
411 void AudioRendererImpl::Run() {
412 #if !defined(OS_LINUX) // SetThreadPriority() not implemented on Linux.
413 audio_thread_->SetThreadPriority(base::kThreadPriority_RealtimeAudio);
scherkus (not reviewing) 2011/06/24 21:09:26 it's ok to remove the DCHECK and have it spew a NO
enal1 2011/06/24 22:01:09 It is exactly what it is doing, I just did not lik
414 #endif
415
416 int bytes;
417 while (sizeof(bytes) == socket_->Receive(&bytes, sizeof(bytes))) {
418 if (bytes == media::AudioOutputController::kPauseMark) {
419 continue;
420 } else if (bytes < 0) {
421 break;
422 }
423 base::AutoLock auto_lock(lock_);
424 if (stopped_) {
425 break;
426 }
427 float playback_rate = GetPlaybackRate();
428 if (playback_rate > 0.0f) {
scherkus (not reviewing) 2011/06/24 21:09:26 nit: try to collapse nested ifs, so check for oppo
enal1 2011/06/24 22:01:09 Done, though personally I strongly prefer nested i
429 DCHECK(shared_memory_.get());
430
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 }
444 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698