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
3 // found in the LICENSE file.
5 #include "content/browser/renderer_host/media/audio_renderer_host.h"
8 #include "base/bind_helpers.h"
9 #include "base/logging.h"
10 #include "base/memory/shared_memory.h"
11 #include "base/metrics/histogram.h"
12 #include "base/process/process.h"
13 #include "content/browser/bad_message.h"
14 #include "content/browser/browser_main_loop.h"
15 #include "content/browser/child_process_security_policy_impl.h"
16 #include "content/browser/loader/resource_dispatcher_host_impl.h"
17 #include "content/browser/media/audio_stream_monitor.h"
18 #include "content/browser/media/capture/audio_mirroring_manager.h"
19 #include "content/browser/media/media_internals.h"
20 #include "content/browser/renderer_host/media/audio_input_device_manager.h"
21 #include "content/browser/renderer_host/media/audio_sync_reader.h"
22 #include "content/browser/renderer_host/media/media_stream_manager.h"
23 #include "content/browser/renderer_host/media/media_stream_ui_proxy.h"
24 #include "content/browser/renderer_host/render_widget_host_impl.h"
25 #include "content/common/media/audio_messages.h"
26 #include "content/public/browser/content_browser_client.h"
27 #include "content/public/browser/media_device_id.h"
28 #include "content/public/browser/media_observer.h"
29 #include "content/public/browser/render_frame_host.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "content/public/common/content_switches.h"
32 #include "media/audio/audio_device_name.h"
33 #include "media/audio/audio_manager_base.h"
34 #include "media/base/audio_bus.h"
35 #include "media/base/limits.h"
37 using media::AudioBus
;
38 using media::AudioManager
;
43 // TODO(aiolos): This is a temporary hack until the resource scheduler is
44 // migrated to RenderFrames for the Site Isolation project. It's called in
45 // response to low frequency playback state changes. http://crbug.com/472869
46 int RenderFrameIdToRenderViewId(int render_process_id
, int render_frame_id
) {
47 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
48 RenderFrameHost
* const frame
=
49 RenderFrameHost::FromID(render_process_id
, render_frame_id
);
50 return frame
? frame
->GetRenderViewHost()->GetRoutingID() : MSG_ROUTING_NONE
;
53 void NotifyResourceDispatcherOfAudioStateChange(int render_process_id
,
56 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
57 if (render_view_id
== MSG_ROUTING_NONE
|| !ResourceDispatcherHostImpl::Get())
60 ResourceDispatcherHostImpl::Get()->OnAudioRenderHostStreamStateChanged(
61 render_process_id
, render_view_id
, is_playing
);
66 class AudioRendererHost::AudioEntry
67 : public media::AudioOutputController::EventHandler
{
69 AudioEntry(AudioRendererHost
* host
,
72 const media::AudioParameters
& params
,
73 const std::string
& output_device_id
,
74 scoped_ptr
<base::SharedMemory
> shared_memory
,
75 scoped_ptr
<media::AudioOutputController::SyncReader
> reader
);
76 ~AudioEntry() override
;
78 int stream_id() const {
82 int render_frame_id() const { return render_frame_id_
; }
84 media::AudioOutputController
* controller() const { return controller_
.get(); }
86 base::SharedMemory
* shared_memory() {
87 return shared_memory_
.get();
90 media::AudioOutputController::SyncReader
* reader() const {
94 bool playing() const { return playing_
; }
95 void set_playing(bool playing
) { playing_
= playing
; }
98 // media::AudioOutputController::EventHandler implementation.
99 void OnCreated() override
;
100 void OnPlaying() override
;
101 void OnPaused() override
;
102 void OnError() override
;
104 AudioRendererHost
* const host_
;
105 const int stream_id_
;
107 // The routing ID of the source RenderFrame.
108 const int render_frame_id_
;
110 // Shared memory for transmission of the audio data. Used by |reader_|.
111 const scoped_ptr
<base::SharedMemory
> shared_memory_
;
113 // The synchronous reader to be used by |controller_|.
114 const scoped_ptr
<media::AudioOutputController::SyncReader
> reader_
;
116 // The AudioOutputController that manages the audio stream.
117 const scoped_refptr
<media::AudioOutputController
> controller_
;
122 AudioRendererHost::AudioEntry::AudioEntry(
123 AudioRendererHost
* host
,
126 const media::AudioParameters
& params
,
127 const std::string
& output_device_id
,
128 scoped_ptr
<base::SharedMemory
> shared_memory
,
129 scoped_ptr
<media::AudioOutputController::SyncReader
> reader
)
131 stream_id_(stream_id
),
132 render_frame_id_(render_frame_id
),
133 shared_memory_(shared_memory
.Pass()),
134 reader_(reader
.Pass()),
135 controller_(media::AudioOutputController::Create(host
->audio_manager_
,
141 DCHECK(controller_
.get());
144 AudioRendererHost::AudioEntry::~AudioEntry() {}
146 ///////////////////////////////////////////////////////////////////////////////
147 // AudioRendererHost implementations.
149 AudioRendererHost::AudioRendererHost(
150 int render_process_id
,
151 media::AudioManager
* audio_manager
,
152 AudioMirroringManager
* mirroring_manager
,
153 MediaInternals
* media_internals
,
154 MediaStreamManager
* media_stream_manager
,
155 const ResourceContext::SaltCallback
& salt_callback
)
156 : BrowserMessageFilter(AudioMsgStart
),
157 render_process_id_(render_process_id
),
158 audio_manager_(audio_manager
),
159 mirroring_manager_(mirroring_manager
),
160 audio_log_(media_internals
->CreateAudioLog(
161 media::AudioLogFactory::AUDIO_OUTPUT_CONTROLLER
)),
162 media_stream_manager_(media_stream_manager
),
163 num_playing_streams_(0),
164 salt_callback_(salt_callback
) {
165 DCHECK(audio_manager_
);
166 DCHECK(media_stream_manager_
);
169 AudioRendererHost::~AudioRendererHost() {
170 DCHECK(audio_entries_
.empty());
173 void AudioRendererHost::GetOutputControllers(
174 const RenderProcessHost::GetAudioOutputControllersCallback
&
176 BrowserThread::PostTaskAndReplyWithResult(
177 BrowserThread::IO
, FROM_HERE
,
178 base::Bind(&AudioRendererHost::DoGetOutputControllers
, this), callback
);
181 void AudioRendererHost::OnChannelClosing() {
182 // Since the IPC sender is gone, close all requested audio streams.
183 while (!audio_entries_
.empty()) {
184 // Note: OnCloseStream() removes the entries from audio_entries_.
185 OnCloseStream(audio_entries_
.begin()->first
);
189 void AudioRendererHost::OnDestruct() const {
190 BrowserThread::DeleteOnIOThread::Destruct(this);
193 void AudioRendererHost::AudioEntry::OnCreated() {
194 BrowserThread::PostTask(
197 base::Bind(&AudioRendererHost::DoCompleteCreation
, host_
, stream_id_
));
200 void AudioRendererHost::AudioEntry::OnPlaying() {
201 BrowserThread::PostTask(
204 base::Bind(&AudioRendererHost::DoNotifyStreamStateChanged
,
210 void AudioRendererHost::AudioEntry::OnPaused() {
211 BrowserThread::PostTask(
214 base::Bind(&AudioRendererHost::DoNotifyStreamStateChanged
,
220 void AudioRendererHost::AudioEntry::OnError() {
221 BrowserThread::PostTask(
224 base::Bind(&AudioRendererHost::ReportErrorAndClose
, host_
, stream_id_
));
227 void AudioRendererHost::DoCompleteCreation(int stream_id
) {
228 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
231 DLOG(WARNING
) << "Renderer process handle is invalid.";
232 ReportErrorAndClose(stream_id
);
236 AudioEntry
* const entry
= LookupById(stream_id
);
238 ReportErrorAndClose(stream_id
);
242 // Once the audio stream is created then complete the creation process by
243 // mapping shared memory and sharing with the renderer process.
244 base::SharedMemoryHandle foreign_memory_handle
;
245 if (!entry
->shared_memory()->ShareToProcess(PeerHandle(),
246 &foreign_memory_handle
)) {
247 // If we failed to map and share the shared memory then close the audio
248 // stream and send an error message.
249 ReportErrorAndClose(entry
->stream_id());
253 AudioSyncReader
* reader
= static_cast<AudioSyncReader
*>(entry
->reader());
255 base::SyncSocket::TransitDescriptor socket_descriptor
;
257 // If we failed to prepare the sync socket for the renderer then we fail
258 // the construction of audio stream.
259 if (!reader
->PrepareForeignSocket(PeerHandle(), &socket_descriptor
)) {
260 ReportErrorAndClose(entry
->stream_id());
264 Send(new AudioMsg_NotifyStreamCreated(
265 entry
->stream_id(), foreign_memory_handle
, socket_descriptor
,
266 entry
->shared_memory()->requested_size()));
269 void AudioRendererHost::DoNotifyStreamStateChanged(int stream_id
,
271 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
273 AudioEntry
* const entry
= LookupById(stream_id
);
277 Send(new AudioMsg_NotifyStreamStateChanged(
279 is_playing
? media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_PLAYING
280 : media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_PAUSED
));
283 AudioStreamMonitor::StartMonitoringStream(
285 entry
->render_frame_id(),
287 base::Bind(&media::AudioOutputController::ReadCurrentPowerAndClip
,
288 entry
->controller()));
290 AudioStreamMonitor::StopMonitoringStream(
291 render_process_id_
, entry
->render_frame_id(), entry
->stream_id());
293 UpdateNumPlayingStreams(entry
, is_playing
);
296 RenderProcessHost::AudioOutputControllerList
297 AudioRendererHost::DoGetOutputControllers() const {
298 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
300 RenderProcessHost::AudioOutputControllerList controllers
;
301 for (AudioEntryMap::const_iterator it
= audio_entries_
.begin();
302 it
!= audio_entries_
.end();
304 controllers
.push_back(it
->second
->controller());
310 ///////////////////////////////////////////////////////////////////////////////
311 // IPC Messages handler
312 bool AudioRendererHost::OnMessageReceived(const IPC::Message
& message
) {
314 IPC_BEGIN_MESSAGE_MAP(AudioRendererHost
, message
)
315 IPC_MESSAGE_HANDLER(AudioHostMsg_CreateStream
, OnCreateStream
)
316 IPC_MESSAGE_HANDLER(AudioHostMsg_PlayStream
, OnPlayStream
)
317 IPC_MESSAGE_HANDLER(AudioHostMsg_PauseStream
, OnPauseStream
)
318 IPC_MESSAGE_HANDLER(AudioHostMsg_CloseStream
, OnCloseStream
)
319 IPC_MESSAGE_HANDLER(AudioHostMsg_SetVolume
, OnSetVolume
)
320 IPC_MESSAGE_HANDLER(AudioHostMsg_SwitchOutputDevice
, OnSwitchOutputDevice
)
321 IPC_MESSAGE_UNHANDLED(handled
= false)
322 IPC_END_MESSAGE_MAP()
327 void AudioRendererHost::OnCreateStream(int stream_id
,
330 const media::AudioParameters
& params
) {
331 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
333 DVLOG(1) << "AudioRendererHost@" << this
334 << "::OnCreateStream(stream_id=" << stream_id
335 << ", render_frame_id=" << render_frame_id
336 << ", session_id=" << session_id
<< ")";
337 DCHECK_GT(render_frame_id
, 0);
339 // media::AudioParameters is validated in the deserializer.
340 if (LookupById(stream_id
) != NULL
) {
341 SendErrorMessage(stream_id
);
345 // Initialize the |output_device_id| to an empty string which indicates that
346 // the default device should be used. If a StreamDeviceInfo instance was found
347 // though, then we use the matched output device.
348 std::string output_device_id
;
349 const StreamDeviceInfo
* info
= media_stream_manager_
->
350 audio_input_device_manager()->GetOpenedDeviceInfoById(session_id
);
352 output_device_id
= info
->device
.matched_output_device_id
;
354 // Create the shared memory and share with the renderer process.
355 uint32 shared_memory_size
= AudioBus::CalculateMemorySize(params
);
356 scoped_ptr
<base::SharedMemory
> shared_memory(new base::SharedMemory());
357 if (!shared_memory
->CreateAndMapAnonymous(shared_memory_size
)) {
358 SendErrorMessage(stream_id
);
362 scoped_ptr
<AudioSyncReader
> reader(
363 new AudioSyncReader(shared_memory
.get(), params
));
364 if (!reader
->Init()) {
365 SendErrorMessage(stream_id
);
369 MediaObserver
* const media_observer
=
370 GetContentClient()->browser()->GetMediaObserver();
372 media_observer
->OnCreatingAudioStream(render_process_id_
, render_frame_id
);
374 scoped_ptr
<AudioEntry
> entry(new AudioEntry(this,
379 shared_memory
.Pass(),
381 if (mirroring_manager_
) {
382 mirroring_manager_
->AddDiverter(
383 render_process_id_
, entry
->render_frame_id(), entry
->controller());
385 audio_entries_
.insert(std::make_pair(stream_id
, entry
.release()));
386 audio_log_
->OnCreated(stream_id
, params
, output_device_id
);
387 MediaInternals::GetInstance()->SetWebContentsTitleForAudioLogEntry(
388 stream_id
, render_process_id_
, render_frame_id
, audio_log_
.get());
391 void AudioRendererHost::OnPlayStream(int stream_id
) {
392 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
394 AudioEntry
* entry
= LookupById(stream_id
);
396 SendErrorMessage(stream_id
);
400 entry
->controller()->Play();
401 audio_log_
->OnStarted(stream_id
);
404 void AudioRendererHost::OnPauseStream(int stream_id
) {
405 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
407 AudioEntry
* entry
= LookupById(stream_id
);
409 SendErrorMessage(stream_id
);
413 entry
->controller()->Pause();
414 audio_log_
->OnStopped(stream_id
);
417 void AudioRendererHost::OnSetVolume(int stream_id
, double volume
) {
418 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
420 AudioEntry
* entry
= LookupById(stream_id
);
422 SendErrorMessage(stream_id
);
426 // Make sure the volume is valid.
427 if (volume
< 0 || volume
> 1.0)
429 entry
->controller()->SetVolume(volume
);
430 audio_log_
->OnSetVolume(stream_id
, volume
);
433 void AudioRendererHost::OnSwitchOutputDevice(int stream_id
,
435 const std::string
& device_id
,
436 const GURL
& security_origin
,
438 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
439 DVLOG(1) << "AudioRendererHost@" << this
440 << "::OnSwitchOutputDevice(stream_id=" << stream_id
441 << ", render_frame_id=" << render_frame_id
442 << ", device_id=" << device_id
443 << ", security_origin=" << security_origin
444 << ", request_id=" << request_id
<< ")";
445 if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanRequestURL(
446 render_process_id_
, security_origin
)) {
447 content::bad_message::ReceivedBadMessage(this,
448 bad_message::ARH_UNAUTHORIZED_URL
);
452 if (device_id
.empty()) {
453 DVLOG(1) << __FUNCTION__
<< ": default output device requested. "
454 << "No permissions check or device translation/validation needed.";
455 DoSwitchOutputDevice(stream_id
, device_id
, request_id
);
457 // Check that MediaStream device permissions have been granted,
458 // hence the use of a MediaStreamUIProxy.
459 scoped_ptr
<MediaStreamUIProxy
> ui_proxy
= MediaStreamUIProxy::Create();
461 // Use MEDIA_DEVICE_AUDIO_CAPTURE instead of MEDIA_DEVICE_AUDIO_OUTPUT
462 // because MediaStreamUIProxy::CheckAccess does not currently support
463 // MEDIA_DEVICE_AUDIO_OUTPUT.
464 // TODO(guidou): Change to MEDIA_DEVICE_AUDIO_OUTPUT when support becomes
465 // available. http://crbug.com/498675
466 ui_proxy
->CheckAccess(
467 security_origin
, MEDIA_DEVICE_AUDIO_CAPTURE
,
468 render_process_id_
, render_frame_id
,
469 base::Bind(&AudioRendererHost::OutputDeviceAccessChecked
, this,
470 base::Passed(&ui_proxy
), stream_id
, device_id
,
471 security_origin
, render_frame_id
, request_id
));
475 void AudioRendererHost::OutputDeviceAccessChecked(
476 scoped_ptr
<MediaStreamUIProxy
> ui_proxy
,
478 const std::string
& device_id
,
479 const GURL
& security_origin
,
483 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
484 DVLOG(1) << __FUNCTION__
;
486 DVLOG(0) << __FUNCTION__
487 << ": Have no access to media devices. Not switching device.";
488 Send(new AudioMsg_NotifyOutputDeviceSwitched(
489 stream_id
, request_id
,
490 media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_AUTHORIZED
));
494 scoped_refptr
<base::SingleThreadTaskRunner
> audio_worker_runner
=
495 AudioManager::Get()->GetWorkerTaskRunner();
496 audio_worker_runner
->PostTask(
498 base::Bind(&AudioRendererHost::StartTranslateOutputDeviceName
, this,
499 stream_id
, device_id
, security_origin
, request_id
));
502 void AudioRendererHost::StartTranslateOutputDeviceName(
504 const std::string
& device_id
,
505 const GURL
& security_origin
,
507 DCHECK(AudioManager::Get()->GetWorkerTaskRunner()->BelongsToCurrentThread());
508 DCHECK(!device_id
.empty());
509 DVLOG(1) << __FUNCTION__
;
511 media::AudioDeviceNames
* device_names(new media::AudioDeviceNames
);
512 AudioManager::Get()->GetAudioOutputDeviceNames(device_names
);
514 BrowserThread::PostTask(
515 BrowserThread::IO
, FROM_HERE
,
516 base::Bind(&AudioRendererHost::FinishTranslateOutputDeviceName
, this,
517 stream_id
, device_id
, security_origin
, request_id
,
518 base::Owned(device_names
)));
521 void AudioRendererHost::FinishTranslateOutputDeviceName(
523 const std::string
& device_id
,
524 const GURL
& security_origin
,
526 media::AudioDeviceNames
* device_names
) {
527 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
528 DCHECK(!device_id
.empty());
529 DVLOG(1) << __FUNCTION__
;
531 std::string raw_device_id
;
532 // Process the enumeration here because |salt_callback_| can run
533 // only on the IO thread
534 for (const auto& device_name
: *device_names
) {
535 const std::string candidate_device_id
= content::GetHMACForMediaDeviceID(
536 salt_callback_
, security_origin
, device_name
.unique_id
);
537 if (candidate_device_id
== device_id
) {
538 DVLOG(1) << "Requested device " << device_name
.unique_id
<< " - "
539 << device_name
.device_name
;
540 raw_device_id
= device_name
.unique_id
;
544 if (raw_device_id
.empty()) {
545 DVLOG(1) << "Requested device " << device_id
<< " could not be found.";
546 Send(new AudioMsg_NotifyOutputDeviceSwitched(
547 stream_id
, request_id
,
548 media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_NOT_FOUND
));
552 DoSwitchOutputDevice(stream_id
, raw_device_id
, request_id
);
555 void AudioRendererHost::DoSwitchOutputDevice(int stream_id
,
556 const std::string
& raw_device_id
,
558 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
559 DVLOG(1) << __FUNCTION__
<< "(" << stream_id
<< ", " << raw_device_id
<< ", "
560 << request_id
<< ")";
561 AudioEntry
* entry
= LookupById(stream_id
);
563 Send(new AudioMsg_NotifyOutputDeviceSwitched(
564 stream_id
, request_id
,
565 media::SWITCH_OUTPUT_DEVICE_RESULT_ERROR_OBSOLETE
));
569 entry
->controller()->SwitchOutputDevice(
570 raw_device_id
, base::Bind(&AudioRendererHost::DoOutputDeviceSwitched
,
571 this, stream_id
, request_id
));
572 audio_log_
->OnSwitchOutputDevice(entry
->stream_id(), raw_device_id
);
575 void AudioRendererHost::DoOutputDeviceSwitched(int stream_id
, int request_id
) {
576 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
577 DVLOG(1) << __FUNCTION__
<< "(" << stream_id
<< ", " << request_id
<< ")";
578 Send(new AudioMsg_NotifyOutputDeviceSwitched(
579 stream_id
, request_id
, media::SWITCH_OUTPUT_DEVICE_RESULT_SUCCESS
));
582 void AudioRendererHost::SendErrorMessage(int stream_id
) {
583 Send(new AudioMsg_NotifyStreamStateChanged(
584 stream_id
, media::AUDIO_OUTPUT_IPC_DELEGATE_STATE_ERROR
));
587 void AudioRendererHost::OnCloseStream(int stream_id
) {
588 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
590 // Prevent oustanding callbacks from attempting to close/delete the same
592 AudioEntryMap::iterator i
= audio_entries_
.find(stream_id
);
593 if (i
== audio_entries_
.end())
595 scoped_ptr
<AudioEntry
> entry(i
->second
);
596 audio_entries_
.erase(i
);
598 media::AudioOutputController
* const controller
= entry
->controller();
600 base::Bind(&AudioRendererHost::DeleteEntry
, this, base::Passed(&entry
)));
601 audio_log_
->OnClosed(stream_id
);
604 void AudioRendererHost::DeleteEntry(scoped_ptr
<AudioEntry
> entry
) {
605 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
607 // De-register the controller from the AudioMirroringManager now that the
608 // controller has closed the AudioOutputStream and shut itself down. This
609 // ensures that calling RemoveDiverter() here won't trigger the controller to
610 // re-start the default AudioOutputStream and cause a brief audio blip to come
611 // out the user's speakers. http://crbug.com/474432
612 if (mirroring_manager_
)
613 mirroring_manager_
->RemoveDiverter(entry
->controller());
615 AudioStreamMonitor::StopMonitoringStream(
616 render_process_id_
, entry
->render_frame_id(), entry
->stream_id());
617 UpdateNumPlayingStreams(entry
.get(), false);
620 void AudioRendererHost::ReportErrorAndClose(int stream_id
) {
621 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
623 // Make sure this isn't a stray callback executing after the stream has been
624 // closed, so error notifications aren't sent after clients believe the stream
626 if (!LookupById(stream_id
))
629 SendErrorMessage(stream_id
);
631 audio_log_
->OnError(stream_id
);
632 OnCloseStream(stream_id
);
635 AudioRendererHost::AudioEntry
* AudioRendererHost::LookupById(int stream_id
) {
636 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
638 AudioEntryMap::const_iterator i
= audio_entries_
.find(stream_id
);
639 return i
!= audio_entries_
.end() ? i
->second
: NULL
;
642 void AudioRendererHost::UpdateNumPlayingStreams(AudioEntry
* entry
,
644 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
645 if (entry
->playing() == is_playing
)
648 bool should_alert_resource_scheduler
;
650 should_alert_resource_scheduler
=
651 !RenderFrameHasActiveAudio(entry
->render_frame_id());
652 entry
->set_playing(true);
653 base::AtomicRefCountInc(&num_playing_streams_
);
655 entry
->set_playing(false);
656 should_alert_resource_scheduler
=
657 !RenderFrameHasActiveAudio(entry
->render_frame_id());
658 base::AtomicRefCountDec(&num_playing_streams_
);
661 if (should_alert_resource_scheduler
&& ResourceDispatcherHostImpl::Get()) {
662 BrowserThread::PostTaskAndReplyWithResult(
663 BrowserThread::UI
, FROM_HERE
,
664 base::Bind(&RenderFrameIdToRenderViewId
, render_process_id_
,
665 entry
->render_frame_id()),
666 base::Bind(&NotifyResourceDispatcherOfAudioStateChange
,
667 render_process_id_
, is_playing
));
671 bool AudioRendererHost::HasActiveAudio() {
672 return !base::AtomicRefCountIsZero(&num_playing_streams_
);
675 bool AudioRendererHost::RenderFrameHasActiveAudio(int render_frame_id
) const {
676 for (AudioEntryMap::const_iterator it
= audio_entries_
.begin();
677 it
!= audio_entries_
.end();
679 AudioEntry
* entry
= it
->second
;
680 if (entry
->render_frame_id() == render_frame_id
&& entry
->playing())
686 } // namespace content