This source file includes following definitions.
- ChromiumToPAChannelPosition
- StreamSuccessCallback
- ContextStateCallback
- BitsToPASampleFormat
- ChannelLayoutToPAChannelMap
- WaitForOperationCompletion
- GetHardwareLatencyInBytes
- CreateInputStream
- CreateOutputStream
#include "media/audio/pulse/pulse_util.h"
#include "base/logging.h"
#include "base/time/time.h"
#include "media/audio/audio_manager_base.h"
#include "media/audio/audio_parameters.h"
namespace media {
namespace pulse {
namespace {
pa_channel_position ChromiumToPAChannelPosition(Channels channel) {
switch (channel) {
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;
default:
NOTREACHED() << "Invalid channel: " << channel;
return PA_CHANNEL_POSITION_INVALID;
}
}
}
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);
}
void ContextStateCallback(pa_context* context, void* mainloop) {
pa_threaded_mainloop* pa_mainloop =
static_cast<pa_threaded_mainloop*>(mainloop);
pa_threaded_mainloop_signal(pa_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_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);
}
int GetHardwareLatencyInBytes(pa_stream* stream,
int sample_rate,
int bytes_per_frame) {
DCHECK(stream);
int negative = 0;
pa_usec_t latency_micros = 0;
if (pa_stream_get_latency(stream, &latency_micros, &negative) != 0)
return 0;
if (negative)
return 0;
return latency_micros * sample_rate * bytes_per_frame /
base::Time::kMicrosecondsPerSecond;
}
#define RETURN_ON_FAILURE(expression, message) do { \
if (!(expression)) { \
DLOG(ERROR) << message; \
return false; \
} \
} while(0)
bool CreateInputStream(pa_threaded_mainloop* mainloop,
pa_context* context,
pa_stream** stream,
const AudioParameters& params,
const std::string& device_id,
pa_stream_notify_cb_t stream_callback,
void* user_data) {
DCHECK(mainloop);
DCHECK(context);
pa_sample_spec sample_specifications;
sample_specifications.format = BitsToPASampleFormat(
params.bits_per_sample());
sample_specifications.rate = params.sample_rate();
sample_specifications.channels = params.channels();
pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap(
params.channel_layout());
pa_channel_map* map = (source_channel_map.channels != 0) ?
&source_channel_map : NULL;
*stream = pa_stream_new(context, "RecordStream", &sample_specifications, map);
RETURN_ON_FAILURE(*stream, "failed to create PA recording stream");
pa_stream_set_state_callback(*stream, stream_callback, user_data);
pa_buffer_attr buffer_attributes;
const unsigned int buffer_size = params.GetBytesPerBuffer();
buffer_attributes.maxlength = static_cast<uint32_t>(-1);
buffer_attributes.tlength = buffer_size;
buffer_attributes.minreq = buffer_size;
buffer_attributes.prebuf = static_cast<uint32_t>(-1);
buffer_attributes.fragsize = buffer_size;
int flags = PA_STREAM_AUTO_TIMING_UPDATE |
PA_STREAM_INTERPOLATE_TIMING |
PA_STREAM_ADJUST_LATENCY |
PA_STREAM_START_CORKED;
RETURN_ON_FAILURE(
pa_stream_connect_record(
*stream,
device_id == AudioManagerBase::kDefaultDeviceId ?
NULL : device_id.c_str(),
&buffer_attributes,
static_cast<pa_stream_flags_t>(flags)) == 0,
"pa_stream_connect_record FAILED ");
while (true) {
pa_stream_state_t stream_state = pa_stream_get_state(*stream);
RETURN_ON_FAILURE(
PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state");
if (stream_state == PA_STREAM_READY)
break;
pa_threaded_mainloop_wait(mainloop);
}
return true;
}
bool CreateOutputStream(pa_threaded_mainloop** mainloop,
pa_context** context,
pa_stream** stream,
const AudioParameters& params,
const std::string& device_id,
pa_stream_notify_cb_t stream_callback,
pa_stream_request_cb_t write_callback,
void* user_data) {
DCHECK(!*mainloop);
DCHECK(!*context);
*mainloop = pa_threaded_mainloop_new();
RETURN_ON_FAILURE(*mainloop, "Failed to create PulseAudio main loop.");
pa_mainloop_api* pa_mainloop_api = pa_threaded_mainloop_get_api(*mainloop);
*context = pa_context_new(pa_mainloop_api, "Chromium");
RETURN_ON_FAILURE(*context, "Failed to create PulseAudio context.");
pa_context_set_state_callback(*context, &ContextStateCallback, *mainloop);
AutoPulseLock auto_lock(*mainloop);
RETURN_ON_FAILURE(pa_threaded_mainloop_start(*mainloop) == 0,
"Failed to start PulseAudio main loop.");
RETURN_ON_FAILURE(
pa_context_connect(*context, NULL, PA_CONTEXT_NOAUTOSPAWN, NULL) == 0,
"Failed to connect PulseAudio context.");
while (true) {
pa_context_state_t context_state = pa_context_get_state(*context);
RETURN_ON_FAILURE(
PA_CONTEXT_IS_GOOD(context_state), "Invalid PulseAudio context state.");
if (context_state == PA_CONTEXT_READY)
break;
pa_threaded_mainloop_wait(*mainloop);
}
pa_sample_spec sample_specifications;
sample_specifications.format = BitsToPASampleFormat(
params.bits_per_sample());
sample_specifications.rate = params.sample_rate();
sample_specifications.channels = params.channels();
pa_channel_map* map = NULL;
pa_channel_map source_channel_map = ChannelLayoutToPAChannelMap(
params.channel_layout());
if (source_channel_map.channels != 0) {
map = &source_channel_map;
}
*stream = pa_stream_new(*context, "Playback", &sample_specifications, map);
RETURN_ON_FAILURE(*stream, "failed to create PA playback stream");
pa_stream_set_state_callback(*stream, stream_callback, user_data);
pa_stream_set_write_callback(*stream, write_callback, user_data);
pa_buffer_attr pa_buffer_attributes;
pa_buffer_attributes.maxlength = static_cast<uint32_t>(-1);
pa_buffer_attributes.minreq = params.GetBytesPerBuffer();
pa_buffer_attributes.prebuf = static_cast<uint32_t>(-1);
pa_buffer_attributes.tlength = params.GetBytesPerBuffer() * 3;
pa_buffer_attributes.fragsize = static_cast<uint32_t>(-1);
RETURN_ON_FAILURE(
pa_stream_connect_playback(
*stream,
device_id == AudioManagerBase::kDefaultDeviceId ?
NULL : device_id.c_str(),
&pa_buffer_attributes,
static_cast<pa_stream_flags_t>(
PA_STREAM_INTERPOLATE_TIMING | PA_STREAM_ADJUST_LATENCY |
PA_STREAM_AUTO_TIMING_UPDATE | PA_STREAM_NOT_MONOTONIC |
PA_STREAM_START_CORKED),
NULL,
NULL) == 0,
"pa_stream_connect_playback FAILED ");
while (true) {
pa_stream_state_t stream_state = pa_stream_get_state(*stream);
RETURN_ON_FAILURE(
PA_STREAM_IS_GOOD(stream_state), "Invalid PulseAudio stream state");
if (stream_state == PA_STREAM_READY)
break;
pa_threaded_mainloop_wait(*mainloop);
}
return true;
}
#undef RETURN_ON_FAILURE
}
}