diff options
author | kxing@chromium.org <kxing@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-13 19:38:07 +0000 |
---|---|---|
committer | kxing@chromium.org <kxing@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-07-13 19:38:07 +0000 |
commit | 677bb0a47d38068702e7a7c7da33f741aaab4bef (patch) | |
tree | 68000557c2b033b7472d2a1d95fa267fdf183bae | |
parent | fe71d3e65e1c2d884e394606441ff161b2a0c036 (diff) | |
download | chromium_src-677bb0a47d38068702e7a7c7da33f741aaab4bef.zip chromium_src-677bb0a47d38068702e7a7c7da33f741aaab4bef.tar.gz chromium_src-677bb0a47d38068702e7a7c7da33f741aaab4bef.tar.bz2 |
Piping for host-side audio capture
Review URL: https://chromiumcodereview.appspot.com/10692082
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@146623 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | remoting/host/audio_capturer.cc | 15 | ||||
-rw-r--r-- | remoting/host/audio_capturer.h | 32 | ||||
-rw-r--r-- | remoting/host/audio_scheduler.cc | 95 | ||||
-rw-r--r-- | remoting/host/audio_scheduler.h | 87 | ||||
-rw-r--r-- | remoting/host/chromoting_host.cc | 31 | ||||
-rw-r--r-- | remoting/host/chromoting_host.h | 16 | ||||
-rw-r--r-- | remoting/host/chromoting_host_unittest.cc | 5 | ||||
-rw-r--r-- | remoting/host/desktop_environment.cc | 19 | ||||
-rw-r--r-- | remoting/host/desktop_environment.h | 13 | ||||
-rw-r--r-- | remoting/host/simple_host_process.cc | 7 | ||||
-rw-r--r-- | remoting/protocol/session_config.cc | 2 | ||||
-rw-r--r-- | remoting/remoting.gyp | 4 |
12 files changed, 305 insertions, 21 deletions
diff --git a/remoting/host/audio_capturer.cc b/remoting/host/audio_capturer.cc new file mode 100644 index 0000000..1901dfc --- /dev/null +++ b/remoting/host/audio_capturer.cc @@ -0,0 +1,15 @@ +// 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 "base/logging.h" +#include "remoting/host/audio_capturer.h" + +namespace remoting { + +scoped_ptr<AudioCapturer> AudioCapturer::Create() { + NOTIMPLEMENTED(); + return scoped_ptr<AudioCapturer>(NULL); +} + +} // namespace remoting diff --git a/remoting/host/audio_capturer.h b/remoting/host/audio_capturer.h new file mode 100644 index 0000000..90aa2e1 --- /dev/null +++ b/remoting/host/audio_capturer.h @@ -0,0 +1,32 @@ +// 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_HOST_AUDIO_CAPTURER_H_ +#define REMOTING_HOST_AUDIO_CAPTURER_H_ + +#include "base/callback.h" +#include "base/memory/scoped_ptr.h" +#include "remoting/proto/audio.pb.h" + +namespace remoting { + +class AudioCapturer { + public: + typedef base::Callback<void(scoped_ptr<AudioPacket> packet)> + PacketCapturedCallback; + + virtual ~AudioCapturer() {} + + static scoped_ptr<AudioCapturer> Create(); + + // Capturers should sample at a 44.1 kHz sampling rate, in uncompressed PCM + // stereo format. Capturers may choose the number of frames per packet. + virtual void Start(const PacketCapturedCallback& callback) = 0; + virtual void Stop() = 0; + virtual bool IsRunning() = 0; +}; + +} // namespace remoting + +#endif // REMOTING_HOST_AUDIO_CAPTURER_H_ diff --git a/remoting/host/audio_scheduler.cc b/remoting/host/audio_scheduler.cc new file mode 100644 index 0000000..ea2e82e --- /dev/null +++ b/remoting/host/audio_scheduler.cc @@ -0,0 +1,95 @@ +// 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/host/audio_scheduler.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/single_thread_task_runner.h" +#include "remoting/host/audio_capturer.h" +#include "remoting/proto/audio.pb.h" +#include "remoting/protocol/audio_stub.h" + +namespace remoting { + +AudioScheduler::AudioScheduler( + scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner, + scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, + AudioCapturer* audio_capturer, + protocol::AudioStub* audio_stub) + : capture_task_runner_(capture_task_runner), + network_task_runner_(network_task_runner), + audio_capturer_(audio_capturer), + audio_stub_(audio_stub), + network_stopped_(false) { + DCHECK(capture_task_runner_); + DCHECK(network_task_runner_); + DCHECK(audio_capturer_); + DCHECK(audio_stub_); + capture_task_runner_->PostTask( + FROM_HERE, base::Bind(&AudioScheduler::DoStart, this)); +} + +void AudioScheduler::Stop(const base::Closure& done_task) { + DCHECK(!done_task.is_null()); + + if (!capture_task_runner_->BelongsToCurrentThread()) { + capture_task_runner_->PostTask(FROM_HERE, base::Bind( + &AudioScheduler::Stop, this, done_task)); + return; + } + + audio_capturer_->Stop(); + + network_task_runner_->PostTask(FROM_HERE, base::Bind( + &AudioScheduler::DoStopOnNetworkThread, this, done_task)); +} + +void AudioScheduler::OnClientDisconnected() { + DCHECK(network_task_runner_->BelongsToCurrentThread()); + DCHECK(audio_stub_); + audio_stub_ = NULL; +} + +AudioScheduler::~AudioScheduler() { +} + +void AudioScheduler::NotifyAudioPacketCaptured(scoped_ptr<AudioPacket> packet) { + network_task_runner_->PostTask( + FROM_HERE, base::Bind(&AudioScheduler::DoSendAudioPacket, + this, base::Passed(packet.Pass()))); +} + +void AudioScheduler::DoStart() { + DCHECK(capture_task_runner_->BelongsToCurrentThread()); + + audio_capturer_->Start( + base::Bind(&AudioScheduler::NotifyAudioPacketCaptured, this)); +} + +void AudioScheduler::DoSendAudioPacket(scoped_ptr<AudioPacket> packet) { + DCHECK(network_task_runner_->BelongsToCurrentThread()); + + if (network_stopped_ || !audio_stub_) + return; + + audio_stub_->ProcessAudioPacket( + packet.Pass(), + base::Bind(&AudioScheduler::OnCaptureCallbackNotified, this)); +} + +void AudioScheduler::OnCaptureCallbackNotified() { +} + +void AudioScheduler::DoStopOnNetworkThread(const base::Closure& done_task) { + DCHECK(network_task_runner_->BelongsToCurrentThread()); + + network_stopped_ = true; + + done_task.Run(); +} + +} // namespace remoting diff --git a/remoting/host/audio_scheduler.h b/remoting/host/audio_scheduler.h new file mode 100644 index 0000000..05a06c6 --- /dev/null +++ b/remoting/host/audio_scheduler.h @@ -0,0 +1,87 @@ +// 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_HOST_AUDIO_SCHEDULER_H_ +#define REMOTING_HOST_AUDIO_SCHEDULER_H_ + +#include "base/callback_forward.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/time.h" + +namespace base { +class SingleThreadTaskRunner; +} // namespace base + +namespace remoting { + +namespace protocol { +class AudioStub; +} // namespace protocol + +class AudioCapturer; +class AudioPacket; + +// A class for controlling AudioCapturer and forwarding audio packets to the +// client. +// +// THREADING +// +// This class works on two threads: the capture and network threads. +// Any encoding that is done on the audio samples will be done on the capture +// thread. +// +// AudioScheduler is responsible for: +// 1. managing the AudioCapturer. +// 2. sending packets of audio samples over the network to the client. +class AudioScheduler : public base::RefCountedThreadSafe<AudioScheduler> { + public: + // Construct an AudioScheduler. TaskRunners are used for message passing + // among the capturer and network threads. The caller is responsible for + // ensuring that the |audio_capturer| and |audio_stub| outlive the + // AudioScheduler. + AudioScheduler( + scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner, + scoped_refptr<base::SingleThreadTaskRunner> network_task_runner, + AudioCapturer* audio_capturer, + protocol::AudioStub* audio_stub); + + // Stop the recording session. + void Stop(const base::Closure& done_task); + + // Called when a client disconnects. + void OnClientDisconnected(); + + private: + friend class base::RefCountedThreadSafe<AudioScheduler>; + virtual ~AudioScheduler(); + + void NotifyAudioPacketCaptured(scoped_ptr<AudioPacket> packet); + + void DoStart(); + + // Sends an audio packet to the client. + void DoSendAudioPacket(scoped_ptr<AudioPacket> packet); + + // Signal network thread to cease activities. + void DoStopOnNetworkThread(const base::Closure& done_task); + + // Called when an AudioPacket has been delivered to the client. + void OnCaptureCallbackNotified(); + + scoped_refptr<base::SingleThreadTaskRunner> capture_task_runner_; + scoped_refptr<base::SingleThreadTaskRunner> network_task_runner_; + + AudioCapturer* audio_capturer_; + + protocol::AudioStub* audio_stub_; + + bool network_stopped_; + + DISALLOW_COPY_AND_ASSIGN(AudioScheduler); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_AUDIO_SCHEDULER_H_ diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc index d9417b6..7a55169 100644 --- a/remoting/host/chromoting_host.cc +++ b/remoting/host/chromoting_host.cc @@ -13,6 +13,7 @@ #include "remoting/base/encoder.h" #include "remoting/base/encoder_row_based.h" #include "remoting/base/encoder_vp8.h" +#include "remoting/host/audio_scheduler.h" #include "remoting/host/chromoting_host_context.h" #include "remoting/host/desktop_environment.h" #include "remoting/host/event_executor.h" @@ -131,6 +132,7 @@ void ChromotingHost::Shutdown(const base::Closure& shutdown_task) { clients_.front()->Disconnect(); } DCHECK(!recorder_.get()); + DCHECK(!audio_scheduler_.get()); // Destroy session manager. session_manager_.reset(); @@ -188,6 +190,7 @@ void ChromotingHost::OnSessionAuthenticated(ClientSession* client) { // Disconnects above must have destroyed all other clients and |recorder_|. DCHECK_EQ(clients_.size(), 1U); DCHECK(!recorder_.get()); + DCHECK(!audio_scheduler_.get()); // Notify observers that there is at least one authenticated client. const std::string& jid = client->client_jid(); @@ -216,6 +219,13 @@ void ChromotingHost::OnSessionChannelsConnected(ClientSession* client) { context_->network_task_runner(), desktop_environment_->capturer(), encoder); + if (client->connection()->session()->config().is_audio_enabled()) { + audio_scheduler_ = new AudioScheduler( + context_->capture_task_runner(), + context_->network_task_runner(), + desktop_environment_->audio_capturer(), + client->connection()->audio_stub()); + } // Immediately add the connection and start the session. recorder_->AddConnection(client->connection()); @@ -247,6 +257,11 @@ void ChromotingHost::OnSessionClosed(ClientSession* client) { recorder_->RemoveConnection(client->connection()); } + if (audio_scheduler_.get()) { + audio_scheduler_->OnClientDisconnected(); + StopAudioScheduler(); + } + if (client->is_authenticated()) { FOR_EACH_OBSERVER(HostStatusObserver, status_observers_, OnClientDisconnected(client->client_jid())); @@ -409,13 +424,23 @@ void ChromotingHost::StopScreenRecorder() { ++stopping_recorders_; scoped_refptr<ScreenRecorder> recorder = recorder_; recorder_ = NULL; - recorder->Stop(base::Bind(&ChromotingHost::OnScreenRecorderStopped, this)); + recorder->Stop(base::Bind(&ChromotingHost::OnRecorderStopped, this)); +} + +void ChromotingHost::StopAudioScheduler() { + DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); + DCHECK(audio_scheduler_.get()); + + ++stopping_recorders_; + scoped_refptr<AudioScheduler> recorder = audio_scheduler_; + audio_scheduler_ = NULL; + recorder->Stop(base::Bind(&ChromotingHost::OnRecorderStopped, this)); } -void ChromotingHost::OnScreenRecorderStopped() { +void ChromotingHost::OnRecorderStopped() { if (!context_->network_task_runner()->BelongsToCurrentThread()) { context_->network_task_runner()->PostTask( - FROM_HERE, base::Bind(&ChromotingHost::OnScreenRecorderStopped, this)); + FROM_HERE, base::Bind(&ChromotingHost::OnRecorderStopped, this)); return; } diff --git a/remoting/host/chromoting_host.h b/remoting/host/chromoting_host.h index 1d4cadc..4b43d36 100644 --- a/remoting/host/chromoting_host.h +++ b/remoting/host/chromoting_host.h @@ -32,6 +32,7 @@ class SessionConfig; class CandidateSessionConfig; } // namespace protocol +class AudioScheduler; class Capturer; class ChromotingHostContext; class DesktopEnvironment; @@ -168,7 +169,8 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>, virtual ~ChromotingHost(); void StopScreenRecorder(); - void OnScreenRecorderStopped(); + void StopAudioScheduler(); + void OnRecorderStopped(); // Called from Shutdown() or OnScreenRecorderStopped() to finish shutdown. void ShutdownFinish(); @@ -190,14 +192,14 @@ class ChromotingHost : public base::RefCountedThreadSafe<ChromotingHost>, // The connections to remote clients. ClientList clients_; - // Session manager for the host process. - // TODO(sergeyu): Do we need to have one screen recorder per client? + // Schedulers for audio and video capture. + // TODO(sergeyu): Do we need to have one set of schedulers per client? scoped_refptr<ScreenRecorder> recorder_; + scoped_refptr<AudioScheduler> audio_scheduler_; - // Number of screen recorders that are currently being - // stopped. Normally set to 0 or 1, but in some cases it may be - // greater than 1, particularly if when second client can connect - // immediately after previous one disconnected. + // Number of screen recorders and audio schedulers that are currently being + // stopped. Used to delay shutdown if one or more recorders/schedulers are + // asynchronously shutting down. int stopping_recorders_; // Tracks the internal state of the host. diff --git a/remoting/host/chromoting_host_unittest.cc b/remoting/host/chromoting_host_unittest.cc index e3b7f4d..96dc883 100644 --- a/remoting/host/chromoting_host_unittest.cc +++ b/remoting/host/chromoting_host_unittest.cc @@ -6,6 +6,7 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop_proxy.h" #include "remoting/jingle_glue/mock_objects.h" +#include "remoting/host/audio_capturer.h" #include "remoting/host/capturer_fake.h" #include "remoting/host/chromoting_host.h" #include "remoting/host/chromoting_host_context.h" @@ -83,11 +84,13 @@ class ChromotingHostTest : public testing::Test { .WillRepeatedly(Return(message_loop_proxy_.get())); scoped_ptr<Capturer> capturer(new CapturerFake()); + scoped_ptr<AudioCapturer> audio_capturer(NULL); event_executor_ = new MockEventExecutor(); desktop_environment_ = DesktopEnvironment::CreateFake( &context_, capturer.Pass(), - scoped_ptr<EventExecutor>(event_executor_)); + scoped_ptr<EventExecutor>(event_executor_), + audio_capturer.Pass()); session_manager_ = new protocol::MockSessionManager(); host_ = new ChromotingHost( diff --git a/remoting/host/desktop_environment.cc b/remoting/host/desktop_environment.cc index c13b8d5..25016c8 100644 --- a/remoting/host/desktop_environment.cc +++ b/remoting/host/desktop_environment.cc @@ -6,6 +6,7 @@ #include "base/bind.h" #include "base/compiler_specific.h" +#include "remoting/host/audio_capturer.h" #include "remoting/host/capturer.h" #include "remoting/host/chromoting_host_context.h" #include "remoting/host/event_executor.h" @@ -23,6 +24,7 @@ scoped_ptr<DesktopEnvironment> DesktopEnvironment::Create( scoped_ptr<EventExecutor> event_executor = EventExecutor::Create( context->desktop_task_runner(), context->ui_task_runner(), capturer.get()); + scoped_ptr<AudioCapturer> audio_capturer = AudioCapturer::Create(); if (capturer.get() == NULL || event_executor.get() == NULL) { LOG(ERROR) << "Unable to create DesktopEnvironment"; @@ -32,7 +34,8 @@ scoped_ptr<DesktopEnvironment> DesktopEnvironment::Create( return scoped_ptr<DesktopEnvironment>( new DesktopEnvironment(context, capturer.Pass(), - event_executor.Pass())); + event_executor.Pass(), + audio_capturer.Pass())); } // static @@ -42,6 +45,7 @@ scoped_ptr<DesktopEnvironment> DesktopEnvironment::CreateForService( scoped_ptr<EventExecutor> event_executor = EventExecutor::Create( context->desktop_task_runner(), context->ui_task_runner(), capturer.get()); + scoped_ptr<AudioCapturer> audio_capturer = AudioCapturer::Create(); if (capturer.get() == NULL || event_executor.get() == NULL) { LOG(ERROR) << "Unable to create DesktopEnvironment"; @@ -58,26 +62,31 @@ scoped_ptr<DesktopEnvironment> DesktopEnvironment::CreateForService( return scoped_ptr<DesktopEnvironment>( new DesktopEnvironment(context, capturer.Pass(), - event_executor.Pass())); + event_executor.Pass(), + audio_capturer.Pass())); } // static scoped_ptr<DesktopEnvironment> DesktopEnvironment::CreateFake( ChromotingHostContext* context, scoped_ptr<Capturer> capturer, - scoped_ptr<EventExecutor> event_executor) { + scoped_ptr<EventExecutor> event_executor, + scoped_ptr<AudioCapturer> audio_capturer) { return scoped_ptr<DesktopEnvironment>( new DesktopEnvironment(context, capturer.Pass(), - event_executor.Pass())); + event_executor.Pass(), + audio_capturer.Pass())); } DesktopEnvironment::DesktopEnvironment( ChromotingHostContext* context, scoped_ptr<Capturer> capturer, - scoped_ptr<EventExecutor> event_executor) + scoped_ptr<EventExecutor> event_executor, + scoped_ptr<AudioCapturer> audio_capturer) : context_(context), capturer_(capturer.Pass()), + audio_capturer_(audio_capturer.Pass()), event_executor_(event_executor.Pass()) { } diff --git a/remoting/host/desktop_environment.h b/remoting/host/desktop_environment.h index 745cf2f..417bec2 100644 --- a/remoting/host/desktop_environment.h +++ b/remoting/host/desktop_environment.h @@ -13,6 +13,7 @@ namespace remoting { +class AudioCapturer; class Capturer; class ChromotingHostContext; @@ -34,27 +35,33 @@ class DesktopEnvironment { static scoped_ptr<DesktopEnvironment> CreateFake( ChromotingHostContext* context, scoped_ptr<Capturer> capturer, - scoped_ptr<EventExecutor> event_executor); + scoped_ptr<EventExecutor> event_executor, + scoped_ptr<AudioCapturer> audio_capturer); virtual ~DesktopEnvironment(); Capturer* capturer() const { return capturer_.get(); } EventExecutor* event_executor() const { return event_executor_.get(); } + AudioCapturer* audio_capturer() const { return audio_capturer_.get(); } void OnSessionStarted(scoped_ptr<protocol::ClipboardStub> client_clipboard); void OnSessionFinished(); private: DesktopEnvironment(ChromotingHostContext* context, scoped_ptr<Capturer> capturer, - scoped_ptr<EventExecutor> event_executor); + scoped_ptr<EventExecutor> event_executor, + scoped_ptr<AudioCapturer> audio_capturer); // Host context used to make sure operations are run on the correct thread. // This is owned by the ChromotingHost. ChromotingHostContext* context_; - // Capturer to be used by ScreenRecorder. + // Used to capture video to deliver to clients. scoped_ptr<Capturer> capturer_; + // Used to capture audio to deliver to clients. + scoped_ptr<AudioCapturer> audio_capturer_; + // Executes input and clipboard events received from the client. scoped_ptr<EventExecutor> event_executor_; diff --git a/remoting/host/simple_host_process.cc b/remoting/host/simple_host_process.cc index e9935d1..eb3db60 100644 --- a/remoting/host/simple_host_process.cc +++ b/remoting/host/simple_host_process.cc @@ -33,6 +33,7 @@ #include "net/base/network_change_notifier.h" #include "net/socket/ssl_server_socket.h" #include "remoting/base/constants.h" +#include "remoting/host/audio_capturer.h" #include "remoting/host/capturer_fake.h" #include "remoting/host/chromoting_host.h" #include "remoting/host/chromoting_host_context.h" @@ -228,8 +229,12 @@ class SimpleHost : public HeartbeatSender::Listener { scoped_ptr<EventExecutor> event_executor = EventExecutor::Create( context_.desktop_task_runner(), context_.ui_task_runner(), capturer.get()); + scoped_ptr<AudioCapturer> audio_capturer(NULL); desktop_environment_ = DesktopEnvironment::CreateFake( - &context_, capturer.Pass(), event_executor.Pass()); + &context_, + capturer.Pass(), + event_executor.Pass(), + audio_capturer.Pass()); } else { desktop_environment_ = DesktopEnvironment::Create(&context_); } diff --git a/remoting/protocol/session_config.cc b/remoting/protocol/session_config.cc index 8479a04..cf6657d 100644 --- a/remoting/protocol/session_config.cc +++ b/remoting/protocol/session_config.cc @@ -46,7 +46,7 @@ SessionConfig SessionConfig::GetDefault() { result.set_video_config(ChannelConfig(ChannelConfig::TRANSPORT_STREAM, kDefaultStreamVersion, ChannelConfig::CODEC_VP8)); - result.set_audio_config(ChannelConfig(ChannelConfig::TRANSPORT_STREAM, + result.set_audio_config(ChannelConfig(ChannelConfig::TRANSPORT_NONE, kDefaultStreamVersion, ChannelConfig::CODEC_VERBATIM)); return result; diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index fddc6d1..d2734fa 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -1106,6 +1106,10 @@ '../crypto/crypto.gyp:crypto', ], 'sources': [ + 'host/audio_capturer.cc', + 'host/audio_capturer.h', + 'host/audio_scheduler.cc', + 'host/audio_scheduler.h', 'host/capturer.h', 'host/capturer_helper.cc', 'host/capturer_helper.h', |