mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-11-29 15:01:07 +00:00
gst-plugin-tutorial: Update markdown for using lazy_static! to declare the debug category
This commit is contained in:
parent
5ca81246f1
commit
916c382171
2 changed files with 72 additions and 66 deletions
|
@ -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 we’ll 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 we’ll 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.
|
||||||
|
@ -192,7 +195,7 @@ impl ObjectSubclass for Rgb2Gray {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
fn class_init(klass: &mut subclass::simple::ClassStruct<Self>) {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -211,15 +214,14 @@ This defines a struct `Rgb2Gray` which is empty for now and an empty implementat
|
||||||
|
|
||||||
`ObjectSubclass` has an associated constant which contains the name of the type, some associated types, and functions for initializing/returning a new instance of our element (`new`) and for initializing the class metadata (`class_init`, more on that later). We simply let those functions proxy to associated functions on the `Rgb2Gray` struct that we’re going to define at a later time.
|
`ObjectSubclass` has an associated constant which contains the name of the type, some associated types, and functions for initializing/returning a new instance of our element (`new`) and for initializing the class metadata (`class_init`, more on that later). We simply let those functions proxy to associated functions on the `Rgb2Gray` struct that we’re going to define at a later time.
|
||||||
|
|
||||||
In addition, we also define a `register` function (the one that is already called from our `plugin_init` function). When `register` function is called it registers the element factory with GStreamer based on the type ID, to be able to create new instances of it with the name “rsrgb2gray” (e.g. when using [`gst::ElementFactory::make`](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer/struct.ElementFactory.html#method.make)). The `get_type` function will register the type with the GObject type system on the first call and the next time it's called (or on all the following calls) it will return the type ID.
|
In addition, we also define a `register` function (the one that is already called from our `plugin_init` function). When `register` function is called it registers the element factory with GStreamer based on the type ID, to be able to create new instances of it with the name “rsrgb2gray” (e.g. when using [`gst::ElementFactory::make`](https://slomo.pages.freedesktop.org/rustdocs/gstreamer/gstreamer/struct.ElementFactory.html#method.make)). The `get_type` function will register the type with the GObject type system on the first call and the next time it's called (or on all the following calls) it will return the type ID.
|
||||||
|
|
||||||
## 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,11 +252,11 @@ impl ObjectSubclass for Rgb2Gray {
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
In the `new` function we return our struct, containing a newly created GStreamer debug category of name “rsrgb2gray”. We’re going to use this debug category later for making use of GStreamer’s 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 don’t 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 don’t want to work in passthrough mode if the input/output formats are the same.
|
||||||
|
|
||||||
Additionally we need to implement various traits on the Rgb2Gray struct, which will later be used to override virtual methods of the various parent classes of our element. For now we can keep the trait implementations empty, except for `ObjectImpl` trait which should simply call the `glib_object_impl!()` macro for some boilerplate code. There is one trait implementation required per parent class.
|
Additionally we need to implement various traits on the Rgb2Gray struct, which will later be used to override virtual methods of the various parent classes of our element. For now we can keep the trait implementations empty, except for `ObjectImpl` trait which should simply call the `glib_object_impl!()` macro for some boilerplate code. There is one trait implementation required per parent class.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
impl ObjectImpl for Rgb2Gray {
|
impl ObjectImpl for Rgb2Gray {
|
||||||
|
@ -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,
|
||||||
|
@ -554,8 +566,8 @@ impl Rgb2Gray {
|
||||||
let r = u32::from(in_p[2]);
|
let r = u32::from(in_p[2]);
|
||||||
|
|
||||||
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,
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -777,12 +779,12 @@ This querying is done with the `LATENCY` query, which we will now also have to
|
||||||
QueryView::Latency(ref mut q) => {
|
QueryView::Latency(ref mut q) => {
|
||||||
let settings = *self.settings.lock().unwrap();
|
let settings = *self.settings.lock().unwrap();
|
||||||
let state = self.state.lock().unwrap();
|
let state = self.state.lock().unwrap();
|
||||||
|
|
||||||
if let Some(ref info) = state.info {
|
if let Some(ref info) = state.info {
|
||||||
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 {
|
||||||
|
@ -791,7 +793,7 @@ This querying is done with the `LATENCY` query, which we will now also have to
|
||||||
}
|
}
|
||||||
_ => BaseSrcImplExt::parent_query(self, element, query),
|
_ => BaseSrcImplExt::parent_query(self, element, query),
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
|
Loading…
Reference in a new issue