summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--remoting/host/audio_capturer.cc15
-rw-r--r--remoting/host/audio_capturer.h32
-rw-r--r--remoting/host/audio_scheduler.cc95
-rw-r--r--remoting/host/audio_scheduler.h87
-rw-r--r--remoting/host/chromoting_host.cc31
-rw-r--r--remoting/host/chromoting_host.h16
-rw-r--r--remoting/host/chromoting_host_unittest.cc5
-rw-r--r--remoting/host/desktop_environment.cc19
-rw-r--r--remoting/host/desktop_environment.h13
-rw-r--r--remoting/host/simple_host_process.cc7
-rw-r--r--remoting/protocol/session_config.cc2
-rw-r--r--remoting/remoting.gyp4
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',