From 1fecd7168a3f9b15e0ed8e973d215e66551fd604 Mon Sep 17 00:00:00 2001 From: 21pages <pages21@163.com> Date: Thu, 7 Jul 2022 20:55:19 +0800 Subject: [PATCH 1/4] hwcodec: linux compatible Signed-off-by: 21pages <pages21@163.com> --- Cargo.lock | 2 +- libs/scrap/Cargo.toml | 2 +- libs/scrap/src/common/convert.rs | 3 +++ 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c91272112..21224012f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2209,7 +2209,7 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hwcodec" version = "0.1.0" -source = "git+https://github.com/21pages/hwcodec#91d1cd327c88490f917457072aeef0676ddb2be7" +source = "git+https://github.com/21pages/hwcodec#890204e0703a3d361fc7a45f035fe75c0575bb1d" dependencies = [ "bindgen", "cc", diff --git a/libs/scrap/Cargo.toml b/libs/scrap/Cargo.toml index 1b269d96e..d40eb0cfd 100644 --- a/libs/scrap/Cargo.toml +++ b/libs/scrap/Cargo.toml @@ -50,5 +50,5 @@ gstreamer = { version = "0.16", optional = true } gstreamer-app = { version = "0.16", features = ["v1_10"], optional = true } gstreamer-video = { version = "0.16", optional = true } -[target.'cfg(target_os = "windows")'.dependencies] +[target.'cfg(any(target_os = "windows", target_os = "linux"))'.dependencies] hwcodec = { git = "https://github.com/21pages/hwcodec", optional = true } diff --git a/libs/scrap/src/common/convert.rs b/libs/scrap/src/common/convert.rs index 306a217ea..2b0223a0a 100644 --- a/libs/scrap/src/common/convert.rs +++ b/libs/scrap/src/common/convert.rs @@ -246,6 +246,7 @@ pub unsafe fn nv12_to_i420( #[cfg(feature = "hwcodec")] pub mod hw { use hbb_common::{anyhow::anyhow, ResultType}; + #[cfg(target_os = "windows")] use hwcodec::{ffmpeg::ffmpeg_linesize_offset_length, AVPixelFormat}; pub fn hw_bgra_to_i420( @@ -381,6 +382,8 @@ pub mod hw { src_stride_y: usize, src_stride_uv: usize, dst: &mut Vec<u8>, + _i420: &mut Vec<u8>, + _align: usize, ) -> ResultType<()> { dst.resize(width * height * 4, 0); unsafe { From 1b1f28b872a32ba7220affe977d285fae35e57f4 Mon Sep 17 00:00:00 2001 From: 21pages <pages21@163.com> Date: Fri, 8 Jul 2022 18:18:58 +0800 Subject: [PATCH 2/4] hwcodec: check when server or each client starts and refactor hwcodec::best() Signed-off-by: 21pages <pages21@163.com> --- libs/hbb_common/src/config.rs | 4 + libs/scrap/src/common/codec.rs | 4 +- libs/scrap/src/common/hwcodec.rs | 127 +++++++++++++------------------ src/server.rs | 17 ++--- 4 files changed, 69 insertions(+), 83 deletions(-) diff --git a/libs/hbb_common/src/config.rs b/libs/hbb_common/src/config.rs index 787ffe5ee..cf657c8dc 100644 --- a/libs/hbb_common/src/config.rs +++ b/libs/hbb_common/src/config.rs @@ -978,6 +978,10 @@ impl HwCodecConfig { pub fn store(&self) { Config::store_(self, "_hwcodec"); } + + pub fn remove() { + std::fs::remove_file(Config::file_("_hwcodec")).ok(); + } } #[cfg(test)] diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 1d9deba68..b48053e0b 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -102,7 +102,7 @@ impl Encoder { codec: Box::new(hw), }), Err(e) => { - HwEncoder::best(true, true); + check_config_process(true); Err(e) } }, @@ -132,7 +132,7 @@ impl Encoder { } let current_encoder_name = HwEncoder::current_name(); if states.len() > 0 { - let (best, _) = HwEncoder::best(false, true); + let best = HwEncoder::best(); let enabled_h264 = best.h264.is_some() && states.len() > 0 && states.iter().all(|(_, s)| s.ScoreH264 > 0); diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index ff23978de..c2c853956 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -123,40 +123,11 @@ impl EncoderApi for HwEncoder { } impl HwEncoder { - /// Get best encoders. - /// - /// # Parameter - /// `force_reset`: force to refresh config. - /// `write`: write to config file. - /// - /// # Return - /// `CodecInfos`: infos. - /// `bool`: whether the config is refreshed. - pub fn best(force_reset: bool, write: bool) -> (CodecInfos, bool) { - let config = get_config(CFG_KEY_ENCODER); - if !force_reset && config.is_ok() { - (config.unwrap(), false) - } else { - let ctx = EncodeContext { - name: String::from(""), - width: 1920, - height: 1080, - pixfmt: DEFAULT_PIXFMT, - align: HW_STRIDE_ALIGN as _, - bitrate: 0, - timebase: DEFAULT_TIME_BASE, - gop: DEFAULT_GOP, - quality: DEFAULT_HW_QUALITY, - rc: DEFAULT_RC, - }; - let encoders = CodecInfo::score(Encoder::avaliable_encoders(ctx)); - if write { - set_config(CFG_KEY_ENCODER, &encoders) - .map_err(|e| log::error!("{:?}", e)) - .ok(); - } - (encoders, true) - } + pub fn best() -> CodecInfos { + get_config(CFG_KEY_ENCODER).unwrap_or(CodecInfos { + h264: None, + h265: None, + }) } pub fn current_name() -> Arc<Mutex<Option<String>>> { @@ -207,24 +178,15 @@ pub struct HwDecoders { } impl HwDecoder { - /// See HwEncoder::best - fn best(force_reset: bool, write: bool) -> (CodecInfos, bool) { - let config = get_config(CFG_KEY_DECODER); - if !force_reset && config.is_ok() { - (config.unwrap(), false) - } else { - let decoders = CodecInfo::score(Decoder::avaliable_decoders()); - if write { - set_config(CFG_KEY_DECODER, &decoders) - .map_err(|e| log::error!("{:?}", e)) - .ok(); - } - (decoders, true) - } + fn best() -> CodecInfos { + get_config(CFG_KEY_DECODER).unwrap_or(CodecInfos { + h264: None, + h265: None, + }) } pub fn new_decoders() -> HwDecoders { - let (best, _) = HwDecoder::best(false, true); + let best = HwDecoder::best(); let mut h264: Option<HwDecoder> = None; let mut h265: Option<HwDecoder> = None; let mut fail = false; @@ -242,7 +204,7 @@ impl HwDecoder { } } if fail { - HwDecoder::best(true, true); + check_config_process(true); } HwDecoders { h264, h265 } } @@ -314,31 +276,52 @@ fn get_config(k: &str) -> ResultType<CodecInfos> { } } -fn set_config(k: &str, v: &CodecInfos) -> ResultType<()> { - match v.serialize() { - Ok(v) => { - let mut config = HwCodecConfig::load(); - config.options.insert(k.to_owned(), v); - config.store(); - Ok(()) - } - Err(_) => Err(anyhow!("Failed to set config:{}", k)), - } -} - pub fn check_config() { - let (encoders, update_encoders) = HwEncoder::best(false, false); - let (decoders, update_decoders) = HwDecoder::best(false, false); - if update_encoders || update_decoders { - if let Ok(encoders) = encoders.serialize() { - if let Ok(decoders) = decoders.serialize() { - let mut config = HwCodecConfig::load(); - config.options.insert(CFG_KEY_ENCODER.to_owned(), encoders); - config.options.insert(CFG_KEY_DECODER.to_owned(), decoders); - config.store(); + let ctx = EncodeContext { + name: String::from(""), + width: 1920, + height: 1080, + pixfmt: DEFAULT_PIXFMT, + align: HW_STRIDE_ALIGN as _, + bitrate: 0, + timebase: DEFAULT_TIME_BASE, + gop: DEFAULT_GOP, + quality: DEFAULT_HW_QUALITY, + rc: DEFAULT_RC, + }; + let encoders = CodecInfo::score(Encoder::avaliable_encoders(ctx)); + let decoders = CodecInfo::score(Decoder::avaliable_decoders()); + + if let Ok(old_encoders) = get_config(CFG_KEY_ENCODER) { + if let Ok(old_decoders) = get_config(CFG_KEY_DECODER) { + if encoders == old_encoders && decoders == old_decoders { return; } } - log::error!("Failed to serialize codec info"); } + + if let Ok(encoders) = encoders.serialize() { + if let Ok(decoders) = decoders.serialize() { + let mut config = HwCodecConfig::load(); + config.options.insert(CFG_KEY_ENCODER.to_owned(), encoders); + config.options.insert(CFG_KEY_DECODER.to_owned(), decoders); + config.store(); + return; + } + } + log::error!("Failed to serialize codec info"); +} + +pub fn check_config_process(force_reset: bool) { + if force_reset { + HwCodecConfig::remove(); + } + if let Ok(exe) = std::env::current_exe() { + std::thread::spawn(move || { + std::process::Command::new(exe) + .arg("--check-hwcodec-config") + .status() + .ok() + }); + }; } diff --git a/src/server.rs b/src/server.rs index a71efaee2..f2d643e1c 100644 --- a/src/server.rs +++ b/src/server.rs @@ -319,6 +319,14 @@ pub async fn start_server(is_server: bool) { log::info!("DISPLAY={:?}", std::env::var("DISPLAY")); log::info!("XAUTHORITY={:?}", std::env::var("XAUTHORITY")); } + #[cfg(feature = "hwcodec")] + { + use std::sync::Once; + static ONCE: Once = Once::new(); + ONCE.call_once(|| { + scrap::hwcodec::check_config_process(false); + }) + } if is_server { std::thread::spawn(move || { @@ -327,15 +335,6 @@ pub async fn start_server(is_server: bool) { std::process::exit(-1); } }); - #[cfg(feature = "hwcodec")] - if let Ok(exe) = std::env::current_exe() { - std::thread::spawn(move || { - std::process::Command::new(exe) - .arg("--check-hwcodec-config") - .status() - .ok() - }); - } #[cfg(windows)] crate::platform::windows::bootstrap(); input_service::fix_key_down_timeout_loop(); From 7aa431d349593467fbbefb05375fbb80d4aa7dbe Mon Sep 17 00:00:00 2001 From: 21pages <pages21@163.com> Date: Sat, 9 Jul 2022 20:17:10 +0800 Subject: [PATCH 3/4] hwcodec: codec preference Signed-off-by: 21pages <pages21@163.com> --- libs/hbb_common/protos/message.proto | 20 +++- libs/scrap/src/common/codec.rs | 143 +++++++++++++++++++-------- src/client.rs | 21 +++- src/server/connection.rs | 16 +++ src/server/video_service.rs | 4 + src/ui/header.css | 1 + src/ui/header.tis | 20 +++- src/ui/remote.rs | 39 ++++++++ 8 files changed, 217 insertions(+), 47 deletions(-) diff --git a/libs/hbb_common/protos/message.proto b/libs/hbb_common/protos/message.proto index 2044388f8..7b4bc3787 100644 --- a/libs/hbb_common/protos/message.proto +++ b/libs/hbb_common/protos/message.proto @@ -72,6 +72,11 @@ message Features { bool privacy_mode = 1; } +message SupportedEncoding { + bool h264 = 1; + bool h265 = 2; +} + message PeerInfo { string username = 1; string hostname = 2; @@ -82,6 +87,7 @@ message PeerInfo { string version = 7; int32 conn_id = 8; Features features = 9; + SupportedEncoding encoding = 10; } message LoginResponse { @@ -434,9 +440,17 @@ enum ImageQuality { } message VideoCodecState { - int32 ScoreVpx = 1; - int32 ScoreH264 = 2; - int32 ScoreH265 = 3; + enum PerferCodec { + Auto = 0; + VPX = 1; + H264 = 2; + H265 = 3; + } + + int32 score_vpx = 1; + int32 score_h264 = 2; + int32 score_h265 = 3; + PerferCodec perfer = 4; } message OptionMessage { diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index b48053e0b..1a18a79c3 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -16,7 +16,11 @@ use hbb_common::{ ResultType, }; #[cfg(feature = "hwcodec")] -use hbb_common::{config::Config2, lazy_static}; +use hbb_common::{ + config::{Config2, PeerConfig}, + lazy_static, + message_proto::video_codec_state::PerferCodec, +}; #[cfg(feature = "hwcodec")] lazy_static::lazy_static! { @@ -113,7 +117,6 @@ impl Encoder { // TODO pub fn update_video_encoder(id: i32, update: EncoderUpdate) { - log::info!("encoder update: {:?}", update); #[cfg(feature = "hwcodec")] { let mut states = PEER_DECODER_STATES.lock().unwrap(); @@ -130,49 +133,75 @@ impl Encoder { } } } - let current_encoder_name = HwEncoder::current_name(); + let name = HwEncoder::current_name(); if states.len() > 0 { let best = HwEncoder::best(); let enabled_h264 = best.h264.is_some() && states.len() > 0 - && states.iter().all(|(_, s)| s.ScoreH264 > 0); + && states.iter().all(|(_, s)| s.score_h264 > 0); let enabled_h265 = best.h265.is_some() && states.len() > 0 - && states.iter().all(|(_, s)| s.ScoreH265 > 0); + && states.iter().all(|(_, s)| s.score_h265 > 0); - // score encoder - let mut score_vpx = SCORE_VPX; - let mut score_h264 = best.h264.as_ref().map_or(0, |c| c.score); - let mut score_h265 = best.h265.as_ref().map_or(0, |c| c.score); - - // score decoder - score_vpx += states.iter().map(|s| s.1.ScoreVpx).sum::<i32>(); - if enabled_h264 { - score_h264 += states.iter().map(|s| s.1.ScoreH264).sum::<i32>(); - } - if enabled_h265 { - score_h265 += states.iter().map(|s| s.1.ScoreH265).sum::<i32>(); + // Preference first + let mut preference = PerferCodec::Auto; + let preferences: Vec<_> = states + .iter() + .filter(|(_, s)| { + s.perfer == PerferCodec::VPX.into() + || s.perfer == PerferCodec::H264.into() && enabled_h264 + || s.perfer == PerferCodec::H265.into() && enabled_h265 + }) + .map(|(_, s)| s.perfer) + .collect(); + if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { + preference = preferences[0].enum_value_or(PerferCodec::Auto); } - if enabled_h265 && score_h265 >= score_vpx && score_h265 >= score_h264 { - *current_encoder_name.lock().unwrap() = Some(best.h265.unwrap().name); - } else if enabled_h264 && score_h264 >= score_vpx && score_h264 >= score_h265 { - *current_encoder_name.lock().unwrap() = Some(best.h264.unwrap().name); - } else { - *current_encoder_name.lock().unwrap() = None; + match preference { + PerferCodec::VPX => *name.lock().unwrap() = None, + PerferCodec::H264 => { + *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)) + } + PerferCodec::H265 => { + *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)) + } + PerferCodec::Auto => { + // score encoder + let mut score_vpx = SCORE_VPX; + let mut score_h264 = best.h264.as_ref().map_or(0, |c| c.score); + let mut score_h265 = best.h265.as_ref().map_or(0, |c| c.score); + + // score decoder + score_vpx += states.iter().map(|s| s.1.score_vpx).sum::<i32>(); + if enabled_h264 { + score_h264 += states.iter().map(|s| s.1.score_h264).sum::<i32>(); + } + if enabled_h265 { + score_h265 += states.iter().map(|s| s.1.score_h265).sum::<i32>(); + } + + if enabled_h265 && score_h265 >= score_vpx && score_h265 >= score_h264 { + *name.lock().unwrap() = best.h265.map_or(None, |c| Some(c.name)); + } else if enabled_h264 + && score_h264 >= score_vpx + && score_h264 >= score_h265 + { + *name.lock().unwrap() = best.h264.map_or(None, |c| Some(c.name)); + } else { + *name.lock().unwrap() = None; + } + } } + log::info!( - "connection count:{}, h264:{}, h265:{}, score: vpx({}), h264({}), h265({}), set current encoder name {:?}", + "connection count:{}, used preference:{:?}, encoder:{:?}", states.len(), - enabled_h264, - enabled_h265, - score_vpx, - score_h264, - score_h265, - current_encoder_name.lock().unwrap() - ) + preference, + name.lock().unwrap() + ) } else { - *current_encoder_name.lock().unwrap() = None; + *name.lock().unwrap() = None; } } #[cfg(not(feature = "hwcodec"))] @@ -192,34 +221,51 @@ impl Encoder { #[cfg(not(feature = "hwcodec"))] return None; } + + pub fn supported_encoding() -> (bool, bool) { + #[cfg(feature = "hwcodec")] + if check_hwcodec_config() { + let best = HwEncoder::best(); + ( + best.h264.as_ref().map_or(false, |c| c.score > 0), + best.h265.as_ref().map_or(false, |c| c.score > 0), + ) + } else { + (false, false) + } + #[cfg(not(feature = "hwcodec"))] + (false, false) + } } #[cfg(feature = "hwcodec")] impl Drop for Decoder { fn drop(&mut self) { *MY_DECODER_STATE.lock().unwrap() = VideoCodecState { - ScoreVpx: SCORE_VPX, + score_vpx: SCORE_VPX, ..Default::default() }; } } impl Decoder { - pub fn video_codec_state() -> VideoCodecState { + pub fn video_codec_state(_id: &str) -> VideoCodecState { // video_codec_state is mainted by creation and destruction of Decoder. // It has been ensured to use after Decoder's creation. #[cfg(feature = "hwcodec")] if check_hwcodec_config() { - return MY_DECODER_STATE.lock().unwrap().clone(); + let mut state = MY_DECODER_STATE.lock().unwrap(); + state.perfer = Self::codec_preference(_id).into(); + state.clone() } else { return VideoCodecState { - ScoreVpx: SCORE_VPX, + score_vpx: SCORE_VPX, ..Default::default() }; } #[cfg(not(feature = "hwcodec"))] VideoCodecState { - ScoreVpx: SCORE_VPX, + score_vpx: SCORE_VPX, ..Default::default() } } @@ -237,9 +283,9 @@ impl Decoder { #[cfg(feature = "hwcodec")] { let mut state = MY_DECODER_STATE.lock().unwrap(); - state.ScoreVpx = SCORE_VPX; - state.ScoreH264 = decoder.hw.h264.as_ref().map_or(0, |d| d.info.score); - state.ScoreH265 = decoder.hw.h265.as_ref().map_or(0, |d| d.info.score); + state.score_vpx = SCORE_VPX; + state.score_h264 = decoder.hw.h264.as_ref().map_or(0, |d| d.info.score); + state.score_h265 = decoder.hw.h265.as_ref().map_or(0, |d| d.info.score); } decoder @@ -316,6 +362,23 @@ impl Decoder { } return Ok(ret); } + + #[cfg(feature = "hwcodec")] + fn codec_preference(id: &str) -> PerferCodec { + let codec = PeerConfig::load(id) + .options + .get("codec-preference") + .map_or("".to_owned(), |c| c.to_owned()); + if codec == "vp9" { + PerferCodec::VPX + } else if codec == "h264" { + PerferCodec::H264 + } else if codec == "h265" { + PerferCodec::H265 + } else { + PerferCodec::Auto + } + } } #[cfg(feature = "hwcodec")] diff --git a/src/client.rs b/src/client.rs index 28101896e..dbea079ea 100644 --- a/src/client.rs +++ b/src/client.rs @@ -784,6 +784,7 @@ pub struct LoginConfigHandler { pub conn_id: i32, features: Option<Features>, session_id: u64, + pub supported_encoding: Option<(bool, bool)>, } impl Deref for LoginConfigHandler { @@ -808,6 +809,7 @@ impl LoginConfigHandler { self.remember = !config.password.is_empty(); self.config = config; self.session_id = rand::random(); + self.supported_encoding = None; } pub fn should_auto_login(&self) -> String { @@ -958,8 +960,7 @@ impl LoginConfigHandler { msg.disable_clipboard = BoolOption::Yes.into(); n += 1; } - // TODO: add option - let state = Decoder::video_codec_state(); + let state = Decoder::video_codec_state(&self.id); msg.video_codec_state = hbb_common::protobuf::MessageField::some(state); n += 1; @@ -1111,6 +1112,10 @@ impl LoginConfigHandler { self.conn_id = pi.conn_id; // no matter if change, for update file time self.save_config(config); + #[cfg(feature = "hwcodec")] + { + self.supported_encoding = Some((pi.encoding.h264, pi.encoding.h265)); + } } pub fn get_remote_dir(&self) -> String { @@ -1163,6 +1168,18 @@ impl LoginConfigHandler { msg_out.set_login_request(lr); msg_out } + + pub fn change_prefer_codec(&self) -> Message { + let state = scrap::codec::Decoder::video_codec_state(&self.id); + let mut misc = Misc::new(); + misc.set_option(OptionMessage { + video_codec_state: hbb_common::protobuf::MessageField::some(state), + ..Default::default() + }); + let mut msg_out = Message::new(); + msg_out.set_misc(misc); + msg_out + } } pub enum MediaData { diff --git a/src/server/connection.rs b/src/server/connection.rs index 869df4196..8b0b3f192 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -635,6 +635,16 @@ impl Connection { pi.hostname = MOBILE_INFO2.lock().unwrap().clone(); pi.platform = "Android".into(); } + #[cfg(feature = "hwcodec")] + { + let (h264, h265) = scrap::codec::Encoder::supported_encoding(); + pi.encoding = Some(SupportedEncoding { + h264, + h265, + ..Default::default() + }) + .into(); + } if self.port_forward_socket.is_some() { let mut msg_out = Message::new(); @@ -1351,6 +1361,12 @@ impl Connection { } } } + if let Some(q) = o.video_codec_state.clone().take() { + scrap::codec::Encoder::update_video_encoder( + self.inner.id(), + scrap::codec::EncoderUpdate::State(q), + ); + } } async fn on_close(&mut self, reason: &str, lock: bool) { diff --git a/src/server/video_service.rs b/src/server/video_service.rs index e6ea713a1..e639fbe24 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -483,6 +483,7 @@ fn run(sp: GenericService) -> ResultType<()> { let mut try_gdi = 1; #[cfg(windows)] log::info!("gdi: {}", c.is_gdi()); + let codec_name = Encoder::current_hw_encoder_name(); while sp.ok() { #[cfg(windows)] @@ -508,6 +509,9 @@ fn run(sp: GenericService) -> ResultType<()> { *SWITCH.lock().unwrap() = true; bail!("SWITCH"); } + if codec_name != Encoder::current_hw_encoder_name() { + bail!("SWITCH"); + } check_privacy_mode_changed(&sp, c.privacy_mode_id)?; #[cfg(windows)] { diff --git a/src/ui/header.css b/src/ui/header.css index fb7b57fda..e248b46d5 100644 --- a/src/ui/header.css +++ b/src/ui/header.css @@ -94,3 +94,4 @@ span#fullscreen.active { button:disabled { opacity: 0.3; } + diff --git a/src/ui/header.tis b/src/ui/header.tis index 35a132c90..6ee3cad01 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -145,6 +145,9 @@ class Header: Reactor.Component { } function renderDisplayPop() { + var codecs = handler.supported_hwcodec(); + var show_codec = handler.has_hwcodec() && (codecs[0] || codecs[1]); + return <popup> <menu.context #display-options> <li #adjust-window style="display:none">{translate('Adjust Window')}</li> @@ -157,6 +160,13 @@ class Header: Reactor.Component { <li #balanced type="image-quality"><span>{svg_checkmark}</span>{translate('Balanced')}</li> <li #low type="image-quality"><span>{svg_checkmark}</span>{translate('Optimize reaction time')}</li> <li #custom type="image-quality"><span>{svg_checkmark}</span>{translate('Custom')}</li> + {show_codec ? <div> + <div .separator /> + <li #auto type="codec-preference"><span>{svg_checkmark}</span>Auto</li> + <li #vp9 type="codec-preference"><span>{svg_checkmark}</span>VP9</li> + {codecs[0] ? <li #h264 type="codec-preference"><span>{svg_checkmark}</span>H264</li> : ""} + {codecs[1] ? <li #h265 type="codec-preference"><span>{svg_checkmark}</span>H265</li> : ""} + </div> : ""} <div .separator /> <li #show-remote-cursor .toggle-option><span>{svg_checkmark}</span>{translate('Show remote cursor')}</li> <li #show-quality-monitor .toggle-option><span>{svg_checkmark}</span>{translate('Show quality monitor')}</li> @@ -311,7 +321,7 @@ class Header: Reactor.Component { } } - event click $(menu#display-options>li) (_, me) { + event click $(menu#display-options li) (_, me) { if (me.id == "custom") { handle_custom_image_quality(); } else if (me.id == "privacy-mode") { @@ -328,6 +338,9 @@ class Header: Reactor.Component { } else if (type == "view-style") { handler.save_view_style(me.id); adaptDisplay(); + } else if (type == "codec-preference") { + handler.set_option("codec-preference", me.id); + handler.change_prefer_codec(); } toggleMenuState(); } @@ -355,7 +368,10 @@ function toggleMenuState() { var s = handler.get_view_style(); if (!s) s = "original"; values.push(s); - for (var el in $$(menu#display-options>li)) { + var c = handler.get_option("codec-preference"); + if (!c) c = "auto"; + values.push(c); + for (var el in $$(menu#display-options li)) { el.attributes.toggleClass("selected", values.indexOf(el.id) >= 0); } for (var id in ["show-remote-cursor", "show-quality-monitor", "disable-audio", "enable-file-transfer", "disable-clipboard", "lock-after-session-end"]) { diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 4d941e1f8..a738d7f8b 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -231,6 +231,9 @@ impl sciter::EventHandler for Handler { fn get_remember(); fn peer_platform(); fn set_write_override(i32, i32, bool, bool, bool); + fn has_hwcodec(); + fn supported_hwcodec(); + fn change_prefer_codec(); } } @@ -595,6 +598,42 @@ impl Handler { true } + fn has_hwcodec(&self) -> bool { + #[cfg(not(feature = "hwcodec"))] + return false; + #[cfg(feature = "hwcodec")] + return true; + } + + fn supported_hwcodec(&self) -> Value { + #[cfg(feature = "hwcodec")] + { + let mut v = Value::array(0); + let decoder = scrap::codec::Decoder::video_codec_state(&self.id); + let mut h264 = decoder.score_h264 > 0; + let mut h265 = decoder.score_h265 > 0; + if let Some((encoding_264, encoding_265)) = self.lc.read().unwrap().supported_encoding { + h264 = h264 && encoding_264; + h265 = h265 && encoding_265; + } + v.push(h264); + v.push(h265); + v + } + #[cfg(not(feature = "hwcodec"))] + { + let mut v = Value::array(0); + v.push(false); + v.push(false); + v + } + } + + fn change_prefer_codec(&self) { + let msg = self.lc.write().unwrap().change_prefer_codec(); + self.send(Data::Message(msg)); + } + fn t(&self, name: String) -> String { crate::client::translate(name) } From 23deae0e523557e9bfbb8fe0e48322a9f7505bd7 Mon Sep 17 00:00:00 2001 From: 21pages <pages21@163.com> Date: Tue, 19 Jul 2022 18:14:34 +0800 Subject: [PATCH 4/4] hwcodec: remove bad MY_DECODER_STATE When reset, the new of the decoder will be after it's drop Signed-off-by: 21pages <pages21@163.com> --- libs/scrap/src/common/codec.rs | 36 ++++++++------------------------ libs/scrap/src/common/hwcodec.rs | 2 +- 2 files changed, 10 insertions(+), 28 deletions(-) diff --git a/libs/scrap/src/common/codec.rs b/libs/scrap/src/common/codec.rs index 1a18a79c3..f0bd1c5f7 100644 --- a/libs/scrap/src/common/codec.rs +++ b/libs/scrap/src/common/codec.rs @@ -25,7 +25,6 @@ use hbb_common::{ #[cfg(feature = "hwcodec")] lazy_static::lazy_static! { static ref PEER_DECODER_STATES: Arc<Mutex<HashMap<i32, VideoCodecState>>> = Default::default(); - static ref MY_DECODER_STATE: Arc<Mutex<VideoCodecState>> = Default::default(); } const SCORE_VPX: i32 = 90; @@ -238,25 +237,18 @@ impl Encoder { } } -#[cfg(feature = "hwcodec")] -impl Drop for Decoder { - fn drop(&mut self) { - *MY_DECODER_STATE.lock().unwrap() = VideoCodecState { - score_vpx: SCORE_VPX, - ..Default::default() - }; - } -} - impl Decoder { pub fn video_codec_state(_id: &str) -> VideoCodecState { - // video_codec_state is mainted by creation and destruction of Decoder. - // It has been ensured to use after Decoder's creation. #[cfg(feature = "hwcodec")] if check_hwcodec_config() { - let mut state = MY_DECODER_STATE.lock().unwrap(); - state.perfer = Self::codec_preference(_id).into(); - state.clone() + let best = HwDecoder::best(); + VideoCodecState { + score_vpx: SCORE_VPX, + score_h264: best.h264.map_or(0, |c| c.score), + score_h265: best.h265.map_or(0, |c| c.score), + perfer: Self::codec_preference(_id).into(), + ..Default::default() + } } else { return VideoCodecState { score_vpx: SCORE_VPX, @@ -272,23 +264,13 @@ impl Decoder { pub fn new(config: DecoderCfg) -> Decoder { let vpx = VpxDecoder::new(config.vpx).unwrap(); - let decoder = Decoder { + Decoder { vpx, #[cfg(feature = "hwcodec")] hw: HwDecoder::new_decoders(), #[cfg(feature = "hwcodec")] i420: vec![], - }; - - #[cfg(feature = "hwcodec")] - { - let mut state = MY_DECODER_STATE.lock().unwrap(); - state.score_vpx = SCORE_VPX; - state.score_h264 = decoder.hw.h264.as_ref().map_or(0, |d| d.info.score); - state.score_h265 = decoder.hw.h265.as_ref().map_or(0, |d| d.info.score); } - - decoder } pub fn handle_video_frame( diff --git a/libs/scrap/src/common/hwcodec.rs b/libs/scrap/src/common/hwcodec.rs index c2c853956..c065d811d 100644 --- a/libs/scrap/src/common/hwcodec.rs +++ b/libs/scrap/src/common/hwcodec.rs @@ -178,7 +178,7 @@ pub struct HwDecoders { } impl HwDecoder { - fn best() -> CodecInfos { + pub fn best() -> CodecInfos { get_config(CFG_KEY_DECODER).unwrap_or(CodecInfos { h264: None, h265: None,