mirror of
https://github.com/servo/servo.git
synced 2025-06-09 09:03:23 +00:00
GStreamer plugin should use GLMemory
This commit is contained in:
parent
bb00a83ec4
commit
06a10c5b65
4 changed files with 299 additions and 254 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4788,6 +4788,7 @@ dependencies = [
|
||||||
"gstreamer",
|
"gstreamer",
|
||||||
"gstreamer-base",
|
"gstreamer-base",
|
||||||
"gstreamer-gl",
|
"gstreamer-gl",
|
||||||
|
"gstreamer-gl-sys",
|
||||||
"gstreamer-video",
|
"gstreamer-video",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libservo",
|
"libservo",
|
||||||
|
|
|
@ -21,7 +21,8 @@ gleam = "0.6"
|
||||||
glib = { version = "0.8", features = ["subclassing"] }
|
glib = { version = "0.8", features = ["subclassing"] }
|
||||||
gstreamer = { version = "0.14", features = ["subclassing"] }
|
gstreamer = { version = "0.14", features = ["subclassing"] }
|
||||||
gstreamer-base = { version = "0.14", features = ["subclassing"] }
|
gstreamer-base = { version = "0.14", features = ["subclassing"] }
|
||||||
gstreamer-gl = { version = "0.14", features = ["v1_16"] }
|
gstreamer-gl = { version = "0.14" }
|
||||||
|
gstreamer-gl-sys = { version = "0.8" }
|
||||||
gstreamer-video = { version = "0.14", features = ["subclassing"] }
|
gstreamer-video = { version = "0.14", features = ["subclassing"] }
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
lazy_static = "1.4"
|
lazy_static = "1.4"
|
||||||
|
|
|
@ -24,18 +24,19 @@ To run locally:
|
||||||
```
|
```
|
||||||
GST_PLUGIN_PATH=target/gstplugins \
|
GST_PLUGIN_PATH=target/gstplugins \
|
||||||
gst-launch-1.0 servosrc \
|
gst-launch-1.0 servosrc \
|
||||||
! queue \
|
! videorate \
|
||||||
! video/x-raw,framerate=25/1,width=512,height=512 \
|
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=1920,height=1080,format=RGBA \
|
||||||
! videoflip video-direction=vert \
|
! glimagesink rotate-method=vertical-flip
|
||||||
! autovideosink
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
*Note*: the following don't work, for some reason the pipeline isn't providing GLMemory.
|
||||||
|
|
||||||
To stream over the network:
|
To stream over the network:
|
||||||
```
|
```
|
||||||
GST_PLUGIN_PATH=target/gstplugins \
|
|
||||||
gst-launch-1.0 servosrc \
|
gst-launch-1.0 servosrc \
|
||||||
! queue \
|
! videorate \
|
||||||
! video/x-raw,framerate=25/1,width=512,height=512 \
|
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=1920,height=1080,format=RGBA \
|
||||||
|
! gldownload \
|
||||||
! videoconvert \
|
! videoconvert \
|
||||||
! videoflip video-direction=vert \
|
! videoflip video-direction=vert \
|
||||||
! theoraenc \
|
! theoraenc \
|
||||||
|
@ -46,9 +47,9 @@ GST_PLUGIN_PATH=target/gstplugins \
|
||||||
To save to a file:
|
To save to a file:
|
||||||
```
|
```
|
||||||
GST_PLUGIN_PATH=target/gstplugins \
|
GST_PLUGIN_PATH=target/gstplugins \
|
||||||
gst-launch-1.0 servosrc num-buffers=2000 \
|
! videorate \
|
||||||
! queue \
|
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=1920,height=1080,format=RGBA \
|
||||||
! video/x-raw,framerate=25/1,width=512,height=512 \
|
! gldownload \
|
||||||
! videoconvert \
|
! videoconvert \
|
||||||
! videoflip video-direction=vert \
|
! videoflip video-direction=vert \
|
||||||
! theoraenc \
|
! theoraenc \
|
||||||
|
|
|
@ -23,6 +23,7 @@ use glib::subclass::object::ObjectImplExt;
|
||||||
use glib::subclass::object::Property;
|
use glib::subclass::object::Property;
|
||||||
use glib::subclass::simple::ClassStruct;
|
use glib::subclass::simple::ClassStruct;
|
||||||
use glib::subclass::types::ObjectSubclass;
|
use glib::subclass::types::ObjectSubclass;
|
||||||
|
use glib::translate::FromGlibPtrBorrow;
|
||||||
use glib::value::Value;
|
use glib::value::Value;
|
||||||
use glib::ParamSpec;
|
use glib::ParamSpec;
|
||||||
use gstreamer::gst_element_error;
|
use gstreamer::gst_element_error;
|
||||||
|
@ -38,9 +39,6 @@ use gstreamer::ErrorMessage;
|
||||||
use gstreamer::FlowError;
|
use gstreamer::FlowError;
|
||||||
use gstreamer::FlowSuccess;
|
use gstreamer::FlowSuccess;
|
||||||
use gstreamer::Format;
|
use gstreamer::Format;
|
||||||
use gstreamer::Fraction;
|
|
||||||
use gstreamer::FractionRange;
|
|
||||||
use gstreamer::IntRange;
|
|
||||||
use gstreamer::LoggableError;
|
use gstreamer::LoggableError;
|
||||||
use gstreamer::PadDirection;
|
use gstreamer::PadDirection;
|
||||||
use gstreamer::PadPresence;
|
use gstreamer::PadPresence;
|
||||||
|
@ -49,8 +47,12 @@ use gstreamer::ResourceError;
|
||||||
use gstreamer_base::subclass::base_src::BaseSrcImpl;
|
use gstreamer_base::subclass::base_src::BaseSrcImpl;
|
||||||
use gstreamer_base::BaseSrc;
|
use gstreamer_base::BaseSrc;
|
||||||
use gstreamer_base::BaseSrcExt;
|
use gstreamer_base::BaseSrcExt;
|
||||||
use gstreamer_video::VideoFormat;
|
use gstreamer_gl::GLContext;
|
||||||
use gstreamer_video::VideoFrameRef;
|
use gstreamer_gl::GLContextExt;
|
||||||
|
use gstreamer_gl::GLContextExtManual;
|
||||||
|
use gstreamer_gl_sys::gst_gl_texture_target_to_gl;
|
||||||
|
use gstreamer_gl_sys::gst_is_gl_memory;
|
||||||
|
use gstreamer_gl_sys::GstGLMemory;
|
||||||
use gstreamer_video::VideoInfo;
|
use gstreamer_video::VideoInfo;
|
||||||
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
@ -80,6 +82,7 @@ use surfman_chains::SwapChain;
|
||||||
use surfman_chains_api::SwapChainAPI;
|
use surfman_chains_api::SwapChainAPI;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
@ -96,89 +99,19 @@ struct ServoSrcGfx {
|
||||||
device: Device,
|
device: Device,
|
||||||
context: Context,
|
context: Context,
|
||||||
gl: Rc<Gl>,
|
gl: Rc<Gl>,
|
||||||
fbo: GLuint,
|
read_fbo: GLuint,
|
||||||
}
|
draw_fbo: GLuint,
|
||||||
|
|
||||||
impl ServoSrcGfx {
|
|
||||||
fn new() -> ServoSrcGfx {
|
|
||||||
let version = surfman::GLVersion { major: 4, minor: 3 };
|
|
||||||
let flags = surfman::ContextAttributeFlags::empty();
|
|
||||||
let attributes = surfman::ContextAttributes { version, flags };
|
|
||||||
|
|
||||||
let connection = surfman::Connection::new().expect("Failed to create connection");
|
|
||||||
let adapter = surfman::Adapter::default().expect("Failed to create adapter");
|
|
||||||
let mut device =
|
|
||||||
surfman::Device::new(&connection, &adapter).expect("Failed to create device");
|
|
||||||
let descriptor = device
|
|
||||||
.create_context_descriptor(&attributes)
|
|
||||||
.expect("Failed to create descriptor");
|
|
||||||
let context = device
|
|
||||||
.create_context(&descriptor)
|
|
||||||
.expect("Failed to create context");
|
|
||||||
let gl = Gl::gl_fns(gl::ffi_gl::Gl::load_with(|s| {
|
|
||||||
device.get_proc_address(&context, s)
|
|
||||||
}));
|
|
||||||
|
|
||||||
// This is a workaround for surfman having a different bootstrap API with Angle
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
let mut device = device;
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
let mut device = Device::Hardware(device);
|
|
||||||
#[cfg(target_os = "windows")]
|
|
||||||
let mut context = context;
|
|
||||||
#[cfg(not(target_os = "windows"))]
|
|
||||||
let mut context = Context::Hardware(context);
|
|
||||||
|
|
||||||
device.make_context_current(&context).unwrap();
|
|
||||||
|
|
||||||
let size = Size2D::new(512, 512);
|
|
||||||
let surface_type = SurfaceType::Generic { size };
|
|
||||||
let surface = device
|
|
||||||
.create_surface(&mut context, SurfaceAccess::GPUCPU, &surface_type)
|
|
||||||
.expect("Failed to create surface");
|
|
||||||
|
|
||||||
gl.viewport(0, 0, size.width, size.height);
|
|
||||||
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
|
|
||||||
|
|
||||||
device
|
|
||||||
.bind_surface_to_context(&mut context, surface)
|
|
||||||
.expect("Failed to bind surface");
|
|
||||||
let fbo = device
|
|
||||||
.context_surface_info(&context)
|
|
||||||
.expect("Failed to get context info")
|
|
||||||
.expect("Failed to get context info")
|
|
||||||
.framebuffer_object;
|
|
||||||
gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
|
|
||||||
debug_assert_eq!(
|
|
||||||
(gl.check_framebuffer_status(gl::FRAMEBUFFER), gl.get_error()),
|
|
||||||
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
|
||||||
);
|
|
||||||
|
|
||||||
let fbo = gl.gen_framebuffers(1)[0];
|
|
||||||
debug_assert_eq!(
|
|
||||||
(gl.check_framebuffer_status(gl::FRAMEBUFFER), gl.get_error()),
|
|
||||||
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
|
||||||
);
|
|
||||||
|
|
||||||
device.make_no_context_current().unwrap();
|
|
||||||
|
|
||||||
Self {
|
|
||||||
device,
|
|
||||||
context,
|
|
||||||
gl,
|
|
||||||
fbo,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ServoSrcGfx {
|
impl Drop for ServoSrcGfx {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
|
self.gl.delete_framebuffers(&[self.read_fbo, self.draw_fbo]);
|
||||||
let _ = self.device.destroy_context(&mut self.context);
|
let _ = self.device.destroy_context(&mut self.context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
static GFX: RefCell<ServoSrcGfx> = RefCell::new(ServoSrcGfx::new());
|
static GFX_CACHE: RefCell<HashMap<GLContext, ServoSrcGfx>> = RefCell::new(HashMap::new());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -196,6 +129,7 @@ const DEFAULT_URL: &'static str =
|
||||||
struct ServoThread {
|
struct ServoThread {
|
||||||
receiver: Receiver<ServoSrcMsg>,
|
receiver: Receiver<ServoSrcMsg>,
|
||||||
swap_chain: SwapChain,
|
swap_chain: SwapChain,
|
||||||
|
gfx: Rc<RefCell<ServoSrcGfx>>,
|
||||||
servo: Servo<ServoSrcWindow>,
|
servo: Servo<ServoSrcWindow>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,10 +138,12 @@ impl ServoThread {
|
||||||
let embedder = Box::new(ServoSrcEmbedder);
|
let embedder = Box::new(ServoSrcEmbedder);
|
||||||
let window = Rc::new(ServoSrcWindow::new());
|
let window = Rc::new(ServoSrcWindow::new());
|
||||||
let swap_chain = window.swap_chain.clone();
|
let swap_chain = window.swap_chain.clone();
|
||||||
|
let gfx = window.gfx.clone();
|
||||||
let servo = Servo::new(embedder, window);
|
let servo = Servo::new(embedder, window);
|
||||||
Self {
|
Self {
|
||||||
receiver,
|
receiver,
|
||||||
swap_chain,
|
swap_chain,
|
||||||
|
gfx,
|
||||||
servo,
|
servo,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -235,8 +171,8 @@ impl ServoThread {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resize(&mut self, size: Size2D<i32, DevicePixel>) {
|
fn resize(&mut self, size: Size2D<i32, DevicePixel>) {
|
||||||
GFX.with(|gfx| {
|
{
|
||||||
let mut gfx = gfx.borrow_mut();
|
let mut gfx = self.gfx.borrow_mut();
|
||||||
let gfx = &mut *gfx;
|
let gfx = &mut *gfx;
|
||||||
self.swap_chain
|
self.swap_chain
|
||||||
.resize(&mut gfx.device, &mut gfx.context, size.to_untyped())
|
.resize(&mut gfx.device, &mut gfx.context, size.to_untyped())
|
||||||
|
@ -248,6 +184,9 @@ impl ServoThread {
|
||||||
.expect("Failed to get context info")
|
.expect("Failed to get context info")
|
||||||
.expect("Failed to get context info")
|
.expect("Failed to get context info")
|
||||||
.framebuffer_object;
|
.framebuffer_object;
|
||||||
|
gfx.device
|
||||||
|
.make_context_current(&gfx.context)
|
||||||
|
.expect("Failed to make current");
|
||||||
gfx.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
|
gfx.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
(
|
(
|
||||||
|
@ -256,20 +195,18 @@ impl ServoThread {
|
||||||
),
|
),
|
||||||
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
||||||
);
|
);
|
||||||
});
|
}
|
||||||
self.servo.handle_events(vec![WindowEvent::Resize]);
|
self.servo.handle_events(vec![WindowEvent::Resize]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Drop for ServoThread {
|
impl Drop for ServoThread {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
GFX.with(|gfx| {
|
let mut gfx = self.gfx.borrow_mut();
|
||||||
let mut gfx = gfx.borrow_mut();
|
|
||||||
let gfx = &mut *gfx;
|
let gfx = &mut *gfx;
|
||||||
self.swap_chain
|
self.swap_chain
|
||||||
.destroy(&mut gfx.device, &mut gfx.context)
|
.destroy(&mut gfx.device, &mut gfx.context)
|
||||||
.expect("Failed to destroy swap chain")
|
.expect("Failed to destroy swap chain")
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -291,54 +228,97 @@ impl EventLoopWaker for ServoSrcEmbedder {
|
||||||
|
|
||||||
struct ServoSrcWindow {
|
struct ServoSrcWindow {
|
||||||
swap_chain: SwapChain,
|
swap_chain: SwapChain,
|
||||||
|
gfx: Rc<RefCell<ServoSrcGfx>>,
|
||||||
gl: Rc<dyn gleam::gl::Gl>,
|
gl: Rc<dyn gleam::gl::Gl>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ServoSrcWindow {
|
impl ServoSrcWindow {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
GFX.with(|gfx| {
|
let version = surfman::GLVersion { major: 4, minor: 3 };
|
||||||
let mut gfx = gfx.borrow_mut();
|
let flags = surfman::ContextAttributeFlags::empty();
|
||||||
let gfx = &mut *gfx;
|
let attributes = surfman::ContextAttributes { version, flags };
|
||||||
let access = SurfaceAccess::GPUCPU;
|
|
||||||
gfx.device
|
let connection = surfman::Connection::new().expect("Failed to create connection");
|
||||||
.make_context_current(&mut gfx.context)
|
let adapter = surfman::Adapter::default().expect("Failed to create adapter");
|
||||||
|
let mut device =
|
||||||
|
surfman::Device::new(&connection, &adapter).expect("Failed to create device");
|
||||||
|
let descriptor = device
|
||||||
|
.create_context_descriptor(&attributes)
|
||||||
|
.expect("Failed to create descriptor");
|
||||||
|
let context = device
|
||||||
|
.create_context(&descriptor)
|
||||||
|
.expect("Failed to create context");
|
||||||
|
|
||||||
|
// This is a workaround for surfman having a different bootstrap API with Angle
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let mut device = device;
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
let mut device = Device::Hardware(device);
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
let mut context = context;
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
let mut context = Context::Hardware(context);
|
||||||
|
|
||||||
|
let gleam =
|
||||||
|
unsafe { gleam::gl::GlFns::load_with(|s| device.get_proc_address(&context, s)) };
|
||||||
|
let gl = Gl::gl_fns(gl::ffi_gl::Gl::load_with(|s| {
|
||||||
|
device.get_proc_address(&context, s)
|
||||||
|
}));
|
||||||
|
|
||||||
|
device
|
||||||
|
.make_context_current(&mut context)
|
||||||
.expect("Failed to make context current");
|
.expect("Failed to make context current");
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(gl.get_error(), gl::NO_ERROR);
|
||||||
(
|
let access = SurfaceAccess::GPUCPU;
|
||||||
gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
|
let size = Size2D::new(512, 512);
|
||||||
gfx.gl.get_error()
|
let surface_type = SurfaceType::Generic { size };
|
||||||
),
|
let surface = device
|
||||||
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
.create_surface(&mut context, access, &surface_type)
|
||||||
);
|
.expect("Failed to create surface");
|
||||||
let swap_chain = SwapChain::create_attached(&mut gfx.device, &mut gfx.context, access)
|
|
||||||
.expect("Failed to create swap chain");
|
device
|
||||||
let fbo = gfx
|
.bind_surface_to_context(&mut context, surface)
|
||||||
.device
|
.expect("Failed to bind surface");
|
||||||
.context_surface_info(&gfx.context)
|
let fbo = device
|
||||||
|
.context_surface_info(&context)
|
||||||
.expect("Failed to get context info")
|
.expect("Failed to get context info")
|
||||||
.expect("Failed to get context info")
|
.expect("Failed to get context info")
|
||||||
.framebuffer_object;
|
.framebuffer_object;
|
||||||
gfx.gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
|
gl.viewport(0, 0, size.width, size.height);
|
||||||
|
gl.bind_framebuffer(gl::FRAMEBUFFER, fbo);
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
(
|
(gl.check_framebuffer_status(gl::FRAMEBUFFER), gl.get_error()),
|
||||||
gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
|
|
||||||
gfx.gl.get_error()
|
|
||||||
),
|
|
||||||
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
||||||
);
|
);
|
||||||
let gl = unsafe {
|
|
||||||
gleam::gl::GlFns::load_with(|s| gfx.device.get_proc_address(&gfx.context, s))
|
let swap_chain = SwapChain::create_attached(&mut device, &mut context, access)
|
||||||
};
|
.expect("Failed to create swap chain");
|
||||||
Self { swap_chain, gl }
|
|
||||||
})
|
let read_fbo = gl.gen_framebuffers(1)[0];
|
||||||
|
let draw_fbo = gl.gen_framebuffers(1)[0];
|
||||||
|
|
||||||
|
device.make_no_context_current().unwrap();
|
||||||
|
|
||||||
|
let gfx = Rc::new(RefCell::new(ServoSrcGfx {
|
||||||
|
device,
|
||||||
|
context,
|
||||||
|
gl,
|
||||||
|
read_fbo,
|
||||||
|
draw_fbo,
|
||||||
|
}));
|
||||||
|
|
||||||
|
Self {
|
||||||
|
swap_chain,
|
||||||
|
gfx,
|
||||||
|
gl: gleam,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl WindowMethods for ServoSrcWindow {
|
impl WindowMethods for ServoSrcWindow {
|
||||||
fn present(&self) {
|
fn present(&self) {
|
||||||
GFX.with(|gfx| {
|
|
||||||
debug!("EMBEDDER present");
|
debug!("EMBEDDER present");
|
||||||
let mut gfx = gfx.borrow_mut();
|
let mut gfx = self.gfx.borrow_mut();
|
||||||
let gfx = &mut *gfx;
|
let gfx = &mut *gfx;
|
||||||
gfx.device
|
gfx.device
|
||||||
.make_context_current(&mut gfx.context)
|
.make_context_current(&mut gfx.context)
|
||||||
|
@ -368,13 +348,11 @@ impl WindowMethods for ServoSrcWindow {
|
||||||
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
||||||
);
|
);
|
||||||
let _ = gfx.device.make_no_context_current();
|
let _ = gfx.device.make_no_context_current();
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_gl_context_current(&self) {
|
fn make_gl_context_current(&self) {
|
||||||
GFX.with(|gfx| {
|
|
||||||
debug!("EMBEDDER make_context_current");
|
debug!("EMBEDDER make_context_current");
|
||||||
let mut gfx = gfx.borrow_mut();
|
let mut gfx = self.gfx.borrow_mut();
|
||||||
let gfx = &mut *gfx;
|
let gfx = &mut *gfx;
|
||||||
gfx.device
|
gfx.device
|
||||||
.make_context_current(&mut gfx.context)
|
.make_context_current(&mut gfx.context)
|
||||||
|
@ -386,7 +364,6 @@ impl WindowMethods for ServoSrcWindow {
|
||||||
),
|
),
|
||||||
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
||||||
);
|
);
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn gl(&self) -> Rc<dyn gleam::gl::Gl> {
|
fn gl(&self) -> Rc<dyn gleam::gl::Gl> {
|
||||||
|
@ -432,6 +409,12 @@ static PROPERTIES: [Property; 1] = [Property("url", |name| {
|
||||||
)
|
)
|
||||||
})];
|
})];
|
||||||
|
|
||||||
|
const CAPS: &str = "video/x-raw(memory:GLMemory),
|
||||||
|
format={RGBA,RGBx},
|
||||||
|
width=[1,2147483647],
|
||||||
|
height=[1,2147483647],
|
||||||
|
framerate=[0/1,2147483647/1]";
|
||||||
|
|
||||||
impl ObjectSubclass for ServoSrc {
|
impl ObjectSubclass for ServoSrc {
|
||||||
const NAME: &'static str = "ServoSrc";
|
const NAME: &'static str = "ServoSrc";
|
||||||
// gstreamer-gl doesn't have support for GLBaseSrc yet
|
// gstreamer-gl doesn't have support for GLBaseSrc yet
|
||||||
|
@ -464,21 +447,7 @@ impl ObjectSubclass for ServoSrc {
|
||||||
env!("CARGO_PKG_AUTHORS"),
|
env!("CARGO_PKG_AUTHORS"),
|
||||||
);
|
);
|
||||||
|
|
||||||
let src_caps = Caps::new_simple(
|
let src_caps = Caps::from_string(CAPS).unwrap();
|
||||||
"video/x-raw",
|
|
||||||
&[
|
|
||||||
("format", &VideoFormat::Bgrx.to_string()),
|
|
||||||
("width", &IntRange::<i32>::new(1, std::i32::MAX)),
|
|
||||||
("height", &IntRange::<i32>::new(1, std::i32::MAX)),
|
|
||||||
(
|
|
||||||
"framerate",
|
|
||||||
&FractionRange::new(
|
|
||||||
Fraction::new(1, std::i32::MAX),
|
|
||||||
Fraction::new(std::i32::MAX, 1),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
let src_pad_template =
|
let src_pad_template =
|
||||||
PadTemplate::new("src", PadDirection::Src, PadPresence::Always, &src_caps).unwrap();
|
PadTemplate::new("src", PadDirection::Src, PadPresence::Always, &src_caps).unwrap();
|
||||||
klass.add_pad_template(src_pad_template);
|
klass.add_pad_template(src_pad_template);
|
||||||
|
@ -525,6 +494,9 @@ impl ObjectImpl for ServoSrc {
|
||||||
|
|
||||||
impl ElementImpl for ServoSrc {}
|
impl ElementImpl for ServoSrc {}
|
||||||
|
|
||||||
|
thread_local! {
|
||||||
|
static GL: RefCell<Option<Rc<Gl>>> = RefCell::new(None);
|
||||||
|
}
|
||||||
impl BaseSrcImpl for ServoSrc {
|
impl BaseSrcImpl for ServoSrc {
|
||||||
fn set_caps(&self, _src: &BaseSrc, outcaps: &Caps) -> Result<(), LoggableError> {
|
fn set_caps(&self, _src: &BaseSrc, outcaps: &Caps) -> Result<(), LoggableError> {
|
||||||
let info = VideoInfo::from_caps(outcaps)
|
let info = VideoInfo::from_caps(outcaps)
|
||||||
|
@ -563,35 +535,85 @@ impl BaseSrcImpl for ServoSrc {
|
||||||
_length: u32,
|
_length: u32,
|
||||||
buffer: &mut BufferRef,
|
buffer: &mut BufferRef,
|
||||||
) -> Result<FlowSuccess, FlowError> {
|
) -> Result<FlowSuccess, FlowError> {
|
||||||
let guard = self.info.lock().map_err(|_| {
|
let memory = buffer.get_all_memory().ok_or_else(|| {
|
||||||
gst_element_error!(src, CoreError::Negotiation, ["Lock poisoned"]);
|
gst_element_error!(src, CoreError::Failed, ["Failed to get memory"]);
|
||||||
FlowError::NotNegotiated
|
FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
let info = guard.as_ref().ok_or_else(|| {
|
let memory = unsafe { memory.into_ptr() };
|
||||||
gst_element_error!(src, CoreError::Negotiation, ["Caps not set yet"]);
|
if unsafe { gst_is_gl_memory(memory) } == 0 {
|
||||||
FlowError::NotNegotiated
|
gst_element_error!(src, CoreError::Failed, ["Memory isn't GL memory"]);
|
||||||
})?;
|
return Err(FlowError::Error);
|
||||||
let mut frame = VideoFrameRef::from_buffer_ref_writable(buffer, info).ok_or_else(|| {
|
}
|
||||||
gst_element_error!(
|
let gl_memory = unsafe { (memory as *mut GstGLMemory).as_ref() }.ok_or_else(|| {
|
||||||
src,
|
gst_element_error!(src, CoreError::Failed, ["Memory is null"]);
|
||||||
CoreError::Failed,
|
|
||||||
["Failed to map output buffer writable"]
|
|
||||||
);
|
|
||||||
FlowError::Error
|
FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
let height = frame.height() as i32;
|
|
||||||
let width = frame.width() as i32;
|
|
||||||
let size = Size2D::new(width, height);
|
|
||||||
let format = frame.format();
|
|
||||||
debug!(
|
|
||||||
"Filling servosrc buffer {}x{} {:?} {:?}",
|
|
||||||
width, height, format, frame,
|
|
||||||
);
|
|
||||||
let data = frame.plane_data_mut(0).unwrap();
|
|
||||||
|
|
||||||
GFX.with(|gfx| {
|
let gl_context = unsafe { GLContext::from_glib_borrow(gl_memory.mem.context) };
|
||||||
let mut gfx = gfx.borrow_mut();
|
let draw_texture_id = gl_memory.tex_id;
|
||||||
let gfx = &mut *gfx;
|
let draw_texture_target = unsafe { gst_gl_texture_target_to_gl(gl_memory.tex_target) };
|
||||||
|
let height = gl_memory.info.height;
|
||||||
|
let width = gl_memory.info.width;
|
||||||
|
let size = Size2D::new(width, height);
|
||||||
|
debug!("Filling texture {} {}x{}", draw_texture_id, width, height);
|
||||||
|
|
||||||
|
gl_context.activate(true).map_err(|_| {
|
||||||
|
gst_element_error!(src, CoreError::Failed, ["Failed to activate GL context"]);
|
||||||
|
FlowError::Error
|
||||||
|
})?;
|
||||||
|
|
||||||
|
GFX_CACHE.with(|gfx_cache| {
|
||||||
|
let mut gfx_cache = gfx_cache.borrow_mut();
|
||||||
|
let gfx = gfx_cache.entry(gl_context.clone()).or_insert_with(|| {
|
||||||
|
debug!("Bootstrapping surfman");
|
||||||
|
let (device, context) = unsafe { surfman::Device::from_current_context() }
|
||||||
|
.expect("Failed to bootstrap surfman");
|
||||||
|
|
||||||
|
// This is a workaround for surfman having a different bootstrap API with Angle
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
let device = Device::Hardware(device);
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
let context = Context::Hardware(context);
|
||||||
|
|
||||||
|
let gl = Gl::gl_fns(gl::ffi_gl::Gl::load_with(|s| {
|
||||||
|
gl_context.get_proc_address(s) as *const _
|
||||||
|
}));
|
||||||
|
let draw_fbo = gl.gen_framebuffers(1)[0];
|
||||||
|
let read_fbo = gl.gen_framebuffers(1)[0];
|
||||||
|
ServoSrcGfx {
|
||||||
|
device,
|
||||||
|
context,
|
||||||
|
gl,
|
||||||
|
read_fbo,
|
||||||
|
draw_fbo,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR);
|
||||||
|
|
||||||
|
// Save the current GL state
|
||||||
|
let mut bound_fbos = [0, 0];
|
||||||
|
unsafe {
|
||||||
|
gfx.gl
|
||||||
|
.get_integer_v(gl::DRAW_FRAMEBUFFER_BINDING, &mut bound_fbos[0..]);
|
||||||
|
gfx.gl
|
||||||
|
.get_integer_v(gl::READ_FRAMEBUFFER_BINDING, &mut bound_fbos[1..]);
|
||||||
|
}
|
||||||
|
|
||||||
|
gfx.gl.bind_framebuffer(gl::FRAMEBUFFER, gfx.draw_fbo);
|
||||||
|
gfx.gl.framebuffer_texture_2d(
|
||||||
|
gl::FRAMEBUFFER,
|
||||||
|
gl::COLOR_ATTACHMENT0,
|
||||||
|
gl::TEXTURE_2D,
|
||||||
|
draw_texture_id,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR);
|
||||||
|
|
||||||
|
gfx.gl.clear_color(0.3, 0.2, 0.1, 1.0);
|
||||||
|
gfx.gl.clear(gl::COLOR_BUFFER_BIT);
|
||||||
|
debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR);
|
||||||
|
|
||||||
if let Some(surface) = self.swap_chain.take_surface() {
|
if let Some(surface) = self.swap_chain.take_surface() {
|
||||||
let surface_size = Size2D::from_untyped(gfx.device.surface_info(&surface).size);
|
let surface_size = Size2D::from_untyped(gfx.device.surface_info(&surface).size);
|
||||||
if size != surface_size {
|
if size != surface_size {
|
||||||
|
@ -600,36 +622,27 @@ impl BaseSrcImpl for ServoSrc {
|
||||||
let _ = self.sender.send(ServoSrcMsg::Resize(size));
|
let _ = self.sender.send(ServoSrcMsg::Resize(size));
|
||||||
}
|
}
|
||||||
|
|
||||||
gfx.device.make_context_current(&gfx.context).unwrap();
|
|
||||||
debug_assert_eq!(
|
|
||||||
(
|
|
||||||
gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
|
|
||||||
gfx.gl.get_error()
|
|
||||||
),
|
|
||||||
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
|
||||||
);
|
|
||||||
|
|
||||||
gfx.gl.viewport(0, 0, width, height);
|
|
||||||
debug_assert_eq!(
|
|
||||||
(
|
|
||||||
gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
|
|
||||||
gfx.gl.get_error()
|
|
||||||
),
|
|
||||||
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
|
||||||
);
|
|
||||||
|
|
||||||
let surface_texture = gfx
|
let surface_texture = gfx
|
||||||
.device
|
.device
|
||||||
.create_surface_texture(&mut gfx.context, surface)
|
.create_surface_texture(&mut gfx.context, surface)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let texture_id = surface_texture.gl_texture();
|
let read_texture_id = surface_texture.gl_texture();
|
||||||
|
let read_texture_target = gfx.device.surface_gl_texture_target();
|
||||||
|
|
||||||
gfx.gl.bind_framebuffer(gl::FRAMEBUFFER, gfx.fbo);
|
gfx.gl.bind_framebuffer(gl::READ_FRAMEBUFFER, gfx.read_fbo);
|
||||||
gfx.gl.framebuffer_texture_2d(
|
gfx.gl.framebuffer_texture_2d(
|
||||||
gl::FRAMEBUFFER,
|
gl::READ_FRAMEBUFFER,
|
||||||
gl::COLOR_ATTACHMENT0,
|
gl::COLOR_ATTACHMENT0,
|
||||||
gfx.device.surface_gl_texture_target(),
|
read_texture_target,
|
||||||
texture_id,
|
read_texture_id,
|
||||||
|
0,
|
||||||
|
);
|
||||||
|
gfx.gl.bind_framebuffer(gl::DRAW_FRAMEBUFFER, gfx.draw_fbo);
|
||||||
|
gfx.gl.framebuffer_texture_2d(
|
||||||
|
gl::DRAW_FRAMEBUFFER,
|
||||||
|
gl::COLOR_ATTACHMENT0,
|
||||||
|
draw_texture_target,
|
||||||
|
draw_texture_id,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
|
@ -640,15 +653,31 @@ impl BaseSrcImpl for ServoSrc {
|
||||||
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: use GL memory to avoid readback
|
gfx.gl.clear_color(0.3, 0.7, 0.3, 0.0);
|
||||||
gfx.gl.read_pixels_into_buffer(
|
gfx.gl.clear(gl::COLOR_BUFFER_BIT);
|
||||||
|
debug_assert_eq!(
|
||||||
|
(
|
||||||
|
gfx.gl.check_framebuffer_status(gl::FRAMEBUFFER),
|
||||||
|
gfx.gl.get_error()
|
||||||
|
),
|
||||||
|
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
||||||
|
);
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"Filling with {}/{} {}",
|
||||||
|
read_texture_id, read_texture_target, surface_size
|
||||||
|
);
|
||||||
|
gfx.gl.blit_framebuffer(
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
surface_size.width,
|
||||||
|
surface_size.height,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
gl::BGRA,
|
gl::COLOR_BUFFER_BIT,
|
||||||
gl::UNSIGNED_BYTE,
|
gl::NEAREST,
|
||||||
data,
|
|
||||||
);
|
);
|
||||||
debug_assert_eq!(
|
debug_assert_eq!(
|
||||||
(
|
(
|
||||||
|
@ -658,15 +687,28 @@ impl BaseSrcImpl for ServoSrc {
|
||||||
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
(gl::FRAMEBUFFER_COMPLETE, gl::NO_ERROR)
|
||||||
);
|
);
|
||||||
|
|
||||||
gfx.device.make_no_context_current().unwrap();
|
|
||||||
|
|
||||||
let surface = gfx
|
let surface = gfx
|
||||||
.device
|
.device
|
||||||
.destroy_surface_texture(&mut gfx.context, surface_texture)
|
.destroy_surface_texture(&mut gfx.context, surface_texture)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
self.swap_chain.recycle_surface(surface);
|
self.swap_chain.recycle_surface(surface);
|
||||||
|
} else {
|
||||||
|
debug!("Failed to get current surface");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore the GL state
|
||||||
|
gfx.gl
|
||||||
|
.bind_framebuffer(gl::DRAW_FRAMEBUFFER, bound_fbos[0] as GLuint);
|
||||||
|
gfx.gl
|
||||||
|
.bind_framebuffer(gl::READ_FRAMEBUFFER, bound_fbos[1] as GLuint);
|
||||||
|
debug_assert_eq!(gfx.gl.get_error(), gl::NO_ERROR);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
gl_context.activate(false).map_err(|_| {
|
||||||
|
gst_element_error!(src, CoreError::Failed, ["Failed to deactivate GL context"]);
|
||||||
|
FlowError::Error
|
||||||
|
})?;
|
||||||
|
|
||||||
let _ = self.sender.send(ServoSrcMsg::Heartbeat);
|
let _ = self.sender.send(ServoSrcMsg::Heartbeat);
|
||||||
Ok(FlowSuccess::Ok)
|
Ok(FlowSuccess::Ok)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue