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 "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
    163