| Index: content/browser/media/audio_stream_monitor.cc | 
| diff --git a/content/browser/media/audio_stream_monitor.cc b/content/browser/media/audio_stream_monitor.cc | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..1d2f76f7f2c02f986f5b52122834363ff96be1e8 | 
| --- /dev/null | 
| +++ b/content/browser/media/audio_stream_monitor.cc | 
| @@ -0,0 +1,162 @@ | 
| +// Copyright 2014 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +#include "content/browser/media/audio_stream_monitor.h" | 
| + | 
| +#include "base/bind.h" | 
| +#include "base/bind_helpers.h" | 
| +#include "content/browser/web_contents/web_contents_impl.h" | 
| +#include "content/public/browser/browser_thread.h" | 
| +#include "content/public/browser/invalidate_type.h" | 
| +#include "content/public/browser/render_frame_host.h" | 
| + | 
| +namespace content { | 
| + | 
| +namespace { | 
| + | 
| +AudioStreamMonitor* AudioStreamMonitorFromRenderFrame(int render_process_id, | 
| +                                                      int render_frame_id) { | 
| +  DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
| +  WebContentsImpl* const web_contents = | 
| +      static_cast<WebContentsImpl*>(WebContents::FromRenderFrameHost( | 
| +          RenderFrameHost::FromID(render_process_id, render_frame_id))); | 
| +  return web_contents ? web_contents->audio_stream_monitor() : NULL; | 
| +} | 
| + | 
| +}  // namespace | 
| + | 
| +AudioStreamMonitor::AudioStreamMonitor(WebContents* contents) | 
| +    : web_contents_(contents), | 
| +      clock_(&default_tick_clock_), | 
| +      was_recently_audible_(false) { | 
| +  DCHECK(web_contents_); | 
| +} | 
| + | 
| +AudioStreamMonitor::~AudioStreamMonitor() {} | 
| + | 
| +bool AudioStreamMonitor::WasRecentlyAudible() const { | 
| +  DCHECK(thread_checker_.CalledOnValidThread()); | 
| +  return was_recently_audible_; | 
| +} | 
| + | 
| +// static | 
| +void AudioStreamMonitor::StartMonitoringStream( | 
| +    int render_process_id, | 
| +    int render_frame_id, | 
| +    int stream_id, | 
| +    const ReadPowerAndClipCallback& read_power_callback) { | 
| +  if (!monitoring_available()) | 
| +    return; | 
| +  BrowserThread::PostTask(BrowserThread::UI, | 
| +                          FROM_HERE, | 
| +                          base::Bind(&StartMonitoringHelper, | 
| +                                     render_process_id, | 
| +                                     render_frame_id, | 
| +                                     stream_id, | 
| +                                     read_power_callback)); | 
| +} | 
| + | 
| +// static | 
| +void AudioStreamMonitor::StopMonitoringStream(int render_process_id, | 
| +                                              int render_frame_id, | 
| +                                              int stream_id) { | 
| +  if (!monitoring_available()) | 
| +    return; | 
| +  BrowserThread::PostTask(BrowserThread::UI, | 
| +                          FROM_HERE, | 
| +                          base::Bind(&StopMonitoringHelper, | 
| +                                     render_process_id, | 
| +                                     render_frame_id, | 
| +                                     stream_id)); | 
| +} | 
| + | 
| +// static | 
| +void AudioStreamMonitor::StartMonitoringHelper( | 
| +    int render_process_id, | 
| +    int render_frame_id, | 
| +    int stream_id, | 
| +    const ReadPowerAndClipCallback& read_power_callback) { | 
| +  DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
| +  AudioStreamMonitor* const monitor = | 
| +      AudioStreamMonitorFromRenderFrame(render_process_id, render_frame_id); | 
| +  if (monitor) { | 
| +    monitor->StartMonitoringStreamOnUIThread( | 
| +        render_process_id, stream_id, read_power_callback); | 
| +  } | 
| +} | 
| + | 
| +// static | 
| +void AudioStreamMonitor::StopMonitoringHelper(int render_process_id, | 
| +                                              int render_frame_id, | 
| +                                              int stream_id) { | 
| +  DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
| +  AudioStreamMonitor* const monitor = | 
| +      AudioStreamMonitorFromRenderFrame(render_process_id, render_frame_id); | 
| +  if (monitor) | 
| +    monitor->StopMonitoringStreamOnUIThread(render_process_id, stream_id); | 
| +} | 
| + | 
| +void AudioStreamMonitor::StartMonitoringStreamOnUIThread( | 
| +    int render_process_id, | 
| +    int stream_id, | 
| +    const ReadPowerAndClipCallback& read_power_callback) { | 
| +  DCHECK(thread_checker_.CalledOnValidThread()); | 
| +  DCHECK(!read_power_callback.is_null()); | 
| +  poll_callbacks_[StreamID(render_process_id, stream_id)] = read_power_callback; | 
| +  if (!poll_timer_.IsRunning()) { | 
| +    poll_timer_.Start( | 
| +        FROM_HERE, | 
| +        base::TimeDelta::FromSeconds(1) / kPowerMeasurementsPerSecond, | 
| +        base::Bind(&AudioStreamMonitor::Poll, base::Unretained(this))); | 
| +  } | 
| +} | 
| + | 
| +void AudioStreamMonitor::StopMonitoringStreamOnUIThread(int render_process_id, | 
| +                                                        int stream_id) { | 
| +  DCHECK(thread_checker_.CalledOnValidThread()); | 
| +  poll_callbacks_.erase(StreamID(render_process_id, stream_id)); | 
| +  if (poll_callbacks_.empty()) | 
| +    poll_timer_.Stop(); | 
| +} | 
| + | 
| +void AudioStreamMonitor::Poll() { | 
| +  for (StreamPollCallbackMap::const_iterator it = poll_callbacks_.begin(); | 
| +       it != poll_callbacks_.end(); | 
| +       ++it) { | 
| +    // TODO(miu): A new UI for delivering specific power level and clipping | 
| +    // information is still in the works.  For now, we throw away all | 
| +    // information except for "is it audible?" | 
| +    const float power_dbfs = it->second.Run().first; | 
| +    const float kSilenceThresholdDBFS = -72.24719896f; | 
| +    if (power_dbfs >= kSilenceThresholdDBFS) { | 
| +      last_blurt_time_ = clock_->NowTicks(); | 
| +      MaybeToggle(); | 
| +      break;  // No need to poll remaining streams. | 
| +    } | 
| +  } | 
| +} | 
| + | 
| +void AudioStreamMonitor::MaybeToggle() { | 
| +  const bool indicator_was_on = was_recently_audible_; | 
| +  const base::TimeTicks off_time = | 
| +      last_blurt_time_ + base::TimeDelta::FromMilliseconds(kHoldOnMilliseconds); | 
| +  const base::TimeTicks now = clock_->NowTicks(); | 
| +  const bool should_indicator_be_on = now < off_time; | 
| + | 
| +  if (should_indicator_be_on != indicator_was_on) { | 
| +    was_recently_audible_ = should_indicator_be_on; | 
| +    web_contents_->NotifyNavigationStateChanged(INVALIDATE_TYPE_TAB); | 
| +  } | 
| + | 
| +  if (!should_indicator_be_on) { | 
| +    off_timer_.Stop(); | 
| +  } else if (!off_timer_.IsRunning()) { | 
| +    off_timer_.Start( | 
| +        FROM_HERE, | 
| +        off_time - now, | 
| +        base::Bind(&AudioStreamMonitor::MaybeToggle, base::Unretained(this))); | 
| +  } | 
| +} | 
| + | 
| +}  // namespace content | 
|  |