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>
Marc-André Lureau committed
Jan 16, 2026 at 18:44 UTC
cd5f9b9c0237936387f801aa01f4a6a0dc203341
1 file changed
+58
-61
audio/pwaudio.c
+58
-61
@@ -31,36 +31,27 @@
31
#define TYPE_AUDIO_PW "audio-pipewire"
32
OBJECT_DECLARE_SIMPLE_TYPE(AudioPw, AUDIO_PW)
33
34
+static AudioBackendClass *audio_pw_parent_class;
35
+
36
struct AudioPw {
37
AudioMixengBackend parent_obj;
36
-};
38
38
-static struct audio_driver pw_audio_driver;
39
+ struct pw_thread_loop *thread_loop;
40
+ struct pw_context *context;
41
40
-static void audio_pw_class_init(ObjectClass *klass, const void *data)
41
-{
42
- AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_CLASS(klass);
42
+ struct pw_core *core;
43
+ struct spa_hook core_listener;
44
+ int last_seq, pending_seq, error;
45
+};
46
44
- k->driver = &pw_audio_driver;
45
-}
47
+static struct audio_driver pw_audio_driver;
48
49
typedef struct pwvolume {
50
uint32_t channels;
51
float values[SPA_AUDIO_MAX_CHANNELS];
52
} pwvolume;
53
52
-typedef struct pwaudio {
53
- Audiodev *dev;
54
- struct pw_thread_loop *thread_loop;
55
- struct pw_context *context;
56
-
57
- struct pw_core *core;
58
- struct spa_hook core_listener;
59
- int last_seq, pending_seq, error;
60
-} pwaudio;
61
-
54
typedef struct PWVoice {
63
- pwaudio *g;
55
struct pw_stream *stream;
56
struct spa_hook stream_listener;
57
struct spa_audio_info_raw info;
@@ -236,9 +227,9 @@ static const struct pw_stream_events playback_stream_events = {
227
static size_t
228
qpw_read(HWVoiceIn *hw, void *data, size_t len)
229
{
230
+ AudioPw *c = AUDIO_PW(hw->s);
231
PWVoiceIn *pw = (PWVoiceIn *) hw;
232
PWVoice *v = &pw->v;
241
- pwaudio *c = v->g;
233
const char *error = NULL;
234
size_t l;
235
int32_t avail;
@@ -273,9 +264,9 @@ done_unlock:
264
265
static size_t qpw_buffer_get_free(HWVoiceOut *hw)
266
{
267
+ AudioPw *c = AUDIO_PW(hw->s);
268
PWVoiceOut *pw = (PWVoiceOut *)hw;
269
PWVoice *v = &pw->v;
278
- pwaudio *c = v->g;
270
const char *error = NULL;
271
int32_t filled, avail;
272
uint32_t index;
@@ -298,9 +289,9 @@ done_unlock:
289
static size_t
290
qpw_write(HWVoiceOut *hw, void *data, size_t len)
291
{
292
+ AudioPw *c = AUDIO_PW(hw->s);
293
PWVoiceOut *pw = (PWVoiceOut *) hw;
294
PWVoice *v = &pw->v;
303
- pwaudio *c = v->g;
295
const char *error = NULL;
296
int32_t filled, avail;
297
uint32_t index;
@@ -434,7 +425,7 @@ pw_to_audfmt(enum spa_audio_format fmt, int *endianness,
425
}
426
427
static int
437
-qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name,
428
+qpw_stream_new(AudioPw *c, PWVoice *v, const char *stream_name,
429
const char *name, enum spa_direction dir)
430
{
431
int res;
@@ -452,8 +443,8 @@ qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name,
443
}
444
445
/* 75% of the timer period for faster updates */
455
- buf_samples = (uint64_t)v->g->dev->timer_period * v->info.rate
456
- * 3 / 4 / 1000000;
446
+ buf_samples = (uint64_t)AUDIO_MIXENG_BACKEND(c)->dev->timer_period
447
+ * v->info.rate * 3 / 4 / 1000000;
448
pw_properties_setf(props, PW_KEY_NODE_LATENCY, "%" PRIu64 "/%u",
449
buf_samples, v->info.rate);
450
@@ -535,11 +526,11 @@ qpw_set_position(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS])
526
static int
527
qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
528
{
529
+ AudioPw *c = AUDIO_PW(hw->s);
530
PWVoiceOut *pw = (PWVoiceOut *) hw;
531
PWVoice *v = &pw->v;
532
struct audsettings obt_as = *as;
541
- pwaudio *c = v->g = drv_opaque;
542
- AudiodevPipewireOptions *popts = &c->dev->u.pipewire;
533
+ AudiodevPipewireOptions *popts = &AUDIO_MIXENG_BACKEND(c)->dev->u.pipewire;
534
AudiodevPipewirePerDirectionOptions *ppdo = popts->out;
535
int r;
536
@@ -554,11 +545,11 @@ qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
545
pw_to_audfmt(v->info.format, &obt_as.endianness, &v->frame_size);
546
v->frame_size *= as->nchannels;
547
557
- v->req = (uint64_t)c->dev->timer_period * v->info.rate
548
+ v->req = (uint64_t)AUDIO_MIXENG_BACKEND(c)->dev->timer_period * v->info.rate
549
* 1 / 2 / 1000000 * v->frame_size;
550
551
/* call the function that creates a new stream for playback */
561
- r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id,
552
+ r = qpw_stream_new(c, v, ppdo->stream_name ?: AUDIO_MIXENG_BACKEND(c)->dev->id,
553
ppdo->name, SPA_DIRECTION_OUTPUT);
554
if (r < 0) {
555
pw_thread_loop_unlock(c->thread_loop);
@@ -582,11 +573,11 @@ qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque)
573
static int
574
qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
575
{
576
+ AudioPw *c = AUDIO_PW(hw->s);
577
PWVoiceIn *pw = (PWVoiceIn *) hw;
578
PWVoice *v = &pw->v;
579
struct audsettings obt_as = *as;
588
- pwaudio *c = v->g = drv_opaque;
589
- AudiodevPipewireOptions *popts = &c->dev->u.pipewire;
580
+ AudiodevPipewireOptions *popts = &AUDIO_MIXENG_BACKEND(c)->dev->u.pipewire;
581
AudiodevPipewirePerDirectionOptions *ppdo = popts->in;
582
int r;
583
@@ -602,7 +593,7 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
593
v->frame_size *= as->nchannels;
594
595
/* call the function that creates a new stream for recording */
605
- r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id,
596
+ r = qpw_stream_new(c, v, ppdo->stream_name ? : AUDIO_MIXENG_BACKEND(c)->dev->id,
597
ppdo->name, SPA_DIRECTION_INPUT);
598
if (r < 0) {
599
pw_thread_loop_unlock(c->thread_loop);
@@ -621,10 +612,8 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
612
}
613
614
static void
624
-qpw_voice_fini(PWVoice *v)
615
+qpw_voice_fini(AudioPw *c, PWVoice *v)
616
{
626
- pwaudio *c = v->g;
627
-
617
if (!v->stream) {
618
return;
619
}
@@ -637,19 +626,18 @@ qpw_voice_fini(PWVoice *v)
626
static void
627
qpw_fini_out(HWVoiceOut *hw)
628
{
640
- qpw_voice_fini(&PW_VOICE_OUT(hw)->v);
629
+ qpw_voice_fini(AUDIO_PW(hw->s), &PW_VOICE_OUT(hw)->v);
630
}
631
632
static void
633
qpw_fini_in(HWVoiceIn *hw)
634
{
646
- qpw_voice_fini(&PW_VOICE_IN(hw)->v);
635
+ qpw_voice_fini(AUDIO_PW(hw->s), &PW_VOICE_IN(hw)->v);
636
}
637
638
static void
650
-qpw_voice_set_enabled(PWVoice *v, bool enable)
639
+qpw_voice_set_enabled(AudioPw *c, PWVoice *v, bool enable)
640
{
652
- pwaudio *c = v->g;
641
pw_thread_loop_lock(c->thread_loop);
642
pw_stream_set_active(v->stream, enable);
643
pw_thread_loop_unlock(c->thread_loop);
@@ -658,19 +646,18 @@ qpw_voice_set_enabled(PWVoice *v, bool enable)
646
static void
647
qpw_enable_out(HWVoiceOut *hw, bool enable)
648
{
661
- qpw_voice_set_enabled(&PW_VOICE_OUT(hw)->v, enable);
649
+ qpw_voice_set_enabled(AUDIO_PW(hw->s), &PW_VOICE_OUT(hw)->v, enable);
650
}
651
652
static void
653
qpw_enable_in(HWVoiceIn *hw, bool enable)
654
{
667
- qpw_voice_set_enabled(&PW_VOICE_IN(hw)->v, enable);
655
+ qpw_voice_set_enabled(AUDIO_PW(hw->s), &PW_VOICE_IN(hw)->v, enable);
656
}
657
658
static void
671
-qpw_voice_set_volume(PWVoice *v, Volume *vol)
659
+qpw_voice_set_volume(AudioPw *c, PWVoice *v, Volume *vol)
660
{
673
- pwaudio *c = v->g;
661
int i, ret;
662
663
pw_thread_loop_lock(c->thread_loop);
@@ -693,16 +680,16 @@ qpw_voice_set_volume(PWVoice *v, Volume *vol)
680
static void
681
qpw_volume_out(HWVoiceOut *hw, Volume *vol)
682
{
696
- qpw_voice_set_volume(&PW_VOICE_OUT(hw)->v, vol);
683
+ qpw_voice_set_volume(AUDIO_PW(hw->s), &PW_VOICE_OUT(hw)->v, vol);
684
}
685
686
static void
687
qpw_volume_in(HWVoiceIn *hw, Volume *vol)
688
{
702
- qpw_voice_set_volume(&PW_VOICE_IN(hw)->v, vol);
689
+ qpw_voice_set_volume(AUDIO_PW(hw->s), &PW_VOICE_IN(hw)->v, vol);
690
}
691
705
-static int wait_resync(pwaudio *pw)
692
+static int wait_resync(AudioPw *pw)
693
{
694
int res;
695
pw->pending_seq = pw_core_sync(pw->core, PW_ID_CORE, pw->pending_seq);
@@ -725,7 +712,7 @@ static int wait_resync(pwaudio *pw)
712
static void
713
on_core_error(void *data, uint32_t id, int seq, int res, const char *message)
714
{
728
- pwaudio *pw = data;
715
+ AudioPw *pw = data;
716
717
error_report("error id:%u seq:%d res:%d (%s): %s",
718
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)
724
static void
725
on_core_done(void *data, uint32_t id, int seq)
726
{
740
- pwaudio *pw = data;
727
+ AudioPw *pw = data;
728
assert(id == PW_ID_CORE);
729
pw->last_seq = seq;
730
if (pw->pending_seq == seq) {
@@ -752,17 +739,20 @@ static const struct pw_core_events core_events = {
739
.error = on_core_error,
740
};
741
755
-static void *
756
-qpw_audio_init(Audiodev *dev, Error **errp)
742
+static bool
743
+audio_pw_realize(AudioBackend *abe, Audiodev *dev, Error **errp)
744
{
758
- g_autofree pwaudio *pw = g_new0(pwaudio, 1);
745
+ AudioPw *pw = AUDIO_PW(abe);
746
747
assert(dev->driver == AUDIODEV_DRIVER_PIPEWIRE);
748
trace_pw_audio_init();
749
750
+ if (!audio_pw_parent_class->realize(abe, dev, errp)) {
751
+ return false;
752
+ }
753
+
754
pw_init(NULL, NULL);
755
765
- pw->dev = dev;
756
pw->thread_loop = pw_thread_loop_new("PipeWire thread loop", NULL);
757
if (pw->thread_loop == NULL) {
758
error_setg_errno(errp, errno, "Could not create PipeWire loop");
@@ -801,8 +791,7 @@ qpw_audio_init(Audiodev *dev, Error **errp)
791
}
792
793
pw_thread_loop_unlock(pw->thread_loop);
804
-
805
- return g_steal_pointer(&pw);
794
+ return true;
795
796
fail:
797
if (pw->thread_loop) {
@@ -810,13 +799,13 @@ fail:
799
}
800
g_clear_pointer(&pw->context, pw_context_destroy);
801
g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy);
813
- return NULL;
802
+ return false;
803
}
804
805
static void
817
-qpw_audio_fini(void *opaque)
806
+audio_pw_finalize(Object *obj)
807
{
819
- pwaudio *pw = opaque;
808
+ AudioPw *pw = AUDIO_PW(obj);
809
810
if (pw->thread_loop) {
811
pw_thread_loop_stop(pw->thread_loop);
@@ -831,9 +820,7 @@ qpw_audio_fini(void *opaque)
820
if (pw->context) {
821
pw_context_destroy(pw->context);
822
}
834
- pw_thread_loop_destroy(pw->thread_loop);
835
-
836
- g_free(pw);
823
+ g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy);
824
}
825
826
static struct audio_pcm_ops qpw_pcm_ops = {
@@ -855,8 +842,6 @@ static struct audio_pcm_ops qpw_pcm_ops = {
842
843
static struct audio_driver pw_audio_driver = {
844
.name = "pipewire",
858
- .init = qpw_audio_init,
859
- .fini = qpw_audio_fini,
845
.pcm_ops = &qpw_pcm_ops,
846
.max_voices_out = INT_MAX,
847
.max_voices_in = INT_MAX,
@@ -864,12 +849,24 @@ static struct audio_driver pw_audio_driver = {
849
.voice_size_in = sizeof(PWVoiceIn),
850
};
851
852
+static void audio_pw_class_init(ObjectClass *klass, const void *data)
853
+{
854
+ AudioBackendClass *b = AUDIO_BACKEND_CLASS(klass);
855
+ AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_CLASS(klass);
856
+
857
+ audio_pw_parent_class = AUDIO_BACKEND_CLASS(object_class_get_parent(klass));
858
+
859
+ b->realize = audio_pw_realize;
860
+ k->driver = &pw_audio_driver;
861
+}
862
+
863
static const TypeInfo audio_types[] = {
864
{
865
.name = TYPE_AUDIO_PW,
866
.parent = TYPE_AUDIO_MIXENG_BACKEND,
867
.instance_size = sizeof(AudioPw),
868
.class_init = audio_pw_class_init,
869
+ .instance_finalize = audio_pw_finalize,
870
},
871
};
872