summaryrefslogtreecommitdiffstats
path: root/remoting/client
diff options
context:
space:
mode:
authorgarykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-10 02:37:47 +0000
committergarykac@chromium.org <garykac@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-10 02:37:47 +0000
commit95674a7482430d7ca837a8667e450081482335f1 (patch)
tree802a6b2ee5805d6a30668d896dca46b3495f0a05 /remoting/client
parentbd918adb442d90922182820d1b52851a404004e4 (diff)
downloadchromium_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.cc124
-rw-r--r--remoting/client/audio_player.h53
-rw-r--r--remoting/client/audio_player_unittest.cc335
-rw-r--r--remoting/client/plugin/pepper_audio_player.cc154
-rw-r--r--remoting/client/plugin/pepper_audio_player.h41
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);
};