Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(231)

Side by Side Diff: remoting/host/audio_capturer_linux.cc

Issue 11316010: Fix AudioCapturer implementation to read from audio pipe even when there are no active clients. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/host/audio_capturer_linux.h" 5 #include "remoting/host/audio_capturer_linux.h"
6 6
7 #include <fcntl.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11
12 #include "base/eintr_wrapper.h"
13 #include "base/file_path.h" 7 #include "base/file_path.h"
14 #include "base/lazy_instance.h" 8 #include "base/lazy_instance.h"
15 #include "base/logging.h" 9 #include "base/logging.h"
16 #include "base/stl_util.h"
17 #include "remoting/proto/audio.pb.h" 10 #include "remoting/proto/audio.pb.h"
18 #include "remoting/host/chromoting_host_context.h"
19 11
20 namespace remoting { 12 namespace remoting {
21 13
22 namespace { 14 namespace {
23 15
24 // PulseAudio's module-pipe-sink must be configured to use the following 16 // PulseAudio's module-pipe-sink must be configured to use the following
25 // parameters for the sink we read from. 17 // parameters for the sink we read from.
26 const AudioPacket_SamplingRate kSamplingRate = AudioPacket::SAMPLING_RATE_44100; 18 const AudioPacket_SamplingRate kSamplingRate = AudioPacket::SAMPLING_RATE_48000;
27 const int kChannels = 2;
28 const int kBytesPerSample = 2;
29 19
30 // Read data from the pipe every 40ms. 20 base::LazyInstance<scoped_refptr<PulseaudioPipeSinkReader> >::Leaky
31 const int kCapturingPeriodMs = 40; 21 g_pulseaudio_pipe_sink_reader = LAZY_INSTANCE_INITIALIZER;
32
33 #if !defined(F_SETPIPE_SZ)
34 // F_SETPIPE_SZ is supported only starting linux 2.6.35, but we want to be able
35 // to compile this code on machines with older kernel.
36 #define F_SETPIPE_SZ 1031
37 #endif // defined(F_SETPIPE_SZ)
38
39 // Pipename used to capture audio stream from.
40 // TODO(sergeyu): Pass this to AudioCapturerLinux constructor once we have
41 // Linux-specific DesktopEnvironmentFactory
42 base::LazyInstance<FilePath>::Leaky
43 g_audio_pipe_name = LAZY_INSTANCE_INITIALIZER;
44
45 const int IsPacketOfSilence(const std::string& data) {
46 const int64* int_buf = reinterpret_cast<const int64*>(data.data());
47 for (size_t i = 0; i < data.size() / sizeof(int64); i++) {
48 if (int_buf[i] != 0)
49 return false;
50 }
51 for (size_t i = data.size() - data.size() % sizeof(int64);
52 i < data.size(); i++) {
53 if (data.data()[i] != 0)
54 return false;
55 }
56 return true;
57 }
58 22
59 } // namespace 23 } // namespace
60 24
61 AudioCapturerLinux::AudioCapturerLinux(const FilePath& pipe_name) { 25 void AudioCapturerLinux::InitializePipeReader(
62 pipe_fd_ = HANDLE_EINTR(open( 26 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
63 pipe_name.value().c_str(), O_RDONLY | O_NONBLOCK)); 27 const FilePath& pipe_name) {
64 if (pipe_fd_ < 0) { 28 g_pulseaudio_pipe_sink_reader.Get() =
65 LOG(ERROR) << "Failed to open " << pipe_name.value(); 29 new PulseaudioPipeSinkReader(task_runner, pipe_name);
66 return; 30 }
67 }
68 31
69 // Set buffer size for the pipe to the double of what's required for samples 32 AudioCapturerLinux::AudioCapturerLinux(
70 // of each capturing period. 33 scoped_refptr<PulseaudioPipeSinkReader> reader)
71 int pipe_buffer_size = 2 * kCapturingPeriodMs * kSamplingRate * kChannels * 34 : reader_(reader) {
72 kBytesPerSample / base::Time::kMillisecondsPerSecond;
73 int result = HANDLE_EINTR(fcntl(pipe_fd_, F_SETPIPE_SZ, pipe_buffer_size));
74 if (result < 0) {
75 PLOG(ERROR) << "fcntl";
76 }
77
78 WaitForPipeReadable();
79 } 35 }
80 36
81 AudioCapturerLinux::~AudioCapturerLinux() { 37 AudioCapturerLinux::~AudioCapturerLinux() {
82 } 38 }
83 39
84 bool AudioCapturerLinux::Start(const PacketCapturedCallback& callback) { 40 bool AudioCapturerLinux::Start(const PacketCapturedCallback& callback) {
85 if (pipe_fd_ < 0)
86 return false;
87
88 callback_ = callback; 41 callback_ = callback;
89 42 reader_->AddCapturer(this);
90 return true; 43 return true;
91 } 44 }
92 45
93 void AudioCapturerLinux::Stop() { 46 void AudioCapturerLinux::Stop() {
47 reader_->RemoveCapturer(this);
94 callback_.Reset(); 48 callback_.Reset();
95 } 49 }
96 50
97 bool AudioCapturerLinux::IsStarted() { 51 bool AudioCapturerLinux::IsStarted() {
98 return !callback_.is_null(); 52 return !callback_.is_null();
99 } 53 }
100 54
101 void AudioCapturerLinux::OnFileCanReadWithoutBlocking(int fd) { 55 void AudioCapturerLinux::OnDataRead(
102 DCHECK_EQ(fd, pipe_fd_); 56 scoped_refptr<base::RefCountedString> data) {
103 StartTimer(); 57 DCHECK(!callback_.is_null());
104 }
105
106 void AudioCapturerLinux::OnFileCanWriteWithoutBlocking(int fd) {
107 NOTREACHED();
108 }
109
110 void AudioCapturerLinux::StartTimer() {
111 started_time_ = base::TimeTicks::Now();
112 last_capture_samples_ = 0;
113 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCapturingPeriodMs),
114 this, &AudioCapturerLinux::DoCapture);
115 }
116
117 void AudioCapturerLinux::DoCapture() {
118 DCHECK_GT(pipe_fd_, 0);
119
120 // Calculate how much we need read from the pipe. Pulseaudio doesn't control
121 // how much data it writes to the pipe, so we need to pace the stream, so
122 // that we read the exact number of the samples per second we need.
123 base::TimeDelta stream_position = base::TimeTicks::Now() - started_time_;
124 int64 stream_position_samples = stream_position.InMilliseconds() *
125 kSamplingRate / base::Time::kMillisecondsPerSecond;
126 int64 samples_to_capture =
127 stream_position_samples - last_capture_samples_;
128 last_capture_samples_ = stream_position_samples;
129 int64 read_size =
130 samples_to_capture * kChannels * kBytesPerSample;
131
132 std::string data = left_over_bytes_;
133 int pos = data.size();
134 left_over_bytes_.clear();
135 data.resize(read_size);
136
137 while (pos < read_size) {
138 int read_result = HANDLE_EINTR(
139 read(pipe_fd_, string_as_array(&data) + pos, read_size - pos));
140 if (read_result >= 0) {
141 pos += read_result;
142 } else {
143 if (errno != EWOULDBLOCK && errno != EAGAIN)
144 PLOG(ERROR) << "read";
145 break;
146 }
147 }
148
149 if (pos == 0) {
150 WaitForPipeReadable();
151 return;
152 }
153
154 // Save any incomplete samples we've read for later. Each packet should
155 // contain integer number of samples.
156 int incomplete_samples_bytes = pos % (kChannels * kBytesPerSample);
157 left_over_bytes_.assign(data, pos - incomplete_samples_bytes,
158 incomplete_samples_bytes);
159 data.resize(pos - incomplete_samples_bytes);
160
161 if (callback_.is_null())
162 return;
163
164 if (IsPacketOfSilence(data))
165 return;
166 58
167 scoped_ptr<AudioPacket> packet(new AudioPacket()); 59 scoped_ptr<AudioPacket> packet(new AudioPacket());
168 packet->add_data(data); 60 packet->add_data(data->data());
169 packet->set_encoding(AudioPacket::ENCODING_RAW); 61 packet->set_encoding(AudioPacket::ENCODING_RAW);
170 packet->set_sampling_rate(kSamplingRate); 62 packet->set_sampling_rate(kSamplingRate);
171 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); 63 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
172 packet->set_channels(AudioPacket::CHANNELS_STEREO); 64 packet->set_channels(AudioPacket::CHANNELS_STEREO);
173 callback_.Run(packet.Pass()); 65 callback_.Run(packet.Pass());
174 } 66 }
175 67
176 void AudioCapturerLinux::WaitForPipeReadable() {
177 timer_.Stop();
178 MessageLoopForIO::current()->WatchFileDescriptor(
179 pipe_fd_, false, MessageLoopForIO::WATCH_READ,
180 &file_descriptor_watcher_, this);
181 }
182
183 void AudioCapturerLinux::SetPipeName(const FilePath& pipe_name) {
184 g_audio_pipe_name.Get() = pipe_name;
185 }
186
187 bool AudioCapturer::IsSupported() { 68 bool AudioCapturer::IsSupported() {
188 return !g_audio_pipe_name.Get().empty(); 69 return g_pulseaudio_pipe_sink_reader.Get() != NULL;
189 } 70 }
190 71
191 scoped_ptr<AudioCapturer> AudioCapturer::Create() { 72 scoped_ptr<AudioCapturer> AudioCapturer::Create() {
192 FilePath path = g_audio_pipe_name.Get(); 73 scoped_refptr<PulseaudioPipeSinkReader> reader =
193 if (path.empty()) 74 g_pulseaudio_pipe_sink_reader.Get();
Wez 2012/10/31 00:56:28 Perhaps the style guide prohibits it, but if you m
Sergey Ulanov 2012/10/31 18:41:24 Static locals are not prohibited as long as they d
75 if (!reader)
194 return scoped_ptr<AudioCapturer>(); 76 return scoped_ptr<AudioCapturer>();
195 return scoped_ptr<AudioCapturer>(new AudioCapturerLinux(path)); 77 return scoped_ptr<AudioCapturer>(new AudioCapturerLinux(reader));
196 } 78 }
197 79
198 } // namespace remoting 80 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698