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

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> 7 #include <fcntl.h>
8 #include <sys/stat.h> 8 #include <sys/stat.h>
9 #include <sys/types.h> 9 #include <sys/types.h>
10 #include <unistd.h> 10 #include <unistd.h>
(...skipping 18 matching lines...) Expand all
29 29
30 // Read data from the pipe every 40ms. 30 // Read data from the pipe every 40ms.
31 const int kCapturingPeriodMs = 40; 31 const int kCapturingPeriodMs = 40;
32 32
33 #if !defined(F_SETPIPE_SZ) 33 #if !defined(F_SETPIPE_SZ)
34 // F_SETPIPE_SZ is supported only starting linux 2.6.35, but we want to be able 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. 35 // to compile this code on machines with older kernel.
36 #define F_SETPIPE_SZ 1031 36 #define F_SETPIPE_SZ 1031
37 #endif // defined(F_SETPIPE_SZ) 37 #endif // defined(F_SETPIPE_SZ)
38 38
39 // Pipename used to capture audio stream from. 39
40 // TODO(sergeyu): Pass this to AudioCapturerLinux constructor once we have 40 base::LazyInstance<scoped_refptr<AudioCapturerLinuxCore> >::Leaky
41 // Linux-specific DesktopEnvironmentFactory 41 g_audio_capturer_core = LAZY_INSTANCE_INITIALIZER;
42 base::LazyInstance<FilePath>::Leaky
43 g_audio_pipe_name = LAZY_INSTANCE_INITIALIZER;
44 42
45 const int IsPacketOfSilence(const std::string& data) { 43 const int IsPacketOfSilence(const std::string& data) {
46 const int64* int_buf = reinterpret_cast<const int64*>(data.data()); 44 const int64* int_buf = reinterpret_cast<const int64*>(data.data());
47 for (size_t i = 0; i < data.size() / sizeof(int64); i++) { 45 for (size_t i = 0; i < data.size() / sizeof(int64); i++) {
48 if (int_buf[i] != 0) 46 if (int_buf[i] != 0)
49 return false; 47 return false;
50 } 48 }
51 for (size_t i = data.size() - data.size() % sizeof(int64); 49 for (size_t i = data.size() - data.size() % sizeof(int64);
52 i < data.size(); i++) { 50 i < data.size(); i++) {
53 if (data.data()[i] != 0) 51 if (data.data()[i] != 0)
54 return false; 52 return false;
55 } 53 }
56 return true; 54 return true;
57 } 55 }
58 56
59 } // namespace 57 } // namespace
60 58
61 AudioCapturerLinux::AudioCapturerLinux(const FilePath& pipe_name) { 59 void AudioCapturerLinuxCore::Initialize(
60 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
61 const FilePath& pipe_name) {
62 g_audio_capturer_core.Get() =
63 new AudioCapturerLinuxCore(task_runner, pipe_name);
64 }
65
66 AudioCapturerLinuxCore::AudioCapturerLinuxCore(
67 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
68 const FilePath& pipe_name)
69 : main_task_runner_(task_runner),
70 capturers_(new ObserverListThreadSafe<AudioCapturerLinux>()) {
71 main_task_runner_->PostTask(FROM_HERE, base::Bind(
72 &AudioCapturerLinuxCore::StartReading, this, pipe_name));
73 }
74
75 void AudioCapturerLinuxCore::StartReading(const FilePath& pipe_name) {
62 pipe_fd_ = HANDLE_EINTR(open( 76 pipe_fd_ = HANDLE_EINTR(open(
63 pipe_name.value().c_str(), O_RDONLY | O_NONBLOCK)); 77 pipe_name.value().c_str(), O_RDONLY | O_NONBLOCK));
64 if (pipe_fd_ < 0) { 78 if (pipe_fd_ < 0) {
65 LOG(ERROR) << "Failed to open " << pipe_name.value(); 79 LOG(ERROR) << "Failed to open " << pipe_name.value();
66 return; 80 return;
67 } 81 }
68 82
69 // Set buffer size for the pipe to the double of what's required for samples 83 // Set buffer size for the pipe to the double of what's required for samples
70 // of each capturing period. 84 // of each capturing period.
71 int pipe_buffer_size = 2 * kCapturingPeriodMs * kSamplingRate * kChannels * 85 int pipe_buffer_size = 2 * kCapturingPeriodMs * kSamplingRate * kChannels *
72 kBytesPerSample / base::Time::kMillisecondsPerSecond; 86 kBytesPerSample / base::Time::kMillisecondsPerSecond;
73 int result = HANDLE_EINTR(fcntl(pipe_fd_, F_SETPIPE_SZ, pipe_buffer_size)); 87 int result = HANDLE_EINTR(fcntl(pipe_fd_, F_SETPIPE_SZ, pipe_buffer_size));
74 if (result < 0) { 88 if (result < 0) {
75 PLOG(ERROR) << "fcntl"; 89 PLOG(ERROR) << "fcntl";
76 } 90 }
77 91
78 WaitForPipeReadable(); 92 WaitForPipeReadable();
79 } 93 }
80 94
81 AudioCapturerLinux::~AudioCapturerLinux() { 95 AudioCapturerLinuxCore::~AudioCapturerLinuxCore() {
82 } 96 }
83 97
84 bool AudioCapturerLinux::Start(const PacketCapturedCallback& callback) { 98 void AudioCapturerLinuxCore::AddCapturer(AudioCapturerLinux* capturer) {
85 if (pipe_fd_ < 0) 99 capturers_->AddObserver(capturer);
86 return false; 100 }
87 101 void AudioCapturerLinuxCore::RemoveCapturer(AudioCapturerLinux* capturer) {
88 callback_ = callback; 102 capturers_->RemoveObserver(capturer);
89
90 return true;
91 } 103 }
92 104
93 void AudioCapturerLinux::Stop() { 105 void AudioCapturerLinuxCore::OnFileCanReadWithoutBlocking(int fd) {
94 callback_.Reset();
95 }
96
97 bool AudioCapturerLinux::IsStarted() {
98 return !callback_.is_null();
99 }
100
101 void AudioCapturerLinux::OnFileCanReadWithoutBlocking(int fd) {
102 DCHECK_EQ(fd, pipe_fd_); 106 DCHECK_EQ(fd, pipe_fd_);
103 StartTimer(); 107 StartTimer();
104 } 108 }
105 109
106 void AudioCapturerLinux::OnFileCanWriteWithoutBlocking(int fd) { 110 void AudioCapturerLinuxCore::OnFileCanWriteWithoutBlocking(int fd) {
107 NOTREACHED(); 111 NOTREACHED();
108 } 112 }
109 113
110 void AudioCapturerLinux::StartTimer() { 114 void AudioCapturerLinuxCore::StartTimer() {
111 started_time_ = base::TimeTicks::Now(); 115 started_time_ = base::TimeTicks::Now();
112 last_capture_samples_ = 0; 116 last_capture_samples_ = 0;
113 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCapturingPeriodMs), 117 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCapturingPeriodMs),
114 this, &AudioCapturerLinux::DoCapture); 118 this, &AudioCapturerLinuxCore::DoCapture);
115 } 119 }
116 120
117 void AudioCapturerLinux::DoCapture() { 121 void AudioCapturerLinuxCore::DoCapture() {
118 DCHECK_GT(pipe_fd_, 0); 122 DCHECK_GT(pipe_fd_, 0);
119 123
120 // Calculate how much we need read from the pipe. Pulseaudio doesn't control 124 // 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 125 // 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. 126 // that we read the exact number of the samples per second we need.
123 base::TimeDelta stream_position = base::TimeTicks::Now() - started_time_; 127 base::TimeDelta stream_position = base::TimeTicks::Now() - started_time_;
124 int64 stream_position_samples = stream_position.InMilliseconds() * 128 int64 stream_position_samples = stream_position.InMilliseconds() *
125 kSamplingRate / base::Time::kMillisecondsPerSecond; 129 kSamplingRate / base::Time::kMillisecondsPerSecond;
126 int64 samples_to_capture = 130 int64 samples_to_capture =
127 stream_position_samples - last_capture_samples_; 131 stream_position_samples - last_capture_samples_;
(...skipping 23 matching lines...) Expand all
151 return; 155 return;
152 } 156 }
153 157
154 // Save any incomplete samples we've read for later. Each packet should 158 // Save any incomplete samples we've read for later. Each packet should
155 // contain integer number of samples. 159 // contain integer number of samples.
156 int incomplete_samples_bytes = pos % (kChannels * kBytesPerSample); 160 int incomplete_samples_bytes = pos % (kChannels * kBytesPerSample);
157 left_over_bytes_.assign(data, pos - incomplete_samples_bytes, 161 left_over_bytes_.assign(data, pos - incomplete_samples_bytes,
158 incomplete_samples_bytes); 162 incomplete_samples_bytes);
159 data.resize(pos - incomplete_samples_bytes); 163 data.resize(pos - incomplete_samples_bytes);
160 164
161 if (callback_.is_null())
162 return;
163
164 if (IsPacketOfSilence(data)) 165 if (IsPacketOfSilence(data))
165 return; 166 return;
166 167
168 scoped_refptr<base::RefCountedString> data_ref =
Wez 2012/10/30 04:20:11 nit: Add a comment e.g. "Dispatch asynchronous not
Sergey Ulanov 2012/10/30 23:59:48 Done.
169 base::RefCountedString::TakeString(&data);
170 capturers_->Notify(&AudioCapturerLinux::OnDataRead, data_ref);
171 }
172
173 void AudioCapturerLinuxCore::WaitForPipeReadable() {
174 timer_.Stop();
175 MessageLoopForIO::current()->WatchFileDescriptor(
176 pipe_fd_, false, MessageLoopForIO::WATCH_READ,
177 &file_descriptor_watcher_, this);
178 }
179
180 AudioCapturerLinux::AudioCapturerLinux(
181 scoped_refptr<AudioCapturerLinuxCore> core)
182 : core_(core) {
183 }
184
185 AudioCapturerLinux::~AudioCapturerLinux() {
186 }
187
188 bool AudioCapturerLinux::Start(const PacketCapturedCallback& callback) {
189 callback_ = callback;
190 core_->AddCapturer(this);
Wez 2012/10/30 04:20:11 nit: Use |g_audio_capturer_core| directly here?
Sergey Ulanov 2012/10/30 23:59:48 See my other comments. I'd like to avoid usage of
191 return true;
192 }
193
194 void AudioCapturerLinux::Stop() {
195 core_->RemoveCapturer(this);
196 callback_.Reset();
197 }
198
199 bool AudioCapturerLinux::IsStarted() {
200 return !callback_.is_null();
201 }
202
203 void AudioCapturerLinux::OnDataRead(
204 scoped_refptr<base::RefCountedString> data) {
205 DCHECK(!callback_.is_null());
206
167 scoped_ptr<AudioPacket> packet(new AudioPacket()); 207 scoped_ptr<AudioPacket> packet(new AudioPacket());
168 packet->add_data(data); 208 packet->add_data(data->data());
169 packet->set_encoding(AudioPacket::ENCODING_RAW); 209 packet->set_encoding(AudioPacket::ENCODING_RAW);
170 packet->set_sampling_rate(kSamplingRate); 210 packet->set_sampling_rate(kSamplingRate);
171 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); 211 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
172 packet->set_channels(AudioPacket::CHANNELS_STEREO); 212 packet->set_channels(AudioPacket::CHANNELS_STEREO);
173 callback_.Run(packet.Pass()); 213 callback_.Run(packet.Pass());
174 } 214 }
175 215
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() { 216 bool AudioCapturer::IsSupported() {
188 return !g_audio_pipe_name.Get().empty(); 217 return g_audio_capturer_core.Get() != NULL;
189 } 218 }
190 219
191 scoped_ptr<AudioCapturer> AudioCapturer::Create() { 220 scoped_ptr<AudioCapturer> AudioCapturer::Create() {
192 FilePath path = g_audio_pipe_name.Get(); 221 scoped_refptr<AudioCapturerLinuxCore> core = g_audio_capturer_core.Get();
193 if (path.empty()) 222 if (!core)
194 return scoped_ptr<AudioCapturer>(); 223 return scoped_ptr<AudioCapturer>();
195 return scoped_ptr<AudioCapturer>(new AudioCapturerLinux(path)); 224 return scoped_ptr<AudioCapturer>(new AudioCapturerLinux(core));
196 } 225 }
197 226
198 } // namespace remoting 227 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698