From: Marc-André Lureau <[email protected]> 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 <[email protected]> Reviewed-by: Akihiko Odaki <[email protected]> Signed-off-by: Marc-André Lureau <[email protected]> --- audio/pwaudio.c | 119 +++++++++++++++++++++++------------------------- 1 file changed, 58 insertions(+), 61 deletions(-) diff --git a/audio/pwaudio.c b/audio/pwaudio.c index 3b524dfea5a..ca61a1b4ef5 100644 --- a/audio/pwaudio.c +++ b/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, }, }; -- 2.53.0
