• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
AudioStreamContainer(const WavAudioHandler & wav_audio)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 
~AudioStreamContainer()46   virtual ~AudioStreamContainer() {
47     DCHECK(AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
48   }
49 
Play()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 
Stop()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.
OnMoreData(AudioBus * dest,AudioBuffersState)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 
OnError(AudioOutputStream *)127   virtual void OnError(AudioOutputStream* /* stream */) OVERRIDE {
128     LOG(ERROR) << "Error during system sound reproduction.";
129   }
130 
StopStream()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 
AudioStreamHandler(const base::StringPiece & wav_data)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 
~AudioStreamHandler()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 
IsInitialized() const183 bool AudioStreamHandler::IsInitialized() const {
184   DCHECK(CalledOnValidThread());
185   return initialized_;
186 }
187 
Play()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 
Stop()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
SetObserverForTesting(TestObserver * observer)209 void AudioStreamHandler::SetObserverForTesting(TestObserver* observer) {
210   g_observer_for_testing = observer;
211 }
212 
213 // static
SetAudioSourceForTesting(AudioOutputStream::AudioSourceCallback * source)214 void AudioStreamHandler::SetAudioSourceForTesting(
215     AudioOutputStream::AudioSourceCallback* source) {
216   g_audio_source_for_testing = source;
217 }
218 
219 }  // namespace media
220