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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: media/audio/pulse/audio_manager_pulse.cc
diff --git a/media/audio/pulse/audio_manager_pulse.cc b/media/audio/pulse/audio_manager_pulse.cc
new file mode 100644
index 0000000000000000000000000000000000000000..53ddde700fd917df40b69ce545758daaeafeb441
--- /dev/null
+++ b/media/audio/pulse/audio_manager_pulse.cc
@@ -0,0 +1,298 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/audio/pulse/audio_manager_pulse.h"
+
+#include "base/command_line.h"
+#include "base/environment.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/nix/xdg_util.h"
+#include "base/process_util.h"
+#include "base/stl_util.h"
+#include "media/audio/audio_util.h"
+#include "media/audio/pulse/pulse_input.h"
+#include "media/audio/pulse/pulse_output.h"
+#include "media/audio/pulse/pulse_stubs.h"
+#include "media/audio/pulse/pulse_util.h"
+
+using media_audio_pulse::kModulePulse;
+using media_audio_pulse::InitializeStubs;
+using media_audio_pulse::StubPathMap;
+
+namespace media {
+
+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.
+
+// Maximum number of output streams that can be open simultaneously.
+static const int kMaxOutputStreams = 50;
+
+static const base::FilePath::CharType kPulseLib[] =
+ FILE_PATH_LITERAL("libpulse.so.0");
+
DaleCurtis 2013/02/20 00:17:38 Extra line.
no longer working on chromium 2013/02/20 14:43:38 Done.
+} // namespace
+
+// static
+AudioManager* AudioManagerPulse::Create() {
+ scoped_ptr<AudioManagerPulse> ret(new AudioManagerPulse());
+ if (ret->Init())
+ return ret.release();
+
+ DVLOG(1) << "PulseAudio is not available on the OS";
+ return NULL;
+}
+
+AudioManagerPulse::AudioManagerPulse()
+ : input_mainloop_(NULL),
+ input_context_(NULL),
+ devices_(NULL),
+ native_input_sample_rate_(0) {
+ SetMaxOutputStreamsAllowed(kMaxOutputStreams);
+}
+
+AudioManagerPulse::~AudioManagerPulse() {
+ 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()?
+ Shutdown();
+}
+
+// Implementation of AudioManager.
+bool AudioManagerPulse::HasAudioOutputDevices() {
+ // TODO(xians): implement this function.
+ return true;
+}
+
+bool AudioManagerPulse::HasAudioInputDevices() {
+ // TODO(xians): implement this function.
+ return true;
+}
+
+bool AudioManagerPulse::CanShowAudioInputSettings() {
DaleCurtis 2013/02/20 00:17:38 Delete.
no longer working on chromium 2013/02/20 14:43:38 Done.
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+
+ switch (base::nix::GetDesktopEnvironment(env.get())) {
+ case base::nix::DESKTOP_ENVIRONMENT_GNOME:
+ case base::nix::DESKTOP_ENVIRONMENT_KDE3:
+ case base::nix::DESKTOP_ENVIRONMENT_KDE4:
+ case base::nix::DESKTOP_ENVIRONMENT_UNITY:
+ return true;
+ case base::nix::DESKTOP_ENVIRONMENT_OTHER:
+ case base::nix::DESKTOP_ENVIRONMENT_XFCE:
+ return false;
+ }
+ // Unless GetDesktopEnvironment() badly misbehaves, this should never happen.
+ NOTREACHED();
+ return false;
+}
+
+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.
+ scoped_ptr<base::Environment> env(base::Environment::Create());
+ CommandLine command_line(CommandLine::NO_PROGRAM);
+ switch (base::nix::GetDesktopEnvironment(env.get())) {
+ case base::nix::DESKTOP_ENVIRONMENT_GNOME:
+ command_line.SetProgram(base::FilePath("gnome-volume-control"));
+ break;
+ case base::nix::DESKTOP_ENVIRONMENT_KDE3:
+ case base::nix::DESKTOP_ENVIRONMENT_KDE4:
+ command_line.SetProgram(base::FilePath("kmix"));
+ break;
+ case base::nix::DESKTOP_ENVIRONMENT_UNITY:
+ command_line.SetProgram(base::FilePath("gnome-control-center"));
+ command_line.AppendArg("sound");
+ command_line.AppendArg("input");
+ break;
+ default:
+ LOG(ERROR) << "Failed to show audio input settings: we don't know "
+ << "what command to use for your desktop environment.";
+ return;
+ }
+ base::LaunchProcess(command_line, base::LaunchOptions(), NULL);
+}
+
+void AudioManagerPulse::GetAudioInputDeviceNames(
+ media::AudioDeviceNames* device_names) {
+ DCHECK(device_names->empty());
+ DCHECK(input_mainloop_);
+ DCHECK(input_context_);
+ devices_ = device_names;
+ AutoPulseLock auto_lock(input_mainloop_);
+ pa_operation* operation = pa_context_get_source_info_list(
+ input_context_, DevicesInfoCallback, this);
+ WaitForOperationCompletion(input_mainloop_, operation);
+
+ 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
+ device_names->push_front(
+ AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
+ AudioManagerBase::kDefaultDeviceId));
+ }
+}
+
+AudioOutputStream* AudioManagerPulse::MakeLinearOutputStream(
+ const AudioParameters& params) {
+ DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+ return MakeOutputStream(params);
+}
+
+AudioOutputStream* AudioManagerPulse::MakeLowLatencyOutputStream(
+ const AudioParameters& params) {
+ DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+ return MakeOutputStream(params);
+}
+
+AudioInputStream* AudioManagerPulse::MakeLinearInputStream(
+ const AudioParameters& params, const std::string& device_id) {
+ DCHECK_EQ(AudioParameters::AUDIO_PCM_LINEAR, params.format());
+ return MakeInputStream(params, device_id);
+}
+
+AudioInputStream* AudioManagerPulse::MakeLowLatencyInputStream(
+ const AudioParameters& params, const std::string& device_id) {
+ DCHECK_EQ(AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format());
+ return MakeInputStream(params, device_id);
+}
+
+AudioOutputStream* AudioManagerPulse::MakeOutputStream(
+ const AudioParameters& params) {
+ return new PulseAudioOutputStream(params, this);
+}
+
+AudioInputStream* AudioManagerPulse::MakeInputStream(
+ const AudioParameters& params, const std::string& device_id) {
+ return new PulseAudioInputStream(this, device_id, params,
+ input_mainloop_, input_context_);
+}
+
+AudioParameters AudioManagerPulse::GetPreferredLowLatencyOutputStreamParameters(
+ const AudioParameters& input_params) {
+ // TODO(xians): figure out the optimized buffer size for the Pulse IO.
+ int buffer_size = GetAudioHardwareBufferSize();
+ if (input_params.frames_per_buffer() < buffer_size)
+ buffer_size = input_params.frames_per_buffer();
+
+ // TODO(dalecurtis): This should include bits per channel and channel layout
+ // eventually.
+ return AudioParameters(
+ AudioParameters::AUDIO_PCM_LOW_LATENCY, input_params.channel_layout(),
+ input_params.sample_rate(), 16, buffer_size);
+}
+
+int AudioManagerPulse::GetNativeSampleRate() {
+ DCHECK(input_mainloop_);
+ DCHECK(input_context_);
+ AutoPulseLock auto_lock(input_mainloop_);
+ pa_operation* operation = pa_context_get_server_info(
+ input_context_, SamplerateInfoCallback, this);
+ WaitForOperationCompletion(input_mainloop_, operation);
+
+ return native_input_sample_rate_;
+}
+
+bool AudioManagerPulse::Init() {
+ DCHECK(!input_mainloop_);
+
+ StubPathMap paths;
+
+ // Check if the pulse library is avialbale.
+ paths[kModulePulse].push_back(kPulseLib);
+ if (!InitializeStubs(paths)) {
+ DLOG(WARNING) << "Failed on loading the Pulse library and symbols";
+ return false;
+ }
+
+ // Create a mainloop API and connect to the default server.
+ // The mainloop is the internal asynchronous API event loop.
+ input_mainloop_ = pa_threaded_mainloop_new();
+ if (!input_mainloop_)
+ return false;
+
+ // Start the threaded mainloop.
+ if (pa_threaded_mainloop_start(input_mainloop_)) {
+ Terminate();
+ return false;
+ }
+
+ // Lock the event loop object, effectively blocking the event loop thread
+ // from processing events. This is necessary.
+ AutoPulseLock auto_lock(input_mainloop_);
+
+ pa_mainloop_api* pa_mainloop_api =
+ pa_threaded_mainloop_get_api(input_mainloop_);
+ input_context_ = pa_context_new(pa_mainloop_api, "Chrome input");
+ DCHECK(input_context_) << "Failed to create PA context";
+ if (!input_context_) {
+ return false;
+ }
+
+ pa_context_set_state_callback(input_context_, &ContextStateCallback,
+ input_mainloop_);
+ if (pa_context_connect(input_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL)) {
+ DLOG(ERROR) << "Failed to connect to the context";
+ return false;
+ }
+
+ // Wait until |input_context_| is ready. pa_threaded_mainloop_wait() must be
+ // called after pa_context_get_state() in case the context is already ready,
+ // otherwise pa_threaded_mainloop_wait() will hang indefinitely.
+ while (true) {
+ pa_context_state_t context_state = pa_context_get_state(input_context_);
+ if (!PA_CONTEXT_IS_GOOD(context_state)) {
+ return false;
+ }
+ if (context_state == PA_CONTEXT_READY)
+ break;
+ pa_threaded_mainloop_wait(input_mainloop_);
+ }
+
+ return true;
+}
+
+void AudioManagerPulse::Terminate() {
+ if (!input_mainloop_) {
+ DCHECK(!input_context_);
+ return;
+ }
+
+ {
+ AutoPulseLock auto_lock(input_mainloop_);
+ if (input_context_) {
+ // Clear our state callback.
+ pa_context_set_state_callback(input_context_, NULL, NULL);
+ pa_context_disconnect(input_context_);
+ pa_context_unref(input_context_);
+ input_context_ = NULL;
+ }
+ }
+
+ pa_threaded_mainloop_stop(input_mainloop_);
+ pa_threaded_mainloop_free(input_mainloop_);
+ input_mainloop_ = NULL;
+}
+
+void AudioManagerPulse::DevicesInfoCallback(pa_context* context,
+ const pa_source_info* info,
+ int error, void *user_data) {
+ AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
+
+ if (error) {
+ // Signal the pulse object that it is done.
+ pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
+ return;
DaleCurtis 2013/02/20 00:17:38 indent is off.
no longer working on chromium 2013/02/20 14:43:38 Done.
+ }
+
+ // Exclude the output devices.
+ if (info->monitor_of_sink == PA_INVALID_INDEX) {
+ manager->devices_->push_back(media::AudioDeviceName(info->description,
+ info->name));
DaleCurtis 2013/02/20 00:17:38 Indent is off.
no longer working on chromium 2013/02/20 14:43:38 Done.
+ }
+}
+
+void AudioManagerPulse::SamplerateInfoCallback(pa_context* context,
+ const pa_server_info* info,
+ void* user_data) {
+ AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
+
+ manager->native_input_sample_rate_ = info->sample_spec.rate;
+ pa_threaded_mainloop_signal(manager->input_mainloop_, 0);
+}
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698