blob: 4c0bc0a5d720c6ce0debbadd6587ac96b8a0fa75 [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
25 if (!web_contents)
26 return nullptr;
27
28 AudioStateProvider* audio_provider = web_contents->audio_state_provider();
29 return audio_provider ? audio_provider->audio_stream_monitor() : nullptr;
dalecurtisbc6572e12014-09-12 19:22:3030}
31
32} // namespace
33
34AudioStreamMonitor::AudioStreamMonitor(WebContents* contents)
timavf6f6f692015-03-04 02:19:3735 : AudioStateProvider(contents),
36 clock_(&default_tick_clock_)
37{
dalecurtisbc6572e12014-09-12 19:22:3038}
39
40AudioStreamMonitor::~AudioStreamMonitor() {}
41
timavf6f6f692015-03-04 02:19:3742bool AudioStreamMonitor::IsAudioStateAvailable() const {
43 return media::AudioOutputController::will_monitor_audio_levels();
44}
45
46// This provider is the monitor.
47AudioStreamMonitor* AudioStreamMonitor::audio_stream_monitor() {
48 return this;
49}
50
dalecurtisbc6572e12014-09-12 19:22:3051bool AudioStreamMonitor::WasRecentlyAudible() const {
52 DCHECK(thread_checker_.CalledOnValidThread());
timavf6f6f692015-03-04 02:19:3753 return AudioStateProvider::WasRecentlyAudible();
dalecurtisbc6572e12014-09-12 19:22:3054}
55
56// static
57void AudioStreamMonitor::StartMonitoringStream(
58 int render_process_id,
59 int render_frame_id,
60 int stream_id,
61 const ReadPowerAndClipCallback& read_power_callback) {
timavf6f6f692015-03-04 02:19:3762 if (!media::AudioOutputController::will_monitor_audio_levels())
dalecurtisbc6572e12014-09-12 19:22:3063 return;
64 BrowserThread::PostTask(BrowserThread::UI,
65 FROM_HERE,
66 base::Bind(&StartMonitoringHelper,
67 render_process_id,
68 render_frame_id,
69 stream_id,
70 read_power_callback));
71}
72
73// static
74void AudioStreamMonitor::StopMonitoringStream(int render_process_id,
75 int render_frame_id,
76 int stream_id) {
timavf6f6f692015-03-04 02:19:3777 if (!media::AudioOutputController::will_monitor_audio_levels())
dalecurtisbc6572e12014-09-12 19:22:3078 return;
79 BrowserThread::PostTask(BrowserThread::UI,
80 FROM_HERE,
81 base::Bind(&StopMonitoringHelper,
82 render_process_id,
83 render_frame_id,
84 stream_id));
85}
86
87// static
88void AudioStreamMonitor::StartMonitoringHelper(
89 int render_process_id,
90 int render_frame_id,
91 int stream_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);
96 if (monitor) {
97 monitor->StartMonitoringStreamOnUIThread(
98 render_process_id, stream_id, read_power_callback);
99 }
100}
101
102// static
103void AudioStreamMonitor::StopMonitoringHelper(int render_process_id,
104 int render_frame_id,
105 int stream_id) {
106 DCHECK_CURRENTLY_ON(BrowserThread::UI);
107 AudioStreamMonitor* const monitor =
108 AudioStreamMonitorFromRenderFrame(render_process_id, render_frame_id);
109 if (monitor)
110 monitor->StopMonitoringStreamOnUIThread(render_process_id, stream_id);
111}
112
113void AudioStreamMonitor::StartMonitoringStreamOnUIThread(
114 int render_process_id,
115 int stream_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()) {
121 poll_timer_.Start(
122 FROM_HERE,
123 base::TimeDelta::FromSeconds(1) / kPowerMeasurementsPerSecond,
124 base::Bind(&AudioStreamMonitor::Poll, base::Unretained(this)));
125 }
126}
127
128void AudioStreamMonitor::StopMonitoringStreamOnUIThread(int render_process_id,
129 int stream_id) {
130 DCHECK(thread_checker_.CalledOnValidThread());
131 poll_callbacks_.erase(StreamID(render_process_id, stream_id));
132 if (poll_callbacks_.empty())
133 poll_timer_.Stop();
134}
135
136void AudioStreamMonitor::Poll() {
137 for (StreamPollCallbackMap::const_iterator it = poll_callbacks_.begin();
138 it != poll_callbacks_.end();
139 ++it) {
140 // TODO(miu): A new UI for delivering specific power level and clipping
141 // information is still in the works. For now, we throw away all
142 // information except for "is it audible?"
143 const float power_dbfs = it->second.Run().first;
144 const float kSilenceThresholdDBFS = -72.24719896f;
145 if (power_dbfs >= kSilenceThresholdDBFS) {
146 last_blurt_time_ = clock_->NowTicks();
147 MaybeToggle();
148 break; // No need to poll remaining streams.
149 }
150 }
151}
152
153void AudioStreamMonitor::MaybeToggle() {
dalecurtisbc6572e12014-09-12 19:22:30154 const base::TimeTicks off_time =
155 last_blurt_time_ + base::TimeDelta::FromMilliseconds(kHoldOnMilliseconds);
156 const base::TimeTicks now = clock_->NowTicks();
157 const bool should_indicator_be_on = now < off_time;
158
timavf6f6f692015-03-04 02:19:37159 Notify(should_indicator_be_on);
dalecurtisbc6572e12014-09-12 19:22:30160
161 if (!should_indicator_be_on) {
162 off_timer_.Stop();
163 } else if (!off_timer_.IsRunning()) {
164 off_timer_.Start(
165 FROM_HERE,
166 off_time - now,
167 base::Bind(&AudioStreamMonitor::MaybeToggle, base::Unretained(this)));
168 }
169}
170
171} // namespace content