Chromium Code Reviews

Side by Side Diff: media/audio/pulse/audio_manager_pulse.cc

Issue 10952024: Adding pulseaudio input support to chrome (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: moved the pulse code to AudioManagerPulse, and addressed Dale's comments. Created 7 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View unified diff | | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "media/audio/pulse/audio_manager_pulse.h"
6
7 #include "base/command_line.h"
8 #include "base/environment.h"
9 #include "base/logging.h"
10 #include "base/nix/xdg_util.h"
11 #include "base/process_util.h"
12 #include "base/stl_util.h"
13 #include "media/audio/audio_util.h"
14 #include "media/audio/pulse/pulse_input.h"
15 #include "media/audio/pulse/pulse_output.h"
16 #include "media/audio/pulse/pulse_util.h"
17
18 namespace media {
19
20 namespace {
21
22 // Maximum number of output streams that can be open simultaneously.
23 static const int kMaxOutputStreams = 50;
24
25 } // namespace
26
27 // static
28 AudioManager* AudioManagerPulse::Create() {
29 scoped_ptr<AudioManagerPulse> ret(new AudioManagerPulse());
30 if (ret->Init())
31 return ret.release();
32
33 DVLOG(1) << "PulseAudio is not available on the OS";
34 return NULL;
35 }
36
37 AudioManagerPulse::AudioManagerPulse()
38 : input_mainloop_(NULL),
39 input_context_(NULL),
40 devices_(NULL),
41 native_input_sample_rate_(0) {
42 SetMaxOutputStreamsAllowed(kMaxOutputStreams);
43 }
44
45 AudioManagerPulse::~AudioManagerPulse() {
46 Terminate();
47 Shutdown();
48 }
49
50 // Implementation of AudioManager.
51 bool AudioManagerPulse::HasAudioOutputDevices() {
52 // TODO(xians): implement this function.
53 return true;
54 }
55
56 bool AudioManagerPulse::HasAudioInputDevices() {
57 // TODO(xians): implement this function.
58 return true;
59 }
60
61 bool AudioManagerPulse::CanShowAudioInputSettings() {
62 scoped_ptr<base::Environment> env(base::Environment::Create());
63
64 switch (base::nix::GetDesktopEnvironment(env.get())) {
65 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
66 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
67 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
68 case base::nix::DESKTOP_ENVIRONMENT_UNITY:
69 return true;
70 case base::nix::DESKTOP_ENVIRONMENT_OTHER:
71 case base::nix::DESKTOP_ENVIRONMENT_XFCE:
72 return false;
73 }
74 // Unless GetDesktopEnvironment() badly misbehaves, this should never happen.
75 NOTREACHED();
76 return false;
77 }
78
79 void AudioManagerPulse::ShowAudioInputSettings() {
80 scoped_ptr<base::Environment> env(base::Environment::Create());
81 CommandLine command_line(CommandLine::NO_PROGRAM);
82 switch (base::nix::GetDesktopEnvironment(env.get())) {
83 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
84 command_line.SetProgram(base::FilePath("gnome-volume-control"));
85 break;
86 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
87 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
88 command_line.SetProgram(base::FilePath("kmix"));
89 break;
90 case base::nix::DESKTOP_ENVIRONMENT_UNITY:
91 command_line.SetProgram(base::FilePath("gnome-control-center"));
92 command_line.AppendArg("sound");
93 command_line.AppendArg("input");
94 break;
95 default:
96 LOG(ERROR) << "Failed to show audio input settings: we don't know "
97 << "what command to use for your desktop environment.";
98 return;
99 }
100 base::LaunchProcess(command_line, base::LaunchOptions(), NULL);
101 }
102
103 void AudioManagerPulse::GetAudioInputDeviceNames(
104 media::AudioDeviceNames* device_names) {
105 DCHECK(device_names->empty());
106 DCHECK(input_mainloop_);
107 DCHECK(input_context_);
108 devices_ = device_names;
109 AutoPulseLock auto_lock(input_mainloop_);
110 pa_operation* operation = pa_context_get_source_info_list(
111 input_context_, DevicesInfoCallback, this);
112 WaitForOperationCompletion(input_mainloop_, operation);
113
114 if (!device_names->empty()) {
115 device_names->push_front(
116 AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
117 AudioManagerBase::kDefaultDeviceId));
118 }
119 }
120
121 AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream(
122 const AudioParameters& params) {
123 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
124 return MakeOutputStream(params);
125 }
126
127 AudioOutputStream* AudioManagerPulse::MakeLowLatencyOutputStream(
128 const AudioParameters& params) {
129 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
130 return MakeOutputStream(params);
131 }
132
133 AudioInputStream* AudioManagerPulse::MakeLinearInputStream(
134 const AudioParameters& params, const std::string& device_id) {
135 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
136 return MakeInputStream(params, device_id);
137 }
138
139 AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream(
140 const AudioParameters& params, const std::string& device_id) {
141 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
142 return MakeInputStream(params, device_id);
143 }
144
145 AudioOutputStream* AudioManagerPulse::MakeOutputStream(
146 const AudioParameters& params) {
147 return new PulseAudioOutputStream(params, this);
148 }
149
150 AudioInputStream* AudioManagerPulse::MakeInputStream(
151 const AudioParameters& params, const std::string& device_id) {
152 return new PulseAudioInputStream(this, device_id, params,
153 input_mainloop_, input_context_);
154 }
155
156 AudioParameters AudioManagerPulse::GetPreferredLowLatencyOutputStreamParameters(
157 const AudioParameters& input_params) {
158 // Pulse allows clients to try a smaller buffer size rather than the default
159 // buffer size.
160 int buffer_size = GetAudioHardwareBufferSize();
161 if (input_params.frames_per_buffer() < buffer_size)
162 buffer_size = input_params.frames_per_buffer();
163
164 // TODO(dalecurtis): This should include bits per channel and channel layout
165 // eventually.
166 return AudioParameters(
167 AudioParameters::AUDIO_PCM_LOW_LATENCY, input_params.channel_layout(),
168 input_params.sample_rate(), 16, buffer_size);
169 }
170
171 int AudioManagerPulse::GetNativeSampleRate() {
172 DCHECK(input_mainloop_);
173 DCHECK(input_context_);
174 AutoPulseLock auto_lock(input_mainloop_);
175 pa_operation* operation = pa_context_get_server_info(
176 input_context_, SamplerateInfoCallback, this);
177 WaitForOperationCompletion(input_mainloop_, operation);
178
179 return native_input_sample_rate_;
180 }
181
182 bool AudioManagerPulse::Init() {
183 DCHECK(!input_mainloop_);
184
185 // Create a mainloop API and connect to the default server.
186 // The mainloop is the internal asynchronous API event loop.
187 input_mainloop_ = pa_threaded_mainloop_new();
188 if (!input_mainloop_)
189 return false;
190
191 // Start the threaded mainloop.
192 if (pa_threaded_mainloop_start(input_mainloop_)) {
193 Terminate();
194 return false;
195 }
196
197 // Lock the event loop object, effectively blocking the event loop thread
198 // from processing events. This is necessary.
199 AutoPulseLock auto_lock(input_mainloop_);
200
201 pa_mainloop_api* pa_mainloop_api =
202 pa_threaded_mainloop_get_api(input_mainloop_);
203 input_context_ = pa_context_new(pa_mainloop_api, "Chrome input");
204 DCHECK(input_context_) << "Failed to create PA context";
205 if (!input_context_) {
206 return false;
207 }
208
209 pa_context_set_state_callback(input_context_, &ContextStateCallback,
210 input_mainloop_);
211 if (pa_context_connect(input_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL)) {
212 DLOG(ERROR) << "Failed to connect to the context";
213 return false;
214 }
215
216 // Wait until |input_context_| is ready. pa_threaded_mainloop_wait() must be
217 // called after pa_context_get_state() in case the context is already ready,
218 // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
219 while (true) {
220 pa_context_state_t context_state = pa_context_get_state(input_context_);
221 if (!PA_CONTEXT_IS_GOOD(context_state)) {
222 return false;
223 }
224 if (context_state == PA_CONTEXT_READY)
225 break;
226 pa_threaded_mainloop_wait(input_mainloop_);
227 }
228
229 return true;
230 }
231
232 void AudioManagerPulse::Terminate() {
233 if (!input_mainloop_) {
234 DCHECK(!input_context_);
235 return;
236 }
237
238 {
239 AutoPulseLock auto_lock(input_mainloop_);
240 if (input_context_) {
241 // Clear our state callback.
242 pa_context_set_state_callback(input_context_, NULL, NULL);
243 pa_context_disconnect(input_context_);
244 pa_context_unref(input_context_);
245 input_context_ = NULL;
246 }
247 }
248
249 pa_threaded_mainloop_stop(input_mainloop_);
250 pa_threaded_mainloop_free(input_mainloop_);
251 input_mainloop_ = NULL;
252 }
253
254 void AudioManagerPulse::DevicesInfoCallback(pa_context* context,
255 const pa_source_info* info,
256 int error, void *user_data) {
257 AudioManagerPulse* pulse = reinterpret_cast<AudioManagerPulse*>(user_data);
258
259 if (error) {
260 // Signal the pulse object that it is done.
261 pa_threaded_mainloop_signal(pulse->input_mainloop_, 0);
262 return;
263 }
264
265 // Exclude the output devices.
266 if (info->monitor_of_sink == PA_INVALID_INDEX) {
267 pulse->devices_->push_back(media::AudioDeviceName(info->description,
268 info->name));
269 }
270 }
271
272 void AudioManagerPulse::SamplerateInfoCallback(pa_context* context,
273 const pa_server_info* info,
274 void* user_data) {
275 AudioManagerPulse* pulse = reinterpret_cast<AudioManagerPulse*>(user_data);
276
277 pulse->native_input_sample_rate_ = info->sample_spec.rate;
278 pa_threaded_mainloop_signal(pulse->input_mainloop_, 0);
279 }
280
281 } // namespace media
OLDNEW

Powered by Google App Engine