vhost-device-sound: Add GStreamer audio backend support
Add gstreamer backend. Add gstreamer-related crates used in the vhost-device-sound module This affects only the vhost-device-sound module, and updates the following dependencies: - dependency-name: gstreamer dependency-version: 0.24.2 - dependency-name: gstreamer-app dependency-version: 0.24.2 - dependency-name: gstreamer-audio dependency-version: 0.24.2 Signed-off-by: nicholasdezai <nicholasdezai@gmail.com>
This commit is contained in:
parent
b6fd16f949
commit
a0261d689e
10 changed files with 1689 additions and 233 deletions
640
Cargo.lock
generated
640
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
- [[#876]](https://github.com/rust-vmm/vhost-device/pull/876) Add GStreamer audio backend support
|
||||||
- [[#806]](https://github.com/rust-vmm/vhost-device/pull/806) Add controls field in VirtioSoundConfig
|
- [[#806]](https://github.com/rust-vmm/vhost-device/pull/806) Add controls field in VirtioSoundConfig
|
||||||
- [[#746]](https://github.com/rust-vmm/vhost-device/pull/746) Add new sampling rates 12000Hz and 24000Hz
|
- [[#746]](https://github.com/rust-vmm/vhost-device/pull/746) Add new sampling rates 12000Hz and 24000Hz
|
||||||
|
|
||||||
|
|
@ -14,6 +15,10 @@
|
||||||
- [[#808]](https://github.com/rust-vmm/vhost-device/pull/808) pipewire: Fix rand module imports
|
- [[#808]](https://github.com/rust-vmm/vhost-device/pull/808) pipewire: Fix rand module imports
|
||||||
- [[#884]](https://github.com/rust-vmm/vhost-device/pull/884) vhost-device-sound/pipewire: fix wrong format
|
- [[#884]](https://github.com/rust-vmm/vhost-device/pull/884) vhost-device-sound/pipewire: fix wrong format
|
||||||
|
|
||||||
|
### Limitations
|
||||||
|
|
||||||
|
- GStreamer backend: 20-bit PCM formats (VIRTIO_SND_PCM_FMT_S20/U20) are not directly supported by GStreamer and are automatically converted to 24/32-bit formats
|
||||||
|
|
||||||
### Deprecated
|
### Deprecated
|
||||||
|
|
||||||
## v0.2.0
|
## v0.2.0
|
||||||
|
|
|
||||||
|
|
@ -12,9 +12,10 @@ edition = "2021"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"]
|
xen = ["vm-memory/xen", "vhost/xen", "vhost-user-backend/xen"]
|
||||||
default = ["alsa-backend", "pw-backend"]
|
default = ["alsa-backend", "pw-backend", "gst-backend"]
|
||||||
alsa-backend = ["dep:alsa"]
|
alsa-backend = ["dep:alsa"]
|
||||||
pw-backend = ["pw"]
|
pw-backend = ["pw"]
|
||||||
|
gst-backend = ["dep:gst", "dep:gst-app", "dep:gst-audio"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = { version = "4.5", features = ["derive"] }
|
clap = { version = "4.5", features = ["derive"] }
|
||||||
|
|
@ -32,6 +33,9 @@ vmm-sys-util = "0.14"
|
||||||
[target.'cfg(target_env = "gnu")'.dependencies]
|
[target.'cfg(target_env = "gnu")'.dependencies]
|
||||||
alsa = { version = "0.10", optional = true }
|
alsa = { version = "0.10", optional = true }
|
||||||
pw = { package = "pipewire", version = "0.9.2", optional = true }
|
pw = { package = "pipewire", version = "0.9.2", optional = true }
|
||||||
|
gst = { package = "gstreamer", version = "0.24.2", optional = true, features = ["v1_24"] }
|
||||||
|
gst-app = { package = "gstreamer-app", version = "0.24.2", optional = true, features = ["v1_24"] }
|
||||||
|
gst-audio = {package = "gstreamer-audio", version = "0.24.2", optional = true, features = ["v1_24"] }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
rstest = "0.26.1"
|
rstest = "0.26.1"
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ generated with help2man target/debug/vhost-device-sound |mandoc
|
||||||
vhost-user Unix domain socket path
|
vhost-user Unix domain socket path
|
||||||
|
|
||||||
--backend <BACKEND>
|
--backend <BACKEND>
|
||||||
audio backend to be used [possible values: null, pipewire, alsa]
|
audio backend to be used [possible values: null, pipewire, alsa, gstreamer]
|
||||||
|
|
||||||
-h, --help
|
-h, --help
|
||||||
Print help
|
Print help
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,7 @@ pub enum BackendType {
|
||||||
Pipewire,
|
Pipewire,
|
||||||
#[cfg(all(feature = "alsa-backend", target_env = "gnu"))]
|
#[cfg(all(feature = "alsa-backend", target_env = "gnu"))]
|
||||||
Alsa,
|
Alsa,
|
||||||
|
#[cfg(all(feature = "gst-backend", target_env = "gnu"))]
|
||||||
|
#[value(name = "gstreamer")]
|
||||||
|
GStreamer,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,15 @@ mod null;
|
||||||
#[cfg(all(feature = "pw-backend", target_env = "gnu"))]
|
#[cfg(all(feature = "pw-backend", target_env = "gnu"))]
|
||||||
mod pipewire;
|
mod pipewire;
|
||||||
|
|
||||||
|
#[cfg(all(feature = "gst-backend", target_env = "gnu"))]
|
||||||
|
mod gstreamer;
|
||||||
|
|
||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
#[cfg(all(feature = "alsa-backend", target_env = "gnu"))]
|
#[cfg(all(feature = "alsa-backend", target_env = "gnu"))]
|
||||||
use self::alsa::AlsaBackend;
|
use self::alsa::AlsaBackend;
|
||||||
|
#[cfg(all(feature = "gst-backend", target_env = "gnu"))]
|
||||||
|
use self::gstreamer::GStreamerBackend;
|
||||||
use self::null::NullBackend;
|
use self::null::NullBackend;
|
||||||
#[cfg(all(feature = "pw-backend", target_env = "gnu"))]
|
#[cfg(all(feature = "pw-backend", target_env = "gnu"))]
|
||||||
use self::pipewire::PwBackend;
|
use self::pipewire::PwBackend;
|
||||||
|
|
@ -61,6 +66,12 @@ pub fn alloc_audio_backend(
|
||||||
}
|
}
|
||||||
#[cfg(all(feature = "alsa-backend", target_env = "gnu"))]
|
#[cfg(all(feature = "alsa-backend", target_env = "gnu"))]
|
||||||
BackendType::Alsa => Ok(Box::new(AlsaBackend::new(streams))),
|
BackendType::Alsa => Ok(Box::new(AlsaBackend::new(streams))),
|
||||||
|
#[cfg(all(feature = "gst-backend", target_env = "gnu"))]
|
||||||
|
BackendType::GStreamer => {
|
||||||
|
Ok(Box::new(GStreamerBackend::new(streams).map_err(|err| {
|
||||||
|
crate::Error::UnexpectedAudioBackendError(err.into())
|
||||||
|
})?))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
1174
vhost-device-sound/src/audio_backends/gstreamer.rs
Normal file
1174
vhost-device-sound/src/audio_backends/gstreamer.rs
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,68 @@
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
use tempfile::TempDir;
|
||||||
|
|
||||||
|
/// A test harness that sets up an isolated environment for GStreamer tests.
|
||||||
|
/// Unlike PipeWire, GStreamer is a library and doesn't need a daemon.
|
||||||
|
/// We only need to isolate runtime dirs and registry files.
|
||||||
|
pub struct GStreamerTestHarness {
|
||||||
|
_runtime_dir: TempDir,
|
||||||
|
old_runtime_dir: Option<String>,
|
||||||
|
old_gst_registry: Option<String>,
|
||||||
|
old_gst_debug: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GStreamerTestHarness {
|
||||||
|
/// Create a new isolated GStreamer test environment.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
let tmpdir = tempfile::tempdir().expect("Failed to create temp dir for GStreamer");
|
||||||
|
|
||||||
|
let old_runtime_dir = env::var("XDG_RUNTIME_DIR").ok();
|
||||||
|
let old_gst_registry = env::var("GST_REGISTRY").ok();
|
||||||
|
let old_gst_debug = env::var("GST_DEBUG").ok();
|
||||||
|
|
||||||
|
log::debug!("old_runtime_dir: {:?}", old_runtime_dir);
|
||||||
|
log::debug!("old_gst_registry: {:?}", old_gst_registry);
|
||||||
|
log::debug!("old_gst_debug: {:?}", old_gst_debug);
|
||||||
|
|
||||||
|
env::set_var("XDG_RUNTIME_DIR", tmpdir.path());
|
||||||
|
env::set_var("GST_REGISTRY", tmpdir.path().join("gst-registry.bin"));
|
||||||
|
env::set_var("GST_DEBUG", "ERROR");
|
||||||
|
|
||||||
|
log::debug!(
|
||||||
|
"Started isolated GStreamer test environment at {:?}",
|
||||||
|
tmpdir.path()
|
||||||
|
);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
_runtime_dir: tmpdir,
|
||||||
|
old_runtime_dir,
|
||||||
|
old_gst_registry,
|
||||||
|
old_gst_debug,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for GStreamerTestHarness {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if let Some(val) = &self.old_runtime_dir {
|
||||||
|
env::set_var("XDG_RUNTIME_DIR", val);
|
||||||
|
} else {
|
||||||
|
env::remove_var("XDG_RUNTIME_DIR");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = &self.old_gst_registry {
|
||||||
|
env::set_var("GST_REGISTRY", val);
|
||||||
|
} else {
|
||||||
|
env::remove_var("GST_REGISTRY");
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(val) = &self.old_gst_debug {
|
||||||
|
env::set_var("GST_DEBUG", val);
|
||||||
|
} else {
|
||||||
|
env::remove_var("GST_DEBUG");
|
||||||
|
}
|
||||||
|
|
||||||
|
log::debug!("Isolated GStreamer environment cleaned up");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -55,6 +55,10 @@ mod tests {
|
||||||
all(feature = "alsa-backend", target_env = "gnu"),
|
all(feature = "alsa-backend", target_env = "gnu"),
|
||||||
case::alsa("alsa", BackendType::Alsa)
|
case::alsa("alsa", BackendType::Alsa)
|
||||||
)]
|
)]
|
||||||
|
#[cfg_attr(
|
||||||
|
all(feature = "gst-backend", target_env = "gnu"),
|
||||||
|
case::gstreamer("gstreamer", BackendType::GStreamer)
|
||||||
|
)]
|
||||||
fn test_cli_backend_arg(#[case] backend_name: &str, #[case] backend: BackendType) {
|
fn test_cli_backend_arg(#[case] backend_name: &str, #[case] backend: BackendType) {
|
||||||
let args: SoundArgs = Parser::parse_from([
|
let args: SoundArgs = Parser::parse_from([
|
||||||
"",
|
"",
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,12 @@ pub enum Error {
|
||||||
DescriptorWriteFailed,
|
DescriptorWriteFailed,
|
||||||
#[error("Could not disconnect stream")]
|
#[error("Could not disconnect stream")]
|
||||||
CouldNotDisconnectStream,
|
CouldNotDisconnectStream,
|
||||||
|
#[cfg(feature = "gst-backend")]
|
||||||
|
#[error("Could not start stream")]
|
||||||
|
CouldNotStartStream,
|
||||||
|
#[cfg(feature = "gst-backend")]
|
||||||
|
#[error("Could not stop stream")]
|
||||||
|
CouldNotStopStream,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Result<T> = std::result::Result<T, Error>;
|
type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
@ -323,7 +329,8 @@ impl Request {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
/// Returns the length of the sound data [`virtio_queue::desc::RawDescriptor`].
|
/// Returns the length of the sound data
|
||||||
|
/// [`virtio_queue::desc::RawDescriptor`].
|
||||||
pub const fn len(&self) -> usize {
|
pub const fn len(&self) -> usize {
|
||||||
self.len
|
self.len
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue