Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Merge tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu
… into staging

ui & audio fixes

# -----BEGIN PGP SIGNATURE-----
#
# iQJQBAABCAA6FiEEh6m9kz+HxgbSdvYt2ujhCXWWnOUFAmS1N6IcHG1hcmNhbmRy
# ZS5sdXJlYXVAcmVkaGF0LmNvbQAKCRDa6OEJdZac5SYsD/44+FIoik9v478pZDTp
# CpaezX+DfsW1zee4Ana5eKJkrVld/xEa6i9/msfUHy12bha+kiJ4a6wLu3H4KRZc
# vX/t6sehG2wNcsV5wLhfcjsKzaNUkYpnxLhIZ0fOYXKA0fSBuM/Bsj6zzGTG6kQA
# nt/cK58r1wy63V7werZbA7BI8PF0opDUw5SrZqN0GeoN5clbdyLdcXvD50ibvkDf
# eOVjNQ3QH8IbihmgBVm1wUV8hTuvYRpBmeLJyk7NeR4bnPl3XGIAgtAY8hJL5LdY
# Bm+I3AuxMSskVcag/22QR8mGR0HhDbf3NZauw4ND3LhSctvNN5syaKHVnY5a9aGe
# QLVEV9pxXGfqzWQcsD2HmbupRoBihmp6+WsIpV8ZtuSfeD6slyObw+lqarSQL9b5
# 2C4UFmGCsCOk8rrczZRDp9IWbm23toc/QcQZtg/LhdlCr8nM+7m0XtyEY5WtT3U1
# 8rJEmjOHHqlD4cVBathc8+ZRjKr8HFRRo1ed6WKMoP6voTsw2fiR7I3Vdc7jO7h9
# A1lMiMoLdAXi0Q2VqbmBdLMgb4fXtLzYl2mcbzW0aEUm8uyUfDy2bkVIIUopu40M
# pROmLjaUzUVE3CruckBUCvoYZtJ5hBtvy3W2k8drBNylnP5B8tEqpxpPb+tSFk82
# xgT6oLp8En8asE293eaACbswuw==
# =W2Xa
# -----END PGP SIGNATURE-----
# gpg: Signature made Mon 17 Jul 2023 01:44:18 PM BST
# gpg:                using RSA key 87A9BD933F87C606D276F62DDAE8E10975969CE5
# gpg:                issuer "marcandre.lureau@redhat.com"
# gpg: Good signature from "Marc-André Lureau <marcandre.lureau@redhat.com>" [full]
# gpg:                 aka "Marc-André Lureau <marcandre.lureau@gmail.com>" [full]

* tag 'ui-pull-request' of https://gitlab.com/marcandre.lureau/qemu:
  audio/pw: improve channel position code
  audio/pw: remove wrong comment
  audio/pw: simplify error reporting in stream creation
  audio/pw: add more error reporting
  audio/pw: factorize some common code
  audio/pw: add more details on error
  audio/pw: trace during init before calling pipewire API
  audio/pw: needless check for NULL
  audio/pw: drop needless case statement
  audio/pw: Pipewire->PipeWire case fix for user-visible text
  tests/lcitool: add pipewire
  libvirt-ci: update submodule to cover pipewire
  ui/gtk: skip refresh if new dmabuf has been submitted
  ui/gtk: set scanout-mode right before scheduling draw
  virtio-gpu-udmabuf: correct naming of QemuDmaBuf size properties
  virtio-gpu: replace the surface with null surface when resetting
  ui/gtk: Make sure the right EGL context is currently bound
  ui/vnc-clipboard: fix infinite loop in inflate_buffer (CVE-2023-3255)
  virtio-gpu: fix potential divide-by-zero regression

Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
  • Loading branch information
rth7680 committed Jul 17, 2023
2 parents ed8ad97 + 92f69a2 commit f44ccac
Show file tree
Hide file tree
Showing 32 changed files with 171 additions and 191 deletions.
212 changes: 77 additions & 135 deletions audio/pwaudio.c
@@ -1,5 +1,5 @@
/*
* QEMU Pipewire audio driver
* QEMU PipeWire audio driver
*
* Copyright (c) 2023 Red Hat Inc.
*
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -197,16 +200,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 = {
Expand Down Expand Up @@ -424,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;
Expand All @@ -436,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
Expand All @@ -448,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;
}

Expand Down Expand Up @@ -477,78 +474,45 @@ 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;
}

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])
{
int r;

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);
}

/* 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;
}

static int
Expand All @@ -566,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 =
Expand All @@ -579,7 +544,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;
}
Expand Down Expand Up @@ -613,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 =
Expand All @@ -623,7 +588,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;
}
Expand All @@ -640,62 +604,55 @@ qpw_init_in(HWVoiceIn *hw, struct audsettings *as, void *drv_opaque)
}

static void
qpw_fini_out(HWVoiceOut *hw)
qpw_voice_fini(PWVoice *v)
{
PWVoiceOut *pw = (PWVoiceOut *) hw;
PWVoice *v = &pw->v;
pwaudio *c = v->g;

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);
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_in(HWVoiceIn *hw)
qpw_fini_out(HWVoiceOut *hw)
{
PWVoiceIn *pw = (PWVoiceIn *) hw;
PWVoice *v = &pw->v;
qpw_voice_fini(&PW_VOICE_OUT(hw)->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);
}
static void
qpw_fini_in(HWVoiceIn *hw)
{
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;
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);
qpw_voice_set_enabled(&PW_VOICE_IN(hw)->v, enable);
}

static void
qpw_volume_out(HWVoiceOut *hw, Volume *vol)
qpw_voice_set_volume(PWVoice *v, Volume *vol)
{
PWVoiceOut *pw = (PWVoiceOut *) hw;
PWVoice *v = &pw->v;
pwaudio *c = v->g;
int i, ret;

Expand All @@ -717,28 +674,15 @@ qpw_volume_out(HWVoiceOut *hw, Volume *vol)
}

static void
qpw_volume_in(HWVoiceIn *hw, Volume *vol)
qpw_volume_out(HWVoiceOut *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");
qpw_voice_set_volume(&PW_VOICE_OUT(hw)->v, vol);
}

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_in(HWVoiceIn *hw, Volume *vol)
{
qpw_voice_set_volume(&PW_VOICE_IN(hw)->v, vol);
}

static int wait_resync(pwaudio *pw)
Expand All @@ -760,6 +704,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)
{
Expand Down Expand Up @@ -794,27 +739,28 @@ 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);
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;
}

Expand Down Expand Up @@ -844,12 +790,8 @@ qpw_audio_init(Audiodev *dev)
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;
}

Expand Down
2 changes: 1 addition & 1 deletion audio/trace-events
Expand Up @@ -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"
Expand Down

0 comments on commit f44ccac

Please sign in to comment.