Hi,
We’re experiencing a memory leak when using the v4l2 AV1 encoder in GStreamer (nvv4l2av1enc). The leak happens when changing the video caps. For example, halving the framerate or resolution. The available memory shrinks until the application crashes. This is a minimal reproducible example using Rust:
main.rs
use std::time::Duration;
use gst::glib::GString;
use gst::prelude::{ElementExt, GstBinExtManual, GstObjectExt, ObjectExt, PadExtManual};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
gst::init()?;
let source_element = gst::ElementFactory::make("videotestsrc")
.name("videotestsrc")
.build()?;
let caps = gst_video::VideoCapsBuilder::new()
.width(1920)
.height(1536)
.framerate(gst::Fraction::new(15, 1))
.build();
let camera_capsfilter = gst::ElementFactory::make("capsfilter")
.name("camera_capsfilter")
.property("caps", &caps)
.build()?;
let videoconvert = gst::ElementFactory::make("nvvidconv")
.name("hw_vidconv")
.build()?;
let videorate = gst::ElementFactory::make("videorate")
.name("videorate")
.property("drop-only", true)
.property("skip-to-first", true)
.build()?;
let queue =gst::ElementFactory::make("queue")
.name("only_queue")
.property("max-size-bytes", 0u32)
.property("max-size-time", 0u64)
.property("max-size-buffers", 4u32)
.build()?;
let caps2 = gst_video::VideoCapsBuilder::new()
.features(["memory:NVMM"])
.width(1920)
.height(1536)
.framerate(gst::Fraction::new(15, 1))
.build();
let capsfilter_for_framerate = gst::ElementFactory::make("capsfilter")
.name("framerate_capsfilter")
.property("caps", &caps2)
.build()?;
let nvv4l2av1enc = gst::ElementFactory::make("nvv4l2av1enc").build()?;
let fakesink = gst::ElementFactory::make("fakesink")
.name("fakesink")
.build()?;
tokio::spawn(toggle_caps(capsfilter_for_framerate.clone()));
let pipeline = gst::Pipeline::new();
pipeline.add_many([
&source_element,
&camera_capsfilter,
&videoconvert,
&videorate,
&queue,
&capsfilter_for_framerate,
&nvv4l2av1enc,
&fakesink,
])?;
gst::Element::link_many([
&source_element,
&camera_capsfilter,
&videoconvert,
&videorate,
&queue,
&capsfilter_for_framerate,
&nvv4l2av1enc,
&fakesink,
])?;
pipeline.set_state(gst::State::Playing)?;
let bus = pipeline.bus().unwrap();
for msg in bus.iter_timed(gst::ClockTime::NONE) {
match msg.view() {
gst::MessageView::Eos(_) => {
println!("End of stream");
break;
}
gst::MessageView::Error(err) => {
eprintln!(
"Error from {}: {} ({})",
err.src().map(|s| s.path_string()).unwrap_or_else(|| GString::from("unknown".to_string())),
err.error(),
err.debug().unwrap_or_else(|| GString::from("no debug info".to_string()))
);
break;
}
_ => (),
}
}
Ok(())
}
async fn toggle_caps(capsfilter: gst::Element) {
let mut current_fps = 15;
let mut current_width = 1920;
let mut current_height = 1536;
loop {
tokio::time::sleep(Duration::from_millis(500)).await;
let caps = gst_video::VideoCapsBuilder::new()
.features(["memory:NVMM"])
.width(current_width)
.height(current_height)
.framerate(gst::Fraction::new(current_fps, 1))
.build();
println!("Setting caps: {}x{} @ {}fps", current_width, current_height, current_fps);
capsfilter.set_property("caps", &caps);
if current_height == 1536 {
current_height = 768;
current_width = 960;
} else {
current_height = 1536;
current_width = 1920;
}
current_fps = if current_fps == 15 { 7 } else { 15 };
}
}
Cargo.toml
[package]
name = "min-reproducable-example"
version = "0.1.0"
edition = "2024"
[dependencies]
anyhow = "1.0.98"
gst = { package = "gstreamer", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs.git", tag = "0.23.5", features = ["v1_24"] }
gst-video = { package = "gstreamer-video", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs.git", tag = "0.23.5", features = ["v1_24"] }
tokio = { version = "1.46.1", features = ["rt", "rt-multi-thread", "macros", "time"] }
Each time we set the caps, we see a change in EMC bandwidth. This also happens when using the h264 HW encoder.
L4T version: 36.4.0
GStreamer version: 1.24.8
Any tips or suggestions would be greatly appreciated!