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

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: rebased and addressed Dale's final 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 using pulse::AutoPulseLock;
28 using pulse::WaitForOperationCompletion;
29
30 // Maximum number of output streams that can be open simultaneously.
31 static const int kMaxOutputStreams = 50;
32
33 static const base::FilePath::CharType kPulseLib[] =
34 FILE_PATH_LITERAL("libpulse.so.0");
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 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 void AudioManagerPulse::ShowAudioInputSettings() {
71 AudioManagerLinux::ShowLinuxAudioInputSettings();
72 }
73
74 void AudioManagerPulse::GetAudioInputDeviceNames(
75 media::AudioDeviceNames* device_names) {
76 DCHECK(device_names->empty());
77 DCHECK(input_mainloop_);
78 DCHECK(input_context_);
79 devices_ = device_names;
80 AutoPulseLock auto_lock(input_mainloop_);
81 pa_operation* operation = pa_context_get_source_info_list(
82 input_context_, DevicesInfoCallback, this);
83 WaitForOperationCompletion(input_mainloop_, operation);
84
85 // Append the default device on the top of the list if the list is not empty.
86 if (!device_names->empty()) {
87 device_names->push_front(
88 AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
89 AudioManagerBase::kDefaultDeviceId));
90 }
91 }
92
93 AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream(
94 const AudioParameters& params) {
95 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
96 return MakeOutputStream(params);
97 }
98
99 AudioOutputStream* AudioManagerPulse::MakeLowLatencyOutputStream(
100 const AudioParameters& params) {
101 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
102 return MakeOutputStream(params);
103 }
104
105 AudioInputStream* AudioManagerPulse::MakeLinearInputStream(
106 const AudioParameters& params, const std::string& device_id) {
107 DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
108 return MakeInputStream(params, device_id);
109 }
110
111 AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream(
112 const AudioParameters& params, const std::string& device_id) {
113 DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
114 return MakeInputStream(params, device_id);
115 }
116
117 AudioOutputStream* AudioManagerPulse::MakeOutputStream(
118 const AudioParameters& params) {
119 return new PulseAudioOutputStream(params, this);
120 }
121
122 AudioInputStream* AudioManagerPulse::MakeInputStream(
123 const AudioParameters& params, const std::string& device_id) {
124 return new PulseAudioInputStream(this, device_id, params,
125 input_mainloop_, input_context_);
126 }
127
128 AudioParameters AudioManagerPulse::GetPreferredLowLatencyOutputStreamParameters(
129 const AudioParameters& input_params) {
130 // TODO(xians): figure out the optimized buffer size for the Pulse IO.
131 int buffer_size = GetAudioHardwareBufferSize();
132 if (input_params.frames_per_buffer() < buffer_size)
133 buffer_size = input_params.frames_per_buffer();
134
135 // TODO(dalecurtis): This should include bits per channel and channel layout
136 // eventually.
137 return AudioParameters(
138 AudioParameters::AUDIO_PCM_LOW_LATENCY, input_params.channel_layout(),
139 input_params.sample_rate(), 16, buffer_size);
140 }
141
142 int AudioManagerPulse::GetNativeSampleRate() {
143 DCHECK(input_mainloop_);
144 DCHECK(input_context_);
145 AutoPulseLock auto_lock(input_mainloop_);
146 pa_operation* operation = pa_context_get_server_info(
147 input_context_, SampleRateInfoCallback, this);
148 WaitForOperationCompletion(input_mainloop_, operation);
149
150 return native_input_sample_rate_;
151 }
152
153 bool AudioManagerPulse::Init() {
154 DCHECK(!input_mainloop_);
155
156 StubPathMap paths;
157
158 // Check if the pulse library is avialbale.
159 paths[kModulePulse].push_back(kPulseLib);
160 if (!InitializeStubs(paths)) {
161 DLOG(WARNING) << "Failed on loading the Pulse library and symbols";
162 return false;
163 }
164
165 // Create a mainloop API and connect to the default server.
166 // The mainloop is the internal asynchronous API event loop.
167 input_mainloop_ = pa_threaded_mainloop_new();
168 if (!input_mainloop_)
169 return false;
170
171 // Start the threaded mainloop.
172 if (pa_threaded_mainloop_start(input_mainloop_)) {
173 DestroyPulse();
174 return false;
175 }
176
177 // Lock the event loop object, effectively blocking the event loop thread
178 // from processing events. This is necessary.
179 AutoPulseLock auto_lock(input_mainloop_);
180
181 pa_mainloop_api* pa_mainloop_api =
182 pa_threaded_mainloop_get_api(input_mainloop_);
183 input_context_ = pa_context_new(pa_mainloop_api, "Chrome input");
184 DCHECK(input_context_) << "Failed to create PA context";
185 if (!input_context_) {
186 return false;
187 }
188
189 pa_context_set_state_callback(input_context_, &pulse::ContextStateCallback,
190 input_mainloop_);
191 if (pa_context_connect(input_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL)) {
192 DLOG(ERROR) << "Failed to connect to the context";
193 return false;
194 }
195
196 // Wait until |input_context_| is ready. pa_threaded_mainloop_wait() must be
197 // called after pa_context_get_state() in case the context is already ready,
198 // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
199 while (true) {
200 pa_context_state_t context_state = pa_context_get_state(input_context_);
201 if (!PA_CONTEXT_IS_GOOD(context_state)) {
202 return false;
203 }
204 if (context_state == PA_CONTEXT_READY)
205 break;
206 pa_threaded_mainloop_wait(input_mainloop_);
207 }
208
209 return true;
210 }
211
212 void AudioManagerPulse::DestroyPulse() {
213 if (!input_mainloop_) {
214 DCHECK(!input_context_);
215 return;
216 }
217
218 {
219 AutoPulseLock auto_lock(input_mainloop_);
220 if (input_context_) {
221 // Clear our state callback.
222 pa_context_set_state_callback(input_context_, NULL, NULL);
223 pa_context_disconnect(input_context_);
224 pa_context_unref(input_context_);
225 input_context_ = NULL;
226 }
227 }
228
229 pa_threaded_mainloop_stop(input_mainloop_);
230 pa_threaded_mainloop_free(input_mainloop_);
231 input_mainloop_ = NULL;
232 }
233
234 void AudioManagerPulse::DevicesInfoCallback(pa_context* context,
235 const pa_source_info* info,
236 int error, void *user_data) {
237 AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
238
239 if (error) {
240 // Signal the pulse object that it is done.
241 pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
242 return;
243 }
244
245 // Exclude the output devices.
246 if (info->monitor_of_sink == PA_INVALID_INDEX) {
247 manager->devices_->push_back(media::AudioDeviceName(info->description,
248 info->name));
249 }
250 }
251
252 void AudioManagerPulse::SampleRateInfoCallback(pa_context* context,
253 const pa_server_info* info,
254 void* user_data) {
255 AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
256
257 manager->native_input_sample_rate_ = info->sample_spec.rate;
258 pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
259 }
260
261 } // 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