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