diff --git a/Cargo.toml b/Cargo.toml index f4606c6..6984306 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ keywords = ["gstreamer", "multimedia"] description = "Crate providing a convenient API to parse GStreamer logs" [dependencies] -itertools = "0.13" +itertools = "0.14" gstreamer = "0.23" regex = "1.3" lazy_static = "1.4" @@ -40,5 +40,5 @@ name = "thread-split" [dev-dependencies] structopt = "0.3" -colored = "2.0" +colored = "3.0" gnuplot = "0.0.44" diff --git a/examples/glcontext.rs b/examples/glcontext.rs new file mode 100644 index 0000000..7065bc4 --- /dev/null +++ b/examples/glcontext.rs @@ -0,0 +1,93 @@ +// Copyright (C) 2025 Guillaume Desmottes +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::{collections::HashMap, fs::File}; + +use gst_log_parser::parse; +use gstreamer as gst; +use structopt::StructOpt; + +#[derive(StructOpt)] +#[structopt( + name = "glcontext", + about = "Check for how long it takes for functions to be scheduled in the GL thread" +)] +struct Opt { + #[structopt(help = "Input file")] + input: String, +} + +fn print_stats(times: Vec) { + let n = times.len(); + println!("{n} function calls. Schedule times:"); + if n == 0 { + return; + } + let (min_idx, min) = times + .iter() + .enumerate() + .min_by(|(_a_idx, a), (_b_idx, b)| a.cmp(b)) + .unwrap(); + let (max_idx, max) = times + .iter() + .enumerate() + .max_by(|(_a_idx, a), (_b_idx, b)| a.cmp(b)) + .unwrap(); + println!(" min: {min} (call {min_idx})"); + println!(" max: {max} (call {max_idx})"); + let sum: gst::ClockTime = times.into_iter().sum(); + let avg = sum.nseconds() / n as u64; + let avg = gst::ClockTime::from_nseconds(avg); + println!(" avg: {avg}"); +} + +fn main() -> anyhow::Result<()> { + let opt = Opt::from_args(); + let f = File::open(opt.input)?; + + let add_re = regex::Regex::new(r"schedule function:(?.*) data:(?.*)")?; + let run_re = regex::Regex::new(r"running function:(?.*) data:(?.*)")?; + let mut pendings = HashMap::new(); + let mut times = vec![]; + + let parsed = parse(f); + for entry in parsed + .filter(|entry| entry.category == "glcontext" && entry.level == gst::DebugLevel::Trace) + { + match entry.function.as_str() { + "gst_gl_context_thread_add" => { + let Some(capture) = add_re.captures(&entry.message) else { + continue; + }; + pendings.insert( + ( + (capture["function"]).to_string(), + (capture["data"]).to_string(), + ), + entry.ts, + ); + } + "_gst_gl_context_thread_run_generic" => { + let Some(capture) = run_re.captures(&entry.message) else { + continue; + }; + if let Some(pending) = pendings.remove(&( + (capture["function"]).to_string(), + (capture["data"]).to_string(), + )) { + times.push(entry.ts - pending); + } + } + _ => {} + } + } + + print_stats(times); + + Ok(()) +}