| Index: media/audio/audio_output_dispatcher_impl.cc
 | 
| ===================================================================
 | 
| --- media/audio/audio_output_dispatcher_impl.cc	(revision 0)
 | 
| +++ media/audio/audio_output_dispatcher_impl.cc	(revision 0)
 | 
| @@ -0,0 +1,185 @@
 | 
| +// Copyright (c) 2012 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 "media/audio/audio_output_dispatcher_impl.h"
 | 
| +
 | 
| +#include <algorithm>
 | 
| +
 | 
| +#include "base/bind.h"
 | 
| +#include "base/compiler_specific.h"
 | 
| +#include "base/message_loop.h"
 | 
| +#include "base/time.h"
 | 
| +#include "media/audio/audio_io.h"
 | 
| +#include "media/audio/audio_output_proxy.h"
 | 
| +#include "media/audio/audio_util.h"
 | 
| +
 | 
| +AudioOutputDispatcherImpl::AudioOutputDispatcherImpl(
 | 
| +    AudioManager* audio_manager,
 | 
| +    const AudioParameters& params,
 | 
| +    base::TimeDelta close_delay)
 | 
| +    : AudioOutputDispatcher(audio_manager, params),
 | 
| +      pause_delay_(base::TimeDelta::FromMilliseconds(
 | 
| +          2 * params.frames_per_buffer() *
 | 
| +          base::Time::kMillisecondsPerSecond / params.sample_rate())),
 | 
| +      paused_proxies_(0),
 | 
| +      ALLOW_THIS_IN_INITIALIZER_LIST(weak_this_(this)),
 | 
| +      close_timer_(FROM_HERE,
 | 
| +                   close_delay,
 | 
| +                   weak_this_.GetWeakPtr(),
 | 
| +                   &AudioOutputDispatcherImpl::ClosePendingStreams) {
 | 
| +}
 | 
| +
 | 
| +AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() {
 | 
| +}
 | 
| +
 | 
| +bool AudioOutputDispatcherImpl::StreamOpened() {
 | 
| +  DCHECK_EQ(MessageLoop::current(), message_loop_);
 | 
| +
 | 
| +  paused_proxies_++;
 | 
| +
 | 
| +  // Ensure that there is at least one open stream.
 | 
| +  if (idle_streams_.empty() && !CreateAndOpenStream()) {
 | 
| +    return false;
 | 
| +  }
 | 
| +
 | 
| +  close_timer_.Reset();
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +AudioOutputStream* AudioOutputDispatcherImpl::StreamStarted(
 | 
| +    AudioOutputStream::AudioSourceCallback* callback,
 | 
| +    AudioOutputProxy* stream_proxy) {
 | 
| +  DCHECK_EQ(MessageLoop::current(), message_loop_);
 | 
| +
 | 
| +  if (idle_streams_.empty() && !CreateAndOpenStream()) {
 | 
| +    return NULL;
 | 
| +  }
 | 
| +
 | 
| +  AudioOutputStream* stream = idle_streams_.back();
 | 
| +  idle_streams_.pop_back();
 | 
| +
 | 
| +  DCHECK_GT(paused_proxies_, 0u);
 | 
| +  paused_proxies_--;
 | 
| +
 | 
| +  close_timer_.Reset();
 | 
| +
 | 
| +  // Schedule task to allocate streams for other proxies if we need to.
 | 
| +  message_loop_->PostTask(FROM_HERE, base::Bind(
 | 
| +      &AudioOutputDispatcherImpl::OpenTask, weak_this_.GetWeakPtr()));
 | 
| +
 | 
| +  double volume = 0;
 | 
| +  stream_proxy->GetVolume(&volume);
 | 
| +  stream->SetVolume(volume);
 | 
| +  stream->Start(callback);
 | 
| +  return stream;
 | 
| +}
 | 
| +
 | 
| +void AudioOutputDispatcherImpl::StreamStopped(
 | 
| +    AudioOutputStream* physical_stream,
 | 
| +    AudioOutputProxy* stream_proxy) {
 | 
| +  DCHECK_EQ(MessageLoop::current(), message_loop_);
 | 
| +
 | 
| +  physical_stream->Stop();
 | 
| +
 | 
| +  paused_proxies_++;
 | 
| +
 | 
| +  pausing_streams_.push_front(physical_stream);
 | 
| +
 | 
| +  // Don't recycle stream until two buffers worth of time has elapsed.
 | 
| +  message_loop_->PostDelayedTask(
 | 
| +      FROM_HERE,
 | 
| +      base::Bind(&AudioOutputDispatcherImpl::StopStreamTask,
 | 
| +                 weak_this_.GetWeakPtr()),
 | 
| +      pause_delay_);
 | 
| +}
 | 
| +
 | 
| +void AudioOutputDispatcherImpl::StreamVolumeSet(
 | 
| +    AudioOutputStream* physical_stream,
 | 
| +    double volume) {
 | 
| +  if (physical_stream)
 | 
| +    physical_stream->SetVolume(volume);
 | 
| +}
 | 
| +
 | 
| +void AudioOutputDispatcherImpl::StopStreamTask() {
 | 
| +  DCHECK_EQ(MessageLoop::current(), message_loop_);
 | 
| +
 | 
| +  if (pausing_streams_.empty())
 | 
| +    return;
 | 
| +
 | 
| +  AudioOutputStream* stream = pausing_streams_.back();
 | 
| +  pausing_streams_.pop_back();
 | 
| +  idle_streams_.push_back(stream);
 | 
| +  close_timer_.Reset();
 | 
| +}
 | 
| +
 | 
| +void AudioOutputDispatcherImpl::StreamClosed(AudioOutputProxy* stream_proxy) {
 | 
| +  DCHECK_EQ(MessageLoop::current(), message_loop_);
 | 
| +
 | 
| +  while (!pausing_streams_.empty()) {
 | 
| +    idle_streams_.push_back(pausing_streams_.back());
 | 
| +    pausing_streams_.pop_back();
 | 
| +  }
 | 
| +
 | 
| +  DCHECK_GT(paused_proxies_, 0u);
 | 
| +  paused_proxies_--;
 | 
| +
 | 
| +  while (idle_streams_.size() > paused_proxies_) {
 | 
| +    idle_streams_.back()->Close();
 | 
| +    idle_streams_.pop_back();
 | 
| +  }
 | 
| +}
 | 
| +
 | 
| +void AudioOutputDispatcherImpl::Shutdown() {
 | 
| +  DCHECK_EQ(MessageLoop::current(), message_loop_);
 | 
| +
 | 
| +  // Cancel any pending tasks to close paused streams or create new ones.
 | 
| +  weak_this_.InvalidateWeakPtrs();
 | 
| +
 | 
| +  // No AudioOutputProxy objects should hold a reference to us when we get
 | 
| +  // to this stage.
 | 
| +  DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
 | 
| +
 | 
| +  AudioOutputStreamList::iterator it = idle_streams_.begin();
 | 
| +  for (; it != idle_streams_.end(); ++it)
 | 
| +    (*it)->Close();
 | 
| +  idle_streams_.clear();
 | 
| +
 | 
| +  it = pausing_streams_.begin();
 | 
| +  for (; it != pausing_streams_.end(); ++it)
 | 
| +    (*it)->Close();
 | 
| +  pausing_streams_.clear();
 | 
| +}
 | 
| +
 | 
| +bool AudioOutputDispatcherImpl::CreateAndOpenStream() {
 | 
| +  AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(params_);
 | 
| +  if (!stream)
 | 
| +    return false;
 | 
| +
 | 
| +  if (!stream->Open()) {
 | 
| +    stream->Close();
 | 
| +    return false;
 | 
| +  }
 | 
| +  idle_streams_.push_back(stream);
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
| +void AudioOutputDispatcherImpl::OpenTask() {
 | 
| +  // Make sure that we have at least one stream allocated if there
 | 
| +  // are paused streams.
 | 
| +  if (paused_proxies_ > 0 && idle_streams_.empty() &&
 | 
| +      pausing_streams_.empty()) {
 | 
| +    CreateAndOpenStream();
 | 
| +  }
 | 
| +
 | 
| +  close_timer_.Reset();
 | 
| +}
 | 
| +
 | 
| +// This method is called by |close_timer_|.
 | 
| +void AudioOutputDispatcherImpl::ClosePendingStreams() {
 | 
| +  DCHECK_EQ(MessageLoop::current(), message_loop_);
 | 
| +  while (!idle_streams_.empty()) {
 | 
| +    idle_streams_.back()->Close();
 | 
| +    idle_streams_.pop_back();
 | 
| +  }
 | 
| +}
 | 
| 
 |