mirror of
https://gitlab.freedesktop.org/gstreamer/gst-plugins-rs.git
synced 2024-11-22 19:41:00 +00:00
tutorial: Port Rgb2Gray to VideoFilter base class
This commit is contained in:
parent
cf637d0288
commit
76a33e8f47
1 changed files with 8 additions and 105 deletions
|
@ -11,6 +11,7 @@ use gst::prelude::*;
|
||||||
use gst::subclass::prelude::*;
|
use gst::subclass::prelude::*;
|
||||||
use gst::{gst_debug, gst_info};
|
use gst::{gst_debug, gst_info};
|
||||||
use gst_base::subclass::prelude::*;
|
use gst_base::subclass::prelude::*;
|
||||||
|
use gst_video::subclass::prelude::*;
|
||||||
|
|
||||||
use std::i32;
|
use std::i32;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
@ -47,17 +48,10 @@ impl Default for Settings {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stream-specific state, i.e. video format configuration
|
|
||||||
struct State {
|
|
||||||
in_info: gst_video::VideoInfo,
|
|
||||||
out_info: gst_video::VideoInfo,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Struct containing all the element data
|
// Struct containing all the element data
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Rgb2Gray {
|
pub struct Rgb2Gray {
|
||||||
settings: Mutex<Settings>,
|
settings: Mutex<Settings>,
|
||||||
state: Mutex<Option<State>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Rgb2Gray {
|
impl Rgb2Gray {
|
||||||
|
@ -94,7 +88,7 @@ impl Rgb2Gray {
|
||||||
impl ObjectSubclass for Rgb2Gray {
|
impl ObjectSubclass for Rgb2Gray {
|
||||||
const NAME: &'static str = "RsRgb2Gray";
|
const NAME: &'static str = "RsRgb2Gray";
|
||||||
type Type = super::Rgb2Gray;
|
type Type = super::Rgb2Gray;
|
||||||
type ParentType = gst_base::BaseTransform;
|
type ParentType = gst_video::VideoFilter;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Implementation of glib::Object virtual methods
|
// Implementation of glib::Object virtual methods
|
||||||
|
@ -347,112 +341,21 @@ impl BaseTransformImpl for Rgb2Gray {
|
||||||
Some(other_caps)
|
Some(other_caps)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the size of one processing unit (i.e. a frame in our case) corresponding
|
impl VideoFilterImpl for Rgb2Gray {
|
||||||
// to the given caps. This is used for allocating a big enough output buffer and
|
|
||||||
// sanity checking the input buffer size, among other things.
|
|
||||||
fn unit_size(&self, _element: &Self::Type, caps: &gst::Caps) -> Option<usize> {
|
|
||||||
gst_video::VideoInfo::from_caps(caps)
|
|
||||||
.map(|info| info.size())
|
|
||||||
.ok()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called whenever the input/output caps are changing, i.e. in the very beginning before data
|
|
||||||
// flow happens and whenever the situation in the pipeline is changing. All buffers after this
|
|
||||||
// call have the caps given here.
|
|
||||||
//
|
|
||||||
// We simply remember the resulting VideoInfo from the caps to be able to use this for knowing
|
|
||||||
// the width, stride, etc when transforming buffers
|
|
||||||
fn set_caps(
|
|
||||||
&self,
|
|
||||||
element: &Self::Type,
|
|
||||||
incaps: &gst::Caps,
|
|
||||||
outcaps: &gst::Caps,
|
|
||||||
) -> Result<(), gst::LoggableError> {
|
|
||||||
let in_info = match gst_video::VideoInfo::from_caps(incaps) {
|
|
||||||
Err(_) => return Err(gst::loggable_error!(CAT, "Failed to parse input caps")),
|
|
||||||
Ok(info) => info,
|
|
||||||
};
|
|
||||||
let out_info = match gst_video::VideoInfo::from_caps(outcaps) {
|
|
||||||
Err(_) => return Err(gst::loggable_error!(CAT, "Failed to parse output caps")),
|
|
||||||
Ok(info) => info,
|
|
||||||
};
|
|
||||||
|
|
||||||
gst_debug!(
|
|
||||||
CAT,
|
|
||||||
obj: element,
|
|
||||||
"Configured for caps {} to {}",
|
|
||||||
incaps,
|
|
||||||
outcaps
|
|
||||||
);
|
|
||||||
|
|
||||||
*self.state.lock().unwrap() = Some(State { in_info, out_info });
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when shutting down the element so we can release all stream-related state
|
|
||||||
// There's also start(), which is called whenever starting the element again
|
|
||||||
fn stop(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
|
|
||||||
// Drop state
|
|
||||||
let _ = self.state.lock().unwrap().take();
|
|
||||||
|
|
||||||
gst_info!(CAT, obj: element, "Stopped");
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
// Does the actual transformation of the input buffer to the output buffer
|
// Does the actual transformation of the input buffer to the output buffer
|
||||||
fn transform(
|
fn transform_frame(
|
||||||
&self,
|
&self,
|
||||||
element: &Self::Type,
|
_element: &Self::Type,
|
||||||
inbuf: &gst::Buffer,
|
in_frame: &gst_video::VideoFrameRef<&gst::BufferRef>,
|
||||||
outbuf: &mut gst::BufferRef,
|
out_frame: &mut gst_video::VideoFrameRef<&mut gst::BufferRef>,
|
||||||
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
) -> Result<gst::FlowSuccess, gst::FlowError> {
|
||||||
// Keep a local copy of the values of all our properties at this very moment. This
|
// Keep a local copy of the values of all our properties at this very moment. This
|
||||||
// ensures that the mutex is never locked for long and the application wouldn't
|
// ensures that the mutex is never locked for long and the application wouldn't
|
||||||
// have to block until this function returns when getting/setting property values
|
// have to block until this function returns when getting/setting property values
|
||||||
let settings = *self.settings.lock().unwrap();
|
let settings = *self.settings.lock().unwrap();
|
||||||
|
|
||||||
// Get a locked reference to our state, i.e. the input and output VideoInfo
|
|
||||||
let mut state_guard = self.state.lock().unwrap();
|
|
||||||
let state = state_guard.as_mut().ok_or_else(|| {
|
|
||||||
gst::element_error!(element, gst::CoreError::Negotiation, ["Have no state yet"]);
|
|
||||||
gst::FlowError::NotNegotiated
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// Map the input buffer as a VideoFrameRef. This is similar to directly mapping
|
|
||||||
// the buffer with inbuf.map_readable() but in addition extracts various video
|
|
||||||
// specific metadata and sets up a convenient data structure that directly gives
|
|
||||||
// pointers to the different planes and has all the information about the raw
|
|
||||||
// video frame, like width, height, stride, video format, etc.
|
|
||||||
//
|
|
||||||
// This fails if the buffer can't be read or is invalid in relation to the video
|
|
||||||
// info that is passed here
|
|
||||||
let in_frame =
|
|
||||||
gst_video::VideoFrameRef::from_buffer_ref_readable(inbuf.as_ref(), &state.in_info)
|
|
||||||
.map_err(|_| {
|
|
||||||
gst::element_error!(
|
|
||||||
element,
|
|
||||||
gst::CoreError::Failed,
|
|
||||||
["Failed to map input buffer readable"]
|
|
||||||
);
|
|
||||||
gst::FlowError::Error
|
|
||||||
})?;
|
|
||||||
|
|
||||||
// And now map the output buffer writable, so we can fill it.
|
|
||||||
let mut out_frame =
|
|
||||||
gst_video::VideoFrameRef::from_buffer_ref_writable(outbuf, &state.out_info).map_err(
|
|
||||||
|_| {
|
|
||||||
gst::element_error!(
|
|
||||||
element,
|
|
||||||
gst::CoreError::Failed,
|
|
||||||
["Failed to map output buffer writable"]
|
|
||||||
);
|
|
||||||
gst::FlowError::Error
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// Keep the various metadata we need for working with the video frames in
|
// Keep the various metadata we need for working with the video frames in
|
||||||
// local variables. This saves some typing below.
|
// local variables. This saves some typing below.
|
||||||
let width = in_frame.width() as usize;
|
let width = in_frame.width() as usize;
|
||||||
|
|
Loading…
Reference in a new issue