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 20namespace { 21 22const int kDefaultFrameCount = 1024; 23const double kOutputVolumePercent = 1.0f; 24 25} // namespace 26 27namespace copresence { 28 29// Public methods. 30 31AudioPlayer::AudioPlayer() 32 : is_playing_(false), stream_(NULL), 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 59bool AudioPlayer::IsPlaying() { 60 return is_playing_; 61} 62 63void 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 71void 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 95void 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 115void AudioPlayer::StopOnAudioThread() { 116 DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); 117 if (!stream_) 118 return; 119 120 stream_->Stop(); 121 is_playing_ = false; 122} 123 124void 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 137void AudioPlayer::FinalizeOnAudioThread() { 138 DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); 139 StopAndCloseOnAudioThread(); 140 delete this; 141} 142 143int 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 163void 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 171void 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