forked from mirrors/gstreamer-rs
examples: Add simple mirror effect implemented as GLFilter element
This commit is contained in:
parent
430d89539e
commit
6c3cc3c422
4 changed files with 220 additions and 26 deletions
|
@ -161,6 +161,10 @@ required-features = ["ges"]
|
|||
name = "glwindow"
|
||||
required-features = ["gl"]
|
||||
|
||||
[[bin]]
|
||||
name = "glfilter"
|
||||
required-features = ["gl"]
|
||||
|
||||
[[bin]]
|
||||
name = "subclass"
|
||||
|
||||
|
|
172
examples/src/bin/glfilter.rs
Normal file
172
examples/src/bin/glfilter.rs
Normal file
|
@ -0,0 +1,172 @@
|
|||
#[path = "../glupload.rs"]
|
||||
mod glupload;
|
||||
use glupload::*;
|
||||
|
||||
#[path = "../examples-common.rs"]
|
||||
pub mod examples_common;
|
||||
|
||||
/// The fragment shader used for transforming GL textures travelling through the
|
||||
/// pipeline. This fragment shader links against the default vertex shader
|
||||
/// provided by [`GLSLStage::new_default_vertex`].
|
||||
const FRAGMENT_SHADER: &str = r#"
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
// The filter draws a fullscreen quad and provides its coordinates here:
|
||||
varying vec2 v_texcoord;
|
||||
|
||||
// The input texture is bound on a uniform sampler named `tex`:
|
||||
uniform sampler2D tex;
|
||||
|
||||
void main () {
|
||||
// Flip texture read coordinate on the x axis to create a mirror effect:
|
||||
gl_FragColor = texture2D(tex, vec2(1.0 - v_texcoord.x, v_texcoord.y));
|
||||
}
|
||||
"#;
|
||||
|
||||
mod mirror {
|
||||
use super::{gl, FRAGMENT_SHADER};
|
||||
use gst::subclass::prelude::*;
|
||||
use gst_base::subclass::prelude::*;
|
||||
use gst_base::subclass::BaseTransformMode;
|
||||
use gst_gl::prelude::*;
|
||||
use gst_gl::subclass::prelude::*;
|
||||
use gst_gl::subclass::GLFilterMode;
|
||||
use gst_gl::*;
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::Mutex;
|
||||
|
||||
pub static CAT: Lazy<gst::DebugCategory> = Lazy::new(|| {
|
||||
gst::DebugCategory::new(
|
||||
"rsglmirrorfilter",
|
||||
gst::DebugColorFlags::empty(),
|
||||
Some("Rust GL Mirror Filter"),
|
||||
)
|
||||
});
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct GLMirrorFilter(ObjectSubclass<imp::GLMirrorFilter>) @extends gst_gl::GLFilter, gst_gl::GLBaseFilter, gst_base::BaseTransform, gst::Element, gst::Object;
|
||||
}
|
||||
|
||||
impl GLMirrorFilter {
|
||||
pub fn new(name: Option<&str>) -> Self {
|
||||
glib::Object::new(&[("name", &name)]).expect("Failed to create GL Mirror Filter Object")
|
||||
}
|
||||
}
|
||||
|
||||
mod imp {
|
||||
use super::*;
|
||||
|
||||
/// Private data consists of the transformation shader which is compiled
|
||||
/// in advance to running the actual filter.
|
||||
#[derive(Default)]
|
||||
pub struct GLMirrorFilter {
|
||||
shader: Mutex<Option<GLShader>>,
|
||||
}
|
||||
|
||||
impl GLMirrorFilter {
|
||||
fn create_shader(
|
||||
&self,
|
||||
filter: &<Self as ObjectSubclass>::Type,
|
||||
context: &GLContext,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
let shader = GLShader::new(context);
|
||||
|
||||
let vertex = GLSLStage::new_default_vertex(context);
|
||||
vertex.compile().unwrap();
|
||||
shader.attach_unlocked(&vertex)?;
|
||||
|
||||
gst::gst_debug!(
|
||||
CAT,
|
||||
obj: filter,
|
||||
"Compiling fragment shader {}",
|
||||
FRAGMENT_SHADER
|
||||
);
|
||||
|
||||
let fragment = GLSLStage::with_strings(
|
||||
context,
|
||||
gl::FRAGMENT_SHADER,
|
||||
// new_default_vertex is compiled with this version and profile:
|
||||
GLSLVersion::None,
|
||||
GLSLProfile::ES | GLSLProfile::COMPATIBILITY,
|
||||
&[FRAGMENT_SHADER],
|
||||
);
|
||||
fragment.compile().unwrap();
|
||||
shader.attach_unlocked(&fragment)?;
|
||||
shader.link().unwrap();
|
||||
|
||||
gst::gst_debug!(
|
||||
CAT,
|
||||
obj: filter,
|
||||
"Successfully compiled and linked {:?}",
|
||||
shader
|
||||
);
|
||||
|
||||
*self.shader.lock().unwrap() = Some(shader);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// See `subclass.rs` for general documentation on creating a subclass. Extended
|
||||
// information like element metadata have been omitted for brevity.
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for GLMirrorFilter {
|
||||
const NAME: &'static str = "RsGLMirrorFilter";
|
||||
type Type = super::GLMirrorFilter;
|
||||
type ParentType = gst_gl::GLFilter;
|
||||
}
|
||||
|
||||
impl ElementImpl for GLMirrorFilter {}
|
||||
impl ObjectImpl for GLMirrorFilter {}
|
||||
impl BaseTransformImpl for GLMirrorFilter {
|
||||
const MODE: BaseTransformMode = BaseTransformMode::NeverInPlace;
|
||||
const PASSTHROUGH_ON_SAME_CAPS: bool = false;
|
||||
const TRANSFORM_IP_ON_PASSTHROUGH: bool = false;
|
||||
}
|
||||
impl GLBaseFilterImpl for GLMirrorFilter {
|
||||
fn gl_start(&self, filter: &Self::Type) -> Result<(), gst::LoggableError> {
|
||||
// Create a shader when GL is started, knowing that the OpenGL context is
|
||||
// available.
|
||||
let context = filter.context().unwrap();
|
||||
self.create_shader(filter, &context)?;
|
||||
self.parent_gl_start(filter)
|
||||
}
|
||||
}
|
||||
impl GLFilterImpl for GLMirrorFilter {
|
||||
const MODE: GLFilterMode = GLFilterMode::Texture;
|
||||
|
||||
fn filter_texture(
|
||||
&self,
|
||||
filter: &Self::Type,
|
||||
input: &gst_gl::GLMemory,
|
||||
output: &gst_gl::GLMemory,
|
||||
) -> Result<(), gst::LoggableError> {
|
||||
let shader = self.shader.lock().unwrap();
|
||||
// Use the underlying filter implementation to transform the input texture into
|
||||
// an output texture with the shader.
|
||||
filter.render_to_target_with_shader(
|
||||
input,
|
||||
output,
|
||||
shader
|
||||
.as_ref()
|
||||
.expect("No shader, call `create_shader` first!"),
|
||||
);
|
||||
self.parent_filter_texture(filter, input, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn example_main() {
|
||||
gst::init().unwrap();
|
||||
let glfilter = mirror::GLMirrorFilter::new(Some("foo"));
|
||||
App::new(Some(glfilter.as_ref()))
|
||||
.and_then(main_loop)
|
||||
.unwrap_or_else(|e| eprintln!("Error! {}", e))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
examples_common::run(example_main);
|
||||
}
|
|
@ -6,7 +6,7 @@ use glupload::*;
|
|||
pub mod examples_common;
|
||||
|
||||
fn example_main() {
|
||||
App::new()
|
||||
App::new(None)
|
||||
.and_then(main_loop)
|
||||
.unwrap_or_else(|e| eprintln!("Error! {}", e))
|
||||
}
|
||||
|
|
|
@ -76,7 +76,7 @@ void main() {
|
|||
#[allow(clippy::unused_unit)]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
#[allow(clippy::manual_non_exhaustive)]
|
||||
mod gl {
|
||||
pub(crate) mod gl {
|
||||
pub use self::Gles2 as Gl;
|
||||
include!(concat!(env!("OUT_DIR"), "/test_gl_bindings.rs"));
|
||||
}
|
||||
|
@ -327,10 +327,10 @@ pub(crate) struct App {
|
|||
}
|
||||
|
||||
impl App {
|
||||
pub(crate) fn new() -> Result<App, Error> {
|
||||
pub(crate) fn new(gl_element: Option<&gst::Element>) -> Result<App, Error> {
|
||||
gst::init()?;
|
||||
|
||||
let (pipeline, appsink, glupload) = App::create_pipeline()?;
|
||||
let (pipeline, appsink, glupload) = App::create_pipeline(gl_element)?;
|
||||
let bus = pipeline
|
||||
.bus()
|
||||
.expect("Pipeline without bus. Shouldn't happen!");
|
||||
|
@ -531,23 +531,18 @@ impl App {
|
|||
}
|
||||
}
|
||||
|
||||
fn create_pipeline() -> Result<(gst::Pipeline, gst_app::AppSink, gst::Element), Error> {
|
||||
fn create_pipeline(
|
||||
gl_element: Option<&gst::Element>,
|
||||
) -> Result<(gst::Pipeline, gst_app::AppSink, gst::Element), Error> {
|
||||
let pipeline = gst::Pipeline::new(None);
|
||||
let src = gst::ElementFactory::make("videotestsrc", None)
|
||||
.map_err(|_| MissingElement("videotestsrc"))?;
|
||||
let sink = gst::ElementFactory::make("glsinkbin", None)
|
||||
.map_err(|_| MissingElement("glsinkbin"))?;
|
||||
|
||||
pipeline.add_many(&[&src, &sink])?;
|
||||
src.link(&sink)?;
|
||||
|
||||
let appsink = gst::ElementFactory::make("appsink", None)
|
||||
.map_err(|_| MissingElement("appsink"))?
|
||||
.dynamic_cast::<gst_app::AppSink>()
|
||||
.expect("Sink element is expected to be an appsink!");
|
||||
|
||||
sink.set_property("sink", &appsink)?;
|
||||
|
||||
appsink.set_property("enable-last-sample", &false)?;
|
||||
appsink.set_property("emit-signals", &false)?;
|
||||
appsink.set_property("max-buffers", &1u32)?;
|
||||
|
@ -559,21 +554,44 @@ impl App {
|
|||
.build();
|
||||
appsink.set_caps(Some(&caps));
|
||||
|
||||
// get the glupload element to extract later the used context in it
|
||||
let mut iter = sink.dynamic_cast::<gst::Bin>().unwrap().iterate_elements();
|
||||
let glupload = loop {
|
||||
match iter.next() {
|
||||
Ok(Some(element)) => {
|
||||
if "glupload" == element.factory().unwrap().name() {
|
||||
break Some(element);
|
||||
}
|
||||
}
|
||||
Err(gst::IteratorError::Resync) => iter.resync(),
|
||||
_ => break None,
|
||||
}
|
||||
};
|
||||
if let Some(gl_element) = gl_element {
|
||||
let glupload = gst::ElementFactory::make("glupload", None)
|
||||
.map_err(|_| MissingElement("glupload"))?;
|
||||
|
||||
Ok((pipeline, appsink, glupload.unwrap()))
|
||||
pipeline.add_many(&[&src, &glupload])?;
|
||||
pipeline.add(gl_element)?;
|
||||
pipeline.add(&appsink)?;
|
||||
|
||||
src.link(&glupload)?;
|
||||
glupload.link(gl_element)?;
|
||||
gl_element.link(&appsink)?;
|
||||
|
||||
Ok((pipeline, appsink, glupload))
|
||||
} else {
|
||||
let sink = gst::ElementFactory::make("glsinkbin", None)
|
||||
.map_err(|_| MissingElement("glsinkbin"))?;
|
||||
|
||||
sink.set_property("sink", &appsink)?;
|
||||
|
||||
pipeline.add_many(&[&src, &sink])?;
|
||||
src.link(&sink)?;
|
||||
|
||||
// get the glupload element to extract later the used context in it
|
||||
let mut iter = sink.dynamic_cast::<gst::Bin>().unwrap().iterate_elements();
|
||||
let glupload = loop {
|
||||
match iter.next() {
|
||||
Ok(Some(element)) => {
|
||||
if "glupload" == element.factory().unwrap().name() {
|
||||
break Some(element);
|
||||
}
|
||||
}
|
||||
Err(gst::IteratorError::Resync) => iter.resync(),
|
||||
_ => break None,
|
||||
}
|
||||
};
|
||||
|
||||
Ok((pipeline, appsink, glupload.unwrap()))
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_messages(bus: &gst::Bus) -> Result<(), Error> {
|
||||
|
|
Loading…
Reference in a new issue