blob: ba86d56a8f05f5b8c1ba9bc5d8e3f581b7d3f96a [file] [log] [blame]
dalecurtisbc6572e12014-09-12 19:22:301// 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.
4
5#include "content/browser/media/audio_stream_monitor.h"
6
7#include "base/bind.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"
13
14namespace content {
15
16namespace {
17
18AudioStreamMonitor* 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)));
timavf6f6f692015-03-04 02:19:3724
avayvodcc85bbd2015-08-28 19:11:1525 return web_contents ? web_contents->audio_stream_monitor() : nullptr;
dalecurtisbc6572e12014-09-12 19:22:3026}
27
28} // namespace
29
30AudioStreamMonitor::AudioStreamMonitor(WebContents* contents)
avayvodcc85bbd2015-08-28 19:11:1531 : web_contents_(contents),
32 clock_(&default_tick_clock_),
mlamouri44e4ef42016-01-20 18:42:2733 was_recently_audible_(false),
altiminec87fd1f2016-11-17 20:22:4934 is_audible_(false),
35 active_streams_(0) {
avayvodcc85bbd2015-08-28 19:11:1536 DCHECK(web_contents_);
dalecurtisbc6572e12014-09-12 19:22:3037}
38
39AudioStreamMonitor::~AudioStreamMonitor() {}
40
41bool AudioStreamMonitor::WasRecentlyAudible() const {
42 DCHECK(thread_checker_.CalledOnValidThread());
avayvodcc85bbd2015-08-28 19:11:1543 return was_recently_audible_;
dalecurtisbc6572e12014-09-12 19:22:3044}
45
mlamouri44e4ef42016-01-20 18:42:2746bool AudioStreamMonitor::IsCurrentlyAudible() const {
47 DCHECK(thread_checker_.CalledOnValidThread());
48 return is_audible_;
49}
50
dalecurtisbc6572e12014-09-12 19:22:3051// static
52void AudioStreamMonitor::StartMonitoringStream(
53 int render_process_id,
54 int render_frame_id,
55 int stream_id,
56 const ReadPowerAndClipCallback& read_power_callback) {
dalecurtisbc6572e12014-09-12 19:22:3057 BrowserThread::PostTask(BrowserThread::UI,
58 FROM_HERE,
59 base::Bind(&StartMonitoringHelper,
60 render_process_id,
61 render_frame_id,
62 stream_id,
63 read_power_callback));
64}
65
66// static
67void AudioStreamMonitor::StopMonitoringStream(int render_process_id,
68 int render_frame_id,
69 int stream_id) {
dalecurtisbc6572e12014-09-12 19:22:3070 BrowserThread::PostTask(BrowserThread::UI,
71 FROM_HERE,
72 base::Bind(&StopMonitoringHelper,
73 render_process_id,
74 render_frame_id,
75 stream_id));
76}
77
78// static
79void AudioStreamMonitor::StartMonitoringHelper(
80 int render_process_id,
81 int render_frame_id,
82 int stream_id,
83 const ReadPowerAndClipCallback& read_power_callback) {
84 DCHECK_CURRENTLY_ON(BrowserThread::UI);
85 AudioStreamMonitor* const monitor =
86 AudioStreamMonitorFromRenderFrame(render_process_id, render_frame_id);
altiminec87fd1f2016-11-17 20:22:4987 if (!monitor)
88 return;
89
90 monitor->OnStreamAdded();
91
92 if (!power_level_monitoring_available())
93 return;
94
95 monitor->StartMonitoringStreamOnUIThread(render_process_id, stream_id,
96 read_power_callback);
dalecurtisbc6572e12014-09-12 19:22:3097}
98
99// static
100void AudioStreamMonitor::StopMonitoringHelper(int render_process_id,
101 int render_frame_id,
102 int stream_id) {
103 DCHECK_CURRENTLY_ON(BrowserThread::UI);
104 AudioStreamMonitor* const monitor =
105 AudioStreamMonitorFromRenderFrame(render_process_id, render_frame_id);
altiminec87fd1f2016-11-17 20:22:49106 if (!monitor)
107 return;
108
109 monitor->OnStreamRemoved();
110
111 if (!power_level_monitoring_available())
112 return;
113
114 monitor->StopMonitoringStreamOnUIThread(render_process_id, stream_id);
dalecurtisbc6572e12014-09-12 19:22:30115}
116
117void AudioStreamMonitor::StartMonitoringStreamOnUIThread(
118 int render_process_id,
119 int stream_id,
120 const ReadPowerAndClipCallback& read_power_callback) {
121 DCHECK(thread_checker_.CalledOnValidThread());
122 DCHECK(!read_power_callback.is_null());
123 poll_callbacks_[StreamID(render_process_id, stream_id)] = read_power_callback;
124 if (!poll_timer_.IsRunning()) {
125 poll_timer_.Start(
126 FROM_HERE,
rvargas89d44c02015-03-21 07:41:21127 base::TimeDelta::FromSeconds(1) /
128 static_cast<int>(kPowerMeasurementsPerSecond),
dalecurtisbc6572e12014-09-12 19:22:30129 base::Bind(&AudioStreamMonitor::Poll, base::Unretained(this)));
130 }
131}
132
133void AudioStreamMonitor::StopMonitoringStreamOnUIThread(int render_process_id,
134 int stream_id) {
135 DCHECK(thread_checker_.CalledOnValidThread());
136 poll_callbacks_.erase(StreamID(render_process_id, stream_id));
137 if (poll_callbacks_.empty())
138 poll_timer_.Stop();
139}
140
141void AudioStreamMonitor::Poll() {
mlamouri44e4ef42016-01-20 18:42:27142 bool was_audible = is_audible_;
143 is_audible_ = false;
144
dalecurtisbc6572e12014-09-12 19:22:30145 for (StreamPollCallbackMap::const_iterator it = poll_callbacks_.begin();
146 it != poll_callbacks_.end();
147 ++it) {
148 // TODO(miu): A new UI for delivering specific power level and clipping
149 // information is still in the works. For now, we throw away all
150 // information except for "is it audible?"
151 const float power_dbfs = it->second.Run().first;
152 const float kSilenceThresholdDBFS = -72.24719896f;
mlamouri44e4ef42016-01-20 18:42:27153
dalecurtisbc6572e12014-09-12 19:22:30154 if (power_dbfs >= kSilenceThresholdDBFS) {
155 last_blurt_time_ = clock_->NowTicks();
mlamouri44e4ef42016-01-20 18:42:27156 is_audible_ = true;
dalecurtisbc6572e12014-09-12 19:22:30157 MaybeToggle();
158 break; // No need to poll remaining streams.
159 }
160 }
mlamouri44e4ef42016-01-20 18:42:27161
162 if (is_audible_ != was_audible)
altimind8bd26c2016-11-04 11:44:54163 web_contents_->OnAudioStateChanged(is_audible_);
dalecurtisbc6572e12014-09-12 19:22:30164}
165
166void AudioStreamMonitor::MaybeToggle() {
avayvodcc85bbd2015-08-28 19:11:15167 const bool indicator_was_on = was_recently_audible_;
dalecurtisbc6572e12014-09-12 19:22:30168 const base::TimeTicks off_time =
169 last_blurt_time_ + base::TimeDelta::FromMilliseconds(kHoldOnMilliseconds);
170 const base::TimeTicks now = clock_->NowTicks();
171 const bool should_indicator_be_on = now < off_time;
172
avayvodcc85bbd2015-08-28 19:11:15173 if (should_indicator_be_on != indicator_was_on) {
174 was_recently_audible_ = should_indicator_be_on;
175 web_contents_->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
176 }
dalecurtisbc6572e12014-09-12 19:22:30177
178 if (!should_indicator_be_on) {
179 off_timer_.Stop();
180 } else if (!off_timer_.IsRunning()) {
181 off_timer_.Start(
182 FROM_HERE,
183 off_time - now,
184 base::Bind(&AudioStreamMonitor::MaybeToggle, base::Unretained(this)));
185 }
186}
187
altiminec87fd1f2016-11-17 20:22:49188void AudioStreamMonitor::OnStreamAdded() {
189 DCHECK_CURRENTLY_ON(BrowserThread::UI);
190 ++active_streams_;
191
192 if (power_level_monitoring_available())
193 return;
194
195 if (active_streams_ == 1) {
196 is_audible_ = true;
197 web_contents_->OnAudioStateChanged(true);
198 MaybeToggle();
199 }
200}
201
202void AudioStreamMonitor::OnStreamRemoved() {
203 DCHECK_CURRENTLY_ON(BrowserThread::UI);
204 --active_streams_;
205
206 if (power_level_monitoring_available())
207 return;
208
209 if (active_streams_ == 0) {
210 is_audible_ = false;
211 web_contents_->OnAudioStateChanged(false);
212 MaybeToggle();
213 }
214}
215
dalecurtisbc6572e12014-09-12 19:22:30216} // namespace content