Home | History | Annotate | Download | only in media
      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