Browse Source

audio/pw: convert to QOM lifecycle methods

Migrate the PipeWire audio backend from the legacy driver init/fini
callbacks to proper QOM realize and finalize methods.

The pwaudio struct fields are now embedded directly in the AudioPw
QOM object instead of being allocated separately as drv_opaque. This
allows accessing the backend state through proper QOM type casting
with AUDIO_PW() rather than casting drv_opaque pointers.

The PipeWire thread loop and context are now managed through the QOM
lifecycle, with initialization in realize and cleanup in finalize.

Reviewed-by: Mark Cave-Ayland <mark.caveayland@nutanix.com>
Reviewed-by: Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
master
Marc-André Lureau 7 months ago
parent
commit
cd5f9b9c02
  1. 119
      audio/pwaudio.c

119
audio/pwaudio.c

@ -31,36 +31,27 @@
#define TYPE_AUDIO_PW "audio-pipewire"
OBJECT_DECLARE_SIMPLE_TYPE(AudioPw, AUDIO_PW)
static AudioBackendClass *audio_pw_parent_class;
struct AudioPw {
AudioMixengBackend parent_obj;
};
static struct audio_driver pw_audio_driver;
struct pw_thread_loop *thread_loop;
struct pw_context *context;
static void audio_pw_class_init(ObjectClass *klass, const void *data)
{
AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_CLASS(klass);
struct pw_core *core;
struct spa_hook core_listener;
int last_seq, pending_seq, error;
};
k->driver = &pw_audio_driver;
}
static struct audio_driver pw_audio_driver;
typedef struct pwvolume {
uint32_t channels;
float values[SPA_AUDIO_MAX_CHANNELS];
} pwvolume;
typedef struct pwaudio {
Audiodev *dev;
struct pw_thread_loop *thread_loop;
struct pw_context *context;
struct pw_core *core;
struct spa_hook core_listener;
int last_seq, pending_seq, error;
} pwaudio;
typedef struct PWVoice {
pwaudio *g;
struct pw_stream *stream;
struct spa_hook stream_listener;
struct spa_audio_info_raw info;
@ -236,9 +227,9 @@ static const struct pw_stream_events playback_stream_events = {
static size_t
qpw_read(HWVoiceIn *hw, void *data, size_t len)
{
AudioPw *c = AUDIO_PW(hw->s);
PWVoiceIn *pw = (PWVoiceIn *) hw;
PWVoice *v = &pw->v;
pwaudio *c = v->g;
const char *error = NULL;
size_t l;
int32_t avail;
@ -273,9 +264,9 @@ done_unlock:
static size_t qpw_buffer_get_free(HWVoiceOut *hw)
{
AudioPw *c = AUDIO_PW(hw->s);
PWVoiceOut *pw = (PWVoiceOut *)hw;
PWVoice *v = &pw->v;
pwaudio *c = v->g;
const char *error = NULL;
int32_t filled, avail;
uint32_t index;
@ -298,9 +289,9 @@ done_unlock:
static size_t
qpw_write(HWVoiceOut *hw, void *data, size_t len)
{
AudioPw *c = AUDIO_PW(hw->s);
PWVoiceOut *pw = (PWVoiceOut *) hw;
PWVoice *v = &pw->v;
pwaudio *c = v->g;
const char *error = NULL;
int32_t filled, avail;
uint32_t index;
@ -434,7 +425,7 @@ pw_to_audfmt(enum spa_audio_format fmt, int *endianness,
}
static int
qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name,
qpw_stream_new(AudioPw *c, PWVoice *v, const char *stream_name,
const char *name, enum spa_direction dir)
{
int res;
@ -452,8 +443,8 @@ qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name,
}
/* 75% of the timer period for faster updates */
buf_samples = (uint64_t)v->g->dev->timer_period * v->info.rate
* 3 / 4 / 1000000;
buf_samples = (uint64_t)AUDIO_MIXENG_BACKEND(c)->dev->timer_period
* v->info.rate * 3 / 4 / 1000000;
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%" PRIu64 "/%u",
buf_samples, v->info.rate);
@ -535,11 +526,11 @@ qpw_set_position(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS])
static int
qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
{
AudioPw *c = AUDIO_PW(hw->s);
PWVoiceOut *pw = (PWVoiceOut *) hw;
PWVoice *v = &pw->v;
struct audsettings obt_as = *as;
pwaudio *c = v->g = drv_opaque;
AudiodevPipewireOptions *popts = &c->dev->u.pipewire;
AudiodevPipewireOptions *popts = &AUDIO_MIXENG_BACKEND(c)->dev->u.pipewire;
AudiodevPipewirePerDirectionOptions *ppdo = popts->out;
int r;
@ -554,11 +545,11 @@ qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
pw_to_audfmt(v->info.format, &obt_as.endianness, &v->frame_size);
v->frame_size *= as->nchannels;
v->req = (uint64_t)c->dev->timer_period * v->info.rate
v->req = (uint64_t)AUDIO_MIXENG_BACKEND(c)->dev->timer_period * v->info.rate
* 1 / 2 / 1000000 * v->frame_size;
/* call the function that creates a new stream for playback */
r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id,
r = qpw_stream_new(c, v, ppdo->stream_name ?: AUDIO_MIXENG_BACKEND(c)->dev->id,
ppdo->name, SPA_DIRECTION_OUTPUT);
if (r < 0) {
pw_thread_loop_unlock(c->thread_loop);
@ -582,11 +573,11 @@ qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
static int
qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
{
AudioPw *c = AUDIO_PW(hw->s);
PWVoiceIn *pw = (PWVoiceIn *) hw;
PWVoice *v = &pw->v;
struct audsettings obt_as = *as;
pwaudio *c = v->g = drv_opaque;
AudiodevPipewireOptions *popts = &c->dev->u.pipewire;
AudiodevPipewireOptions *popts = &AUDIO_MIXENG_BACKEND(c)->dev->u.pipewire;
AudiodevPipewirePerDirectionOptions *ppdo = popts->in;
int r;
@ -602,7 +593,7 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
v->frame_size *= as->nchannels;
/* call the function that creates a new stream for recording */
r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id,
r = qpw_stream_new(c, v, ppdo->stream_name ? : AUDIO_MIXENG_BACKEND(c)->dev->id,
ppdo->name, SPA_DIRECTION_INPUT);
if (r < 0) {
pw_thread_loop_unlock(c->thread_loop);
@ -621,10 +612,8 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}
static void
qpw_voice_fini(PWVoice *v)
qpw_voice_fini(AudioPw *c, PWVoice *v)
{
pwaudio *c = v->g;
if (!v->stream) {
return;
}
@ -637,19 +626,18 @@ qpw_voice_fini(PWVoice *v)
static void
qpw_fini_out(HWVoiceOut *hw)
{
qpw_voice_fini(&PW_VOICE_OUT(hw)->v);
qpw_voice_fini(AUDIO_PW(hw->s), &PW_VOICE_OUT(hw)->v);
}
static void
qpw_fini_in(HWVoiceIn *hw)
{
qpw_voice_fini(&PW_VOICE_IN(hw)->v);
qpw_voice_fini(AUDIO_PW(hw->s), &PW_VOICE_IN(hw)->v);
}
static void
qpw_voice_set_enabled(PWVoice *v, bool enable)
qpw_voice_set_enabled(AudioPw *c, PWVoice *v, bool enable)
{
pwaudio *c = v->g;
pw_thread_loop_lock(c->thread_loop);
pw_stream_set_active(v->stream, enable);
pw_thread_loop_unlock(c->thread_loop);
@ -658,19 +646,18 @@ qpw_voice_set_enabled(PWVoice *v, bool enable)
static void
qpw_enable_out(HWVoiceOut *hw, bool enable)
{
qpw_voice_set_enabled(&PW_VOICE_OUT(hw)->v, enable);
qpw_voice_set_enabled(AUDIO_PW(hw->s), &PW_VOICE_OUT(hw)->v, enable);
}
static void
qpw_enable_in(HWVoiceIn *hw, bool enable)
{
qpw_voice_set_enabled(&PW_VOICE_IN(hw)->v, enable);
qpw_voice_set_enabled(AUDIO_PW(hw->s), &PW_VOICE_IN(hw)->v, enable);
}
static void
qpw_voice_set_volume(PWVoice *v, Volume *vol)
qpw_voice_set_volume(AudioPw *c, PWVoice *v, Volume *vol)
{
pwaudio *c = v->g;
int i, ret;
pw_thread_loop_lock(c->thread_loop);
@ -693,16 +680,16 @@ qpw_voice_set_volume(PWVoice *v, Volume *vol)
static void
qpw_volume_out(HWVoiceOut *hw, Volume *vol)
{
qpw_voice_set_volume(&PW_VOICE_OUT(hw)->v, vol);
qpw_voice_set_volume(AUDIO_PW(hw->s), &PW_VOICE_OUT(hw)->v, vol);
}
static void
qpw_volume_in(HWVoiceIn *hw, Volume *vol)
{
qpw_voice_set_volume(&PW_VOICE_IN(hw)->v, vol);
qpw_voice_set_volume(AUDIO_PW(hw->s), &PW_VOICE_IN(hw)->v, vol);
}
static int wait_resync(pwaudio *pw)
static int wait_resync(AudioPw *pw)
{
int res;
pw->pending_seq = pw_core_sync(pw->core, PW_ID_CORE, pw->pending_seq);
@ -725,7 +712,7 @@ static int wait_resync(pwaudio *pw)
static void
on_core_error(void *data, uint32_t id, int seq, int res, const char *message)
{
pwaudio *pw = data;
AudioPw *pw = data;
error_report("error id:%u seq:%d res:%d (%s): %s",
id, seq, res, spa_strerror(res), message);
@ -737,7 +724,7 @@ on_core_error(void *data, uint32_t id, int seq, int res, const char *message)
static void
on_core_done(void *data, uint32_t id, int seq)
{
pwaudio *pw = data;
AudioPw *pw = data;
assert(id == PW_ID_CORE);
pw->last_seq = seq;
if (pw->pending_seq == seq) {
@ -752,17 +739,20 @@ static const struct pw_core_events core_events = {
.error = on_core_error,
};
static void *
qpw_audio_init(Audiodev *dev, Error **errp)
static bool
audio_pw_realize(AudioBackend *abe, Audiodev *dev, Error **errp)
{
g_autofree pwaudio *pw = g_new0(pwaudio, 1);
AudioPw *pw = AUDIO_PW(abe);
assert(dev->driver == AUDIODEV_DRIVER_PIPEWIRE);
trace_pw_audio_init();
if (!audio_pw_parent_class->realize(abe, dev, errp)) {
return false;
}
pw_init(NULL, NULL);
pw->dev = dev;
pw->thread_loop = pw_thread_loop_new("PipeWire thread loop", NULL);
if (pw->thread_loop == NULL) {
error_setg_errno(errp, errno, "Could not create PipeWire loop");
@ -801,8 +791,7 @@ qpw_audio_init(Audiodev *dev, Error **errp)
}
pw_thread_loop_unlock(pw->thread_loop);
return g_steal_pointer(&pw);
return true;
fail:
if (pw->thread_loop) {
@ -810,13 +799,13 @@ fail:
}
g_clear_pointer(&pw->context, pw_context_destroy);
g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy);
return NULL;
return false;
}
static void
qpw_audio_fini(void *opaque)
audio_pw_finalize(Object *obj)
{
pwaudio *pw = opaque;
AudioPw *pw = AUDIO_PW(obj);
if (pw->thread_loop) {
pw_thread_loop_stop(pw->thread_loop);
@ -831,9 +820,7 @@ qpw_audio_fini(void *opaque)
if (pw->context) {
pw_context_destroy(pw->context);
}
pw_thread_loop_destroy(pw->thread_loop);
g_free(pw);
g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy);
}
static struct audio_pcm_ops qpw_pcm_ops = {
@ -855,8 +842,6 @@ static struct audio_pcm_ops qpw_pcm_ops = {
static struct audio_driver pw_audio_driver = {
.name = "pipewire",
.init = qpw_audio_init,
.fini = qpw_audio_fini,
.pcm_ops = &qpw_pcm_ops,
.max_voices_out = INT_MAX,
.max_voices_in = INT_MAX,
@ -864,12 +849,24 @@ static struct audio_driver pw_audio_driver = {
.voice_size_in = sizeof(PWVoiceIn),
};
static void audio_pw_class_init(ObjectClass *klass, const void *data)
{
AudioBackendClass *b = AUDIO_BACKEND_CLASS(klass);
AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_CLASS(klass);
audio_pw_parent_class = AUDIO_BACKEND_CLASS(object_class_get_parent(klass));
b->realize = audio_pw_realize;
k->driver = &pw_audio_driver;
}
static const TypeInfo audio_types[] = {
{
.name = TYPE_AUDIO_PW,
.parent = TYPE_AUDIO_MIXENG_BACKEND,
.instance_size = sizeof(AudioPw),
.class_init = audio_pw_class_init,
.instance_finalize = audio_pw_finalize,
},
};

Loading…
Cancel
Save