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

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: used the stubs script to do the dynamic linking Created 7 years, 10 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
(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/file_path.h"
10 #include "base/logging.h"
11 #include "base/nix/xdg_util.h"
12 #include "base/process_util.h"
13 #include "base/stl_util.h"
14 #include "media/audio/audio_util.h"
15 #include "media/audio/pulse/pulse_input.h"
16 #include "media/audio/pulse/pulse_output.h"
17 #include "media/audio/pulse/pulse_stubs.h"
18 #include "media/audio/pulse/pulse_util.h"
19
20 using media_audio_pulse::kModulePulse;
21 using media_audio_pulse::InitializeStubs;
22 using media_audio_pulse::StubPathMap;
23
24 namespace media {
25
26 namespace {
DaleCurtis 2013/02/20 00:17:38 No need for namespace {}. Media style is static co
no longer working on chromium 2013/02/20 14:43:38 Done.
27
28 // Maximum number of output streams that can be open simultaneously.
29 static const int kMaxOutputStreams = 50;
30
31 static const base::FilePath::CharType kPulseLib[] =
32 FILE_PATH_LITERAL("libpulse.so.0");
33
DaleCurtis 2013/02/20 00:17:38 Extra line.
no longer working on chromium 2013/02/20 14:43:38 Done.
34 } // namespace
35
36 // static
37 AudioManager* AudioManagerPulse::Create() {
38 scoped_ptr<AudioManagerPulse> ret(new AudioManagerPulse());
39 if (ret->Init())
40 return ret.release();
41
42 DVLOG(1) << "PulseAudio is not available on the OS";
43 return NULL;
44 }
45
46 AudioManagerPulse::AudioManagerPulse()
47 : input_mainloop_(NULL),
48 input_context_(NULL),
49 devices_(NULL),
50 native_input_sample_rate_(0) {
51 SetMaxOutputStreamsAllowed(kMaxOutputStreams);
52 }
53
54 AudioManagerPulse::~AudioManagerPulse() {
55 Terminate();
DaleCurtis 2013/02/20 00:17:38 How about a more descriptive name than Terminate()
no longer working on chromium 2013/02/20 14:43:38 How about DestroyPulse()?
56 Shutdown();
57 }
58
59 // Implementation of AudioManager.
60 bool AudioManagerPulse::HasAudioOutputDevices() {
61 // TODO(xians): implement this function.
62 return true;
63 }
64
65 bool AudioManagerPulse::HasAudioInputDevices() {
66 // TODO(xians): implement this function.
67 return true;
68 }
69
70 bool AudioManagerPulse::CanShowAudioInputSettings() {
DaleCurtis 2013/02/20 00:17:38 Delete.
no longer working on chromium 2013/02/20 14:43:38 Done.
71 scoped_ptr<base::Environment> env(base::Environment::Create());
72
73 switch (base::nix::GetDesktopEnvironment(env.get())) {
74 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
75 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
76 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
77 case base::nix::DESKTOP_ENVIRONMENT_UNITY:
78 return true;
79 case base::nix::DESKTOP_ENVIRONMENT_OTHER:
80 case base::nix::DESKTOP_ENVIRONMENT_XFCE:
81 return false;
82 }
83 // Unless GetDesktopEnvironment() badly misbehaves, this should never happen.
84 NOTREACHED();
85 return false;
86 }
87
88 void AudioManagerPulse::ShowAudioInputSettings() {
DaleCurtis 2013/02/20 00:17:38 Same as AudioManagerLinux, just make it a static t
no longer working on chromium 2013/02/20 14:43:38 Done.
89 scoped_ptr<base::Environment> env(base::Environment::Create());
90 CommandLine command_line(CommandLine::NO_PROGRAM);
91 switch (base::nix::GetDesktopEnvironment(env.get())) {
92 case base::nix::DESKTOP_ENVIRONMENT_GNOME:
93 command_line.SetProgram(base::FilePath("gnome-volume-control"));
94 break;
95 case base::nix::DESKTOP_ENVIRONMENT_KDE3:
96 case base::nix::DESKTOP_ENVIRONMENT_KDE4:
97 command_line.SetProgram(base::FilePath("kmix"));
98 break;
99 case base::nix::DESKTOP_ENVIRONMENT_UNITY:
100 command_line.SetProgram(base::FilePath("gnome-control-center"));
101 command_line.AppendArg("sound");
102 command_line.AppendArg("input");
103 break;
104 default:
105 LOG(ERROR) << "Failed to show audio input settings: we don't know "
106 << "what command to use for your desktop environment.";
107 return;
108 }
109 base::LaunchProcess(command_line, base::LaunchOptions(), NULL);
110 }
111
112 void AudioManagerPulse::GetAudioInputDeviceNames(
113 media::AudioDeviceNames* device_names) {
114 DCHECK(device_names->empty());
115 DCHECK(input_mainloop_);
116 DCHECK(input_context_);
117 devices_ = device_names;
118 AutoPulseLock auto_lock(input_mainloop_);
119 pa_operation* operation = pa_context_get_source_info_list(
120 input_context_, DevicesInfoCallback, this);
121 WaitForOperationCompletion(input_mainloop_, operation);
122
123 if (!device_names->empty()) {
DaleCurtis 2013/02/20 00:17:38 Should this be "if (device_name->empty())" ? Othe
no longer working on chromium 2013/02/20 14:43:38 No, we append the default device on top of the lis
124 device_names->push_front(
125 AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
126 AudioManagerBase::kDefaultDeviceId));
127 }
128 }
129
130 AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream(
131 const AudioParameters& params) {
132 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
133 return MakeOutputStream(params);
134 }
135
136 AudioOutputStream* AudioManagerPulse::MakeLowLatencyOutputStream(
137 const AudioParameters& params) {
138 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
139 return MakeOutputStream(params);
140 }
141
142 AudioInputStream* AudioManagerPulse::MakeLinearInputStream(
143 const AudioParameters& params, const std::string& device_id) {
144 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
145 return MakeInputStream(params, device_id);
146 }
147
148 AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream(
149 const AudioParameters& params, const std::string& device_id) {
150 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
151 return MakeInputStream(params, device_id);
152 }
153
154 AudioOutputStream* AudioManagerPulse::MakeOutputStream(
155 const AudioParameters& params) {
156 return new PulseAudioOutputStream(params, this);
157 }
158
159 AudioInputStream* AudioManagerPulse::MakeInputStream(
160 const AudioParameters& params, const std::string& device_id) {
161 return new PulseAudioInputStream(this, device_id, params,
162 input_mainloop_, input_context_);
163 }
164
165 AudioParameters AudioManagerPulse::GetPreferredLowLatencyOutputStreamParameters(
166 const AudioParameters& input_params) {
167 // TODO(xians): figure out the optimized buffer size for the Pulse IO.
168 int buffer_size = GetAudioHardwareBufferSize();
169 if (input_params.frames_per_buffer() < buffer_size)
170 buffer_size = input_params.frames_per_buffer();
171
172 // TODO(dalecurtis): This should include bits per channel and channel layout
173 // eventually.
174 return AudioParameters(
175 AudioParameters::AUDIO_PCM_LOW_LATENCY, input_params.channel_layout(),
176 input_params.sample_rate(), 16, buffer_size);
177 }
178
179 int AudioManagerPulse::GetNativeSampleRate() {
180 DCHECK(input_mainloop_);
181 DCHECK(input_context_);
182 AutoPulseLock auto_lock(input_mainloop_);
183 pa_operation* operation = pa_context_get_server_info(
184 input_context_, SamplerateInfoCallback, this);
185 WaitForOperationCompletion(input_mainloop_, operation);
186
187 return native_input_sample_rate_;
188 }
189
190 bool AudioManagerPulse::Init() {
191 DCHECK(!input_mainloop_);
192
193 StubPathMap paths;
194
195 // Check if the pulse library is avialbale.
196 paths[kModulePulse].push_back(kPulseLib);
197 if (!InitializeStubs(paths)) {
198 DLOG(WARNING) << "Failed on loading the Pulse library and symbols";
199 return false;
200 }
201
202 // Create a mainloop API and connect to the default server.
203 // The mainloop is the internal asynchronous API event loop.
204 input_mainloop_ = pa_threaded_mainloop_new();
205 if (!input_mainloop_)
206 return false;
207
208 // Start the threaded mainloop.
209 if (pa_threaded_mainloop_start(input_mainloop_)) {
210 Terminate();
211 return false;
212 }
213
214 // Lock the event loop object, effectively blocking the event loop thread
215 // from processing events. This is necessary.
216 AutoPulseLock auto_lock(input_mainloop_);
217
218 pa_mainloop_api* pa_mainloop_api =
219 pa_threaded_mainloop_get_api(input_mainloop_);
220 input_context_ = pa_context_new(pa_mainloop_api, "Chrome input");
221 DCHECK(input_context_) << "Failed to create PA context";
222 if (!input_context_) {
223 return false;
224 }
225
226 pa_context_set_state_callback(input_context_, &ContextStateCallback,
227 input_mainloop_);
228 if (pa_context_connect(input_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL)) {
229 DLOG(ERROR) << "Failed to connect to the context";
230 return false;
231 }
232
233 // Wait until |input_context_| is ready. pa_threaded_mainloop_wait() must be
234 // called after pa_context_get_state() in case the context is already ready,
235 // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
236 while (true) {
237 pa_context_state_t context_state = pa_context_get_state(input_context_);
238 if (!PA_CONTEXT_IS_GOOD(context_state)) {
239 return false;
240 }
241 if (context_state == PA_CONTEXT_READY)
242 break;
243 pa_threaded_mainloop_wait(input_mainloop_);
244 }
245
246 return true;
247 }
248
249 void AudioManagerPulse::Terminate() {
250 if (!input_mainloop_) {
251 DCHECK(!input_context_);
252 return;
253 }
254
255 {
256 AutoPulseLock auto_lock(input_mainloop_);
257 if (input_context_) {
258 // Clear our state callback.
259 pa_context_set_state_callback(input_context_, NULL, NULL);
260 pa_context_disconnect(input_context_);
261 pa_context_unref(input_context_);
262 input_context_ = NULL;
263 }
264 }
265
266 pa_threaded_mainloop_stop(input_mainloop_);
267 pa_threaded_mainloop_free(input_mainloop_);
268 input_mainloop_ = NULL;
269 }
270
271 void AudioManagerPulse::DevicesInfoCallback(pa_context* context,
272 const pa_source_info* info,
273 int error, void *user_data) {
274 AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
275
276 if (error) {
277 // Signal the pulse object that it is done.
278 pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
279 return;
DaleCurtis 2013/02/20 00:17:38 indent is off.
no longer working on chromium 2013/02/20 14:43:38 Done.
280 }
281
282 // Exclude the output devices.
283 if (info->monitor_of_sink == PA_INVALID_INDEX) {
284 manager->devices_->push_back(media::AudioDeviceName(info->description,
285 info->name));
DaleCurtis 2013/02/20 00:17:38 Indent is off.
no longer working on chromium 2013/02/20 14:43:38 Done.
286 }
287 }
288
289 void AudioManagerPulse::SamplerateInfoCallback(pa_context* context,
290 const pa_server_info* info,
291 void* user_data) {
292 AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
293
294 manager->native_input_sample_rate_ = info->sample_spec.rate;
295 pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
296 }
297
298 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698