Chromium Code Reviews| Index: components/copresence/mediums/audio/audio_player.cc |
| diff --git a/components/copresence/mediums/audio/audio_player.cc b/components/copresence/mediums/audio/audio_player.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..017f6ded6653df22ae505e9dec80a9ef7c6a7c83 |
| --- /dev/null |
| +++ b/components/copresence/mediums/audio/audio_player.cc |
| @@ -0,0 +1,199 @@ |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "components/copresence/mediums/audio/audio_player.h" |
| + |
| +#include <algorithm> |
| +#include <vector> |
| + |
| +#include "base/bind.h" |
| +#include "base/bind_helpers.h" |
| +#include "base/logging.h" |
| +#include "base/run_loop.h" |
| +#include "components/copresence/common/copresence_constants.h" |
| +#include "content/public/browser/browser_thread.h" |
| +#include "media/audio/audio_manager.h" |
| +#include "media/audio/audio_parameters.h" |
| +#include "media/base/audio_bus.h" |
| + |
| +namespace { |
| + |
| +const int kDefaultFrameCount = 1024; |
| +const double kOutputVolumePercent = 1.0; |
| +} |
|
xiyuan
2014/07/25 21:02:10
nit: insert an empty line before and add " // nam
rkc
2014/07/28 21:02:02
Done.
|
| + |
| +namespace copresence { |
| + |
| +// Public methods. |
| + |
| +AudioPlayer::AudioPlayer() |
| + : stream_(NULL), |
| + frame_index_(0), |
| + is_playing_(false), |
| + output_stream_for_testing_(NULL) { |
| + media::AudioManager::Get()->GetTaskRunner()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&AudioPlayer::InitializeOnAudioThread, |
| + base::Unretained(this))); |
| +} |
| + |
| +AudioPlayer::~AudioPlayer() { |
|
Daniel Erat
2014/07/28 21:18:18
please use the same order in the .cc file as in th
rkc
2014/07/29 00:33:35
Done.
|
| +} |
| + |
| +AudioPlayer::AudioPlayer(media::AudioOutputStream* output_stream_for_testing) |
| + : stream_(NULL), |
| + frame_index_(0), |
| + is_playing_(false), |
| + output_stream_for_testing_(output_stream_for_testing) { |
| + media::AudioManager::Get()->GetTaskRunner()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&AudioPlayer::InitializeOnAudioThread, |
|
Daniel Erat
2014/07/28 21:18:17
how about adding a separate Init() method that doe
rkc
2014/07/29 00:33:35
Done.
|
| + base::Unretained(this))); |
| +} |
| + |
| +void AudioPlayer::Play( |
| + const scoped_refptr<media::AudioBusRefCounted>& samples) { |
| + media::AudioManager::Get()->GetTaskRunner()->PostTask( |
| + FROM_HERE, |
| + base::Bind( |
| + &AudioPlayer::PlayOnAudioThread, base::Unretained(this), samples)); |
| +} |
| + |
| +void AudioPlayer::Stop() { |
| + media::AudioManager::Get()->GetTaskRunner()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&AudioPlayer::StopOnAudioThread, base::Unretained(this))); |
| +} |
| + |
| +void AudioPlayer::Finalize() { |
| + media::AudioManager::Get()->GetTaskRunner()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&AudioPlayer::FinalizeOnAudioThread, base::Unretained(this))); |
| +} |
| + |
| +// Private methods. |
| + |
| +void AudioPlayer::InitializeOnAudioThread() { |
| + DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); |
| + stream_ = output_stream_for_testing_ |
| + ? output_stream_for_testing_ |
| + : media::AudioManager::Get()->MakeAudioOutputStreamProxy( |
| + media::AudioParameters( |
| + media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| + media::CHANNEL_LAYOUT_MONO, |
| + kDefaultSampleRate, |
| + kDefaultBitsPerSample, |
| + kDefaultFrameCount), |
| + std::string()); |
| + |
| + if (!stream_ || !stream_->Open()) { |
| + LOG(ERROR) << "Failed to open an output stream."; |
| + if (stream_) { |
| + stream_->Close(); |
| + stream_ = NULL; |
| + } |
| + return; |
| + } |
| + stream_->SetVolume(kOutputVolumePercent); |
| +} |
| + |
| +void AudioPlayer::PlayOnAudioThread( |
| + const scoped_refptr<media::AudioBusRefCounted>& samples) { |
| + DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); |
| + if (!stream_) |
| + return; |
| + |
| + { |
| + base::AutoLock al(state_lock_); |
| + |
| + samples_ = samples; |
| + frame_index_ = 0; |
| + |
| + if (is_playing_) |
| + return; |
| + } |
| + |
| + DVLOG(2) << "Playing Audio."; |
| + is_playing_ = true; |
|
Daniel Erat
2014/07/28 21:18:17
the header has a comment saying that all members a
rkc
2014/07/29 00:33:35
All the methods that must run on certain threads h
|
| + stream_->Start(this); |
| +} |
| + |
| +void AudioPlayer::StopOnAudioThread() { |
| + DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); |
| + if (!stream_) |
| + return; |
| + |
| + stream_->Stop(); |
| + is_playing_ = false; |
| +} |
| + |
| +void AudioPlayer::StopAndCloseOnAudioThread() { |
| + DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); |
| + if (!stream_) |
| + return; |
| + |
| + if (is_playing_) |
| + stream_->Stop(); |
| + stream_->Close(); |
| + stream_ = NULL; |
| + |
| + is_playing_ = false; |
| +} |
| + |
| +void AudioPlayer::FinalizeOnAudioThread() { |
| + DCHECK(media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()); |
| + StopAndCloseOnAudioThread(); |
| + delete this; |
| +} |
| + |
| +int AudioPlayer::OnMoreData(media::AudioBus* dest, |
| + media::AudioBuffersState /* state */) { |
| + base::AutoLock al(state_lock_); |
| + DCHECK(is_playing_); |
| + |
| + // Continuously play our samples till explicitly told to stop. |
| + const int leftover_frames = samples_->frames() - frame_index_; |
| + const int frames_to_copy = std::min(dest->frames(), leftover_frames); |
| + |
| + samples_->CopyPartialFramesTo(frame_index_, frames_to_copy, 0, dest); |
| + frame_index_ += frames_to_copy; |
| + |
| + // If we didn't fill the destination audio bus, wrap around and fill the rest. |
| + if (leftover_frames <= dest->frames()) { |
| + samples_->CopyPartialFramesTo( |
| + 0, dest->frames() - frames_to_copy, frames_to_copy, dest); |
| + frame_index_ = dest->frames() - frames_to_copy; |
| + } |
| + |
| + return dest->frames(); |
| +} |
| + |
| +void AudioPlayer::OnError(media::AudioOutputStream* /* stream */) { |
| + LOG(ERROR) << "Error during system sound reproduction."; |
| + media::AudioManager::Get()->GetTaskRunner()->PostTask( |
| + FROM_HERE, |
| + base::Bind(&AudioPlayer::StopAndCloseOnAudioThread, |
| + base::Unretained(this))); |
| +} |
| + |
| +void AudioPlayer::FlushAudioLoop() { |
| + if (media::AudioManager::Get()->GetTaskRunner()->BelongsToCurrentThread()) |
| + return; |
| + |
| + // All this does is flush the tasks on the audio thread. |
| + base::RunLoop rl; |
|
DaleCurtis
2014/07/25 20:41:29
This isn't allowed in production code. See base/r
rkc
2014/07/28 21:02:02
Done.
|
| + media::AudioManager::Get()->GetTaskRunner()->PostTaskAndReply( |
| + FROM_HERE, |
| + base::Bind(base::IgnoreResult(&AudioPlayer::IsPlaying), |
| + base::Unretained(this)), |
| + rl.QuitClosure()); |
| + rl.Run(); |
| +} |
| + |
| +bool AudioPlayer::IsPlaying() { |
| + FlushAudioLoop(); |
| + return is_playing_; |
| +} |
| + |
| +} // namespace copresence |