Progress on the IPC

This commit is contained in:
Vladimir Borisov 2021-07-16 17:33:58 +03:00
parent 56d255f44a
commit d3d05a43fb
4 changed files with 211 additions and 110 deletions

View file

@ -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",

View file

@ -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<Vec<serde_json::Value>>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct RPCResponseDataTransport {
properties: Vec<Vec<String>>,
signals: Vec<String>,
methods: Vec<Vec<String>>,
}
#[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::<String>(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();

View file

@ -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<Option<mpv::MpvHandler>>,
}
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<T: mpv::MpvFormat>(&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<Option<(mpsc::Sender<String>, Arc<Mutex<mpsc::Receiver<String>>>)>>,
}
impl PartialUi for Player {
@ -28,39 +14,66 @@ impl PartialUi for Player {
data: &mut Self,
parent: Option<W>,
) -> 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::<String>();
let (tx1, rx1) = mpsc::channel::<String>();
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(())
}
}

View file

@ -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<Vec<serde_json::Value>>,
}
#[derive(Serialize, Deserialize, Debug, Clone)]
struct RPCResponseDataTransport {
properties: Vec<Vec<String>>,
signals: Vec<String>,
methods: Vec<Vec<String>>,
}
#[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<OnceCell<Controller>>,
pub channel: RefCell<Option<(mpsc::Sender<String>, Arc<Mutex<mpsc::Receiver<String>>>)>>,
notice: nwg::Notice,
compute: RefCell<Option<thread::JoinHandle<()>>>,
message: Arc<Mutex<Option<String>>>,
}
impl WebView {
@ -61,9 +40,19 @@ impl PartialUi for WebView {
data: &mut Self,
parent: Option<W>,
) -> Result<(), nwg::NwgError> {
let (tx, rx) = mpsc::channel::<String>();
let (tx1, rx1) = mpsc::channel::<String>();
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;
}
_ => {}
}
}