Home | History | Annotate | Download | only in audio
      1 // Copyright (c) 2012 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 "media/audio/audio_output_dispatcher_impl.h"
      6 
      7 #include <algorithm>
      8 
      9 #include "base/bind.h"
     10 #include "base/compiler_specific.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/time/time.h"
     13 #include "media/audio/audio_io.h"
     14 #include "media/audio/audio_output_proxy.h"
     15 #include "media/audio/audio_util.h"
     16 
     17 namespace media {
     18 
     19 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl(
     20     AudioManager* audio_manager,
     21     const AudioParameters& params,
     22     const std::string& input_device_id,
     23     const base::TimeDelta& close_delay)
     24     : AudioOutputDispatcher(audio_manager, params, input_device_id),
     25       pause_delay_(base::TimeDelta::FromMicroseconds(
     26           2 * params.frames_per_buffer() * base::Time::kMicrosecondsPerSecond /
     27           static_cast<float>(params.sample_rate()))),
     28       paused_proxies_(0),
     29       weak_this_(this),
     30       close_timer_(FROM_HERE,
     31                    close_delay,
     32                    this,
     33                    &AudioOutputDispatcherImpl::ClosePendingStreams) {
     34 }
     35 
     36 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() {
     37   DCHECK(proxy_to_physical_map_.empty());
     38   DCHECK(idle_streams_.empty());
     39   DCHECK(pausing_streams_.empty());
     40 }
     41 
     42 bool AudioOutputDispatcherImpl::OpenStream() {
     43   DCHECK_EQ(base::MessageLoop::current(), message_loop_);
     44 
     45   paused_proxies_++;
     46 
     47   // Ensure that there is at least one open stream.
     48   if (idle_streams_.empty() && !CreateAndOpenStream()) {
     49     paused_proxies_--;
     50     return false;
     51   }
     52 
     53   close_timer_.Reset();
     54   return true;
     55 }
     56 
     57 bool AudioOutputDispatcherImpl::StartStream(
     58     AudioOutputStream::AudioSourceCallback* callback,
     59     AudioOutputProxy* stream_proxy) {
     60   DCHECK_EQ(base::MessageLoop::current(), message_loop_);
     61 
     62   if (idle_streams_.empty() && !CreateAndOpenStream())
     63     return false;
     64 
     65   AudioOutputStream* physical_stream = idle_streams_.back();
     66   DCHECK(physical_stream);
     67   idle_streams_.pop_back();
     68 
     69   DCHECK_GT(paused_proxies_, 0u);
     70   --paused_proxies_;
     71 
     72   close_timer_.Reset();
     73 
     74   // Schedule task to allocate streams for other proxies if we need to.
     75   message_loop_->PostTask(FROM_HERE, base::Bind(
     76       &AudioOutputDispatcherImpl::OpenTask, weak_this_.GetWeakPtr()));
     77 
     78   double volume = 0;
     79   stream_proxy->GetVolume(&volume);
     80   physical_stream->SetVolume(volume);
     81   physical_stream->Start(callback);
     82   proxy_to_physical_map_[stream_proxy] = physical_stream;
     83   return true;
     84 }
     85 
     86 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) {
     87   DCHECK_EQ(base::MessageLoop::current(), message_loop_);
     88 
     89   AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
     90   DCHECK(it != proxy_to_physical_map_.end());
     91   AudioOutputStream* physical_stream = it->second;
     92   proxy_to_physical_map_.erase(it);
     93 
     94   physical_stream->Stop();
     95 
     96   ++paused_proxies_;
     97 
     98   pausing_streams_.push_front(physical_stream);
     99 
    100   // Don't recycle stream until two buffers worth of time has elapsed.
    101   message_loop_->PostDelayedTask(
    102       FROM_HERE,
    103       base::Bind(&AudioOutputDispatcherImpl::StopStreamTask,
    104                  weak_this_.GetWeakPtr()),
    105       pause_delay_);
    106 }
    107 
    108 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy,
    109                                                 double volume) {
    110   DCHECK_EQ(base::MessageLoop::current(), message_loop_);
    111   AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
    112   if (it != proxy_to_physical_map_.end()) {
    113     AudioOutputStream* physical_stream = it->second;
    114     physical_stream->SetVolume(volume);
    115   }
    116 }
    117 
    118 void AudioOutputDispatcherImpl::StopStreamTask() {
    119   DCHECK_EQ(base::MessageLoop::current(), message_loop_);
    120 
    121   if (pausing_streams_.empty())
    122     return;
    123 
    124   AudioOutputStream* stream = pausing_streams_.back();
    125   pausing_streams_.pop_back();
    126   idle_streams_.push_back(stream);
    127   close_timer_.Reset();
    128 }
    129 
    130 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) {
    131   DCHECK_EQ(base::MessageLoop::current(), message_loop_);
    132 
    133   while (!pausing_streams_.empty()) {
    134     idle_streams_.push_back(pausing_streams_.back());
    135     pausing_streams_.pop_back();
    136   }
    137 
    138   DCHECK_GT(paused_proxies_, 0u);
    139   paused_proxies_--;
    140 
    141   while (idle_streams_.size() > paused_proxies_) {
    142     idle_streams_.back()->Close();
    143     idle_streams_.pop_back();
    144   }
    145 }
    146 
    147 void AudioOutputDispatcherImpl::Shutdown() {
    148   DCHECK_EQ(base::MessageLoop::current(), message_loop_);
    149 
    150   // Cancel any pending tasks to close paused streams or create new ones.
    151   weak_this_.InvalidateWeakPtrs();
    152 
    153   // No AudioOutputProxy objects should hold a reference to us when we get
    154   // to this stage.
    155   DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
    156 
    157   AudioOutputStreamList::iterator it = idle_streams_.begin();
    158   for (; it != idle_streams_.end(); ++it)
    159     (*it)->Close();
    160   idle_streams_.clear();
    161 
    162   it = pausing_streams_.begin();
    163   for (; it != pausing_streams_.end(); ++it)
    164     (*it)->Close();
    165   pausing_streams_.clear();
    166 }
    167 
    168 bool AudioOutputDispatcherImpl::CreateAndOpenStream() {
    169   DCHECK_EQ(base::MessageLoop::current(), message_loop_);
    170   AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
    171       params_, input_device_id_);
    172   if (!stream)
    173     return false;
    174 
    175   if (!stream->Open()) {
    176     stream->Close();
    177     return false;
    178   }
    179   idle_streams_.push_back(stream);
    180   return true;
    181 }
    182 
    183 void AudioOutputDispatcherImpl::OpenTask() {
    184   DCHECK_EQ(base::MessageLoop::current(), message_loop_);
    185   // Make sure that we have at least one stream allocated if there
    186   // are paused streams.
    187   if (paused_proxies_ > 0 && idle_streams_.empty() &&
    188       pausing_streams_.empty()) {
    189     CreateAndOpenStream();
    190   }
    191 
    192   close_timer_.Reset();
    193 }
    194 
    195 // This method is called by |close_timer_|.
    196 void AudioOutputDispatcherImpl::ClosePendingStreams() {
    197   DCHECK_EQ(base::MessageLoop::current(), message_loop_);
    198   while (!idle_streams_.empty()) {
    199     idle_streams_.back()->Close();
    200     idle_streams_.pop_back();
    201   }
    202 }
    203 
    204 }  // namespace media
    205