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/metrics/histogram.h" | 8 #include "base/metrics/histogram.h" |
9 #include "base/process.h" | 9 #include "base/process.h" |
10 #include "base/shared_memory.h" | 10 #include "base/shared_memory.h" |
(...skipping 12 matching lines...) Expand all Loading... | |
23 struct AudioRendererHost::AudioEntry { | 23 struct AudioRendererHost::AudioEntry { |
24 AudioEntry(); | 24 AudioEntry(); |
25 ~AudioEntry(); | 25 ~AudioEntry(); |
26 | 26 |
27 // The AudioOutputController that manages the audio stream. | 27 // The AudioOutputController that manages the audio stream. |
28 scoped_refptr<media::AudioOutputController> controller; | 28 scoped_refptr<media::AudioOutputController> controller; |
29 | 29 |
30 // The audio stream ID. | 30 // The audio stream ID. |
31 int stream_id; | 31 int stream_id; |
32 | 32 |
33 // The routing ID of the source render view. | |
34 int render_view_id; | |
35 | |
33 // Shared memory for transmission of the audio data. | 36 // Shared memory for transmission of the audio data. |
34 base::SharedMemory shared_memory; | 37 base::SharedMemory shared_memory; |
35 | 38 |
36 // The synchronous reader to be used by the controller. We have the | 39 // The synchronous reader to be used by the controller. We have the |
37 // ownership of the reader. | 40 // ownership of the reader. |
38 scoped_ptr<media::AudioOutputController::SyncReader> reader; | 41 scoped_ptr<media::AudioOutputController::SyncReader> reader; |
39 | 42 |
40 // Set to true after we called Close() for the controller. | 43 // Set to true after we called Close() for the controller. |
41 bool pending_close; | 44 bool pending_close; |
42 }; | 45 }; |
43 | 46 |
44 AudioRendererHost::AudioEntry::AudioEntry() | 47 AudioRendererHost::AudioEntry::AudioEntry() |
45 : stream_id(0), | 48 : stream_id(0), |
49 render_view_id(MSG_ROUTING_NONE), | |
46 pending_close(false) { | 50 pending_close(false) { |
47 } | 51 } |
48 | 52 |
49 AudioRendererHost::AudioEntry::~AudioEntry() {} | 53 AudioRendererHost::AudioEntry::~AudioEntry() {} |
50 | 54 |
55 AudioRendererHost::MirroringDestination::~MirroringDestination() {} | |
56 | |
51 /////////////////////////////////////////////////////////////////////////////// | 57 /////////////////////////////////////////////////////////////////////////////// |
52 // AudioRendererHost implementations. | 58 // AudioRendererHost implementations. |
53 AudioRendererHost::AudioRendererHost( | 59 AudioRendererHost::AudioRendererHost( |
60 int render_process_id, | |
54 media::AudioManager* audio_manager, MediaObserver* media_observer) | 61 media::AudioManager* audio_manager, MediaObserver* media_observer) |
55 : audio_manager_(audio_manager), | 62 : render_process_id_(render_process_id), |
63 audio_manager_(audio_manager), | |
56 media_observer_(media_observer) { | 64 media_observer_(media_observer) { |
57 } | 65 } |
58 | 66 |
59 AudioRendererHost::~AudioRendererHost() { | 67 AudioRendererHost::~AudioRendererHost() { |
60 DCHECK(audio_entries_.empty()); | 68 DCHECK(audio_entries_.empty()); |
69 DCHECK(mirror_sessions_.empty()); | |
70 } | |
71 | |
72 base::LazyInstance<AudioRendererHost::ActiveHostMap>::Leaky | |
73 AudioRendererHost::g_host_map_ = LAZY_INSTANCE_INITIALIZER; | |
tommi (sloooow) - chröme
2012/12/18 13:41:26
thinking out loud here... this map is only accesse
| |
74 | |
75 void AudioRendererHost::OnChannelConnected(int32 peer_pid) { | |
76 g_host_map_.Get().insert(std::make_pair(render_process_id_, this)); | |
77 | |
78 BrowserMessageFilter::OnChannelConnected(peer_pid); | |
61 } | 79 } |
62 | 80 |
63 void AudioRendererHost::OnChannelClosing() { | 81 void AudioRendererHost::OnChannelClosing() { |
64 BrowserMessageFilter::OnChannelClosing(); | 82 BrowserMessageFilter::OnChannelClosing(); |
65 | 83 |
66 // Since the IPC channel is gone, close all requested audio streams. | 84 // Since the IPC channel is gone, close all requested audio streams. |
67 DeleteEntries(); | 85 DeleteEntries(); |
86 | |
87 while (!mirror_sessions_.empty()) { | |
88 MirrorSessionMap::iterator it = mirror_sessions_.begin(); | |
89 DoStopMirroring(render_process_id_, it->first, it->second); | |
90 } | |
91 | |
92 g_host_map_.Get().erase(render_process_id_); | |
68 } | 93 } |
69 | 94 |
70 void AudioRendererHost::OnDestruct() const { | 95 void AudioRendererHost::OnDestruct() const { |
71 BrowserThread::DeleteOnIOThread::Destruct(this); | 96 BrowserThread::DeleteOnIOThread::Destruct(this); |
72 } | 97 } |
73 | 98 |
74 /////////////////////////////////////////////////////////////////////////////// | 99 /////////////////////////////////////////////////////////////////////////////// |
75 // media::AudioOutputController::EventHandler implementations. | 100 // media::AudioOutputController::EventHandler implementations. |
76 void AudioRendererHost::OnCreated(media::AudioOutputController* controller) { | 101 void AudioRendererHost::OnCreated(media::AudioOutputController* controller) { |
77 BrowserThread::PostTask( | 102 BrowserThread::PostTask( |
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
209 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream) | 234 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream, OnPauseStream) |
210 IPC_MESSAGE_HANDLER(AudioHostMsg_FlushStream, OnFlushStream) | 235 IPC_MESSAGE_HANDLER(AudioHostMsg_FlushStream, OnFlushStream) |
211 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream) | 236 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream, OnCloseStream) |
212 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume) | 237 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume, OnSetVolume) |
213 IPC_MESSAGE_UNHANDLED(handled = false) | 238 IPC_MESSAGE_UNHANDLED(handled = false) |
214 IPC_END_MESSAGE_MAP_EX() | 239 IPC_END_MESSAGE_MAP_EX() |
215 | 240 |
216 return handled; | 241 return handled; |
217 } | 242 } |
218 | 243 |
244 // static | |
245 AudioRendererHost* AudioRendererHost::FromRenderProcessID( | |
246 int render_process_id) { | |
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
248 | |
249 // Note: It's possible for look-ups to fail since, for example, an | |
250 // AudioRendererHost could be shutdown before StartMirroring() is called. | |
251 // It's common for mirroring sessions to target a different render | |
252 // process/view than the one that will receive the mirrored audio data. | |
253 // Example sequence of events: | |
254 // | |
255 // 1. RenderProcess_1 wants to mirror a tab within RenderProcess_2. | |
256 // 2. RenderProcess_1 starts sending IPCs to initiate a mirroring session. | |
257 // 3. In the meantime, RenderProcess_2 is shut down (e.g., tab closed). The | |
258 // AudioRendererHost for RenderProcess_2 is destroyed as a result. | |
259 // 4. A browser-side entity for RenderProcess_1 attempts to call | |
260 // StartMirroring(), but since the AudioRendererHost for RenderProcess_2 | |
261 // is gone, the look-up here fails. | |
262 | |
263 ActiveHostMap& host_map = g_host_map_.Get(); | |
264 ActiveHostMap::const_iterator it = host_map.find(render_process_id); | |
265 return it == host_map.end() ? NULL : it->second; | |
266 } | |
267 | |
268 // static | |
269 void AudioRendererHost::StartMirroring( | |
270 int render_process_id, int render_view_id, | |
271 const scoped_refptr<MirroringDestination>& destination) { | |
272 BrowserThread::PostTask( | |
273 BrowserThread::IO, FROM_HERE, | |
274 base::Bind(&AudioRendererHost::DoStartMirroring, | |
275 render_process_id, render_view_id, destination)); | |
276 } | |
277 | |
278 // static | |
279 void AudioRendererHost::DoStartMirroring( | |
280 int render_process_id, int render_view_id, | |
281 const scoped_refptr<MirroringDestination>& destination) { | |
282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
283 DCHECK(destination); | |
284 | |
285 AudioRendererHost* const host = FromRenderProcessID(render_process_id); | |
286 if (!host) { | |
tommi (sloooow) - chröme
2012/12/18 13:41:26
no {}
| |
287 return; // See comment in FromRenderProcessID(). | |
288 } | |
289 | |
290 // Insert an entry into the set of active mirroring sessions. If a mirroring | |
291 // session is already active for |render_process_id| + |render_view_id|, | |
292 // replace the entry. | |
293 MirrorSessionMap::iterator session_it = | |
294 host->mirror_sessions_.find(render_view_id); | |
295 scoped_refptr<MirroringDestination> old_destination; | |
296 if (session_it == host->mirror_sessions_.end()) { | |
297 host->mirror_sessions_.insert(std::make_pair(render_view_id, destination)); | |
298 | |
299 DVLOG(1) << "Start mirroring render_process_id:render_view_id=" | |
300 << render_process_id << ':' << render_view_id | |
301 << " --> MirroringDestination@" << destination; | |
302 } else { | |
303 old_destination = session_it->second; | |
304 session_it->second = destination; | |
305 | |
306 DVLOG(1) << "Switch mirroring of render_process_id:render_view_id=" | |
307 << render_process_id << ':' << render_view_id | |
308 << " MirroringDestination@" << old_destination | |
309 << " --> MirroringDestination@" << destination; | |
310 } | |
311 | |
312 // Divert any existing audio streams to |destination|. If streams were | |
313 // already diverted to the |old_destination|, remove them. | |
314 for (AudioEntryMap::const_iterator it = host->audio_entries_.begin(); | |
315 it != host->audio_entries_.end(); ++it) { | |
316 AudioEntry* const entry = it->second; | |
317 if (entry->render_view_id == render_view_id && !entry->pending_close) { | |
318 if (old_destination) | |
319 entry->controller->RevertDiversion(); | |
320 entry->controller->DivertToStream( | |
321 destination->AddInput(entry->controller->params())); | |
322 } | |
323 } | |
324 } | |
325 | |
326 // static | |
327 void AudioRendererHost::StopMirroring( | |
328 int render_process_id, int render_view_id, | |
329 const scoped_refptr<MirroringDestination>& destination) { | |
330 BrowserThread::PostTask( | |
331 BrowserThread::IO, FROM_HERE, | |
332 base::Bind(&AudioRendererHost::DoStopMirroring, | |
333 render_process_id, render_view_id, destination)); | |
334 } | |
335 | |
336 // static | |
337 void AudioRendererHost::DoStopMirroring( | |
338 int render_process_id, int render_view_id, | |
339 const scoped_refptr<MirroringDestination>& destination) { | |
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
341 | |
342 AudioRendererHost* const host = FromRenderProcessID(render_process_id); | |
343 if (!host) { | |
tommi (sloooow) - chröme
2012/12/18 13:41:26
no {}
| |
344 return; // See comment in FromRenderProcessID(). | |
345 } | |
346 | |
347 MirrorSessionMap::iterator session_it = | |
348 host->mirror_sessions_.find(render_view_id); | |
349 if (session_it == host->mirror_sessions_.end() || | |
350 destination != session_it->second) { | |
351 return; | |
352 } | |
353 | |
354 DVLOG(1) << "Stop mirroring render_process_id:render_view_id=" | |
355 << render_process_id << ':' << render_view_id | |
356 << " --> MirroringDestination@" << destination; | |
357 | |
358 // Revert the "divert" for each audio stream currently active in the mirroring | |
359 // session being stopped. | |
360 for (AudioEntryMap::const_iterator it = host->audio_entries_.begin(); | |
361 it != host->audio_entries_.end(); ++it) { | |
362 AudioEntry* const entry = it->second; | |
363 if (entry->render_view_id == render_view_id && !entry->pending_close) { | |
tommi (sloooow) - chröme
2012/12/18 13:41:26
same here (no {}) and throughout for single line i
| |
364 entry->controller->RevertDiversion(); | |
365 } | |
366 } | |
367 | |
368 host->mirror_sessions_.erase(session_it); | |
369 } | |
370 | |
219 void AudioRendererHost::OnCreateStream( | 371 void AudioRendererHost::OnCreateStream( |
220 int stream_id, const media::AudioParameters& params, int input_channels) { | 372 int stream_id, const media::AudioParameters& params, int input_channels) { |
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 373 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
222 DCHECK(LookupById(stream_id) == NULL); | 374 DCHECK(LookupById(stream_id) == NULL); |
223 | 375 |
224 media::AudioParameters audio_params(params); | 376 media::AudioParameters audio_params(params); |
225 uint32 buffer_size = media::AudioBus::CalculateMemorySize(audio_params); | 377 uint32 buffer_size = media::AudioBus::CalculateMemorySize(audio_params); |
226 DCHECK_GT(buffer_size, 0U); | 378 DCHECK_GT(buffer_size, 0U); |
227 DCHECK_LE(buffer_size, | 379 DCHECK_LE(buffer_size, |
228 static_cast<uint32>(media::limits::kMaxPacketSizeInBytes)); | 380 static_cast<uint32>(media::limits::kMaxPacketSizeInBytes)); |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
278 // If we have created the controller successfully, create an entry and add it | 430 // If we have created the controller successfully, create an entry and add it |
279 // to the map. | 431 // to the map. |
280 entry->stream_id = stream_id; | 432 entry->stream_id = stream_id; |
281 audio_entries_.insert(std::make_pair(stream_id, entry.release())); | 433 audio_entries_.insert(std::make_pair(stream_id, entry.release())); |
282 if (media_observer_) | 434 if (media_observer_) |
283 media_observer_->OnSetAudioStreamStatus(this, stream_id, "created"); | 435 media_observer_->OnSetAudioStreamStatus(this, stream_id, "created"); |
284 } | 436 } |
285 | 437 |
286 void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id, | 438 void AudioRendererHost::OnAssociateStreamWithProducer(int stream_id, |
287 int render_view_id) { | 439 int render_view_id) { |
288 // TODO(miu): Will use render_view_id in upcoming change. | 440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
289 DVLOG(1) << "AudioRendererHost@" << this | 441 |
290 << "::OnAssociateStreamWithProducer(stream_id=" << stream_id | 442 AudioEntry* const entry = LookupById(stream_id); |
291 << ", render_view_id=" << render_view_id << ")"; | 443 if (!entry) { |
444 SendErrorMessage(stream_id); | |
445 return; | |
446 } | |
447 | |
448 if (entry->render_view_id == render_view_id) | |
449 return; // No change. | |
450 | |
451 MirrorSessionMap::const_iterator prev_session_it = | |
452 mirror_sessions_.find(entry->render_view_id); | |
453 if (prev_session_it != mirror_sessions_.end()) { | |
454 entry->controller->RevertDiversion(); | |
455 } | |
456 | |
457 entry->render_view_id = render_view_id; | |
458 | |
459 MirrorSessionMap::const_iterator next_session_it = | |
460 mirror_sessions_.find(render_view_id); | |
461 if (next_session_it != mirror_sessions_.end()) { | |
462 entry->controller->DivertToStream( | |
463 next_session_it->second->AddInput(entry->controller->params())); | |
464 } | |
292 } | 465 } |
293 | 466 |
294 void AudioRendererHost::OnPlayStream(int stream_id) { | 467 void AudioRendererHost::OnPlayStream(int stream_id) { |
295 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | 468 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
296 | 469 |
297 AudioEntry* entry = LookupById(stream_id); | 470 AudioEntry* entry = LookupById(stream_id); |
298 if (!entry) { | 471 if (!entry) { |
299 SendErrorMessage(stream_id); | 472 SendErrorMessage(stream_id); |
300 return; | 473 return; |
301 } | 474 } |
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
435 return NULL; | 608 return NULL; |
436 } | 609 } |
437 | 610 |
438 media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting( | 611 media::AudioOutputController* AudioRendererHost::LookupControllerByIdForTesting( |
439 int stream_id) { | 612 int stream_id) { |
440 AudioEntry* const entry = LookupById(stream_id); | 613 AudioEntry* const entry = LookupById(stream_id); |
441 return entry ? entry->controller : NULL; | 614 return entry ? entry->controller : NULL; |
442 } | 615 } |
443 | 616 |
444 } // namespace content | 617 } // namespace content |
OLD | NEW |