Roll src/third_party/WebKit d9c6159:8139f33 (svn 201974:201975)
[chromium-blink-merge.git] / content / browser / media / audio_stream_monitor.cc
blob2f6a7e45ce0eb1205a10417d1d81cc7d1cf712bd
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"
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"
14 namespace content {
16 namespace {
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)));
25 return web_contents ? web_contents->audio_stream_monitor() : nullptr;
28 } // namespace
30 AudioStreamMonitor::AudioStreamMonitor(WebContents* contents)
31 : web_contents_(contents),
32 clock_(&default_tick_clock_),
33 was_recently_audible_(false)
35 DCHECK(web_contents_);
38 AudioStreamMonitor::~AudioStreamMonitor() {}
40 bool AudioStreamMonitor::WasRecentlyAudible() const {
41 DCHECK(thread_checker_.CalledOnValidThread());
42 return was_recently_audible_;
45 // static
46 void AudioStreamMonitor::StartMonitoringStream(
47 int render_process_id,
48 int render_frame_id,
49 int stream_id,
50 const ReadPowerAndClipCallback& read_power_callback) {
51 if (!monitoring_available())
52 return;
53 BrowserThread::PostTask(BrowserThread::UI,
54 FROM_HERE,
55 base::Bind(&StartMonitoringHelper,
56 render_process_id,
57 render_frame_id,
58 stream_id,
59 read_power_callback));
62 // static
63 void AudioStreamMonitor::StopMonitoringStream(int render_process_id,
64 int render_frame_id,
65 int stream_id) {
66 if (!media::AudioOutputController::will_monitor_audio_levels())
67 return;
68 BrowserThread::PostTask(BrowserThread::UI,
69 FROM_HERE,
70 base::Bind(&StopMonitoringHelper,
71 render_process_id,
72 render_frame_id,
73 stream_id));
76 // static
77 void AudioStreamMonitor::StartMonitoringHelper(
78 int render_process_id,
79 int render_frame_id,
80 int stream_id,
81 const ReadPowerAndClipCallback& read_power_callback) {
82 DCHECK_CURRENTLY_ON(BrowserThread::UI);
83 AudioStreamMonitor* const monitor =
84 AudioStreamMonitorFromRenderFrame(render_process_id, render_frame_id);
85 if (monitor) {
86 monitor->StartMonitoringStreamOnUIThread(
87 render_process_id, stream_id, read_power_callback);
91 // static
92 void AudioStreamMonitor::StopMonitoringHelper(int render_process_id,
93 int render_frame_id,
94 int stream_id) {
95 DCHECK_CURRENTLY_ON(BrowserThread::UI);
96 AudioStreamMonitor* const monitor =
97 AudioStreamMonitorFromRenderFrame(render_process_id, render_frame_id);
98 if (monitor)
99 monitor->StopMonitoringStreamOnUIThread(render_process_id, stream_id);
102 void AudioStreamMonitor::StartMonitoringStreamOnUIThread(
103 int render_process_id,
104 int stream_id,
105 const ReadPowerAndClipCallback& read_power_callback) {
106 DCHECK(thread_checker_.CalledOnValidThread());
107 DCHECK(!read_power_callback.is_null());
108 poll_callbacks_[StreamID(render_process_id, stream_id)] = read_power_callback;
109 if (!poll_timer_.IsRunning()) {
110 poll_timer_.Start(
111 FROM_HERE,
112 base::TimeDelta::FromSeconds(1) /
113 static_cast<int>(kPowerMeasurementsPerSecond),
114 base::Bind(&AudioStreamMonitor::Poll, base::Unretained(this)));
118 void AudioStreamMonitor::StopMonitoringStreamOnUIThread(int render_process_id,
119 int stream_id) {
120 DCHECK(thread_checker_.CalledOnValidThread());
121 poll_callbacks_.erase(StreamID(render_process_id, stream_id));
122 if (poll_callbacks_.empty())
123 poll_timer_.Stop();
126 void AudioStreamMonitor::Poll() {
127 for (StreamPollCallbackMap::const_iterator it = poll_callbacks_.begin();
128 it != poll_callbacks_.end();
129 ++it) {
130 // TODO(miu): A new UI for delivering specific power level and clipping
131 // information is still in the works. For now, we throw away all
132 // information except for "is it audible?"
133 const float power_dbfs = it->second.Run().first;
134 const float kSilenceThresholdDBFS = -72.24719896f;
135 if (power_dbfs >= kSilenceThresholdDBFS) {
136 last_blurt_time_ = clock_->NowTicks();
137 MaybeToggle();
138 break; // No need to poll remaining streams.
143 void AudioStreamMonitor::MaybeToggle() {
144 const bool indicator_was_on = was_recently_audible_;
145 const base::TimeTicks off_time =
146 last_blurt_time_ + base::TimeDelta::FromMilliseconds(kHoldOnMilliseconds);
147 const base::TimeTicks now = clock_->NowTicks();
148 const bool should_indicator_be_on = now < off_time;
150 if (should_indicator_be_on != indicator_was_on) {
151 was_recently_audible_ = should_indicator_be_on;
152 web_contents_->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB);
155 if (!should_indicator_be_on) {
156 off_timer_.Stop();
157 } else if (!off_timer_.IsRunning()) {
158 off_timer_.Start(
159 FROM_HERE,
160 off_time - now,
161 base::Bind(&AudioStreamMonitor::MaybeToggle, base::Unretained(this)));
165 } // namespace content