[feat] allow pinch zoom outside the remote image on mobile

This commit is contained in:
dijunkun
2026-10-18 22:09:35 +08:00
parent acd56dfc29
commit 36f7213162
4 changed files with 12 additions and 5 deletions
+1 -1
View File
@@ -7,7 +7,7 @@
- 官方或自托管 WSS 信令服务器;授权后进入前台即自动注册或登录本机身份,首页实时显示连接、重连及失败状态。发起桌面连接时复用该身份;切换服务器会重新登录,撤回授权会关闭连接。已有远程会话优先通过系统画中画保持连接,画中画不可用时由带连接通知的前台服务兜底;首页空闲信令在后台关闭。
- 优先使用 MediaCodec 硬件解码 H.264 / AV1,硬件缓冲区直接提交 Surface;设备无对应硬件、分辨率超限或运行失败时,按原编码格式回退到 OpenH264 / dav1d。软件输出由独立显示线程将 NV12 转 RGBA;只保留最新待显示帧。MiniRTC 原有的通用压缩帧解码队列保持不变。
- 48 kHz 单声道 PCM 播放,支持静音,音频队列有容量限制。
- 相对鼠标(触控板)和绝对位置模式;单指点击、绝对模式按住拖动、长按右键,以及外接键盘、鼠标。画面手势与 iOS 一致:双指捏合以触点为中心缩放(1–10 倍),放大后双指拖动画面,原始比例下双指上下滑动控制滚轮。相对鼠标移到可见画面的上、下边缘后继续向外移动,会反向上下平移画面,不触发左右平移;未放大时和按住虚拟鼠标键拖动时同样生效,停止移动即停止平移,抬手保留位置。移动到极限时,画面可见部分至少占当前显示区域高度的 20%,与缩放倍数无关;整幅画面高度不足该比例时完整保留,范围计入上下黑边;缩放结束及缩回 1 倍时保留平移位置,仅在超过可见边界时限制位置;切换显示器恢复居中。
- 相对鼠标(触控板)和绝对位置模式;单指点击、绝对模式按住拖动、长按右键,以及外接键盘、鼠标。画面手势与 iOS 一致:双指可在整个触控区域(包括画面外黑边)捏合,以触点为中心缩放(1–10 倍),放大后双指拖动画面,原始比例下双指上下滑动控制滚轮。相对鼠标移到可见画面的上、下边缘后继续向外移动,会反向上下平移画面,不触发左右平移;未放大时和按住虚拟鼠标键拖动时同样生效,停止移动即停止平移,抬手保留位置。移动到极限时,画面可见部分至少占当前显示区域高度的 20%,与缩放倍数无关;整幅画面高度不足该比例时完整保留,范围计入上下黑边;缩放结束及缩回 1 倍时保留平移位置,仅在超过可见边界时限制位置;切换显示器恢复居中。
- 横竖屏等比例显示、远端多显示器切换。与 iOS 一致的画面质量(低/中/高)、采集帧率(30/60 fps)、画面偏好;支持能力检测、电脑端确认、失败/5 秒超时回退及连续操作的应答排序。
- 根据常见移动设备解码能力和屏幕尺寸设置起始参数:显示长边不超过 1280 像素时默认低画质(最高 720p)/30 fps,其余默认中画质(最高 1080p)/30 fps。用户保存的画面偏好不变,仍可手动选择高画质或 60 fps。
- 网络状态按视频、音频、数据、合计展示收发速率和丢包率,并显示实际提交帧率、分辨率、画面延时、平滑 RTT、P2P/TURN 和实际 SRTP 状态。每秒刷新,过期或无效测量显示“—”。
@@ -78,7 +78,8 @@ final class RemoteVideoView extends FrameLayout implements SurfaceHolder.Callbac
scales = new ScaleGestureDetector(context, new ScaleGestureDetector.SimpleOnScaleGestureListener() {
@Override public boolean onScaleBegin(ScaleGestureDetector detector) {
if (mouseInput.hasVirtualButton()) return false;
if (!viewport.contains(detector.getFocusX(), detector.getFocusY())) return false;
// Pinches can start anywhere on the touch surface, including letterboxing.
if (viewport.width() <= 0 || viewport.height() <= 0) return false;
viewportGesture = true;
viewportPanActive = true;
return true;
@@ -375,7 +375,8 @@ final class RemoteTouchSurface: UIView, UIGestureRecognizerDelegate {
let focus = recognizer.location(in: self)
switch recognizer.state {
case .began:
pinchActive = renderRect()?.contains(focus) == true
// The full touch surface accepts pinches, including letterboxing.
pinchActive = bounds.contains(focus) && renderRect() != nil
recognizer.scale = 1
case .changed:
guard pinchActive else { return }
@@ -414,13 +415,13 @@ final class RemoteTouchSurface: UIView, UIGestureRecognizerDelegate {
if viewportScale > 1.001 || pinchActive {
switch recognizer.state {
case .began:
viewportPanActive = renderRect()?.contains(
viewportPanActive = pinchActive || renderRect()?.contains(
recognizer.location(in: self)
) == true
recognizer.setTranslation(.zero, in: self)
case .changed:
if !viewportPanActive {
viewportPanActive = renderRect()?.contains(
viewportPanActive = pinchActive || renderRect()?.contains(
recognizer.location(in: self)
) == true
}
+5
View File
@@ -431,6 +431,11 @@ Old previews saved without this choice are removed on upgrade.
## Pointer edge panning
Pinch-to-zoom can start anywhere on the touch surface, including black margins
outside the image, in either pointer mode. A pinch also follows the fingers'
midpoint movement when it starts in a margin. Held virtual mouse buttons still
prevent viewport gestures.
In relative mouse mode, continuing to move outward at the top or bottom of the
visible image pans the video vertically in the opposite direction. Moving
down-left at the bottom edge pans upward without shifting the video horizontally;