From d3d05a43fb1252e6e600578613da0244cb4e0176 Mon Sep 17 00:00:00 2001 From: Vladimir Borisov Date: Fri, 16 Jul 2021 17:33:58 +0300 Subject: [PATCH] Progress on the IPC --- Cargo.toml | 2 +- src/stremio_app/stremio_app.rs | 108 ++++++++++++++-- .../stremio_player/stremio_player.rs | 115 ++++++++++-------- .../stremio_wevbiew/stremio_wevbiew.rs | 96 +++++++-------- 4 files changed, 211 insertions(+), 110 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 68e8d7c..1ec9197 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,7 +5,7 @@ edition = "2018" [dependencies] once_cell = "1.3.1" -native-windows-gui = { version = "1.0.4", features = ["high-dpi"] } +native-windows-gui = { version = "1.0.4", features = ["high-dpi", "notice"] } native-windows-derive = "1.0.3" winapi = { version = "0.3.9", features = [ "libloaderapi", "handleapi", diff --git a/src/stremio_app/stremio_app.rs b/src/stremio_app/stremio_app.rs index 3448e6c..ec07abc 100644 --- a/src/stremio_app/stremio_app.rs +++ b/src/stremio_app/stremio_app.rs @@ -1,10 +1,43 @@ use native_windows_derive::NwgUi; use native_windows_gui as nwg; +use serde::{Deserialize, Serialize}; +use serde_json; use std::cmp; +use std::sync::Arc; +use std::thread; use crate::stremio_app::stremio_player::Player; use crate::stremio_app::stremio_wevbiew::WebView; +////////////////////////////////////////// +#[derive(Serialize, Deserialize, Debug, Clone)] +struct RPCRequest { + id: u64, + args: Option>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct RPCResponseDataTransport { + properties: Vec>, + signals: Vec, + methods: Vec>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct RPCResponseData { + transport: RPCResponseDataTransport, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +struct RPCResponse { + id: u64, + object: String, + #[serde(rename = "type")] + response_type: u32, + data: RPCResponseData, +} +////////////////////////////////////////// + #[derive(Default, NwgUi)] pub struct MainWindow { #[nwg_control(title: "Stremio", flags: "MAIN_WINDOW|VISIBLE")] @@ -26,13 +59,74 @@ impl MainWindow { self.window .set_size(dimensions.0 as u32, dimensions.1 as u32); self.window.set_position(x, y); - // let video_path = "/home/ivo/storage/bbb_sunflower_1080p_30fps_normal.mp4"; - let video_path = "http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4"; - self.player.command(&["loadfile", video_path]); - // self.player.set_prop("time-pos", 120.0); - self.player.set_prop("speed", 2.0); - // self.player.set_prop("pause", true); - self.player.command(&["stop"]); + + let player_channel = self.player.channel.borrow(); + let (player_tx, player_rx) = player_channel.as_ref().unwrap(); + let player_tx = player_tx.clone(); + let player_rx = Arc::clone(player_rx); + + let web_channel = self.webview.channel.borrow(); + let (web_tx, web_rx) = web_channel.as_ref().unwrap(); + let web_tx = web_tx.clone(); + let web_rx = Arc::clone(web_rx); + thread::spawn(move || { + loop { + // Read message from player + { + let rx = player_rx.lock().unwrap(); + if let Ok(msg) = rx.try_recv() { + println!("APP GOT FROM PLAYER {}", msg); + // web_tx.send(msg).ok(); + } + }; + + // read message from WebView + { + let rx = web_rx.lock().unwrap(); + if let Ok(msg) = rx.try_recv() { + let msg: RPCRequest = serde_json::from_str(&msg).unwrap(); + if msg.id == 0 { + let resp: RPCResponse = RPCResponse { + id: 0, + object: "transport".to_string(), + response_type: 3, + data: RPCResponseData { + transport: RPCResponseDataTransport { + properties: vec![ + vec![], + vec![ + "".to_string(), + "shellVersion".to_string(), + "".to_string(), + "5.0.0".to_string(), + ], + ], + signals: vec![], + methods: vec![vec!["onEvent".to_string(), "".to_string()]], + }, + }, + }; + let resp_json = serde_json::to_string(&resp).unwrap(); + web_tx.send(resp_json).ok(); + } else if let Some(args) = msg.args { + // TODO: this can panic + let method = serde_json::from_value::(args[0].clone()).unwrap(); + if method.starts_with("mpv-") { + let resp_json = serde_json::to_string(&args).unwrap(); + player_tx.send(resp_json).ok(); + } + } + } + }; + } + }); + // // let video_path = "/home/ivo/storage/bbb_sunflower_1080p_30fps_normal.mp4"; + // let video_path = "http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4"; + // self.player.command(&["loadfile", video_path]); + // // self.player.set_prop("time-pos", 120.0); + // self.player.set_prop("speed", 2.0); + // // self.player.set_prop("pause", true); + // self.player.command(&["stop"]); } fn on_quit(&self) { nwg::stop_thread_dispatch(); diff --git a/src/stremio_app/stremio_player/stremio_player.rs b/src/stremio_app/stremio_player/stremio_player.rs index 4748cf8..a8111b1 100644 --- a/src/stremio_app/stremio_player/stremio_player.rs +++ b/src/stremio_app/stremio_player/stremio_player.rs @@ -1,26 +1,12 @@ use native_windows_gui::{self as nwg, PartialUi}; use std::cell::RefCell; +use std::sync::mpsc; +use std::sync::{Arc, Mutex}; +use std::thread; #[derive(Default)] pub struct Player { - mpv: RefCell>, -} - -impl Player { - pub fn command(&self, args: &[&str]) { - let mut mpv = self.mpv.borrow_mut(); - let mpv = mpv.as_mut().expect("Failed to create MPV"); - if let Err(e) = mpv.command(args) { - eprintln!("Failed to execute command {:?} - {:?}", args, e); - } - } - pub fn set_prop(&self, prop: &str, val: T) { - let mut mpv = self.mpv.borrow_mut(); - let mpv = mpv.as_mut().expect("Failed to create MPV"); - if let Err(e) = mpv.set_property(prop, val) { - eprintln!("Failed to set property {} - {:?}", prop, e); - } - } + pub channel: RefCell, Arc>>)>>, } impl PartialUi for Player { @@ -28,39 +14,66 @@ impl PartialUi for Player { data: &mut Self, parent: Option, ) -> Result<(), nwg::NwgError> { - let mut mpv_builder = - mpv::MpvHandlerBuilder::new().expect("Error while creating MPV builder"); - mpv_builder - .set_option( - "wid", - parent - .expect("No parent window") - .into() - .hwnd() - .expect("Cannot obtain window handle") as i64, - ) - .expect("failed setting wid"); - // mpv_builder.set_option("vo", "gpu").expect("unable to set vo"); - // win, opengl: works but least performancy, 10-15% CPU - // winvk, vulkan: works as good as d3d11 - // d3d11, d1d11: works great - // dxinterop, auto: works, slightly more cpu use than d3d11 - // default (auto) seems to be d3d11 (vo/gpu/d3d11) - mpv_builder - .set_option("gpu-context", "angle") - .and_then(|_| mpv_builder.set_option("gpu-api", "auto")) - .expect("setting gpu options failed"); - mpv_builder - .try_hardware_decoding() - .expect("failed setting hwdec"); - mpv_builder - .set_option("terminal", "yes") - .expect("failed setting terminal"); - mpv_builder - .set_option("msg-level", "all=v") - .expect("failed setting msg-level"); - //mpv_builder.set_option("quiet", "yes").expect("failed setting msg-level"); - data.mpv = RefCell::new(mpv_builder.build().ok()); + let (tx, rx) = mpsc::channel::(); + let (tx1, rx1) = mpsc::channel::(); + data.channel = RefCell::new(Some((tx, Arc::new(Mutex::new(rx1))))); + let hwnd = parent + .expect("No parent window") + .into() + .hwnd() + .expect("Cannot obtain window handle") as i64; + thread::spawn(move || { + let mut mpv_builder = + mpv::MpvHandlerBuilder::new().expect("Error while creating MPV builder"); + mpv_builder + .set_option("wid", hwnd) + .expect("failed setting wid"); + // mpv_builder.set_option("vo", "gpu").expect("unable to set vo"); + // win, opengl: works but least performancy, 10-15% CPU + // winvk, vulkan: works as good as d3d11 + // d3d11, d1d11: works great + // dxinterop, auto: works, slightly more cpu use than d3d11 + // default (auto) seems to be d3d11 (vo/gpu/d3d11) + mpv_builder + .set_option("gpu-context", "angle") + .and_then(|_| mpv_builder.set_option("gpu-api", "auto")) + .expect("setting gpu options failed"); + mpv_builder + .try_hardware_decoding() + .expect("failed setting hwdec"); + mpv_builder + .set_option("terminal", "yes") + .expect("failed setting terminal"); + mpv_builder + .set_option("msg-level", "all=v") + .expect("failed setting msg-level"); + //mpv_builder.set_option("quiet", "yes").expect("failed setting msg-level"); + let mut mpv = mpv_builder.build().unwrap(); + 'main: loop { + // wait up to 0.0 seconds for an event. + while let Some(event) = mpv.wait_event(0.0) { + // even if you don't do anything with the events, it is still necessary to empty + // the event loop + // TODO: Parse and format the Event in proper JSON format + tx1.send(format!("{:?}", event)).ok(); + println!("RECEIVED EVENT : {:?}", event); + match event { + mpv::Event::Shutdown | mpv::Event::EndFile(_) => { + break 'main; + } + _ => {} + }; + } + if let Ok(msg) = rx.try_recv() { + println!("PLAYER RECEIVED MESSAGE: {}", msg); + // let video_path = "http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_normal.mp4"; + // mpv.command(&["loadfile", video_path]).ok(); + // mpv.command(&["stop"]).ok(); + // mpv.set_property("paused", true).ok(); + } + } + }); + Ok(()) } } diff --git a/src/stremio_app/stremio_wevbiew/stremio_wevbiew.rs b/src/stremio_app/stremio_wevbiew/stremio_wevbiew.rs index 2bd1f5e..1fc30a1 100644 --- a/src/stremio_app/stremio_wevbiew/stremio_wevbiew.rs +++ b/src/stremio_app/stremio_wevbiew/stremio_wevbiew.rs @@ -1,43 +1,22 @@ use native_windows_gui::{self as nwg, PartialUi}; use once_cell::unsync::OnceCell; -use serde::{Deserialize, Serialize}; -use serde_json; +use std::cell::RefCell; use std::mem; use std::rc::Rc; +use std::sync::mpsc; +use std::sync::{Arc, Mutex}; +use std::thread; use webview2::Controller; use winapi::shared::windef::HWND__; use winapi::um::winuser::*; -#[derive(Serialize, Deserialize, Debug, Clone)] -struct RPCRequest { - id: u64, - args: Option>, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -struct RPCResponseDataTransport { - properties: Vec>, - signals: Vec, - methods: Vec>, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -struct RPCResponseData { - transport: RPCResponseDataTransport, -} - -#[derive(Serialize, Deserialize, Debug, Clone)] -struct RPCResponse { - id: u64, - object: String, - #[serde(rename = "type")] - response_type: u32, - data: RPCResponseData, -} - #[derive(Default)] pub struct WebView { controller: Rc>, + pub channel: RefCell, Arc>>)>>, + notice: nwg::Notice, + compute: RefCell>>, + message: Arc>>, } impl WebView { @@ -61,9 +40,19 @@ impl PartialUi for WebView { data: &mut Self, parent: Option, ) -> Result<(), nwg::NwgError> { + let (tx, rx) = mpsc::channel::(); + let (tx1, rx1) = mpsc::channel::(); + data.channel = RefCell::new(Some((tx, Arc::new(Mutex::new(rx1))))); + let parent = parent.expect("No parent window").into(); - let hwnd = parent.hwnd().expect("Cannot obtain window handle"); + + let hwnd = parent.hwnd().expect("Cannot obtain window handle") as i64; + nwg::Notice::builder() + .parent(parent) + .build(&mut data.notice) + .ok(); let controller_clone = data.controller.clone(); + let hwnd = hwnd as *mut HWND__; let result = webview2::EnvironmentBuilder::new() .with_additional_browser_arguments("--disable-gpu") .build(move |env| { @@ -98,30 +87,12 @@ impl PartialUi for WebView { |_| Ok(()), ) .ok(); - webview.add_web_message_received(|w, msg| { + webview.add_web_message_received(move |_w, msg| { let msg = msg.try_get_web_message_as_string()?; - let msg: RPCRequest = serde_json::from_str(&msg).unwrap(); - dbg!(msg.clone()); - if msg.id == 0 { - let resp: RPCResponse = RPCResponse { - id: 0, - object: "transport".to_string(), - response_type: 3, - data: RPCResponseData { - transport: RPCResponseDataTransport { - properties: vec![vec![], vec!["".to_string(), "shellVersion".to_string(), "".to_string(), "5.0.0".to_string()]], - signals: vec![], - methods: vec![vec!["onEvent".to_string(), "".to_string()]] - } - } - }; - let resp_json = serde_json::to_string(&resp).unwrap(); - dbg!(resp_json.clone()); - w.post_web_message_as_string(&resp_json).ok(); - } + tx1.send(msg).ok(); Ok(()) }).ok(); - WebView::resize_to_window_bounds_and_show(Some(&controller), parent.hwnd()); + WebView::resize_to_window_bounds_and_show(Some(&controller), Some(hwnd)); controller_clone .set(controller) .expect("Cannot update the controller"); @@ -135,6 +106,17 @@ impl PartialUi for WebView { &format!("{}", e), ); } + + let sender = data.notice.sender(); + let message = data.message.clone(); + *data.compute.borrow_mut() = Some(thread::spawn(move || loop { + if let Ok(msg) = rx.try_recv() { + let mut message = message.lock().unwrap(); + *message = Some(msg); + sender.notice(); + } + })); + Ok(()) } fn process_event<'a>( @@ -145,6 +127,7 @@ impl PartialUi for WebView { ) { use nwg::Event as E; match evt { + E::OnInit => {} E::OnResize | E::OnWindowMaximize => { WebView::resize_to_window_bounds_and_show(self.controller.get(), handle.hwnd()); } @@ -153,6 +136,17 @@ impl PartialUi for WebView { controller.put_is_visible(false).ok(); } } + E::OnNotice => { + let msg = self.message.clone(); + let mut msg = msg.lock().unwrap(); + if let Some(msg) = &*msg { + if let Some(controller) = self.controller.get() { + let webview = controller.get_webview().expect("Cannot get vebview"); + webview.post_web_message_as_string(msg).ok(); + } + } + *msg = None; + } _ => {} } }