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

Side by Side Diff: chrome/browser/renderer_host/audio_renderer_host.cc

Issue 6532073: Move core pieces of browser\renderer_host to src\content. (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 9 years, 10 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
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698