diff options
author | kxing@chromium.org <kxing@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-02 23:24:21 +0000 |
---|---|---|
committer | kxing@chromium.org <kxing@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-02 23:24:21 +0000 |
commit | f8705114c9171db859e1884cc8b6f9e7f5d7f411 (patch) | |
tree | bebca04a58a7af42be4bf1994bbd978d135f8b2c | |
parent | b53913c4601229c87fe010aafbb20d258198e2db (diff) | |
download | chromium_src-f8705114c9171db859e1884cc8b6f9e7f5d7f411.zip chromium_src-f8705114c9171db859e1884cc8b6f9e7f5d7f411.tar.gz chromium_src-f8705114c9171db859e1884cc8b6f9e7f5d7f411.tar.bz2 |
Client code for playing back audio
Review URL: https://chromiumcodereview.appspot.com/10699030
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@145217 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | remoting/client/audio_player.h | 38 | ||||
-rw-r--r-- | remoting/client/chromoting_client.cc | 10 | ||||
-rw-r--r-- | remoting/client/chromoting_client.h | 7 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_instance.cc | 5 | ||||
-rw-r--r-- | remoting/client/plugin/chromoting_instance.h | 2 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_audio_player.cc | 113 | ||||
-rw-r--r-- | remoting/client/plugin/pepper_audio_player.h | 67 | ||||
-rw-r--r-- | remoting/remoting.gyp | 3 |
8 files changed, 241 insertions, 4 deletions
diff --git a/remoting/client/audio_player.h b/remoting/client/audio_player.h new file mode 100644 index 0000000..eed8786 --- /dev/null +++ b/remoting/client/audio_player.h @@ -0,0 +1,38 @@ +// 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. + +#ifndef REMOTING_CLIENT_AUDIO_PLAYER_H_ +#define REMOTING_CLIENT_AUDIO_PLAYER_H_ + +#include "base/memory/scoped_ptr.h" + +namespace pp { +class Instance; +} // namespace pp + +namespace remoting { + +class AudioPacket; + +class AudioPlayer { + public: + virtual ~AudioPlayer() {} + + // Returns true if successful, false otherwise. + virtual bool Start() = 0; + + virtual void ProcessAudioPacket(scoped_ptr<AudioPacket> packet) = 0; + + virtual bool IsRunning() const = 0; + + protected: + AudioPlayer() {} + + private: + DISALLOW_COPY_AND_ASSIGN(AudioPlayer); +}; + +} // namespace remoting + +#endif // REMOTING_CLIENT_AUDIO_PLAYER_H_ diff --git a/remoting/client/chromoting_client.cc b/remoting/client/chromoting_client.cc index f118667..9d9df67 100644 --- a/remoting/client/chromoting_client.cc +++ b/remoting/client/chromoting_client.cc @@ -5,10 +5,12 @@ #include "remoting/client/chromoting_client.h" #include "base/bind.h" +#include "remoting/client/audio_player.h" #include "remoting/client/chromoting_view.h" #include "remoting/client/client_context.h" #include "remoting/client/rectangle_update_decoder.h" #include "remoting/proto/audio.pb.h" +#include "remoting/proto/video.pb.h" #include "remoting/protocol/authentication_method.h" #include "remoting/protocol/connection_to_host.h" #include "remoting/protocol/negotiating_authenticator.h" @@ -32,12 +34,14 @@ ChromotingClient::ChromotingClient( scoped_refptr<base::SingleThreadTaskRunner> task_runner, protocol::ConnectionToHost* connection, ChromotingView* view, - RectangleUpdateDecoder* rectangle_decoder) + RectangleUpdateDecoder* rectangle_decoder, + AudioPlayer* audio_player) : config_(config), task_runner_(task_runner), connection_(connection), view_(view), rectangle_decoder_(rectangle_decoder), + audio_player_(audio_player), packet_being_processed_(false), last_sequence_number_(0), weak_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { @@ -143,7 +147,9 @@ int ChromotingClient::GetPendingVideoPackets() { void ChromotingClient::ProcessAudioPacket(scoped_ptr<AudioPacket> packet, const base::Closure& done) { - // TODO(kxing): Playback audio. + audio_player_->ProcessAudioPacket(packet.Pass()); + if (!audio_player_->IsRunning() && connection_->config().is_audio_enabled()) + audio_player_->Start(); done.Run(); } diff --git a/remoting/client/chromoting_client.h b/remoting/client/chromoting_client.h index 03ef41c..d15073f 100644 --- a/remoting/client/chromoting_client.h +++ b/remoting/client/chromoting_client.h @@ -10,6 +10,7 @@ #include <list> #include "base/callback.h" +#include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/time.h" #include "remoting/client/client_config.h" @@ -33,6 +34,7 @@ namespace protocol { class TransportFactory; } // namespace protocol +class AudioPlayer; class RectangleUpdateDecoder; // TODO(sergeyu): Move VideoStub implementation to RectangleUpdateDecoder. @@ -46,7 +48,9 @@ class ChromotingClient : public protocol::ConnectionToHost::HostEventCallback, scoped_refptr<base::SingleThreadTaskRunner> task_runner, protocol::ConnectionToHost* connection, ChromotingView* view, - RectangleUpdateDecoder* rectangle_decoder); + RectangleUpdateDecoder* rectangle_decoder, + AudioPlayer* audio_player); + virtual ~ChromotingClient(); // Start/stop the client. Must be called on the main thread. @@ -108,6 +112,7 @@ class ChromotingClient : public protocol::ConnectionToHost::HostEventCallback, protocol::ConnectionToHost* connection_; ChromotingView* view_; RectangleUpdateDecoder* rectangle_decoder_; + AudioPlayer* audio_player_; // If non-NULL, this is called when the client is done. base::Closure client_done_; diff --git a/remoting/client/plugin/chromoting_instance.cc b/remoting/client/plugin/chromoting_instance.cc index fb3da91..77cd55f 100644 --- a/remoting/client/plugin/chromoting_instance.cc +++ b/remoting/client/plugin/chromoting_instance.cc @@ -31,6 +31,7 @@ #include "remoting/client/client_config.h" #include "remoting/client/chromoting_client.h" #include "remoting/client/frame_consumer_proxy.h" +#include "remoting/client/plugin/pepper_audio_player.h" #include "remoting/client/plugin/pepper_input_handler.h" #include "remoting/client/plugin/pepper_port_allocator.h" #include "remoting/client/plugin/pepper_view.h" @@ -207,6 +208,7 @@ bool ChromotingInstance::Init(uint32_t argc, context_.decode_task_runner(), consumer_proxy); view_.reset(new PepperView(this, &context_, rectangle_decoder_.get())); consumer_proxy->Attach(view_->AsWeakPtr()); + audio_player_.reset(new PepperAudioPlayer(this)); return true; } @@ -379,7 +381,8 @@ void ChromotingInstance::Connect(const ClientConfig& config) { host_connection_.reset(new protocol::ConnectionToHost(true)); client_.reset(new ChromotingClient(config, context_.main_task_runner(), host_connection_.get(), view_.get(), - rectangle_decoder_.get())); + rectangle_decoder_.get(), + audio_player_.get())); // Construct the input pipeline mouse_input_filter_.reset( diff --git a/remoting/client/plugin/chromoting_instance.h b/remoting/client/plugin/chromoting_instance.h index 5c8fa487..dbc840d 100644 --- a/remoting/client/plugin/chromoting_instance.h +++ b/remoting/client/plugin/chromoting_instance.h @@ -55,6 +55,7 @@ class ChromotingClient; class ChromotingStats; class ClientContext; class FrameConsumerProxy; +class PepperAudioPlayer; class PepperInputHandler; class PepperView; class PepperXmppProxy; @@ -211,6 +212,7 @@ class ChromotingInstance : #endif KeyEventMapper key_mapper_; scoped_ptr<PepperInputHandler> input_handler_; + scoped_ptr<PepperAudioPlayer> audio_player_; scoped_ptr<ChromotingClient> client_; // XmppProxy is a refcounted interface used to perform thread-switching and diff --git a/remoting/client/plugin/pepper_audio_player.cc b/remoting/client/plugin/pepper_audio_player.cc new file mode 100644 index 0000000..bf67436 --- /dev/null +++ b/remoting/client/plugin/pepper_audio_player.cc @@ -0,0 +1,113 @@ +// 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/plugin/pepper_audio_player.h" + +#include <algorithm> + +#include "base/stl_util.h" +#include "remoting/proto/audio.pb.h" + +namespace { +// Constants used to create an audio configuration resource. +// The sample count we will request from the browser. +const uint32_t kSampleFrameCount = 4096u; +// The number of channels in the audio stream (only supporting stereo audio +// for now). +const uint32_t kChannels = 2u; +const int kSampleSizeBytes = 2; +} // namespace + +namespace remoting { + +bool PepperAudioPlayer::IsRunning() const { + return running_; +} + +PepperAudioPlayer::PepperAudioPlayer(pp::Instance* instance) + : samples_per_frame_(kSampleFrameCount), + bytes_consumed_(0), + running_(false) { + // Ask the browser/device for an appropriate sample frame count size. + samples_per_frame_ = + pp::AudioConfig::RecommendSampleFrameCount(instance, + PP_AUDIOSAMPLERATE_44100, + kSampleFrameCount); + + // Create an audio configuration resource. + pp::AudioConfig audio_config = pp::AudioConfig(instance, + PP_AUDIOSAMPLERATE_44100, + samples_per_frame_); + + // Create an audio resource. + audio_ = pp::Audio(instance, audio_config, PepperAudioPlayerCallback, this); +} + +PepperAudioPlayer::~PepperAudioPlayer() { } + +bool PepperAudioPlayer::Start() { + running_ = audio_.StartPlayback(); + return running_; +} + +void PepperAudioPlayer::ProcessAudioPacket(scoped_ptr<AudioPacket> packet) { + // TODO(kxing): Limit the size of the queue so that latency doesn't grow + // too large. + if (packet->data().size() % (kChannels * kSampleSizeBytes) != 0) { + LOG(WARNING) << "Received corrupted packet."; + return; + } + base::AutoLock auto_lock(lock_); + + 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().size() == bytes_consumed_) { + delete queued_packets_.front(); + queued_packets_.pop_front(); + bytes_consumed_ = 0; + continue; + } + + const std::string& packet_data = queued_packets_.front()->data(); + 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; + } +} + +} // namespace remoting diff --git a/remoting/client/plugin/pepper_audio_player.h b/remoting/client/plugin/pepper_audio_player.h new file mode 100644 index 0000000..925b0dc --- /dev/null +++ b/remoting/client/plugin/pepper_audio_player.h @@ -0,0 +1,67 @@ +// 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. + +#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" + +namespace remoting { + +class AudioPacket; + +class PepperAudioPlayer : public AudioPlayer { + public: + explicit PepperAudioPlayer(pp::Instance* instance); + virtual ~PepperAudioPlayer(); + + // Returns true if successful, false otherwise. + virtual bool Start() OVERRIDE; + + virtual void ProcessAudioPacket(scoped_ptr<AudioPacket> packet) OVERRIDE; + + virtual bool IsRunning() const OVERRIDE; + + private: + typedef std::list<AudioPacket*> AudioPacketQueue; + + // Function called by the browser when it needs more audio samples. + static void PepperAudioPlayerCallback(void* samples, + uint32_t buffer_size, + void* data); + + void FillWithSamples(void* samples, uint32_t buffer_size); + + pp::Audio audio_; + + // The count of sample frames per channel in an audio buffer. + uint32_t samples_per_frame_; + + // Protects |queued_packets_| and |bytes_consumed_|. + // This is necessary to prevent races, + // because Pepper will callback on a separate thread. + base::Lock lock_; + + AudioPacketQueue queued_packets_; + + // The number of bytes from |queued_packets_| that have been consumed. + size_t bytes_consumed_; + + bool running_; + + DISALLOW_COPY_AND_ASSIGN(PepperAudioPlayer); +}; + +} // namespace remoting + +#endif // REMOTING_CLIENT_PLUGIN_PEPPER_AUDIO_PLAYER_H_ diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 4a8d8064..fddc6d1 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -833,6 +833,8 @@ 'client/plugin/chromoting_instance.h', 'client/plugin/mac_key_event_processor.cc', 'client/plugin/mac_key_event_processor.h', + 'client/plugin/pepper_audio_player.cc', + 'client/plugin/pepper_audio_player.h', 'client/plugin/pepper_entrypoints.cc', 'client/plugin/pepper_entrypoints.h', 'client/plugin/pepper_input_handler.cc', @@ -1288,6 +1290,7 @@ 'remoting_protocol', ], 'sources': [ + 'client/audio_player.h', 'client/chromoting_client.cc', 'client/chromoting_client.h', 'client/chromoting_stats.cc', |