audio_player.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
11320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Copyright 2014 The Chromium Authors. All rights reserved.
21320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// Use of this source code is governed by a BSD-style license that can be
31320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci// found in the LICENSE file.
41320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
51320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "components/copresence/mediums/audio/audio_player.h"
61320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
71320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include <algorithm>
81320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include <vector>
91320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
101320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/bind.h"
111320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/bind_helpers.h"
121320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/logging.h"
131320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "base/run_loop.h"
141320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "components/copresence/public/copresence_constants.h"
151320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "content/public/browser/browser_thread.h"
161320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "media/audio/audio_manager.h"
171320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "media/audio/audio_parameters.h"
181320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "media/base/audio_bus.h"
191320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
201320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccinamespace {
211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
22const int kDefaultFrameCount = 1024;
23const double kOutputVolumePercent = 1.0f;
24
25}  // namespace
26
27namespace copresence {
28
29// Public methods.
30
31AudioPlayer::AudioPlayer()
32    : stream_(NULL), is_playing_(false), frame_index_(0) {
33}
34
35AudioPlayer::~AudioPlayer() {
36}
37
38void AudioPlayer::Initialize() {
39  media::AudioManager::Get()->GetTaskRunner()->PostTask(
40      FROM_HERE,
41      base::Bind(&AudioPlayer::InitializeOnAudioThread,
42                 base::Unretained(this)));
43}
44
45void 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
53void AudioPlayer::Stop() {
54  media::AudioManager::Get()->GetTaskRunner()->PostTask(
55      FROM_HERE,
56      base::Bind(&AudioPlayer::StopOnAudioThread, base::Unretained(this)));
57}
58
59void AudioPlayer::Finalize() {
60  media::AudioManager::Get()->GetTaskRunner()->PostTask(
61      FROM_HERE,
62      base::Bind(&AudioPlayer::FinalizeOnAudioThread, base::Unretained(this)));
63}
64
65// Private methods.
66
67void AudioPlayer::InitializeOnAudioThread() {
68  DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
69  stream_ = output_stream_for_testing_
70                ? output_stream_for_testing_.get()
71                : media::AudioManager::Get()->MakeAudioOutputStreamProxy(
72                      media::AudioParameters(
73                          media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
74                          media::CHANNEL_LAYOUT_MONO,
75                          kDefaultSampleRate,
76                          kDefaultBitsPerSample,
77                          kDefaultFrameCount),
78                      std::string());
79
80  if (!stream_ || !stream_->Open()) {
81    LOG(ERROR) << "Failed to open an output stream.";
82    if (stream_) {
83      stream_->Close();
84      stream_ = NULL;
85    }
86    return;
87  }
88  stream_->SetVolume(kOutputVolumePercent);
89}
90
91void AudioPlayer::PlayOnAudioThread(
92    const scoped_refptr<media::AudioBusRefCounted>& samples) {
93  DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
94  if (!stream_)
95    return;
96
97  {
98    base::AutoLock al(state_lock_);
99
100    samples_ = samples;
101    frame_index_ = 0;
102
103    if (is_playing_)
104      return;
105  }
106
107  DVLOG(2) << "Playing Audio.";
108  is_playing_ = true;
109  stream_->Start(this);
110}
111
112void AudioPlayer::StopOnAudioThread() {
113  DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
114  if (!stream_)
115    return;
116
117  stream_->Stop();
118  is_playing_ = false;
119}
120
121void AudioPlayer::StopAndCloseOnAudioThread() {
122  DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
123  if (!stream_)
124    return;
125
126  if (is_playing_)
127    stream_->Stop();
128  stream_->Close();
129  stream_ = NULL;
130
131  is_playing_ = false;
132}
133
134void AudioPlayer::FinalizeOnAudioThread() {
135  DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread());
136  StopAndCloseOnAudioThread();
137  delete this;
138}
139
140int AudioPlayer::OnMoreData(media::AudioBus* dest,
141                            media::AudioBuffersState /* state */) {
142  base::AutoLock al(state_lock_);
143  // Continuously play our samples till explicitly told to stop.
144  const int leftover_frames = samples_->frames() - frame_index_;
145  const int frames_to_copy = std::min(dest->frames(), leftover_frames);
146
147  samples_->CopyPartialFramesTo(frame_index_, frames_to_copy, 0, dest);
148  frame_index_ += frames_to_copy;
149
150  // If we didn't fill the destination audio bus, wrap around and fill the rest.
151  if (leftover_frames <= dest->frames()) {
152    samples_->CopyPartialFramesTo(
153        0, dest->frames() - frames_to_copy, frames_to_copy, dest);
154    frame_index_ = dest->frames() - frames_to_copy;
155  }
156
157  return dest->frames();
158}
159
160void AudioPlayer::OnError(media::AudioOutputStream* /* stream */) {
161  LOG(ERROR) << "Error during system sound reproduction.";
162  media::AudioManager::Get()->GetTaskRunner()->PostTask(
163      FROM_HERE,
164      base::Bind(&AudioPlayer::StopAndCloseOnAudioThread,
165                 base::Unretained(this)));
166}
167
168void AudioPlayer::FlushAudioLoopForTesting() {
169  if (media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread())
170    return;
171
172  // Queue task on the audio thread, when it is executed, that means we've
173  // successfully executed all the tasks before us.
174  base::RunLoop rl;
175  media::AudioManager::Get()->GetTaskRunner()->PostTaskAndReply(
176      FROM_HERE,
177      base::Bind(base::IgnoreResult(&AudioPlayer::FlushAudioLoopForTesting),
178                 base::Unretained(this)),
179      rl.QuitClosure());
180  rl.Run();
181}
182
183}  // namespace copresence
184