diff options
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/host/audio_capturer.h | 5 | ||||
-rw-r--r-- | remoting/host/audio_capturer_linux.cc | 163 | ||||
-rw-r--r-- | remoting/host/audio_capturer_linux.h | 58 | ||||
-rw-r--r-- | remoting/host/audio_capturer_mac.cc | 4 | ||||
-rw-r--r-- | remoting/host/audio_capturer_win.cc | 10 | ||||
-rw-r--r-- | remoting/host/audio_capturer_win.h | 7 | ||||
-rw-r--r-- | remoting/host/chromoting_host.cc | 3 | ||||
-rw-r--r-- | remoting/remoting.gyp | 2 |
8 files changed, 243 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(); diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index b41fd56..82381c0 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -1196,8 +1196,10 @@ 'host/audio_capturer.cc', 'host/audio_capturer.h', 'host/audio_capturer_linux.cc', + 'host/audio_capturer_linux.h', 'host/audio_capturer_mac.cc', 'host/audio_capturer_win.cc', + 'host/audio_capturer_win.h', 'host/audio_scheduler.cc', 'host/audio_scheduler.h', 'host/capture_scheduler.cc', |