Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
49a6818
videostats: add video encoder stats elements
diegonieto Jun 15, 2025
46d5eab
video-encoder-stats: add vmaf metrics
diegonieto Jul 23, 2025
78fd66e
video-compare-stats: show half decoded outputs for mixing
diegonieto Jul 24, 2025
fe14dbb
video-encoder-stats: fix issue for different encoders
diegonieto Jul 31, 2025
683a3a7
video-compare-mixer: set CPU as default mixer
diegonieto Aug 4, 2025
2f396af
video-compare-stats: init element without the need of specify a backend
diegonieto Aug 6, 2025
f90dc64
video-compare-mixer: add property to allow split the screen or duplicate
diegonieto Aug 7, 2025
98ba55d
video-compare-mixer: handle crop for glvideomixer
diegonieto Aug 7, 2025
0591022
video-encoder-stats: allow to specify the internal decoder
diegonieto Aug 8, 2025
538c261
video-encoder-stats: implement request pad for decoded data
diegonieto Aug 12, 2025
41b4721
video-encoder-stats: calculate score every framerate frames
diegonieto Aug 14, 2025
d58fdb9
videostats: add originalbuffer internal dependency for building
diegonieto Aug 18, 2025
1072f15
encoderstats: add max. internal buffers metric
diegonieto Aug 20, 2025
a1b9e9f
videostats: update data layout
diegonieto Aug 21, 2025
cc73a84
encoderstats: fix name causing problems CPU stats
diegonieto Aug 25, 2025
bb37bbb
videostats: fix originalbuffer version
diegonieto Aug 26, 2025
6dc303a
video-compare-mixer: add navigation events
diegonieto Aug 27, 2025
99f0809
video-encoder-stats: create vmaf property and handle optional behaviour
diegonieto Sep 2, 2025
ae5e025
video-encoder-stats: handle input queues internally
diegonieto Sep 2, 2025
8859ea1
video-encoder-stats: add latency metric
diegonieto Sep 2, 2025
f5a33a5
video-encoder-stats: let vmaf score a N/A by default
diegonieto Sep 17, 2025
281ef4f
video-encoder-stats: add silent property and post stats as messages
diegonieto Sep 17, 2025
6290b22
videoencoderstats: update metric output as average process time
diegonieto Sep 18, 2025
0f8b586
video-encoder-stats: update meta timestamps of the current buffer ins…
diegonieto Sep 18, 2025
25205b3
Update video/stats/src/videoencoderstats.rs
diegonieto Sep 19, 2025
abc43b2
Update video/stats/src/videoencoderstats.rs
diegonieto Sep 19, 2025
77a5145
videocomparestats: remove end to end latency
diegonieto Sep 19, 2025
d75ac79
videostats: add plugin register in all the examples
diegonieto Sep 19, 2025
1242a6b
videostats: update to the latest gst-plugins-rs bindings
diegonieto Sep 23, 2025
8010eea
video-compare-mixer: unify overlay into a single element
diegonieto Sep 24, 2025
4226fc6
video-compare-mixer: add "overlay-stats" property to control overlay
diegonieto Sep 25, 2025
d72a5af
video-compare-mixer: remove deprected code
diegonieto Sep 26, 2025
c14ec55
video-encoder-stats: remove println!
diegonieto Sep 26, 2025
d93cd6c
video-compare-mixer: improve layout alignment and polish code
diegonieto Sep 28, 2025
4cdb1dc
videostats: update license and description
diegonieto Oct 2, 2025
5048d0b
video-encoder-stats: isolate encoder GstObject between two queues
diegonieto Oct 12, 2025
967e882
videostats: merge examples into a single one
diegonieto Oct 12, 2025
75e1447
video-compare-mixer: fix alignment issue
diegonieto Oct 21, 2025
48c5ad1
video-encoder-stats: provide a stats message when EOS
diegonieto Oct 28, 2025
b0ec05f
videoencoderstats: provide Max. buffers inside metric
diegonieto Oct 28, 2025
70ac20b
videoencoderstats: add num buffers needed for the first output metric
diegonieto Oct 28, 2025
83bf65f
videoencoderstats: add current buffers seens metric
diegonieto Oct 28, 2025
2f28375
video-encoder-stats: handle fractional framerate cases
msabiniok Dec 22, 2025
c7cb30d
video-encoder-stats: Fixes UX for "Num. Buffers first output"
rgonzalezfluendo Jan 21, 2026
6fbeeec
video-encoder-stats: Add max_processing_time
rgonzalezfluendo Jan 21, 2026
4e00969
videoencoderstats: fmt
rgonzalezfluendo Jan 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,252 changes: 776 additions & 476 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ members = [
"video/videofx",
"video/vvdec",
"video/webp",
"video/stats",
]

# Only plugins without external dependencies
Expand Down Expand Up @@ -124,6 +125,7 @@ default-members = [
"video/hsv",
"video/png",
"video/rav1e",
"video/stats",
]

[profile.release]
Expand Down
51 changes: 51 additions & 0 deletions video/stats/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
[package]
name = "gst-plugin-videostats"
version = "0.1.0"
authors = ["Diego Nieto <dnieto@fluendo.com>"]
repository = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs"
license = "MPL-2.0"
edition = "2021"
description = "GStreamer Video Stats Plugin"

[dependencies]
hound = "3"
anyhow = "1"
glib.workspace = true
gst.workspace = true
gst-video.workspace = true
human_bytes = { version = "0.4", default-features = false }
atomic_refcell = "0.1"
gst-plugin-originalbuffer = { git = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs", tag = "0.13.0" }

[dev-dependencies]
gst-check.workspace = true

[lib]
name = "gstvideostats"
crate-type = ["cdylib", "rlib"]
path = "src/lib.rs"

[build-dependencies]
gst-plugin-version-helper = { git = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs" }

[features]
capi = []
static = []
doc = ["gst/v1_16"]

[package.metadata.capi]
min_version = "0.9.21"

[package.metadata.capi.header]
enabled = false

[package.metadata.capi.library]
install_subdir = "gstreamer-1.0"
versioning = false
import_library = false

[target.'cfg(target_os = "linux")'.dependencies]
procfs = { version = "0.17", default-features = false }

[package.metadata.capi.pkg_config]
requires_private = "gstreamer-1.0, gstreamer-base-1.0, gobject-2.0, glib-2.0, gmodule-2.0"
37 changes: 37 additions & 0 deletions video/stats/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Video Encoder Stats

- `video-encoder-stats`:
The element that collects statistics from a video encoder, and attach them onto the `GstBuffers` as metadata. It helps analyze encoding performance and quality metrics.

- `video-compare-mixer`:
The element in charge of comparing and mixing multiple video streams. Useful for side-by-side quality comparisons or blending outputs from different encoders.

User can change the video showed using the next keys:

1: Only first video
2: Only second video
3: First and second videos split mode (default)
4: First and second videos side by side mode (default)
5: Move side by side border left
6: Move side by side border right

Also click in the botton of the video can be done to change the side by side border

User can change the video player zoom using the next keys:

+: Zoom in
-: Zoom out
Up/Down/Right/Left: Move the frame
r: reset the zoom position
R: reset the zoom

Also mouse navigation events can be used for a better UX.

- `videoencoderstatsmeta`:
Defines metadata structures and logic for handling video encoder statistics along the pipeline. It has been defined as `GstVideoEncoderStatsMetaAPI`.


**`video-encoder-stats`** example:
```
cargo r --example video-encoder-stats
```
3 changes: 3 additions & 0 deletions video/stats/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn main() {
gst_plugin_version_helper::info()
}
87 changes: 87 additions & 0 deletions video/stats/examples/video-stats.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (C) 2025, Fluendo S.A.
// Author: Diego Nieto <dnieto@fluendo.com>
//
// This Source Code Form is subject to the terms of the Mozilla Public License, v2.0.
// If a copy of the MPL was not distributed with this file, You can obtain one at
// <https://mozilla.org/MPL/2.0/>.
//
// SPDX-License-Identifier: MPL-2.0

use anyhow::Error;
use gst::prelude::*;
use std::env;

fn print_usage() {
println!("Usage: video-stats [PIPELINE_TYPE]");
println!();
println!("Pipeline types:");
println!(" default - Default pipeline with VMAF stats and decoder request pads");
println!(" zero-latency - Zero latency pipeline with ultrafast encoding, no VMAF");
println!(" split-screen - Split screen comparison using video-compare-mixer");
println!(" no-vmaf - Pipeline without VMAF statistics");
println!();
println!("If no argument is provided, 'default' pipeline will be used.");
}

fn get_pipeline_string(pipeline_type: &str) -> Result<&'static str, Error> {
match pipeline_type {
"default" => Ok("souphttpsrc location=\"https://ftp.nluug.nl/pub/graphics/blender/demo/movies/ToS/tears_of_steel_1080p.mov\" ! qtdemux name=demux demux.video_0 ! queue ! decodebin3 ! videoconvertscale ! capsfilter caps=\"video/x-raw,aspect-ratio=1/1\" ! tee name=tee ! video-encoder-stats encoder=\"x264enc bitrate=1024\" decoder=\"h264parse ! avdec_h264\" name=vs0 ! fakesink tee. ! video-encoder-stats encoder=\"x264enc bitrate=512\" name=vs1 ! fakesink video-compare-mixer split-screen=false backend=CPU name=mixer vs0.decoder_src ! mixer.sink_0 vs1.decoder_src ! mixer.sink_1 mixer. ! autovideosink"),

"zero-latency" => Ok("souphttpsrc location=\"https://ftp.nluug.nl/pub/graphics/blender/demo/movies/ToS/tears_of_steel_1080p.mov\" ! qtdemux name=demux demux.video_0 ! queue ! decodebin3 ! videoconvertscale ! capsfilter caps=\"video/x-raw,aspect-ratio=1/1\" ! tee name=tee ! video-encoder-stats encoder=\"x264enc bitrate=1024 tune=zerolatency speed-preset=ultrafast threads=4\" name=vs0 vmaf-stats=false tee. ! video-encoder-stats encoder=\"x264enc bitrate=512 tune=zerolatency speed-preset=ultrafast\" name=vs1 video-compare-mixer split-screen=true backend=CPU name=mixer vs0.src ! h264parse ! avdec_h264 ! mixer.sink_0 vs1.src ! h264parse ! avdec_h264 ! mixer.sink_1 mixer. ! autovideosink"),

"split-screen" => Ok("souphttpsrc location=\"https://ftp.nluug.nl/pub/graphics/blender/demo/movies/ToS/tears_of_steel_1080p.mov\" ! qtdemux name=demux demux.video_0 ! queue ! decodebin3 ! videoconvertscale ! capsfilter caps=\"video/x-raw,aspect-ratio=1/1\" ! tee name=tee ! video-encoder-stats encoder=\"x264enc\" decoder=\"h264parse ! avdec_h264\" name=vs0 tee. ! video-encoder-stats encoder=\"x264enc bitrate=512\" name=vs1 video-compare-mixer split-screen=true backend=CPU name=mixer vs0.src ! decodebin3 ! mixer.sink_0 vs1.src ! decodebin3 ! mixer.sink_1 mixer. ! autovideosink"),

"no-vmaf" => Ok("souphttpsrc location=\"https://ftp.nluug.nl/pub/graphics/blender/demo/movies/ToS/tears_of_steel_1080p.mov\" ! qtdemux name=demux demux.video_0 ! queue ! decodebin3 ! videoconvertscale ! capsfilter caps=\"video/x-raw,aspect-ratio=1/1\" ! tee name=tee ! video-encoder-stats encoder=\"x264enc bitrate=1024\" name=vs0 vmaf-stats=false tee. ! video-encoder-stats encoder=\"x264enc bitrate=512\" name=vs1 video-compare-mixer split-screen=true backend=CPU name=mixer vs0.src ! h264parse ! avdec_h264 ! mixer.sink_0 vs1.src ! h264parse ! avdec_h264 ! mixer.sink_1 mixer. ! autovideosink"),

_ => {
println!("Unknown pipeline type: {}", pipeline_type);
print_usage();
Err(anyhow::anyhow!("Invalid pipeline type"))
}
}
}

fn main() -> Result<(), Error> {
gst::init()?;

gstvideostats::plugin_register_static().expect("Failed to register videostats plugin");

// Parse command line arguments
let args: Vec<String> = env::args().collect();
let pipeline_type = if args.len() > 1 {
if args[1] == "--help" || args[1] == "-h" {
print_usage();
return Ok(());
}
&args[1]
} else {
"default"
};

println!("Using pipeline type: {}", pipeline_type);

let pipeline_string = get_pipeline_string(pipeline_type)?;
let pipeline = gst::parse::launch(pipeline_string)?;

// Alternative test pipelines (commented out for reference)
// let pipeline = gst::parse::launch("videotestsrc is-live=true ! videoconvertscale ! capsfilter caps=\"video/x-raw,width=640,height=480,aspect-ratio=1/1,framerate=30/1\" ! tee name=tee ! video-encoder-stats encoder=\"x264enc bitrate=1024\" decoder=\"h264parse ! avdec_h264\" name=vs0 ! fakesink tee. ! video-encoder-stats encoder=\"x264enc bitrate=256\" name=vs1 ! fakesink video-compare-mixer split-screen=true backend=CPU name=mixer vs0.decoder_src ! mixer.sink_0 vs1.decoder_src ! mixer.sink_1 mixer. ! autovideosink")?;
// let pipeline = gst::parse::launch("gltestsrc is-live=true pattern=13 ! gldownload ! videoconvert ! capsfilter caps=\"video/x-raw,width=640,height=480,framerate=30/1\" ! tee name=tee tee.src_0 ! video-encoder-stats encoder=\"x264enc bitrate=1024\" decoder=\"h264parse ! avdec_h264\" name=vs0 ! fakesink tee.src_1 ! video-encoder-stats encoder=\"x264enc bitrate=512\" name=vs1 ! fakesink video-compare-mixer split-screen=true backend=CPU name=mixer vs0.decoder_src ! mixer.sink_0 vs1.decoder_src ! mixer.sink_1 mixer. ! autovideosink")?;

pipeline.set_state(gst::State::Playing)?;

let bus = pipeline.bus().unwrap();
while let Some(msg) = bus.timed_pop(gst::ClockTime::NONE) {
use gst::MessageView;
match msg.view() {
MessageView::Eos(..) => {
break;
}
MessageView::Error(..) => unreachable!(),
_ => (),
}
}

pipeline.set_state(gst::State::Null)?;

Ok(())
}
Loading