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

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
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 } // 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();
Wez 2012/09/06 21:47:29 Why do this here, rather than in Start?
Sergey Ulanov 2012/09/06 23:18:53 Capturer is started only when a user is connected,
Wez 2012/09/06 23:32:39 Do we actually need to do that, given that there w
Sergey Ulanov 2012/09/07 00:22:03 Yes, we do need to do it. Otherwise we may break s
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) {
82 NOTREACHED();
83 }
84
85 void AudioCapturerLinux::StartTimer() {
86 started_time_ = base::TimeTicks::Now();
87 last_capture_time_samples_ = 0;
88 timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds(kCapturingPeriodMs),
89 this, &AudioCapturerLinux::DoCapture);
90 }
91
92 void AudioCapturerLinux::DoCapture() {
93 DCHECK_GT(pipe_fd_, 0);
94
95 base::TimeDelta stream_position = base::TimeTicks::Now() - started_time_;
Wez 2012/09/06 21:47:29 Add a comment explaining what this calculation is
Sergey Ulanov 2012/09/06 23:18:53 Done.
96 int64 stream_position_samples = stream_position.InMilliseconds() *
97 kSamplingRate / base::Time::kMillisecondsPerSecond;
98 int64 samples_to_capture =
99 stream_position_samples - last_capture_time_samples_;
100 last_capture_time_samples_ = stream_position_samples;
101 int64 read_size =
102 samples_to_capture * kChannels * kBytesPerSample;
103
104 std::string data;
105 data.resize(read_size);
106 int pos = 0;
107 while (pos < read_size) {
108 int read_result = HANDLE_EINTR(
109 read(pipe_fd_, string_as_array(&data) + pos, read_size - pos));
110 if (read_result >= 0) {
111 pos += read_result;
112 } else {
113 if (errno != EWOULDBLOCK && errno != EAGAIN)
114 PLOG(ERROR) << "read";
115 break;
116 }
117 }
118
119 if (pos == 0) {
120 Sleep();
121 return;
122 }
123
124 if (!callback_.is_null()) {
125 data.resize(pos);
126
127 scoped_ptr<AudioPacket> packet(new AudioPacket());
128 packet->add_data(data);
129 packet->set_encoding(AudioPacket::ENCODING_RAW);
130 packet->set_sampling_rate(kSamplingRate);
131 packet->set_bytes_per_sample(AudioPacket::BYTES_PER_SAMPLE_2);
132 packet->set_channels(AudioPacket::CHANNELS_STEREO);
133 callback_.Run(packet.Pass());
134 }
135 }
136
137 void AudioCapturerLinux::Sleep() {
Wez 2012/09/06 21:47:29 Sleep -> e.g. WaitForDataReady / WaitForPipe / Wai
Sergey Ulanov 2012/09/06 23:18:53 Done.
138 timer_.Stop();
139 MessageLoopForIO::current()->WatchFileDescriptor(
140 pipe_fd_, false, MessageLoopForIO::WATCH_READ,
141 &file_descriptor_watcher_, this);
142 }
143
10 scoped_ptr<AudioCapturer> AudioCapturer::Create() { 144 scoped_ptr<AudioCapturer> AudioCapturer::Create() {
11 NOTIMPLEMENTED(); 145 CommandLine* cl = CommandLine::ForCurrentProcess();
12 return scoped_ptr<AudioCapturer>(NULL); 146 FilePath path = cl->GetSwitchValuePath(kAudioPipeOptionName);
147 if (path.empty())
148 return scoped_ptr<AudioCapturer>();
Wez 2012/09/06 21:47:29 You'll need to update this to provide a SupportsAu
Sergey Ulanov 2012/09/06 23:18:53 Done.
149 return scoped_ptr<AudioCapturer>(new AudioCapturerLinux(path));
13 } 150 }
14 151
15 } // namespace remoting 152 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698