Add audio playback again

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
Marc-André Lureau 2021-07-09 16:12:43 +04:00
parent fc78781ec2
commit 36c84d517f
3 changed files with 115 additions and 11 deletions

View File

@ -22,6 +22,29 @@ pub struct PCMInfo {
pub be: bool,
}
impl PCMInfo {
pub fn gst_caps(&self) -> String {
let format = format!(
"{}{}{}",
if self.is_float {
"F"
} else if self.is_signed {
"S"
} else {
"U"
},
self.bits,
if self.be { "BE" } else { "LE" }
);
format!(
"audio/x-raw,format={format},channels={channels},rate={rate},layout=interleaved",
format = format,
channels = self.nchannels,
rate = self.freq,
)
}
}
#[derive(Debug)]
pub struct Volume {
pub mute: bool,
@ -63,7 +86,7 @@ trait Audio {
#[derivative(Debug)]
pub struct Audio {
#[derivative(Debug = "ignore")]
pub proxy: AudioProxy<'static>,
pub proxy: AsyncAudioProxy<'static>,
}
#[derive(Debug)]
@ -235,20 +258,21 @@ impl<E: 'static + EventSender<Event = AudioInEvent>> AudioInListener<E> {
}
impl Audio {
pub fn new(conn: &zbus::Connection) -> Result<Self> {
let proxy = AudioProxy::new(conn)?;
pub async fn new(conn: &zbus::azync::Connection) -> Result<Self> {
let proxy = AsyncAudioProxy::new(conn).await?;
Ok(Self { proxy })
}
pub fn available(conn: &zbus::Connection) -> bool {
pub async fn available(conn: &zbus::azync::Connection) -> bool {
// TODO: we may want to generalize interface detection
let ip = zbus::fdo::IntrospectableProxy::builder(conn)
let ip = zbus::fdo::AsyncIntrospectableProxy::builder(conn)
.destination("org.qemu")
.path("/org/qemu/Display1")
.unwrap()
.build()
.build_async()
.await
.unwrap();
let introspect = zbus::xml::Node::from_str(&ip.introspect().unwrap()).unwrap();
let introspect = zbus::xml::Node::from_str(&ip.introspect().await.unwrap()).unwrap();
let has_audio = introspect
.nodes()
.iter()
@ -256,10 +280,12 @@ impl Audio {
has_audio
}
pub fn listen_out(&self) -> Result<Receiver<AudioOutEvent>> {
pub async fn listen_out(&self) -> Result<Receiver<AudioOutEvent>> {
let (p0, p1) = UnixStream::pair()?;
let (tx, rx) = mpsc::channel();
self.proxy.register_out_listener(p0.as_raw_fd().into())?;
self.proxy
.register_out_listener(p0.as_raw_fd().into())
.await?;
let _thread = thread::spawn(move || {
let c = zbus::Connection::new_unix_client(p1, false).unwrap();
@ -283,10 +309,12 @@ impl Audio {
Ok(rx)
}
pub fn listen_in(&self) -> Result<Receiver<AudioInEvent>> {
pub async fn listen_in(&self) -> Result<Receiver<AudioInEvent>> {
let (p0, p1) = UnixStream::pair()?;
let (tx, rx) = mpsc::channel();
self.proxy.register_in_listener(p0.as_raw_fd().into())?;
self.proxy
.register_in_listener(p0.as_raw_fd().into())
.await?;
let _thread = thread::spawn(move || {
let c = zbus::Connection::new_unix_client(p1, false).unwrap();

66
qemu-rdw/src/audio.rs Normal file
View File

@ -0,0 +1,66 @@
use std::error::Error;
use std::result::Result;
use std::thread;
use qemu_display_listener::Audio;
#[derive(Debug, Default)]
pub struct Handler {
thread: Option<thread::JoinHandle<()>>,
}
impl Handler {
pub async fn new(conn: &zbus::azync::Connection) -> Result<Self, Box<dyn Error>> {
if !Audio::available(conn).await {
log::debug!("No qemu audio provided on the bus");
return Ok(Self::default());
}
let audio = Audio::new(conn).await?;
let rx = audio.listen_out().await?;
let mut gst = rdw::GstAudio::new()?;
let thread = thread::spawn(move || loop {
match rx.recv() {
Ok(event) => {
use qemu_display_listener::AudioOutEvent::*;
match event {
Init { id, info } => {
if let Err(e) = gst.init_out(id, &info.gst_caps()) {
log::warn!("Failed to initialize audio stream: {}", e);
}
}
Fini { id } => {
gst.fini_out(id);
}
SetEnabled { id, enabled } => {
if let Err(e) = gst.set_enabled_out(id, enabled) {
log::warn!("Failed to set enabled audio stream: {}", e);
}
}
SetVolume { id, volume } => {
if let Err(e) = gst.set_volume_out(
id,
volume.mute,
volume.volume.first().map(|v| *v as f64 / 255f64),
) {
log::warn!("Failed to set volume: {}", e);
}
}
Write { id, data } => {
if let Err(e) = gst.write_out(id, data) {
log::warn!("Failed to output stream: {}", e);
}
}
}
}
Err(e) => log::warn!("Audio thread error: {}", e),
}
});
Ok(Self {
thread: Some(thread),
})
}
}

View File

@ -1,9 +1,11 @@
use gio::ApplicationFlags;
use glib::{clone, MainContext};
use gtk::{gio, glib, prelude::*};
use once_cell::sync::OnceCell;
use qemu_display_listener::Console;
use zbus::Connection;
mod audio;
mod display_qemu;
fn main() {
@ -15,6 +17,8 @@ fn main() {
.expect("Failed to connect to DBus")
.into();
let audio = std::sync::Arc::new(OnceCell::new());
app.connect_activate(move |app| {
let window = gtk::ApplicationWindow::new(app);
@ -22,11 +26,17 @@ fn main() {
window.set_default_size(1024, 768);
let conn = conn.clone();
let audio_clone = audio.clone();
MainContext::default().spawn_local(clone!(@strong window => async move {
let console = Console::new(&conn, 0).await.expect("Failed to get the QEMU console");
let display = display_qemu::DisplayQemu::new(console);
window.set_child(Some(&display));
match audio::Handler::new(&conn).await {
Ok(handler) => audio_clone.set(handler).unwrap(),
Err(e) => log::warn!("Failed to setup audio: {}", e),
}
window.show();
}));
});