Skip to content

Commit

Permalink
Block the gstreamer plugin waiting for the next frame
Browse files Browse the repository at this point in the history
  • Loading branch information
Alan Jeffrey committed Dec 12, 2019
1 parent 904c23f commit 24678da
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 4 deletions.
4 changes: 0 additions & 4 deletions ports/gstplugin/README.md
Expand Up @@ -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
```
Expand All @@ -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 \
Expand All @@ -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 \
Expand Down Expand Up @@ -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
```
51 changes: 51 additions & 0 deletions ports/gstplugin/servowebsrc.rs
Expand Up @@ -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;
Expand Down Expand Up @@ -89,15 +90,27 @@ 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<ServoWebSrcMsg>,
swap_chain: SwapChain,
url: Mutex<Option<String>>,
info: Mutex<Option<VideoInfo>>,
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 {
Expand Down Expand Up @@ -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<ServoWebSrcMsg>,
swap_chain: SwapChain,
Expand Down Expand Up @@ -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,
}
}

Expand Down Expand Up @@ -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::<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
let mut gst_gl_context = std::ptr::null_mut();
let el = src.upcast_ref::<Element>();
Expand Down Expand Up @@ -577,6 +611,23 @@ impl BaseSrcImpl for ServoWebSrc {
}

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
let pool_guard = self.buffer_pool.lock().unwrap();
let pool = pool_guard.as_ref().ok_or(FlowError::NotNegotiated)?;
Expand Down

0 comments on commit 24678da

Please sign in to comment.