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

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: addressed Dale's comments 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
« no previous file with comments | « media/audio/pulse/audio_manager_pulse.h ('k') | media/audio/pulse/pulse.sigs » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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/linux/audio_manager_linux.h"
16 #include "media/audio/pulse/pulse_input.h"
17 #include "media/audio/pulse/pulse_output.h"
18 #include "media/audio/pulse/pulse_stubs.h"
19 #include "media/audio/pulse/pulse_util.h"
20
21 using media_audio_pulse::kModulePulse;
22 using media_audio_pulse::InitializeStubs;
23 using media_audio_pulse::StubPathMap;
24
25 namespace media {
26
27 // Maximum number of output streams that can be open simultaneously.
28 static const int kMaxOutputStreams = 50;
29
30 static const base::FilePath::CharType kPulseLib[] =
31 FILE_PATH_LITERAL("libpulse.so.0");
32
33 // static
34 AudioManager* AudioManagerPulse::Create() {
35 scoped_ptr<AudioManagerPulse> ret(new AudioManagerPulse());
36 if (ret->Init())
37 return ret.release();
38
39 DVLOG(1) << "PulseAudio is not available on the OS";
40 return NULL;
41 }
42
43 AudioManagerPulse::AudioManagerPulse()
44 : input_mainloop_(NULL),
45 input_context_(NULL),
46 devices_(NULL),
47 native_input_sample_rate_(0) {
48 SetMaxOutputStreamsAllowed(kMaxOutputStreams);
49 }
50
51 AudioManagerPulse::~AudioManagerPulse() {
52 DestroyPulse();
53 Shutdown();
54 }
55
56 // Implementation of AudioManager.
57 bool AudioManagerPulse::HasAudioOutputDevices() {
58 // TODO(xians): implement this function.
59 return true;
60 }
61
62 bool AudioManagerPulse::HasAudioInputDevices() {
63 // TODO(xians): implement this function.
64 return true;
65 }
66
67 void AudioManagerPulse::ShowAudioInputSettings() {
68 AudioManagerLinux::ShowLinuxAudioInputSettings();
69 }
70
71 void AudioManagerPulse::GetAudioInputDeviceNames(
72 media::AudioDeviceNames* device_names) {
73 DCHECK(device_names->empty());
74 DCHECK(input_mainloop_);
75 DCHECK(input_context_);
76 devices_ = device_names;
77 pulse::AutoPulseLock auto_lock(input_mainloop_);
DaleCurtis 2013/02/21 21:54:22 Could add pulse::AutoPulseLock and pulse::WaitFor.
no longer working on chromium 2013/02/22 13:12:29 Done.
78 pa_operation* operation = pa_context_get_source_info_list(
79 input_context_, DevicesInfoCallback, this);
80 pulse::WaitForOperationCompletion(input_mainloop_, operation);
81
82 // Append the default device on the top of the list if the list is not empty.
83 if (!device_names->empty()) {
84 device_names->push_front(
85 AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
86 AudioManagerBase::kDefaultDeviceId));
87 }
88 }
89
90 AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream(
91 const AudioParameters& params) {
92 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
93 return MakeOutputStream(params);
94 }
95
96 AudioOutputStream* AudioManagerPulse::MakeLowLatencyOutputStream(
97 const AudioParameters& params) {
98 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
99 return MakeOutputStream(params);
100 }
101
102 AudioInputStream* AudioManagerPulse::MakeLinearInputStream(
103 const AudioParameters& params, const std::string& device_id) {
104 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
105 return MakeInputStream(params, device_id);
106 }
107
108 AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream(
109 const AudioParameters& params, const std::string& device_id) {
110 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
111 return MakeInputStream(params, device_id);
112 }
113
114 AudioOutputStream* AudioManagerPulse::MakeOutputStream(
115 const AudioParameters& params) {
116 return new PulseAudioOutputStream(params, this);
117 }
118
119 AudioInputStream* AudioManagerPulse::MakeInputStream(
120 const AudioParameters& params, const std::string& device_id) {
121 return new PulseAudioInputStream(this, device_id, params,
122 input_mainloop_, input_context_);
123 }
124
125 AudioParameters AudioManagerPulse::GetPreferredLowLatencyOutputStreamParameters(
126 const AudioParameters& input_params) {
127 // TODO(xians): figure out the optimized buffer size for the Pulse IO.
128 int buffer_size = GetAudioHardwareBufferSize();
129 if (input_params.frames_per_buffer() < buffer_size)
130 buffer_size = input_params.frames_per_buffer();
131
132 // TODO(dalecurtis): This should include bits per channel and channel layout
133 // eventually.
134 return AudioParameters(
135 AudioParameters::AUDIO_PCM_LOW_LATENCY, input_params.channel_layout(),
136 input_params.sample_rate(), 16, buffer_size);
137 }
138
139 int AudioManagerPulse::GetNativeSampleRate() {
140 DCHECK(input_mainloop_);
141 DCHECK(input_context_);
142 pulse::AutoPulseLock auto_lock(input_mainloop_);
143 pa_operation* operation = pa_context_get_server_info(
144 input_context_, SampleRateInfoCallback, this);
145 pulse::WaitForOperationCompletion(input_mainloop_, operation);
146
147 return native_input_sample_rate_;
148 }
149
150 bool AudioManagerPulse::Init() {
151 DCHECK(!input_mainloop_);
152
153 StubPathMap paths;
154
155 // Check if the pulse library is avialbale.
156 paths[kModulePulse].push_back(kPulseLib);
157 if (!InitializeStubs(paths)) {
158 DLOG(WARNING) << "Failed on loading the Pulse library and symbols";
159 return false;
160 }
161
162 // Create a mainloop API and connect to the default server.
163 // The mainloop is the internal asynchronous API event loop.
164 input_mainloop_ = pa_threaded_mainloop_new();
165 if (!input_mainloop_)
166 return false;
167
168 // Start the threaded mainloop.
169 if (pa_threaded_mainloop_start(input_mainloop_)) {
170 DestroyPulse();
171 return false;
172 }
173
174 // Lock the event loop object, effectively blocking the event loop thread
175 // from processing events. This is necessary.
176 pulse::AutoPulseLock auto_lock(input_mainloop_);
177
178 pa_mainloop_api* pa_mainloop_api =
179 pa_threaded_mainloop_get_api(input_mainloop_);
180 input_context_ = pa_context_new(pa_mainloop_api, "Chrome input");
181 DCHECK(input_context_) << "Failed to create PA context";
182 if (!input_context_) {
183 return false;
184 }
185
186 pa_context_set_state_callback(input_context_, &pulse::ContextStateCallback,
187 input_mainloop_);
188 if (pa_context_connect(input_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL)) {
189 DLOG(ERROR) << "Failed to connect to the context";
190 return false;
191 }
192
193 // Wait until |input_context_| is ready. pa_threaded_mainloop_wait() must be
194 // called after pa_context_get_state() in case the context is already ready,
195 // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
196 while (true) {
197 pa_context_state_t context_state = pa_context_get_state(input_context_);
198 if (!PA_CONTEXT_IS_GOOD(context_state)) {
199 return false;
200 }
201 if (context_state == PA_CONTEXT_READY)
202 break;
203 pa_threaded_mainloop_wait(input_mainloop_);
204 }
205
206 return true;
207 }
208
209 void AudioManagerPulse::DestroyPulse() {
210 if (!input_mainloop_) {
211 DCHECK(!input_context_);
212 return;
213 }
214
215 {
216 pulse::AutoPulseLock auto_lock(input_mainloop_);
217 if (input_context_) {
218 // Clear our state callback.
219 pa_context_set_state_callback(input_context_, NULL, NULL);
220 pa_context_disconnect(input_context_);
221 pa_context_unref(input_context_);
222 input_context_ = NULL;
223 }
224 }
225
226 pa_threaded_mainloop_stop(input_mainloop_);
227 pa_threaded_mainloop_free(input_mainloop_);
228 input_mainloop_ = NULL;
229 }
230
231 void AudioManagerPulse::DevicesInfoCallback(pa_context* context,
232 const pa_source_info* info,
233 int error, void *user_data) {
234 AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
235
236 if (error) {
237 // Signal the pulse object that it is done.
238 pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
239 return;
240 }
241
242 // Exclude the output devices.
243 if (info->monitor_of_sink == PA_INVALID_INDEX) {
244 manager->devices_->push_back(media::AudioDeviceName(info->description,
245 info->name));
246 }
247 }
248
249 void AudioManagerPulse::SampleRateInfoCallback(pa_context* context,
250 const pa_server_info* info,
251 void* user_data) {
252 AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
253
254 manager->native_input_sample_rate_ = info->sample_spec.rate;
255 pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
256 }
257
258 } // namespace media
OLDNEW
« no previous file with comments | « media/audio/pulse/audio_manager_pulse.h ('k') | media/audio/pulse/pulse.sigs » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698