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

Side by Side Diff: remoting/host/linux/audio_pipe_reader.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
« no previous file with comments | « remoting/host/linux/audio_pipe_reader.h ('k') | remoting/host/remoting_me2me_host.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/linux/audio_pipe_reader.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>
11 11
12 #include "base/eintr_wrapper.h" 12 #include "base/eintr_wrapper.h"
13 #include "base/file_path.h" 13 #include "base/file_path.h"
14 #include "base/lazy_instance.h"
15 #include "base/logging.h" 14 #include "base/logging.h"
16 #include "base/stl_util.h" 15 #include "base/stl_util.h"
17 #include "remoting/proto/audio.pb.h"
18 #include "remoting/host/chromoting_host_context.h"
19 16
20 namespace remoting { 17 namespace remoting {
21 18
22 namespace { 19 namespace {
23 20
24 // PulseAudio's module-pipe-sink must be configured to use the following 21 // PulseAudio's module-pipe-sink must be configured to use the following
25 // parameters for the sink we read from. 22 // parameters for the sink we read from.
26 const AudioPacket_SamplingRate kSamplingRate = AudioPacket::SAMPLING_RATE_44100; 23 const int kSamplingRate = 48000;
27 const int kChannels = 2; 24 const int kChannels = 2;
28 const int kBytesPerSample = 2; 25 const int kBytesPerSample = 2;
29 26
30 // Read data from the pipe every 40ms. 27 // Read data from the pipe every 40ms.
31 const int kCapturingPeriodMs = 40; 28 const int kCapturingPeriodMs = 40;
32 29
33 #if !defined(F_SETPIPE_SZ) 30 #if !defined(F_SETPIPE_SZ)
34 // F_SETPIPE_SZ is supported only starting linux 2.6.35, but we want to be able 31 // 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. 32 // to compile this code on machines with older kernel.
36 #define F_SETPIPE_SZ 1031 33 #define F_SETPIPE_SZ 1031
37 #endif // defined(F_SETPIPE_SZ) 34 #endif // defined(F_SETPIPE_SZ)
38 35
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) { 36 const int IsPacketOfSilence(const std::string& data) {
46 const int64* int_buf = reinterpret_cast<const int64*>(data.data()); 37 const int64* int_buf = reinterpret_cast<const int64*>(data.data());
47 for (size_t i = 0; i < data.size() / sizeof(int64); i++) { 38 for (size_t i = 0; i < data.size() / sizeof(int64); i++) {
48 if (int_buf[i] != 0) 39 if (int_buf[i] != 0)
49 return false; 40 return false;
50 } 41 }
51 for (size_t i = data.size() - data.size() % sizeof(int64); 42 for (size_t i = data.size() - data.size() % sizeof(int64);
52 i < data.size(); i++) { 43 i < data.size(); i++) {
53 if (data.data()[i] != 0) 44 if (data.data()[i] != 0)
54 return false; 45 return false;
55 } 46 }
56 return true; 47 return true;
57 } 48 }
58 49
59 } // namespace 50 } // namespace
60 51
61 AudioCapturerLinux::AudioCapturerLinux(const FilePath& pipe_name) { 52 AudioPipeReader::AudioPipeReader(
53 scoped_refptr<base::SingleThreadTaskRunner> task_runner,
54 const FilePath& pipe_name)
55 : task_runner_(task_runner),
56 observers_(new ObserverListThreadSafe<StreamObserver>()) {
57 task_runner_->PostTask(FROM_HERE, base::Bind(
58 &AudioPipeReader::StartOnAudioThread, this, pipe_name));
59 }
60
61 void AudioPipeReader::StartOnAudioThread(const FilePath& pipe_name) {
62 DCHECK(task_runner_->BelongsToCurrentThread());
63
62 pipe_fd_ = HANDLE_EINTR(open( 64 pipe_fd_ = HANDLE_EINTR(open(
63 pipe_name.value().c_str(), O_RDONLY | O_NONBLOCK)); 65 pipe_name.value().c_str(), O_RDONLY | O_NONBLOCK));
64 if (pipe_fd_ < 0) { 66 if (pipe_fd_ < 0) {
65 LOG(ERROR) << "Failed to open " << pipe_name.value(); 67 LOG(ERROR) << "Failed to open " << pipe_name.value();
66 return; 68 return;
67 } 69 }
68 70
69 // Set buffer size for the pipe to the double of what's required for samples 71 // Set buffer size for the pipe to the double of what's required for samples
70 // of each capturing period. 72 // of each capturing period.
71 int pipe_buffer_size = 2 * kCapturingPeriodMs * kSamplingRate * kChannels * 73 int pipe_buffer_size = 2 * kCapturingPeriodMs * kSamplingRate * kChannels *
72 kBytesPerSample / base::Time::kMillisecondsPerSecond; 74 kBytesPerSample / base::Time::kMillisecondsPerSecond;
73 int result = HANDLE_EINTR(fcntl(pipe_fd_, F_SETPIPE_SZ, pipe_buffer_size)); 75 int result = HANDLE_EINTR(fcntl(pipe_fd_, F_SETPIPE_SZ, pipe_buffer_size));
74 if (result < 0) { 76 if (result < 0) {
75 PLOG(ERROR) << "fcntl"; 77 PLOG(ERROR) << "fcntl";
76 } 78 }
77 79
78 WaitForPipeReadable(); 80 WaitForPipeReadable();
79 } 81 }
80 82
81 AudioCapturerLinux::~AudioCapturerLinux() { 83 AudioPipeReader::~AudioPipeReader() {
82 } 84 }
83 85
84 bool AudioCapturerLinux::Start(const PacketCapturedCallback& callback) { 86 void AudioPipeReader::AddObserver(StreamObserver* observer) {
85 if (pipe_fd_ < 0) 87 observers_->AddObserver(observer);
86 return false; 88 }
87 89 void AudioPipeReader::RemoveObserver(StreamObserver* observer) {
88 callback_ = callback; 90 observers_->RemoveObserver(observer);
89
90 return true;
91 } 91 }
92 92
93 void AudioCapturerLinux::Stop() { 93 void AudioPipeReader::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_); 94 DCHECK_EQ(fd, pipe_fd_);
103 StartTimer(); 95 StartTimer();
104 } 96 }
105 97
106 void AudioCapturerLinux::OnFileCanWriteWithoutBlocking(int fd) { 98 void AudioPipeReader::OnFileCanWriteWithoutBlocking(int fd) {
107 NOTREACHED(); 99 NOTREACHED();
108 } 100 }
109 101
110 void AudioCapturerLinux::StartTimer() { 102 void AudioPipeReader::StartTimer() {
103 DCHECK(task_runner_->BelongsToCurrentThread());
111 started_time_ = base::TimeTicks::Now(); 104 started_time_ = base::TimeTicks::Now();
112 last_capture_samples_ = 0; 105 last_capture_samples_ = 0;
113 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCapturingPeriodMs), 106 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCapturingPeriodMs),
114 this, &AudioCapturerLinux::DoCapture); 107 this, &AudioPipeReader::DoCapture);
115 } 108 }
116 109
117 void AudioCapturerLinux::DoCapture() { 110 void AudioPipeReader::DoCapture() {
111 DCHECK(task_runner_->BelongsToCurrentThread());
118 DCHECK_GT(pipe_fd_, 0); 112 DCHECK_GT(pipe_fd_, 0);
119 113
120 // Calculate how much we need read from the pipe. Pulseaudio doesn't control 114 // 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 115 // 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. 116 // that we read the exact number of the samples per second we need.
123 base::TimeDelta stream_position = base::TimeTicks::Now() - started_time_; 117 base::TimeDelta stream_position = base::TimeTicks::Now() - started_time_;
124 int64 stream_position_samples = stream_position.InMilliseconds() * 118 int64 stream_position_samples = stream_position.InMilliseconds() *
125 kSamplingRate / base::Time::kMillisecondsPerSecond; 119 kSamplingRate / base::Time::kMillisecondsPerSecond;
126 int64 samples_to_capture = 120 int64 samples_to_capture =
127 stream_position_samples - last_capture_samples_; 121 stream_position_samples - last_capture_samples_;
(...skipping 23 matching lines...) Expand all
151 return; 145 return;
152 } 146 }
153 147
154 // Save any incomplete samples we've read for later. Each packet should 148 // Save any incomplete samples we've read for later. Each packet should
155 // contain integer number of samples. 149 // contain integer number of samples.
156 int incomplete_samples_bytes = pos % (kChannels * kBytesPerSample); 150 int incomplete_samples_bytes = pos % (kChannels * kBytesPerSample);
157 left_over_bytes_.assign(data, pos - incomplete_samples_bytes, 151 left_over_bytes_.assign(data, pos - incomplete_samples_bytes,
158 incomplete_samples_bytes); 152 incomplete_samples_bytes);
159 data.resize(pos - incomplete_samples_bytes); 153 data.resize(pos - incomplete_samples_bytes);
160 154
161 if (callback_.is_null())
162 return;
163
164 if (IsPacketOfSilence(data)) 155 if (IsPacketOfSilence(data))
165 return; 156 return;
166 157
167 scoped_ptr<AudioPacket> packet(new AudioPacket()); 158 // Dispatch asynchronous notification to the stream observers.
168 packet->add_data(data); 159 scoped_refptr<base::RefCountedString> data_ref =
169 packet->set_encoding(AudioPacket::ENCODING_RAW); 160 base::RefCountedString::TakeString(&data);
170 packet->set_sampling_rate(kSamplingRate); 161 observers_->Notify(&StreamObserver::OnDataRead, data_ref);
171 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
172 packet->set_channels(AudioPacket::CHANNELS_STEREO);
173 callback_.Run(packet.Pass());
174 } 162 }
175 163
176 void AudioCapturerLinux::WaitForPipeReadable() { 164 void AudioPipeReader::WaitForPipeReadable() {
177 timer_.Stop(); 165 timer_.Stop();
178 MessageLoopForIO::current()->WatchFileDescriptor( 166 MessageLoopForIO::current()->WatchFileDescriptor(
179 pipe_fd_, false, MessageLoopForIO::WATCH_READ, 167 pipe_fd_, false, MessageLoopForIO::WATCH_READ,
180 &file_descriptor_watcher_, this); 168 &file_descriptor_watcher_, this);
181 } 169 }
182 170
183 void AudioCapturerLinux::SetPipeName(const FilePath& pipe_name) {
184 g_audio_pipe_name.Get() = pipe_name;
185 }
186
187 bool AudioCapturer::IsSupported() {
188 return !g_audio_pipe_name.Get().empty();
189 }
190
191 scoped_ptr<AudioCapturer> AudioCapturer::Create() {
192 FilePath path = g_audio_pipe_name.Get();
193 if (path.empty())
194 return scoped_ptr<AudioCapturer>();
195 return scoped_ptr<AudioCapturer>(new AudioCapturerLinux(path));
196 }
197
198 } // namespace remoting 171 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/linux/audio_pipe_reader.h ('k') | remoting/host/remoting_me2me_host.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698