blob: 36e11030eb105138d231d53c7af5a0280d4ce3e4 [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),
34 is_audible_(false)
timavf6f6f692015-03-04 02:19:3735{
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) {
avayvodcc85bbd2015-08-28 19:11:1557 if (!monitoring_available())
dalecurtisbc6572e12014-09-12 19:22:3058 return;
59 BrowserThread::PostTask(BrowserThread::UI,
60 FROM_HERE,
61 base::Bind(&StartMonitoringHelper,
62 render_process_id,
63 render_frame_id,
64 stream_id,
65 read_power_callback));
66}
67
68// static
69void AudioStreamMonitor::StopMonitoringStream(int render_process_id,
70 int render_frame_id,
71 int stream_id) {
timavf6f6f692015-03-04 02:19:3772 if (!media::AudioOutputController::will_monitor_audio_levels())
dalecurtisbc6572e12014-09-12 19:22:3073 return;
74 BrowserThread::PostTask(BrowserThread::UI,
75 FROM_HERE,
76 base::Bind(&StopMonitoringHelper,
77 render_process_id,
78 render_frame_id,
79 stream_id));
80}
81
82// static
83void AudioStreamMonitor::StartMonitoringHelper(
84 int render_process_id,
85 int render_frame_id,
86 int stream_id,
87 const ReadPowerAndClipCallback& read_power_callback) {
88 DCHECK_CURRENTLY_ON(BrowserThread::UI);
89 AudioStreamMonitor* const monitor =
90 AudioStreamMonitorFromRenderFrame(render_process_id, render_frame_id);
91 if (monitor) {
92 monitor->StartMonitoringStreamOnUIThread(
93 render_process_id, stream_id, read_power_callback);
94 }
95}
96
97// static
98void AudioStreamMonitor::StopMonitoringHelper(int render_process_id,
99 int render_frame_id,
100 int stream_id) {
101 DCHECK_CURRENTLY_ON(BrowserThread::UI);
102 AudioStreamMonitor* const monitor =
103 AudioStreamMonitorFromRenderFrame(render_process_id, render_frame_id);
104 if (monitor)
105 monitor->StopMonitoringStreamOnUIThread(render_process_id, stream_id);
106}
107
108void AudioStreamMonitor::StartMonitoringStreamOnUIThread(
109 int render_process_id,
110 int stream_id,
111 const ReadPowerAndClipCallback& read_power_callback) {
112 DCHECK(thread_checker_.CalledOnValidThread());
113 DCHECK(!read_power_callback.is_null());
114 poll_callbacks_[StreamID(render_process_id, stream_id)] = read_power_callback;
115 if (!poll_timer_.IsRunning()) {
116 poll_timer_.Start(
117 FROM_HERE,
rvargas89d44c02015-03-21 07:41:21118 base::TimeDelta::FromSeconds(1) /
119 static_cast<int>(kPowerMeasurementsPerSecond),
dalecurtisbc6572e12014-09-12 19:22:30120 base::Bind(&AudioStreamMonitor::Poll, base::Unretained(this)));
121 }
122}
123
124void AudioStreamMonitor::StopMonitoringStreamOnUIThread(int render_process_id,
125 int stream_id) {
126 DCHECK(thread_checker_.CalledOnValidThread());
127 poll_callbacks_.erase(StreamID(render_process_id, stream_id));
128 if (poll_callbacks_.empty())
129 poll_timer_.Stop();
130}
131
132void AudioStreamMonitor::Poll() {
mlamouri44e4ef42016-01-20 18:42:27133 bool was_audible = is_audible_;
134 is_audible_ = false;
135
dalecurtisbc6572e12014-09-12 19:22:30136 for (StreamPollCallbackMap::const_iterator it = poll_callbacks_.begin();
137 it != poll_callbacks_.end();
138 ++it) {
139 // TODO(miu): A new UI for delivering specific power level and clipping
140 // information is still in the works. For now, we throw away all
141 // information except for "is it audible?"
142 const float power_dbfs = it->second.Run().first;
143 const float kSilenceThresholdDBFS = -72.24719896f;
mlamouri44e4ef42016-01-20 18:42:27144
dalecurtisbc6572e12014-09-12 19:22:30145 if (power_dbfs >= kSilenceThresholdDBFS) {
146 last_blurt_time_ = clock_->NowTicks();
mlamouri44e4ef42016-01-20 18:42:27147 is_audible_ = true;
dalecurtisbc6572e12014-09-12 19:22:30148 MaybeToggle();
149 break; // No need to poll remaining streams.
150 }
151 }
mlamouri44e4ef42016-01-20 18:42:27152
153 if (is_audible_ != was_audible)
altimind8bd26c2016-11-04 11:44:54154 web_contents_->OnAudioStateChanged(is_audible_);
dalecurtisbc6572e12014-09-12 19:22:30155}
156
157void AudioStreamMonitor::MaybeToggle() {
avayvodcc85bbd2015-08-28 19:11:15158 const bool indicator_was_on = was_recently_audible_;
dalecurtisbc6572e12014-09-12 19:22:30159 const base::TimeTicks off_time =
160 last_blurt_time_ + base::TimeDelta::FromMilliseconds(kHoldOnMilliseconds);
161 const base::TimeTicks now = clock_->NowTicks();
162 const bool should_indicator_be_on = now < off_time;
163
avayvodcc85bbd2015-08-28 19:11:15164 if (should_indicator_be_on != indicator_was_on) {
165 was_recently_audible_ = should_indicator_be_on;
166 web_contents_->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
167 }
dalecurtisbc6572e12014-09-12 19:22:30168
169 if (!should_indicator_be_on) {
170 off_timer_.Stop();
171 } else if (!off_timer_.IsRunning()) {
172 off_timer_.Start(
173 FROM_HERE,
174 off_time - now,
175 base::Bind(&AudioStreamMonitor::MaybeToggle, base::Unretained(this)));
176 }
177}
178
179} // namespace content