summaryrefslogtreecommitdiffstats
path: root/remoting/host
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/host')
-rw-r--r--remoting/host/audio_capturer.h5
-rw-r--r--remoting/host/audio_capturer_linux.cc163
-rw-r--r--remoting/host/audio_capturer_linux.h58
-rw-r--r--remoting/host/audio_capturer_mac.cc4
-rw-r--r--remoting/host/audio_capturer_win.cc10
-rw-r--r--remoting/host/audio_capturer_win.h7
-rw-r--r--remoting/host/chromoting_host.cc3
7 files changed, 241 insertions, 9 deletions
diff --git a/remoting/host/audio_capturer.h b/remoting/host/audio_capturer.h
index 1c456c1..570d585 100644
--- a/remoting/host/audio_capturer.h
+++ b/remoting/host/audio_capturer.h
@@ -19,6 +19,9 @@ class AudioCapturer {
virtual ~AudioCapturer() {}
+ // Returns true if audio capturing is supported on this platform. If this
+ // returns true, then Create() must not return NULL.
+ static bool IsSupported();
static scoped_ptr<AudioCapturer> Create();
// Capturers should sample at a 44.1 or 48 kHz sampling rate, in uncompressed
@@ -29,7 +32,7 @@ class AudioCapturer {
// resources.
virtual void Stop() = 0;
// Returns true if the audio capturer is running.
- virtual bool IsRunning() = 0;
+ virtual bool IsStarted() = 0;
static bool IsValidSampleRate(int sample_rate);
};
diff --git a/remoting/host/audio_capturer_linux.cc b/remoting/host/audio_capturer_linux.cc
index 1901dfc..f406e73 100644
--- a/remoting/host/audio_capturer_linux.cc
+++ b/remoting/host/audio_capturer_linux.cc
@@ -2,14 +2,171 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "remoting/host/audio_capturer_linux.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "base/command_line.h"
+#include "base/eintr_wrapper.h"
+#include "base/file_path.h"
#include "base/logging.h"
-#include "remoting/host/audio_capturer.h"
+#include "base/stl_util.h"
+#include "remoting/proto/audio.pb.h"
namespace remoting {
+namespace {
+
+const char kAudioPipeOptionName[] = "audio-pipe-name";
+
+// PulseAudio's module-pipe-sink must be configured to use the following
+// parameters for the sink we read from.
+const AudioPacket_SamplingRate kSamplingRate = AudioPacket::SAMPLING_RATE_44100;
+const int kChannels = 2;
+const int kBytesPerSample = 2;
+
+// Read data from the pipe every 40ms.
+const int kCapturingPeriodMs = 40;
+
+#if !defined(F_SETPIPE_SZ)
+// F_SETPIPE_SZ is supported only starting linux 2.6.35, but we want to be able
+// to compile this code on machines with older kernel.
+#define F_SETPIPE_SZ 1031
+#endif // defined(F_SETPIPE_SZ)
+
+} // namespace
+
+AudioCapturerLinux::AudioCapturerLinux(const FilePath& pipe_name) {
+ pipe_fd_ = HANDLE_EINTR(open(
+ pipe_name.value().c_str(), O_RDONLY | O_NONBLOCK));
+ if (pipe_fd_ < 0) {
+ LOG(ERROR) << "Failed to open " << pipe_name.value();
+ return;
+ }
+
+ // Set buffer size for the pipe to the double of what's required for samples
+ // of each capturing period.
+ int pipe_buffer_size = 2 * kCapturingPeriodMs * kSamplingRate * kChannels *
+ kBytesPerSample / base::Time::kMillisecondsPerSecond;
+ int result = HANDLE_EINTR(fcntl(pipe_fd_, F_SETPIPE_SZ, pipe_buffer_size));
+ if (result < 0) {
+ PLOG(ERROR) << "fcntl";
+ }
+
+ WaitForPipeReadable();
+}
+
+AudioCapturerLinux::~AudioCapturerLinux() {
+}
+
+bool AudioCapturerLinux::Start(const PacketCapturedCallback& callback) {
+ if (pipe_fd_ < 0)
+ return false;
+
+ callback_ = callback;
+
+ return true;
+}
+
+void AudioCapturerLinux::Stop() {
+ callback_.Reset();
+}
+
+bool AudioCapturerLinux::IsStarted() {
+ return !callback_.is_null();
+}
+
+void AudioCapturerLinux::OnFileCanReadWithoutBlocking(int fd) {
+ DCHECK_EQ(fd, pipe_fd_);
+ StartTimer();
+}
+
+void AudioCapturerLinux::OnFileCanWriteWithoutBlocking(int fd) {
+ NOTREACHED();
+}
+
+void AudioCapturerLinux::StartTimer() {
+ started_time_ = base::TimeTicks::Now();
+ last_capture_samples_ = 0;
+ timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCapturingPeriodMs),
+ this, &AudioCapturerLinux::DoCapture);
+}
+
+void AudioCapturerLinux::DoCapture() {
+ DCHECK_GT(pipe_fd_, 0);
+
+ // Calculate how much we need read from the pipe. Pulseaudio doesn't control
+ // how much data it writes to the pipe, so we need to pace the stream, so
+ // that we read the exact number of the samples per second we need.
+ base::TimeDelta stream_position = base::TimeTicks::Now() - started_time_;
+ int64 stream_position_samples = stream_position.InMilliseconds() *
+ kSamplingRate / base::Time::kMillisecondsPerSecond;
+ int64 samples_to_capture =
+ stream_position_samples - last_capture_samples_;
+ last_capture_samples_ = stream_position_samples;
+ int64 read_size =
+ samples_to_capture * kChannels * kBytesPerSample;
+
+ std::string data = left_over_bytes_;
+ int pos = data.size();
+ left_over_bytes_.clear();
+ data.resize(read_size);
+
+ while (pos < read_size) {
+ int read_result = HANDLE_EINTR(
+ read(pipe_fd_, string_as_array(&data) + pos, read_size - pos));
+ if (read_result >= 0) {
+ pos += read_result;
+ } else {
+ if (errno != EWOULDBLOCK && errno != EAGAIN)
+ PLOG(ERROR) << "read";
+ break;
+ }
+ }
+
+ if (pos == 0) {
+ WaitForPipeReadable();
+ return;
+ }
+
+ // Save any incomplete samples we've read for later. Each packet should
+ // contain integer number of samples.
+ int incomplete_samples_bytes = pos % (kChannels * kBytesPerSample);
+ left_over_bytes_.assign(data, pos - incomplete_samples_bytes,
+ incomplete_samples_bytes);
+ data.resize(pos - incomplete_samples_bytes);
+
+ if (!callback_.is_null()) {
+ scoped_ptr<AudioPacket> packet(new AudioPacket());
+ packet->add_data(data);
+ packet->set_encoding(AudioPacket::ENCODING_RAW);
+ packet->set_sampling_rate(kSamplingRate);
+ packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
+ packet->set_channels(AudioPacket::CHANNELS_STEREO);
+ callback_.Run(packet.Pass());
+ }
+}
+
+void AudioCapturerLinux::WaitForPipeReadable() {
+ timer_.Stop();
+ MessageLoopForIO::current()->WatchFileDescriptor(
+ pipe_fd_, false, MessageLoopForIO::WATCH_READ,
+ &file_descriptor_watcher_, this);
+}
+
+bool AudioCapturer::IsSupported() {
+ CommandLine* cl = CommandLine::ForCurrentProcess();
+ return !cl->GetSwitchValuePath(kAudioPipeOptionName).empty();
+}
+
scoped_ptr<AudioCapturer> AudioCapturer::Create() {
- NOTIMPLEMENTED();
- return scoped_ptr<AudioCapturer>(NULL);
+ CommandLine* cl = CommandLine::ForCurrentProcess();
+ FilePath path = cl->GetSwitchValuePath(kAudioPipeOptionName);
+ DCHECK(!path.empty());
+ return scoped_ptr<AudioCapturer>(new AudioCapturerLinux(path));
}
} // namespace remoting
diff --git a/remoting/host/audio_capturer_linux.h b/remoting/host/audio_capturer_linux.h
new file mode 100644
index 0000000..c85beb3
--- /dev/null
+++ b/remoting/host/audio_capturer_linux.h
@@ -0,0 +1,58 @@
+// 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_LINUX_H_
+#define REMOTING_HOST_AUDIO_CAPTURER_LINUX_H_
+
+#include "remoting/host/audio_capturer.h"
+
+#include "base/message_loop.h"
+#include "base/time.h"
+#include "base/timer.h"
+
+class FilePath;
+
+namespace remoting {
+
+class AudioCapturerLinux : public AudioCapturer,
+ public MessageLoopForIO::Watcher {
+ public:
+ explicit AudioCapturerLinux(const FilePath& pipe_name);
+ virtual ~AudioCapturerLinux();
+
+ // AudioCapturer interface.
+ virtual bool Start(const PacketCapturedCallback& callback) OVERRIDE;
+ virtual void Stop() OVERRIDE;
+ virtual bool IsStarted() OVERRIDE;
+
+ // MessageLoopForIO::Watcher interface.
+ virtual void OnFileCanReadWithoutBlocking(int fd) OVERRIDE;
+ virtual void OnFileCanWriteWithoutBlocking(int fd) OVERRIDE;
+
+ private:
+ void StartTimer();
+ void DoCapture();
+ void WaitForPipeReadable();
+
+ int pipe_fd_;
+ base::RepeatingTimer<AudioCapturerLinux> timer_;
+ PacketCapturedCallback callback_;
+
+ // Time when capturing was started.
+ base::TimeTicks started_time_;
+
+ // Stream position of the last capture.
+ int64 last_capture_samples_;
+
+ // Bytes left from the previous read.
+ std::string left_over_bytes_;
+
+ MessageLoopForIO::FileDescriptorWatcher file_descriptor_watcher_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioCapturerLinux);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_AUDIO_CAPTURER_LINUX_H_
diff --git a/remoting/host/audio_capturer_mac.cc b/remoting/host/audio_capturer_mac.cc
index 1901dfc..1db67c2 100644
--- a/remoting/host/audio_capturer_mac.cc
+++ b/remoting/host/audio_capturer_mac.cc
@@ -7,6 +7,10 @@
namespace remoting {
+bool AudioCapturer::IsSupported() {
+ return false;
+}
+
scoped_ptr<AudioCapturer> AudioCapturer::Create() {
NOTIMPLEMENTED();
return scoped_ptr<AudioCapturer>(NULL);
diff --git a/remoting/host/audio_capturer_win.cc b/remoting/host/audio_capturer_win.cc
index 6479afa..90714ac 100644
--- a/remoting/host/audio_capturer_win.cc
+++ b/remoting/host/audio_capturer_win.cc
@@ -199,7 +199,7 @@ bool AudioCapturerWin::Start(const PacketCapturedCallback& callback) {
void AudioCapturerWin::Stop() {
DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(IsRunning());
+ DCHECK(IsStarted());
capture_timer_.reset();
mm_device_.Release();
@@ -211,7 +211,7 @@ void AudioCapturerWin::Stop() {
thread_checker_.DetachFromThread();
}
-bool AudioCapturerWin::IsRunning() {
+bool AudioCapturerWin::IsStarted() {
DCHECK(thread_checker_.CalledOnValidThread());
return capture_timer_.get() != NULL;
}
@@ -219,7 +219,7 @@ bool AudioCapturerWin::IsRunning() {
void AudioCapturerWin::DoCapture() {
DCHECK(AudioCapturer::IsValidSampleRate(sampling_rate_));
DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(IsRunning());
+ DCHECK(IsStarted());
// Fetch all packets from the audio capture endpoint buffer.
while (true) {
@@ -279,6 +279,10 @@ bool AudioCapturerWin::IsPacketOfSilence(
return true;
}
+bool AudioCapturer::IsSupported() {
+ return true;
+}
+
scoped_ptr<AudioCapturer> AudioCapturer::Create() {
return scoped_ptr<AudioCapturer>(new AudioCapturerWin());
}
diff --git a/remoting/host/audio_capturer_win.h b/remoting/host/audio_capturer_win.h
index bfd9803..4b0d38e 100644
--- a/remoting/host/audio_capturer_win.h
+++ b/remoting/host/audio_capturer_win.h
@@ -2,6 +2,9 @@
// 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_WIN_H_
+#define REMOTING_HOST_AUDIO_CAPTURER_WIN_H_
+
#include <audioclient.h>
#include <mmdeviceapi.h>
@@ -24,7 +27,7 @@ class AudioCapturerWin : public AudioCapturer {
// AudioCapturer interface.
virtual bool Start(const PacketCapturedCallback& callback) OVERRIDE;
virtual void Stop() OVERRIDE;
- virtual bool IsRunning() OVERRIDE;
+ virtual bool IsStarted() OVERRIDE;
static bool IsPacketOfSilence(const int16* samples, int number_of_samples);
@@ -52,3 +55,5 @@ class AudioCapturerWin : public AudioCapturer {
};
} // namespace remoting
+
+#endif // REMOTING_HOST_AUDIO_CAPTURER_WIN_H_
diff --git a/remoting/host/chromoting_host.cc b/remoting/host/chromoting_host.cc
index 7dae872..8b71a1f 100644
--- a/remoting/host/chromoting_host.cc
+++ b/remoting/host/chromoting_host.cc
@@ -16,6 +16,7 @@
#include "remoting/codec/video_encoder.h"
#include "remoting/codec/video_encoder_row_based.h"
#include "remoting/codec/video_encoder_vp8.h"
+#include "remoting/host/audio_capturer.h"
#include "remoting/host/audio_scheduler.h"
#include "remoting/host/chromoting_host_context.h"
#include "remoting/host/desktop_environment.h"
@@ -83,7 +84,7 @@ ChromotingHost::ChromotingHost(
DCHECK(desktop_environment_);
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
- if (!desktop_environment_->audio_capturer()) {
+ if (!AudioCapturer::IsSupported()) {
// Disable audio by replacing our list of supported audio configurations
// with the NONE config.
protocol_config_->mutable_audio_configs()->clear();