OLD | NEW |
---|---|
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 20ms. | |
32 const int kCapturingPeriodMs = 20; | |
33 | |
34 } // namespace | |
35 | |
36 AudioCapturerLinux::AudioCapturerLinux(const FilePath& pipe_name) { | |
37 pipe_fd_ = HANDLE_EINTR(open( | |
38 pipe_name.value().c_str(), O_RDONLY | O_NONBLOCK)); | |
39 if (pipe_fd_ < 0) { | |
40 LOG(ERROR) << "Failed to open " << pipe_name.value(); | |
41 return; | |
42 } | |
43 | |
44 // Set buffer size for the pipe to the double of what's required for samples | |
45 // of each capturing period. | |
46 int pipe_buffer_size = 2 * kCapturingPeriodMs * kSamplingRate * kChannels * | |
47 kBytesPerSample / base::Time::kMillisecondsPerSecond; | |
48 int result = HANDLE_EINTR(fcntl(pipe_fd_, F_SETPIPE_SZ, pipe_buffer_size)); | |
49 if (result < 0) { | |
50 PLOG(ERROR) << "fcntl"; | |
51 } | |
52 | |
53 Sleep(); | |
54 } | |
55 | |
56 AudioCapturerLinux::~AudioCapturerLinux() { | |
57 } | |
58 | |
59 bool AudioCapturerLinux::Start(const PacketCapturedCallback& callback) { | |
60 if (pipe_fd_ < 0) | |
61 return false; | |
62 | |
63 callback_ = callback; | |
64 | |
65 return true; | |
66 } | |
67 | |
68 void AudioCapturerLinux::Stop() { | |
69 callback_.Reset(); | |
70 } | |
71 | |
72 bool AudioCapturerLinux::IsRunning() { | |
73 return !callback_.is_null(); | |
74 } | |
75 | |
76 void AudioCapturerLinux::OnFileCanReadWithoutBlocking(int fd) { | |
77 DCHECK_EQ(fd, pipe_fd_); | |
78 StartTimer(); | |
79 } | |
80 | |
81 void AudioCapturerLinux::OnFileCanWriteWithoutBlocking(int fd) { | |
Wez
2012/09/04 21:41:45
nit: NOTREACHED()?
Sergey Ulanov
2012/09/05 01:35:49
Done.
| |
82 } | |
83 | |
84 void AudioCapturerLinux::StartTimer() { | |
85 started_time_ = base::TimeTicks::Now(); | |
86 last_capture_time_samples_ = 0; | |
87 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCapturingPeriodMs), | |
88 this, &AudioCapturerLinux::DoCapture); | |
89 } | |
90 | |
91 void AudioCapturerLinux::DoCapture() { | |
92 DCHECK_GT(pipe_fd_, 0); | |
93 | |
94 base::TimeDelta stream_position = base::TimeTicks::Now() - started_time_; | |
95 int64 stream_position_samples = stream_position.InMilliseconds() * | |
96 kSamplingRate / base::Time::kMillisecondsPerSecond; | |
97 int64 samples_to_capture = | |
98 stream_position_samples - last_capture_time_samples_; | |
Wez
2012/09/04 21:41:46
Do you need these calculations, and timers going o
Sergey Ulanov
2012/09/05 01:35:49
No. PulseAudio's pipe sink plugin doesn't control
| |
99 last_capture_time_samples_ = stream_position_samples; | |
100 int64 read_size = | |
101 samples_to_capture * kChannels * kBytesPerSample; | |
102 | |
103 std::string data; | |
104 data.resize(read_size); | |
105 int pos = 0; | |
106 while (pos < read_size) { | |
107 int read_result = HANDLE_EINTR( | |
108 read(pipe_fd_, string_as_array(&data) + pos, read_size - pos)); | |
109 if (read_result >= 0) { | |
110 pos += read_result; | |
111 } else { | |
112 if (read_result != EWOULDBLOCK && read_result != EAGAIN) | |
113 PLOG(ERROR) << "read"; | |
114 break; | |
115 } | |
116 } | |
117 | |
118 if (pos == 0) { | |
119 Sleep(); | |
120 return; | |
121 } | |
122 | |
123 if (!callback_.is_null()) { | |
124 data.resize(pos); | |
125 | |
126 scoped_ptr<AudioPacket> packet(new AudioPacket()); | |
127 packet->add_data(data); | |
128 packet->set_encoding(AudioPacket::ENCODING_RAW); | |
129 packet->set_sampling_rate(kSamplingRate); | |
130 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2); | |
131 packet->set_channels(AudioPacket::CHANNELS_STEREO); | |
132 callback_.Run(packet.Pass()); | |
133 } | |
134 } | |
135 | |
136 void AudioCapturerLinux::Sleep() { | |
137 timer_.Stop(); | |
138 MessageLoopForIO::current()->WatchFileDescriptor( | |
139 pipe_fd_, false, MessageLoopForIO::WATCH_READ, | |
140 &file_descriptor_watcher_, this); | |
141 } | |
142 | |
10 scoped_ptr<AudioCapturer> AudioCapturer::Create() { | 143 scoped_ptr<AudioCapturer> AudioCapturer::Create() { |
11 NOTIMPLEMENTED(); | 144 CommandLine* cl = CommandLine::ForCurrentProcess(); |
12 return scoped_ptr<AudioCapturer>(NULL); | 145 FilePath path = cl->GetSwitchValuePath(kAudioPipeOptionName); |
146 if (path.empty()) | |
147 return scoped_ptr<AudioCapturer>(); | |
148 return scoped_ptr<AudioCapturer>(new AudioCapturerLinux(path)); | |
13 } | 149 } |
14 | 150 |
15 } // namespace remoting | 151 } // namespace remoting |
OLD | NEW |