hsv: Port detecter/filter to VideoFilter base class

This commit is contained in:
Sebastian Dröge 2021-10-16 15:24:11 +03:00
parent 76a33e8f47
commit cecacf5430
3 changed files with 58 additions and 194 deletions

View file

@ -13,7 +13,6 @@ gst-base = { package = "gstreamer-base", git = "https://gitlab.freedesktop.org/g
gst-video = { package = "gstreamer-video", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" } gst-video = { package = "gstreamer-video", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" } gst-audio = { package = "gstreamer-audio", git = "https://gitlab.freedesktop.org/gstreamer/gstreamer-rs" }
byte-slice-cast = "1.0" byte-slice-cast = "1.0"
atomic_refcell = "0.1"
num-traits = "0.2" num-traits = "0.2"
once_cell = "1.0" once_cell = "1.0"

View file

@ -12,8 +12,8 @@ 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 atomic_refcell::AtomicRefCell;
use std::i32; use std::i32;
use std::sync::Mutex; use std::sync::Mutex;
@ -54,17 +54,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 HsvDetector { pub struct HsvDetector {
settings: Mutex<Settings>, settings: Mutex<Settings>,
state: AtomicRefCell<Option<State>>,
} }
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| { static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
@ -79,7 +72,7 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
impl ObjectSubclass for HsvDetector { impl ObjectSubclass for HsvDetector {
const NAME: &'static str = "HsvDetector"; const NAME: &'static str = "HsvDetector";
type Type = super::HsvDetector; type Type = super::HsvDetector;
type ParentType = gst_base::BaseTransform; type ParentType = gst_video::VideoFilter;
} }
fn video_input_formats() -> Vec<glib::SendValue> { fn video_input_formats() -> Vec<glib::SendValue> {
@ -463,90 +456,22 @@ impl BaseTransformImpl for HsvDetector {
Some(other_caps) Some(other_caps)
} }
} }
}
fn unit_size(&self, _element: &Self::Type, caps: &gst::Caps) -> Option<usize> { impl VideoFilterImpl for HsvDetector {
gst_video::VideoInfo::from_caps(caps) fn transform_frame(
.map(|info| info.size())
.ok()
}
fn set_caps(
&self, &self,
element: &Self::Type, _element: &Self::Type,
incaps: &gst::Caps, in_frame: &gst_video::VideoFrameRef<&gst::BufferRef>,
outcaps: &gst::Caps, out_frame: &mut gst_video::VideoFrameRef<&mut gst::BufferRef>,
) -> 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.borrow_mut() = Some(State { in_info, out_info });
Ok(())
}
fn stop(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
// Drop state
*self.state.borrow_mut() = None;
gst_info!(CAT, obj: element, "Stopped");
Ok(())
}
fn transform(
&self,
element: &Self::Type,
inbuf: &gst::Buffer,
outbuf: &mut gst::BufferRef,
) -> Result<gst::FlowSuccess, gst::FlowError> { ) -> Result<gst::FlowSuccess, gst::FlowError> {
let mut state_guard = self.state.borrow_mut(); match in_frame.format() {
let state = state_guard.as_mut().ok_or(gst::FlowError::NotNegotiated)?;
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
},
)?;
match state.in_info.format() {
gst_video::VideoFormat::Rgbx | gst_video::VideoFormat::Rgb => { gst_video::VideoFormat::Rgbx | gst_video::VideoFormat::Rgb => {
match state.out_info.format() { match out_frame.format() {
gst_video::VideoFormat::Rgba => { gst_video::VideoFormat::Rgba => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_rgb( hsvutils::from_rgb(
in_p[..3].try_into().expect("slice with incorrect length"), in_p[..3].try_into().expect("slice with incorrect length"),
@ -560,8 +485,8 @@ impl BaseTransformImpl for HsvDetector {
} }
gst_video::VideoFormat::Argb => { gst_video::VideoFormat::Argb => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_rgb( hsvutils::from_rgb(
in_p[..3].try_into().expect("slice with incorrect length"), in_p[..3].try_into().expect("slice with incorrect length"),
@ -575,8 +500,8 @@ impl BaseTransformImpl for HsvDetector {
} }
gst_video::VideoFormat::Bgra => { gst_video::VideoFormat::Bgra => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_rgb( hsvutils::from_rgb(
in_p[..3].try_into().expect("slice with incorrect length"), in_p[..3].try_into().expect("slice with incorrect length"),
@ -592,8 +517,8 @@ impl BaseTransformImpl for HsvDetector {
} }
gst_video::VideoFormat::Abgr => { gst_video::VideoFormat::Abgr => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_rgb( hsvutils::from_rgb(
in_p[..3].try_into().expect("slice with incorrect length"), in_p[..3].try_into().expect("slice with incorrect length"),
@ -611,11 +536,11 @@ impl BaseTransformImpl for HsvDetector {
} }
} }
gst_video::VideoFormat::Xrgb => { gst_video::VideoFormat::Xrgb => {
match state.out_info.format() { match out_frame.format() {
gst_video::VideoFormat::Rgba => { gst_video::VideoFormat::Rgba => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_rgb( hsvutils::from_rgb(
in_p[1..4].try_into().expect("slice with incorrect length"), in_p[1..4].try_into().expect("slice with incorrect length"),
@ -629,8 +554,8 @@ impl BaseTransformImpl for HsvDetector {
} }
gst_video::VideoFormat::Argb => { gst_video::VideoFormat::Argb => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_rgb( hsvutils::from_rgb(
in_p[1..4].try_into().expect("slice with incorrect length"), in_p[1..4].try_into().expect("slice with incorrect length"),
@ -644,8 +569,8 @@ impl BaseTransformImpl for HsvDetector {
} }
gst_video::VideoFormat::Bgra => { gst_video::VideoFormat::Bgra => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_rgb( hsvutils::from_rgb(
in_p[1..4].try_into().expect("slice with incorrect length"), in_p[1..4].try_into().expect("slice with incorrect length"),
@ -661,8 +586,8 @@ impl BaseTransformImpl for HsvDetector {
} }
gst_video::VideoFormat::Abgr => { gst_video::VideoFormat::Abgr => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_rgb( hsvutils::from_rgb(
in_p[1..4].try_into().expect("slice with incorrect length"), in_p[1..4].try_into().expect("slice with incorrect length"),
@ -680,11 +605,11 @@ impl BaseTransformImpl for HsvDetector {
}; };
} }
gst_video::VideoFormat::Bgrx | gst_video::VideoFormat::Bgr => { gst_video::VideoFormat::Bgrx | gst_video::VideoFormat::Bgr => {
match state.out_info.format() { match out_frame.format() {
gst_video::VideoFormat::Rgba => { gst_video::VideoFormat::Rgba => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_bgr( hsvutils::from_bgr(
in_p[..3].try_into().expect("slice with incorrect length"), in_p[..3].try_into().expect("slice with incorrect length"),
@ -700,8 +625,8 @@ impl BaseTransformImpl for HsvDetector {
} }
gst_video::VideoFormat::Argb => { gst_video::VideoFormat::Argb => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_bgr( hsvutils::from_bgr(
in_p[..3].try_into().expect("slice with incorrect length"), in_p[..3].try_into().expect("slice with incorrect length"),
@ -717,8 +642,8 @@ impl BaseTransformImpl for HsvDetector {
} }
gst_video::VideoFormat::Bgra => { gst_video::VideoFormat::Bgra => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_bgr( hsvutils::from_bgr(
in_p[..3].try_into().expect("slice with incorrect length"), in_p[..3].try_into().expect("slice with incorrect length"),
@ -732,8 +657,8 @@ impl BaseTransformImpl for HsvDetector {
} }
gst_video::VideoFormat::Abgr => { gst_video::VideoFormat::Abgr => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_bgr( hsvutils::from_bgr(
in_p[..3].try_into().expect("slice with incorrect length"), in_p[..3].try_into().expect("slice with incorrect length"),
@ -748,11 +673,11 @@ impl BaseTransformImpl for HsvDetector {
_ => unreachable!(), _ => unreachable!(),
} }
} }
gst_video::VideoFormat::Xbgr => match state.out_info.format() { gst_video::VideoFormat::Xbgr => match out_frame.format() {
gst_video::VideoFormat::Rgba => { gst_video::VideoFormat::Rgba => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_bgr( hsvutils::from_bgr(
in_p[1..4].try_into().expect("slice with incorrect length"), in_p[1..4].try_into().expect("slice with incorrect length"),
@ -768,8 +693,8 @@ impl BaseTransformImpl for HsvDetector {
} }
gst_video::VideoFormat::Argb => { gst_video::VideoFormat::Argb => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_bgr( hsvutils::from_bgr(
in_p[1..4].try_into().expect("slice with incorrect length"), in_p[1..4].try_into().expect("slice with incorrect length"),
@ -785,8 +710,8 @@ impl BaseTransformImpl for HsvDetector {
} }
gst_video::VideoFormat::Bgra => { gst_video::VideoFormat::Bgra => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_bgr( hsvutils::from_bgr(
in_p[1..4].try_into().expect("slice with incorrect length"), in_p[1..4].try_into().expect("slice with incorrect length"),
@ -800,8 +725,8 @@ impl BaseTransformImpl for HsvDetector {
} }
gst_video::VideoFormat::Abgr => { gst_video::VideoFormat::Abgr => {
self.hsv_detect( self.hsv_detect(
&in_frame, in_frame,
&mut out_frame, out_frame,
|in_p| { |in_p| {
hsvutils::from_bgr( hsvutils::from_bgr(
in_p[1..4].try_into().expect("slice with incorrect length"), in_p[1..4].try_into().expect("slice with incorrect length"),

View file

@ -7,12 +7,12 @@
// except according to those terms. // except according to those terms.
use gst::glib; use gst::glib;
use gst::gst_info;
use gst::prelude::*; use gst::prelude::*;
use gst::subclass::prelude::*; use gst::subclass::prelude::*;
use gst::{gst_debug, gst_info};
use gst_base::subclass::prelude::*; use gst_base::subclass::prelude::*;
use gst_video::subclass::prelude::*;
use atomic_refcell::AtomicRefCell;
use std::i32; use std::i32;
use std::sync::Mutex; use std::sync::Mutex;
@ -50,16 +50,10 @@ impl Default for Settings {
} }
} }
// Stream-specific state, i.e. video format configuration
struct State {
info: gst_video::VideoInfo,
}
// Struct containing all the element data // Struct containing all the element data
#[derive(Default)] #[derive(Default)]
pub struct HsvFilter { pub struct HsvFilter {
settings: Mutex<Settings>, settings: Mutex<Settings>,
state: AtomicRefCell<Option<State>>,
} }
static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| { static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
@ -74,7 +68,7 @@ static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
impl ObjectSubclass for HsvFilter { impl ObjectSubclass for HsvFilter {
const NAME: &'static str = "HsvFilter"; const NAME: &'static str = "HsvFilter";
type Type = super::HsvFilter; type Type = super::HsvFilter;
type ParentType = gst_base::BaseTransform; type ParentType = gst_video::VideoFilter;
} }
impl HsvFilter { impl HsvFilter {
@ -357,74 +351,20 @@ impl BaseTransformImpl for HsvFilter {
gst_base::subclass::BaseTransformMode::AlwaysInPlace; gst_base::subclass::BaseTransformMode::AlwaysInPlace;
const PASSTHROUGH_ON_SAME_CAPS: bool = false; const PASSTHROUGH_ON_SAME_CAPS: bool = false;
const TRANSFORM_IP_ON_PASSTHROUGH: bool = false; const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
}
fn unit_size(&self, _element: &Self::Type, caps: &gst::Caps) -> Option<usize> { impl VideoFilterImpl for HsvFilter {
gst_video::VideoInfo::from_caps(caps) fn transform_frame_ip(
.map(|info| info.size())
.ok()
}
fn set_caps(
&self, &self,
element: &Self::Type, _element: &Self::Type,
incaps: &gst::Caps, frame: &mut gst_video::VideoFrameRef<&mut gst::BufferRef>,
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.borrow_mut() = Some(State { info: out_info });
Ok(())
}
fn stop(&self, element: &Self::Type) -> Result<(), gst::ErrorMessage> {
// Drop state
*self.state.borrow_mut() = None;
gst_info!(CAT, obj: element, "Stopped");
Ok(())
}
fn transform_ip(
&self,
element: &Self::Type,
buf: &mut gst::BufferRef,
) -> Result<gst::FlowSuccess, gst::FlowError> { ) -> Result<gst::FlowSuccess, gst::FlowError> {
let mut state_guard = self.state.borrow_mut(); match frame.format() {
let state = state_guard.as_mut().ok_or(gst::FlowError::NotNegotiated)?;
let mut frame = gst_video::VideoFrameRef::from_buffer_ref_writable(buf, &state.info)
.map_err(|_| {
gst::element_error!(
element,
gst::CoreError::Failed,
["Failed to map output buffer writable"]
);
gst::FlowError::Error
})?;
match state.info.format() {
gst_video::VideoFormat::Rgbx gst_video::VideoFormat::Rgbx
| gst_video::VideoFormat::Rgba | gst_video::VideoFormat::Rgba
| gst_video::VideoFormat::Rgb => { | gst_video::VideoFormat::Rgb => {
self.hsv_filter( self.hsv_filter(
&mut frame, frame,
|p| hsvutils::from_rgb(p[..3].try_into().expect("slice with incorrect length")), |p| hsvutils::from_rgb(p[..3].try_into().expect("slice with incorrect length")),
|hsv, p| { |hsv, p| {
p[..3].copy_from_slice(&hsvutils::to_rgb(hsv)); p[..3].copy_from_slice(&hsvutils::to_rgb(hsv));
@ -433,7 +373,7 @@ impl BaseTransformImpl for HsvFilter {
} }
gst_video::VideoFormat::Xrgb | gst_video::VideoFormat::Argb => { gst_video::VideoFormat::Xrgb | gst_video::VideoFormat::Argb => {
self.hsv_filter( self.hsv_filter(
&mut frame, frame,
|p| { |p| {
hsvutils::from_rgb(p[1..4].try_into().expect("slice with incorrect length")) hsvutils::from_rgb(p[1..4].try_into().expect("slice with incorrect length"))
}, },
@ -446,7 +386,7 @@ impl BaseTransformImpl for HsvFilter {
| gst_video::VideoFormat::Bgra | gst_video::VideoFormat::Bgra
| gst_video::VideoFormat::Bgr => { | gst_video::VideoFormat::Bgr => {
self.hsv_filter( self.hsv_filter(
&mut frame, frame,
|p| hsvutils::from_bgr(p[..3].try_into().expect("slice with incorrect length")), |p| hsvutils::from_bgr(p[..3].try_into().expect("slice with incorrect length")),
|hsv, p| { |hsv, p| {
p[..3].copy_from_slice(&hsvutils::to_bgr(hsv)); p[..3].copy_from_slice(&hsvutils::to_bgr(hsv));
@ -455,7 +395,7 @@ impl BaseTransformImpl for HsvFilter {
} }
gst_video::VideoFormat::Xbgr | gst_video::VideoFormat::Abgr => { gst_video::VideoFormat::Xbgr | gst_video::VideoFormat::Abgr => {
self.hsv_filter( self.hsv_filter(
&mut frame, frame,
|p| { |p| {
hsvutils::from_bgr(p[1..4].try_into().expect("slice with incorrect length")) hsvutils::from_bgr(p[1..4].try_into().expect("slice with incorrect length"))
}, },