mirror of
https://github.com/rustdesk/qemu-display.git
synced 2025-08-17 16:25:39 +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,
|
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)]
|
#[derive(Debug)]
|
||||||
pub struct Volume {
|
pub struct Volume {
|
||||||
pub mute: bool,
|
pub mute: bool,
|
||||||
@ -63,7 +86,7 @@ trait Audio {
|
|||||||
#[derivative(Debug)]
|
#[derivative(Debug)]
|
||||||
pub struct Audio {
|
pub struct Audio {
|
||||||
#[derivative(Debug = "ignore")]
|
#[derivative(Debug = "ignore")]
|
||||||
pub proxy: AudioProxy<'static>,
|
pub proxy: AsyncAudioProxy<'static>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -235,20 +258,21 @@ impl<E: 'static + EventSender<Event = AudioInEvent>> AudioInListener<E> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Audio {
|
impl Audio {
|
||||||
pub fn new(conn: &zbus::Connection) -> Result<Self> {
|
pub async fn new(conn: &zbus::azync::Connection) -> Result<Self> {
|
||||||
let proxy = AudioProxy::new(conn)?;
|
let proxy = AsyncAudioProxy::new(conn).await?;
|
||||||
Ok(Self { proxy })
|
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
|
// 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")
|
.destination("org.qemu")
|
||||||
.path("/org/qemu/Display1")
|
.path("/org/qemu/Display1")
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.build()
|
.build_async()
|
||||||
|
.await
|
||||||
.unwrap();
|
.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
|
let has_audio = introspect
|
||||||
.nodes()
|
.nodes()
|
||||||
.iter()
|
.iter()
|
||||||
@ -256,10 +280,12 @@ impl Audio {
|
|||||||
has_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 (p0, p1) = UnixStream::pair()?;
|
||||||
let (tx, rx) = mpsc::channel();
|
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 _thread = thread::spawn(move || {
|
||||||
let c = zbus::Connection::new_unix_client(p1, false).unwrap();
|
let c = zbus::Connection::new_unix_client(p1, false).unwrap();
|
||||||
@ -283,10 +309,12 @@ impl Audio {
|
|||||||
Ok(rx)
|
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 (p0, p1) = UnixStream::pair()?;
|
||||||
let (tx, rx) = mpsc::channel();
|
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 _thread = thread::spawn(move || {
|
||||||
let c = zbus::Connection::new_unix_client(p1, false).unwrap();
|
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 gio::ApplicationFlags;
|
||||||
use glib::{clone, MainContext};
|
use glib::{clone, MainContext};
|
||||||
use gtk::{gio, glib, prelude::*};
|
use gtk::{gio, glib, prelude::*};
|
||||||
|
use once_cell::sync::OnceCell;
|
||||||
use qemu_display_listener::Console;
|
use qemu_display_listener::Console;
|
||||||
use zbus::Connection;
|
use zbus::Connection;
|
||||||
|
|
||||||
|
mod audio;
|
||||||
mod display_qemu;
|
mod display_qemu;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@ -15,6 +17,8 @@ fn main() {
|
|||||||
.expect("Failed to connect to DBus")
|
.expect("Failed to connect to DBus")
|
||||||
.into();
|
.into();
|
||||||
|
|
||||||
|
let audio = std::sync::Arc::new(OnceCell::new());
|
||||||
|
|
||||||
app.connect_activate(move |app| {
|
app.connect_activate(move |app| {
|
||||||
let window = gtk::ApplicationWindow::new(app);
|
let window = gtk::ApplicationWindow::new(app);
|
||||||
|
|
||||||
@ -22,11 +26,17 @@ fn main() {
|
|||||||
window.set_default_size(1024, 768);
|
window.set_default_size(1024, 768);
|
||||||
|
|
||||||
let conn = conn.clone();
|
let conn = conn.clone();
|
||||||
|
let audio_clone = audio.clone();
|
||||||
MainContext::default().spawn_local(clone!(@strong window => async move {
|
MainContext::default().spawn_local(clone!(@strong window => async move {
|
||||||
let console = Console::new(&conn, 0).await.expect("Failed to get the QEMU console");
|
let console = Console::new(&conn, 0).await.expect("Failed to get the QEMU console");
|
||||||
let display = display_qemu::DisplayQemu::new(console);
|
let display = display_qemu::DisplayQemu::new(console);
|
||||||
window.set_child(Some(&display));
|
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();
|
window.show();
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user