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

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: switched to dynamic linking and addressed Andrew'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 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..b81d73875bdc40a3a7b27cb92b686111b804eb4f
--- /dev/null
+++ b/media/audio/pulse/audio_manager_pulse.cc
@@ -0,0 +1,297 @@
+// 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/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_util.h"
+#include "media/audio/pulse/pulse_wrapper.h"
+
+namespace media {
+
+namespace {
+
+// Maximum number of output streams that can be open simultaneously.
+static const int kMaxOutputStreams = 50;
+
+} // 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();
+ 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() {
+ 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() {
+ 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(wrapper_.get(), input_mainloop_);
+ pa_operation* operation = wrapper_->pa_context_get_source_info_list_(
+ input_context_, DevicesInfoCallback, this);
+ WaitForOperationCompletion(wrapper_.get(), input_mainloop_, operation);
+
+ if (!device_names->empty()) {
+ 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(wrapper_.get(), params, this);
+}
+
+AudioInputStream* AudioManagerPulse::MakeInputStream(
+ const AudioParameters& params, const std::string& device_id) {
+ return new PulseAudioInputStream(wrapper_.get(), 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);
DaleCurtis 2013/02/15 00:28:51 We can land it this way, but I'm pretty sure we'll
no longer working on chromium 2013/02/15 16:07:33 Yes, we definitely need to use the native sample r
+}
+
+int AudioManagerPulse::GetNativeSampleRate() {
DaleCurtis 2013/02/15 00:28:51 We'll want to plumb this into AudioUtil for before
no longer working on chromium 2013/02/15 16:07:33 Great, FYI, we need to access some member variable
+ DCHECK(input_mainloop_);
+ DCHECK(input_context_);
+ AutoPulseLock auto_lock(wrapper_.get(), input_mainloop_);
+ pa_operation* operation = wrapper_->pa_context_get_server_info_(
+ input_context_, SamplerateInfoCallback, this);
+ WaitForOperationCompletion(wrapper_.get(), input_mainloop_, operation);
+
+ return native_input_sample_rate_;
+}
+
+bool AudioManagerPulse::Init() {
+ DCHECK(!input_mainloop_);
+
+ wrapper_.reset(PulseWrapper::Create());
+ if (!wrapper_) {
+ DLOG(WARNING) << "Failed on loading the Pulse library and symbols";
+ return false;
+ }
+ DLOG(WARNING) << "after loading the Pulse library and symbols";
+ // Create a mainloop API and connect to the default server.
+ // The mainloop is the internal asynchronous API event loop.
+ input_mainloop_ = wrapper_->pa_threaded_mainloop_new_();
+ if (!input_mainloop_)
+ return false;
+ DLOG(WARNING) << "after 2";
+ // Start the threaded mainloop.
+ if (wrapper_->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(wrapper_.get(), input_mainloop_);
+
+ pa_mainloop_api* pa_mainloop_api =
+ wrapper_->pa_threaded_mainloop_get_api_(input_mainloop_);
+ input_context_ = wrapper_->pa_context_new_(pa_mainloop_api, "Chrome input");
+ DCHECK(input_context_) << "Failed to create PA context";
+ if (!input_context_) {
+ return false;
+ }
+
+ wrapper_->pa_context_set_state_callback_(
+ input_context_, &ContextStateCallback, this);
+ if (wrapper_->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 =
+ wrapper_->pa_context_get_state_(input_context_);
+ if (!PA_CONTEXT_IS_GOOD(context_state)) {
+ return false;
+ }
+ if (context_state == PA_CONTEXT_READY)
+ break;
+ wrapper_->pa_threaded_mainloop_wait_(input_mainloop_);
+ }
+
+ return true;
+}
+
+void AudioManagerPulse::Terminate() {
+ if (!input_mainloop_) {
+ DCHECK(!input_context_);
+ return;
+ }
+
+ {
+ AutoPulseLock auto_lock(wrapper_.get(), input_mainloop_);
+ if (input_context_) {
+ // Clear our state callback.
+ wrapper_->pa_context_set_state_callback_(input_context_, NULL, NULL);
+ wrapper_->pa_context_disconnect_(input_context_);
+ wrapper_->pa_context_unref_(input_context_);
+ input_context_ = NULL;
+ }
+ }
+
+ wrapper_->pa_threaded_mainloop_stop_(input_mainloop_);
+ wrapper_->pa_threaded_mainloop_free_(input_mainloop_);
+ input_mainloop_ = NULL;
+}
+
+// static, |pa_context| state changed cb.
+void AudioManagerPulse::ContextStateCallback(pa_context* context,
+ void* user_data) {
+ AudioManagerPulse* manager = reinterpret_cast<AudioManagerPulse*>(user_data);
+ manager->wrapper_->pa_threaded_mainloop_signal_(manager->input_mainloop_, 0);
+}
+
+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.
+ manager->wrapper_->pa_threaded_mainloop_signal_(
+ manager->input_mainloop_, 0);
+ return;
+ }
+
+ // Exclude the output devices.
+ if (info->monitor_of_sink == PA_INVALID_INDEX) {
+ manager->devices_->push_back(media::AudioDeviceName(info->description,
+ info->name));
+ }
+}
+
+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;
+ manager->wrapper_->pa_threaded_mainloop_signal_(manager->input_mainloop_, 0);
+}
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698