Merge remote-tracking branch 'upstream/master'

# Conflicts:
#	src/server/connection.rs
This commit is contained in:
mcfans
2023-10-29 23:32:43 +08:00
112 changed files with 2604 additions and 1183 deletions

View File

@@ -102,6 +102,7 @@ message PeerInfo {
SupportedEncoding encoding = 10;
SupportedResolutions resolutions = 11;
// Use JSON's key-value format which is friendly for peer to handle.
// NOTE: Only support one-level dictionaries (for peer to update), and the key is of type string.
string platform_additions = 12;
}
@@ -498,6 +499,11 @@ message CaptureDisplays {
repeated int32 set = 3;
}
message ToggleVirtualDisplay {
int32 display = 1;
bool on = 2;
}
message PermissionInfo {
enum Permission {
Keyboard = 0;
@@ -697,6 +703,7 @@ message Misc {
bool client_record_status = 29;
CaptureDisplays capture_displays = 30;
int32 refresh_video_display = 31;
ToggleVirtualDisplay toggle_virtual_display = 32;
}
}

View File

@@ -290,6 +290,12 @@ pub struct PeerConfig {
skip_serializing_if = "String::is_empty"
)]
pub displays_as_individual_windows: String,
#[serde(
default = "PeerConfig::default_use_all_my_displays_for_the_remote_session",
deserialize_with = "PeerConfig::deserialize_use_all_my_displays_for_the_remote_session",
skip_serializing_if = "String::is_empty"
)]
pub use_all_my_displays_for_the_remote_session: String,
#[serde(
default,
@@ -335,6 +341,8 @@ impl Default for PeerConfig {
view_only: Default::default(),
reverse_mouse_wheel: Self::default_reverse_mouse_wheel(),
displays_as_individual_windows: Self::default_displays_as_individual_windows(),
use_all_my_displays_for_the_remote_session:
Self::default_use_all_my_displays_for_the_remote_session(),
custom_resolutions: Default::default(),
options: Self::default_options(),
ui_flutter: Default::default(),
@@ -561,7 +569,7 @@ impl Config {
pub fn get_home() -> PathBuf {
#[cfg(any(target_os = "android", target_os = "ios"))]
return Self::path(APP_HOME_DIR.read().unwrap().as_str());
return PathBuf::from(APP_HOME_DIR.read().unwrap().as_str());
#[cfg(not(any(target_os = "android", target_os = "ios")))]
{
if let Some(path) = dirs_next::home_dir() {
@@ -615,6 +623,13 @@ impl Config {
std::fs::create_dir_all(&path).ok();
return path;
}
#[cfg(target_os = "android")]
{
let mut path = Self::get_home();
path.push(format!("{}/Logs", *APP_NAME.read().unwrap()));
std::fs::create_dir_all(&path).ok();
return path;
}
if let Some(path) = Self::path("").parent() {
let mut path: PathBuf = path.into();
path.push("log");
@@ -1156,6 +1171,11 @@ impl PeerConfig {
deserialize_displays_as_individual_windows,
UserDefaultConfig::read().get("displays_as_individual_windows")
);
serde_field_string!(
default_use_all_my_displays_for_the_remote_session,
deserialize_use_all_my_displays_for_the_remote_session,
UserDefaultConfig::read().get("use_all_my_displays_for_the_remote_session")
);
fn default_custom_image_quality() -> Vec<i32> {
let f: f64 = UserDefaultConfig::read()

View File

@@ -19,7 +19,7 @@ cfg-if = "1.0"
num_cpus = "1.15"
lazy_static = "1.4"
hbb_common = { path = "../hbb_common" }
webm = "1.0"
webm = { git = "https://github.com/21pages/rust-webm" }
[dependencies.winapi]
version = "0.3"

View File

@@ -362,13 +362,14 @@ pub fn check_config_process() {
let f = || {
// Clear to avoid checking process errors
// But when the program is just started, the configuration file has not been updated, and the new connection will read an empty configuration
// TODO: --server start multi times on windows startup, which will clear the last config and cause concurrent file writing
HwCodecConfig::clear();
if let Ok(exe) = std::env::current_exe() {
if let Some(_) = exe.file_name().to_owned() {
let arg = "--check-hwcodec-config";
if let Ok(mut child) = std::process::Command::new(exe).arg(arg).spawn() {
// wait up to 10 seconds
for _ in 0..10 {
// wait up to 30 seconds, it maybe slow on windows startup for poorly performing machines
for _ in 0..30 {
std::thread::sleep(std::time::Duration::from_secs(1));
if let Ok(Some(_)) = child.try_wait() {
break;

View File

@@ -49,9 +49,12 @@ impl RecorderContext {
}
let file = if self.server { "s" } else { "c" }.to_string()
+ &self.id.clone()
+ &chrono::Local::now().format("_%Y%m%d%H%M%S_").to_string()
+ &self.format.to_string()
+ if self.format == CodecFormat::VP9 || self.format == CodecFormat::VP8 {
+ &chrono::Local::now().format("_%Y%m%d%H%M%S%3f_").to_string()
+ &self.format.to_string().to_lowercase()
+ if self.format == CodecFormat::VP9
|| self.format == CodecFormat::VP8
|| self.format == CodecFormat::AV1
{
".webm"
} else {
".mp4"
@@ -83,6 +86,7 @@ pub enum RecordState {
pub struct Recorder {
pub inner: Box<dyn RecorderApi>,
ctx: RecorderContext,
pts: Option<i64>,
}
impl Deref for Recorder {
@@ -101,19 +105,18 @@ impl DerefMut for Recorder {
impl Recorder {
pub fn new(mut ctx: RecorderContext) -> ResultType<Self> {
if ctx.format == CodecFormat::AV1 {
bail!("not support av1 recording");
}
ctx.set_filename()?;
let recorder = match ctx.format {
CodecFormat::VP8 | CodecFormat::VP9 => Recorder {
CodecFormat::VP8 | CodecFormat::VP9 | CodecFormat::AV1 => Recorder {
inner: Box::new(WebmRecorder::new(ctx.clone())?),
ctx,
pts: None,
},
#[cfg(feature = "hwcodec")]
_ => Recorder {
inner: Box::new(HwRecorder::new(ctx.clone())?),
ctx,
pts: None,
},
#[cfg(not(feature = "hwcodec"))]
_ => bail!("unsupported codec type"),
@@ -125,13 +128,16 @@ impl Recorder {
fn change(&mut self, mut ctx: RecorderContext) -> ResultType<()> {
ctx.set_filename()?;
self.inner = match ctx.format {
CodecFormat::VP8 | CodecFormat::VP9 => Box::new(WebmRecorder::new(ctx.clone())?),
CodecFormat::VP8 | CodecFormat::VP9 | CodecFormat::AV1 => {
Box::new(WebmRecorder::new(ctx.clone())?)
}
#[cfg(feature = "hwcodec")]
_ => Box::new(HwRecorder::new(ctx.clone())?),
#[cfg(not(feature = "hwcodec"))]
_ => bail!("unsupported codec type"),
};
self.ctx = ctx;
self.pts = None;
self.send_state(RecordState::NewFile(self.ctx.filename.clone()));
Ok(())
}
@@ -153,7 +159,10 @@ impl Recorder {
..self.ctx.clone()
})?;
}
vp8s.frames.iter().map(|f| self.write_video(f)).count();
for f in vp8s.frames.iter() {
self.check_pts(f.pts)?;
self.write_video(f);
}
}
video_frame::Union::Vp9s(vp9s) => {
if self.ctx.format != CodecFormat::VP9 {
@@ -162,7 +171,22 @@ impl Recorder {
..self.ctx.clone()
})?;
}
vp9s.frames.iter().map(|f| self.write_video(f)).count();
for f in vp9s.frames.iter() {
self.check_pts(f.pts)?;
self.write_video(f);
}
}
video_frame::Union::Av1s(av1s) => {
if self.ctx.format != CodecFormat::AV1 {
self.change(RecorderContext {
format: CodecFormat::AV1,
..self.ctx.clone()
})?;
}
for f in av1s.frames.iter() {
self.check_pts(f.pts)?;
self.write_video(f);
}
}
#[cfg(feature = "hwcodec")]
video_frame::Union::H264s(h264s) => {
@@ -172,8 +196,9 @@ impl Recorder {
..self.ctx.clone()
})?;
}
if self.ctx.format == CodecFormat::H264 {
h264s.frames.iter().map(|f| self.write_video(f)).count();
for f in h264s.frames.iter() {
self.check_pts(f.pts)?;
self.write_video(f);
}
}
#[cfg(feature = "hwcodec")]
@@ -184,8 +209,9 @@ impl Recorder {
..self.ctx.clone()
})?;
}
if self.ctx.format == CodecFormat::H265 {
h265s.frames.iter().map(|f| self.write_video(f)).count();
for f in h265s.frames.iter() {
self.check_pts(f.pts)?;
self.write_video(f);
}
}
_ => bail!("unsupported frame type"),
@@ -194,6 +220,17 @@ impl Recorder {
Ok(())
}
fn check_pts(&mut self, pts: i64) -> ResultType<()> {
// https://stackoverflow.com/questions/76379101/how-to-create-one-playable-webm-file-from-two-different-video-tracks-with-same-c
let old_pts = self.pts;
self.pts = Some(pts);
if old_pts.clone().unwrap_or_default() > pts {
log::info!("pts {:?}->{}, change record filename", old_pts, pts);
self.change(self.ctx.clone())?;
}
Ok(())
}
fn send_state(&self, state: RecordState) {
self.ctx.tx.as_ref().map(|tx| tx.send(state));
}
@@ -230,10 +267,19 @@ impl RecorderApi for WebmRecorder {
None,
if ctx.format == CodecFormat::VP9 {
mux::VideoCodecId::VP9
} else {
} else if ctx.format == CodecFormat::VP8 {
mux::VideoCodecId::VP8
} else {
mux::VideoCodecId::AV1
},
);
if ctx.format == CodecFormat::AV1 {
// [129, 8, 12, 0] in 3.6.0, but zero works
let codec_private = vec![0, 0, 0, 0];
if !webm.set_codec_private(vt.track_number(), &codec_private) {
bail!("Failed to set codec private");
}
}
Ok(WebmRecorder {
vt,
webm: Some(webm),

View File

@@ -1,9 +1,10 @@
#[cfg(windows)]
pub mod win10;
use hbb_common::ResultType;
#[cfg(windows)]
use hbb_common::lazy_static;
use hbb_common::{bail, ResultType};
use std::path::Path;
use hbb_common::{bail, lazy_static};
#[cfg(windows)]
use std::path::PathBuf;
#[cfg(windows)]
use std::sync::Mutex;
@@ -33,18 +34,25 @@ pub fn download_driver() -> ResultType<()> {
Ok(())
}
#[no_mangle]
pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> {
#[cfg(windows)]
#[cfg(windows)]
fn get_driver_install_abs_path() -> ResultType<PathBuf> {
let install_path = win10::DRIVER_INSTALL_PATH;
#[cfg(not(windows))]
let install_path = "";
let abs_path = Path::new(install_path).canonicalize()?;
let exe_file = std::env::current_exe()?;
let abs_path = match exe_file.parent() {
Some(cur_dir) => cur_dir.join(install_path),
None => bail!(
"Invalid exe parent for {}",
exe_file.to_string_lossy().as_ref()
),
};
if !abs_path.exists() {
bail!("{} not exists", install_path)
}
Ok(abs_path)
}
#[no_mangle]
pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> {
#[cfg(windows)]
unsafe {
{
@@ -54,6 +62,7 @@ pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> {
bail!("{}", e);
}
let abs_path = get_driver_install_abs_path()?;
let full_install_path: Vec<u16> = abs_path
.to_string_lossy()
.as_ref()
@@ -76,19 +85,10 @@ pub fn install_update_driver(_reboot_required: &mut bool) -> ResultType<()> {
#[no_mangle]
pub fn uninstall_driver(_reboot_required: &mut bool) -> ResultType<()> {
#[cfg(windows)]
let install_path = win10::DRIVER_INSTALL_PATH;
#[cfg(not(windows))]
let install_path = "";
let abs_path = Path::new(install_path).canonicalize()?;
if !abs_path.exists() {
bail!("{} not exists", install_path)
}
#[cfg(windows)]
unsafe {
{
let abs_path = get_driver_install_abs_path()?;
let full_install_path: Vec<u16> = abs_path
.to_string_lossy()
.as_ref()