Handle playback volume

Signed-off-by: Marc-André Lureau <marcandre.lureau@redhat.com>
This commit is contained in:
Marc-André Lureau 2021-03-07 18:26:08 +04:00
parent 3b7f0c5793
commit 0f75905cc8
3 changed files with 67 additions and 7 deletions

View File

@ -20,11 +20,18 @@ pub struct PCMInfo {
pub be: bool, pub be: bool,
} }
#[derive(Debug)]
pub struct Volume {
pub mute: bool,
pub volume: Vec<u8>,
}
#[derive(Debug)] #[derive(Debug)]
pub enum AudioOutEvent { pub enum AudioOutEvent {
Init { id: u64, info: PCMInfo }, Init { id: u64, info: PCMInfo },
Fini { id: u64 }, Fini { id: u64 },
SetEnabled { id: u64, enabled: bool }, SetEnabled { id: u64, enabled: bool },
SetVolume { id: u64, volume: Volume },
Write { id: u64, data: Vec<u8> }, Write { id: u64, data: Vec<u8> },
} }
@ -33,6 +40,7 @@ pub enum AudioInEvent {
Init { id: u64, info: PCMInfo }, Init { id: u64, info: PCMInfo },
Fini { id: u64 }, Fini { id: u64 },
SetEnabled { id: u64, enabled: bool }, SetEnabled { id: u64, enabled: bool },
SetVolume { id: u64, volume: Volume },
Read { id: u64 }, Read { id: u64 },
} }
@ -119,6 +127,17 @@ impl<E: 'static + EventSender<Event = AudioOutEvent>> AudioOutListener<E> {
self.send(AudioOutEvent::SetEnabled { id, enabled }) self.send(AudioOutEvent::SetEnabled { id, enabled })
} }
/// SetVolume method
fn set_volume(&mut self, id: u64, mute: bool, volume: serde_bytes::ByteBuf) {
self.send(AudioOutEvent::SetVolume {
id,
volume: Volume {
mute,
volume: volume.into_vec(),
},
});
}
/// Write method /// Write method
fn write(&mut self, id: u64, data: serde_bytes::ByteBuf) { fn write(&mut self, id: u64, data: serde_bytes::ByteBuf) {
self.send(AudioOutEvent::Write { self.send(AudioOutEvent::Write {
@ -191,6 +210,17 @@ impl<E: 'static + EventSender<Event = AudioInEvent>> AudioInListener<E> {
self.send(AudioInEvent::SetEnabled { id, enabled }) self.send(AudioInEvent::SetEnabled { id, enabled })
} }
/// SetVolume method
fn set_volume(&mut self, id: u64, mute: bool, volume: serde_bytes::ByteBuf) {
self.send(AudioInEvent::SetVolume {
id,
volume: Volume {
mute,
volume: volume.into_vec(),
},
});
}
/// Read method /// Read method
fn read(&mut self, id: u64, size: u64) -> Vec<u8> { fn read(&mut self, id: u64, size: u64) -> Vec<u8> {
dbg!((id, size)); dbg!((id, size));

View File

@ -20,6 +20,7 @@ glib = { git = "https://github.com/gtk-rs/gtk-rs", optional = true }
derivative = "2.2.0" derivative = "2.2.0"
gst = { package = "gstreamer", version = "0.16.7" } gst = { package = "gstreamer", version = "0.16.7" }
gst-app = { package = "gstreamer-app", version = "0.16.5" } gst-app = { package = "gstreamer-app", version = "0.16.5" }
gst-audio = { package = "gstreamer-audio", version = "0.16.5" }
[dependencies.gtk] [dependencies.gtk]
package = "gtk4" package = "gtk4"

View File

@ -1,13 +1,15 @@
use gst::prelude::*; use gst::prelude::*;
use gst_audio::StreamVolumeExt;
use qemu_display_listener::{Audio, PCMInfo};
use std::thread::{self, JoinHandle}; use std::thread::{self, JoinHandle};
use std::{collections::HashMap, error::Error}; use std::{collections::HashMap, error::Error};
use qemu_display_listener::{Audio, PCMInfo};
#[derive(Debug)] #[derive(Debug)]
struct OutStream { struct OutStream {
pipeline: gst::Pipeline, pipeline: gst::Pipeline,
src: gst_app::AppSrc, src: gst_app::AppSrc,
sink: gst::Element,
} }
fn pcminfo_as_caps(info: &PCMInfo) -> String { fn pcminfo_as_caps(info: &PCMInfo) -> String {
@ -44,23 +46,28 @@ impl OutStream {
.unwrap() .unwrap()
.dynamic_cast::<gst_app::AppSrc>() .dynamic_cast::<gst_app::AppSrc>()
.unwrap(); .unwrap();
let sink = pipeline.get_by_name("sink").unwrap();
Ok(Self { pipeline, src }) Ok(Self {
pipeline,
src,
sink,
})
} }
} }
#[derive(Debug)] #[derive(Debug)]
pub struct GstAudio { pub struct GstAudio {
thread: JoinHandle<()>, out_thread: JoinHandle<()>,
} }
impl GstAudio { impl GstAudio {
pub fn new(audio: Audio) -> Result<Self, Box<dyn Error>> { pub fn new(audio: Audio) -> Result<Self, Box<dyn Error>> {
gst::init()?; gst::init()?;
// TODO audio.listen_in() for capture.
let rx = audio.listen_out()?; let rx = audio.listen_out()?;
let mut out = HashMap::new(); let mut out = HashMap::new();
let thread = thread::spawn(move || loop { let out_thread = thread::spawn(move || loop {
match rx.recv() { match rx.recv() {
Ok(event) => { Ok(event) => {
use qemu_display_listener::AudioOutEvent::*; use qemu_display_listener::AudioOutEvent::*;
@ -95,6 +102,28 @@ impl GstAudio {
eprintln!("Stream was not setup yet: {}", id); eprintln!("Stream was not setup yet: {}", id);
} }
} }
SetVolume { id, volume } => {
if let Some(s) = out.get(&id) {
if let Some(stream_vol) = s
.pipeline
.get_by_interface(gst_audio::StreamVolume::static_type())
{
let stream_vol = stream_vol
.dynamic_cast::<gst_audio::StreamVolume>()
.unwrap();
stream_vol.set_mute(volume.mute);
if let Some(vol) = volume.volume.first() {
let vol = *vol as f64 / 255f64;
stream_vol
.set_volume(gst_audio::StreamVolumeFormat::Cubic, vol);
}
} else {
eprintln!("Volume not implemented for this pipeline");
}
} else {
eprintln!("Stream was not setup yet: {}", id);
}
}
Write { id, data } => { Write { id, data } => {
if let Some(s) = out.get(&id) { if let Some(s) = out.get(&id) {
let b = gst::Buffer::from_slice(data); let b = gst::Buffer::from_slice(data);
@ -108,6 +137,6 @@ impl GstAudio {
Err(e) => eprintln!("Audio thread error: {}", e), Err(e) => eprintln!("Audio thread error: {}", e),
} }
}); });
Ok(Self { thread }) Ok(Self { out_thread })
} }
} }