fix black screen issue when controlling the second screen on versions that lack multiple display support while using vram decoding (#7836)

* avoid create unnecessary video decoder

Signed-off-by: 21pages <pages21@163.com>

* controlled side uses the most frequent selected codec

Signed-off-by: 21pages <pages21@163.com>

* fix black screen when control old version's second screen

For versions that do not support multiple displays, the display parameter is always 0, need set type of current display

Signed-off-by: 21pages <pages21@163.com>

---------

Signed-off-by: 21pages <pages21@163.com>
This commit is contained in:
21pages 2024-04-26 19:42:47 +08:00 committed by GitHub
parent 105a758914
commit 2626dcbc5f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 69 additions and 53 deletions

4
Cargo.lock generated
View File

@ -3038,8 +3038,8 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]] [[package]]
name = "hwcodec" name = "hwcodec"
version = "0.4.1" version = "0.4.2"
source = "git+https://github.com/21pages/hwcodec#17870c015a3f371339a91c5305d1e920bd8284e3" source = "git+https://github.com/21pages/hwcodec#b8ed1e869b3456af6a5f77b7617f861d9a99dcdd"
dependencies = [ dependencies = [
"bindgen 0.59.2", "bindgen 0.59.2",
"cc", "cc",

View File

@ -1,5 +1,6 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_gpu_texture_renderer/flutter_gpu_texture_renderer.dart'; import 'package:flutter_gpu_texture_renderer/flutter_gpu_texture_renderer.dart';
import 'package:flutter_hbb/common/shared_state.dart';
import 'package:flutter_hbb/consts.dart'; import 'package:flutter_hbb/consts.dart';
import 'package:flutter_hbb/models/model.dart'; import 'package:flutter_hbb/models/model.dart';
import 'package:get/get.dart'; import 'package:get/get.dart';
@ -152,40 +153,36 @@ class TextureModel {
TextureModel(this.parent); TextureModel(this.parent);
setTextureType({required int display, required bool gpuTexture}) { setTextureType({required int display, required bool gpuTexture}) {
debugPrint("setTextureType: display:$display, isGpuTexture:$gpuTexture"); debugPrint("setTextureType: display=$display, isGpuTexture=$gpuTexture");
var texture = _control[display]; ensureControl(display);
if (texture == null) { _control[display]?.setTextureType(gpuTexture: gpuTexture);
texture = _Control(); // For versions that do not support multiple displays, the display parameter is always 0, need set type of current display
_control[display] = texture; final ffi = parent.target;
if (ffi == null) return;
if (!ffi.ffiModel.pi.isSupportMultiDisplay) {
final currentDisplay = CurrentDisplayState.find(ffi.id).value;
if (currentDisplay != display) {
debugPrint(
"setTextureType: currentDisplay=$currentDisplay, isGpuTexture=$gpuTexture");
ensureControl(currentDisplay);
_control[currentDisplay]?.setTextureType(gpuTexture: gpuTexture);
}
} }
texture.setTextureType(gpuTexture: gpuTexture);
} }
setRgbaTextureId({required int display, required int id}) { setRgbaTextureId({required int display, required int id}) {
var ctl = _control[display]; ensureControl(display);
if (ctl == null) { _control[display]?.setRgbaTextureId(id);
ctl = _Control();
_control[display] = ctl;
}
ctl.setRgbaTextureId(id);
} }
setGpuTextureId({required int display, required int id}) { setGpuTextureId({required int display, required int id}) {
var ctl = _control[display]; ensureControl(display);
if (ctl == null) { _control[display]?.setGpuTextureId(id);
ctl = _Control();
_control[display] = ctl;
}
ctl.setGpuTextureId(id);
} }
RxInt getTextureId(int display) { RxInt getTextureId(int display) {
var ctl = _control[display]; ensureControl(display);
if (ctl == null) { return _control[display]!.textureID;
ctl = _Control();
_control[display] = ctl;
}
return ctl.textureID;
} }
updateCurrentDisplay(int curDisplay) { updateCurrentDisplay(int curDisplay) {
@ -241,4 +238,12 @@ class TextureModel {
await texture.destroy(ffi); await texture.destroy(ffi);
} }
} }
ensureControl(int display) {
var ctl = _control[display];
if (ctl == null) {
ctl = _Control();
_control[display] = ctl;
}
}
} }

View File

@ -221,7 +221,6 @@ impl Encoder {
let h265_useable = let h265_useable =
_all_support_h265_decoding && (h265vram_encoding || h265hw_encoding.is_some()); _all_support_h265_decoding && (h265vram_encoding || h265hw_encoding.is_some());
let mut name = ENCODE_CODEC_NAME.lock().unwrap(); let mut name = ENCODE_CODEC_NAME.lock().unwrap();
let mut preference = PreferCodec::Auto;
let preferences: Vec<_> = decodings let preferences: Vec<_> = decodings
.iter() .iter()
.filter(|(_, s)| { .filter(|(_, s)| {
@ -233,9 +232,20 @@ impl Encoder {
}) })
.map(|(_, s)| s.prefer) .map(|(_, s)| s.prefer)
.collect(); .collect();
if preferences.len() > 0 && preferences.iter().all(|&p| p == preferences[0]) { // find the most frequent preference
preference = preferences[0].enum_value_or(PreferCodec::Auto); let mut counts = Vec::new();
for pref in &preferences {
match counts.iter_mut().find(|(p, _)| p == pref) {
Some((_, count)) => *count += 1,
None => counts.push((pref.clone(), 1)),
}
} }
let max_count = counts.iter().map(|(_, count)| *count).max().unwrap_or(0);
let (most_frequent, _) = counts
.into_iter()
.find(|(_, count)| *count == max_count)
.unwrap_or((PreferCodec::Auto.into(), 0));
let preference = most_frequent.enum_value_or(PreferCodec::Auto);
#[allow(unused_mut)] #[allow(unused_mut)]
let mut auto_codec = CodecName::VP9; let mut auto_codec = CodecName::VP9;

View File

@ -24,8 +24,6 @@ use hwcodec::{
}, },
}; };
const OUTPUT_SHARED_HANDLE: bool = false;
// https://www.reddit.com/r/buildapc/comments/d2m4ny/two_graphics_cards_two_monitors/ // https://www.reddit.com/r/buildapc/comments/d2m4ny/two_graphics_cards_two_monitors/
// https://www.reddit.com/r/techsupport/comments/t2v9u6/dual_monitor_setup_with_dual_gpu/ // https://www.reddit.com/r/techsupport/comments/t2v9u6/dual_monitor_setup_with_dual_gpu/
// https://cybersided.com/two-monitors-two-gpus/ // https://cybersided.com/two-monitors-two-gpus/
@ -331,8 +329,8 @@ impl VRamDecoder {
} }
pub fn new(format: CodecFormat, luid: Option<i64>) -> ResultType<Self> { pub fn new(format: CodecFormat, luid: Option<i64>) -> ResultType<Self> {
log::info!("try create {format:?} vram decoder, luid: {luid:?}");
let ctx = Self::try_get(format, luid).ok_or(anyhow!("Failed to get decode context"))?; let ctx = Self::try_get(format, luid).ok_or(anyhow!("Failed to get decode context"))?;
log::info!("try create vram decoder: {ctx:?}");
match Decoder::new(ctx) { match Decoder::new(ctx) {
Ok(decoder) => Ok(Self { decoder }), Ok(decoder) => Ok(Self { decoder }),
Err(_) => { Err(_) => {
@ -376,7 +374,7 @@ pub(crate) fn check_available_vram() -> String {
gop: MAX_GOP as _, gop: MAX_GOP as _,
}; };
let encoders = encode::available(d); let encoders = encode::available(d);
let decoders = decode::available(OUTPUT_SHARED_HANDLE); let decoders = decode::available();
let available = Available { let available = Available {
e: encoders, e: encoders,
d: decoders, d: decoders,

View File

@ -1580,7 +1580,10 @@ impl LoginConfigHandler {
/// ///
/// * `ignore_default` - If `true`, ignore the default value of the option. /// * `ignore_default` - If `true`, ignore the default value of the option.
fn get_option_message(&self, ignore_default: bool) -> Option<OptionMessage> { fn get_option_message(&self, ignore_default: bool) -> Option<OptionMessage> {
if self.conn_type.eq(&ConnType::PORT_FORWARD) || self.conn_type.eq(&ConnType::RDP) || self.conn_type.eq(&ConnType::FILE_TRANSFER) { if self.conn_type.eq(&ConnType::PORT_FORWARD)
|| self.conn_type.eq(&ConnType::RDP)
|| self.conn_type.eq(&ConnType::FILE_TRANSFER)
{
return None; return None;
} }
let mut msg = OptionMessage::new(); let mut msg = OptionMessage::new();
@ -2110,7 +2113,7 @@ where
std::thread::spawn(move || { std::thread::spawn(move || {
#[cfg(windows)] #[cfg(windows)]
sync_cpu_usage(); sync_cpu_usage();
let mut handler_controller_map = Vec::new(); let mut handler_controller_map = HashMap::new();
// let mut count = Vec::new(); // let mut count = Vec::new();
// let mut duration = std::time::Duration::ZERO; // let mut duration = std::time::Duration::ZERO;
// let mut skip_beginning = Vec::new(); // let mut skip_beginning = Vec::new();
@ -2141,17 +2144,18 @@ where
let display = vf.display as usize; let display = vf.display as usize;
let start = std::time::Instant::now(); let start = std::time::Instant::now();
let format = CodecFormat::from(&vf); let format = CodecFormat::from(&vf);
if handler_controller_map.len() <= display { if !handler_controller_map.contains_key(&display) {
for _i in handler_controller_map.len()..=display { handler_controller_map.insert(
handler_controller_map.push(VideoHandlerController { display,
handler: VideoHandler::new(format, _i), VideoHandlerController {
handler: VideoHandler::new(format, display),
count: 0, count: 0,
duration: std::time::Duration::ZERO, duration: std::time::Duration::ZERO,
skip_beginning: 0, skip_beginning: 0,
}); },
} );
} }
if let Some(handler_controller) = handler_controller_map.get_mut(display) { if let Some(handler_controller) = handler_controller_map.get_mut(&display) {
let mut pixelbuffer = true; let mut pixelbuffer = true;
let mut tmp_chroma = None; let mut tmp_chroma = None;
match handler_controller.handler.handle_frame( match handler_controller.handler.handle_frame(
@ -2219,7 +2223,7 @@ where
let mut should_update_supported = false; let mut should_update_supported = false;
handler_controller_map handler_controller_map
.iter() .iter()
.map(|h| { .map(|(_, h)| {
if !h.handler.decoder.valid() || h.handler.fail_counter >= MAX_DECODE_FAIL_COUNTER { if !h.handler.decoder.valid() || h.handler.fail_counter >= MAX_DECODE_FAIL_COUNTER {
let mut lc = session.lc.write().unwrap(); let mut lc = session.lc.write().unwrap();
let format = h.handler.decoder.format(); let format = h.handler.decoder.format();
@ -2238,21 +2242,20 @@ where
} }
} }
MediaData::Reset(display) => { MediaData::Reset(display) => {
if let Some(handler_controler) = handler_controller_map.get_mut(display) { if let Some(handler_controler) = handler_controller_map.get_mut(&display) {
handler_controler.handler.reset(None); handler_controler.handler.reset(None);
} }
} }
MediaData::RecordScreen(start, display, w, h, id) => { MediaData::RecordScreen(start, display, w, h, id) => {
log::info!("record screen command: start: {start}, display: {display}"); log::info!("record screen command: start: {start}, display: {display}");
if handler_controller_map.len() == 1 { // Compatible with the sciter version(single ui session).
// Compatible with the sciter version(single ui session). // For the sciter version, there're no multi-ui-sessions for one connection.
// For the sciter version, there're no multi-ui-sessions for one connection. // The display is always 0, video_handler_controllers.len() is always 1. So we use the first video handler.
// The display is always 0, video_handler_controllers.len() is always 1. So we use the first video handler. if let Some(handler_controler) = handler_controller_map.get_mut(&display) {
handler_controller_map[0] handler_controler.handler.record_screen(start, w, h, id);
.handler } else if handler_controller_map.len() == 1 {
.record_screen(start, w, h, id); if let Some(handler_controler) =
} else { handler_controller_map.values_mut().next()
if let Some(handler_controler) = handler_controller_map.get_mut(display)
{ {
handler_controler.handler.record_screen(start, w, h, id); handler_controler.handler.record_screen(start, w, h, id);
} }