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/single_thread_task_runner.h"
     12 #include "base/time/time.h"
     13 #include "media/audio/audio_io.h"
     14 #include "media/audio/audio_output_proxy.h"
     15 
     16 namespace media {
     17 
     18 AudioOutputDispatcherImpl::AudioOutputDispatcherImpl(
     19     AudioManager* audio_manager,
     20     const AudioParameters& params,
     21     const std::string& output_device_id,
     22     const base::TimeDelta& close_delay)
     23     : AudioOutputDispatcher(audio_manager,
     24                             params,
     25                             output_device_id),
     26       idle_proxies_(0),
     27       close_timer_(FROM_HERE,
     28                    close_delay,
     29                    this,
     30                    &AudioOutputDispatcherImpl::CloseAllIdleStreams),
     31       audio_log_(
     32           audio_manager->CreateAudioLog(AudioLogFactory::AUDIO_OUTPUT_STREAM)),
     33       audio_stream_id_(0) {}
     34 
     35 AudioOutputDispatcherImpl::~AudioOutputDispatcherImpl() {
     36   DCHECK_EQ(idle_proxies_, 0u);
     37   DCHECK(proxy_to_physical_map_.empty());
     38   DCHECK(idle_streams_.empty());
     39 }
     40 
     41 bool AudioOutputDispatcherImpl::OpenStream() {
     42   DCHECK(task_runner_->BelongsToCurrentThread());
     43 
     44   // Ensure that there is at least one open stream.
     45   if (idle_streams_.empty() && !CreateAndOpenStream())
     46     return false;
     47 
     48   ++idle_proxies_;
     49   close_timer_.Reset();
     50   return true;
     51 }
     52 
     53 bool AudioOutputDispatcherImpl::StartStream(
     54     AudioOutputStream::AudioSourceCallback* callback,
     55     AudioOutputProxy* stream_proxy) {
     56   DCHECK(task_runner_->BelongsToCurrentThread());
     57   DCHECK(proxy_to_physical_map_.find(stream_proxy) ==
     58          proxy_to_physical_map_.end());
     59 
     60   if (idle_streams_.empty() && !CreateAndOpenStream())
     61     return false;
     62 
     63   AudioOutputStream* physical_stream = idle_streams_.back();
     64   idle_streams_.pop_back();
     65 
     66   DCHECK_GT(idle_proxies_, 0u);
     67   --idle_proxies_;
     68 
     69   double volume = 0;
     70   stream_proxy->GetVolume(&volume);
     71   physical_stream->SetVolume(volume);
     72   const int stream_id = audio_stream_ids_[physical_stream];
     73   audio_log_->OnSetVolume(stream_id, volume);
     74   physical_stream->Start(callback);
     75   audio_log_->OnStarted(stream_id);
     76   proxy_to_physical_map_[stream_proxy] = physical_stream;
     77 
     78   close_timer_.Reset();
     79   return true;
     80 }
     81 
     82 void AudioOutputDispatcherImpl::StopStream(AudioOutputProxy* stream_proxy) {
     83   DCHECK(task_runner_->BelongsToCurrentThread());
     84 
     85   AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
     86   DCHECK(it != proxy_to_physical_map_.end());
     87   AudioOutputStream* physical_stream = it->second;
     88   proxy_to_physical_map_.erase(it);
     89 
     90   physical_stream->Stop();
     91   audio_log_->OnStopped(audio_stream_ids_[physical_stream]);
     92   ++idle_proxies_;
     93   idle_streams_.push_back(physical_stream);
     94 
     95   close_timer_.Reset();
     96 }
     97 
     98 void AudioOutputDispatcherImpl::StreamVolumeSet(AudioOutputProxy* stream_proxy,
     99                                                 double volume) {
    100   DCHECK(task_runner_->BelongsToCurrentThread());
    101   AudioStreamMap::iterator it = proxy_to_physical_map_.find(stream_proxy);
    102   if (it != proxy_to_physical_map_.end()) {
    103     AudioOutputStream* physical_stream = it->second;
    104     physical_stream->SetVolume(volume);
    105     audio_log_->OnSetVolume(audio_stream_ids_[physical_stream], volume);
    106   }
    107 }
    108 
    109 void AudioOutputDispatcherImpl::CloseStream(AudioOutputProxy* stream_proxy) {
    110   DCHECK(task_runner_->BelongsToCurrentThread());
    111 
    112   DCHECK_GT(idle_proxies_, 0u);
    113   --idle_proxies_;
    114 
    115   // Leave at least a single stream running until the close timer fires to help
    116   // cycle time when streams are opened and closed repeatedly.
    117   CloseIdleStreams(std::max(idle_proxies_, static_cast<size_t>(1)));
    118   close_timer_.Reset();
    119 }
    120 
    121 void AudioOutputDispatcherImpl::Shutdown() {
    122   DCHECK(task_runner_->BelongsToCurrentThread());
    123 
    124   // Close all idle streams immediately.  The |close_timer_| will handle
    125   // invalidating any outstanding tasks upon its destruction.
    126   CloseAllIdleStreams();
    127 
    128   // No AudioOutputProxy objects should hold a reference to us when we get
    129   // to this stage.
    130   DCHECK(HasOneRef()) << "Only the AudioManager should hold a reference";
    131 }
    132 
    133 bool AudioOutputDispatcherImpl::CreateAndOpenStream() {
    134   DCHECK(task_runner_->BelongsToCurrentThread());
    135   AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
    136       params_, device_id_);
    137   if (!stream)
    138     return false;
    139 
    140   if (!stream->Open()) {
    141     stream->Close();
    142     return false;
    143   }
    144 
    145   const int stream_id = audio_stream_id_++;
    146   audio_stream_ids_[stream] = stream_id;
    147   audio_log_->OnCreated(
    148       stream_id, params_, device_id_);
    149 
    150   idle_streams_.push_back(stream);
    151   return true;
    152 }
    153 
    154 void AudioOutputDispatcherImpl::CloseAllIdleStreams() {
    155   DCHECK(task_runner_->BelongsToCurrentThread());
    156   CloseIdleStreams(0);
    157 }
    158 
    159 void AudioOutputDispatcherImpl::CloseIdleStreams(size_t keep_alive) {
    160   DCHECK(task_runner_->BelongsToCurrentThread());
    161   if (idle_streams_.size() <= keep_alive)
    162     return;
    163   for (size_t i = keep_alive; i < idle_streams_.size(); ++i) {
    164     AudioOutputStream* stream = idle_streams_[i];
    165     stream->Close();
    166 
    167     AudioStreamIDMap::iterator it = audio_stream_ids_.find(stream);
    168     DCHECK(it != audio_stream_ids_.end());
    169     audio_log_->OnClosed(it->second);
    170     audio_stream_ids_.erase(it);
    171   }
    172   idle_streams_.erase(idle_streams_.begin() + keep_alive, idle_streams_.end());
    173 }
    174 
    175 }  // namespace media
    176