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
|
||||
|
||||
- [[#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
|
||||
- [[#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
|
||||
- [[#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
|
||||
|
||||
## v0.2.0
|
||||
|
|
|
|||
|
|
@ -12,9 +12,10 @@ edition = "2021"
|
|||
|
||||
[features]
|
||||
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"]
|
||||
pw-backend = ["pw"]
|
||||
gst-backend = ["dep:gst", "dep:gst-app", "dep:gst-audio"]
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5", features = ["derive"] }
|
||||
|
|
@ -32,6 +33,9 @@ vmm-sys-util = "0.14"
|
|||
[target.'cfg(target_env = "gnu")'.dependencies]
|
||||
alsa = { version = "0.10", 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]
|
||||
rstest = "0.26.1"
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ generated with help2man target/debug/vhost-device-sound |mandoc
|
|||
vhost-user Unix domain socket path
|
||||
|
||||
--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
|
||||
Print help
|
||||
|
|
|
|||
|
|
@ -25,4 +25,7 @@ pub enum BackendType {
|
|||
Pipewire,
|
||||
#[cfg(all(feature = "alsa-backend", target_env = "gnu"))]
|
||||
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"))]
|
||||
mod pipewire;
|
||||
|
||||
#[cfg(all(feature = "gst-backend", target_env = "gnu"))]
|
||||
mod gstreamer;
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
#[cfg(all(feature = "alsa-backend", target_env = "gnu"))]
|
||||
use self::alsa::AlsaBackend;
|
||||
#[cfg(all(feature = "gst-backend", target_env = "gnu"))]
|
||||
use self::gstreamer::GStreamerBackend;
|
||||
use self::null::NullBackend;
|
||||
#[cfg(all(feature = "pw-backend", target_env = "gnu"))]
|
||||
use self::pipewire::PwBackend;
|
||||
|
|
@ -61,6 +66,12 @@ pub fn alloc_audio_backend(
|
|||
}
|
||||
#[cfg(all(feature = "alsa-backend", target_env = "gnu"))]
|
||||
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"),
|
||||
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) {
|
||||
let args: SoundArgs = Parser::parse_from([
|
||||
"",
|
||||
|
|
|
|||
|
|
@ -29,6 +29,12 @@ pub enum Error {
|
|||
DescriptorWriteFailed,
|
||||
#[error("Could not disconnect stream")]
|
||||
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>;
|
||||
|
|
@ -323,7 +329,8 @@ impl Request {
|
|||
}
|
||||
|
||||
#[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 {
|
||||
self.len
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue