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

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

Issue 10907041: Implement Linux audio capturer. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 3 months 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/audio_capturer_linux.h ('k') | remoting/host/audio_capturer_mac.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"
6
7 #include <fcntl.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11
12 #include "base/command_line.h"
13 #include "base/eintr_wrapper.h"
14 #include "base/file_path.h"
5 #include "base/logging.h" 15 #include "base/logging.h"
6 #include "remoting/host/audio_capturer.h" 16 #include "base/stl_util.h"
17 #include "remoting/proto/audio.pb.h"
7 18
8 namespace remoting { 19 namespace remoting {
9 20
21 namespace {
22
23 const char kAudioPipeOptionName[] = "audio-pipe-name";
24
25 // PulseAudio's module-pipe-sink must be configured to use the following
26 // parameters for the sink we read from.
27 const AudioPacket_SamplingRate kSamplingRate = AudioPacket::SAMPLING_RATE_44100;
28 const int kChannels = 2;
29 const int kBytesPerSample = 2;
30
31 // Read data from the pipe every 40ms.
32 const int kCapturingPeriodMs = 40;
33
34 #if !defined(F_SETPIPE_SZ)
35 // F_SETPIPE_SZ is supported only starting linux 2.6.35, but we want to be able
36 // to compile this code on machines with older kernel.
37 #define F_SETPIPE_SZ 1031
38 #endif // defined(F_SETPIPE_SZ)
39
40 } // namespace
41
42 AudioCapturerLinux::AudioCapturerLinux(const FilePath& pipe_name) {
43 pipe_fd_ = HANDLE_EINTR(open(
44 pipe_name.value().c_str(), O_RDONLY | O_NONBLOCK));
45 if (pipe_fd_ < 0) {
46 LOG(ERROR) << "Failed to open " << pipe_name.value();
47 return;
48 }
49
50 // Set buffer size for the pipe to the double of what's required for samples
51 // of each capturing period.
52 int pipe_buffer_size = 2 * kCapturingPeriodMs * kSamplingRate * kChannels *
53 kBytesPerSample / base::Time::kMillisecondsPerSecond;
54 int result = HANDLE_EINTR(fcntl(pipe_fd_, F_SETPIPE_SZ, pipe_buffer_size));
55 if (result < 0) {
56 PLOG(ERROR) << "fcntl";
57 }
58
59 WaitForPipeReadable();
60 }
61
62 AudioCapturerLinux::~AudioCapturerLinux() {
63 }
64
65 bool AudioCapturerLinux::Start(const PacketCapturedCallback& callback) {
66 if (pipe_fd_ < 0)
67 return false;
68
69 callback_ = callback;
70
71 return true;
72 }
73
74 void AudioCapturerLinux::Stop() {
75 callback_.Reset();
76 }
77
78 bool AudioCapturerLinux::IsStarted() {
79 return !callback_.is_null();
80 }
81
82 void AudioCapturerLinux::OnFileCanReadWithoutBlocking(int fd) {
83 DCHECK_EQ(fd, pipe_fd_);
84 StartTimer();
85 }
86
87 void AudioCapturerLinux::OnFileCanWriteWithoutBlocking(int fd) {
88 NOTREACHED();
89 }
90
91 void AudioCapturerLinux::StartTimer() {
92 started_time_ = base::TimeTicks::Now();
93 last_capture_samples_ = 0;
94 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCapturingPeriodMs),
95 this, &AudioCapturerLinux::DoCapture);
96 }
97
98 void AudioCapturerLinux::DoCapture() {
99 DCHECK_GT(pipe_fd_, 0);
100
101 // Calculate how much we need read from the pipe. Pulseaudio doesn't control
102 // how much data it writes to the pipe, so we need to pace the stream, so
103 // that we read the exact number of the samples per second we need.
104 base::TimeDelta stream_position = base::TimeTicks::Now() - started_time_;
105 int64 stream_position_samples = stream_position.InMilliseconds() *
106 kSamplingRate / base::Time::kMillisecondsPerSecond;
107 int64 samples_to_capture =
108 stream_position_samples - last_capture_samples_;
109 last_capture_samples_ = stream_position_samples;
110 int64 read_size =
111 samples_to_capture * kChannels * kBytesPerSample;
112
113 std::string data = left_over_bytes_;
114 int pos = data.size();
115 left_over_bytes_.clear();
116 data.resize(read_size);
117
118 while (pos < read_size) {
119 int read_result = HANDLE_EINTR(
120 read(pipe_fd_, string_as_array(&data) + pos, read_size - pos));
121 if (read_result >= 0) {
122 pos += read_result;
123 } else {
124 if (errno != EWOULDBLOCK && errno != EAGAIN)
125 PLOG(ERROR) << "read";
126 break;
127 }
128 }
129
130 if (pos == 0) {
131 WaitForPipeReadable();
132 return;
133 }
134
135 // Save any incomplete samples we've read for later. Each packet should
136 // contain integer number of samples.
137 int incomplete_samples_bytes = pos % (kChannels * kBytesPerSample);
138 left_over_bytes_.assign(data, pos - incomplete_samples_bytes,
139 incomplete_samples_bytes);
140 data.resize(pos - incomplete_samples_bytes);
141
142 if (!callback_.is_null()) {
143 scoped_ptr<AudioPacket> packet(new AudioPacket());
144 packet->add_data(data);
145 packet->set_encoding(AudioPacket::ENCODING_RAW);
146 packet->set_sampling_rate(kSamplingRate);
147 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
148 packet->set_channels(AudioPacket::CHANNELS_STEREO);
149 callback_.Run(packet.Pass());
150 }
151 }
152
153 void AudioCapturerLinux::WaitForPipeReadable() {
154 timer_.Stop();
155 MessageLoopForIO::current()->WatchFileDescriptor(
156 pipe_fd_, false, MessageLoopForIO::WATCH_READ,
157 &file_descriptor_watcher_, this);
158 }
159
160 bool AudioCapturer::IsSupported() {
161 CommandLine* cl = CommandLine::ForCurrentProcess();
162 return !cl->GetSwitchValuePath(kAudioPipeOptionName).empty();
163 }
164
10 scoped_ptr<AudioCapturer> AudioCapturer::Create() { 165 scoped_ptr<AudioCapturer> AudioCapturer::Create() {
11 NOTIMPLEMENTED(); 166 CommandLine* cl = CommandLine::ForCurrentProcess();
12 return scoped_ptr<AudioCapturer>(NULL); 167 FilePath path = cl->GetSwitchValuePath(kAudioPipeOptionName);
168 DCHECK(!path.empty());
169 return scoped_ptr<AudioCapturer>(new AudioCapturerLinux(path));
13 } 170 }
14 171
15 } // namespace remoting 172 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/audio_capturer_linux.h ('k') | remoting/host/audio_capturer_mac.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698