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 "chrome/browser/media/audio_stream_monitor.h" 6 7 #include "base/bind.h" 8 #include "base/bind_helpers.h" 9 #include "content/public/browser/invalidate_type.h" 10 #include "content/public/browser/web_contents.h" 11 12 DEFINE_WEB_CONTENTS_USER_DATA_KEY(AudioStreamMonitor); 13 14 AudioStreamMonitor::AudioStreamMonitor(content::WebContents* contents) 15 : web_contents_(contents), 16 clock_(&default_tick_clock_), 17 was_recently_audible_(false) { 18 DCHECK(web_contents_); 19 } 20 21 AudioStreamMonitor::~AudioStreamMonitor() {} 22 23 bool AudioStreamMonitor::WasRecentlyAudible() const { 24 DCHECK(thread_checker_.CalledOnValidThread()); 25 return was_recently_audible_; 26 } 27 28 void AudioStreamMonitor::StartMonitoringStream( 29 int stream_id, 30 const ReadPowerAndClipCallback& read_power_callback) { 31 DCHECK(thread_checker_.CalledOnValidThread()); 32 DCHECK(!read_power_callback.is_null()); 33 poll_callbacks_[stream_id] = read_power_callback; 34 if (!poll_timer_.IsRunning()) { 35 poll_timer_.Start( 36 FROM_HERE, 37 base::TimeDelta::FromSeconds(1) / kPowerMeasurementsPerSecond, 38 base::Bind(&AudioStreamMonitor::Poll, base::Unretained(this))); 39 } 40 } 41 42 void AudioStreamMonitor::StopMonitoringStream(int stream_id) { 43 DCHECK(thread_checker_.CalledOnValidThread()); 44 poll_callbacks_.erase(stream_id); 45 if (poll_callbacks_.empty()) 46 poll_timer_.Stop(); 47 } 48 49 void AudioStreamMonitor::Poll() { 50 for (StreamPollCallbackMap::const_iterator it = poll_callbacks_.begin(); 51 it != poll_callbacks_.end(); 52 ++it) { 53 // TODO(miu): A new UI for delivering specific power level and clipping 54 // information is still in the works. For now, we throw away all 55 // information except for "is it audible?" 56 const float power_dbfs = it->second.Run().first; 57 const float kSilenceThresholdDBFS = -72.24719896f; 58 if (power_dbfs >= kSilenceThresholdDBFS) { 59 last_blurt_time_ = clock_->NowTicks(); 60 MaybeToggle(); 61 break; // No need to poll remaining streams. 62 } 63 } 64 } 65 66 void AudioStreamMonitor::MaybeToggle() { 67 const bool indicator_was_on = was_recently_audible_; 68 const base::TimeTicks off_time = 69 last_blurt_time_ + base::TimeDelta::FromMilliseconds(kHoldOnMilliseconds); 70 const base::TimeTicks now = clock_->NowTicks(); 71 const bool should_indicator_be_on = now < off_time; 72 73 if (should_indicator_be_on != indicator_was_on) { 74 was_recently_audible_ = should_indicator_be_on; 75 web_contents_->NotifyNavigationStateChanged(content::INVALIDATE_TYPE_TAB); 76 } 77 78 if (!should_indicator_be_on) { 79 off_timer_.Stop(); 80 } else if (!off_timer_.IsRunning()) { 81 off_timer_.Start( 82 FROM_HERE, 83 off_time - now, 84 base::Bind(&AudioStreamMonitor::MaybeToggle, base::Unretained(this))); 85 } 86 } 87