[PATCH 09/43] audio/pw: convert to QOM lifecycle methods

marcandre.lureau@redhat.com posted 43 patches 1 week, 6 days ago
Maintainers: Gerd Hoffmann <kraxel@redhat.com>, Christian Schoenebeck <qemu_oss@crudebyte.com>, "Marc-André Lureau" <marcandre.lureau@redhat.com>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, Akihiko Odaki <odaki@rsg.ci.i.u-tokyo.ac.jp>, Thomas Huth <huth@tuxfamily.org>, Alexandre Ratchov <alex@caoua.org>, Laurent Vivier <laurent@vivier.eu>, Manos Pitsidianakis <manos.pitsidianakis@linaro.org>, "Michael S. Tsirkin" <mst@redhat.com>, Alistair Francis <alistair@alistair23.me>, "Edgar E. Iglesias" <edgar.iglesias@gmail.com>, Peter Maydell <peter.maydell@linaro.org>
[PATCH 09/43] audio/pw: convert to QOM lifecycle methods
Posted by marcandre.lureau@redhat.com 1 week, 6 days ago
From: Marc-André Lureau <marcandre.lureau@redhat.com>

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.

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
---
 audio/pwaudio.c | 119 +++++++++++++++++++++++-------------------------
 1 file changed, 58 insertions(+), 61 deletions(-)

diff --git a/audio/pwaudio.c b/audio/pwaudio.c
index ed48a9754fe..ddf5f2c70f7 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;
-};
 
-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,11 +849,23 @@ 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_pw_info = {
     .name = TYPE_AUDIO_PW,
     .parent = TYPE_AUDIO_MIXENG_BACKEND,
     .instance_size = sizeof(AudioPw),
     .class_init = audio_pw_class_init,
+    .instance_finalize = audio_pw_finalize,
 };
 
 static void
-- 
2.52.0