From c61fa71a7066e4320ef8f0c73b3188f3223961f0 Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 7 Aug 2023 13:31:11 +0800 Subject: [PATCH 1/3] Revert "hide recording button if using av1" This reverts commit c2023e8ca3d857180269dd05df984a684c81b4b1. --- .../lib/desktop/widgets/remote_toolbar.dart | 24 ++++++------------- libs/scrap/src/common/record.rs | 3 --- src/ui/header.tis | 2 +- src/ui/remote.tis | 1 - 4 files changed, 8 insertions(+), 22 deletions(-) diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 88638f0b187..240add91966 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -478,7 +478,7 @@ class _RemoteToolbarState extends State { toolbarItems.add(_ChatMenu(id: widget.id, ffi: widget.ffi)); toolbarItems.add(_VoiceCallMenu(id: widget.id, ffi: widget.ffi)); } - toolbarItems.add(_RecordMenu(ffi: widget.ffi)); + toolbarItems.add(_RecordMenu()); toolbarItems.add(_CloseMenu(id: widget.id, ffi: widget.ffi)); return Column( mainAxisSize: MainAxisSize.min, @@ -1370,12 +1370,11 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { } for (final r in resolutions) { - if (r.width == _localResolution!.width && - r.height == _localResolution!.height) { + if (r.width == _localResolution!.width && r.height == _localResolution!.height) { return r; } } - + return null; } @@ -1646,17 +1645,16 @@ class _VoiceCallMenu extends StatelessWidget { } class _RecordMenu extends StatelessWidget { - final FFI ffi; - const _RecordMenu({Key? key, required this.ffi}) : super(key: key); + const _RecordMenu({Key? key}) : super(key: key); @override Widget build(BuildContext context) { - var ffiModel = Provider.of(context); + var ffi = Provider.of(context); var recordingModel = Provider.of(context); final visible = - recordingModel.start || ffiModel.permissions['recording'] != false; + recordingModel.start || ffi.permissions['recording'] != false; if (!visible) return Offstage(); - final menuButton = _IconMenuButton( + return _IconMenuButton( assetName: 'assets/rec.svg', tooltip: recordingModel.start ? 'Stop session recording' @@ -1669,14 +1667,6 @@ class _RecordMenu extends StatelessWidget { ? _ToolbarTheme.hoverRedColor : _ToolbarTheme.hoverBlueColor, ); - return ChangeNotifierProvider.value( - value: ffi.qualityMonitorModel, - child: Consumer( - builder: (context, model, child) => Offstage( - // If already started, AV1->Hidden/Stop, Other->Start, same as actual - offstage: model.data.codecFormat == 'AV1', - child: menuButton, - ))); } } diff --git a/libs/scrap/src/common/record.rs b/libs/scrap/src/common/record.rs index 2893cbf1860..9de70ae1499 100644 --- a/libs/scrap/src/common/record.rs +++ b/libs/scrap/src/common/record.rs @@ -101,9 +101,6 @@ impl DerefMut for Recorder { impl Recorder { pub fn new(mut ctx: RecorderContext) -> ResultType { - if ctx.format == CodecFormat::AV1 { - bail!("not support av1 recording"); - } ctx.set_filename()?; let recorder = match ctx.format { CodecFormat::VP8 | CodecFormat::VP9 => Recorder { diff --git a/src/ui/header.tis b/src/ui/header.tis index a6cb2645df2..8385142345f 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -144,7 +144,7 @@ class Header: Reactor.Component { {svg_action} {svg_display} {svg_keyboard} - {recording_enabled && qualityMonitorData[4] != "AV1" ? {recording ? svg_recording_on : svg_recording_off} : ""} + {recording_enabled ? {recording ? svg_recording_on : svg_recording_off} : ""} {this.renderKeyboardPop()} {this.renderDisplayPop()} {this.renderActionPop()} diff --git a/src/ui/remote.tis b/src/ui/remote.tis index 022d436686d..a99cd188bf5 100644 --- a/src/ui/remote.tis +++ b/src/ui/remote.tis @@ -522,7 +522,6 @@ handler.updateQualityStatus = function(speed, fps, delay, bitrate, codec_format) bitrate ? qualityMonitorData[3] = bitrate:null; codec_format ? qualityMonitorData[4] = codec_format:null; qualityMonitor.update(); - if (codec_format) header.update(); } handler.setPermission = function(name, enabled) { From 510cffb305f17088059744696e21d662415fa8dd Mon Sep 17 00:00:00 2001 From: 21pages Date: Mon, 7 Aug 2023 15:18:34 +0800 Subject: [PATCH 2/3] av1 record, set zero codec private Signed-off-by: 21pages --- Cargo.lock | 10 ++++------ libs/scrap/Cargo.toml | 2 +- libs/scrap/src/common/record.rs | 29 +++++++++++++++++++++++++---- src/server/video_service.rs | 6 ++---- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 519476e9351..b606cdb218a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6712,18 +6712,16 @@ dependencies = [ [[package]] name = "webm" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecb047148a12ef1fd8ab26302bca7e82036f005c3073b48e17cc1b44ec577136" +version = "1.1.0" +source = "git+https://github.com/21pages/rust-webm#d2c4d3ac133c7b0e4c0f656da710b48391981e64" dependencies = [ "webm-sys", ] [[package]] name = "webm-sys" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ded6ec82ccf51fe265b0b2b1579cac839574ed910c17baac58e807f8a9de7f3" +version = "1.0.4" +source = "git+https://github.com/21pages/rust-webm#d2c4d3ac133c7b0e4c0f656da710b48391981e64" dependencies = [ "cc", ] diff --git a/libs/scrap/Cargo.toml b/libs/scrap/Cargo.toml index 4956403a9bf..d8b176597aa 100644 --- a/libs/scrap/Cargo.toml +++ b/libs/scrap/Cargo.toml @@ -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" diff --git a/libs/scrap/src/common/record.rs b/libs/scrap/src/common/record.rs index 9de70ae1499..09d5efd3ff4 100644 --- a/libs/scrap/src/common/record.rs +++ b/libs/scrap/src/common/record.rs @@ -51,7 +51,10 @@ impl RecorderContext { + &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 { + + if self.format == CodecFormat::VP9 + || self.format == CodecFormat::VP8 + || self.format == CodecFormat::AV1 + { ".webm" } else { ".mp4" @@ -103,7 +106,7 @@ impl Recorder { pub fn new(mut ctx: RecorderContext) -> ResultType { 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, }, @@ -122,7 +125,9 @@ 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"))] @@ -161,6 +166,15 @@ impl Recorder { } vp9s.frames.iter().map(|f| self.write_video(f)).count(); } + video_frame::Union::Av1s(av1s) => { + if self.ctx.format != CodecFormat::AV1 { + self.change(RecorderContext { + format: CodecFormat::AV1, + ..self.ctx.clone() + })?; + } + av1s.frames.iter().map(|f| self.write_video(f)).count(); + } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { if self.ctx.format != CodecFormat::H264 { @@ -227,10 +241,17 @@ 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]; + webm.set_codec_private(vt.track_number(), &codec_private); + } Ok(WebmRecorder { vt, webm: Some(webm), diff --git a/src/server/video_service.rs b/src/server/video_service.rs index 074b041c926..c2a9b611370 100644 --- a/src/server/video_service.rs +++ b/src/server/video_service.rs @@ -436,8 +436,7 @@ fn run(vs: VideoService) -> ResultType<()> { log::info!("init quality={:?}, abr enabled:{}", quality, abr); let codec_name = Encoder::negotiated_codec(); let recorder = get_recorder(c.width, c.height, &codec_name); - let last_recording = - (recorder.lock().unwrap().is_some() || video_qos.record()) && codec_name != CodecName::AV1; + let last_recording = recorder.lock().unwrap().is_some() || video_qos.record(); drop(video_qos); let encoder_cfg = get_encoder_config(&c, quality, last_recording); @@ -479,8 +478,7 @@ fn run(vs: VideoService) -> ResultType<()> { allow_err!(encoder.set_quality(quality)); video_qos.store_bitrate(encoder.bitrate()); } - let recording = (recorder.lock().unwrap().is_some() || video_qos.record()) - && codec_name != CodecName::AV1; + let recording = recorder.lock().unwrap().is_some() || video_qos.record(); if recording != last_recording { bail!("SWITCH"); } From 7a5bc864fa357c9b2729868ee525d5c56fdaf216 Mon Sep 17 00:00:00 2001 From: 21pages Date: Wed, 18 Oct 2023 22:39:28 +0800 Subject: [PATCH 3/3] fix client side record Signed-off-by: 21pages --- flutter/lib/common/widgets/toolbar.dart | 5 +- flutter/lib/desktop/pages/remote_page.dart | 3 +- .../lib/desktop/widgets/remote_toolbar.dart | 8 +- flutter/lib/mobile/pages/remote_page.dart | 4 +- flutter/lib/models/model.dart | 77 +++++++++++-------- libs/scrap/src/common/record.rs | 48 +++++++++--- src/client.rs | 8 +- src/server/connection.rs | 3 +- src/ui/header.tis | 14 +++- src/ui/remote.rs | 6 ++ 10 files changed, 118 insertions(+), 58 deletions(-) diff --git a/flutter/lib/common/widgets/toolbar.dart b/flutter/lib/common/widgets/toolbar.dart index 757a03fec69..28b10785b49 100644 --- a/flutter/lib/common/widgets/toolbar.dart +++ b/flutter/lib/common/widgets/toolbar.dart @@ -224,11 +224,8 @@ List toolbarControls(BuildContext context, String id, FFI ffi) { )); } // record - var codecFormat = ffi.qualityMonitorModel.data.codecFormat; if (!isDesktop && - (ffi.recordingModel.start || - (perms["recording"] != false && - (codecFormat == "VP8" || codecFormat == "VP9")))) { + (ffi.recordingModel.start || (perms["recording"] != false))) { v.add(TTextMenu( child: Row( children: [ diff --git a/flutter/lib/desktop/pages/remote_page.dart b/flutter/lib/desktop/pages/remote_page.dart index abbb8785dd1..cb9cf49c9e6 100644 --- a/flutter/lib/desktop/pages/remote_page.dart +++ b/flutter/lib/desktop/pages/remote_page.dart @@ -677,7 +677,8 @@ class _ImagePaintState extends State { } else { final key = cache.updateGetKey(scale); if (!cursor.cachedKeys.contains(key)) { - debugPrint("Register custom cursor with key $key (${cache.hotx},${cache.hoty})"); + debugPrint( + "Register custom cursor with key $key (${cache.hotx},${cache.hoty})"); // [Safety] // It's ok to call async registerCursor in current synchronous context, // because activating the cursor is also an async call and will always diff --git a/flutter/lib/desktop/widgets/remote_toolbar.dart b/flutter/lib/desktop/widgets/remote_toolbar.dart index 240add91966..fd4fcc434ab 100644 --- a/flutter/lib/desktop/widgets/remote_toolbar.dart +++ b/flutter/lib/desktop/widgets/remote_toolbar.dart @@ -1370,11 +1370,12 @@ class _ResolutionsMenuState extends State<_ResolutionsMenu> { } for (final r in resolutions) { - if (r.width == _localResolution!.width && r.height == _localResolution!.height) { + if (r.width == _localResolution!.width && + r.height == _localResolution!.height) { return r; } } - + return null; } @@ -1652,7 +1653,8 @@ class _RecordMenu extends StatelessWidget { var ffi = Provider.of(context); var recordingModel = Provider.of(context); final visible = - recordingModel.start || ffi.permissions['recording'] != false; + (recordingModel.start || ffi.permissions['recording'] != false) && + ffi.pi.currentDisplay != kAllDisplayValue; if (!visible) return Offstage(); return _IconMenuButton( assetName: 'assets/rec.svg', diff --git a/flutter/lib/mobile/pages/remote_page.dart b/flutter/lib/mobile/pages/remote_page.dart index 24935501217..ff71c9347ec 100644 --- a/flutter/lib/mobile/pages/remote_page.dart +++ b/flutter/lib/mobile/pages/remote_page.dart @@ -763,7 +763,9 @@ void showOptions( children.add(InkWell( onTap: () { if (i == cur) return; - bind.sessionSwitchDisplay(sessionId: gFFI.sessionId, value: Int32List.fromList([i])); + gFFI.recordingModel.onClose(); + bind.sessionSwitchDisplay( + sessionId: gFFI.sessionId, value: Int32List.fromList([i])); gFFI.dialogManager.dismissAll(); }, child: Ink( diff --git a/flutter/lib/models/model.dart b/flutter/lib/models/model.dart index ce4cbe5230c..433d3c2d4ec 100644 --- a/flutter/lib/models/model.dart +++ b/flutter/lib/models/model.dart @@ -860,6 +860,8 @@ class FfiModel with ChangeNotifier { // Directly switch to the new display without waiting for the response. switchToNewDisplay(int display, SessionID sessionId, String peerId) { + // VideoHandler creation is upon when video frames are received, so either caching commands(don't know next width/height) or stopping recording when switching displays. + parent.target?.recordingModel.onClose(); // no need to wait for the response pi.currentDisplay = display; updateCurDisplay(sessionId); @@ -868,7 +870,6 @@ class FfiModel with ChangeNotifier { } catch (e) { // } - parent.target?.recordingModel.onSwitchDisplay(); } updateBlockInputState(Map evt, String peerId) { @@ -1850,56 +1851,66 @@ class RecordingModel with ChangeNotifier { int? width = parent.target?.canvasModel.getDisplayWidth(); int? height = parent.target?.canvasModel.getDisplayHeight(); if (sessionId == null || width == null || height == null) return; - final currentDisplay = parent.target?.ffiModel.pi.currentDisplay; - if (currentDisplay != kAllDisplayValue) { - bind.sessionRecordScreen( - sessionId: sessionId, - start: true, - display: currentDisplay!, - width: width, - height: height); - } + final pi = parent.target?.ffiModel.pi; + if (pi == null) return; + final currentDisplay = pi.currentDisplay; + if (currentDisplay == kAllDisplayValue) return; + bind.sessionRecordScreen( + sessionId: sessionId, + start: true, + display: currentDisplay, + width: width, + height: height); } toggle() async { if (isIOS) return; final sessionId = parent.target?.sessionId; if (sessionId == null) return; + final pi = parent.target?.ffiModel.pi; + if (pi == null) return; + final currentDisplay = pi.currentDisplay; + if (currentDisplay == kAllDisplayValue) return; _start = !_start; notifyListeners(); - await bind.sessionRecordStatus(sessionId: sessionId, status: _start); + await _sendStatusMessage(sessionId, pi, _start); if (_start) { - final pi = parent.target?.ffiModel.pi; - if (pi != null) { - sessionRefreshVideo(sessionId, pi); + sessionRefreshVideo(sessionId, pi); + if (versionCmp(pi.version, '1.2.4') >= 0) { + // will not receive SwitchDisplay since 1.2.4 + onSwitchDisplay(); } } else { - final currentDisplay = parent.target?.ffiModel.pi.currentDisplay; - if (currentDisplay != kAllDisplayValue) { - bind.sessionRecordScreen( - sessionId: sessionId, - start: false, - display: currentDisplay!, - width: 0, - height: 0); - } + bind.sessionRecordScreen( + sessionId: sessionId, + start: false, + display: currentDisplay, + width: 0, + height: 0); } } - onClose() { + onClose() async { if (isIOS) return; final sessionId = parent.target?.sessionId; if (sessionId == null) return; + if (!_start) return; _start = false; - final currentDisplay = parent.target?.ffiModel.pi.currentDisplay; - if (currentDisplay != kAllDisplayValue) { - bind.sessionRecordScreen( - sessionId: sessionId, - start: false, - display: currentDisplay!, - width: 0, - height: 0); - } + final pi = parent.target?.ffiModel.pi; + if (pi == null) return; + final currentDisplay = pi.currentDisplay; + if (currentDisplay == kAllDisplayValue) return; + await _sendStatusMessage(sessionId, pi, false); + bind.sessionRecordScreen( + sessionId: sessionId, + start: false, + display: currentDisplay, + width: 0, + height: 0); + } + + _sendStatusMessage(SessionID sessionId, PeerInfo pi, bool status) async { + await bind.sessionRecordStatus(sessionId: sessionId, status: status); } } diff --git a/libs/scrap/src/common/record.rs b/libs/scrap/src/common/record.rs index 09d5efd3ff4..3c0ee2a95fc 100644 --- a/libs/scrap/src/common/record.rs +++ b/libs/scrap/src/common/record.rs @@ -49,8 +49,8 @@ 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() + + &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 @@ -86,6 +86,7 @@ pub enum RecordState { pub struct Recorder { pub inner: Box, ctx: RecorderContext, + pts: Option, } impl Deref for Recorder { @@ -109,11 +110,13 @@ impl 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"), @@ -134,6 +137,7 @@ impl Recorder { _ => bail!("unsupported codec type"), }; self.ctx = ctx; + self.pts = None; self.send_state(RecordState::NewFile(self.ctx.filename.clone())); Ok(()) } @@ -155,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 { @@ -164,7 +171,10 @@ 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 { @@ -173,7 +183,10 @@ impl Recorder { ..self.ctx.clone() })?; } - av1s.frames.iter().map(|f| self.write_video(f)).count(); + for f in av1s.frames.iter() { + self.check_pts(f.pts)?; + self.write_video(f); + } } #[cfg(feature = "hwcodec")] video_frame::Union::H264s(h264s) => { @@ -183,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")] @@ -195,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"), @@ -205,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)); } @@ -250,7 +276,9 @@ impl RecorderApi for WebmRecorder { if ctx.format == CodecFormat::AV1 { // [129, 8, 12, 0] in 3.6.0, but zero works let codec_private = vec![0, 0, 0, 0]; - webm.set_codec_private(vt.track_number(), &codec_private); + if !webm.set_codec_private(vt.track_number(), &codec_private) { + bail!("Failed to set codec private"); + } } Ok(WebmRecorder { vt, diff --git a/src/client.rs b/src/client.rs index da4559c9930..5a3c35467ba 100644 --- a/src/client.rs +++ b/src/client.rs @@ -999,16 +999,19 @@ pub struct VideoHandler { pub rgb: ImageRgb, recorder: Arc>>, record: bool, + _display: usize, // useful for debug } impl VideoHandler { /// Create a new video handler. - pub fn new() -> Self { + pub fn new(_display: usize) -> Self { + log::info!("new video handler for display #{_display}"); VideoHandler { decoder: Decoder::new(), rgb: ImageRgb::new(ImageFormat::ARGB, crate::DST_STRIDE_RGBA), recorder: Default::default(), record: false, + _display, } } @@ -1900,7 +1903,7 @@ where if handler_controller_map.len() <= display { for _i in handler_controller_map.len()..=display { handler_controller_map.push(VideoHandlerController { - handler: VideoHandler::new(), + handler: VideoHandler::new(_i), count: 0, duration: std::time::Duration::ZERO, skip_beginning: 0, @@ -1960,6 +1963,7 @@ where } } MediaData::RecordScreen(start, display, w, h, id) => { + log::info!("record screen command: start:{start}, display:{display}"); if handler_controller_map.len() == 1 { // Compatible with the sciter version(single ui session). // For the sciter version, there're no multi-ui-sessions for one connection. diff --git a/src/server/connection.rs b/src/server/connection.rs index 1b005ea5e76..86096288d5e 100644 --- a/src/server/connection.rs +++ b/src/server/connection.rs @@ -2104,7 +2104,7 @@ impl Connection { display, video_service::OPTION_REFRESH, super::service::SERVICE_OPTION_VALUE_TRUE, - ) + ); }); } @@ -2145,6 +2145,7 @@ impl Connection { // Send display changed message. // For compatibility with old versions ( < 1.2.4 ). + // sciter need it in new version if let Some(msg_out) = video_service::make_display_changed_msg(self.display_idx, None) { self.send(msg_out).await; } diff --git a/src/ui/header.tis b/src/ui/header.tis index 8385142345f..029dd2629ab 100644 --- a/src/ui/header.tis +++ b/src/ui/header.tis @@ -299,14 +299,22 @@ class Header: Reactor.Component { header.update(); handler.record_status(recording); // 0 is just a dummy value. It will be ignored by the handler. - if (recording) + if (recording) { handler.refresh_video(0); - else - handler.record_screen(false, 0, display_width, display_height); + if (handler.version_cmp(pi.version, '1.2.4') >= 0) handler.record_screen(recording, pi.current_display, display_width, display_height); + } + else { + handler.record_screen(recording, pi.current_display, display_width, display_height); + } } event click $(#screen) (_, me) { if (pi.current_display == me.index) return; + if (recording) { + recording = false; + handler.record_screen(false, pi.current_display, display_width, display_height); + handler.record_status(false); + } handler.switch_display(me.index); } diff --git a/src/ui/remote.rs b/src/ui/remote.rs index 71ef8f84db2..bb01ef9f4bb 100644 --- a/src/ui/remote.rs +++ b/src/ui/remote.rs @@ -243,6 +243,7 @@ impl InvokeUiSession for SciterHandler { pi_sciter.set_item("sas_enabled", pi.sas_enabled); pi_sciter.set_item("displays", Self::make_displays_array(&pi.displays)); pi_sciter.set_item("current_display", pi.current_display); + pi_sciter.set_item("version", pi.version.clone()); self.call("updatePi", &make_args!(pi_sciter)); } @@ -469,6 +470,7 @@ impl sciter::EventHandler for SciterSession { fn restart_remote_device(); fn request_voice_call(); fn close_voice_call(); + fn version_cmp(String, String); } } @@ -757,6 +759,10 @@ impl SciterSession { log::error!("Failed to spawn IP tunneling: {}", err); } } + + fn version_cmp(&self, v1: String, v2: String) -> i32 { + (hbb_common::get_version_number(&v1) - hbb_common::get_version_number(&v2)) as i32 + } } pub fn make_fd(id: i32, entries: &Vec, only_count: bool) -> Value {