Use exceptions when opening and reseting the PulseAudio backend

This commit is contained in:
Chris Robinson 2019-05-04 20:35:24 -07:00
parent 95d19be36b
commit 31c29f1ea8

View File

@ -38,6 +38,7 @@
#include "alu.h"
#include "alconfig.h"
#include "compat.h"
#include "alexcpt.h"
#include <pulse/pulseaudio.h>
@ -264,7 +265,7 @@ void wait_for_operation(pa_operation *op, std::unique_lock<std::mutex> &plock)
}
pa_context *connect_context(std::unique_lock<std::mutex> &plock, ALboolean silent)
pa_context *connect_context(std::unique_lock<std::mutex> &plock)
{
const char *name{"OpenAL Soft"};
@ -280,11 +281,7 @@ pa_context *connect_context(std::unique_lock<std::mutex> &plock, ALboolean silen
}
pa_context *context{pa_context_new(pa_mainloop_get_api(pulse_mainloop), name)};
if(!context)
{
ERR("pa_context_new() failed\n");
return nullptr;
}
if(!context) throw al::backend_exception{ALC_OUT_OF_MEMORY, "pa_context_new() failed"};
pa_context_set_state_callback(context, context_state_callback, nullptr);
@ -308,25 +305,15 @@ pa_context *connect_context(std::unique_lock<std::mutex> &plock, ALboolean silen
if(err < 0)
{
if(!silent)
ERR("Context did not connect: %s\n", pa_strerror(err));
pa_context_unref(context);
context = nullptr;
throw al::backend_exception{ALC_INVALID_VALUE, "Context did not connect (%s)",
pa_strerror(err)};
}
return context;
}
pa_context *pulse_open(void(*state_cb)(pa_context*,void*), void *ptr)
{
std::unique_lock<std::mutex> plock{pulse_lock};
pa_context *context{connect_context(plock, AL_FALSE)};
if(LIKELY(context))
pa_context_set_state_callback(context, state_cb, ptr);
return context;
}
void pulse_close(pa_context *context, pa_stream *stream)
{
std::lock_guard<std::mutex> _{pulse_lock};
@ -366,13 +353,11 @@ pa_stream *pulse_connect_stream(const char *device_name, std::unique_lock<std::m
pa_context *context, pa_stream_flags_t flags, pa_buffer_attr *attr, pa_sample_spec *spec,
pa_channel_map *chanmap, BackendType type)
{
pa_stream *stream{pa_stream_new(context,
(type==BackendType::Playback) ? "Playback Stream" : "Capture Stream", spec, chanmap)};
const char *stream_id{(type==BackendType::Playback) ? "Playback Stream" : "Capture Stream"};
pa_stream *stream{pa_stream_new(context, stream_id, spec, chanmap)};
if(!stream)
{
ERR("pa_stream_new_with_proplist() failed: %s\n", pa_strerror(pa_context_errno(context)));
return nullptr;
}
throw al::backend_exception{ALC_OUT_OF_MEMORY, "pa_stream_new() failed (%s)",
pa_strerror(pa_context_errno(context))};
pa_stream_set_state_callback(stream, stream_state_callback, nullptr);
@ -381,9 +366,9 @@ pa_stream *pulse_connect_stream(const char *device_name, std::unique_lock<std::m
pa_stream_connect_record(stream, device_name, attr, flags)};
if(err < 0)
{
ERR("Stream did not connect: %s\n", pa_strerror(err));
pa_stream_unref(stream);
return nullptr;
throw al::backend_exception{ALC_INVALID_VALUE, "%s did not connect (%s)", stream_id,
pa_strerror(err)};
}
pa_stream_state_t state;
@ -391,9 +376,10 @@ pa_stream *pulse_connect_stream(const char *device_name, std::unique_lock<std::m
{
if(!PA_STREAM_IS_GOOD(state))
{
ERR("Stream did not get ready: %s\n", pa_strerror(pa_context_errno(context)));
int err{pa_context_errno(context)};
pa_stream_unref(stream);
return nullptr;
throw al::backend_exception{ALC_INVALID_VALUE, "%s did not get ready (%s)", stream_id,
pa_strerror(err)};
}
pulse_condvar.wait(plock);
@ -440,23 +426,21 @@ void probePlaybackDevices()
{
PlaybackDevices.clear();
std::unique_lock<std::mutex> plock{pulse_lock};
try {
std::unique_lock<std::mutex> plock{pulse_lock};
pa_context *context{connect_context(plock, AL_FALSE)};
if(!context) return;
pa_context *context{connect_context(plock)};
pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS |
PA_STREAM_DONT_MOVE};
const pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE};
pa_sample_spec spec{};
spec.format = PA_SAMPLE_S16NE;
spec.rate = 44100;
spec.channels = 2;
pa_sample_spec spec{};
spec.format = PA_SAMPLE_S16NE;
spec.rate = 44100;
spec.channels = 2;
pa_stream *stream{pulse_connect_stream(nullptr, plock, context, flags, nullptr, &spec, nullptr,
BackendType::Playback)};
if(stream)
{
pa_stream *stream{pulse_connect_stream(nullptr, plock, context, flags, nullptr, &spec,
nullptr, BackendType::Playback)};
pa_operation *op{pa_context_get_sink_info_by_name(context,
pa_stream_get_device_name(stream), device_sink_callback, nullptr)};
wait_for_operation(op, plock);
@ -464,13 +448,16 @@ void probePlaybackDevices()
pa_stream_disconnect(stream);
pa_stream_unref(stream);
stream = nullptr;
op = pa_context_get_sink_info_list(context, device_sink_callback, nullptr);
wait_for_operation(op, plock);
pa_context_disconnect(context);
pa_context_unref(context);
}
catch(std::exception &e) {
ERR("Error enumerating devices: %s\n", e.what());
}
pa_operation *op{pa_context_get_sink_info_list(context, device_sink_callback, nullptr)};
wait_for_operation(op, plock);
pa_context_disconnect(context);
pa_context_unref(context);
}
@ -510,23 +497,21 @@ void probeCaptureDevices()
{
CaptureDevices.clear();
std::unique_lock<std::mutex> plock{pulse_lock};
try {
std::unique_lock<std::mutex> plock{pulse_lock};
pa_context *context{connect_context(plock, AL_FALSE)};
if(!context) return;
pa_context *context{connect_context(plock)};
pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS |
PA_STREAM_DONT_MOVE};
const pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE |
PA_STREAM_FIX_CHANNELS | PA_STREAM_DONT_MOVE};
pa_sample_spec spec{};
spec.format = PA_SAMPLE_S16NE;
spec.rate = 44100;
spec.channels = 1;
pa_sample_spec spec{};
spec.format = PA_SAMPLE_S16NE;
spec.rate = 44100;
spec.channels = 1;
pa_stream *stream{pulse_connect_stream(nullptr, plock, context, flags, nullptr, &spec, nullptr,
BackendType::Capture)};
if(stream)
{
pa_stream *stream{pulse_connect_stream(nullptr, plock, context, flags, nullptr, &spec, nullptr,
BackendType::Capture)};
pa_operation *op{pa_context_get_source_info_by_name(context,
pa_stream_get_device_name(stream), device_source_callback, nullptr)};
wait_for_operation(op, plock);
@ -534,13 +519,16 @@ void probeCaptureDevices()
pa_stream_disconnect(stream);
pa_stream_unref(stream);
stream = nullptr;
op = pa_context_get_source_info_list(context, device_source_callback, nullptr);
wait_for_operation(op, plock);
pa_context_disconnect(context);
pa_context_unref(context);
}
catch(std::exception &e) {
ERR("Error enumerating devices: %s\n", e.what());
}
pa_operation *op{pa_context_get_source_info_list(context, device_source_callback, nullptr)};
wait_for_operation(op, plock);
pa_context_disconnect(context);
pa_context_unref(context);
}
@ -763,16 +751,16 @@ ALCenum PulsePlayback::open(const ALCchar *name)
{ return entry.name == name; }
);
if(iter == PlaybackDevices.cend())
return ALC_INVALID_VALUE;
throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
pulse_name = iter->device_name.c_str();
dev_name = iter->name.c_str();
}
mContext = pulse_open(&PulsePlayback::contextStateCallbackC, this);
if(!mContext) return ALC_INVALID_VALUE;
std::unique_lock<std::mutex> plock{pulse_lock};
mContext = connect_context(plock);
pa_context_set_state_callback(mContext, &PulsePlayback::contextStateCallbackC, this);
pa_stream_flags_t flags{PA_STREAM_FIX_FORMAT | PA_STREAM_FIX_RATE | PA_STREAM_FIX_CHANNELS};
if(!GetConfigValueBool(nullptr, "pulse", "allow-moves", 1))
flags |= PA_STREAM_DONT_MOVE;
@ -782,21 +770,15 @@ ALCenum PulsePlayback::open(const ALCchar *name)
spec.rate = 44100;
spec.channels = 2;
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
if(!pulse_name)
{
pulse_name = getenv("ALSOFT_PULSE_DEFAULT");
if(pulse_name && !pulse_name[0]) pulse_name = nullptr;
}
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
mStream = pulse_connect_stream(pulse_name, plock, mContext, flags, nullptr, &spec, nullptr,
BackendType::Playback);
if(!mStream)
{
plock.unlock();
pulse_close(mContext, mStream);
mContext = nullptr;
return ALC_INVALID_VALUE;
}
pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
mFrameSize = pa_frame_size(pa_stream_get_sample_spec(mStream));
@ -877,10 +859,7 @@ ALCboolean PulsePlayback::reset()
mSpec.channels = mDevice->channelsFromFmt();
if(pa_sample_spec_valid(&mSpec) == 0)
{
ERR("Invalid sample format\n");
return ALC_FALSE;
}
throw al::backend_exception{ALC_INVALID_VALUE, "Invalid sample spec"};
const char *mapname{nullptr};
pa_channel_map chanmap;
@ -912,10 +891,8 @@ ALCboolean PulsePlayback::reset()
break;
}
if(!pa_channel_map_parse(&chanmap, mapname))
{
ERR("Failed to build channel map for %s\n", DevFmtChannelsString(mDevice->FmtChans));
return ALC_FALSE;
}
throw al::backend_exception{ALC_INVALID_VALUE, "Could not build channel map for %s",
DevFmtChannelsString(mDevice->FmtChans)};
SetDefaultWFXChannelOrder(mDevice);
mAttr.maxlength = -1;
@ -926,7 +903,6 @@ ALCboolean PulsePlayback::reset()
mStream = pulse_connect_stream(mDeviceName.c_str(), plock, mContext, flags, &mAttr, &mSpec,
&chanmap, BackendType::Playback);
if(!mStream) return ALC_FALSE;
pa_stream_set_state_callback(mStream, &PulsePlayback::streamStateCallbackC, this);
pa_stream_set_moved_callback(mStream, &PulsePlayback::streamMovedCallbackC, this);
@ -1152,16 +1128,16 @@ ALCenum PulseCapture::open(const ALCchar *name)
{ return entry.name == name; }
);
if(iter == CaptureDevices.cend())
return ALC_INVALID_VALUE;
throw al::backend_exception{ALC_INVALID_VALUE, "Device name \"%s\" not found", name};
pulse_name = iter->device_name.c_str();
mDevice->DeviceName = iter->name;
}
mContext = pulse_open(&PulseCapture::contextStateCallbackC, this);
if(!mContext) return ALC_INVALID_VALUE;
std::unique_lock<std::mutex> plock{pulse_lock};
mContext = connect_context(plock);
pa_context_set_state_callback(mContext, &PulseCapture::contextStateCallbackC, this);
switch(mDevice->FmtType)
{
case DevFmtUByte:
@ -1179,8 +1155,8 @@ ALCenum PulseCapture::open(const ALCchar *name)
case DevFmtByte:
case DevFmtUShort:
case DevFmtUInt:
ERR("%s capture samples not supported\n", DevFmtTypeString(mDevice->FmtType));
return ALC_INVALID_VALUE;
throw al::backend_exception{ALC_INVALID_VALUE, "%s capture samples not supported",
DevFmtTypeString(mDevice->FmtType)};
}
const char *mapname{nullptr};
@ -1209,29 +1185,18 @@ ALCenum PulseCapture::open(const ALCchar *name)
mapname = "front-left,front-right,front-center,lfe,rear-left,rear-right,side-left,side-right";
break;
case DevFmtAmbi3D:
ERR("%s capture samples not supported\n", DevFmtChannelsString(mDevice->FmtChans));
return ALC_INVALID_VALUE;
throw al::backend_exception{ALC_INVALID_VALUE, "%s capture samples not supported",
DevFmtChannelsString(mDevice->FmtChans)};
}
if(!pa_channel_map_parse(&chanmap, mapname))
{
ERR("Failed to build channel map for %s\n", DevFmtChannelsString(mDevice->FmtChans));
return ALC_INVALID_VALUE;
}
throw al::backend_exception{ALC_INVALID_VALUE, "Could not build channel map for %s",
DevFmtChannelsString(mDevice->FmtChans)};
mSpec.rate = mDevice->Frequency;
mSpec.channels = mDevice->channelsFromFmt();
if(pa_sample_spec_valid(&mSpec) == 0)
{
ERR("Invalid sample format\n");
return ALC_INVALID_VALUE;
}
if(!pa_channel_map_init_auto(&chanmap, mSpec.channels, PA_CHANNEL_MAP_WAVEEX))
{
ERR("Couldn't build map for channel count (%d)!\n", mSpec.channels);
return ALC_INVALID_VALUE;
}
throw al::backend_exception{ALC_INVALID_VALUE, "Invalid sample format"};
ALuint samples{mDevice->BufferSize};
samples = maxu(samples, 100 * mDevice->Frequency / 1000);
@ -1249,7 +1214,6 @@ ALCenum PulseCapture::open(const ALCchar *name)
TRACE("Connecting to \"%s\"\n", pulse_name ? pulse_name : "(default)");
mStream = pulse_connect_stream(pulse_name, plock, mContext, flags, &mAttr, &mSpec, &chanmap,
BackendType::Capture);
if(!mStream) return ALC_INVALID_VALUE;
pa_stream_set_moved_callback(mStream, &PulseCapture::streamMovedCallbackC, this);
pa_stream_set_state_callback(mStream, &PulseCapture::streamStateCallbackC, this);
@ -1437,15 +1401,16 @@ bool PulseBackendFactory::init()
if(!GetConfigValueBool(nullptr, "pulse", "spawn-server", 1))
pulse_ctx_flags |= PA_CONTEXT_NOAUTOSPAWN;
std::unique_lock<std::mutex> plock{pulse_lock};
pa_context *context{connect_context(plock, AL_TRUE)};
if(!context) return false;
pa_context_disconnect(context);
pa_context_unref(context);
return true;
try {
std::unique_lock<std::mutex> plock{pulse_lock};
pa_context *context{connect_context(plock)};
pa_context_disconnect(context);
pa_context_unref(context);
return true;
}
catch(...) {
return false;
}
}
bool PulseBackendFactory::querySupport(BackendType type)