feat(runner): DMA-BUF support for Intel/AMD GPUs (#283)

## Description
Adds DMA-BUF support for non-NVIDIA GPUs using GL elements as conversion
workaround.

Tested with QSV and VA encoders, note that H.264 seems to only work for
QSV encoder, for `vah264(lp)enc` theres major CPU usage with DMA-BUF
enabled.

Don't mind the branch name, I was working on relay before and changed
gears to runner after noticing some DMA-BUF stuff 😅

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Chores**
- Added a `.dockerignore` file to exclude the `target` directory from
Docker builds.
	- Updated `.gitignore` to ignore the `target` directory.

- **New Features**
- Enhanced video processing pipeline with updated handling of DMA-BUF
support, including improved compatibility for different GPU vendors and
refined video element configurations.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

---------

Co-authored-by: DatCaptainHorse <DatCaptainHorse@users.noreply.github.com>
This commit is contained in:
Kristian Ollikainen
2025-05-20 08:50:24 +03:00
committed by GitHub
parent 6e19b2e9a0
commit d7e6da12ac
3 changed files with 42 additions and 21 deletions

View File

@@ -0,0 +1 @@
target/

View File

@@ -1 +1,2 @@
*.mp4
*.mp4
target/

View File

@@ -172,7 +172,7 @@ fn handle_encoder_audio(args: &args::Args) -> String {
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
// Parse command line arguments
let mut args = args::Args::new();
let args = args::Args::new();
if args.app.verbose {
args.debug_print();
}
@@ -206,10 +206,10 @@ async fn main() -> Result<(), Box<dyn Error>> {
}
let gpu = gpu.unwrap();
// TODO: Currently DMA-BUF only works for NVIDIA
if args.app.dma_buf && *gpu.vendor() != GPUVendor::NVIDIA {
log::warn!("DMA-BUF is currently unsupported outside NVIDIA GPUs, force disabling..");
args.app.dma_buf = false;
if args.app.dma_buf {
log::warn!(
"DMA-BUF is experimental, it may or may not improve performance, or even work at all."
);
}
// Handle video encoder selection
@@ -288,7 +288,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
))?;
caps_filter.set_property("caps", &caps);
// GL Upload Element
// GL Upload element
let glupload = gst::ElementFactory::make("glupload").build()?;
// GL color convert element
@@ -299,6 +299,9 @@ async fn main() -> Result<(), Box<dyn Error>> {
let gl_caps = gst::Caps::from_str("video/x-raw(memory:GLMemory),format=NV12")?;
gl_caps_filter.set_property("caps", &gl_caps);
// GL download element (needed only for DMA-BUF outside NVIDIA GPUs)
let gl_download = gst::ElementFactory::make("gldownload").build()?;
// Video Converter Element
let video_converter = gst::ElementFactory::make("videoconvert").build()?;
@@ -372,7 +375,11 @@ async fn main() -> Result<(), Box<dyn Error>> {
// If DMA-BUF is enabled, add glupload, color conversion and caps filter
if args.app.dma_buf {
pipeline.add_many(&[&glupload, &glcolorconvert, &gl_caps_filter])?;
if *gpu.vendor() == GPUVendor::NVIDIA {
pipeline.add_many(&[&glupload, &glcolorconvert, &gl_caps_filter])?;
} else {
pipeline.add_many(&[&glupload, &glcolorconvert, &gl_caps_filter, &gl_download])?;
}
}
// Link main audio branch
@@ -389,19 +396,31 @@ async fn main() -> Result<(), Box<dyn Error>> {
// With DMA-BUF, also link glupload and it's caps
if args.app.dma_buf {
// Link video source to caps_filter, glupload, gl_caps_filter, video_converter, video_encoder, webrtcsink
gst::Element::link_many(&[
&video_source,
&caps_filter,
&video_queue,
&video_clocksync,
&glupload,
&glcolorconvert,
&gl_caps_filter,
&video_encoder,
])?;
if *gpu.vendor() == GPUVendor::NVIDIA {
gst::Element::link_many(&[
&video_source,
&caps_filter,
&video_queue,
&video_clocksync,
&glupload,
&glcolorconvert,
&gl_caps_filter,
&video_encoder,
])?;
} else {
gst::Element::link_many(&[
&video_source,
&caps_filter,
&video_queue,
&video_clocksync,
&glupload,
&glcolorconvert,
&gl_caps_filter,
&gl_download,
&video_encoder,
])?;
}
} else {
// Link video source to caps_filter, video_converter, video_encoder, webrtcsink
gst::Element::link_many(&[
&video_source,
&caps_filter,
@@ -437,7 +456,7 @@ async fn main() -> Result<(), Box<dyn Error>> {
let result = run_pipeline(pipeline.clone()).await;
match result {
Ok(_) => log::info!("All tasks completed successfully"),
Ok(_) => log::info!("All tasks finished"),
Err(e) => {
log::error!("Error occurred in one of the tasks: {}", e);
return Err("Error occurred in one of the tasks".into());