mirror of
https://gitlab.com/marcandre.lureau/qemu-display.git
synced 2024-12-22 05:35:20 +00:00
Add audio playback again
Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
parent
fc78781ec2
commit
36c84d517f
@ -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
66
qemu-rdw/src/audio.rs
Normal 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),
|
||||
})
|
||||
}
|
||||
}
|
@ -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();
|
||||
}));
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user