gst-plugin-tutorial: Update markdown for using lazy_static! to declare the debug category

This commit is contained in:
Sebastian Dröge 2019-12-17 09:35:21 +02:00
parent 5ca81246f1
commit 916c382171
2 changed files with 72 additions and 66 deletions

View file

@ -45,6 +45,7 @@ glib = { git = "https://github.com/gtk-rs/glib" }
gstreamer = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" } gstreamer = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gstreamer-base = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" } gstreamer-base = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gstreamer-video = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" } gstreamer-video = { git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
lazy_static = "1.0"
[lib] [lib]
name = "gstrstutorial" name = "gstrstutorial"
@ -55,7 +56,7 @@ path = "src/lib.rs"
gst-plugin-version-helper = { git = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs" } gst-plugin-version-helper = { git = "https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs" }
``` ```
We depend on the `gstreamer`, `gstreamer-base` and `gstreamer-video` crates for various GStreamer APIs that will be used later, and the `glib` crate to be able to use some GLib API that well need. GStreamer is building upon GLib, and this leaks through in various places. We also have one build dependency on the `gst-plugin-version-helper` crate, which helps to get some information about the plugin for the `gst_plugin_define!` macro automatically. We depend on the `gstreamer`, `gstreamer-base` and `gstreamer-video` crates for various GStreamer APIs that will be used later, and the `glib` crate to be able to use some GLib API that well need. GStreamer is building upon GLib, and this leaks through in various places. We also have one build dependency on the `gst-plugin-version-helper` crate, which helps to get some information about the plugin for the `gst_plugin_define!` macro automatically, and the `lazy_static` crate, which allows to declare lazily initialized global variables.
With the basic project structure being set-up, we should be able to compile the project with `cargo build` now, which will download and build all dependencies and then creates a file called `target/debug/libgstrstutorial.so` (or .dll on Windows, .dylib on macOS). This is going to be our GStreamer plugin. With the basic project structure being set-up, we should be able to compile the project with `cargo build` now, which will download and build all dependencies and then creates a file called `target/debug/libgstrstutorial.so` (or .dll on Windows, .dylib on macOS). This is going to be our GStreamer plugin.
@ -78,6 +79,8 @@ extern crate glib;
extern crate gstreamer as gst; extern crate gstreamer as gst;
extern crate gstreamer_base as gst_base; extern crate gstreamer_base as gst_base;
extern crate gstreamer_video as gst_video; extern crate gstreamer_video as gst_video;
#[macro_use]
extern crate lazy_static;
``` ```
Next we make use of the `gst_plugin_define!` `macro` from the `gstreamer` crate to set-up the static metadata of the plugin (and make the shared library recognizeable by GStreamer to be a valid plugin), and to define the name of our entry point function (`plugin_init`) where we will register all the elements that this plugin provides. Next we make use of the `gst_plugin_define!` `macro` from the `gstreamer` crate to set-up the static metadata of the plugin (and make the shared library recognizeable by GStreamer to be a valid plugin), and to define the name of our entry point function (`plugin_init`) where we will register all the elements that this plugin provides.
@ -215,11 +218,10 @@ In addition, we also define a `register` function (the one that is already calle
## Type Class & Instance Initialization ## Type Class & Instance Initialization
As a next step we implement the `new` funtion and `class_init` functions. In the first version, this struct is almost empty but we will later use it to store all state of our element. As a next step we implement the `new` funtion and `class_init` functions. In the first version, this struct is empty for now but we will later use it to store all state of our element.
```rust ```rust
struct Rgb2Gray { struct Rgb2Gray {
cat: gst::DebugCategory,
} }
impl Rgb2Gray{} impl Rgb2Gray{}
@ -229,11 +231,6 @@ impl ObjectSubclass for Rgb2Gray {
fn new() -> Self { fn new() -> Self {
Self { Self {
cat: gst::DebugCategory::new(
"rsrgb2gray",
gst::DebugColorFlags::empty(),
Some("Rust RGB-GRAY converter"),
),
} }
} }
@ -255,7 +252,7 @@ impl ObjectSubclass for Rgb2Gray {
``` ```
In the `new` function we return our struct, containing a newly created GStreamer debug category of name “rsrgb2gray”. Were going to use this debug category later for making use of GStreamers debug logging system for logging the state and changes of our element. In the `new` function we return our empty struct.
In the `class_init` function we, again, set up some metadata for our new element. In this case these are a description, a classification of our element, a longer description and the author. The metadata can later be retrieved and made use of via the [`Registry`](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer/struct.Registry.html) and [`PluginFeature`](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer/struct.PluginFeature.html)/[`ElementFactory`](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer/struct.ElementFactory.html) API. We also configure the `BaseTransform` class and define that we will never operate in-place (producing our output in the input buffer), and that we dont want to work in passthrough mode if the input/output formats are the same. In the `class_init` function we, again, set up some metadata for our new element. In this case these are a description, a classification of our element, a longer description and the author. The metadata can later be retrieved and made use of via the [`Registry`](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer/struct.Registry.html) and [`PluginFeature`](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer/struct.PluginFeature.html)/[`ElementFactory`](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer/struct.ElementFactory.html) API. We also configure the `BaseTransform` class and define that we will never operate in-place (producing our output in the input buffer), and that we dont want to work in passthrough mode if the input/output formats are the same.
@ -307,6 +304,27 @@ pub fn register(plugin: &gst::Plugin) -> Result<(), glib::BoolError> {
} }
``` ```
## Debug Category
To be able to later have a debug category available for our new element that
can be used for logging, we make use of the `lazy_static!` macro from the
crate with the same name. This crate allows to declare lazily initialized
global variables, i.e. on the very first use the provided code will be
executed and the result will be stored for all later uses.
```rust
lazy_static! {
static ref CAT: gst::DebugCategory = gst::DebugCategory::new(
"rsrgb2gray",
gst::DebugColorFlags::empty(),
Some("Rust RGB-GRAY converter"),
);
}
```
We give the debug category the same name as our element, which generally is
the convention with GStreamer elements.
## Caps & Pad Templates ## Caps & Pad Templates
Data flow of GStreamer elements is happening via pads, which are the input(s) and output(s) (or sinks and sources in GStreamer terminology) of an element. Via the pads, buffers containing actual media data, events or queries are transferred. An element can have any number of sink and source pads, but our new element will only have one of each. Data flow of GStreamer elements is happening via pads, which are the input(s) and output(s) (or sinks and sources in GStreamer terminology) of an element. Via the pads, buffers containing actual media data, events or queries are transferred. An element can have any number of sink and source pads, but our new element will only have one of each.
@ -387,7 +405,6 @@ struct State {
} }
struct Rgb2Gray { struct Rgb2Gray {
cat: gst::DebugCategory,
state: Mutex<Option<State>> state: Mutex<Option<State>>
} }
@ -398,11 +415,6 @@ impl ObjectSubclass for Rgb2Gray {
fn new() -> Self { fn new() -> Self {
Self { Self {
cat: gst::DebugCategory::new(
"rsrgb2gray",
gst::DebugColorFlags::empty(),
"Rust RGB-GRAY converter",
),
state: Mutex::new(None), state: Mutex::new(None),
} }
} }
@ -431,7 +443,7 @@ impl BaseTransformImpl for Rgb2Gray {
}; };
gst_debug!( gst_debug!(
self.cat, CAT,
obj: element, obj: element,
"Configured for caps {} to {}", "Configured for caps {} to {}",
incaps, incaps,
@ -447,7 +459,7 @@ impl BaseTransformImpl for Rgb2Gray {
// Drop state // Drop state
let _ = self.state.lock().unwrap().take(); let _ = self.state.lock().unwrap().take();
gst_info!(self.cat, obj: element, "Stopped"); gst_info!(CAT, obj: element, "Stopped");
Ok(()) Ok(())
} }
@ -511,7 +523,7 @@ impl BaseTransformImpl for Rgb2Gray {
}; };
gst_debug!( gst_debug!(
self.cat, CAT,
obj: element, obj: element,
"Transformed caps from {} to {} in direction {:?}", "Transformed caps from {} to {} in direction {:?}",
caps, caps,
@ -555,7 +567,7 @@ impl Rgb2Gray {
let gray = ((r * R_Y) + (g * G_Y) + (b * B_Y)) / 65536; let gray = ((r * R_Y) + (g * G_Y) + (b * B_Y)) / 65536;
(gray as u8) gray as u8
} }
} }
``` ```
@ -574,7 +586,6 @@ impl BaseTransformImpl for Rgb2Gray {
inbuf: &gst::Buffer, inbuf: &gst::Buffer,
outbuf: &mut gst::BufferRef, outbuf: &mut gst::BufferRef,
) -> Result<gst::FlowSuccess, gst::FlowError> { ) -> Result<gst::FlowSuccess, gst::FlowError> {
let mut state_guard = self.state.lock().unwrap(); let mut state_guard = self.state.lock().unwrap();
let state = state_guard.as_mut().ok_or_else(|| { let state = state_guard.as_mut().ok_or_else(|| {
gst_element_error!(element, gst::CoreError::Negotiation, ["Have no state yet"]); gst_element_error!(element, gst::CoreError::Negotiation, ["Have no state yet"]);
@ -745,7 +756,6 @@ static PROPERTIES: [subclass::Property; 2] = [
]; ];
struct Rgb2Gray { struct Rgb2Gray {
cat: gst::DebugCategory,
settings: Mutex<Settings>, settings: Mutex<Settings>,
state: Mutex<Option<State>>, state: Mutex<Option<State>>,
} }
@ -757,11 +767,6 @@ impl ObjectSubclass for Rgb2Gray {
fn new() -> Self { fn new() -> Self {
Self { Self {
cat: gst::DebugCategory::new(
"rsrgb2gray",
gst::DebugColorFlags::empty(),
"Rust RGB-GRAY converter",
),
settings: Mutex::new(Default::default()), settings: Mutex::new(Default::default()),
state: Mutex::new(None), state: Mutex::new(None),
} }
@ -794,7 +799,7 @@ impl ObjectImpl for Rgb2Gray {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let invert = value.get_some().expect("type checked upstream"); let invert = value.get_some().expect("type checked upstream");
gst_info!( gst_info!(
self.cat, CAT,
obj: element, obj: element,
"Changing invert from {} to {}", "Changing invert from {} to {}",
settings.invert, settings.invert,
@ -806,7 +811,7 @@ impl ObjectImpl for Rgb2Gray {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let shift = value.get_some().expect("type checked upstream"); let shift = value.get_some().expect("type checked upstream");
gst_info!( gst_info!(
self.cat, CAT,
obj: element, obj: element,
"Changing shift from {} to {}", "Changing shift from {} to {}",
settings.shift, settings.shift,

View file

@ -134,11 +134,18 @@ impl Default for State {
// Struct containing all the element data // Struct containing all the element data
struct SineSrc { struct SineSrc {
cat: gst::DebugCategory,
settings: Mutex<Settings>, settings: Mutex<Settings>,
state: Mutex<State>, state: Mutex<State>,
} }
lazy_static! {
static ref CAT: gst::DebugCategory = gst::DebugCategory::new(
"rssinesrc",
gst::DebugColorFlags::empty(),
Some("Rust Sine Wave Source"),
);
}
impl SineSrc { impl SineSrc {
// Called when a new instance is to be created // Called when a new instance is to be created
fn new(element: &BaseSrc) -> Box<BaseSrcImpl<BaseSrc>> { fn new(element: &BaseSrc) -> Box<BaseSrcImpl<BaseSrc>> {
@ -148,11 +155,6 @@ impl SineSrc {
element.set_format(gst::Format::Time); element.set_format(gst::Format::Time);
Box::new(Self { Box::new(Self {
cat: gst::DebugCategory::new(
"rssinesrc",
gst::DebugColorFlags::empty(),
Some("Rust Sine Wave Source"),
),
settings: Mutex::new(Default::default()), settings: Mutex::new(Default::default()),
state: Mutex::new(Default::default()), state: Mutex::new(Default::default()),
}) })
@ -220,7 +222,7 @@ impl ObjectImpl<BaseSrc> for SineSrc {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let samples_per_buffer = value.get_some().expect("type checked upstream"); let samples_per_buffer = value.get_some().expect("type checked upstream");
gst_info!( gst_info!(
self.cat, CAT,
obj: &element, obj: &element,
"Changing samples-per-buffer from {} to {}", "Changing samples-per-buffer from {} to {}",
settings.samples_per_buffer, settings.samples_per_buffer,
@ -236,7 +238,7 @@ impl ObjectImpl<BaseSrc> for SineSrc {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let freq = value.get_some().expect("type checked upstream"); let freq = value.get_some().expect("type checked upstream");
gst_info!( gst_info!(
self.cat, CAT,
obj: &element, obj: &element,
"Changing freq from {} to {}", "Changing freq from {} to {}",
settings.freq, settings.freq,
@ -248,7 +250,7 @@ impl ObjectImpl<BaseSrc> for SineSrc {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let volume = value.get_some().expect("type checked upstream"); let volume = value.get_some().expect("type checked upstream");
gst_info!( gst_info!(
self.cat, CAT,
obj: &element, obj: &element,
"Changing volume from {} to {}", "Changing volume from {} to {}",
settings.volume, settings.volume,
@ -260,7 +262,7 @@ impl ObjectImpl<BaseSrc> for SineSrc {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let mute = value.get_some().expect("type checked upstream"); let mute = value.get_some().expect("type checked upstream");
gst_info!( gst_info!(
self.cat, CAT,
obj: &element, obj: &element,
"Changing mute from {} to {}", "Changing mute from {} to {}",
settings.mute, settings.mute,
@ -272,7 +274,7 @@ impl ObjectImpl<BaseSrc> for SineSrc {
let mut settings = self.settings.lock().unwrap(); let mut settings = self.settings.lock().unwrap();
let is_live = value.get_some().expect("type checked upstream"); let is_live = value.get_some().expect("type checked upstream");
gst_info!( gst_info!(
self.cat, CAT,
obj: &element, obj: &element,
"Changing is-live from {} to {}", "Changing is-live from {} to {}",
settings.is_live, settings.is_live,
@ -324,7 +326,7 @@ impl BaseSrcImpl<BaseSrc> for SineSrc {
// Reset state // Reset state
*self.state.lock().unwrap() = Default::default(); *self.state.lock().unwrap() = Default::default();
gst_info!(self.cat, obj: element, "Started"); gst_info!(CAT, obj: element, "Started");
true true
} }
@ -334,7 +336,7 @@ impl BaseSrcImpl<BaseSrc> for SineSrc {
// Reset state // Reset state
*self.state.lock().unwrap() = Default::default(); *self.state.lock().unwrap() = Default::default();
gst_info!(self.cat, obj: element, "Stopped"); gst_info!(CAT, obj: element, "Stopped");
true true
} }
@ -385,7 +387,7 @@ First of all, we need to get notified whenever the caps that our source is confi
Some(info) => info, Some(info) => info,
}; };
gst_debug!(self.cat, obj: element, "Configuring for caps {}", caps); gst_debug!(CAT, obj: element, "Configuring for caps {}", caps);
element.set_blocksize(info.bpf() * (*self.settings.lock().unwrap()).samples_per_buffer); element.set_blocksize(info.bpf() * (*self.settings.lock().unwrap()).samples_per_buffer);
@ -581,7 +583,7 @@ Now that this is done, we need to implement the `BaseSrc::create` virtual meth
// point but at most samples_per_buffer samples per buffer // point but at most samples_per_buffer samples per buffer
let n_samples = if let Some(sample_stop) = state.sample_stop { let n_samples = if let Some(sample_stop) = state.sample_stop {
if sample_stop <= state.sample_offset { if sample_stop <= state.sample_offset {
gst_log!(self.cat, obj: element, "At EOS"); gst_log!(CAT, obj: element, "At EOS");
return Err(gst::FlowReturn::Eos); return Err(gst::FlowReturn::Eos);
} }
@ -640,7 +642,7 @@ Now that this is done, we need to implement the `BaseSrc::create` virtual meth
state.sample_offset += n_samples; state.sample_offset += n_samples;
drop(state); drop(state);
gst_debug!(self.cat, obj: element, "Produced buffer {:?}", buffer); gst_debug!(CAT, obj: element, "Produced buffer {:?}", buffer);
Ok(buffer) Ok(buffer)
} }
@ -707,7 +709,7 @@ For working in live mode, we have to add a few different parts in various places
let id = clock.new_single_shot_id(wait_until).unwrap(); let id = clock.new_single_shot_id(wait_until).unwrap();
gst_log!( gst_log!(
self.cat, CAT,
obj: element, obj: element,
"Waiting until {}, now {}", "Waiting until {}, now {}",
wait_until, wait_until,
@ -715,7 +717,7 @@ For working in live mode, we have to add a few different parts in various places
); );
let (res, jitter) = id.wait(); let (res, jitter) = id.wait();
gst_log!( gst_log!(
self.cat, CAT,
obj: element, obj: element,
"Waited res {:?} jitter {}", "Waited res {:?} jitter {}",
res, res,
@ -723,7 +725,7 @@ For working in live mode, we have to add a few different parts in various places
); );
} }
gst_debug!(self.cat, obj: element, "Produced buffer {:?}", buffer); gst_debug!(CAT, obj: element, "Produced buffer {:?}", buffer);
Ok(buffer) Ok(buffer)
} }
@ -782,7 +784,7 @@ This querying is done with the `LATENCY` query, which we will now also have to
let latency = gst::SECOND let latency = gst::SECOND
.mul_div_floor(settings.samples_per_buffer as u64, info.rate() as u64) .mul_div_floor(settings.samples_per_buffer as u64, info.rate() as u64)
.unwrap(); .unwrap();
gst_debug!(self.cat, obj: element, "Returning latency {}", latency); gst_debug!(CAT, obj: element, "Returning latency {}", latency);
q.set(settings.is_live, latency, gst::CLOCK_TIME_NONE); q.set(settings.is_live, latency, gst::CLOCK_TIME_NONE);
true true
} else { } else {
@ -816,7 +818,6 @@ struct ClockWait {
} }
struct SineSrc { struct SineSrc {
cat: gst::DebugCategory,
settings: Mutex<Settings>, settings: Mutex<Settings>,
state: Mutex<State>, state: Mutex<State>,
clock_wait: Mutex<ClockWait>, clock_wait: Mutex<ClockWait>,
@ -827,7 +828,7 @@ struct SineSrc {
fn unlock(&self, element: &BaseSrc) -> bool { fn unlock(&self, element: &BaseSrc) -> bool {
// This should unblock the create() function ASAP, so we // This should unblock the create() function ASAP, so we
// just unschedule the clock it here, if any. // just unschedule the clock it here, if any.
gst_debug!(self.cat, obj: element, "Unlocking"); gst_debug!(CAT, obj: element, "Unlocking");
let mut clock_wait = self.clock_wait.lock().unwrap(); let mut clock_wait = self.clock_wait.lock().unwrap();
if let Some(clock_id) = clock_wait.clock_id.take() { if let Some(clock_id) = clock_wait.clock_id.take() {
clock_id.unschedule(); clock_id.unschedule();
@ -846,7 +847,7 @@ Once everything is unlocked, we need to reset things again so that data flow can
fn unlock_stop(&self, element: &BaseSrc) -> bool { fn unlock_stop(&self, element: &BaseSrc) -> bool {
// This signals that unlocking is done, so we can reset // This signals that unlocking is done, so we can reset
// all values again. // all values again.
gst_debug!(self.cat, obj: element, "Unlock stop"); gst_debug!(CAT, obj: element, "Unlock stop");
let mut clock_wait = self.clock_wait.lock().unwrap(); let mut clock_wait = self.clock_wait.lock().unwrap();
clock_wait.flushing = false; clock_wait.flushing = false;
@ -864,7 +865,7 @@ Now as a last step, we need to actually make use of the new struct we added arou
// so that we immediately stop waiting on e.g. shutdown. // so that we immediately stop waiting on e.g. shutdown.
let mut clock_wait = self.clock_wait.lock().unwrap(); let mut clock_wait = self.clock_wait.lock().unwrap();
if clock_wait.flushing { if clock_wait.flushing {
gst_debug!(self.cat, obj: element, "Flushing"); gst_debug!(CAT, obj: element, "Flushing");
return Err(gst::FlowReturn::Flushing); return Err(gst::FlowReturn::Flushing);
} }
@ -873,7 +874,7 @@ Now as a last step, we need to actually make use of the new struct we added arou
drop(clock_wait); drop(clock_wait);
gst_log!( gst_log!(
self.cat, CAT,
obj: element, obj: element,
"Waiting until {}, now {}", "Waiting until {}, now {}",
wait_until, wait_until,
@ -881,7 +882,7 @@ Now as a last step, we need to actually make use of the new struct we added arou
); );
let (res, jitter) = id.wait(); let (res, jitter) = id.wait();
gst_log!( gst_log!(
self.cat, CAT,
obj: element, obj: element,
"Waited res {:?} jitter {}", "Waited res {:?} jitter {}",
res, res,
@ -892,7 +893,7 @@ Now as a last step, we need to actually make use of the new struct we added arou
// If the clock ID was unscheduled, unlock() was called // If the clock ID was unscheduled, unlock() was called
// and we should return Flushing immediately. // and we should return Flushing immediately.
if res == gst::ClockReturn::Unscheduled { if res == gst::ClockReturn::Unscheduled {
gst_debug!(self.cat, obj: element, "Flushing"); gst_debug!(CAT, obj: element, "Flushing");
return Err(gst::FlowReturn::Flushing); return Err(gst::FlowReturn::Flushing);
} }
``` ```
@ -921,7 +922,7 @@ Seeking is implemented in the `BaseSrc::do_seek` virtual method, and signallin
// and for calculating the timestamps, etc. // and for calculating the timestamps, etc.
if segment.get_rate() < 0.0 { if segment.get_rate() < 0.0 {
gst_error!(self.cat, obj: element, "Reverse playback not supported"); gst_error!(CAT, obj: element, "Reverse playback not supported");
return false; return false;
} }
@ -953,7 +954,7 @@ Seeking is implemented in the `BaseSrc::do_seek` virtual method, and signallin
(sample_offset as f64).rem(2.0 * PI * (settings.freq as f64) / (rate as f64)); (sample_offset as f64).rem(2.0 * PI * (settings.freq as f64) / (rate as f64));
gst_debug!( gst_debug!(
self.cat, CAT,
obj: element, obj: element,
"Seeked to {}-{:?} (accum: {}) for segment {:?}", "Seeked to {}-{:?} (accum: {}) for segment {:?}",
sample_offset, sample_offset,
@ -975,7 +976,7 @@ Seeking is implemented in the `BaseSrc::do_seek` virtual method, and signallin
if state.info.is_none() { if state.info.is_none() {
gst_error!( gst_error!(
self.cat, CAT,
obj: element, obj: element,
"Can only seek in Default format if sample rate is known" "Can only seek in Default format if sample rate is known"
); );
@ -989,7 +990,7 @@ Seeking is implemented in the `BaseSrc::do_seek` virtual method, and signallin
(sample_offset as f64).rem(2.0 * PI * (settings.freq as f64) / (rate as f64)); (sample_offset as f64).rem(2.0 * PI * (settings.freq as f64) / (rate as f64));
gst_debug!( gst_debug!(
self.cat, CAT,
obj: element, obj: element,
"Seeked to {}-{:?} (accum: {}) for segment {:?}", "Seeked to {}-{:?} (accum: {}) for segment {:?}",
sample_offset, sample_offset,
@ -1008,7 +1009,7 @@ Seeking is implemented in the `BaseSrc::do_seek` virtual method, and signallin
true true
} else { } else {
gst_error!( gst_error!(
self.cat, CAT,
obj: element, obj: element,
"Can't seek in format {:?}", "Can't seek in format {:?}",
segment.get_format() segment.get_format()