mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-11-22 11:30:59 +00:00
tracers: pipeline_snapshot: Make it controllable inside apps
Exposing properties so that user can configure it when instantiating it Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs/-/merge_requests/1889>
This commit is contained in:
parent
27b02445d0
commit
e531c7f625
2 changed files with 96 additions and 34 deletions
|
@ -45,6 +45,7 @@ use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
use gst::glib;
|
use gst::glib;
|
||||||
use gst::glib::translate::ToGlibPtr;
|
use gst::glib::translate::ToGlibPtr;
|
||||||
|
use gst::glib::Properties;
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
use gst::subclass::prelude::*;
|
use gst::subclass::prelude::*;
|
||||||
use std::sync::LazyLock;
|
use std::sync::LazyLock;
|
||||||
|
@ -120,7 +121,7 @@ impl Settings {
|
||||||
|
|
||||||
if let Ok(dot_dir) = s.get("dot-dir") {
|
if let Ok(dot_dir) = s.get("dot-dir") {
|
||||||
self.set_dot_dir(dot_dir);
|
self.set_dot_dir(dot_dir);
|
||||||
gst::log!(CAT, imp: imp, "dot-dir = {:?}", self.dot_dir);
|
gst::log!(CAT, imp = imp, "dot-dir = {:?}", self.dot_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(dot_prefix) = s.get("dot-prefix") {
|
if let Ok(dot_prefix) = s.get("dot-prefix") {
|
||||||
|
@ -135,12 +136,18 @@ impl Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Properties, Debug, Default)]
|
||||||
|
#[properties(wrapper_type = super::PipelineSnapshot)]
|
||||||
pub struct PipelineSnapshot {
|
pub struct PipelineSnapshot {
|
||||||
|
#[property(name="dot-dir", get, set = Self::set_dot_dir, construct_only, type = String, member = dot_dir, blurb = "Directory where to place dot files")]
|
||||||
|
#[property(name="dot-prefix", get, set, type = String, member = dot_prefix, blurb = "Prefix for dot files")]
|
||||||
|
#[property(name="dot-ts", get, set, type = bool, member = dot_ts, blurb = "Add timestamp to dot files")]
|
||||||
|
settings: RwLock<Settings>,
|
||||||
pipelines: Arc<Mutex<HashMap<ElementPtr, glib::WeakRef<gst::Element>>>>,
|
pipelines: Arc<Mutex<HashMap<ElementPtr, glib::WeakRef<gst::Element>>>>,
|
||||||
handles: Mutex<Option<Handles>>,
|
handles: Mutex<Option<Handles>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
struct Handles {
|
struct Handles {
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
signal: signal_hook::iterator::Handle,
|
signal: signal_hook::iterator::Handle,
|
||||||
|
@ -154,12 +161,13 @@ impl ObjectSubclass for PipelineSnapshot {
|
||||||
type ParentType = gst::Tracer;
|
type ParentType = gst::Tracer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[glib::derived_properties]
|
||||||
impl ObjectImpl for PipelineSnapshot {
|
impl ObjectImpl for PipelineSnapshot {
|
||||||
fn constructed(&self) {
|
fn constructed(&self) {
|
||||||
let _ = START_TIME.as_ref();
|
let _ = START_TIME.as_ref();
|
||||||
self.parent_constructed();
|
self.parent_constructed();
|
||||||
|
|
||||||
let mut settings = Settings::default();
|
let mut settings = self.settings.write().unwrap();
|
||||||
if let Some(params) = self.obj().property::<Option<String>>("params") {
|
if let Some(params) = self.obj().property::<Option<String>>("params") {
|
||||||
settings.update_from_params(self, params);
|
settings.update_from_params(self, params);
|
||||||
}
|
}
|
||||||
|
@ -167,11 +175,26 @@ impl ObjectImpl for PipelineSnapshot {
|
||||||
self.register_hook(TracerHook::ElementNew);
|
self.register_hook(TracerHook::ElementNew);
|
||||||
self.register_hook(TracerHook::ObjectDestroyed);
|
self.register_hook(TracerHook::ObjectDestroyed);
|
||||||
|
|
||||||
if let Err(err) = self.setup_signal(settings) {
|
if let Err(err) = self.setup_signal() {
|
||||||
gst::warning!(CAT, imp = self, "failed to setup UNIX signals: {}", err);
|
gst::warning!(CAT, imp = self, "failed to setup UNIX signals: {}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn signals() -> &'static [glib::subclass::Signal] {
|
||||||
|
static SIGNALS: Lazy<Vec<glib::subclass::Signal>> = Lazy::new(|| {
|
||||||
|
vec![glib::subclass::Signal::builder("snapshot")
|
||||||
|
.action()
|
||||||
|
.class_handler(|_, args| {
|
||||||
|
args[0].get::<super::PipelineSnapshot>().unwrap().snapshot();
|
||||||
|
|
||||||
|
None
|
||||||
|
})
|
||||||
|
.build()]
|
||||||
|
});
|
||||||
|
|
||||||
|
SIGNALS.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
fn dispose(&self) {
|
fn dispose(&self) {
|
||||||
let mut handles = self.handles.lock().unwrap();
|
let mut handles = self.handles.lock().unwrap();
|
||||||
if let Some(handles) = handles.take() {
|
if let Some(handles) = handles.take() {
|
||||||
|
@ -203,49 +226,81 @@ impl TracerImpl for PipelineSnapshot {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PipelineSnapshot {
|
impl PipelineSnapshot {
|
||||||
#[cfg(unix)]
|
fn set_dot_dir(&self, dot_dir: Option<String>) {
|
||||||
fn setup_signal(&self, settings: Settings) -> anyhow::Result<()> {
|
let mut settings = self.settings.write().unwrap();
|
||||||
use signal_hook::consts::signal::*;
|
settings.set_dot_dir(dot_dir);
|
||||||
use signal_hook::iterator::Signals;
|
}
|
||||||
|
|
||||||
let mut signals = Signals::new([SIGUSR1])?;
|
|
||||||
let signal_handle = signals.handle();
|
|
||||||
|
|
||||||
let tracer_weak = self.obj().downgrade();
|
|
||||||
let pipelines = self.pipelines.clone();
|
|
||||||
|
|
||||||
let thread_handle = std::thread::spawn(move || {
|
|
||||||
for signal in &mut signals {
|
|
||||||
match signal {
|
|
||||||
SIGUSR1 => {
|
|
||||||
let Some(tracer) = tracer_weak.upgrade() else {
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
pub(crate) fn snapshot(&self) {
|
||||||
let pipelines = {
|
let pipelines = {
|
||||||
let weaks = pipelines.lock().unwrap();
|
let weaks = self.pipelines.lock().unwrap();
|
||||||
weaks
|
weaks
|
||||||
.values()
|
.values()
|
||||||
.filter_map(|w| w.upgrade())
|
.filter_map(|w| w.upgrade())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let settings = self.settings.read().unwrap();
|
||||||
|
let dot_dir = if let Some(dot_dir) = settings.dot_dir.as_ref() {
|
||||||
|
dot_dir
|
||||||
|
} else {
|
||||||
|
gst::info!(CAT, imp = self, "No dot-dir set, not dumping pipelines");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
for pipeline in pipelines.into_iter() {
|
for pipeline in pipelines.into_iter() {
|
||||||
let pipeline = pipeline.downcast::<gst::Pipeline>().unwrap();
|
let pipeline = pipeline.downcast::<gst::Pipeline>().unwrap();
|
||||||
gst::debug!(CAT, obj = tracer, "dump {}", pipeline.name());
|
gst::debug!(CAT, imp = self, "dump {}", pipeline.name());
|
||||||
|
|
||||||
let dump_name = format!("{}{}", settings.dot_prefix, pipeline.name());
|
let dump_name = if settings.dot_ts {
|
||||||
|
format!(
|
||||||
if settings.dot_ts {
|
"{}-{}{}",
|
||||||
pipeline.debug_to_dot_file_with_ts(
|
gst::get_timestamp() - *START_TIME,
|
||||||
gst::DebugGraphDetails::all(),
|
settings.dot_prefix.as_ref().map_or("", |s| s.as_str()),
|
||||||
&dump_name,
|
pipeline.name()
|
||||||
);
|
)
|
||||||
} else {
|
} else {
|
||||||
pipeline
|
format!(
|
||||||
.debug_to_dot_file(gst::DebugGraphDetails::all(), &dump_name);
|
"{}{}",
|
||||||
|
settings.dot_prefix.as_ref().map_or("", |s| s.as_str()),
|
||||||
|
pipeline.name()
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
let dot_path = format!("{}/{}.dot", dot_dir, dump_name);
|
||||||
|
gst::debug!(CAT, imp = self, "Writing {}", dot_path);
|
||||||
|
match std::fs::File::create(&dot_path) {
|
||||||
|
Ok(mut f) => {
|
||||||
|
let data = pipeline.debug_to_dot_data(gst::DebugGraphDetails::all());
|
||||||
|
if let Err(e) = f.write_all(data.as_bytes()) {
|
||||||
|
gst::warning!(CAT, imp = self, "Failed to write {}: {}", dot_path, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(e) => {
|
||||||
|
gst::warning!(CAT, imp = self, "Failed to create {}: {}", dot_path, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
fn setup_signal(&self) -> anyhow::Result<()> {
|
||||||
|
use signal_hook::consts::signal::*;
|
||||||
|
use signal_hook::iterator::Signals;
|
||||||
|
|
||||||
|
let mut signals = Signals::new([SIGUSR1])?;
|
||||||
|
let signal_handle = signals.handle();
|
||||||
|
|
||||||
|
let this_weak = self.obj().downgrade();
|
||||||
|
let thread_handle = std::thread::spawn(move || {
|
||||||
|
for signal in &mut signals {
|
||||||
|
match signal {
|
||||||
|
SIGUSR1 => {
|
||||||
|
if let Some(this) = this_weak.upgrade() {
|
||||||
|
this.snapshot();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
use gst::glib;
|
use gst::glib;
|
||||||
use gst::prelude::*;
|
use gst::prelude::*;
|
||||||
|
use gst::subclass::prelude::*;
|
||||||
|
|
||||||
mod imp;
|
mod imp;
|
||||||
|
|
||||||
|
@ -15,6 +16,12 @@ glib::wrapper! {
|
||||||
pub struct PipelineSnapshot(ObjectSubclass<imp::PipelineSnapshot>) @extends gst::Tracer, gst::Object;
|
pub struct PipelineSnapshot(ObjectSubclass<imp::PipelineSnapshot>) @extends gst::Tracer, gst::Object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl PipelineSnapshot {
|
||||||
|
fn snapshot(&self) {
|
||||||
|
self.imp().snapshot()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
|
||||||
gst::Tracer::register(
|
gst::Tracer::register(
|
||||||
Some(plugin),
|
Some(plugin),
|
||||||
|
|
Loading…
Reference in a new issue