• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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 "components/copresence/mediums/audio/audio_player.h"
6 
7 #include <algorithm>
8 #include <string>
9 
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/logging.h"
13 #include "base/run_loop.h"
14 #include "components/copresence/public/copresence_constants.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "media/audio/audio_manager.h"
17 #include "media/audio/audio_parameters.h"
18 #include "media/base/audio_bus.h"
19 
20 namespace {
21 
22 const int kDefaultFrameCount = 1024;
23 const double kOutputVolumePercent = 1.0f;
24 
25 }  // namespace
26 
27 namespace copresence {
28 
29 // Public methods.
30 
AudioPlayer()31 AudioPlayer::AudioPlayer()
32     : is_playing_(false), stream_(NULL), frame_index_(0) {
33 }
34 
~AudioPlayer()35 AudioPlayer::~AudioPlayer() {
36 }
37 
Initialize()38 void AudioPlayer::Initialize() {
39   media::AudioManager::Get()->GetTaskRunner()->PostTask(
40       FROM_HERE,
41       base::Bind(&AudioPlayer::InitializeOnAudioThread,
42                  base::Unretained(this)));
43 }
44 
Play(const scoped_refptr<media::AudioBusRefCounted> & samples)45 void AudioPlayer::Play(
46     const scoped_refptr<media::AudioBusRefCounted>& samples) {
47   media::AudioManager::Get()->GetTaskRunner()->PostTask(
48       FROM_HERE,
49       base::Bind(
50           &AudioPlayer::PlayOnAudioThread, base::Unretained(this), samples));
51 }
52 
Stop()53 void AudioPlayer::Stop() {
54   media::AudioManager::Get()->GetTaskRunner()->PostTask(
55       FROM_HERE,
56       base::Bind(&AudioPlayer::StopOnAudioThread, base::Unretained(this)));
57 }
58 
IsPlaying()59 bool AudioPlayer::IsPlaying() {
60   return is_playing_;
61 }
62 
Finalize()63 void AudioPlayer::Finalize() {
64   media::AudioManager::Get()->GetTaskRunner()->PostTask(
65       FROM_HERE,
66       base::Bind(&AudioPlayer::FinalizeOnAudioThread, base::Unretained(this)));
67 }
68 
69 // Private methods.
70 
InitializeOnAudioThread()71 void AudioPlayer::InitializeOnAudioThread() {
72   DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
73   stream_ = output_stream_for_testing_
74                 ? output_stream_for_testing_.get()
75                 : media::AudioManager::Get()->MakeAudioOutputStreamProxy(
76                       media::AudioParameters(
77                           media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
78                           media::CHANNEL_LAYOUT_MONO,
79                           kDefaultSampleRate,
80                           kDefaultBitsPerSample,
81                           kDefaultFrameCount),
82                       std::string());
83 
84   if (!stream_ || !stream_->Open()) {
85     LOG(ERROR) << "Failed to open an output stream.";
86     if (stream_) {
87       stream_->Close();
88       stream_ = NULL;
89     }
90     return;
91   }
92   stream_->SetVolume(kOutputVolumePercent);
93 }
94 
PlayOnAudioThread(const scoped_refptr<media::AudioBusRefCounted> & samples)95 void AudioPlayer::PlayOnAudioThread(
96     const scoped_refptr<media::AudioBusRefCounted>& samples) {
97   DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
98   if (!stream_)
99     return;
100 
101   {
102     base::AutoLock al(state_lock_);
103 
104     samples_ = samples;
105     frame_index_ = 0;
106 
107     if (is_playing_)
108       return;
109   }
110 
111   is_playing_ = true;
112   stream_->Start(this);
113 }
114 
StopOnAudioThread()115 void AudioPlayer::StopOnAudioThread() {
116   DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
117   if (!stream_)
118     return;
119 
120   stream_->Stop();
121   is_playing_ = false;
122 }
123 
StopAndCloseOnAudioThread()124 void AudioPlayer::StopAndCloseOnAudioThread() {
125   DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
126   if (!stream_)
127     return;
128 
129   if (is_playing_)
130     stream_->Stop();
131   stream_->Close();
132   stream_ = NULL;
133 
134   is_playing_ = false;
135 }
136 
FinalizeOnAudioThread()137 void AudioPlayer::FinalizeOnAudioThread() {
138   DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
139   StopAndCloseOnAudioThread();
140   delete this;
141 }
142 
OnMoreData(media::AudioBus * dest,media::AudioBuffersState)143 int AudioPlayer::OnMoreData(media::AudioBus* dest,
144                             media::AudioBuffersState /* state */) {
145   base::AutoLock al(state_lock_);
146   // Continuously play our samples till explicitly told to stop.
147   const int leftover_frames = samples_->frames() - frame_index_;
148   const int frames_to_copy = std::min(dest->frames(), leftover_frames);
149 
150   samples_->CopyPartialFramesTo(frame_index_, frames_to_copy, 0, dest);
151   frame_index_ += frames_to_copy;
152 
153   // If we didn't fill the destination audio bus, wrap around and fill the rest.
154   if (leftover_frames <= dest->frames()) {
155     samples_->CopyPartialFramesTo(
156         0, dest->frames() - frames_to_copy, frames_to_copy, dest);
157     frame_index_ = dest->frames() - frames_to_copy;
158   }
159 
160   return dest->frames();
161 }
162 
OnError(media::AudioOutputStream *)163 void AudioPlayer::OnError(media::AudioOutputStream* /* stream */) {
164   LOG(ERROR) << "Error during system sound reproduction.";
165   media::AudioManager::Get()->GetTaskRunner()->PostTask(
166       FROM_HERE,
167       base::Bind(&AudioPlayer::StopAndCloseOnAudioThread,
168                  base::Unretained(this)));
169 }
170 
FlushAudioLoopForTesting()171 void AudioPlayer::FlushAudioLoopForTesting() {
172   if (media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread())
173     return;
174 
175   // Queue task on the audio thread, when it is executed, that means we've
176   // successfully executed all the tasks before us.
177   base::RunLoop rl;
178   media::AudioManager::Get()->GetTaskRunner()->PostTaskAndReply(
179       FROM_HERE,
180       base::Bind(base::IgnoreResult(&AudioPlayer::FlushAudioLoopForTesting),
181                  base::Unretained(this)),
182       rl.QuitClosure());
183   rl.Run();
184 }
185 
186 }  // namespace copresence
187