mirror of
https://github.com/servo/servo.git
synced 2025-07-23 15:23:42 +01:00
Auto merge of #25246 - asajeffrey:gstplugin-framerates, r=Manishearth
Block the gstreamer plugin waiting for the next frame <!-- Please describe your changes on the following line: --> Get the GStreamer plugin to produce frames at the requested rate rather than relying on downstream elements to perform throttling. --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [x] `./mach build -d` does not report any errors - [x] `./mach test-tidy` does not report any errors - [x] These changes fix #24833 - [x] These changes do not require tests because <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->
This commit is contained in:
commit
c54d09afaf
2 changed files with 51 additions and 4 deletions
|
@ -24,7 +24,6 @@ To run locally:
|
||||||
```
|
```
|
||||||
GST_PLUGIN_PATH=target/gstplugins \
|
GST_PLUGIN_PATH=target/gstplugins \
|
||||||
gst-launch-1.0 servowebsrc \
|
gst-launch-1.0 servowebsrc \
|
||||||
! videorate \
|
|
||||||
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=1920,height=1080,format=RGBA \
|
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=1920,height=1080,format=RGBA \
|
||||||
! glimagesink rotate-method=vertical-flip
|
! glimagesink rotate-method=vertical-flip
|
||||||
```
|
```
|
||||||
|
@ -33,7 +32,6 @@ To stream over the network:
|
||||||
```
|
```
|
||||||
GST_PLUGIN_PATH=target/gstplugins \
|
GST_PLUGIN_PATH=target/gstplugins \
|
||||||
gst-launch-1.0 servowebsrc \
|
gst-launch-1.0 servowebsrc \
|
||||||
! videorate \
|
|
||||||
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=512,height=256 \
|
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=512,height=256 \
|
||||||
! glcolorconvert \
|
! glcolorconvert \
|
||||||
! gldownload \
|
! gldownload \
|
||||||
|
@ -47,7 +45,6 @@ To save to a file:
|
||||||
```
|
```
|
||||||
GST_PLUGIN_PATH=target/gstplugins \
|
GST_PLUGIN_PATH=target/gstplugins \
|
||||||
gst-launch-1.0 servowebsrc \
|
gst-launch-1.0 servowebsrc \
|
||||||
! videorate \
|
|
||||||
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=512,height=256 \
|
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=512,height=256 \
|
||||||
! glcolorconvert \
|
! glcolorconvert \
|
||||||
! gldownload \
|
! gldownload \
|
||||||
|
@ -126,7 +123,6 @@ GST_PLUGIN_SCANNER=$PWD/support/linux/gstreamer/gst/libexec/gstreamer-1.0/gst-pl
|
||||||
LD_LIBRARY_PATH=$PWD/support/linux/gstreamer/gst/lib \
|
LD_LIBRARY_PATH=$PWD/support/linux/gstreamer/gst/lib \
|
||||||
LD_PRELOAD=$PWD/target/gstplugins/libgstservoplugin.so \
|
LD_PRELOAD=$PWD/target/gstplugins/libgstservoplugin.so \
|
||||||
gst-launch-1.0 servowebsrc \
|
gst-launch-1.0 servowebsrc \
|
||||||
! queue \
|
|
||||||
! videoflip video-direction=vert \
|
! videoflip video-direction=vert \
|
||||||
! ximagesink
|
! ximagesink
|
||||||
```
|
```
|
||||||
|
|
|
@ -43,6 +43,7 @@ use gstreamer::Element;
|
||||||
use gstreamer::ErrorMessage;
|
use gstreamer::ErrorMessage;
|
||||||
use gstreamer::FlowError;
|
use gstreamer::FlowError;
|
||||||
use gstreamer::Format;
|
use gstreamer::Format;
|
||||||
|
use gstreamer::Fraction;
|
||||||
use gstreamer::LoggableError;
|
use gstreamer::LoggableError;
|
||||||
use gstreamer::PadDirection;
|
use gstreamer::PadDirection;
|
||||||
use gstreamer::PadPresence;
|
use gstreamer::PadPresence;
|
||||||
|
@ -89,8 +90,12 @@ use std::cell::RefCell;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use std::sync::atomic::AtomicU64;
|
||||||
|
use std::sync::atomic::Ordering;
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
use std::thread;
|
use std::thread;
|
||||||
|
use std::time::Duration;
|
||||||
|
use std::time::Instant;
|
||||||
|
|
||||||
pub struct ServoWebSrc {
|
pub struct ServoWebSrc {
|
||||||
sender: Sender<ServoWebSrcMsg>,
|
sender: Sender<ServoWebSrcMsg>,
|
||||||
|
@ -98,6 +103,14 @@ pub struct ServoWebSrc {
|
||||||
url: Mutex<Option<String>>,
|
url: Mutex<Option<String>>,
|
||||||
info: Mutex<Option<VideoInfo>>,
|
info: Mutex<Option<VideoInfo>>,
|
||||||
buffer_pool: Mutex<Option<BufferPool>>,
|
buffer_pool: Mutex<Option<BufferPool>>,
|
||||||
|
// When did the plugin get created?
|
||||||
|
start: Instant,
|
||||||
|
// How long should each frame last?
|
||||||
|
// TODO: make these AtomicU128s once that's stable
|
||||||
|
frame_duration_micros: AtomicU64,
|
||||||
|
// When should the next frame be displayed?
|
||||||
|
// (in microseconds, elapsed time since the start)
|
||||||
|
next_frame_micros: AtomicU64,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ServoWebSrcGfx {
|
struct ServoWebSrcGfx {
|
||||||
|
@ -131,6 +144,9 @@ enum ServoWebSrcMsg {
|
||||||
const DEFAULT_URL: &'static str =
|
const DEFAULT_URL: &'static str =
|
||||||
"https://rawcdn.githack.com/mrdoob/three.js/r105/examples/webgl_animation_cloth.html";
|
"https://rawcdn.githack.com/mrdoob/three.js/r105/examples/webgl_animation_cloth.html";
|
||||||
|
|
||||||
|
// Default framerate is 60fps
|
||||||
|
const DEFAULT_FRAME_DURATION: Duration = Duration::from_micros(16_667);
|
||||||
|
|
||||||
struct ServoThread {
|
struct ServoThread {
|
||||||
receiver: Receiver<ServoWebSrcMsg>,
|
receiver: Receiver<ServoWebSrcMsg>,
|
||||||
swap_chain: SwapChain,
|
swap_chain: SwapChain,
|
||||||
|
@ -437,12 +453,18 @@ impl ObjectSubclass for ServoWebSrc {
|
||||||
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);
|
let buffer_pool = Mutex::new(None);
|
||||||
|
let start = Instant::now();
|
||||||
|
let frame_duration_micros = AtomicU64::new(DEFAULT_FRAME_DURATION.as_micros() as u64);
|
||||||
|
let next_frame_micros = AtomicU64::new(0);
|
||||||
Self {
|
Self {
|
||||||
sender,
|
sender,
|
||||||
swap_chain,
|
swap_chain,
|
||||||
info,
|
info,
|
||||||
url,
|
url,
|
||||||
buffer_pool,
|
buffer_pool,
|
||||||
|
start,
|
||||||
|
frame_duration_micros,
|
||||||
|
next_frame_micros,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,6 +533,18 @@ impl BaseSrcImpl for ServoWebSrc {
|
||||||
.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);
|
||||||
|
|
||||||
|
// Save the framerate if it is set
|
||||||
|
let framerate = outcaps
|
||||||
|
.get_structure(0)
|
||||||
|
.and_then(|cap| cap.get::<Fraction>("framerate"));
|
||||||
|
if let Some(framerate) = framerate {
|
||||||
|
let frame_duration_micros =
|
||||||
|
1_000_000 * *framerate.denom() as u64 / *framerate.numer() as u64;
|
||||||
|
debug!("Setting frame duration to {}micros", frame_duration_micros);
|
||||||
|
self.frame_duration_micros
|
||||||
|
.store(frame_duration_micros, Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
// Get the downstream GL context
|
// Get the downstream GL context
|
||||||
let mut gst_gl_context = std::ptr::null_mut();
|
let mut gst_gl_context = std::ptr::null_mut();
|
||||||
let el = src.upcast_ref::<Element>();
|
let el = src.upcast_ref::<Element>();
|
||||||
|
@ -577,6 +611,23 @@ impl BaseSrcImpl for ServoWebSrc {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn create(&self, src: &BaseSrc, _offset: u64, _length: u32) -> Result<Buffer, FlowError> {
|
fn create(&self, src: &BaseSrc, _offset: u64, _length: u32) -> Result<Buffer, FlowError> {
|
||||||
|
// We block waiting for the next frame to be needed.
|
||||||
|
// TODO: Once get_times is in BaseSrcImpl, we can use that instead.
|
||||||
|
// It's been merged but not yet published.
|
||||||
|
// https://github.com/servo/servo/issues/25234
|
||||||
|
let elapsed_micros = self.start.elapsed().as_micros() as u64;
|
||||||
|
let frame_duration_micros = self.frame_duration_micros.load(Ordering::SeqCst);
|
||||||
|
let next_frame_micros = self
|
||||||
|
.next_frame_micros
|
||||||
|
.fetch_add(frame_duration_micros, Ordering::SeqCst);
|
||||||
|
if elapsed_micros < next_frame_micros {
|
||||||
|
// Delay by at most a second
|
||||||
|
let delay = 1_000_000.min(next_frame_micros - elapsed_micros);
|
||||||
|
debug!("Waiting for {}micros", delay);
|
||||||
|
thread::sleep(Duration::from_micros(delay));
|
||||||
|
debug!("Done waiting");
|
||||||
|
}
|
||||||
|
|
||||||
// Get the buffer pool
|
// Get the buffer pool
|
||||||
let pool_guard = self.buffer_pool.lock().unwrap();
|
let pool_guard = self.buffer_pool.lock().unwrap();
|
||||||
let pool = pool_guard.as_ref().ok_or(FlowError::NotNegotiated)?;
|
let pool = pool_guard.as_ref().ok_or(FlowError::NotNegotiated)?;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue