diff options
author | garykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-10 02:37:47 +0000 |
---|---|---|
committer | garykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-10 02:37:47 +0000 |
commit | 95674a7482430d7ca837a8667e450081482335f1 (patch) | |
tree | 802a6b2ee5805d6a30668d896dca46b3495f0a05 /remoting/client | |
parent | bd918adb442d90922182820d1b52851a404004e4 (diff) | |
download | chromium_src-95674a7482430d7ca837a8667e450081482335f1.zip chromium_src-95674a7482430d7ca837a8667e450081482335f1.tar.gz chromium_src-95674a7482430d7ca837a8667e450081482335f1.tar.bz2 |
[Chromoting] Refactor audio playback to make it testable and add unittests.
Review URL: https://chromiumcodereview.appspot.com/11013029
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@161017 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting/client')
-rw-r--r-- | remoting/client/audio_player.cc | 124 | ||||
-rw-r--r-- | remoting/client/audio_player.h | 53 | ||||
-rw-r--r-- | remoting/client/audio_player_unittest.cc | 335 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_audio_player.cc | 154 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_audio_player.h | 41 |
5 files changed, 531 insertions, 176 deletions
diff --git a/remoting/client/audio_player.cc b/remoting/client/audio_player.cc new file mode 100644 index 0000000..ac61e4f --- /dev/null +++ b/remoting/client/audio_player.cc @@ -0,0 +1,124 @@ +// Copyright (c) 2012 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 "remoting/client/audio_player.h" + +#include <algorithm> + +#include "base/stl_util.h" + +// The number of channels in the audio stream (only supporting stereo audio +// for now). +const int kChannels = 2u; +const int kSampleSizeBytes = 2; + +// If queue grows bigger than 150ms we start dropping packets. +const int kMaxQueueLatencyMs = 150; + +namespace remoting { + +AudioPlayer::AudioPlayer() + : sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID), + start_failed_(false), + queued_samples_(0), + bytes_consumed_(0) { +} + +AudioPlayer::~AudioPlayer() {} + +void AudioPlayer::ProcessAudioPacket(scoped_ptr<AudioPacket> packet) { + CHECK_EQ(1, packet->data_size()); + DCHECK_EQ(AudioPacket::ENCODING_RAW, packet->encoding()); + DCHECK_NE(AudioPacket::SAMPLING_RATE_INVALID, packet->sampling_rate()); + DCHECK_EQ(kSampleSizeBytes, packet->bytes_per_sample()); + DCHECK_EQ(static_cast<int>(kChannels), packet->channels()); + DCHECK_EQ(packet->data(0).size() % (kChannels * kSampleSizeBytes), 0u); + + // No-op if the Pepper player won't start. + if (start_failed_) { + return; + } + + // Start the Pepper audio player if this is the first packet. + if (sampling_rate_ != packet->sampling_rate()) { + // Drop all packets currently in the queue, since they are sampled at the + // wrong rate. + { + base::AutoLock auto_lock(lock_); + STLDeleteElements(&queued_packets_); + queued_samples_ = 0; + bytes_consumed_ = 0; + } + + sampling_rate_ = packet->sampling_rate(); + bool success = ResetAudioPlayer(sampling_rate_); + if (!success) { + start_failed_ = true; + return; + } + } + + base::AutoLock auto_lock(lock_); + + if (queued_samples_ > kMaxQueueLatencyMs * sampling_rate_ / + base::Time::kMillisecondsPerSecond) { + STLDeleteElements(&queued_packets_); + queued_samples_ = 0; + bytes_consumed_ = 0; + } + + queued_samples_ += packet->data(0).size() / (kChannels * kSampleSizeBytes); + queued_packets_.push_back(packet.release()); +} + +// static +void AudioPlayer::AudioPlayerCallback(void* samples, + uint32 buffer_size, + void* data) { + AudioPlayer* audio_player = static_cast<AudioPlayer*>(data); + audio_player->FillWithSamples(samples, buffer_size); +} + +void AudioPlayer::FillWithSamples(void* samples, uint32 buffer_size) { + base::AutoLock auto_lock(lock_); + + const size_t bytes_needed = kChannels * kSampleSizeBytes * + GetSamplesPerFrame(); + + // Make sure we don't overrun the buffer. + CHECK_EQ(buffer_size, bytes_needed); + + char* next_sample = static_cast<char*>(samples); + size_t bytes_extracted = 0; + + while (bytes_extracted < bytes_needed) { + // Check if we've run out of samples for this packet. + if (queued_packets_.empty()) { + memset(next_sample, 0, bytes_needed - bytes_extracted); + return; + } + + // Pop off the packet if we've already consumed all its bytes. + if (queued_packets_.front()->data(0).size() == bytes_consumed_) { + delete queued_packets_.front(); + queued_packets_.pop_front(); + bytes_consumed_ = 0; + continue; + } + + const std::string& packet_data = queued_packets_.front()->data(0); + size_t bytes_to_copy = std::min( + packet_data.size() - bytes_consumed_, + bytes_needed - bytes_extracted); + memcpy(next_sample, packet_data.data() + bytes_consumed_, bytes_to_copy); + + next_sample += bytes_to_copy; + bytes_consumed_ += bytes_to_copy; + bytes_extracted += bytes_to_copy; + queued_samples_ -= bytes_to_copy / kSampleSizeBytes / kChannels; + DCHECK_GE(queued_samples_, 0); + } +} + +} // namespace remoting diff --git a/remoting/client/audio_player.h b/remoting/client/audio_player.h index 0397d09..1272fd8 100644 --- a/remoting/client/audio_player.h +++ b/remoting/client/audio_player.h @@ -5,26 +5,61 @@ #ifndef REMOTING_CLIENT_AUDIO_PLAYER_H_ #define REMOTING_CLIENT_AUDIO_PLAYER_H_ -#include "base/memory/scoped_ptr.h" +#include <list> -namespace pp { -class Instance; -} // namespace pp +#include "base/bind.h" +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/ref_counted.h" +#include "base/synchronization/lock.h" +#include "remoting/proto/audio.pb.h" +#include "testing/gtest/include/gtest/gtest.h" namespace remoting { -class AudioPacket; - class AudioPlayer { public: - virtual ~AudioPlayer() {} + virtual ~AudioPlayer(); - virtual void ProcessAudioPacket(scoped_ptr<AudioPacket> packet) = 0; + void ProcessAudioPacket(scoped_ptr<AudioPacket> packet); protected: - AudioPlayer() {} + AudioPlayer(); + + // Return the recommended number of samples to include in a frame. + virtual uint32 GetSamplesPerFrame() = 0; + + // Resets the audio player and starts playback. + // Returns true on success. + virtual bool ResetAudioPlayer(AudioPacket::SamplingRate sampling_rate) = 0; + + // Function called by the browser when it needs more audio samples. + static void AudioPlayerCallback(void* samples, + uint32 buffer_size, + void* data); private: + friend class AudioPlayerTest; + + typedef std::list<AudioPacket*> AudioPacketQueue; + + void FillWithSamples(void* samples, uint32 buffer_size); + + AudioPacket::SamplingRate sampling_rate_; + + bool start_failed_; + + // Protects |queued_packets_|, |queued_samples_ and |bytes_consumed_|. This is + // necessary to prevent races, because Pepper will call the callback on a + // separate thread. + base::Lock lock_; + + AudioPacketQueue queued_packets_; + int queued_samples_; + + // The number of bytes from |queued_packets_| that have been consumed. + size_t bytes_consumed_; + DISALLOW_COPY_AND_ASSIGN(AudioPlayer); }; diff --git a/remoting/client/audio_player_unittest.cc b/remoting/client/audio_player_unittest.cc new file mode 100644 index 0000000..e4e6fa6 --- /dev/null +++ b/remoting/client/audio_player_unittest.cc @@ -0,0 +1,335 @@ +// Copyright (c) 2012 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 "remoting/client/audio_player.h" + +#include "base/memory/scoped_ptr.h" +#include "remoting/client/audio_player.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +const int kAudioSamplesPerFrame = 25; +const int kAudioSampleBytes = 4; +const int kAudioFrameBytes = kAudioSamplesPerFrame * kAudioSampleBytes; +const int kPaddingBytes = 16; + +// TODO(garykac): Generate random audio data in the tests rather than having +// a single constant value. +const uint8 kDefaultBufferData = 0x5A; +const uint8 kDummyAudioData = 0x8B; + +} // namespace + +namespace remoting { + +class FakeAudioPlayer : public AudioPlayer { + public: + FakeAudioPlayer() {}; + + bool ResetAudioPlayer(AudioPacket::SamplingRate) { + return true; + }; + + uint32 GetSamplesPerFrame() { + return kAudioSamplesPerFrame; + }; +}; + +class AudioPlayerTest : public ::testing::Test { + protected: + virtual void SetUp() { + audio_.reset(new FakeAudioPlayer()); + buffer_.reset(new char[kAudioFrameBytes + kPaddingBytes]); + } + + virtual void TearDown() {} + + void ConsumeAudioFrame() { + uint8* buffer = reinterpret_cast<uint8*>(buffer_.get()); + memset(buffer, kDefaultBufferData, kAudioFrameBytes + kPaddingBytes); + AudioPlayer::AudioPlayerCallback(reinterpret_cast<void*>(buffer_.get()), + kAudioFrameBytes, + reinterpret_cast<void*>(audio_.get())); + // Verify we haven't written beyond the end of the buffer. + for (int i = 0; i < kPaddingBytes; i++) + ASSERT_EQ(kDefaultBufferData, *(buffer + kAudioFrameBytes + i)); + } + + // Check that the first |num_bytes| bytes are filled with audio data and + // the rest of the buffer is zero-filled. + void CheckAudioFrameBytes(int num_bytes) { + uint8* buffer = reinterpret_cast<uint8*>(buffer_.get()); + int i = 0; + for (; i < num_bytes; i++) { + ASSERT_EQ(kDummyAudioData, *(buffer + i)); + } + // Rest of audio frame must be filled with '0's. + for (; i < kAudioFrameBytes; i++) { + ASSERT_EQ(0, *(buffer + i)); + } + } + + void SetQueuedSamples(int num_samples) { + audio_->queued_samples_ = num_samples; + } + + int GetNumQueuedSamples() { + return audio_->queued_samples_; + } + + int GetNumQueuedPackets() { + return static_cast<int>(audio_->queued_packets_.size()); + } + + int GetBytesConsumed() { + return static_cast<int>(audio_->bytes_consumed_); + } + + scoped_ptr<AudioPlayer> audio_; + scoped_array<char> buffer_; +}; + +scoped_ptr<AudioPacket> CreatePacketWithSamplingRate( + AudioPacket::SamplingRate rate, int samples) { + scoped_ptr<AudioPacket> packet(new AudioPacket()); + packet->set_encoding(AudioPacket::ENCODING_RAW); + packet->set_sampling_rate(rate); + packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); + packet->set_channels(AudioPacket::CHANNELS_STEREO); + + // The data must be a multiple of 4 bytes (channels x bytes_per_sample). + std::string data; + data.resize(samples * kAudioSampleBytes, kDummyAudioData); + packet->add_data(data); + + return packet.Pass(); +} + +scoped_ptr<AudioPacket> CreatePacket44100Hz(int samples) { + return CreatePacketWithSamplingRate(AudioPacket::SAMPLING_RATE_44100, + samples); +} + +scoped_ptr<AudioPacket> CreatePacket48000Hz(int samples) { + return CreatePacketWithSamplingRate(AudioPacket::SAMPLING_RATE_48000, + samples); +} + +TEST_F(AudioPlayerTest, Init) { + ASSERT_EQ(0, GetNumQueuedPackets()); + + scoped_ptr<AudioPacket> packet(CreatePacket44100Hz(10)); + audio_->ProcessAudioPacket(packet.Pass()); + ASSERT_EQ(1, GetNumQueuedPackets()); +} + +TEST_F(AudioPlayerTest, MultipleSamples) { + scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(10)); + audio_->ProcessAudioPacket(packet1.Pass()); + ASSERT_EQ(10, GetNumQueuedSamples()); + ASSERT_EQ(1, GetNumQueuedPackets()); + + scoped_ptr<AudioPacket> packet2(CreatePacket44100Hz(20)); + audio_->ProcessAudioPacket(packet2.Pass()); + ASSERT_EQ(30, GetNumQueuedSamples()); + ASSERT_EQ(2, GetNumQueuedPackets()); +} + +TEST_F(AudioPlayerTest, ChangeSampleRate) { + scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(10)); + audio_->ProcessAudioPacket(packet1.Pass()); + ASSERT_EQ(10, GetNumQueuedSamples()); + ASSERT_EQ(1, GetNumQueuedPackets()); + + // New packet with different sampling rate causes previous samples to + // be removed. + scoped_ptr<AudioPacket> packet2(CreatePacket48000Hz(20)); + audio_->ProcessAudioPacket(packet2.Pass()); + ASSERT_EQ(20, GetNumQueuedSamples()); + ASSERT_EQ(1, GetNumQueuedPackets()); +} + +TEST_F(AudioPlayerTest, ExceedLatency) { + scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(10)); + audio_->ProcessAudioPacket(packet1.Pass()); + ASSERT_EQ(10, GetNumQueuedSamples()); + ASSERT_EQ(1, GetNumQueuedPackets()); + + // Fake lots of queued samples. + SetQueuedSamples(20000); + + // Previous sample should have been deleted because of latency (too many + // unprocessed samples). + scoped_ptr<AudioPacket> packet2(CreatePacket44100Hz(20)); + audio_->ProcessAudioPacket(packet2.Pass()); + ASSERT_EQ(20, GetNumQueuedSamples()); + ASSERT_EQ(1, GetNumQueuedPackets()); +} + +// Incoming packets: 100 +// Consume: 25 (w/ 75 remaining, offset 25 into packet) +TEST_F(AudioPlayerTest, ConsumePartialPacket) { + int total_samples = 0; + int bytes_consumed = 0; + + // Process 100 samples. + int packet1_samples = 100; + scoped_ptr<AudioPacket> packet(CreatePacket44100Hz(packet1_samples)); + total_samples += packet1_samples; + audio_->ProcessAudioPacket(packet.Pass()); + ASSERT_EQ(total_samples, GetNumQueuedSamples()); + ASSERT_EQ(1, GetNumQueuedPackets()); + ASSERT_EQ(bytes_consumed, GetBytesConsumed()); + + // Consume one frame (=25) of samples. + ConsumeAudioFrame(); + total_samples -= kAudioSamplesPerFrame; + bytes_consumed += kAudioFrameBytes; + ASSERT_EQ(total_samples, GetNumQueuedSamples()); + ASSERT_EQ(1, GetNumQueuedPackets()); + ASSERT_EQ(bytes_consumed, GetBytesConsumed()); + CheckAudioFrameBytes(kAudioFrameBytes); + + // Remaining samples. + ASSERT_EQ(75, total_samples); + ASSERT_EQ(25 * kAudioSampleBytes, bytes_consumed); +} + +// Incoming packets: 20, 70 +// Consume: 25, 25 (w/ 40 remaining, offset 30 into packet) +TEST_F(AudioPlayerTest, ConsumeAcrossPackets) { + int total_samples = 0; + int bytes_consumed = 0; + + // Packet 1. + int packet1_samples = 20; + scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(packet1_samples)); + total_samples += packet1_samples; + audio_->ProcessAudioPacket(packet1.Pass()); + ASSERT_EQ(total_samples, GetNumQueuedSamples()); + + // Packet 2. + int packet2_samples = 70; + scoped_ptr<AudioPacket> packet2(CreatePacket44100Hz(packet2_samples)); + total_samples += packet2_samples; + audio_->ProcessAudioPacket(packet2.Pass()); + ASSERT_EQ(total_samples, GetNumQueuedSamples()); + ASSERT_EQ(bytes_consumed, GetBytesConsumed()); + + // Consume 1st frame of 25 samples. + // This will consume the entire 1st packet. + ConsumeAudioFrame(); + total_samples -= kAudioSamplesPerFrame; + bytes_consumed += kAudioFrameBytes - (packet1_samples * kAudioSampleBytes); + ASSERT_EQ(total_samples, GetNumQueuedSamples()); + ASSERT_EQ(1, GetNumQueuedPackets()); + ASSERT_EQ(bytes_consumed, GetBytesConsumed()); + CheckAudioFrameBytes(kAudioFrameBytes); + + // Consume 2nd frame of 25 samples. + ConsumeAudioFrame(); + total_samples -= kAudioSamplesPerFrame; + bytes_consumed += kAudioFrameBytes; + ASSERT_EQ(total_samples, GetNumQueuedSamples()); + ASSERT_EQ(1, GetNumQueuedPackets()); + ASSERT_EQ(bytes_consumed, GetBytesConsumed()); + CheckAudioFrameBytes(kAudioFrameBytes); + + // Remaining samples. + ASSERT_EQ(40, total_samples); + ASSERT_EQ(30 * kAudioSampleBytes, bytes_consumed); +} + +// Incoming packets: 50, 30 +// Consume: 25, 25, 25 (w/ 5 remaining, offset 25 into packet) +TEST_F(AudioPlayerTest, ConsumeEntirePacket) { + int total_samples = 0; + int bytes_consumed = 0; + + // Packet 1. + int packet1_samples = 50; + scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(packet1_samples)); + total_samples += packet1_samples; + audio_->ProcessAudioPacket(packet1.Pass()); + ASSERT_EQ(total_samples, GetNumQueuedSamples()); + ASSERT_EQ(bytes_consumed, GetBytesConsumed()); + + // Packet 2. + int packet2_samples = 30; + scoped_ptr<AudioPacket> packet2(CreatePacket44100Hz(packet2_samples)); + total_samples += packet2_samples; + audio_->ProcessAudioPacket(packet2.Pass()); + ASSERT_EQ(total_samples, GetNumQueuedSamples()); + ASSERT_EQ(bytes_consumed, GetBytesConsumed()); + + // Consume 1st frame of 25 samples. + ConsumeAudioFrame(); + total_samples -= kAudioSamplesPerFrame; + bytes_consumed += kAudioFrameBytes; + ASSERT_EQ(total_samples, GetNumQueuedSamples()); + ASSERT_EQ(2, GetNumQueuedPackets()); + ASSERT_EQ(bytes_consumed, GetBytesConsumed()); + CheckAudioFrameBytes(kAudioFrameBytes); + + // Consume 2nd frame of 25 samples. + // This will consume the entire first packet (exactly), but the entry for + // this packet will stick around (empty) until the next audio chunk is + // consumed. + ConsumeAudioFrame(); + total_samples -= kAudioSamplesPerFrame; + bytes_consumed += kAudioFrameBytes; + ASSERT_EQ(total_samples, GetNumQueuedSamples()); + ASSERT_EQ(2, GetNumQueuedPackets()); + ASSERT_EQ(bytes_consumed, GetBytesConsumed()); + CheckAudioFrameBytes(kAudioFrameBytes); + + // Consume 3rd frame of 25 samples. + ConsumeAudioFrame(); + total_samples -= kAudioSamplesPerFrame; + bytes_consumed += kAudioFrameBytes - (packet1_samples * kAudioSampleBytes); + ASSERT_EQ(total_samples, GetNumQueuedSamples()); + ASSERT_EQ(1, GetNumQueuedPackets()); + ASSERT_EQ(bytes_consumed, GetBytesConsumed()); + CheckAudioFrameBytes(kAudioFrameBytes); + + // Remaining samples. + ASSERT_EQ(5, total_samples); + ASSERT_EQ(25 * kAudioSampleBytes, bytes_consumed); +} + +// Incoming packets: <none> +// Consume: 25 +TEST_F(AudioPlayerTest, NoDataToConsume) { + // Attempt to consume a frame of 25 samples. + ConsumeAudioFrame(); + ASSERT_EQ(0, GetNumQueuedSamples()); + ASSERT_EQ(0, GetNumQueuedPackets()); + ASSERT_EQ(0, GetBytesConsumed()); + CheckAudioFrameBytes(0); +} + +// Incoming packets: 10 +// Consume: 25 +TEST_F(AudioPlayerTest, NotEnoughDataToConsume) { + int total_samples = 0; + int bytes_consumed = 0; + + // Packet 1. + int packet1_samples = 10; + scoped_ptr<AudioPacket> packet1(CreatePacket44100Hz(packet1_samples)); + total_samples += packet1_samples; + audio_->ProcessAudioPacket(packet1.Pass()); + ASSERT_EQ(total_samples, GetNumQueuedSamples()); + ASSERT_EQ(bytes_consumed, GetBytesConsumed()); + + // Attempt to consume a frame of 25 samples. + ConsumeAudioFrame(); + ASSERT_EQ(0, GetNumQueuedSamples()); + ASSERT_EQ(0, GetNumQueuedPackets()); + ASSERT_EQ(0, GetBytesConsumed()); + CheckAudioFrameBytes(packet1_samples * kAudioSampleBytes); +} + +} // namespace remoting diff --git a/remoting/client/plugin/pepper_audio_player.cc b/remoting/client/plugin/pepper_audio_player.cc index cbb1218..0353079 100644 --- a/remoting/client/plugin/pepper_audio_player.cc +++ b/remoting/client/plugin/pepper_audio_player.cc @@ -8,64 +8,49 @@ #include "base/stl_util.h" -namespace { - // The frame size we will request from the browser. const int kFrameSizeMs = 40; -// The number of channels in the audio stream (only supporting stereo audio -// for now). -const int kChannels = 2u; -const int kSampleSizeBytes = 2; - -// If queue grows bigger than 150ms we start dropping packets. -const int kMaxQueueLatencyMs = 150; - -PP_AudioSampleRate ConvertToPepperSampleRate( - remoting::AudioPacket::SamplingRate sampling_rate) { - switch (sampling_rate) { - case remoting::AudioPacket::SAMPLING_RATE_44100: - return PP_AUDIOSAMPLERATE_44100; - case remoting::AudioPacket::SAMPLING_RATE_48000: - return PP_AUDIOSAMPLERATE_48000; - default: - NOTREACHED(); - } - return PP_AUDIOSAMPLERATE_NONE; -} - -} // namespace - namespace remoting { PepperAudioPlayer::PepperAudioPlayer(pp::Instance* instance) : instance_(instance), - sampling_rate_(AudioPacket::SAMPLING_RATE_INVALID), - samples_per_frame_(0), - start_failed_(false), - queued_samples_(0), - bytes_consumed_(0) { + samples_per_frame_(0) { } -PepperAudioPlayer::~PepperAudioPlayer() {} +PepperAudioPlayer::~PepperAudioPlayer() { +} + +uint32 PepperAudioPlayer::GetSamplesPerFrame() { + return samples_per_frame_; +} bool PepperAudioPlayer::ResetAudioPlayer( - AudioPacket::SamplingRate sampling_rate) { - sampling_rate_ = sampling_rate; - PP_AudioSampleRate sample_rate = - ConvertToPepperSampleRate(sampling_rate); + AudioPacket::SamplingRate sampling_rate) { + PP_AudioSampleRate pp_sampling_rate = PP_AUDIOSAMPLERATE_NONE; + switch (sampling_rate) { + case AudioPacket::SAMPLING_RATE_44100: + pp_sampling_rate = PP_AUDIOSAMPLERATE_44100; + break; + case AudioPacket::SAMPLING_RATE_48000: + pp_sampling_rate = PP_AUDIOSAMPLERATE_48000; + break; + default: + LOG(ERROR) << "Unsupported audio sampling rate: " << sampling_rate; + return false; + } // Ask the browser/device for an appropriate frame size. samples_per_frame_ = pp::AudioConfig::RecommendSampleFrameCount( - instance_, sample_rate, + instance_, pp_sampling_rate, kFrameSizeMs * sampling_rate / base::Time::kMillisecondsPerSecond); // Create an audio configuration resource. pp::AudioConfig audio_config = pp::AudioConfig( - instance_, sample_rate, samples_per_frame_); + instance_, pp_sampling_rate, samples_per_frame_); // Create an audio resource. - audio_ = pp::Audio(instance_, audio_config, PepperAudioPlayerCallback, this); + audio_ = pp::Audio(instance_, audio_config, AudioPlayerCallback, this); // Immediately start the player. bool success = audio_.StartPlayback(); @@ -74,97 +59,4 @@ bool PepperAudioPlayer::ResetAudioPlayer( return success; } -void PepperAudioPlayer::ProcessAudioPacket(scoped_ptr<AudioPacket> packet) { - CHECK_EQ(1, packet->data_size()); - DCHECK_EQ(AudioPacket::ENCODING_RAW, packet->encoding()); - DCHECK_NE(AudioPacket::SAMPLING_RATE_INVALID, packet->sampling_rate()); - DCHECK_EQ(kSampleSizeBytes, packet->bytes_per_sample()); - DCHECK_EQ(static_cast<int>(kChannels), packet->channels()); - DCHECK_EQ(packet->data(0).size() % (kChannels * kSampleSizeBytes), 0u); - - // No-op if the Pepper player won't start. - if (start_failed_) { - return; - } - - // Start the Pepper audio player if this is the first packet. - if (sampling_rate_ != packet->sampling_rate()) { - // Drop all packets currently in the queue, since they are sampled at the - // wrong rate. - { - base::AutoLock auto_lock(lock_); - STLDeleteElements(&queued_packets_); - queued_samples_ = 0; - bytes_consumed_ = 0; - } - - bool success = ResetAudioPlayer(packet->sampling_rate()); - if (!success) { - start_failed_ = true; - return; - } - } - - base::AutoLock auto_lock(lock_); - - if (queued_samples_ > kMaxQueueLatencyMs * sampling_rate_ / - base::Time::kMillisecondsPerSecond) { - STLDeleteElements(&queued_packets_); - queued_samples_ = 0; - bytes_consumed_ = 0; - } - - queued_samples_ += packet->data(0).size() / (kChannels * kSampleSizeBytes); - queued_packets_.push_back(packet.release()); -} - -// static -void PepperAudioPlayer::PepperAudioPlayerCallback(void* samples, - uint32_t buffer_size, - void* data) { - PepperAudioPlayer* audio_player = static_cast<PepperAudioPlayer*>(data); - audio_player->FillWithSamples(samples, buffer_size); -} - -void PepperAudioPlayer::FillWithSamples(void* samples, uint32_t buffer_size) { - base::AutoLock auto_lock(lock_); - - const size_t bytes_needed = kChannels * samples_per_frame_ * - kSampleSizeBytes; - - // Make sure we don't overrun the buffer. - CHECK_EQ(buffer_size, bytes_needed); - - char* next_sample = static_cast<char*>(samples); - size_t bytes_extracted = 0; - - while (bytes_extracted < bytes_needed) { - // Check if we've run out of samples for this packet. - if (queued_packets_.empty()) { - memset(next_sample, 0, bytes_needed - bytes_extracted); - return; - } - - // Pop off the packet if we've already consumed all its bytes. - if (queued_packets_.front()->data(0).size() == bytes_consumed_) { - delete queued_packets_.front(); - queued_packets_.pop_front(); - bytes_consumed_ = 0; - continue; - } - - const std::string& packet_data = queued_packets_.front()->data(0); - size_t bytes_to_copy = std::min( - packet_data.size() - bytes_consumed_, - bytes_needed - bytes_extracted); - memcpy(next_sample, packet_data.data() + bytes_consumed_, bytes_to_copy); - - next_sample += bytes_to_copy; - bytes_consumed_ += bytes_to_copy; - bytes_extracted += bytes_to_copy; - queued_samples_ -= bytes_to_copy / kSampleSizeBytes / kChannels; - DCHECK_GE(queued_samples_, 0); - } -} - } // namespace remoting diff --git a/remoting/client/plugin/pepper_audio_player.h b/remoting/client/plugin/pepper_audio_player.h index 2a0cf757..fc8bbcd 100644 --- a/remoting/client/plugin/pepper_audio_player.h +++ b/remoting/client/plugin/pepper_audio_player.h @@ -5,13 +5,7 @@ #ifndef REMOTING_CLIENT_PLUGIN_PEPPER_AUDIO_PLAYER_H_ #define REMOTING_CLIENT_PLUGIN_PEPPER_AUDIO_PLAYER_H_ -#include <list> - -#include "base/bind.h" #include "base/callback.h" -#include "base/memory/scoped_ptr.h" -#include "base/memory/ref_counted.h" -#include "base/synchronization/lock.h" #include "ppapi/cpp/audio.h" #include "ppapi/cpp/instance.h" #include "remoting/client/audio_player.h" @@ -24,42 +18,17 @@ class PepperAudioPlayer : public AudioPlayer { explicit PepperAudioPlayer(pp::Instance* instance); virtual ~PepperAudioPlayer(); - virtual void ProcessAudioPacket(scoped_ptr<AudioPacket> packet) OVERRIDE; - - private: - typedef std::list<AudioPacket*> AudioPacketQueue; - - // Resets the audio player and starts the playback. - // Returns true on success. - bool ResetAudioPlayer(AudioPacket::SamplingRate sampling_rate); - - // Function called by the browser when it needs more audio samples. - static void PepperAudioPlayerCallback(void* samples, - uint32_t buffer_size, - void* data); + virtual uint32 GetSamplesPerFrame() OVERRIDE; - void FillWithSamples(void* samples, uint32_t buffer_size); + virtual bool ResetAudioPlayer(AudioPacket::SamplingRate sampling_rate) + OVERRIDE; + private: pp::Instance* instance_; pp::Audio audio_; - AudioPacket::SamplingRate sampling_rate_; - // The count of sample frames per channel in an audio buffer. - uint32_t samples_per_frame_; - - bool start_failed_; - - // Protects |queued_packets_|, |queued_samples_ and |bytes_consumed_|. This is - // necessary to prevent races, because Pepper will call the callback on a - // separate thread. - base::Lock lock_; - - AudioPacketQueue queued_packets_; - int queued_samples_; - - // The number of bytes from |queued_packets_| that have been consumed. - size_t bytes_consumed_; + uint32 samples_per_frame_; DISALLOW_COPY_AND_ASSIGN(PepperAudioPlayer); }; |