Home | History | Annotate | Download | only in sounds
      1 // Copyright 2013 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/sounds/audio_stream_handler.h"
      6 
      7 #include <string>
      8 
      9 #include "base/cancelable_callback.h"
     10 #include "base/logging.h"
     11 #include "base/single_thread_task_runner.h"
     12 #include "base/synchronization/lock.h"
     13 #include "base/time/time.h"
     14 #include "media/audio/audio_manager.h"
     15 #include "media/audio/audio_manager_base.h"
     16 #include "media/base/channel_layout.h"
     17 
     18 namespace media {
     19 
     20 namespace {
     21 
     22 // Volume percent.
     23 const double kOutputVolumePercent = 0.8;
     24 
     25 // The number of frames each OnMoreData() call will request.
     26 const int kDefaultFrameCount = 1024;
     27 
     28 // Keep alive timeout for audio stream.
     29 const int kKeepAliveMs = 1500;
     30 
     31 AudioStreamHandler::TestObserver* g_observer_for_testing = NULL;
     32 AudioOutputStream::AudioSourceCallback* g_audio_source_for_testing = NULL;
     33 
     34 }  // namespace
     35 
     36 class AudioStreamHandler::AudioStreamContainer
     37     : public AudioOutputStream::AudioSourceCallback {
     38  public:
     39   AudioStreamContainer(const WavAudioHandler& wav_audio)
     40       : started_(false),
     41         stream_(NULL),
     42         cursor_(0),
     43         delayed_stop_posted_(false),
     44         wav_audio_(wav_audio) {}
     45 
     46   virtual ~AudioStreamContainer() {
     47     DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
     48   }
     49 
     50   void Play() {
     51     DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
     52 
     53     if (!stream_) {
     54       const AudioParameters& p = wav_audio_.params();
     55       const AudioParameters params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
     56                                    p.channel_layout(),
     57                                    p.sample_rate(),
     58                                    p.bits_per_sample(),
     59                                    kDefaultFrameCount);
     60       stream_ = AudioManager::Get()->MakeAudioOutputStreamProxy(params,
     61                                                                 std::string());
     62       if (!stream_ || !stream_->Open()) {
     63         LOG(ERROR) << "Failed to open an output stream.";
     64         return;
     65       }
     66       stream_->SetVolume(kOutputVolumePercent);
     67     }
     68 
     69     {
     70       base::AutoLock al(state_lock_);
     71 
     72       delayed_stop_posted_ = false;
     73       stop_closure_.Reset(base::Bind(&AudioStreamContainer::StopStream,
     74                                      base::Unretained(this)));
     75 
     76       if (started_) {
     77         if (wav_audio_.AtEnd(cursor_))
     78           cursor_ = 0;
     79         return;
     80       }
     81 
     82       cursor_ = 0;
     83     }
     84 
     85     started_ = true;
     86     if (g_audio_source_for_testing)
     87       stream_->Start(g_audio_source_for_testing);
     88     else
     89       stream_->Start(this);
     90 
     91     if (g_observer_for_testing)
     92       g_observer_for_testing->OnPlay();
     93   }
     94 
     95   void Stop() {
     96     DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
     97     StopStream();
     98     if (stream_)
     99       stream_->Close();
    100     stream_ = NULL;
    101     stop_closure_.Cancel();
    102   }
    103 
    104  private:
    105   // AudioOutputStream::AudioSourceCallback overrides:
    106   // Following methods could be called from *ANY* thread.
    107   virtual int OnMoreData(AudioBus* dest,
    108                          AudioBuffersState /* state */) OVERRIDE {
    109     base::AutoLock al(state_lock_);
    110     size_t bytes_written = 0;
    111 
    112     if (wav_audio_.AtEnd(cursor_) ||
    113         !wav_audio_.CopyTo(dest, cursor_, &bytes_written)) {
    114       if (delayed_stop_posted_)
    115         return 0;
    116       delayed_stop_posted_ = true;
    117       AudioManager::Get()->GetTaskRunner()->PostDelayedTask(
    118           FROM_HERE,
    119           stop_closure_.callback(),
    120           base::TimeDelta::FromMilliseconds(kKeepAliveMs));
    121       return 0;
    122     }
    123     cursor_ += bytes_written;
    124     return dest->frames();
    125   }
    126 
    127   virtual void OnError(AudioOutputStream* /* stream */) OVERRIDE {
    128     LOG(ERROR) << "Error during system sound reproduction.";
    129   }
    130 
    131   void StopStream() {
    132     DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
    133 
    134     if (stream_ && started_) {
    135       // Do not hold the |state_lock_| while stopping the output stream.
    136       stream_->Stop();
    137       if (g_observer_for_testing)
    138         g_observer_for_testing->OnStop(cursor_);
    139     }
    140 
    141     started_ = false;
    142   }
    143 
    144   // Must only be accessed on the AudioManager::GetTaskRunner() thread.
    145   bool started_;
    146   AudioOutputStream* stream_;
    147 
    148   // All variables below must be accessed under |state_lock_| when |started_|.
    149   base::Lock state_lock_;
    150   size_t cursor_;
    151   bool delayed_stop_posted_;
    152   const WavAudioHandler wav_audio_;
    153   base::CancelableClosure stop_closure_;
    154 
    155   DISALLOW_COPY_AND_ASSIGN(AudioStreamContainer);
    156 };
    157 
    158 AudioStreamHandler::AudioStreamHandler(const base::StringPiece& wav_data)
    159     : wav_audio_(wav_data),
    160       initialized_(false) {
    161   AudioManager* manager = AudioManager::Get();
    162   if (!manager) {
    163     LOG(ERROR) << "Can't get access to audio manager.";
    164     return;
    165   }
    166   if (!wav_audio_.params().IsValid()) {
    167     LOG(ERROR) << "Audio params are invalid.";
    168     return;
    169   }
    170   stream_.reset(new AudioStreamContainer(wav_audio_));
    171   initialized_ = true;
    172 }
    173 
    174 AudioStreamHandler::~AudioStreamHandler() {
    175   DCHECK(CalledOnValidThread());
    176   AudioManager::Get()->GetTaskRunner()->PostTask(
    177       FROM_HERE,
    178       base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get())));
    179   AudioManager::Get()->GetTaskRunner()->DeleteSoon(FROM_HERE,
    180                                                    stream_.release());
    181 }
    182 
    183 bool AudioStreamHandler::IsInitialized() const {
    184   DCHECK(CalledOnValidThread());
    185   return initialized_;
    186 }
    187 
    188 bool AudioStreamHandler::Play() {
    189   DCHECK(CalledOnValidThread());
    190 
    191   if (!IsInitialized())
    192     return false;
    193 
    194   AudioManager::Get()->GetTaskRunner()->PostTask(
    195       FROM_HERE,
    196       base::Bind(base::IgnoreResult(&AudioStreamContainer::Play),
    197                  base::Unretained(stream_.get())));
    198   return true;
    199 }
    200 
    201 void AudioStreamHandler::Stop() {
    202   DCHECK(CalledOnValidThread());
    203   AudioManager::Get()->GetTaskRunner()->PostTask(
    204       FROM_HERE,
    205       base::Bind(&AudioStreamContainer::Stop, base::Unretained(stream_.get())));
    206 }
    207 
    208 // static
    209 void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) {
    210   g_observer_for_testing = observer;
    211 }
    212 
    213 // static
    214 void AudioStreamHandler::SetAudioSourceForTesting(
    215     AudioOutputStream::AudioSourceCallback* source) {
    216   g_audio_source_for_testing = source;
    217 }
    218 
    219 }  // namespace media
    220