| Index: media/audio/pulse/pulse_util.cc
|
| diff --git a/media/audio/pulse/pulse_util.cc b/media/audio/pulse/pulse_util.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..963905865e748a340caaeccb3ed55d780d7ee194
|
| --- /dev/null
|
| +++ b/media/audio/pulse/pulse_util.cc
|
| @@ -0,0 +1,248 @@
|
| +// Copyright (c) 2012 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/pulse_util.h"
|
| +
|
| +#include "base/logging.h"
|
| +#include "media/audio/audio_manager_base.h"
|
| +
|
| +namespace media {
|
| +
|
| +namespace {
|
| +
|
| +pa_channel_position ChromiumToPAChannelPosition(Channels channel) {
|
| + switch (channel) {
|
| + // PulseAudio does not differentiate between left/right and
|
| + // stereo-left/stereo-right, both translate to front-left/front-right.
|
| + case LEFT:
|
| + return PA_CHANNEL_POSITION_FRONT_LEFT;
|
| + case RIGHT:
|
| + return PA_CHANNEL_POSITION_FRONT_RIGHT;
|
| + case CENTER:
|
| + return PA_CHANNEL_POSITION_FRONT_CENTER;
|
| + case LFE:
|
| + return PA_CHANNEL_POSITION_LFE;
|
| + case BACK_LEFT:
|
| + return PA_CHANNEL_POSITION_REAR_LEFT;
|
| + case BACK_RIGHT:
|
| + return PA_CHANNEL_POSITION_REAR_RIGHT;
|
| + case LEFT_OF_CENTER:
|
| + return PA_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER;
|
| + case RIGHT_OF_CENTER:
|
| + return PA_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER;
|
| + case BACK_CENTER:
|
| + return PA_CHANNEL_POSITION_REAR_CENTER;
|
| + case SIDE_LEFT:
|
| + return PA_CHANNEL_POSITION_SIDE_LEFT;
|
| + case SIDE_RIGHT:
|
| + return PA_CHANNEL_POSITION_SIDE_RIGHT;
|
| + case CHANNELS_MAX:
|
| + return PA_CHANNEL_POSITION_INVALID;
|
| + default:
|
| + NOTREACHED() << "Invalid channel: " << channel;
|
| + return PA_CHANNEL_POSITION_INVALID;
|
| + }
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +PulseInputObject::PulseInputObject()
|
| + : pa_mainloop_(NULL),
|
| + pa_context_(NULL),
|
| + devices(NULL),
|
| + sample_rate_(0),
|
| + initialized_(false) {
|
| + Initialize();
|
| +}
|
| +
|
| +PulseInputObject::~PulseInputObject() {
|
| + Terminate();
|
| +}
|
| +
|
| +void PulseInputObject::GetInputDevices(AudioDeviceNames* device_names) {
|
| + DCHECK(pa_mainloop_);
|
| + DCHECK(pa_context_);
|
| + devices = device_names;
|
| + AutoPulseLock auto_lock(pa_mainloop_);
|
| + pa_operation* operation = pa_context_get_source_info_list(
|
| + pa_context_, DevicesInfoCallback, this);
|
| + WaitForOperationCompletion(pa_mainloop_, operation);
|
| +
|
| + if (!device_names->empty()) {
|
| + device_names->push_front(
|
| + AudioDeviceName(AudioManagerBase::kDefaultDeviceName,
|
| + AudioManagerBase::kDefaultDeviceId));
|
| + }
|
| +}
|
| +
|
| +int PulseInputObject::GetNativeSampleRate() {
|
| + DCHECK(pa_mainloop_);
|
| + DCHECK(pa_context_);
|
| + AutoPulseLock auto_lock(pa_mainloop_);
|
| + pa_operation* operation = pa_context_get_server_info(
|
| + pa_context_, SamplerateInfoCallback, this);
|
| + WaitForOperationCompletion(pa_mainloop_, operation);
|
| +
|
| + return sample_rate_;
|
| +}
|
| +
|
| +void PulseInputObject::Initialize() {
|
| + DCHECK(!initialized_);
|
| + DCHECK(!pa_mainloop_);
|
| +
|
| + // Create a mainloop API and connect to the default server.
|
| + // The mainloop is the internal asynchronous API event loop.
|
| + pa_mainloop_ = pa_threaded_mainloop_new();
|
| + if (!pa_mainloop_)
|
| + return;
|
| +
|
| + // Start the threaded mainloop.
|
| + if (pa_threaded_mainloop_start(pa_mainloop_)) {
|
| + Terminate();
|
| + return;
|
| + }
|
| +
|
| + // Lock the event loop object, effectively blocking the event loop thread
|
| + // from processing events. This is necessary.
|
| + AutoPulseLock auto_lock(pa_mainloop_);
|
| +
|
| + pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(pa_mainloop_);
|
| + pa_context_ = pa_context_new(pa_mainloop_api, "Chrome input");
|
| + DCHECK(pa_context_) << "Failed to create PA context";
|
| + if (!pa_context_) {
|
| + return;
|
| + }
|
| +
|
| + pa_context_set_state_callback(pa_context_, &ContextStateCallback, this);
|
| + if (pa_context_connect(pa_context_, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL)) {
|
| + DLOG(ERROR) << "Failed to connect to the context";
|
| + return;
|
| + }
|
| +
|
| + // Wait until |pa_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(pa_context_);
|
| + if (!PA_CONTEXT_IS_GOOD(context_state)) {
|
| + return;
|
| + }
|
| + if (context_state == PA_CONTEXT_READY)
|
| + break;
|
| + pa_threaded_mainloop_wait(pa_mainloop_);
|
| + }
|
| +
|
| + initialized_ = true;
|
| +}
|
| +
|
| +void PulseInputObject::Terminate() {
|
| + if (!pa_mainloop_) {
|
| + DCHECK(!pa_context_);
|
| + return;
|
| + }
|
| +
|
| + {
|
| + AutoPulseLock auto_lock(pa_mainloop_);
|
| + if (pa_context_) {
|
| + // Clear our state callback.
|
| + pa_context_set_state_callback(pa_context_, NULL, NULL);
|
| + pa_context_disconnect(pa_context_);
|
| + pa_context_unref(pa_context_);
|
| + pa_context_ = NULL;
|
| + }
|
| + }
|
| +
|
| + pa_threaded_mainloop_stop(pa_mainloop_);
|
| + pa_threaded_mainloop_free(pa_mainloop_);
|
| + pa_mainloop_ = NULL;
|
| + initialized_ = false;
|
| +}
|
| +
|
| +void PulseInputObject::ContextStateCallback(pa_context* context,
|
| + void* user_data) {
|
| + PulseInputObject* pulse = reinterpret_cast<PulseInputObject*>(user_data);
|
| + pa_threaded_mainloop_signal(pulse->mainloop(), 0);
|
| +}
|
| +
|
| +void PulseInputObject::DevicesInfoCallback(pa_context* context,
|
| + const pa_source_info* info,
|
| + int error, void *user_data) {
|
| + PulseInputObject* pulse = reinterpret_cast<PulseInputObject*>(user_data);
|
| +
|
| + if (error) {
|
| + // Signal the pulse object that it is done.
|
| + pa_threaded_mainloop_signal(pulse->mainloop(), 0);
|
| + return;
|
| + }
|
| +
|
| + // Exclude the output devices.
|
| + if (info->monitor_of_sink == PA_INVALID_INDEX) {
|
| + pulse->devices->push_back(media::AudioDeviceName(info->description,
|
| + info->name));
|
| + }
|
| +}
|
| +
|
| +void PulseInputObject::SamplerateInfoCallback(pa_context* context,
|
| + const pa_server_info* info,
|
| + void* user_data) {
|
| + PulseInputObject* pulse = reinterpret_cast<PulseInputObject*>(user_data);
|
| +
|
| + pulse->sample_rate_ = info->sample_spec.rate;
|
| + pa_threaded_mainloop_signal(pulse->mainloop(), 0);
|
| +}
|
| +
|
| +pa_sample_format_t BitsToPASampleFormat(int bits_per_sample) {
|
| + switch (bits_per_sample) {
|
| + case 8:
|
| + return PA_SAMPLE_U8;
|
| + case 16:
|
| + return PA_SAMPLE_S16LE;
|
| + case 24:
|
| + return PA_SAMPLE_S24LE;
|
| + case 32:
|
| + return PA_SAMPLE_S32LE;
|
| + default:
|
| + NOTREACHED() << "Invalid bits per sample: " << bits_per_sample;
|
| + return PA_SAMPLE_INVALID;
|
| + }
|
| +}
|
| +
|
| +// pa_stream_success_cb_t
|
| +void StreamSuccessCallback(pa_stream* s, int error, void* mainloop) {
|
| + pa_threaded_mainloop* pa_mainloop =
|
| + static_cast<pa_threaded_mainloop*>(mainloop);
|
| + pa_threaded_mainloop_signal(pa_mainloop, 0);
|
| +}
|
| +
|
| +pa_channel_map ChannelLayoutToPAChannelMap(ChannelLayout channel_layout) {
|
| + pa_channel_map channel_map;
|
| + pa_channel_map_init(&channel_map);
|
| +
|
| + channel_map.channels = ChannelLayoutToChannelCount(channel_layout);
|
| + for (Channels ch = LEFT; ch < CHANNELS_MAX;
|
| + ch = static_cast<Channels>(ch + 1)) {
|
| + int channel_index = ChannelOrder(channel_layout, ch);
|
| + if (channel_index < 0)
|
| + continue;
|
| +
|
| + channel_map.map[channel_index] = ChromiumToPAChannelPosition(ch);
|
| + }
|
| +
|
| + return channel_map;
|
| +}
|
| +
|
| +void WaitForOperationCompletion(pa_threaded_mainloop* pa_mainloop,
|
| + pa_operation* operation) {
|
| + if (!operation) {
|
| + DLOG(WARNING) << "Operation is NULL";
|
| + return;
|
| + }
|
| +
|
| + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING)
|
| + pa_threaded_mainloop_wait(pa_mainloop);
|
| +
|
| + pa_operation_unref(operation);
|
| +}
|
| +
|
| +} // namespace media
|
|
|