OLD | NEW |
| (Empty) |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/renderer_host/audio_renderer_host.h" | |
6 | |
7 #include "base/metrics/histogram.h" | |
8 #include "base/process.h" | |
9 #include "base/shared_memory.h" | |
10 #include "chrome/browser/renderer_host/audio_sync_reader.h" | |
11 #include "chrome/common/render_messages.h" | |
12 #include "chrome/common/render_messages_params.h" | |
13 #include "ipc/ipc_logging.h" | |
14 | |
15 // The minimum number of samples in a hardware packet. | |
16 // This value is selected so that we can handle down to 5khz sample rate. | |
17 static const int kMinSamplesPerHardwarePacket = 1024; | |
18 | |
19 // The maximum number of samples in a hardware packet. | |
20 // This value is selected so that we can handle up to 192khz sample rate. | |
21 static const int kMaxSamplesPerHardwarePacket = 64 * 1024; | |
22 | |
23 // This constant governs the hardware audio buffer size, this value should be | |
24 // chosen carefully. | |
25 // This value is selected so that we have 8192 samples for 48khz streams. | |
26 static const int kMillisecondsPerHardwarePacket = 170; | |
27 | |
28 static uint32 SelectSamplesPerPacket(AudioParameters params) { | |
29 // Select the number of samples that can provide at least | |
30 // |kMillisecondsPerHardwarePacket| worth of audio data. | |
31 int samples = kMinSamplesPerHardwarePacket; | |
32 while (samples <= kMaxSamplesPerHardwarePacket && | |
33 samples * base::Time::kMillisecondsPerSecond < | |
34 params.sample_rate * kMillisecondsPerHardwarePacket) { | |
35 samples *= 2; | |
36 } | |
37 return samples; | |
38 } | |
39 | |
40 AudioRendererHost::AudioEntry::AudioEntry() | |
41 : render_view_id(0), | |
42 stream_id(0), | |
43 pending_buffer_request(false), | |
44 pending_close(false) { | |
45 } | |
46 | |
47 AudioRendererHost::AudioEntry::~AudioEntry() {} | |
48 | |
49 /////////////////////////////////////////////////////////////////////////////// | |
50 // AudioRendererHost implementations. | |
51 AudioRendererHost::AudioRendererHost() { | |
52 } | |
53 | |
54 AudioRendererHost::~AudioRendererHost() { | |
55 DCHECK(audio_entries_.empty()); | |
56 } | |
57 | |
58 void AudioRendererHost::OnChannelClosing() { | |
59 BrowserMessageFilter::OnChannelClosing(); | |
60 | |
61 // Since the IPC channel is gone, close all requested audio streams. | |
62 DeleteEntries(); | |
63 } | |
64 | |
65 void AudioRendererHost::OnDestruct() const { | |
66 BrowserThread::DeleteOnIOThread::Destruct(this); | |
67 } | |
68 | |
69 /////////////////////////////////////////////////////////////////////////////// | |
70 // media::AudioOutputController::EventHandler implementations. | |
71 void AudioRendererHost::OnCreated(media::AudioOutputController* controller) { | |
72 BrowserThread::PostTask( | |
73 BrowserThread::IO, | |
74 FROM_HERE, | |
75 NewRunnableMethod( | |
76 this, | |
77 &AudioRendererHost::DoCompleteCreation, | |
78 make_scoped_refptr(controller))); | |
79 } | |
80 | |
81 void AudioRendererHost::OnPlaying(media::AudioOutputController* controller) { | |
82 BrowserThread::PostTask( | |
83 BrowserThread::IO, | |
84 FROM_HERE, | |
85 NewRunnableMethod( | |
86 this, | |
87 &AudioRendererHost::DoSendPlayingMessage, | |
88 make_scoped_refptr(controller))); | |
89 } | |
90 | |
91 void AudioRendererHost::OnPaused(media::AudioOutputController* controller) { | |
92 BrowserThread::PostTask( | |
93 BrowserThread::IO, | |
94 FROM_HERE, | |
95 NewRunnableMethod( | |
96 this, | |
97 &AudioRendererHost::DoSendPausedMessage, | |
98 make_scoped_refptr(controller))); | |
99 } | |
100 | |
101 void AudioRendererHost::OnError(media::AudioOutputController* controller, | |
102 int error_code) { | |
103 BrowserThread::PostTask( | |
104 BrowserThread::IO, | |
105 FROM_HERE, | |
106 NewRunnableMethod(this, | |
107 &AudioRendererHost::DoHandleError, | |
108 make_scoped_refptr(controller), | |
109 error_code)); | |
110 } | |
111 | |
112 void AudioRendererHost::OnMoreData(media::AudioOutputController* controller, | |
113 AudioBuffersState buffers_state) { | |
114 BrowserThread::PostTask( | |
115 BrowserThread::IO, | |
116 FROM_HERE, | |
117 NewRunnableMethod(this, | |
118 &AudioRendererHost::DoRequestMoreData, | |
119 make_scoped_refptr(controller), | |
120 buffers_state)); | |
121 } | |
122 | |
123 void AudioRendererHost::DoCompleteCreation( | |
124 media::AudioOutputController* controller) { | |
125 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
126 | |
127 AudioEntry* entry = LookupByController(controller); | |
128 if (!entry) | |
129 return; | |
130 | |
131 if (!peer_handle()) { | |
132 NOTREACHED() << "Renderer process handle is invalid."; | |
133 DeleteEntryOnError(entry); | |
134 return; | |
135 } | |
136 | |
137 // Once the audio stream is created then complete the creation process by | |
138 // mapping shared memory and sharing with the renderer process. | |
139 base::SharedMemoryHandle foreign_memory_handle; | |
140 if (!entry->shared_memory.ShareToProcess(peer_handle(), | |
141 &foreign_memory_handle)) { | |
142 // If we failed to map and share the shared memory then close the audio | |
143 // stream and send an error message. | |
144 DeleteEntryOnError(entry); | |
145 return; | |
146 } | |
147 | |
148 if (entry->controller->LowLatencyMode()) { | |
149 AudioSyncReader* reader = | |
150 static_cast<AudioSyncReader*>(entry->reader.get()); | |
151 | |
152 #if defined(OS_WIN) | |
153 base::SyncSocket::Handle foreign_socket_handle; | |
154 #else | |
155 base::FileDescriptor foreign_socket_handle; | |
156 #endif | |
157 | |
158 // If we failed to prepare the sync socket for the renderer then we fail | |
159 // the construction of audio stream. | |
160 if (!reader->PrepareForeignSocketHandle(peer_handle(), | |
161 &foreign_socket_handle)) { | |
162 DeleteEntryOnError(entry); | |
163 return; | |
164 } | |
165 | |
166 Send(new ViewMsg_NotifyLowLatencyAudioStreamCreated( | |
167 entry->render_view_id, entry->stream_id, foreign_memory_handle, | |
168 foreign_socket_handle, entry->shared_memory.created_size())); | |
169 return; | |
170 } | |
171 | |
172 // The normal audio stream has created, send a message to the renderer | |
173 // process. | |
174 Send(new ViewMsg_NotifyAudioStreamCreated( | |
175 entry->render_view_id, entry->stream_id, foreign_memory_handle, | |
176 entry->shared_memory.created_size())); | |
177 } | |
178 | |
179 void AudioRendererHost::DoSendPlayingMessage( | |
180 media::AudioOutputController* controller) { | |
181 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
182 | |
183 AudioEntry* entry = LookupByController(controller); | |
184 if (!entry) | |
185 return; | |
186 | |
187 ViewMsg_AudioStreamState_Params params; | |
188 params.state = ViewMsg_AudioStreamState_Params::kPlaying; | |
189 Send(new ViewMsg_NotifyAudioStreamStateChanged( | |
190 entry->render_view_id, entry->stream_id, params)); | |
191 } | |
192 | |
193 void AudioRendererHost::DoSendPausedMessage( | |
194 media::AudioOutputController* controller) { | |
195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
196 | |
197 AudioEntry* entry = LookupByController(controller); | |
198 if (!entry) | |
199 return; | |
200 | |
201 ViewMsg_AudioStreamState_Params params; | |
202 params.state = ViewMsg_AudioStreamState_Params::kPaused; | |
203 Send(new ViewMsg_NotifyAudioStreamStateChanged( | |
204 entry->render_view_id, entry->stream_id, params)); | |
205 } | |
206 | |
207 void AudioRendererHost::DoRequestMoreData( | |
208 media::AudioOutputController* controller, | |
209 AudioBuffersState buffers_state) { | |
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
211 | |
212 // If we already have a pending request then return. | |
213 AudioEntry* entry = LookupByController(controller); | |
214 if (!entry || entry->pending_buffer_request) | |
215 return; | |
216 | |
217 DCHECK(!entry->controller->LowLatencyMode()); | |
218 entry->pending_buffer_request = true; | |
219 Send(new ViewMsg_RequestAudioPacket( | |
220 entry->render_view_id, entry->stream_id, buffers_state)); | |
221 } | |
222 | |
223 void AudioRendererHost::DoHandleError(media::AudioOutputController* controller, | |
224 int error_code) { | |
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
226 | |
227 AudioEntry* entry = LookupByController(controller); | |
228 if (!entry) | |
229 return; | |
230 | |
231 DeleteEntryOnError(entry); | |
232 } | |
233 | |
234 /////////////////////////////////////////////////////////////////////////////// | |
235 // IPC Messages handler | |
236 bool AudioRendererHost::OnMessageReceived(const IPC::Message& message, | |
237 bool* message_was_ok) { | |
238 bool handled = true; | |
239 IPC_BEGIN_MESSAGE_MAP_EX(AudioRendererHost, message, *message_was_ok) | |
240 IPC_MESSAGE_HANDLER(ViewHostMsg_CreateAudioStream, OnCreateStream) | |
241 IPC_MESSAGE_HANDLER(ViewHostMsg_PlayAudioStream, OnPlayStream) | |
242 IPC_MESSAGE_HANDLER(ViewHostMsg_PauseAudioStream, OnPauseStream) | |
243 IPC_MESSAGE_HANDLER(ViewHostMsg_FlushAudioStream, OnFlushStream) | |
244 IPC_MESSAGE_HANDLER(ViewHostMsg_CloseAudioStream, OnCloseStream) | |
245 IPC_MESSAGE_HANDLER(ViewHostMsg_NotifyAudioPacketReady, OnNotifyPacketReady) | |
246 IPC_MESSAGE_HANDLER(ViewHostMsg_GetAudioVolume, OnGetVolume) | |
247 IPC_MESSAGE_HANDLER(ViewHostMsg_SetAudioVolume, OnSetVolume) | |
248 IPC_MESSAGE_UNHANDLED(handled = false) | |
249 IPC_END_MESSAGE_MAP_EX() | |
250 | |
251 return handled; | |
252 } | |
253 | |
254 void AudioRendererHost::OnCreateStream( | |
255 const IPC::Message& msg, int stream_id, | |
256 const ViewHostMsg_Audio_CreateStream_Params& params, bool low_latency) { | |
257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
258 DCHECK(LookupById(msg.routing_id(), stream_id) == NULL); | |
259 | |
260 AudioParameters audio_params(params.params); | |
261 | |
262 // Select the hardware packet size if not specified. | |
263 if (!audio_params.samples_per_packet) { | |
264 audio_params.samples_per_packet = SelectSamplesPerPacket(audio_params); | |
265 } | |
266 uint32 packet_size = audio_params.GetPacketSize(); | |
267 | |
268 scoped_ptr<AudioEntry> entry(new AudioEntry()); | |
269 // Create the shared memory and share with the renderer process. | |
270 if (!entry->shared_memory.CreateAndMapAnonymous(packet_size)) { | |
271 // If creation of shared memory failed then send an error message. | |
272 SendErrorMessage(msg.routing_id(), stream_id); | |
273 return; | |
274 } | |
275 | |
276 if (low_latency) { | |
277 // If this is the low latency mode, we need to construct a SyncReader first. | |
278 scoped_ptr<AudioSyncReader> reader( | |
279 new AudioSyncReader(&entry->shared_memory)); | |
280 | |
281 // Then try to initialize the sync reader. | |
282 if (!reader->Init()) { | |
283 SendErrorMessage(msg.routing_id(), stream_id); | |
284 return; | |
285 } | |
286 | |
287 // If we have successfully created the SyncReader then assign it to the | |
288 // entry and construct an AudioOutputController. | |
289 entry->reader.reset(reader.release()); | |
290 entry->controller = | |
291 media::AudioOutputController::CreateLowLatency(this, audio_params, | |
292 entry->reader.get()); | |
293 } else { | |
294 // The choice of buffer capacity is based on experiment. | |
295 entry->controller = | |
296 media::AudioOutputController::Create(this, audio_params, | |
297 3 * packet_size); | |
298 } | |
299 | |
300 if (!entry->controller) { | |
301 SendErrorMessage(msg.routing_id(), stream_id); | |
302 return; | |
303 } | |
304 | |
305 // If we have created the controller successfully create a entry and add it | |
306 // to the map. | |
307 entry->render_view_id = msg.routing_id(); | |
308 entry->stream_id = stream_id; | |
309 | |
310 audio_entries_.insert(std::make_pair( | |
311 AudioEntryId(msg.routing_id(), stream_id), | |
312 entry.release())); | |
313 } | |
314 | |
315 void AudioRendererHost::OnPlayStream(const IPC::Message& msg, int stream_id) { | |
316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
317 | |
318 AudioEntry* entry = LookupById(msg.routing_id(), stream_id); | |
319 if (!entry) { | |
320 SendErrorMessage(msg.routing_id(), stream_id); | |
321 return; | |
322 } | |
323 | |
324 entry->controller->Play(); | |
325 } | |
326 | |
327 void AudioRendererHost::OnPauseStream(const IPC::Message& msg, int stream_id) { | |
328 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
329 | |
330 AudioEntry* entry = LookupById(msg.routing_id(), stream_id); | |
331 if (!entry) { | |
332 SendErrorMessage(msg.routing_id(), stream_id); | |
333 return; | |
334 } | |
335 | |
336 entry->controller->Pause(); | |
337 } | |
338 | |
339 void AudioRendererHost::OnFlushStream(const IPC::Message& msg, int stream_id) { | |
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
341 | |
342 AudioEntry* entry = LookupById(msg.routing_id(), stream_id); | |
343 if (!entry) { | |
344 SendErrorMessage(msg.routing_id(), stream_id); | |
345 return; | |
346 } | |
347 | |
348 entry->controller->Flush(); | |
349 } | |
350 | |
351 void AudioRendererHost::OnCloseStream(const IPC::Message& msg, int stream_id) { | |
352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
353 | |
354 AudioEntry* entry = LookupById(msg.routing_id(), stream_id); | |
355 | |
356 if (entry) | |
357 CloseAndDeleteStream(entry); | |
358 } | |
359 | |
360 void AudioRendererHost::OnSetVolume(const IPC::Message& msg, int stream_id, | |
361 double volume) { | |
362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
363 | |
364 AudioEntry* entry = LookupById(msg.routing_id(), stream_id); | |
365 if (!entry) { | |
366 SendErrorMessage(msg.routing_id(), stream_id); | |
367 return; | |
368 } | |
369 | |
370 // Make sure the volume is valid. | |
371 if (volume < 0 || volume > 1.0) | |
372 return; | |
373 entry->controller->SetVolume(volume); | |
374 } | |
375 | |
376 void AudioRendererHost::OnGetVolume(const IPC::Message& msg, int stream_id) { | |
377 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
378 NOTREACHED() << "This message shouldn't be received"; | |
379 } | |
380 | |
381 void AudioRendererHost::OnNotifyPacketReady( | |
382 const IPC::Message& msg, int stream_id, uint32 packet_size) { | |
383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
384 | |
385 AudioEntry* entry = LookupById(msg.routing_id(), stream_id); | |
386 if (!entry) { | |
387 SendErrorMessage(msg.routing_id(), stream_id); | |
388 return; | |
389 } | |
390 | |
391 DCHECK(!entry->controller->LowLatencyMode()); | |
392 CHECK(packet_size <= entry->shared_memory.created_size()); | |
393 | |
394 if (!entry->pending_buffer_request) { | |
395 NOTREACHED() << "Buffer received but no such pending request"; | |
396 } | |
397 entry->pending_buffer_request = false; | |
398 | |
399 // Enqueue the data to media::AudioOutputController. | |
400 entry->controller->EnqueueData( | |
401 reinterpret_cast<uint8*>(entry->shared_memory.memory()), | |
402 packet_size); | |
403 } | |
404 | |
405 void AudioRendererHost::SendErrorMessage(int32 render_view_id, | |
406 int32 stream_id) { | |
407 ViewMsg_AudioStreamState_Params state; | |
408 state.state = ViewMsg_AudioStreamState_Params::kError; | |
409 Send(new ViewMsg_NotifyAudioStreamStateChanged( | |
410 render_view_id, stream_id, state)); | |
411 } | |
412 | |
413 void AudioRendererHost::DeleteEntries() { | |
414 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
415 | |
416 for (AudioEntryMap::iterator i = audio_entries_.begin(); | |
417 i != audio_entries_.end(); ++i) { | |
418 CloseAndDeleteStream(i->second); | |
419 } | |
420 } | |
421 | |
422 void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) { | |
423 if (!entry->pending_close) { | |
424 entry->controller->Close( | |
425 NewRunnableMethod(this, &AudioRendererHost::OnStreamClosed, entry)); | |
426 entry->pending_close = true; | |
427 } | |
428 } | |
429 | |
430 void AudioRendererHost::OnStreamClosed(AudioEntry* entry) { | |
431 // Delete the entry after we've closed the stream. | |
432 BrowserThread::PostTask( | |
433 BrowserThread::IO, FROM_HERE, | |
434 NewRunnableMethod(this, &AudioRendererHost::DeleteEntry, entry)); | |
435 } | |
436 | |
437 void AudioRendererHost::DeleteEntry(AudioEntry* entry) { | |
438 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
439 | |
440 // Delete the entry when this method goes out of scope. | |
441 scoped_ptr<AudioEntry> entry_deleter(entry); | |
442 | |
443 // Erase the entry from the map. | |
444 audio_entries_.erase( | |
445 AudioEntryId(entry->render_view_id, entry->stream_id)); | |
446 } | |
447 | |
448 void AudioRendererHost::DeleteEntryOnError(AudioEntry* entry) { | |
449 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
450 | |
451 // Sends the error message first before we close the stream because | |
452 // |entry| is destroyed in DeleteEntry(). | |
453 SendErrorMessage(entry->render_view_id, entry->stream_id); | |
454 CloseAndDeleteStream(entry); | |
455 } | |
456 | |
457 AudioRendererHost::AudioEntry* AudioRendererHost::LookupById( | |
458 int route_id, int stream_id) { | |
459 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
460 | |
461 AudioEntryMap::iterator i = audio_entries_.find( | |
462 AudioEntryId(route_id, stream_id)); | |
463 if (i != audio_entries_.end() && !i->second->pending_close) | |
464 return i->second; | |
465 return NULL; | |
466 } | |
467 | |
468 AudioRendererHost::AudioEntry* AudioRendererHost::LookupByController( | |
469 media::AudioOutputController* controller) { | |
470 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
471 | |
472 // Iterate the map of entries. | |
473 // TODO(hclam): Implement a faster look up method. | |
474 for (AudioEntryMap::iterator i = audio_entries_.begin(); | |
475 i != audio_entries_.end(); ++i) { | |
476 if (!i->second->pending_close && controller == i->second->controller.get()) | |
477 return i->second; | |
478 } | |
479 return NULL; | |
480 } | |
OLD | NEW |