diff --git a/ports/gstplugin/README.md b/ports/gstplugin/README.md index 7c8b08b71f20..ca9d30ed87de 100644 --- a/ports/gstplugin/README.md +++ b/ports/gstplugin/README.md @@ -24,7 +24,6 @@ To run locally: ``` GST_PLUGIN_PATH=target/gstplugins \ gst-launch-1.0 servowebsrc \ - ! videorate \ ! video/x-raw\(memory:GLMemory\),framerate=50/1,width=1920,height=1080,format=RGBA \ ! glimagesink rotate-method=vertical-flip ``` @@ -33,7 +32,6 @@ To stream over the network: ``` GST_PLUGIN_PATH=target/gstplugins \ gst-launch-1.0 servowebsrc \ - ! videorate \ ! video/x-raw\(memory:GLMemory\),framerate=50/1,width=512,height=256 \ ! glcolorconvert \ ! gldownload \ @@ -47,7 +45,6 @@ To save to a file: ``` GST_PLUGIN_PATH=target/gstplugins \ gst-launch-1.0 servowebsrc \ - ! videorate \ ! video/x-raw\(memory:GLMemory\),framerate=50/1,width=512,height=256 \ ! glcolorconvert \ ! 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_PRELOAD=$PWD/target/gstplugins/libgstservoplugin.so \ gst-launch-1.0 servowebsrc \ - ! queue \ ! videoflip video-direction=vert \ ! ximagesink ``` diff --git a/ports/gstplugin/servowebsrc.rs b/ports/gstplugin/servowebsrc.rs index 0a19f4c4b87a..2b7b30d389a4 100644 --- a/ports/gstplugin/servowebsrc.rs +++ b/ports/gstplugin/servowebsrc.rs @@ -43,6 +43,7 @@ use gstreamer::Element; use gstreamer::ErrorMessage; use gstreamer::FlowError; use gstreamer::Format; +use gstreamer::Fraction; use gstreamer::LoggableError; use gstreamer::PadDirection; use gstreamer::PadPresence; @@ -89,8 +90,12 @@ use std::cell::RefCell; use std::collections::HashMap; use std::convert::TryFrom; use std::rc::Rc; +use std::sync::atomic::AtomicU64; +use std::sync::atomic::Ordering; use std::sync::Mutex; use std::thread; +use std::time::Duration; +use std::time::Instant; pub struct ServoWebSrc { sender: Sender, @@ -98,6 +103,14 @@ pub struct ServoWebSrc { url: Mutex>, info: Mutex>, buffer_pool: Mutex>, + // 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 { @@ -131,6 +144,9 @@ enum ServoWebSrcMsg { const DEFAULT_URL: &'static str = "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 { receiver: Receiver, swap_chain: SwapChain, @@ -437,12 +453,18 @@ impl ObjectSubclass for ServoWebSrc { let info = Mutex::new(None); let url = 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 { sender, swap_chain, info, url, 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"))?; *self.info.lock().unwrap() = Some(info); + // Save the framerate if it is set + let framerate = outcaps + .get_structure(0) + .and_then(|cap| cap.get::("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 let mut gst_gl_context = std::ptr::null_mut(); let el = src.upcast_ref::(); @@ -577,6 +611,23 @@ impl BaseSrcImpl for ServoWebSrc { } fn create(&self, src: &BaseSrc, _offset: u64, _length: u32) -> Result { + // 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 let pool_guard = self.buffer_pool.lock().unwrap(); let pool = pool_guard.as_ref().ok_or(FlowError::NotNegotiated)?;