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