mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer-rs.git
synced 2024-09-27 14:30:36 +00:00
174 lines
5.6 KiB
Rust
174 lines
5.6 KiB
Rust
#![allow(clippy::non_send_fields_in_send_ty)]
|
|
|
|
use anyhow::Result;
|
|
|
|
#[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 std::sync::Mutex;
|
|
|
|
use gst_base::subclass::BaseTransformMode;
|
|
use gst_gl::{
|
|
prelude::*,
|
|
subclass::{prelude::*, GLFilterMode},
|
|
*,
|
|
};
|
|
use once_cell::sync::Lazy;
|
|
|
|
use super::{gl, FRAGMENT_SHADER};
|
|
|
|
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::builder().property("name", name).build()
|
|
}
|
|
}
|
|
|
|
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, 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::debug!(
|
|
CAT,
|
|
imp: self,
|
|
"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::debug!(
|
|
CAT,
|
|
imp: self,
|
|
"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 GstObjectImpl 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) -> Result<(), gst::LoggableError> {
|
|
let filter = self.obj();
|
|
|
|
// Create a shader when GL is started, knowing that the OpenGL context is
|
|
// available.
|
|
let context = GLBaseFilterExt::context(&*filter).unwrap();
|
|
self.create_shader(&context)?;
|
|
self.parent_gl_start()
|
|
}
|
|
}
|
|
impl GLFilterImpl for GLMirrorFilter {
|
|
const MODE: GLFilterMode = GLFilterMode::Texture;
|
|
|
|
fn filter_texture(
|
|
&self,
|
|
input: &gst_gl::GLMemory,
|
|
output: &gst_gl::GLMemory,
|
|
) -> Result<(), gst::LoggableError> {
|
|
let filter = self.obj();
|
|
|
|
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(input, output)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn example_main() -> Result<()> {
|
|
gst::init().unwrap();
|
|
let glfilter = mirror::GLMirrorFilter::new(Some("Mirror filter"));
|
|
App::new(Some(glfilter.as_ref())).and_then(main_loop)
|
|
}
|
|
|
|
fn main() -> Result<()> {
|
|
examples_common::run(example_main)
|
|
}
|