Auto merge of #27456 - asajeffrey:gst-plugin-hubs, r=Manishearth

Get the gstreamer plugin to stream Hubs rooms

<!-- Please describe your changes on the following line: -->

This is thje changes needed to the gstreamer plugin to stream a Hubs room:
- Add the ability to specify servo prefs as a gstreamer plugin property
- Fix a deadlock caused by using  bounded channel, which Hubs hits because it takes ~30s to enter webxr.
Also add a recipe for streaming Hubs.

---
<!-- 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 do not require tests because we don't test the gstreamer plugin

<!-- 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:
bors-servo 2020-07-30 16:46:51 -04:00 committed by GitHub
commit a360b873be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 60 additions and 6 deletions

View file

@ -91,6 +91,23 @@ GST_PLUGIN_PATH=target/gstplugins \
This requires the webxr content to support the `sessionavailable` event for launching directly into immersive mode. This requires the webxr content to support the `sessionavailable` event for launching directly into immersive mode.
Values for `webxr` include `none`, `left-right`, `red-cyan`, `cubemap` and `spherical`. Values for `webxr` include `none`, `left-right`, `red-cyan`, `cubemap` and `spherical`.
To stream a Hubs room and save to a file (there'll be ~30s black at the beginning while Hubs starts up):
```
GST_PLUGIN_PATH=$PWD/target/gstplugins \
gst-launch-1.0 -e servowebsrc \
url="https://hubs.mozilla.com/$ROOM?no_force_webvr&vr_entry_type=vr_now" \
webxr=red-cyan \
prefs='{"dom.gamepad.enabled":true, "dom.svg.enabled":true, "dom.canvas_capture.enabled": true, "dom.canvas_capture.enabled":true, "dom.webrtc.enabled":true, "dom.webrtc.transceiver.enabled":true}' \
! video/x-raw\(memory:GLMemory\),framerate=50/1,width=1920,height=1080,format=RGBA \
! glvideoflip video-direction=vert \
! glcolorconvert \
! gldownload \
! queue \
! x264enc \
! mp4mux \
! filesink location=test.mp4
```
*Note*: killing the gstreamer pipeline with control-C sometimes locks up macOS to the point *Note*: killing the gstreamer pipeline with control-C sometimes locks up macOS to the point
of needing a power cycle. Killing the pipeline by closing the window seems to work. of needing a power cycle. Killing the pipeline by closing the window seems to work.

View file

@ -75,6 +75,9 @@ use servo::compositing::windowing::WindowMethods;
use servo::embedder_traits::EmbedderProxy; use servo::embedder_traits::EmbedderProxy;
use servo::embedder_traits::EventLoopWaker; use servo::embedder_traits::EventLoopWaker;
use servo::msg::constellation_msg::TopLevelBrowsingContextId; use servo::msg::constellation_msg::TopLevelBrowsingContextId;
use servo::servo_config::prefs::add_user_prefs;
use servo::servo_config::prefs::read_prefs_map;
use servo::servo_config::prefs::PrefValue;
use servo::servo_config::set_pref; use servo::servo_config::set_pref;
use servo::servo_url::ServoUrl; use servo::servo_url::ServoUrl;
use servo::webrender_api::units::DevicePixel; use servo::webrender_api::units::DevicePixel;
@ -115,6 +118,7 @@ pub struct ServoWebSrc {
sender: Sender<ServoWebSrcMsg>, sender: Sender<ServoWebSrcMsg>,
url: Mutex<Option<String>>, url: Mutex<Option<String>>,
webxr_mode: Mutex<Option<WebXRMode>>, webxr_mode: Mutex<Option<WebXRMode>>,
prefs: Mutex<Option<String>>,
outcaps: Mutex<Option<Caps>>, outcaps: Mutex<Option<Caps>>,
info: Mutex<Option<VideoInfo>>, info: Mutex<Option<VideoInfo>>,
buffer_pool: Mutex<Option<BufferPool>>, buffer_pool: Mutex<Option<BufferPool>>,
@ -172,6 +176,7 @@ enum ServoWebSrcMsg {
ConnectionWhichImplementsDebug, ConnectionWhichImplementsDebug,
ServoUrl, ServoUrl,
Option<WebXRMode>, Option<WebXRMode>,
HashMap<String, PrefValue>,
Size2D<i32, DevicePixel>, Size2D<i32, DevicePixel>,
), ),
GetSwapChain(Sender<SwapChain<Device>>), GetSwapChain(Sender<SwapChain<Device>>),
@ -195,13 +200,16 @@ struct ServoThread {
impl ServoThread { impl ServoThread {
fn new(sender: Sender<ServoWebSrcMsg>, receiver: Receiver<ServoWebSrcMsg>) -> Self { fn new(sender: Sender<ServoWebSrcMsg>, receiver: Receiver<ServoWebSrcMsg>) -> Self {
let (connection, url, webxr_mode, size) = match receiver.recv() { let (connection, url, webxr_mode, prefs, size) = match receiver.recv() {
Ok(ServoWebSrcMsg::Start(connection, url, webxr_mode, size)) => { Ok(ServoWebSrcMsg::Start(connection, url, webxr_mode, prefs, size)) => {
(connection.0, url, webxr_mode, size) (connection.0, url, webxr_mode, prefs, size)
}, },
e => panic!("Failed to start ({:?})", e), e => panic!("Failed to start ({:?})", e),
}; };
info!("Created new servo thread for {} ({:?})", url, webxr_mode); info!(
"Created new servo thread for {} ({:?}, {:?})",
url, webxr_mode, prefs
);
let window = Rc::new(ServoWebSrcWindow::new(connection, webxr_mode, sender, size)); let window = Rc::new(ServoWebSrcWindow::new(connection, webxr_mode, sender, size));
let embedder = Box::new(ServoWebSrcEmbedder::new(&window)); let embedder = Box::new(ServoWebSrcEmbedder::new(&window));
let webrender_swap_chain = window let webrender_swap_chain = window
@ -224,6 +232,8 @@ impl ServoThread {
}, },
}; };
add_user_prefs(prefs);
Self { Self {
receiver, receiver,
servo, servo,
@ -443,7 +453,16 @@ impl WebXRWindow for ServoWebSrcWebXR {
} }
} }
static PROPERTIES: [Property; 2] = [ static PROPERTIES: [Property; 3] = [
Property("prefs", |name| {
ParamSpec::string(
name,
"prefs",
"Servo preferences",
None,
glib::ParamFlags::READWRITE,
)
}),
Property("url", |name| { Property("url", |name| {
ParamSpec::string( ParamSpec::string(
name, name,
@ -485,6 +504,7 @@ impl ObjectSubclass for ServoWebSrc {
let info = Mutex::new(None); let info = Mutex::new(None);
let outcaps = Mutex::new(None); let outcaps = Mutex::new(None);
let url = Mutex::new(None); let url = Mutex::new(None);
let prefs = Mutex::new(None);
let webxr_mode = Mutex::new(None); let webxr_mode = Mutex::new(None);
let buffer_pool = Mutex::new(None); let buffer_pool = Mutex::new(None);
let gl_context = Mutex::new(None); let gl_context = Mutex::new(None);
@ -497,6 +517,7 @@ impl ObjectSubclass for ServoWebSrc {
info, info,
outcaps, outcaps,
url, url,
prefs,
webxr_mode, webxr_mode,
buffer_pool, buffer_pool,
gl_context, gl_context,
@ -539,6 +560,11 @@ impl ObjectImpl for ServoWebSrc {
fn set_property(&self, _obj: &Object, id: usize, value: &Value) { fn set_property(&self, _obj: &Object, id: usize, value: &Value) {
let prop = &PROPERTIES[id]; let prop = &PROPERTIES[id];
match *prop { match *prop {
Property("prefs", ..) => {
let mut guard = self.prefs.lock().expect("Failed to lock mutex");
let prefs = value.get().expect("Failed to get prefs value");
*guard = prefs;
},
Property("url", ..) => { Property("url", ..) => {
let mut guard = self.url.lock().expect("Failed to lock mutex"); let mut guard = self.url.lock().expect("Failed to lock mutex");
let url = value.get().expect("Failed to get url value"); let url = value.get().expect("Failed to get url value");
@ -564,6 +590,10 @@ impl ObjectImpl for ServoWebSrc {
fn get_property(&self, _obj: &Object, id: usize) -> Result<Value, ()> { fn get_property(&self, _obj: &Object, id: usize) -> Result<Value, ()> {
let prop = &PROPERTIES[id]; let prop = &PROPERTIES[id];
match *prop { match *prop {
Property("prefs", ..) => {
let guard = self.url.lock().expect("Failed to lock mutex");
Ok(Value::from(guard.as_ref()))
},
Property("url", ..) => { Property("url", ..) => {
let guard = self.url.lock().expect("Failed to lock mutex"); let guard = self.url.lock().expect("Failed to lock mutex");
Ok(Value::from(guard.as_ref())) Ok(Value::from(guard.as_ref()))
@ -761,6 +791,12 @@ impl ServoWebSrc {
error!("Failed to parse url {} ({:?})", url_string, e); error!("Failed to parse url {} ({:?})", url_string, e);
FlowError::Error FlowError::Error
})?; })?;
let prefs_guard = self.prefs.lock().expect("Poisoned mutex");
let prefs_string = prefs_guard.as_ref().map(|s| &**s).unwrap_or("{}");
let prefs = read_prefs_map(prefs_string).map_err(|e| {
error!("Failed to parse prefs {} ({:?})", prefs_string, e);
FlowError::Error
})?;
let size = self let size = self
.info .info
.lock() .lock()
@ -773,6 +809,7 @@ impl ServoWebSrc {
ConnectionWhichImplementsDebug(connection), ConnectionWhichImplementsDebug(connection),
url, url,
webxr_mode, webxr_mode,
prefs,
size, size,
)); ));
@ -925,7 +962,7 @@ impl ServoWebSrc {
if gfx.swap_chain.is_none() { if gfx.swap_chain.is_none() {
debug!("Getting the swap chain"); debug!("Getting the swap chain");
let (acks, ackr) = crossbeam_channel::bounded(1); let (acks, ackr) = crossbeam_channel::unbounded();
let _ = self.sender.send(ServoWebSrcMsg::GetSwapChain(acks)); let _ = self.sender.send(ServoWebSrcMsg::GetSwapChain(acks));
gfx.swap_chain = ackr.recv_timeout(Duration::from_millis(16)).ok(); gfx.swap_chain = ackr.recv_timeout(Duration::from_millis(16)).ok();
} }