From 9c18a9234bab9d5e903f897b30fb4a37888aebfc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Tue, 4 Jul 2023 11:16:42 +0200 Subject: [PATCH 01/19] virtio-gpu: fix potential divide-by-zero regression MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit 9462ff4695aa0 ("virtio-gpu/win32: allocate shareable 2d resources/images") introduces a division, which can lead to crashes when "height" is 0. Fixes: https://gitlab.com/qemu-project/qemu/-/issues/1744 Reviewed-by: Alexander Bulekov Signed-off-by: Marc-André Lureau --- hw/display/virtio-gpu.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index befa7d6d78..e937c4e348 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -303,10 +303,11 @@ static void virtio_gpu_resource_create_2d(VirtIOGPU *g, goto end; } #endif - res->image = pixman_image_create_bits(pformat, - c2d.width, - c2d.height, - bits, res->hostmem / c2d.height); + res->image = pixman_image_create_bits( + pformat, + c2d.width, + c2d.height, + bits, c2d.height ? res->hostmem / c2d.height : 0); #ifdef WIN32 if (res->image) { pixman_image_set_destroy_function(res->image, win32_pixman_image_destroy, res->handle); @@ -1272,9 +1273,10 @@ static int virtio_gpu_load(QEMUFile *f, void *opaque, size_t size, return -EINVAL; } #endif - res->image = pixman_image_create_bits(pformat, - res->width, res->height, - bits, res->hostmem / res->height); + res->image = pixman_image_create_bits( + pformat, + res->width, res->height, + bits, res->height ? res->hostmem / res->height : 0); if (!res->image) { g_free(res); return -EINVAL; From d921fea338c1059a27ce7b75309d7a2e485f710b Mon Sep 17 00:00:00 2001 From: Mauro Matteo Cascella Date: Tue, 4 Jul 2023 10:41:22 +0200 Subject: [PATCH 02/19] ui/vnc-clipboard: fix infinite loop in inflate_buffer (CVE-2023-3255) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A wrong exit condition may lead to an infinite loop when inflating a valid zlib buffer containing some extra bytes in the `inflate_buffer` function. The bug only occurs post-authentication. Return the buffer immediately if the end of the compressed data has been reached (Z_STREAM_END). Fixes: CVE-2023-3255 Fixes: 0bf41cab ("ui/vnc: clipboard support") Reported-by: Kevin Denis Signed-off-by: Mauro Matteo Cascella Reviewed-by: Marc-André Lureau Tested-by: Marc-André Lureau Message-ID: <20230704084210.101822-1-mcascell@redhat.com> --- ui/vnc-clipboard.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ui/vnc-clipboard.c b/ui/vnc-clipboard.c index 8aeadfaa21..c759be3438 100644 --- a/ui/vnc-clipboard.c +++ b/ui/vnc-clipboard.c @@ -50,8 +50,11 @@ static uint8_t *inflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size) ret = inflate(&stream, Z_FINISH); switch (ret) { case Z_OK: - case Z_STREAM_END: break; + case Z_STREAM_END: + *size = stream.total_out; + inflateEnd(&stream); + return out; case Z_BUF_ERROR: out_len <<= 1; if (out_len > (1 << 20)) { @@ -66,11 +69,6 @@ static uint8_t *inflate_buffer(uint8_t *in, uint32_t in_len, uint32_t *size) } } - *size = stream.total_out; - inflateEnd(&stream); - - return out; - err_end: inflateEnd(&stream); err: From 83b4b236ed54dab35f1b821ee2b6f3101c45c8cc Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 28 Jun 2023 12:15:04 -0700 Subject: [PATCH 03/19] ui/gtk: Make sure the right EGL context is currently bound MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Observed a wrong context is bound when changing the scanout mode. To prevent problem, it is needed to make sure to bind the right context in gtk_egl_set_scanout_mode/gtk_gl_area_set_scanout_mode as well as unbind one in the end of gd_egl_update/gd_gl_area_update. Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Reviewed-by: Marc-André Lureau Message-ID: <20230628191504.17185-1-dongwon.kim@intel.com> --- ui/gtk-egl.c | 4 ++++ ui/gtk-gl-area.c | 2 ++ 2 files changed, 6 insertions(+) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index d59b8cd7d7..42db1bb6cf 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -32,6 +32,8 @@ static void gtk_egl_set_scanout_mode(VirtualConsole *vc, bool scanout) vc->gfx.scanout_mode = scanout; if (!vc->gfx.scanout_mode) { + eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, + vc->gfx.esurface, vc->gfx.ectx); egl_fb_destroy(&vc->gfx.guest_fb); if (vc->gfx.surface) { surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); @@ -135,6 +137,8 @@ void gd_egl_update(DisplayChangeListener *dcl, vc->gfx.esurface, vc->gfx.ectx); surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h); vc->gfx.glupdates++; + eglMakeCurrent(qemu_egl_display, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); } void gd_egl_refresh(DisplayChangeListener *dcl) diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 7367dfd793..a9a7fdf50c 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -26,6 +26,7 @@ static void gtk_gl_area_set_scanout_mode(VirtualConsole *vc, bool scanout) vc->gfx.scanout_mode = scanout; if (!vc->gfx.scanout_mode) { + gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); egl_fb_destroy(&vc->gfx.guest_fb); if (vc->gfx.surface) { surface_gl_destroy_texture(vc->gfx.gls, vc->gfx.ds); @@ -115,6 +116,7 @@ void gd_gl_area_update(DisplayChangeListener *dcl, gtk_gl_area_make_current(GTK_GL_AREA(vc->gfx.drawing_area)); surface_gl_update_texture(vc->gfx.gls, vc->gfx.ds, x, y, w, h); vc->gfx.glupdates++; + gdk_gl_context_clear_current(); } void gd_gl_area_refresh(DisplayChangeListener *dcl) From 0d0be87659b06ef7ce07ad07376086bd28e4d71b Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Tue, 27 Jun 2023 15:44:51 -0700 Subject: [PATCH 04/19] virtio-gpu: replace the surface with null surface when resetting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The primary guest scanout shows the booting screen right after reboot but additional guest displays (i.e. max_ouptuts > 1) will keep displaying the old frames until the guest virtio gpu driver gets initialized, which could cause some confusion. A better way is to to replace the surface with a place holder that tells the display is not active during the reset of virtio-gpu device. And to immediately update the surface with the place holder image after the switch, displaychangelistener_gfx_switch needs to be called with 'update == TRUE' in dpy_gfx_replace_surface when the new surface is NULL. Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Acked-by: Marc-André Lureau Message-ID: <20230627224451.11739-1-dongwon.kim@intel.com> --- hw/display/virtio-gpu.c | 5 +++++ ui/console.c | 11 ++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/hw/display/virtio-gpu.c b/hw/display/virtio-gpu.c index e937c4e348..e8603d78ca 100644 --- a/hw/display/virtio-gpu.c +++ b/hw/display/virtio-gpu.c @@ -1397,6 +1397,7 @@ void virtio_gpu_reset(VirtIODevice *vdev) VirtIOGPU *g = VIRTIO_GPU(vdev); struct virtio_gpu_simple_resource *res, *tmp; struct virtio_gpu_ctrl_command *cmd; + int i = 0; QTAILQ_FOREACH_SAFE(res, &g->reslist, next, tmp) { virtio_gpu_resource_destroy(g, res); @@ -1415,6 +1416,10 @@ void virtio_gpu_reset(VirtIODevice *vdev) g_free(cmd); } + for (i = 0; i < g->parent_obj.conf.max_outputs; i++) { + dpy_gfx_replace_surface(g->parent_obj.scanout[i].con, NULL); + } + virtio_gpu_base_reset(VIRTIO_GPU_BASE(vdev)); } diff --git a/ui/console.c b/ui/console.c index c1544e0fb8..8da2170a7e 100644 --- a/ui/console.c +++ b/ui/console.c @@ -1898,6 +1898,7 @@ void dpy_gfx_replace_surface(QemuConsole *con, static const char placeholder_msg[] = "Display output is not active."; DisplayState *s = con->ds; DisplaySurface *old_surface = con->surface; + DisplaySurface *new_surface = surface; DisplayChangeListener *dcl; int width; int height; @@ -1911,19 +1912,19 @@ void dpy_gfx_replace_surface(QemuConsole *con, height = 480; } - surface = qemu_create_placeholder_surface(width, height, placeholder_msg); + new_surface = qemu_create_placeholder_surface(width, height, placeholder_msg); } - assert(old_surface != surface); + assert(old_surface != new_surface); con->scanout.kind = SCANOUT_SURFACE; - con->surface = surface; - dpy_gfx_create_texture(con, surface); + con->surface = new_surface; + dpy_gfx_create_texture(con, new_surface); QLIST_FOREACH(dcl, &s->listeners, next) { if (con != (dcl->con ? dcl->con : active_console)) { continue; } - displaychangelistener_gfx_switch(dcl, surface, FALSE); + displaychangelistener_gfx_switch(dcl, new_surface, surface ? FALSE : TRUE); } dpy_gfx_destroy_texture(con, old_surface); qemu_free_displaysurface(old_surface); From 9ac06df8b684e6409ebc7af6337500e7484e28b3 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Wed, 12 Jul 2023 21:04:44 -0700 Subject: [PATCH 05/19] virtio-gpu-udmabuf: correct naming of QemuDmaBuf size properties MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace 'width' and 'height' in QemuDmaBuf with 'backing_widht' and 'backing_height' as these commonly indicate the size of the whole surface (e.g. guest's Xorg extended display). Then use 'width' and 'height' for sub region in there (e.g. guest's scanouts). Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Reviewed-by: Marc-André Lureau Message-ID: <20230713040444.32267-1-dongwon.kim@intel.com> --- hw/display/virtio-gpu-udmabuf.c | 12 ++++++------ include/ui/console.h | 4 ++-- ui/dbus-listener.c | 8 ++++---- ui/egl-helpers.c | 8 ++++---- ui/gtk-egl.c | 10 ++++++---- ui/gtk-gl-area.c | 7 ++++--- 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/hw/display/virtio-gpu-udmabuf.c b/hw/display/virtio-gpu-udmabuf.c index ef1a740de5..d51184d658 100644 --- a/hw/display/virtio-gpu-udmabuf.c +++ b/hw/display/virtio-gpu-udmabuf.c @@ -181,13 +181,13 @@ static VGPUDMABuf } dmabuf = g_new0(VGPUDMABuf, 1); - dmabuf->buf.width = fb->width; - dmabuf->buf.height = fb->height; + dmabuf->buf.width = r->width; + dmabuf->buf.height = r->height; dmabuf->buf.stride = fb->stride; dmabuf->buf.x = r->x; dmabuf->buf.y = r->y; - dmabuf->buf.scanout_width = r->width; - dmabuf->buf.scanout_height = r->height; + dmabuf->buf.backing_width = fb->width; + dmabuf->buf.backing_height = fb->height; dmabuf->buf.fourcc = qemu_pixman_to_drm_format(fb->format); dmabuf->buf.fd = res->dmabuf_fd; dmabuf->buf.allow_fences = true; @@ -218,8 +218,8 @@ int virtio_gpu_update_dmabuf(VirtIOGPU *g, g->dmabuf.primary[scanout_id] = new_primary; qemu_console_resize(scanout->con, - new_primary->buf.scanout_width, - new_primary->buf.scanout_height); + new_primary->buf.width, + new_primary->buf.height); dpy_gl_scanout_dmabuf(scanout->con, &new_primary->buf); if (old_primary) { diff --git a/include/ui/console.h b/include/ui/console.h index f27b2aad4f..3e8b22d6c6 100644 --- a/include/ui/console.h +++ b/include/ui/console.h @@ -201,8 +201,8 @@ typedef struct QemuDmaBuf { uint32_t texture; uint32_t x; uint32_t y; - uint32_t scanout_width; - uint32_t scanout_height; + uint32_t backing_width; + uint32_t backing_height; bool y0_top; void *sync; int fence_fd; diff --git a/ui/dbus-listener.c b/ui/dbus-listener.c index 0240c39510..68ff343799 100644 --- a/ui/dbus-listener.c +++ b/ui/dbus-listener.c @@ -415,13 +415,13 @@ static void dbus_scanout_texture(DisplayChangeListener *dcl, backing_width, backing_height, x, y, w, h); #ifdef CONFIG_GBM QemuDmaBuf dmabuf = { - .width = backing_width, - .height = backing_height, + .width = w, + .height = h, .y0_top = backing_y_0_top, .x = x, .y = y, - .scanout_width = w, - .scanout_height = h, + .backing_width = backing_width, + .backing_height = backing_height, }; assert(tex_id); diff --git a/ui/egl-helpers.c b/ui/egl-helpers.c index 8f9fbf583e..3d19dbe382 100644 --- a/ui/egl-helpers.c +++ b/ui/egl-helpers.c @@ -148,8 +148,8 @@ void egl_fb_blit(egl_fb *dst, egl_fb *src, bool flip) if (src->dmabuf) { x1 = src->dmabuf->x; y1 = src->dmabuf->y; - w = src->dmabuf->scanout_width; - h = src->dmabuf->scanout_height; + w = src->dmabuf->width; + h = src->dmabuf->height; } w = (x1 + w) > src->width ? src->width - x1 : w; @@ -314,9 +314,9 @@ void egl_dmabuf_import_texture(QemuDmaBuf *dmabuf) } attrs[i++] = EGL_WIDTH; - attrs[i++] = dmabuf->width; + attrs[i++] = dmabuf->backing_width; attrs[i++] = EGL_HEIGHT; - attrs[i++] = dmabuf->height; + attrs[i++] = dmabuf->backing_height; attrs[i++] = EGL_LINUX_DRM_FOURCC_EXT; attrs[i++] = dmabuf->fourcc; diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 42db1bb6cf..eee821d73a 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -262,9 +262,10 @@ void gd_egl_scanout_dmabuf(DisplayChangeListener *dcl, } gd_egl_scanout_texture(dcl, dmabuf->texture, - dmabuf->y0_top, dmabuf->width, dmabuf->height, - dmabuf->x, dmabuf->y, dmabuf->scanout_width, - dmabuf->scanout_height, NULL); + dmabuf->y0_top, + dmabuf->backing_width, dmabuf->backing_height, + dmabuf->x, dmabuf->y, dmabuf->width, + dmabuf->height, NULL); if (dmabuf->allow_fences) { vc->gfx.guest_fb.dmabuf = dmabuf; @@ -284,7 +285,8 @@ void gd_egl_cursor_dmabuf(DisplayChangeListener *dcl, if (!dmabuf->texture) { return; } - egl_fb_setup_for_tex(&vc->gfx.cursor_fb, dmabuf->width, dmabuf->height, + egl_fb_setup_for_tex(&vc->gfx.cursor_fb, + dmabuf->backing_width, dmabuf->backing_height, dmabuf->texture, false); } else { egl_fb_destroy(&vc->gfx.cursor_fb); diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index a9a7fdf50c..4513d3d059 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -301,9 +301,10 @@ void gd_gl_area_scanout_dmabuf(DisplayChangeListener *dcl, } gd_gl_area_scanout_texture(dcl, dmabuf->texture, - dmabuf->y0_top, dmabuf->width, dmabuf->height, - dmabuf->x, dmabuf->y, dmabuf->scanout_width, - dmabuf->scanout_height, NULL); + dmabuf->y0_top, + dmabuf->backing_width, dmabuf->backing_height, + dmabuf->x, dmabuf->y, dmabuf->width, + dmabuf->height, NULL); if (dmabuf->allow_fences) { vc->gfx.guest_fb.dmabuf = dmabuf; From 92b58156e745b01a20861a872e327693a7e729b1 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Thu, 6 Jul 2023 11:33:54 -0700 Subject: [PATCH 06/19] ui/gtk: set scanout-mode right before scheduling draw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting scanout mode is better to be done very last minute right because the mode can be reset anytime after it is set in dpy_gl_scanout_texture by any asynchronouse dpy_refresh call, which eventually cancels drawing of the guest scanout texture. Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Acked-by: Marc-André Lureau Message-ID: <20230706183355.29361-1-dongwon.kim@intel.com> --- ui/gtk-egl.c | 2 +- ui/gtk-gl-area.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index eee821d73a..98b3a116bf 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -242,7 +242,6 @@ void gd_egl_scanout_texture(DisplayChangeListener *dcl, eglMakeCurrent(qemu_egl_display, vc->gfx.esurface, vc->gfx.esurface, vc->gfx.ectx); - gtk_egl_set_scanout_mode(vc, true); egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height, backing_id, false); } @@ -353,6 +352,7 @@ void gd_egl_flush(DisplayChangeListener *dcl, if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) { graphic_hw_gl_block(vc->gfx.dcl.con, true); vc->gfx.guest_fb.dmabuf->draw_submitted = true; + gtk_egl_set_scanout_mode(vc, true); gtk_widget_queue_draw_area(area, x, y, w, h); return; } diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 4513d3d059..28d9e49888 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -264,7 +264,6 @@ void gd_gl_area_scanout_texture(DisplayChangeListener *dcl, return; } - gtk_gl_area_set_scanout_mode(vc, true); egl_fb_setup_for_tex(&vc->gfx.guest_fb, backing_width, backing_height, backing_id, false); } @@ -284,6 +283,7 @@ void gd_gl_area_scanout_flush(DisplayChangeListener *dcl, if (vc->gfx.guest_fb.dmabuf && !vc->gfx.guest_fb.dmabuf->draw_submitted) { graphic_hw_gl_block(vc->gfx.dcl.con, true); vc->gfx.guest_fb.dmabuf->draw_submitted = true; + gtk_gl_area_set_scanout_mode(vc, true); } gtk_gl_area_queue_render(GTK_GL_AREA(vc->gfx.drawing_area)); } From 1be878eb97abf8d43c9c5868bca69862ccae5da3 Mon Sep 17 00:00:00 2001 From: Dongwon Kim Date: Thu, 6 Jul 2023 11:33:55 -0700 Subject: [PATCH 07/19] ui/gtk: skip refresh if new dmabuf has been submitted MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Skip refresh if a new dmabuf (guest scanout frame) has already been submitted and ready to be drawn because the scanout will be updated with new frame anyway. Cc: Gerd Hoffmann Cc: Marc-André Lureau Cc: Vivek Kasireddy Signed-off-by: Dongwon Kim Acked-by: Marc-André Lureau Message-ID: <20230706183355.29361-2-dongwon.kim@intel.com> --- ui/gtk-egl.c | 4 ++++ ui/gtk-gl-area.c | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/ui/gtk-egl.c b/ui/gtk-egl.c index 98b3a116bf..4c29ac10d0 100644 --- a/ui/gtk-egl.c +++ b/ui/gtk-egl.c @@ -148,6 +148,10 @@ void gd_egl_refresh(DisplayChangeListener *dcl) gd_update_monitor_refresh_rate( vc, vc->window ? vc->window : vc->gfx.drawing_area); + if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) { + return; + } + if (!vc->gfx.esurface) { gd_egl_init(vc); if (!vc->gfx.esurface) { diff --git a/ui/gtk-gl-area.c b/ui/gtk-gl-area.c index 28d9e49888..1ce34a249e 100644 --- a/ui/gtk-gl-area.c +++ b/ui/gtk-gl-area.c @@ -125,6 +125,10 @@ void gd_gl_area_refresh(DisplayChangeListener *dcl) gd_update_monitor_refresh_rate(vc, vc->window ? vc->window : vc->gfx.drawing_area); + if (vc->gfx.guest_fb.dmabuf && vc->gfx.guest_fb.dmabuf->draw_submitted) { + return; + } + if (!vc->gfx.gls) { if (!gtk_widget_get_realized(vc->gfx.drawing_area)) { return; From 6200e0ff5f921d7d2b6facaa99f85512dd08a958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 6 May 2023 20:37:24 +0400 Subject: [PATCH 08/19] libvirt-ci: update submodule to cover pipewire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Message-Id: <20230506163735.3481387-2-marcandre.lureau@redhat.com> --- tests/lcitool/libvirt-ci | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/lcitool/libvirt-ci b/tests/lcitool/libvirt-ci index b0f44f929a..9bff3b763b 160000 --- a/tests/lcitool/libvirt-ci +++ b/tests/lcitool/libvirt-ci @@ -1 +1 @@ -Subproject commit b0f44f929a81c0a604fb7fbf8afc34d37ab0eae9 +Subproject commit 9bff3b763b5531a1490e238bfbf77306dc3a6dbb From 62259d816c7cc61c59baa14aecaa3bdb3caa34eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 6 May 2023 20:37:25 +0400 Subject: [PATCH 09/19] tests/lcitool: add pipewire MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Message-Id: <20230506163735.3481387-3-marcandre.lureau@redhat.com> --- tests/docker/dockerfiles/alpine.docker | 1 + tests/docker/dockerfiles/centos8.docker | 1 + tests/docker/dockerfiles/debian-amd64-cross.docker | 1 + tests/docker/dockerfiles/debian-amd64.docker | 1 + tests/docker/dockerfiles/debian-arm64-cross.docker | 1 + tests/docker/dockerfiles/debian-armel-cross.docker | 1 + tests/docker/dockerfiles/debian-armhf-cross.docker | 1 + tests/docker/dockerfiles/debian-mips64el-cross.docker | 1 + tests/docker/dockerfiles/debian-mipsel-cross.docker | 1 + tests/docker/dockerfiles/debian-ppc64el-cross.docker | 1 + tests/docker/dockerfiles/debian-s390x-cross.docker | 1 + tests/docker/dockerfiles/fedora.docker | 1 + tests/docker/dockerfiles/opensuse-leap.docker | 1 + tests/docker/dockerfiles/ubuntu2204.docker | 1 + tests/lcitool/projects/qemu.yml | 1 + 15 files changed, 15 insertions(+) diff --git a/tests/docker/dockerfiles/alpine.docker b/tests/docker/dockerfiles/alpine.docker index 43370f7b36..fa455f1474 100644 --- a/tests/docker/dockerfiles/alpine.docker +++ b/tests/docker/dockerfiles/alpine.docker @@ -77,6 +77,7 @@ RUN apk update && \ numactl-dev \ openssh-client \ pcre-dev \ + pipewire-dev \ pixman-dev \ pkgconf \ pulseaudio-dev \ diff --git a/tests/docker/dockerfiles/centos8.docker b/tests/docker/dockerfiles/centos8.docker index 78f454b782..da7dc818fb 100644 --- a/tests/docker/dockerfiles/centos8.docker +++ b/tests/docker/dockerfiles/centos8.docker @@ -90,6 +90,7 @@ RUN dnf distro-sync -y && \ openssh-clients \ pam-devel \ pcre-static \ + pipewire-devel \ pixman-devel \ pkgconfig \ pulseaudio-libs-devel \ diff --git a/tests/docker/dockerfiles/debian-amd64-cross.docker b/tests/docker/dockerfiles/debian-amd64-cross.docker index 016c2321f1..b7bdc01243 100644 --- a/tests/docker/dockerfiles/debian-amd64-cross.docker +++ b/tests/docker/dockerfiles/debian-amd64-cross.docker @@ -116,6 +116,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:amd64 \ libnuma-dev:amd64 \ libpam0g-dev:amd64 \ + libpipewire-0.3-dev:amd64 \ libpixman-1-dev:amd64 \ libpmem-dev:amd64 \ libpng-dev:amd64 \ diff --git a/tests/docker/dockerfiles/debian-amd64.docker b/tests/docker/dockerfiles/debian-amd64.docker index e39871c7bb..6d2fa38e3e 100644 --- a/tests/docker/dockerfiles/debian-amd64.docker +++ b/tests/docker/dockerfiles/debian-amd64.docker @@ -69,6 +69,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnuma-dev \ libpam0g-dev \ libpcre2-dev \ + libpipewire-0.3-dev \ libpixman-1-dev \ libpmem-dev \ libpng-dev \ diff --git a/tests/docker/dockerfiles/debian-arm64-cross.docker b/tests/docker/dockerfiles/debian-arm64-cross.docker index 3c114efa11..68165c2f23 100644 --- a/tests/docker/dockerfiles/debian-arm64-cross.docker +++ b/tests/docker/dockerfiles/debian-arm64-cross.docker @@ -116,6 +116,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:arm64 \ libnuma-dev:arm64 \ libpam0g-dev:arm64 \ + libpipewire-0.3-dev:arm64 \ libpixman-1-dev:arm64 \ libpng-dev:arm64 \ libpulse-dev:arm64 \ diff --git a/tests/docker/dockerfiles/debian-armel-cross.docker b/tests/docker/dockerfiles/debian-armel-cross.docker index dfbd47db89..2fb65308c7 100644 --- a/tests/docker/dockerfiles/debian-armel-cross.docker +++ b/tests/docker/dockerfiles/debian-armel-cross.docker @@ -116,6 +116,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:armel \ libnuma-dev:armel \ libpam0g-dev:armel \ + libpipewire-0.3-dev:armel \ libpixman-1-dev:armel \ libpng-dev:armel \ libpulse-dev:armel \ diff --git a/tests/docker/dockerfiles/debian-armhf-cross.docker b/tests/docker/dockerfiles/debian-armhf-cross.docker index 4e0084e896..df77ccb57b 100644 --- a/tests/docker/dockerfiles/debian-armhf-cross.docker +++ b/tests/docker/dockerfiles/debian-armhf-cross.docker @@ -116,6 +116,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:armhf \ libnuma-dev:armhf \ libpam0g-dev:armhf \ + libpipewire-0.3-dev:armhf \ libpixman-1-dev:armhf \ libpng-dev:armhf \ libpulse-dev:armhf \ diff --git a/tests/docker/dockerfiles/debian-mips64el-cross.docker b/tests/docker/dockerfiles/debian-mips64el-cross.docker index 88adf333e9..63a3d7aa3b 100644 --- a/tests/docker/dockerfiles/debian-mips64el-cross.docker +++ b/tests/docker/dockerfiles/debian-mips64el-cross.docker @@ -115,6 +115,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:mips64el \ libnuma-dev:mips64el \ libpam0g-dev:mips64el \ + libpipewire-0.3-dev:mips64el \ libpixman-1-dev:mips64el \ libpng-dev:mips64el \ libpulse-dev:mips64el \ diff --git a/tests/docker/dockerfiles/debian-mipsel-cross.docker b/tests/docker/dockerfiles/debian-mipsel-cross.docker index 256e8b5dfe..ac87bbb095 100644 --- a/tests/docker/dockerfiles/debian-mipsel-cross.docker +++ b/tests/docker/dockerfiles/debian-mipsel-cross.docker @@ -115,6 +115,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:mipsel \ libnuma-dev:mipsel \ libpam0g-dev:mipsel \ + libpipewire-0.3-dev:mipsel \ libpixman-1-dev:mipsel \ libpng-dev:mipsel \ libpulse-dev:mipsel \ diff --git a/tests/docker/dockerfiles/debian-ppc64el-cross.docker b/tests/docker/dockerfiles/debian-ppc64el-cross.docker index 4d19cd2bd7..def11f1693 100644 --- a/tests/docker/dockerfiles/debian-ppc64el-cross.docker +++ b/tests/docker/dockerfiles/debian-ppc64el-cross.docker @@ -116,6 +116,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:ppc64el \ libnuma-dev:ppc64el \ libpam0g-dev:ppc64el \ + libpipewire-0.3-dev:ppc64el \ libpixman-1-dev:ppc64el \ libpng-dev:ppc64el \ libpulse-dev:ppc64el \ diff --git a/tests/docker/dockerfiles/debian-s390x-cross.docker b/tests/docker/dockerfiles/debian-s390x-cross.docker index 642bbde3d1..80028e1eea 100644 --- a/tests/docker/dockerfiles/debian-s390x-cross.docker +++ b/tests/docker/dockerfiles/debian-s390x-cross.docker @@ -116,6 +116,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnfs-dev:s390x \ libnuma-dev:s390x \ libpam0g-dev:s390x \ + libpipewire-0.3-dev:s390x \ libpixman-1-dev:s390x \ libpng-dev:s390x \ libpulse-dev:s390x \ diff --git a/tests/docker/dockerfiles/fedora.docker b/tests/docker/dockerfiles/fedora.docker index 8a35a17617..c5b6c96943 100644 --- a/tests/docker/dockerfiles/fedora.docker +++ b/tests/docker/dockerfiles/fedora.docker @@ -98,6 +98,7 @@ exec "$@"\n' > /usr/bin/nosync && \ openssh-clients \ pam-devel \ pcre-static \ + pipewire-devel \ pixman-devel \ pkgconfig \ pulseaudio-libs-devel \ diff --git a/tests/docker/dockerfiles/opensuse-leap.docker b/tests/docker/dockerfiles/opensuse-leap.docker index 185abe57d8..37c83e5e4e 100644 --- a/tests/docker/dockerfiles/opensuse-leap.docker +++ b/tests/docker/dockerfiles/opensuse-leap.docker @@ -88,6 +88,7 @@ RUN zypper update -y && \ openssh \ pam-devel \ pcre-devel-static \ + pipewire-devel \ pkgconfig \ python39-base \ python39-pip \ diff --git a/tests/docker/dockerfiles/ubuntu2204.docker b/tests/docker/dockerfiles/ubuntu2204.docker index 1d442cdfe6..8f939870ae 100644 --- a/tests/docker/dockerfiles/ubuntu2204.docker +++ b/tests/docker/dockerfiles/ubuntu2204.docker @@ -69,6 +69,7 @@ RUN export DEBIAN_FRONTEND=noninteractive && \ libnuma-dev \ libpam0g-dev \ libpcre2-dev \ + libpipewire-0.3-dev \ libpixman-1-dev \ libpmem-dev \ libpng-dev \ diff --git a/tests/lcitool/projects/qemu.yml b/tests/lcitool/projects/qemu.yml index 21fd3d2cf9..d452a891ee 100644 --- a/tests/lcitool/projects/qemu.yml +++ b/tests/lcitool/projects/qemu.yml @@ -85,6 +85,7 @@ packages: - pam - pcre-static - pixman + - pipewire - pkg-config - pulseaudio - python3 From 20c512480522a035627d4f53e28bc7b0ecb779b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 6 May 2023 20:37:26 +0400 Subject: [PATCH 10/19] audio/pw: Pipewire->PipeWire case fix for user-visible text MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit "PipeWire" is the correct case. Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Volker Rümelin Message-Id: <20230506163735.3481387-4-marcandre.lureau@redhat.com> --- audio/pwaudio.c | 10 +++++----- audio/trace-events | 2 +- meson.build | 2 +- meson_options.txt | 2 +- qapi/audio.json | 12 ++++++------ qemu-options.hx | 4 ++-- scripts/meson-buildoptions.sh | 2 +- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/audio/pwaudio.c b/audio/pwaudio.c index 1d108bdebb..9eb69bfd18 100644 --- a/audio/pwaudio.c +++ b/audio/pwaudio.c @@ -1,5 +1,5 @@ /* - * QEMU Pipewire audio driver + * QEMU PipeWire audio driver * * Copyright (c) 2023 Red Hat Inc. * @@ -800,21 +800,21 @@ qpw_audio_init(Audiodev *dev) assert(dev->driver == AUDIODEV_DRIVER_PIPEWIRE); pw->dev = dev; - pw->thread_loop = pw_thread_loop_new("Pipewire thread loop", NULL); + pw->thread_loop = pw_thread_loop_new("PipeWire thread loop", NULL); if (pw->thread_loop == NULL) { - error_report("Could not create Pipewire loop"); + error_report("Could not create PipeWire loop"); goto fail; } pw->context = pw_context_new(pw_thread_loop_get_loop(pw->thread_loop), NULL, 0); if (pw->context == NULL) { - error_report("Could not create Pipewire context"); + error_report("Could not create PipeWire context"); goto fail; } if (pw_thread_loop_start(pw->thread_loop) < 0) { - error_report("Could not start Pipewire loop"); + error_report("Could not start PipeWire loop"); goto fail; } diff --git a/audio/trace-events b/audio/trace-events index 85dbb506b2..ab04f020ce 100644 --- a/audio/trace-events +++ b/audio/trace-events @@ -24,7 +24,7 @@ pw_read(int32_t avail, uint32_t index, size_t len) "avail=%d index=%u len=%zu" pw_write(int32_t filled, int32_t avail, uint32_t index, size_t len) "filled=%d avail=%d index=%u len=%zu" pw_vol(const char *ret) "set volume: %s" pw_period(uint64_t quantum, uint32_t rate) "period =%" PRIu64 "/%u" -pw_audio_init(void) "Initialize Pipewire context" +pw_audio_init(void) "Initialize PipeWire context" # audio.c audio_timer_start(int interval) "interval %d ms" diff --git a/meson.build b/meson.build index 5fcdb37a71..98e68ef0b1 100644 --- a/meson.build +++ b/meson.build @@ -4251,7 +4251,7 @@ if targetos == 'linux' summary_info += {'ALSA support': alsa} summary_info += {'PulseAudio support': pulse} endif -summary_info += {'Pipewire support': pipewire} +summary_info += {'PipeWire support': pipewire} summary_info += {'JACK support': jack} summary(summary_info, bool_yn: true, section: 'Audio backends') diff --git a/meson_options.txt b/meson_options.txt index bbb5c7e886..aaea5ddd77 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -267,7 +267,7 @@ option('oss', type: 'feature', value: 'auto', option('pa', type: 'feature', value: 'auto', description: 'PulseAudio sound support') option('pipewire', type: 'feature', value: 'auto', - description: 'Pipewire sound support') + description: 'PipeWire sound support') option('sndio', type: 'feature', value: 'auto', description: 'sndio sound support') diff --git a/qapi/audio.json b/qapi/audio.json index 534f10d8b1..519697c0cd 100644 --- a/qapi/audio.json +++ b/qapi/audio.json @@ -328,17 +328,17 @@ ## # @AudiodevPipewirePerDirectionOptions: # -# Options of the Pipewire backend that are used for both playback and +# Options of the PipeWire backend that are used for both playback and # recording. # # @name: name of the sink/source to use # -# @stream-name: name of the Pipewire stream created by qemu. Can be -# used to identify the stream in Pipewire when you create multiple -# Pipewire devices or run multiple qemu instances (default: +# @stream-name: name of the PipeWire stream created by qemu. Can be +# used to identify the stream in PipeWire when you create multiple +# PipeWire devices or run multiple qemu instances (default: # audiodev's id) # -# @latency: latency you want Pipewire to achieve in microseconds +# @latency: latency you want PipeWire to achieve in microseconds # (default 46000) # # Since: 8.1 @@ -353,7 +353,7 @@ ## # @AudiodevPipewireOptions: # -# Options of the Pipewire audio backend. +# Options of the PipeWire audio backend. # # @in: options of the capture stream # diff --git a/qemu-options.hx b/qemu-options.hx index f8f384e551..29b98c3d4c 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -963,10 +963,10 @@ SRST to honor this value but actual latencies may be lower or higher. ``-audiodev pipewire,id=id[,prop[=value][,...]]`` - Creates a backend using Pipewire. This backend is available on + Creates a backend using PipeWire. This backend is available on most systems. - Pipewire specific options are: + PipeWire specific options are: ``in|out.latency=usecs`` Desired latency in microseconds. diff --git a/scripts/meson-buildoptions.sh b/scripts/meson-buildoptions.sh index 7dd5709ef4..9da3fe299b 100644 --- a/scripts/meson-buildoptions.sh +++ b/scripts/meson-buildoptions.sh @@ -145,7 +145,7 @@ meson_options_help() { printf "%s\n" ' oss OSS sound support' printf "%s\n" ' pa PulseAudio sound support' printf "%s\n" ' parallels parallels image format support' - printf "%s\n" ' pipewire Pipewire sound support' + printf "%s\n" ' pipewire PipeWire sound support' printf "%s\n" ' png PNG support with libpng' printf "%s\n" ' pvrdma Enable PVRDMA support' printf "%s\n" ' qcow1 qcow1 image format support' From 2d216959e1e065ccb5523e4ce67b6f9ad47b17eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 6 May 2023 20:37:27 +0400 Subject: [PATCH 11/19] audio/pw: drop needless case statement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Volker Rümelin Message-Id: <20230506163735.3481387-5-marcandre.lureau@redhat.com> --- audio/pwaudio.c | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/audio/pwaudio.c b/audio/pwaudio.c index 9eb69bfd18..51cfc0b052 100644 --- a/audio/pwaudio.c +++ b/audio/pwaudio.c @@ -197,16 +197,6 @@ on_stream_state_changed(void *data, enum pw_stream_state old, trace_pw_state_changed(pw_stream_get_node_id(v->stream), pw_stream_state_as_string(state)); - - switch (state) { - case PW_STREAM_STATE_ERROR: - case PW_STREAM_STATE_UNCONNECTED: - break; - case PW_STREAM_STATE_PAUSED: - case PW_STREAM_STATE_CONNECTING: - case PW_STREAM_STATE_STREAMING: - break; - } } static const struct pw_stream_events capture_stream_events = { From 3b2876086b2c5a23a82d70eeb9f2b68c833c2809 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 6 May 2023 20:37:28 +0400 Subject: [PATCH 12/19] audio/pw: needless check for NULL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit g_clear_pointer() already checks for NULL. Signed-off-by: Marc-André Lureau Reviewed-by: Volker Rümelin Message-Id: <20230506163735.3481387-6-marcandre.lureau@redhat.com> --- audio/pwaudio.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/audio/pwaudio.c b/audio/pwaudio.c index 51cfc0b052..6ca4ef4f62 100644 --- a/audio/pwaudio.c +++ b/audio/pwaudio.c @@ -834,12 +834,8 @@ fail: if (pw->thread_loop) { pw_thread_loop_stop(pw->thread_loop); } - if (pw->context) { - g_clear_pointer(&pw->context, pw_context_destroy); - } - if (pw->thread_loop) { - g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy); - } + g_clear_pointer(&pw->context, pw_context_destroy); + g_clear_pointer(&pw->thread_loop, pw_thread_loop_destroy); return NULL; } From 87048d20e64d5613ad08c59acfb0529d7f82ae70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 6 May 2023 20:37:29 +0400 Subject: [PATCH 13/19] audio/pw: trace during init before calling pipewire API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Philippe Mathieu-Daudé Reviewed-by: Volker Rümelin Message-Id: <20230506163735.3481387-7-marcandre.lureau@redhat.com> --- audio/pwaudio.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/audio/pwaudio.c b/audio/pwaudio.c index 6ca4ef4f62..2b12b40934 100644 --- a/audio/pwaudio.c +++ b/audio/pwaudio.c @@ -784,10 +784,11 @@ static void * qpw_audio_init(Audiodev *dev) { g_autofree pwaudio *pw = g_new0(pwaudio, 1); - pw_init(NULL, NULL); - trace_pw_audio_init(); assert(dev->driver == AUDIODEV_DRIVER_PIPEWIRE); + trace_pw_audio_init(); + + pw_init(NULL, NULL); pw->dev = dev; pw->thread_loop = pw_thread_loop_new("PipeWire thread loop", NULL); From 24a9095c13e34e95a89adbe7cb8e11179ff48f94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 6 May 2023 20:37:30 +0400 Subject: [PATCH 14/19] audio/pw: add more details on error MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PipeWire uses errno to report error details. Signed-off-by: Marc-André Lureau Reviewed-by: Volker Rümelin Message-Id: <20230506163735.3481387-8-marcandre.lureau@redhat.com> --- audio/pwaudio.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/audio/pwaudio.c b/audio/pwaudio.c index 2b12b40934..d0bc4680a6 100644 --- a/audio/pwaudio.c +++ b/audio/pwaudio.c @@ -750,6 +750,7 @@ static int wait_resync(pwaudio *pw) } return 0; } + static void on_core_error(void *data, uint32_t id, int seq, int res, const char *message) { @@ -793,19 +794,19 @@ qpw_audio_init(Audiodev *dev) pw->dev = dev; pw->thread_loop = pw_thread_loop_new("PipeWire thread loop", NULL); if (pw->thread_loop == NULL) { - error_report("Could not create PipeWire loop"); + error_report("Could not create PipeWire loop: %s", g_strerror(errno)); goto fail; } pw->context = pw_context_new(pw_thread_loop_get_loop(pw->thread_loop), NULL, 0); if (pw->context == NULL) { - error_report("Could not create PipeWire context"); + error_report("Could not create PipeWire context: %s", g_strerror(errno)); goto fail; } if (pw_thread_loop_start(pw->thread_loop) < 0) { - error_report("Could not start PipeWire loop"); + error_report("Could not start PipeWire loop: %s", g_strerror(errno)); goto fail; } From 92fd78689d0b06a00637e4e821a502b194d09328 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 6 May 2023 20:37:31 +0400 Subject: [PATCH 15/19] audio/pw: factorize some common code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Volker Rümelin Message-Id: <20230506163735.3481387-9-marcandre.lureau@redhat.com> --- audio/pwaudio.c | 115 +++++++++++++++++++++--------------------------- 1 file changed, 49 insertions(+), 66 deletions(-) diff --git a/audio/pwaudio.c b/audio/pwaudio.c index d0bc4680a6..70f0c46240 100644 --- a/audio/pwaudio.c +++ b/audio/pwaudio.c @@ -66,6 +66,9 @@ typedef struct PWVoiceIn { PWVoice v; } PWVoiceIn; +#define PW_VOICE_IN(v) ((PWVoiceIn *)v) +#define PW_VOICE_OUT(v) ((PWVoiceOut *)v) + static void stream_destroy(void *data) { @@ -629,106 +632,86 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) return 0; } +static void +qpw_voice_fini(PWVoice *v) +{ + pwaudio *c = v->g; + + if (!v->stream) { + return; + } + pw_thread_loop_lock(c->thread_loop); + pw_stream_destroy(v->stream); + v->stream = NULL; + pw_thread_loop_unlock(c->thread_loop); +} + static void qpw_fini_out(HWVoiceOut *hw) { - PWVoiceOut *pw = (PWVoiceOut *) hw; - PWVoice *v = &pw->v; - - if (v->stream) { - pwaudio *c = v->g; - pw_thread_loop_lock(c->thread_loop); - pw_stream_destroy(v->stream); - v->stream = NULL; - pw_thread_loop_unlock(c->thread_loop); - } + qpw_voice_fini(&PW_VOICE_OUT(hw)->v); } static void qpw_fini_in(HWVoiceIn *hw) { - PWVoiceIn *pw = (PWVoiceIn *) hw; - PWVoice *v = &pw->v; - - if (v->stream) { - pwaudio *c = v->g; - pw_thread_loop_lock(c->thread_loop); - pw_stream_destroy(v->stream); - v->stream = NULL; - pw_thread_loop_unlock(c->thread_loop); - } + qpw_voice_fini(&PW_VOICE_IN(hw)->v); } static void -qpw_enable_out(HWVoiceOut *hw, bool enable) +qpw_voice_set_enabled(PWVoice *v, bool enable) { - PWVoiceOut *po = (PWVoiceOut *) hw; - PWVoice *v = &po->v; 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); } +static void +qpw_enable_out(HWVoiceOut *hw, bool enable) +{ + qpw_voice_set_enabled(&PW_VOICE_OUT(hw)->v, enable); +} + static void qpw_enable_in(HWVoiceIn *hw, bool enable) { - PWVoiceIn *pi = (PWVoiceIn *) hw; - PWVoice *v = &pi->v; + qpw_voice_set_enabled(&PW_VOICE_IN(hw)->v, enable); +} + +static void +qpw_voice_set_volume(PWVoice *v, Volume *vol) +{ pwaudio *c = v->g; + int i, ret; + pw_thread_loop_lock(c->thread_loop); - pw_stream_set_active(v->stream, enable); + v->volume.channels = vol->channels; + + for (i = 0; i < vol->channels; ++i) { + v->volume.values[i] = (float)vol->vol[i] / 255; + } + + ret = pw_stream_set_control(v->stream, + SPA_PROP_channelVolumes, v->volume.channels, v->volume.values, 0); + trace_pw_vol(ret == 0 ? "success" : "failed"); + + v->muted = vol->mute; + float val = v->muted ? 1.f : 0.f; + ret = pw_stream_set_control(v->stream, SPA_PROP_mute, 1, &val, 0); pw_thread_loop_unlock(c->thread_loop); } static void qpw_volume_out(HWVoiceOut *hw, Volume *vol) { - PWVoiceOut *pw = (PWVoiceOut *) hw; - PWVoice *v = &pw->v; - pwaudio *c = v->g; - int i, ret; - - pw_thread_loop_lock(c->thread_loop); - v->volume.channels = vol->channels; - - for (i = 0; i < vol->channels; ++i) { - v->volume.values[i] = (float)vol->vol[i] / 255; - } - - ret = pw_stream_set_control(v->stream, - SPA_PROP_channelVolumes, v->volume.channels, v->volume.values, 0); - trace_pw_vol(ret == 0 ? "success" : "failed"); - - v->muted = vol->mute; - float val = v->muted ? 1.f : 0.f; - ret = pw_stream_set_control(v->stream, SPA_PROP_mute, 1, &val, 0); - pw_thread_loop_unlock(c->thread_loop); + qpw_voice_set_volume(&PW_VOICE_OUT(hw)->v, vol); } static void qpw_volume_in(HWVoiceIn *hw, Volume *vol) { - PWVoiceIn *pw = (PWVoiceIn *) hw; - PWVoice *v = &pw->v; - pwaudio *c = v->g; - int i, ret; - - pw_thread_loop_lock(c->thread_loop); - v->volume.channels = vol->channels; - - for (i = 0; i < vol->channels; ++i) { - v->volume.values[i] = (float)vol->vol[i] / 255; - } - - ret = pw_stream_set_control(v->stream, - SPA_PROP_channelVolumes, v->volume.channels, v->volume.values, 0); - trace_pw_vol(ret == 0 ? "success" : "failed"); - - v->muted = vol->mute; - float val = v->muted ? 1.f : 0.f; - ret = pw_stream_set_control(v->stream, SPA_PROP_mute, 1, &val, 0); - pw_thread_loop_unlock(c->thread_loop); + qpw_voice_set_volume(&PW_VOICE_IN(hw)->v, vol); } static int wait_resync(pwaudio *pw) From 0c57a05533d12a3788739666f67b425709f4a132 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 6 May 2023 20:37:32 +0400 Subject: [PATCH 16/19] audio/pw: add more error reporting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marc-André Lureau Reviewed-by: Volker Rümelin Message-Id: <20230506163735.3481387-10-marcandre.lureau@redhat.com> --- audio/pwaudio.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/audio/pwaudio.c b/audio/pwaudio.c index 70f0c46240..f1c5e5dd48 100644 --- a/audio/pwaudio.c +++ b/audio/pwaudio.c @@ -429,6 +429,10 @@ create_stream(pwaudio *c, PWVoice *v, const char *stream_name, struct pw_properties *props; props = pw_properties_new(NULL, NULL); + if (!props) { + error_report("Failed to create PW properties: %s", g_strerror(errno)); + return -1; + } /* 75% of the timer period for faster updates */ buf_samples = (uint64_t)v->g->dev->timer_period * v->info.rate @@ -441,8 +445,8 @@ create_stream(pwaudio *c, PWVoice *v, const char *stream_name, pw_properties_set(props, PW_KEY_TARGET_OBJECT, name); } v->stream = pw_stream_new(c->core, stream_name, props); - if (v->stream == NULL) { + error_report("Failed to create PW stream: %s", g_strerror(errno)); return -1; } @@ -470,6 +474,7 @@ create_stream(pwaudio *c, PWVoice *v, const char *stream_name, PW_STREAM_FLAG_MAP_BUFFERS | PW_STREAM_FLAG_RT_PROCESS, params, n_params); if (res < 0) { + error_report("Failed to connect PW stream: %s", g_strerror(errno)); pw_stream_destroy(v->stream); return -1; } From 6f1b280e44297713bbdfffc92add4a64241b43d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 6 May 2023 20:37:33 +0400 Subject: [PATCH 17/19] audio/pw: simplify error reporting in stream creation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit create_stream() now reports on all error paths. Signed-off-by: Marc-André Lureau Reviewed-by: Volker Rümelin Message-Id: <20230506163735.3481387-11-marcandre.lureau@redhat.com> --- audio/pwaudio.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/audio/pwaudio.c b/audio/pwaudio.c index f1c5e5dd48..7d5005b971 100644 --- a/audio/pwaudio.c +++ b/audio/pwaudio.c @@ -486,8 +486,6 @@ static int qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name, const char *name, enum spa_direction dir) { - int r; - switch (v->info.channels) { case 8: v->info.position[0] = SPA_AUDIO_CHANNEL_FL; @@ -540,13 +538,7 @@ qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name, } /* create a new unconnected pwstream */ - r = create_stream(c, v, stream_name, name, dir); - if (r < 0) { - AUD_log(AUDIO_CAP, "Failed to create stream."); - return -1; - } - - return r; + return create_stream(c, v, stream_name, name, dir); } static int @@ -577,7 +569,6 @@ qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id, ppdo->name, SPA_DIRECTION_OUTPUT); if (r < 0) { - error_report("qpw_stream_new for playback failed"); pw_thread_loop_unlock(c->thread_loop); return -1; } @@ -621,7 +612,6 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) r = qpw_stream_new(c, v, ppdo->stream_name ? : c->dev->id, ppdo->name, SPA_DIRECTION_INPUT); if (r < 0) { - error_report("qpw_stream_new for recording failed"); pw_thread_loop_unlock(c->thread_loop); return -1; } From 8297b3d3d00e475d5b2ba042ed2077167d312492 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 6 May 2023 20:37:34 +0400 Subject: [PATCH 18/19] audio/pw: remove wrong comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The stream is actually created connected. Signed-off-by: Marc-André Lureau Reviewed-by: Volker Rümelin Message-Id: <20230506163735.3481387-12-marcandre.lureau@redhat.com> --- audio/pwaudio.c | 1 - 1 file changed, 1 deletion(-) diff --git a/audio/pwaudio.c b/audio/pwaudio.c index 7d5005b971..a101ffeff1 100644 --- a/audio/pwaudio.c +++ b/audio/pwaudio.c @@ -537,7 +537,6 @@ qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name, break; } - /* create a new unconnected pwstream */ return create_stream(c, v, stream_name, name, dir); } From 92f69a2c9bca26ee756c7cb932142664aca9c9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Lureau?= Date: Sat, 6 May 2023 20:37:35 +0400 Subject: [PATCH 19/19] audio/pw: improve channel position code MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow PulseAudio backend comment and code, and only implement the channels QEMU actually supports at this point, and add the same comment about limits and future mappings. Simplify a bit the code. Signed-off-by: Marc-André Lureau Reviewed-by: Volker Rümelin Message-Id: <20230506163735.3481387-13-marcandre.lureau@redhat.com> --- audio/pwaudio.c | 75 +++++++++++++++++-------------------------------- 1 file changed, 26 insertions(+), 49 deletions(-) diff --git a/audio/pwaudio.c b/audio/pwaudio.c index a101ffeff1..b6a38738ee 100644 --- a/audio/pwaudio.c +++ b/audio/pwaudio.c @@ -417,8 +417,8 @@ pw_to_audfmt(enum spa_audio_format fmt, int *endianness, } static int -create_stream(pwaudio *c, PWVoice *v, const char *stream_name, - const char *name, enum spa_direction dir) +qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name, + const char *name, enum spa_direction dir) { int res; uint32_t n_params; @@ -482,62 +482,37 @@ create_stream(pwaudio *c, PWVoice *v, const char *stream_name, return 0; } -static int -qpw_stream_new(pwaudio *c, PWVoice *v, const char *stream_name, - const char *name, enum spa_direction dir) +static void +qpw_set_position(uint32_t channels, uint32_t position[SPA_AUDIO_MAX_CHANNELS]) { - switch (v->info.channels) { + memcpy(position, (uint32_t[SPA_AUDIO_MAX_CHANNELS]) { SPA_AUDIO_CHANNEL_UNKNOWN, }, + sizeof(uint32_t) * SPA_AUDIO_MAX_CHANNELS); + /* + * TODO: This currently expects the only frontend supporting more than 2 + * channels is the usb-audio. We will need some means to set channel + * order when a new frontend gains multi-channel support. + */ + switch (channels) { case 8: - v->info.position[0] = SPA_AUDIO_CHANNEL_FL; - v->info.position[1] = SPA_AUDIO_CHANNEL_FR; - v->info.position[2] = SPA_AUDIO_CHANNEL_FC; - v->info.position[3] = SPA_AUDIO_CHANNEL_LFE; - v->info.position[4] = SPA_AUDIO_CHANNEL_RL; - v->info.position[5] = SPA_AUDIO_CHANNEL_RR; - v->info.position[6] = SPA_AUDIO_CHANNEL_SL; - v->info.position[7] = SPA_AUDIO_CHANNEL_SR; - break; + position[6] = SPA_AUDIO_CHANNEL_SL; + position[7] = SPA_AUDIO_CHANNEL_SR; + /* fallthrough */ case 6: - v->info.position[0] = SPA_AUDIO_CHANNEL_FL; - v->info.position[1] = SPA_AUDIO_CHANNEL_FR; - v->info.position[2] = SPA_AUDIO_CHANNEL_FC; - v->info.position[3] = SPA_AUDIO_CHANNEL_LFE; - v->info.position[4] = SPA_AUDIO_CHANNEL_RL; - v->info.position[5] = SPA_AUDIO_CHANNEL_RR; - break; - case 5: - v->info.position[0] = SPA_AUDIO_CHANNEL_FL; - v->info.position[1] = SPA_AUDIO_CHANNEL_FR; - v->info.position[2] = SPA_AUDIO_CHANNEL_FC; - v->info.position[3] = SPA_AUDIO_CHANNEL_LFE; - v->info.position[4] = SPA_AUDIO_CHANNEL_RC; - break; - case 4: - v->info.position[0] = SPA_AUDIO_CHANNEL_FL; - v->info.position[1] = SPA_AUDIO_CHANNEL_FR; - v->info.position[2] = SPA_AUDIO_CHANNEL_FC; - v->info.position[3] = SPA_AUDIO_CHANNEL_RC; - break; - case 3: - v->info.position[0] = SPA_AUDIO_CHANNEL_FL; - v->info.position[1] = SPA_AUDIO_CHANNEL_FR; - v->info.position[2] = SPA_AUDIO_CHANNEL_LFE; - break; + position[2] = SPA_AUDIO_CHANNEL_FC; + position[3] = SPA_AUDIO_CHANNEL_LFE; + position[4] = SPA_AUDIO_CHANNEL_RL; + position[5] = SPA_AUDIO_CHANNEL_RR; + /* fallthrough */ case 2: - v->info.position[0] = SPA_AUDIO_CHANNEL_FL; - v->info.position[1] = SPA_AUDIO_CHANNEL_FR; + position[0] = SPA_AUDIO_CHANNEL_FL; + position[1] = SPA_AUDIO_CHANNEL_FR; break; case 1: - v->info.position[0] = SPA_AUDIO_CHANNEL_MONO; + position[0] = SPA_AUDIO_CHANNEL_MONO; break; default: - for (size_t i = 0; i < v->info.channels; i++) { - v->info.position[i] = SPA_AUDIO_CHANNEL_UNKNOWN; - } - break; + dolog("Internal error: unsupported channel count %d\n", channels); } - - return create_stream(c, v, stream_name, name, dir); } static int @@ -555,6 +530,7 @@ qpw_init_out(HWVoiceOut *hw, struct audsettings *as, void *drv_opaque) v->info.format = audfmt_to_pw(as->fmt, as->endianness); v->info.channels = as->nchannels; + qpw_set_position(as->nchannels, v->info.position); v->info.rate = as->freq; obt_as.fmt = @@ -601,6 +577,7 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque) v->info.format = audfmt_to_pw(as->fmt, as->endianness); v->info.channels = as->nchannels; + qpw_set_position(as->nchannels, v->info.position); v->info.rate = as->freq; obt_as.fmt =