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

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

Issue 8477037: Simplify AudioRendererImpl by using AudioDevice. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 1 month 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_device.h" 5 #include "content/renderer/media/audio_device.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/debug/trace_event.h" 8 #include "base/debug/trace_event.h"
9 #include "base/message_loop.h" 9 #include "base/message_loop.h"
10 #include "base/time.h" 10 #include "base/time.h"
11 #include "content/common/child_process.h" 11 #include "content/common/child_process.h"
12 #include "content/common/media/audio_messages.h" 12 #include "content/common/media/audio_messages.h"
13 #include "content/common/view_messages.h" 13 #include "content/common/view_messages.h"
14 #include "content/renderer/render_thread_impl.h" 14 #include "content/renderer/render_thread_impl.h"
15 #include "media/audio/audio_output_controller.h"
15 #include "media/audio/audio_util.h" 16 #include "media/audio/audio_util.h"
16 17
18 AudioDevice::AudioDevice()
19 : buffer_size_(0),
20 channels_(0),
21 bits_per_sample_(16),
22 sample_rate_(0),
23 latency_format_(AudioParameters::AUDIO_PCM_LOW_LATENCY),
24 callback_(0),
25 is_initialized_(false),
26 audio_delay_milliseconds_(0),
27 volume_(1.0),
28 stream_id_(0),
29 play_on_start_(true),
30 is_started_(false),
31 shared_memory_size_(0) {
32 filter_ = RenderThreadImpl::current()->audio_message_filter();
33 }
34
17 AudioDevice::AudioDevice(size_t buffer_size, 35 AudioDevice::AudioDevice(size_t buffer_size,
18 int channels, 36 int channels,
19 double sample_rate, 37 double sample_rate,
20 RenderCallback* callback) 38 RenderCallback* callback)
21 : buffer_size_(buffer_size), 39 : bits_per_sample_(16),
22 channels_(channels), 40 is_initialized_(false),
23 bits_per_sample_(16),
24 sample_rate_(sample_rate),
25 callback_(callback),
26 audio_delay_milliseconds_(0), 41 audio_delay_milliseconds_(0),
27 volume_(1.0), 42 volume_(1.0),
28 stream_id_(0) { 43 stream_id_(0),
44 play_on_start_(true),
45 is_started_(false),
46 shared_memory_size_(0) {
29 filter_ = RenderThreadImpl::current()->audio_message_filter(); 47 filter_ = RenderThreadImpl::current()->audio_message_filter();
48 Initialize(buffer_size,
49 channels,
50 sample_rate,
51 AudioParameters::AUDIO_PCM_LOW_LATENCY,
52 callback);
53 }
54
55 void AudioDevice::Initialize(size_t buffer_size,
56 int channels,
57 double sample_rate,
58 AudioParameters::Format latency_format,
59 RenderCallback* callback) {
60 CHECK_EQ(0, stream_id_) <<
61 "AudioDevice::Initialize() must be called before Start()";
62
63 buffer_size_ = buffer_size;
64 channels_ = channels;
65 sample_rate_ = sample_rate;
66 latency_format_ = latency_format;
67 callback_ = callback;
68
69 // Cleanup from any previous initialization.
70 for (size_t i = 0; i < audio_data_.size(); ++i)
71 delete [] audio_data_[i];
72
30 audio_data_.reserve(channels); 73 audio_data_.reserve(channels);
31 for (int i = 0; i < channels; ++i) { 74 for (int i = 0; i < channels; ++i) {
32 float* channel_data = new float[buffer_size]; 75 float* channel_data = new float[buffer_size];
33 audio_data_.push_back(channel_data); 76 audio_data_.push_back(channel_data);
34 } 77 }
78
79 is_initialized_ = true;
80 }
81
82 bool AudioDevice::IsInitialized() {
83 return is_initialized_;
35 } 84 }
36 85
37 AudioDevice::~AudioDevice() { 86 AudioDevice::~AudioDevice() {
38 // The current design requires that the user calls Stop() before deleting 87 // The current design requires that the user calls Stop() before deleting
39 // this class. 88 // this class.
40 CHECK_EQ(0, stream_id_); 89 CHECK_EQ(0, stream_id_);
41 for (int i = 0; i < channels_; ++i) 90 for (int i = 0; i < channels_; ++i)
42 delete [] audio_data_[i]; 91 delete [] audio_data_[i];
43 } 92 }
44 93
45 void AudioDevice::Start() { 94 void AudioDevice::Start() {
46 AudioParameters params; 95 AudioParameters params;
47 params.format = AudioParameters::AUDIO_PCM_LOW_LATENCY; 96 params.format = latency_format_;
48 params.channels = channels_; 97 params.channels = channels_;
49 params.sample_rate = static_cast<int>(sample_rate_); 98 params.sample_rate = static_cast<int>(sample_rate_);
50 params.bits_per_sample = bits_per_sample_; 99 params.bits_per_sample = bits_per_sample_;
51 params.samples_per_packet = buffer_size_; 100 params.samples_per_packet = buffer_size_;
52 101
53 ChildProcess::current()->io_message_loop()->PostTask( 102 ChildProcess::current()->io_message_loop()->PostTask(
54 FROM_HERE, 103 FROM_HERE,
55 base::Bind(&AudioDevice::InitializeOnIOThread, this, params)); 104 base::Bind(&AudioDevice::InitializeOnIOThread, this, params));
56 } 105 }
57 106
58 bool AudioDevice::Stop() { 107 bool AudioDevice::Stop() {
59 // Max waiting time for Stop() to complete. If this time limit is passed, 108 // Max waiting time for Stop() to complete. If this time limit is passed,
60 // we will stop waiting and return false. It ensures that Stop() can't block 109 // we will stop waiting and return false. It ensures that Stop() can't block
61 // the calling thread forever. 110 // the calling thread forever.
62 const base::TimeDelta kMaxTimeOut = base::TimeDelta::FromMilliseconds(1000); 111 const base::TimeDelta kMaxTimeOut = base::TimeDelta::FromMilliseconds(1000);
63 112
64 base::WaitableEvent completion(false, false); 113 base::WaitableEvent completion(false, false);
65 114
66 ChildProcess::current()->io_message_loop()->PostTask( 115 ChildProcess::current()->io_message_loop()->PostTask(
67 FROM_HERE, 116 FROM_HERE,
68 base::Bind(&AudioDevice::ShutDownOnIOThread, this, &completion)); 117 base::Bind(&AudioDevice::ShutDownOnIOThread, this, &completion));
no longer working on chromium 2011/11/28 15:17:41 We use a WitableEvent in ShutDownOnIOThread() to s
vrk (LEFT CHROMIUM) 2011/12/02 22:54:46 Hmm, I think you're right, but since this isn't my
69 118
70 // We wait here for the IO task to be completed to remove race conflicts 119 // We wait here for the IO task to be completed to remove race conflicts
71 // with OnLowLatencyCreated() and to ensure that Stop() acts as a synchronous 120 // with OnLowLatencyCreated() and to ensure that Stop() acts as a synchronous
72 // function call. 121 // function call.
73 if (completion.TimedWait(kMaxTimeOut)) { 122 if (completion.TimedWait(kMaxTimeOut)) {
74 if (audio_thread_.get()) { 123 ShutDownAudioThread();
75 socket_->Close();
76 audio_thread_->Join();
77 audio_thread_.reset(NULL);
78 }
79 } else { 124 } else {
80 LOG(ERROR) << "Failed to shut down audio output on IO thread"; 125 LOG(ERROR) << "Failed to shut down audio output on IO thread";
81 return false; 126 return false;
82 } 127 }
83 128
84 return true; 129 return true;
85 } 130 }
86 131
132 void AudioDevice::Play() {
133 ChildProcess::current()->io_message_loop()->PostTask(
134 FROM_HERE,
135 base::Bind(&AudioDevice::PlayOnIOThread, this));
136 }
137
138 void AudioDevice::Pause(bool flush) {
139 ChildProcess::current()->io_message_loop()->PostTask(
140 FROM_HERE,
141 base::Bind(&AudioDevice::PauseOnIOThread, this, flush));
142 }
143
87 bool AudioDevice::SetVolume(double volume) { 144 bool AudioDevice::SetVolume(double volume) {
88 if (volume < 0 || volume > 1.0) 145 if (volume < 0 || volume > 1.0)
89 return false; 146 return false;
90 147
91 ChildProcess::current()->io_message_loop()->PostTask( 148 ChildProcess::current()->io_message_loop()->PostTask(
92 FROM_HERE, 149 FROM_HERE,
93 base::Bind(&AudioDevice::SetVolumeOnIOThread, this, volume)); 150 base::Bind(&AudioDevice::SetVolumeOnIOThread, this, volume));
94 151
95 volume_ = volume; 152 volume_ = volume;
96 153
97 return true; 154 return true;
98 } 155 }
99 156
100 void AudioDevice::GetVolume(double* volume) { 157 void AudioDevice::GetVolume(double* volume) {
101 // Return a locally cached version of the current scaling factor. 158 // Return a locally cached version of the current scaling factor.
102 *volume = volume_; 159 *volume = volume_;
103 } 160 }
104 161
105 void AudioDevice::InitializeOnIOThread(const AudioParameters& params) { 162 void AudioDevice::InitializeOnIOThread(const AudioParameters& params) {
106 // Make sure we don't call Start() more than once. 163 // Make sure we don't create the stream more than once.
107 DCHECK_EQ(0, stream_id_); 164 DCHECK_EQ(0, stream_id_);
108 if (stream_id_) 165 if (stream_id_)
109 return; 166 return;
110 167
111 stream_id_ = filter_->AddDelegate(this); 168 stream_id_ = filter_->AddDelegate(this);
112 Send(new AudioHostMsg_CreateStream(stream_id_, params, true)); 169 Send(new AudioHostMsg_CreateStream(stream_id_, params, true));
113 } 170 }
114 171
115 void AudioDevice::StartOnIOThread() { 172 void AudioDevice::PlayOnIOThread() {
no longer working on chromium 2011/11/28 15:17:41 What happens if the clients call Play() or Pause()
vrk (LEFT CHROMIUM) 2011/12/02 22:54:46 This is a good question, but I think it may be som
116 if (stream_id_) 173 if (stream_id_ && is_started_)
117 Send(new AudioHostMsg_PlayStream(stream_id_)); 174 Send(new AudioHostMsg_PlayStream(stream_id_));
175 else
176 play_on_start_ = true;
177 }
178
179 void AudioDevice::PauseOnIOThread(bool flush) {
no longer working on chromium 2011/11/28 15:17:41 Same question as PlayOnIOThread()
vrk (LEFT CHROMIUM) 2011/12/02 22:54:46 Ditto above.
180 if (stream_id_ && is_started_) {
181 Send(new AudioHostMsg_PauseStream(stream_id_));
182 if (flush)
183 Send(new AudioHostMsg_FlushStream(stream_id_));
184 } else {
185 // Note that |flush| isn't relevant here since this is the case where
186 // the stream is first starting.
187 play_on_start_ = false;
188 }
118 } 189 }
119 190
120 void AudioDevice::ShutDownOnIOThread(base::WaitableEvent* completion) { 191 void AudioDevice::ShutDownOnIOThread(base::WaitableEvent* completion) {
no longer working on chromium 2011/11/28 15:17:41 We should clean up all the states like is_started,
vrk (LEFT CHROMIUM) 2011/12/02 22:54:46 Yeah, these states are probably an indication that
192 is_started_ = false;
193
121 // Make sure we don't call shutdown more than once. 194 // Make sure we don't call shutdown more than once.
122 if (!stream_id_) { 195 if (!stream_id_) {
123 completion->Signal(); 196 if (completion)
197 completion->Signal();
124 return; 198 return;
125 } 199 }
126 200
127 filter_->RemoveDelegate(stream_id_); 201 filter_->RemoveDelegate(stream_id_);
128 Send(new AudioHostMsg_CloseStream(stream_id_)); 202 Send(new AudioHostMsg_CloseStream(stream_id_));
vrk (LEFT CHROMIUM) 2011/11/22 01:17:39 Doesn't AudioHostMsg_CloseStream need to be synchr
vrk (LEFT CHROMIUM) 2011/12/02 22:54:46 Since this potential problem was not introduced by
129 stream_id_ = 0; 203 stream_id_ = 0;
130 204
131 completion->Signal(); 205 if (completion)
206 completion->Signal();
132 } 207 }
133 208
134 void AudioDevice::SetVolumeOnIOThread(double volume) { 209 void AudioDevice::SetVolumeOnIOThread(double volume) {
135 if (stream_id_) 210 if (stream_id_)
136 Send(new AudioHostMsg_SetVolume(stream_id_, volume)); 211 Send(new AudioHostMsg_SetVolume(stream_id_, volume));
137 } 212 }
138 213
139 void AudioDevice::OnRequestPacket(AudioBuffersState buffers_state) { 214 void AudioDevice::OnRequestPacket(AudioBuffersState buffers_state) {
140 // This method does not apply to the low-latency system. 215 // This method does not apply to the low-latency system.
141 NOTIMPLEMENTED();
142 } 216 }
143 217
144 void AudioDevice::OnStateChanged(AudioStreamState state) { 218 void AudioDevice::OnStateChanged(AudioStreamState state) {
145 if (state == kAudioStreamError) { 219 if (state == kAudioStreamError) {
146 DLOG(WARNING) << "AudioDevice::OnStateChanged(kError)"; 220 DLOG(WARNING) << "AudioDevice::OnStateChanged(kError)";
147 } 221 }
148 NOTIMPLEMENTED();
149 } 222 }
150 223
151 void AudioDevice::OnCreated( 224 void AudioDevice::OnCreated(
152 base::SharedMemoryHandle handle, uint32 length) { 225 base::SharedMemoryHandle handle, uint32 length) {
153 // Not needed in this simple implementation. 226 // Not needed in this simple implementation.
154 NOTIMPLEMENTED();
155 } 227 }
156 228
157 void AudioDevice::OnLowLatencyCreated( 229 void AudioDevice::OnLowLatencyCreated(
158 base::SharedMemoryHandle handle, 230 base::SharedMemoryHandle handle,
159 base::SyncSocket::Handle socket_handle, 231 base::SyncSocket::Handle socket_handle,
160 uint32 length) { 232 uint32 length) {
161 DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop()); 233 DCHECK(MessageLoop::current() == ChildProcess::current()->io_message_loop());
162 #if defined(OS_WIN) 234 #if defined(OS_WIN)
163 DCHECK(handle); 235 DCHECK(handle);
164 DCHECK(socket_handle); 236 DCHECK(socket_handle);
165 #else 237 #else
166 DCHECK_GE(handle.fd, 0); 238 DCHECK_GE(handle.fd, 0);
167 DCHECK_GE(socket_handle, 0); 239 DCHECK_GE(socket_handle, 0);
168 #endif 240 #endif
169 DCHECK(length); 241 DCHECK(length);
170 242
171 // Takes care of the case when Stop() is called before OnLowLatencyCreated(). 243 // Takes care of the case when Stop() is called before OnLowLatencyCreated().
172 if (!stream_id_) { 244 if (!stream_id_) {
173 base::SharedMemory::CloseHandle(handle); 245 base::SharedMemory::CloseHandle(handle);
174 // Close the socket handler. 246 // Close the socket handler.
175 base::SyncSocket socket(socket_handle); 247 base::SyncSocket socket(socket_handle);
176 return; 248 return;
177 } 249 }
178 250
179 shared_memory_.reset(new base::SharedMemory(handle, false)); 251 shared_memory_.reset(new base::SharedMemory(handle, false));
180 shared_memory_->Map(length); 252 shared_memory_->Map(length);
253 shared_memory_size_ = length;
181 254
182 DCHECK_GE(length, buffer_size_ * sizeof(int16) * channels_); 255 DCHECK_GE(length, buffer_size_ * sizeof(int16) * channels_);
183 256
184 socket_.reset(new base::SyncSocket(socket_handle)); 257 {
185 // Allow the client to pre-populate the buffer. 258 // Synchronize with ShutDownAudioThread().
186 FireRenderCallback(); 259 base::AutoLock auto_lock(lock_);
187 260
188 audio_thread_.reset( 261 socket_.reset(new base::SyncSocket(socket_handle));
189 new base::DelegateSimpleThread(this, "renderer_audio_thread")); 262 // Allow the client to pre-populate the buffer.
190 audio_thread_->Start(); 263 FireRenderCallback();
191 264
192 MessageLoop::current()->PostTask( 265 DCHECK(!audio_thread_.get());
193 FROM_HERE, 266 audio_thread_.reset(
194 base::Bind(&AudioDevice::StartOnIOThread, this)); 267 new base::DelegateSimpleThread(this, "renderer_audio_thread"));
268 audio_thread_->Start();
269 }
270
271 // We handle the case where Play() and/or Pause() may have been called
272 // multiple times before OnLowLatencyCreated() gets called.
273 is_started_ = true;
274 if (play_on_start_) {
275 MessageLoop::current()->PostTask(
276 FROM_HERE,
277 base::Bind(&AudioDevice::PlayOnIOThread, this));
no longer working on chromium 2011/11/28 15:17:41 One potential racing condition here, for example,
vrk (LEFT CHROMIUM) 2011/12/02 22:54:46 Yes, you're right; good catch! I think calling Pla
278 }
195 } 279 }
196 280
197 void AudioDevice::OnVolume(double volume) { 281 void AudioDevice::OnVolume(double volume) {
198 NOTIMPLEMENTED(); 282 NOTIMPLEMENTED();
199 } 283 }
200 284
201 void AudioDevice::Send(IPC::Message* message) { 285 void AudioDevice::Send(IPC::Message* message) {
202 filter_->Send(message); 286 filter_->Send(message);
203 } 287 }
204 288
205 // Our audio thread runs here. 289 // Our audio thread runs here.
206 void AudioDevice::Run() { 290 void AudioDevice::Run() {
207 audio_thread_->SetThreadPriority(base::kThreadPriority_RealtimeAudio); 291 audio_thread_->SetThreadPriority(base::kThreadPriority_RealtimeAudio);
208 292
209 int pending_data; 293 int pending_data;
210 const int samples_per_ms = static_cast<int>(sample_rate_) / 1000; 294 const int samples_per_ms = static_cast<int>(sample_rate_) / 1000;
211 const int bytes_per_ms = channels_ * (bits_per_sample_ / 8) * samples_per_ms; 295 const int bytes_per_ms = channels_ * (bits_per_sample_ / 8) * samples_per_ms;
212 296
213 while ((sizeof(pending_data) == socket_->Receive(&pending_data, 297 while (sizeof(pending_data) ==
214 sizeof(pending_data))) && 298 socket_->Receive(&pending_data, sizeof(pending_data))) {
215 (pending_data >= 0)) { 299 if (pending_data == media::AudioOutputController::kPauseMark) {
300 memset(shared_memory_data(), 0, shared_memory_size_);
301 continue;
302 } else if (pending_data < 0) {
303 break;
304 }
216 // Convert the number of pending bytes in the render buffer 305 // Convert the number of pending bytes in the render buffer
217 // into milliseconds. 306 // into milliseconds.
218 audio_delay_milliseconds_ = pending_data / bytes_per_ms; 307 audio_delay_milliseconds_ = pending_data / bytes_per_ms;
219 FireRenderCallback(); 308 FireRenderCallback();
220 } 309 }
221 } 310 }
222 311
223 void AudioDevice::FireRenderCallback() { 312 void AudioDevice::FireRenderCallback() {
224 TRACE_EVENT0("audio", "AudioDevice::FireRenderCallback"); 313 TRACE_EVENT0("audio", "AudioDevice::FireRenderCallback");
225 314
226 if (callback_) { 315 if (callback_) {
227 // Update the audio-delay measurement then ask client to render audio. 316 // Update the audio-delay measurement then ask client to render audio.
228 callback_->Render(audio_data_, buffer_size_, audio_delay_milliseconds_); 317 callback_->Render(audio_data_, buffer_size_, audio_delay_milliseconds_);
229 318
230 // Interleave, scale, and clip to int16. 319 // Interleave, scale, and clip to int16.
320 // TODO(crogers): avoid converting to integer here, and pass the data
321 // to the browser process as float, so we don't lose precision for
322 // audio hardware which has better than 16bit precision.
231 media::InterleaveFloatToInt16(audio_data_, 323 media::InterleaveFloatToInt16(audio_data_,
232 static_cast<int16*>(shared_memory_data()), 324 static_cast<int16*>(shared_memory_data()),
233 buffer_size_); 325 buffer_size_);
234 } 326 }
235 } 327 }
236 328
329 void AudioDevice::ShutDownAudioThread() {
no longer working on chromium 2011/11/28 15:17:41 Please see comment in Stop().
vrk (LEFT CHROMIUM) 2011/12/02 22:54:46 Done.
330 // Synchronize with OnLowLatencyCreated().
331 base::AutoLock auto_lock(lock_);
332
333 if (socket_.get()) {
334 socket_->Close();
335 }
336 if (audio_thread_.get()) {
337 audio_thread_->Join();
338 audio_thread_.reset(NULL);
339 }
340 // Note that the socket can't be reset until *after* joining the thread.
341 socket_.reset(NULL);
342 }
343
237 double AudioDevice::GetAudioHardwareSampleRate() { 344 double AudioDevice::GetAudioHardwareSampleRate() {
238 // Uses cached value if possible. 345 // Uses cached value if possible.
239 static double hardware_sample_rate = 0; 346 static double hardware_sample_rate = 0;
240 if (!hardware_sample_rate) { 347 if (!hardware_sample_rate) {
241 RenderThreadImpl::current()->Send( 348 RenderThreadImpl::current()->Send(
242 new ViewHostMsg_GetHardwareSampleRate(&hardware_sample_rate)); 349 new ViewHostMsg_GetHardwareSampleRate(&hardware_sample_rate));
243 } 350 }
244 return hardware_sample_rate; 351 return hardware_sample_rate;
245 } 352 }
246 353
247 size_t AudioDevice::GetAudioHardwareBufferSize() { 354 size_t AudioDevice::GetAudioHardwareBufferSize() {
248 // Uses cached value if possible. 355 // Uses cached value if possible.
acolwell GONE FROM CHROMIUM 2011/11/17 19:16:12 FYI. You might need to coordinate w/ tommi on this
vrk (LEFT CHROMIUM) 2011/12/02 22:54:46 Thanks Aaron! These methods weren't being used in
249 static uint32 buffer_size = 0; 356 static uint32 buffer_size = 0;
250 357
251 if (!buffer_size) { 358 if (!buffer_size) {
252 RenderThreadImpl::current()->Send( 359 RenderThreadImpl::current()->Send(
253 new ViewHostMsg_GetHardwareBufferSize(&buffer_size)); 360 new ViewHostMsg_GetHardwareBufferSize(&buffer_size));
254 } 361 }
255 362
256 return static_cast<size_t>(buffer_size); 363 return static_cast<size_t>(buffer_size);
257 } 364 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698