OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/browser/renderer_host/media/audio_renderer_host.h" | 5 #include "content/browser/renderer_host/media/audio_renderer_host.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/lazy_instance.h" | |
8 #include "base/metrics/histogram.h" | 9 #include "base/metrics/histogram.h" |
9 #include "base/process.h" | 10 #include "base/process.h" |
10 #include "base/shared_memory.h" | 11 #include "base/shared_memory.h" |
11 #include "content/browser/browser_main_loop.h" | 12 #include "content/browser/browser_main_loop.h" |
12 #include "content/browser/renderer_host/media/audio_sync_reader.h" | 13 #include "content/browser/renderer_host/media/audio_sync_reader.h" |
14 #include "content/browser/renderer_host/media/web_contents_audio_input_stream.h" | |
13 #include "content/common/media/audio_messages.h" | 15 #include "content/common/media/audio_messages.h" |
14 #include "content/public/browser/media_observer.h" | 16 #include "content/public/browser/media_observer.h" |
17 #include "media/audio/audio_io.h" | |
15 #include "media/audio/shared_memory_util.h" | 18 #include "media/audio/shared_memory_util.h" |
16 #include "media/base/audio_bus.h" | 19 #include "media/base/audio_bus.h" |
17 #include "media/base/limits.h" | 20 #include "media/base/limits.h" |
18 | 21 |
19 using media::AudioBus; | 22 using media::AudioBus; |
20 | 23 |
21 namespace content { | 24 namespace content { |
22 | 25 |
23 struct AudioRendererHost::AudioEntry { | 26 struct AudioRendererHost::AudioEntry { |
24 AudioEntry(); | 27 AudioEntry(); |
25 ~AudioEntry(); | 28 ~AudioEntry(); |
26 | 29 |
27 // The AudioOutputController that manages the audio stream. | 30 // The AudioOutputController that manages the audio stream. |
28 scoped_refptr<media::AudioOutputController> controller; | 31 scoped_refptr<media::AudioOutputController> controller; |
29 | 32 |
30 // The audio stream ID. | 33 // The audio stream ID. |
31 int stream_id; | 34 int stream_id; |
32 | 35 |
36 // The routing ID of the source render view. | |
37 int render_view_id; | |
38 | |
33 // Shared memory for transmission of the audio data. | 39 // Shared memory for transmission of the audio data. |
34 base::SharedMemory shared_memory; | 40 base::SharedMemory shared_memory; |
35 | 41 |
36 // The synchronous reader to be used by the controller. We have the | 42 // The synchronous reader to be used by the controller. We have the |
37 // ownership of the reader. | 43 // ownership of the reader. |
38 scoped_ptr<media::AudioOutputController::SyncReader> reader; | 44 scoped_ptr<media::AudioOutputController::SyncReader> reader; |
39 | 45 |
46 // When non-NULL, normal audio output is being diverted for audio mirroring. | |
47 scoped_ptr<media::DivertedAudioOutputStream> diverted_stream; | |
Alpha Left Google
2012/11/20 21:49:43
Should this be owned by AudioOutputController inst
| |
48 | |
40 // Set to true after we called Close() for the controller. | 49 // Set to true after we called Close() for the controller. |
41 bool pending_close; | 50 bool pending_close; |
42 }; | 51 }; |
43 | 52 |
44 AudioRendererHost::AudioEntry::AudioEntry() | 53 AudioRendererHost::AudioEntry::AudioEntry() |
45 : stream_id(0), | 54 : stream_id(0), |
55 render_view_id(MSG_ROUTING_NONE), | |
46 pending_close(false) { | 56 pending_close(false) { |
47 } | 57 } |
48 | 58 |
49 AudioRendererHost::AudioEntry::~AudioEntry() {} | 59 AudioRendererHost::AudioEntry::~AudioEntry() {} |
50 | 60 |
61 namespace { | |
62 | |
63 struct GlobalLookup { | |
64 base::Lock lock; | |
Alpha Left Google
2012/11/20 21:49:43
I don't think a lock is necessary, rather ASSERT f
miu
2012/11/21 08:27:48
Unfortunately, the lock will be needed since an en
| |
65 std::map<int, AudioRendererHost*> by_process_id; | |
66 }; | |
67 | |
68 base::LazyInstance<GlobalLookup>::Leaky g_lookup = LAZY_INSTANCE_INITIALIZER; | |
69 | |
70 void RegisterAudioRendererHost(int render_process_id, AudioRendererHost* arh) { | |
71 GlobalLookup& lookup = g_lookup.Get(); | |
72 base::AutoLock guard(lookup.lock); | |
73 lookup.by_process_id.insert(std::make_pair(render_process_id, arh)); | |
74 } | |
75 | |
76 void DeregisterAudioRendererHost(int render_process_id) { | |
77 GlobalLookup& lookup = g_lookup.Get(); | |
78 base::AutoLock guard(lookup.lock); | |
79 lookup.by_process_id.erase(render_process_id); | |
80 } | |
81 | |
82 } // namespace | |
83 | |
84 // static | |
85 scoped_refptr<AudioRendererHost> AudioRendererHost::FromRenderProcessID( | |
86 int render_process_id) { | |
87 GlobalLookup& lookup = g_lookup.Get(); | |
Alpha Left Google
2012/11/20 21:49:43
Assert for threads.
| |
88 base::AutoLock guard(lookup.lock); | |
89 std::map<int, AudioRendererHost*>::const_iterator it = | |
90 lookup.by_process_id.find(render_process_id); | |
91 return (it != lookup.by_process_id.end()) ? it->second : NULL; | |
92 } | |
93 | |
51 /////////////////////////////////////////////////////////////////////////////// | 94 /////////////////////////////////////////////////////////////////////////////// |
52 // AudioRendererHost implementations. | 95 // AudioRendererHost implementations. |
53 AudioRendererHost::AudioRendererHost( | 96 AudioRendererHost::AudioRendererHost( |
97 int render_process_id, | |
54 media::AudioManager* audio_manager, MediaObserver* media_observer) | 98 media::AudioManager* audio_manager, MediaObserver* media_observer) |
55 : audio_manager_(audio_manager), | 99 : render_process_id_(render_process_id), |
100 audio_manager_(audio_manager), | |
56 media_observer_(media_observer) { | 101 media_observer_(media_observer) { |
102 RegisterAudioRendererHost(render_process_id_, this); | |
57 } | 103 } |
58 | 104 |
59 AudioRendererHost::~AudioRendererHost() { | 105 AudioRendererHost::~AudioRendererHost() { |
60 DCHECK(audio_entries_.empty()); | 106 DCHECK(audio_entries_.empty()); |
107 DCHECK(mirror_sessions_.empty()); | |
108 DeregisterAudioRendererHost(render_process_id_); | |
Alpha Left Google
2012/11/20 21:49:43
How does each |destination| know when to call Stop
miu
2012/11/21 08:27:48
AudioRendererHost is ref-counted. Each |destinati
| |
61 } | 109 } |
62 | 110 |
63 void AudioRendererHost::OnChannelClosing() { | 111 void AudioRendererHost::OnChannelClosing() { |
64 BrowserMessageFilter::OnChannelClosing(); | 112 BrowserMessageFilter::OnChannelClosing(); |
65 | 113 |
66 // Since the IPC channel is gone, close all requested audio streams. | 114 // Since the IPC channel is gone, close all requested audio streams. |
67 DeleteEntries(); | 115 DeleteEntries(); |
68 } | 116 } |
69 | 117 |
70 void AudioRendererHost::OnDestruct() const { | 118 void AudioRendererHost::OnDestruct() const { |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
207 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream) | 255 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream) |
208 IPC_MESSAGE_HANDLER(AudioHostMsg_FlushStream, OnFlushStream) | 256 IPC_MESSAGE_HANDLER(AudioHostMsg_FlushStream, OnFlushStream) |
209 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream) | 257 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream) |
210 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume) | 258 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume) |
211 IPC_MESSAGE_UNHANDLED(handled = false) | 259 IPC_MESSAGE_UNHANDLED(handled = false) |
212 IPC_END_MESSAGE_MAP_EX() | 260 IPC_END_MESSAGE_MAP_EX() |
213 | 261 |
214 return handled; | 262 return handled; |
215 } | 263 } |
216 | 264 |
265 void AudioRendererHost::StartMirroring( | |
266 int render_view_id, WebContentsAudioInputStream* destination) { | |
267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
268 DCHECK(destination); | |
269 | |
270 MirrorSessionMap::iterator session_it = mirror_sessions_.find(render_view_id); | |
271 if (session_it != mirror_sessions_.end()) { | |
272 DVLOG(1) << "Forcing StopMirroring(" << render_view_id | |
273 << ") on existing mirroring session (@" << session_it->second | |
274 << ")."; | |
275 StopMirroring(render_view_id, session_it->second); | |
Alpha Left Google
2012/11/20 21:49:43
Should this just be an error?
miu
2012/11/21 08:27:48
Justin and I talked about this. He believe the ex
| |
276 } | |
277 | |
278 DVLOG(1) << "StartMirroring RenderView " << render_view_id | |
279 << " --> WebContentsAudioInputStream@" << destination; | |
280 | |
281 mirror_sessions_.insert(std::make_pair(render_view_id, destination)); | |
282 for (AudioEntryMap::const_iterator it = audio_entries_.begin(); | |
283 it != audio_entries_.end(); ++it) { | |
284 if (it->second->render_view_id == render_view_id) { | |
285 it->second->diverted_stream.reset(it->second->controller->Divert()); | |
286 destination->AddAudioOutputStream(it->second->diverted_stream.get()); | |
287 } | |
288 } | |
289 } | |
290 | |
291 void AudioRendererHost::StopMirroring( | |
292 int render_view_id, WebContentsAudioInputStream* destination) { | |
293 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
294 | |
295 MirrorSessionMap::iterator session_it = mirror_sessions_.find(render_view_id); | |
296 if (session_it == mirror_sessions_.end() || | |
297 destination != session_it->second) { | |
298 return; | |
299 } | |
300 | |
301 DVLOG(1) << "StopMirroring RenderView " << render_view_id | |
302 << " --> WebContentsAudioInputStream@" << destination; | |
303 | |
304 for (AudioEntryMap::const_iterator it = audio_entries_.begin(); | |
305 it != audio_entries_.end(); ++it) { | |
306 if (it->second->render_view_id == render_view_id) { | |
307 destination->RemoveAudioOutputStream(it->second->diverted_stream.get()); | |
308 it->second->diverted_stream.reset(); | |
309 } | |
310 } | |
311 mirror_sessions_.erase(session_it); | |
312 } | |
313 | |
217 void AudioRendererHost::OnCreateStream( | 314 void AudioRendererHost::OnCreateStream( |
218 int stream_id, const media::AudioParameters& params, int input_channels) { | 315 int stream_id, const media::AudioParameters& params, int input_channels) { |
219 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
220 DCHECK(LookupById(stream_id) == NULL); | 317 DCHECK(LookupById(stream_id) == NULL); |
221 | 318 |
222 media::AudioParameters audio_params(params); | 319 media::AudioParameters audio_params(params); |
223 uint32 buffer_size = media::AudioBus::CalculateMemorySize(audio_params); | 320 uint32 buffer_size = media::AudioBus::CalculateMemorySize(audio_params); |
224 DCHECK_GT(buffer_size, 0U); | 321 DCHECK_GT(buffer_size, 0U); |
225 DCHECK_LE(buffer_size, | 322 DCHECK_LE(buffer_size, |
226 static_cast<uint32>(media::limits::kMaxPacketSizeInBytes)); | 323 static_cast<uint32>(media::limits::kMaxPacketSizeInBytes)); |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
274 } | 371 } |
275 | 372 |
276 // If we have created the controller successfully, create an entry and add it | 373 // If we have created the controller successfully, create an entry and add it |
277 // to the map. | 374 // to the map. |
278 entry->stream_id = stream_id; | 375 entry->stream_id = stream_id; |
279 audio_entries_.insert(std::make_pair(stream_id, entry.release())); | 376 audio_entries_.insert(std::make_pair(stream_id, entry.release())); |
280 if (media_observer_) | 377 if (media_observer_) |
281 media_observer_->OnSetAudioStreamStatus(this, stream_id, "created"); | 378 media_observer_->OnSetAudioStreamStatus(this, stream_id, "created"); |
282 } | 379 } |
283 | 380 |
381 void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id, | |
382 int render_view_id) { | |
383 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
384 | |
385 AudioEntry* const entry = LookupById(stream_id); | |
386 if (!entry) { | |
387 SendErrorMessage(stream_id); | |
388 return; | |
389 } | |
390 | |
391 if (entry->render_view_id == render_view_id) | |
392 return; // No change. | |
393 | |
394 MirrorSessionMap::const_iterator prev_session_it = | |
Alpha Left Google
2012/11/20 21:49:43
Comments to explain what this is doing.
miu
2012/11/21 08:27:48
Done.
| |
395 mirror_sessions_.find(entry->render_view_id); | |
396 scoped_ptr<media::DivertedAudioOutputStream> diverted_stream; | |
397 if (prev_session_it != mirror_sessions_.end()) { | |
398 prev_session_it->second-> | |
399 RemoveAudioOutputStream(entry->diverted_stream.get()); | |
400 diverted_stream.swap(entry->diverted_stream); | |
401 } | |
402 | |
403 entry->render_view_id = render_view_id; | |
404 | |
405 MirrorSessionMap::const_iterator next_session_it = | |
Alpha Left Google
2012/11/20 21:49:43
Would be helpful to explain what this is supposed
miu
2012/11/21 08:27:48
Done.
| |
406 mirror_sessions_.find(render_view_id); | |
407 if (next_session_it != mirror_sessions_.end()) { | |
408 if (diverted_stream) { | |
409 entry->diverted_stream.swap(diverted_stream); | |
410 } else { | |
411 entry->diverted_stream.reset(entry->controller->Divert()); | |
412 } | |
413 next_session_it->second->AddAudioOutputStream(entry->diverted_stream.get()); | |
414 } | |
415 } | |
416 | |
284 void AudioRendererHost::OnPlayStream(int stream_id) { | 417 void AudioRendererHost::OnPlayStream(int stream_id) { |
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
286 | 419 |
287 AudioEntry* entry = LookupById(stream_id); | 420 AudioEntry* entry = LookupById(stream_id); |
288 if (!entry) { | 421 if (!entry) { |
289 SendErrorMessage(stream_id); | 422 SendErrorMessage(stream_id); |
290 return; | 423 return; |
291 } | 424 } |
292 | 425 |
293 entry->controller->Play(); | 426 entry->controller->Play(); |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
363 for (AudioEntryMap::iterator i = audio_entries_.begin(); | 496 for (AudioEntryMap::iterator i = audio_entries_.begin(); |
364 i != audio_entries_.end(); ++i) { | 497 i != audio_entries_.end(); ++i) { |
365 CloseAndDeleteStream(i->second); | 498 CloseAndDeleteStream(i->second); |
366 } | 499 } |
367 } | 500 } |
368 | 501 |
369 void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) { | 502 void AudioRendererHost::CloseAndDeleteStream(AudioEntry* entry) { |
370 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 503 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
371 | 504 |
372 if (!entry->pending_close) { | 505 if (!entry->pending_close) { |
506 MirrorSessionMap::const_iterator session_it = | |
507 mirror_sessions_.find(entry->render_view_id); | |
508 if (session_it != mirror_sessions_.end()) { | |
509 session_it->second->RemoveAudioOutputStream(entry->diverted_stream.get()); | |
510 entry->diverted_stream.reset(); | |
511 } | |
373 entry->controller->Close( | 512 entry->controller->Close( |
374 base::Bind(&AudioRendererHost::DeleteEntry, this, entry)); | 513 base::Bind(&AudioRendererHost::DeleteEntry, this, entry)); |
375 entry->pending_close = true; | 514 entry->pending_close = true; |
376 } | 515 } |
377 } | 516 } |
378 | 517 |
379 void AudioRendererHost::DeleteEntry(AudioEntry* entry) { | 518 void AudioRendererHost::DeleteEntry(AudioEntry* entry) { |
380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 519 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
381 | 520 |
382 // Delete the entry when this method goes out of scope. | 521 // Delete the entry when this method goes out of scope. |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
425 return NULL; | 564 return NULL; |
426 } | 565 } |
427 | 566 |
428 media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting( | 567 media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting( |
429 int stream_id) { | 568 int stream_id) { |
430 AudioEntry* const entry = LookupById(stream_id); | 569 AudioEntry* const entry = LookupById(stream_id); |
431 return entry ? entry->controller : NULL; | 570 return entry ? entry->controller : NULL; |
432 } | 571 } |
433 | 572 |
434 } // namespace content | 573 } // namespace content |
OLD | NEW |