mirror of
https://github.com/servo/servo.git
synced 2025-08-04 05:00:08 +01:00
Get the servosrc gstreamer plugin to use a GL buffer pool
This commit is contained in:
parent
2d2cd2b7d7
commit
bd1e4f87b5
5 changed files with 80 additions and 21 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -4789,6 +4789,7 @@ dependencies = [
|
||||||
"gstreamer-base",
|
"gstreamer-base",
|
||||||
"gstreamer-gl",
|
"gstreamer-gl",
|
||||||
"gstreamer-gl-sys",
|
"gstreamer-gl-sys",
|
||||||
|
"gstreamer-sys",
|
||||||
"gstreamer-video",
|
"gstreamer-video",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libservo",
|
"libservo",
|
||||||
|
|
|
@ -23,6 +23,7 @@ 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" }
|
gstreamer-gl = { version = "0.14" }
|
||||||
gstreamer-gl-sys = { version = "0.8" }
|
gstreamer-gl-sys = { version = "0.8" }
|
||||||
|
gstreamer-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"
|
||||||
|
|
|
@ -29,15 +29,14 @@ GST_PLUGIN_PATH=target/gstplugins \
|
||||||
! glimagesink rotate-method=vertical-flip
|
! glimagesink rotate-method=vertical-flip
|
||||||
```
|
```
|
||||||
|
|
||||||
*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 \
|
||||||
! videorate \
|
! videorate \
|
||||||
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=1920,height=1080,format=RGBA \
|
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=512,height=256 \
|
||||||
|
! glcolorconvert \
|
||||||
! gldownload \
|
! gldownload \
|
||||||
! videoconvert \
|
|
||||||
! videoflip video-direction=vert \
|
! videoflip video-direction=vert \
|
||||||
! theoraenc \
|
! theoraenc \
|
||||||
! oggmux \
|
! oggmux \
|
||||||
|
@ -47,10 +46,11 @@ To stream over the network:
|
||||||
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 \
|
||||||
! videorate \
|
! videorate \
|
||||||
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=1920,height=1080,format=RGBA \
|
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=512,height=256 \
|
||||||
|
! glcolorconvert \
|
||||||
! gldownload \
|
! gldownload \
|
||||||
! videoconvert \
|
|
||||||
! videoflip video-direction=vert \
|
! videoflip video-direction=vert \
|
||||||
! theoraenc \
|
! theoraenc \
|
||||||
! oggmux \
|
! oggmux \
|
||||||
|
|
|
@ -17,6 +17,7 @@ use glib::glib_object_impl;
|
||||||
use glib::glib_object_subclass;
|
use glib::glib_object_subclass;
|
||||||
use glib::object::Cast;
|
use glib::object::Cast;
|
||||||
use glib::object::Object;
|
use glib::object::Object;
|
||||||
|
use glib::object::ObjectType;
|
||||||
use glib::subclass::object::ObjectClassSubclassExt;
|
use glib::subclass::object::ObjectClassSubclassExt;
|
||||||
use glib::subclass::object::ObjectImpl;
|
use glib::subclass::object::ObjectImpl;
|
||||||
use glib::subclass::object::ObjectImplExt;
|
use glib::subclass::object::ObjectImplExt;
|
||||||
|
@ -32,12 +33,15 @@ use gstreamer::gst_loggable_error;
|
||||||
use gstreamer::subclass::element::ElementClassSubclassExt;
|
use gstreamer::subclass::element::ElementClassSubclassExt;
|
||||||
use gstreamer::subclass::element::ElementImpl;
|
use gstreamer::subclass::element::ElementImpl;
|
||||||
use gstreamer::subclass::ElementInstanceStruct;
|
use gstreamer::subclass::ElementInstanceStruct;
|
||||||
use gstreamer::BufferRef;
|
use gstreamer::Buffer;
|
||||||
|
use gstreamer::BufferPool;
|
||||||
|
use gstreamer::BufferPoolExt;
|
||||||
|
use gstreamer::BufferPoolExtManual;
|
||||||
use gstreamer::Caps;
|
use gstreamer::Caps;
|
||||||
use gstreamer::CoreError;
|
use gstreamer::CoreError;
|
||||||
|
use gstreamer::Element;
|
||||||
use gstreamer::ErrorMessage;
|
use gstreamer::ErrorMessage;
|
||||||
use gstreamer::FlowError;
|
use gstreamer::FlowError;
|
||||||
use gstreamer::FlowSuccess;
|
|
||||||
use gstreamer::Format;
|
use gstreamer::Format;
|
||||||
use gstreamer::LoggableError;
|
use gstreamer::LoggableError;
|
||||||
use gstreamer::PadDirection;
|
use gstreamer::PadDirection;
|
||||||
|
@ -93,6 +97,7 @@ pub struct ServoSrc {
|
||||||
swap_chain: SwapChain,
|
swap_chain: SwapChain,
|
||||||
url: Mutex<Option<String>>,
|
url: Mutex<Option<String>>,
|
||||||
info: Mutex<Option<VideoInfo>>,
|
info: Mutex<Option<VideoInfo>>,
|
||||||
|
buffer_pool: Mutex<Option<BufferPool>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ServoSrcGfx {
|
struct ServoSrcGfx {
|
||||||
|
@ -431,11 +436,13 @@ impl ObjectSubclass for ServoSrc {
|
||||||
let swap_chain = ackr.recv().expect("Failed to get swap chain");
|
let swap_chain = ackr.recv().expect("Failed to get swap chain");
|
||||||
let info = Mutex::new(None);
|
let info = Mutex::new(None);
|
||||||
let url = Mutex::new(None);
|
let url = Mutex::new(None);
|
||||||
|
let buffer_pool = Mutex::new(None);
|
||||||
Self {
|
Self {
|
||||||
sender,
|
sender,
|
||||||
swap_chain,
|
swap_chain,
|
||||||
info,
|
info,
|
||||||
url,
|
url,
|
||||||
|
buffer_pool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -498,10 +505,47 @@ thread_local! {
|
||||||
static GL: RefCell<Option<Rc<Gl>>> = RefCell::new(None);
|
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> {
|
||||||
|
// Save the video info for later use
|
||||||
let info = VideoInfo::from_caps(outcaps)
|
let info = VideoInfo::from_caps(outcaps)
|
||||||
.ok_or_else(|| gst_loggable_error!(CATEGORY, "Failed to get video info"))?;
|
.ok_or_else(|| gst_loggable_error!(CATEGORY, "Failed to get video info"))?;
|
||||||
*self.info.lock().unwrap() = Some(info);
|
*self.info.lock().unwrap() = Some(info);
|
||||||
|
|
||||||
|
// Get the downstream GL context
|
||||||
|
let mut gst_gl_context = std::ptr::null_mut();
|
||||||
|
let el = src.upcast_ref::<Element>();
|
||||||
|
unsafe {
|
||||||
|
gstreamer_gl_sys::gst_gl_query_local_gl_context(
|
||||||
|
el.as_ptr(),
|
||||||
|
gstreamer_sys::GST_PAD_SRC,
|
||||||
|
&mut gst_gl_context,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if gst_gl_context.is_null() {
|
||||||
|
return Err(gst_loggable_error!(CATEGORY, "Failed to get GL context"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new buffer pool for GL memory
|
||||||
|
let gst_gl_buffer_pool =
|
||||||
|
unsafe { gstreamer_gl_sys::gst_gl_buffer_pool_new(gst_gl_context) };
|
||||||
|
if gst_gl_buffer_pool.is_null() {
|
||||||
|
return Err(gst_loggable_error!(
|
||||||
|
CATEGORY,
|
||||||
|
"Failed to create buffer pool"
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let pool = unsafe { BufferPool::from_glib_borrow(gst_gl_buffer_pool) };
|
||||||
|
|
||||||
|
// Configure the buffer pool with the negotiated caps
|
||||||
|
let mut config = pool.get_config();
|
||||||
|
let (_, size, min_buffers, max_buffers) = config.get_params().unwrap_or((None, 0, 0, 1024));
|
||||||
|
config.set_params(Some(outcaps), size, min_buffers, max_buffers);
|
||||||
|
pool.set_config(config)
|
||||||
|
.map_err(|_| gst_loggable_error!(CATEGORY, "Failed to update config"))?;
|
||||||
|
|
||||||
|
// Save the buffer pool for later use
|
||||||
|
*self.buffer_pool.lock().expect("Poisoned lock") = Some(pool);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,6 +553,10 @@ impl BaseSrcImpl for ServoSrc {
|
||||||
u64::try_from(self.info.lock().ok()?.as_ref()?.size()).ok()
|
u64::try_from(self.info.lock().ok()?.as_ref()?.size()).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_seekable(&self, _: &BaseSrc) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
fn start(&self, _src: &BaseSrc) -> Result<(), ErrorMessage> {
|
fn start(&self, _src: &BaseSrc) -> Result<(), ErrorMessage> {
|
||||||
info!("Starting");
|
info!("Starting");
|
||||||
let guard = self
|
let guard = self
|
||||||
|
@ -528,13 +576,20 @@ impl BaseSrcImpl for ServoSrc {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fill(
|
fn create(&self, src: &BaseSrc, _offset: u64, _length: u32) -> Result<Buffer, FlowError> {
|
||||||
&self,
|
// Get the buffer pool
|
||||||
src: &BaseSrc,
|
let pool_guard = self.buffer_pool.lock().unwrap();
|
||||||
_offset: u64,
|
let pool = pool_guard.as_ref().ok_or(FlowError::NotNegotiated)?;
|
||||||
_length: u32,
|
|
||||||
buffer: &mut BufferRef,
|
// Activate the pool if necessary
|
||||||
) -> Result<FlowSuccess, FlowError> {
|
if !pool.is_active() {
|
||||||
|
pool.set_active(true).map_err(|_| FlowError::Error)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get a buffer to fill
|
||||||
|
let buffer = pool.acquire_buffer(None)?;
|
||||||
|
|
||||||
|
// Get the GL memory from the buffer
|
||||||
let memory = buffer.get_all_memory().ok_or_else(|| {
|
let memory = buffer.get_all_memory().ok_or_else(|| {
|
||||||
gst_element_error!(src, CoreError::Failed, ["Failed to get memory"]);
|
gst_element_error!(src, CoreError::Failed, ["Failed to get memory"]);
|
||||||
FlowError::Error
|
FlowError::Error
|
||||||
|
@ -549,6 +604,7 @@ impl BaseSrcImpl for ServoSrc {
|
||||||
FlowError::Error
|
FlowError::Error
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
|
// Get the data out of the memory
|
||||||
let gl_context = unsafe { GLContext::from_glib_borrow(gl_memory.mem.context) };
|
let gl_context = unsafe { GLContext::from_glib_borrow(gl_memory.mem.context) };
|
||||||
let draw_texture_id = gl_memory.tex_id;
|
let draw_texture_id = gl_memory.tex_id;
|
||||||
let draw_texture_target = unsafe { gst_gl_texture_target_to_gl(gl_memory.tex_target) };
|
let draw_texture_target = unsafe { gst_gl_texture_target_to_gl(gl_memory.tex_target) };
|
||||||
|
@ -710,6 +766,6 @@ impl BaseSrcImpl for ServoSrc {
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
let _ = self.sender.send(ServoSrcMsg::Heartbeat);
|
let _ = self.sender.send(ServoSrcMsg::Heartbeat);
|
||||||
Ok(FlowSuccess::Ok)
|
Ok(buffer)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,11 @@
|
||||||
<p>Start the video stream with:</p>
|
<p>Start the video stream with:</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
gst-launch-1.0 servosrc \
|
gst-launch-1.0 servosrc url=https://mrdoob.neocities.org/018/ \
|
||||||
! queue \
|
! videorate \
|
||||||
! video/x-raw,framerate=25/1,width=512,height=512 \
|
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=512,height=256 \
|
||||||
! videoconvert \
|
! glcolorconvert \
|
||||||
|
! gldownload \
|
||||||
! videoflip video-direction=vert \
|
! videoflip video-direction=vert \
|
||||||
! theoraenc \
|
! theoraenc \
|
||||||
! oggmux \
|
! oggmux \
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue