@samitouri / QOSamiQemu / commits / 5bb7016727

audio/pa: convert to QOM lifecycle methods

Migrate the PulseAudio backend from the legacy driver init/fini callbacks to proper QOM realize and finalize methods. The paaudio struct fields are now embedded directly in the AudioPa QOM object instead of being allocated separately as drv_opaque. This allows accessing the backend state through proper QOM type casting with AUDIO_PA() rather than casting drv_opaque pointers. The PulseAudio connection is now managed through the QOM lifecycle, with the connection reference acquired in realize and released 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:22 UTC 5bb7016727ac5fcc91efa141d411cafc9dfe7b1e
1 file changed +57 -52
audio/paaudio.c
+57 -52
@@ -14,19 +14,10 @@
14 #define TYPE_AUDIO_PA "audio-pa"
15 OBJECT_DECLARE_SIMPLE_TYPE(AudioPa, AUDIO_PA)
16
17 -struct AudioPa {
18 - AudioMixengBackend parent_obj;
19 -};
17 +static AudioBackendClass *audio_pa_parent_class;
18
19 static struct audio_driver pa_audio_driver;
20
23 -static void audio_pa_class_init(ObjectClass *klass, const void *data)
24 -{
25 - AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_CLASS(klass);
26 -
27 - k->driver = &pa_audio_driver;
28 -}
29 -
21 typedef struct PAConnection {
22 char *server;
23 int refcount;
@@ -36,18 +27,19 @@ typedef struct PAConnection {
27 pa_context *context;
28 } PAConnection;
29
39 -static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns =
40 - QTAILQ_HEAD_INITIALIZER(pa_conns);
30 +struct AudioPa {
31 + AudioMixengBackend parent_obj;
32
42 -typedef struct {
43 - Audiodev *dev;
33 PAConnection *conn;
45 -} paaudio;
34 +};
35 +
36 +static QTAILQ_HEAD(PAConnectionHead, PAConnection) pa_conns =
37 + QTAILQ_HEAD_INITIALIZER(pa_conns);
38
39 typedef struct {
40 HWVoiceOut hw;
41 pa_stream *stream;
50 - paaudio *g;
42 + AudioPa *g;
43 } PAVoiceOut;
44
45 typedef struct {
@@ -55,7 +47,7 @@ typedef struct {
47 pa_stream *stream;
48 const void *read_data;
49 size_t read_length;
58 - paaudio *g;
50 + AudioPa *g;
51 } PAVoiceIn;
52
53 static void qpa_conn_fini(PAConnection *c);
@@ -529,23 +521,25 @@ fail:
521 static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
522 void *drv_opaque)
523 {
524 + AudioMixengBackend *amb = hw->s;
525 + AudioPa *apa = AUDIO_PA(amb);
526 int error;
527 pa_sample_spec ss;
528 pa_buffer_attr ba;
529 struct audsettings obt_as = *as;
530 PAVoiceOut *pa = (PAVoiceOut *) hw;
537 - paaudio *g = pa->g = drv_opaque;
538 - AudiodevPaOptions *popts = &g->dev->u.pa;
531 + AudiodevPaOptions *popts = &amb->dev->u.pa;
532 AudiodevPaPerDirectionOptions *ppdo = popts->out;
540 - PAConnection *c = g->conn;
533 + PAConnection *c = apa->conn;
534
535 + pa->g = apa;
536 ss.format = audfmt_to_pa (as->fmt, as->endianness);
537 ss.channels = as->nchannels;
538 ss.rate = as->freq;
539
540 ba.tlength = pa_usec_to_bytes(ppdo->latency, &ss);
541 ba.minreq = pa_usec_to_bytes(MIN(ppdo->latency >> 2,
548 - (g->dev->timer_period >> 2) * 3), &ss);
542 + (amb->dev->timer_period >> 2) * 3), &ss);
543 ba.maxlength = -1;
544 ba.prebuf = -1;
545
@@ -553,7 +547,7 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
547
548 pa->stream = qpa_simple_new (
549 c,
556 - ppdo->stream_name ?: g->dev->id,
550 + ppdo->stream_name ?: amb->dev->id,
551 PA_STREAM_PLAYBACK,
552 ppdo->name,
553 &ss,
@@ -578,23 +572,25 @@ static int qpa_init_out(HWVoiceOut *hw, struct audsettings *as,
572
573 static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
574 {
575 + AudioMixengBackend *amb = hw->s;
576 + AudioPa *apa = AUDIO_PA(amb);
577 int error;
578 pa_sample_spec ss;
579 pa_buffer_attr ba;
580 struct audsettings obt_as = *as;
581 PAVoiceIn *pa = (PAVoiceIn *) hw;
586 - paaudio *g = pa->g = drv_opaque;
587 - AudiodevPaOptions *popts = &g->dev->u.pa;
582 + AudiodevPaOptions *popts = &amb->dev->u.pa;
583 AudiodevPaPerDirectionOptions *ppdo = popts->in;
589 - PAConnection *c = g->conn;
584 + PAConnection *c = apa->conn;
585
586 + pa->g = apa;
587 ss.format = audfmt_to_pa (as->fmt, as->endianness);
588 ss.channels = as->nchannels;
589 ss.rate = as->freq;
590
595 - ba.fragsize = pa_usec_to_bytes((g->dev->timer_period >> 1) * 3, &ss);
591 + ba.fragsize = pa_usec_to_bytes((amb->dev->timer_period >> 1) * 3, &ss);
592 ba.maxlength = pa_usec_to_bytes(
597 - MAX(ppdo->latency, g->dev->timer_period * 3), &ss);
593 + MAX(ppdo->latency, amb->dev->timer_period * 3), &ss);
594 ba.minreq = -1;
595 ba.prebuf = -1;
596
@@ -602,7 +598,7 @@ static int qpa_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
598
599 pa->stream = qpa_simple_new (
600 c,
605 - ppdo->stream_name ?: g->dev->id,
601 + ppdo->stream_name ?: amb->dev->id,
602 PA_STREAM_RECORD,
603 ppdo->name,
604 &ss,
@@ -834,15 +830,20 @@ fail:
830 return NULL;
831 }
832
837 -static void *qpa_audio_init(Audiodev *dev, Error **errp)
833 +static bool
834 +audio_pa_realize(AudioBackend *abe, Audiodev *dev, Error **errp)
835 {
839 - paaudio *g;
836 + AudioPa *apa = AUDIO_PA(abe);
837 AudiodevPaOptions *popts = &dev->u.pa;
838 const char *server;
839 PAConnection *c;
840
841 assert(dev->driver == AUDIODEV_DRIVER_PA);
842
843 + if (!audio_pa_parent_class->realize(abe, dev, errp)) {
844 + return false;
845 + }
846 +
847 if (!popts->server) {
848 char pidfile[64];
849 char *runtime;
@@ -851,42 +852,38 @@ static void *qpa_audio_init(Audiodev *dev, Error **errp)
852 runtime = getenv("XDG_RUNTIME_DIR");
853 if (!runtime) {
854 error_setg(errp, "XDG_RUNTIME_DIR not set");
854 - return NULL;
855 + return false;
856 }
857 snprintf(pidfile, sizeof(pidfile), "%s/pulse/pid", runtime);
858 if (stat(pidfile, &st) != 0) {
859 error_setg_errno(errp, errno, "could not stat pidfile %s", pidfile);
859 - return NULL;
860 + return false;
861 }
862 }
863
864 qpa_validate_per_direction_opts(dev, popts->in);
865 qpa_validate_per_direction_opts(dev, popts->out);
866
866 - g = g_new0(paaudio, 1);
867 server = popts->server;
868 -
869 - g->dev = dev;
870 -
868 QTAILQ_FOREACH(c, &pa_conns, list) {
869 if (server == NULL || c->server == NULL ?
870 server == c->server :
871 strcmp(server, c->server) == 0) {
875 - g->conn = c;
872 + apa->conn = c;
873 break;
874 }
875 }
879 - if (!g->conn) {
880 - g->conn = qpa_conn_init(server);
876 + if (!apa->conn) {
877 + apa->conn = qpa_conn_init(server);
878 }
882 - if (!g->conn) {
883 - g_free(g);
879 + if (!apa->conn) {
880 error_setg(errp, "could not connect to PulseAudio server");
885 - return NULL;
881 + return false;
882 }
883
888 - ++g->conn->refcount;
889 - return g;
884 + ++apa->conn->refcount;
885 +
886 + return true;
887 }
888
889 static void qpa_conn_fini(PAConnection *c)
@@ -908,16 +905,14 @@ static void qpa_conn_fini(PAConnection *c)
905 g_free(c);
906 }
907
911 -static void qpa_audio_fini (void *opaque)
908 +static void audio_pa_finalize(Object *obj)
909 {
913 - paaudio *g = opaque;
914 - PAConnection *c = g->conn;
910 + AudioPa *apa = AUDIO_PA(obj);
911 + PAConnection *c = apa->conn;
912
916 - if (--c->refcount == 0) {
913 + if (c && --c->refcount == 0) {
914 qpa_conn_fini(c);
915 }
919 -
920 - g_free(g);
916 }
917
918 static struct audio_pcm_ops qpa_pcm_ops = {
@@ -939,8 +934,6 @@ static struct audio_pcm_ops qpa_pcm_ops = {
934
935 static struct audio_driver pa_audio_driver = {
936 .name = "pa",
942 - .init = qpa_audio_init,
943 - .fini = qpa_audio_fini,
937 .pcm_ops = &qpa_pcm_ops,
938 .max_voices_out = INT_MAX,
939 .max_voices_in = INT_MAX,
@@ -948,12 +941,24 @@ static struct audio_driver pa_audio_driver = {
941 .voice_size_in = sizeof (PAVoiceIn),
942 };
943
944 +static void audio_pa_class_init(ObjectClass *klass, const void *data)
945 +{
946 + AudioBackendClass *b = AUDIO_BACKEND_CLASS(klass);
947 + AudioMixengBackendClass *k = AUDIO_MIXENG_BACKEND_CLASS(klass);
948 +
949 + audio_pa_parent_class = AUDIO_BACKEND_CLASS(object_class_get_parent(klass));
950 +
951 + b->realize = audio_pa_realize;
952 + k->driver = &pa_audio_driver;
953 +}
954 +
955 static const TypeInfo audio_types[] = {
956 {
957 .name = TYPE_AUDIO_PA,
958 .parent = TYPE_AUDIO_MIXENG_BACKEND,
959 .instance_size = sizeof(AudioPa),
960 .class_init = audio_pa_class_init,
961 + .instance_finalize = audio_pa_finalize,
962 },
963 };
964