1 // Copyright 2014 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/media/audio_stream_monitor.h"
8 #include "base/bind_helpers.h"
9 #include "content/browser/web_contents/web_contents_impl.h"
10 #include "content/public/browser/browser_thread.h"
11 #include "content/public/browser/invalidate_type.h"
12 #include "content/public/browser/render_frame_host.h"
18 AudioStreamMonitor
* AudioStreamMonitorFromRenderFrame(int render_process_id
,
19 int render_frame_id
) {
20 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
21 WebContentsImpl
* const web_contents
=
22 static_cast<WebContentsImpl
*>(WebContents::FromRenderFrameHost(
23 RenderFrameHost::FromID(render_process_id
, render_frame_id
)));
28 AudioStateProvider
* audio_provider
= web_contents
->audio_state_provider();
29 return audio_provider
? audio_provider
->audio_stream_monitor() : nullptr;
34 AudioStreamMonitor::AudioStreamMonitor(WebContents
* contents
)
35 : AudioStateProvider(contents
),
36 clock_(&default_tick_clock_
)
40 AudioStreamMonitor::~AudioStreamMonitor() {}
42 bool AudioStreamMonitor::IsAudioStateAvailable() const {
43 return media::AudioOutputController::will_monitor_audio_levels();
46 // This provider is the monitor.
47 AudioStreamMonitor
* AudioStreamMonitor::audio_stream_monitor() {
51 bool AudioStreamMonitor::WasRecentlyAudible() const {
52 DCHECK(thread_checker_
.CalledOnValidThread());
53 return AudioStateProvider::WasRecentlyAudible();
57 void AudioStreamMonitor::StartMonitoringStream(
58 int render_process_id
,
61 const ReadPowerAndClipCallback
& read_power_callback
) {
62 if (!media::AudioOutputController::will_monitor_audio_levels())
64 BrowserThread::PostTask(BrowserThread::UI
,
66 base::Bind(&StartMonitoringHelper
,
70 read_power_callback
));
74 void AudioStreamMonitor::StopMonitoringStream(int render_process_id
,
77 if (!media::AudioOutputController::will_monitor_audio_levels())
79 BrowserThread::PostTask(BrowserThread::UI
,
81 base::Bind(&StopMonitoringHelper
,
88 void AudioStreamMonitor::StartMonitoringHelper(
89 int render_process_id
,
92 const ReadPowerAndClipCallback
& read_power_callback
) {
93 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
94 AudioStreamMonitor
* const monitor
=
95 AudioStreamMonitorFromRenderFrame(render_process_id
, render_frame_id
);
97 monitor
->StartMonitoringStreamOnUIThread(
98 render_process_id
, stream_id
, read_power_callback
);
103 void AudioStreamMonitor::StopMonitoringHelper(int render_process_id
,
106 DCHECK_CURRENTLY_ON(BrowserThread::UI
);
107 AudioStreamMonitor
* const monitor
=
108 AudioStreamMonitorFromRenderFrame(render_process_id
, render_frame_id
);
110 monitor
->StopMonitoringStreamOnUIThread(render_process_id
, stream_id
);
113 void AudioStreamMonitor::StartMonitoringStreamOnUIThread(
114 int render_process_id
,
116 const ReadPowerAndClipCallback
& read_power_callback
) {
117 DCHECK(thread_checker_
.CalledOnValidThread());
118 DCHECK(!read_power_callback
.is_null());
119 poll_callbacks_
[StreamID(render_process_id
, stream_id
)] = read_power_callback
;
120 if (!poll_timer_
.IsRunning()) {
123 base::TimeDelta::FromSeconds(1) /
124 static_cast<int>(kPowerMeasurementsPerSecond
),
125 base::Bind(&AudioStreamMonitor::Poll
, base::Unretained(this)));
129 void AudioStreamMonitor::StopMonitoringStreamOnUIThread(int render_process_id
,
131 DCHECK(thread_checker_
.CalledOnValidThread());
132 poll_callbacks_
.erase(StreamID(render_process_id
, stream_id
));
133 if (poll_callbacks_
.empty())
137 void AudioStreamMonitor::Poll() {
138 for (StreamPollCallbackMap::const_iterator it
= poll_callbacks_
.begin();
139 it
!= poll_callbacks_
.end();
141 // TODO(miu): A new UI for delivering specific power level and clipping
142 // information is still in the works. For now, we throw away all
143 // information except for "is it audible?"
144 const float power_dbfs
= it
->second
.Run().first
;
145 const float kSilenceThresholdDBFS
= -72.24719896f
;
146 if (power_dbfs
>= kSilenceThresholdDBFS
) {
147 last_blurt_time_
= clock_
->NowTicks();
149 break; // No need to poll remaining streams.
154 void AudioStreamMonitor::MaybeToggle() {
155 const base::TimeTicks off_time
=
156 last_blurt_time_
+ base::TimeDelta::FromMilliseconds(kHoldOnMilliseconds
);
157 const base::TimeTicks now
= clock_
->NowTicks();
158 const bool should_indicator_be_on
= now
< off_time
;
160 Notify(should_indicator_be_on
);
162 if (!should_indicator_be_on
) {
164 } else if (!off_timer_
.IsRunning()) {
168 base::Bind(&AudioStreamMonitor::MaybeToggle
, base::Unretained(this)));
172 } // namespace content