From 474c7942a31ea92303df96ca124610bdf000092d Mon Sep 17 00:00:00 2001 From: TongTongStudio Date: Fri, 17 Jul 2026 00:34:49 +0800 Subject: [PATCH] feat: add root shell --- .../activity/main/MainActivity.java | 2 +- .../controlled/input/InputCommandHandler.java | 9 ++ .../ttstd/controlled/input/InputExecutor.java | 5 + .../controlled/input/RootShellInputUtils.java | 93 +++++++++++++++++++ .../controlled/input/ShellInputUtils.java | 4 +- .../service/ScreenCaptureService.java | 49 ++++++++-- .../ttstd/controlled/webrtc/WebRtcClient.java | 78 +++++++--------- 7 files changed, 187 insertions(+), 53 deletions(-) create mode 100644 WebRTCControlled/app/src/main/java/com/ttstd/controlled/input/RootShellInputUtils.java diff --git a/WebRTCControlled/app/src/main/java/com/ttstd/controlled/activity/main/MainActivity.java b/WebRTCControlled/app/src/main/java/com/ttstd/controlled/activity/main/MainActivity.java index 4af7188..cb61355 100644 --- a/WebRTCControlled/app/src/main/java/com/ttstd/controlled/activity/main/MainActivity.java +++ b/WebRTCControlled/app/src/main/java/com/ttstd/controlled/activity/main/MainActivity.java @@ -98,7 +98,7 @@ public class MainActivity extends BaseMvvmActivity h264Payloads = new ArrayList<>(); - // 首先找到所有的 H264 payload type (可能包含多个 profile) + List videoPayloads = new ArrayList<>(); + // 识别所有视频负载类型 for (String line : lines) { String trimmedLine = line.trim(); - if (trimmedLine.startsWith("a=rtpmap:") && trimmedLine.contains("H264/90000")) { + if (trimmedLine.startsWith("a=rtpmap:") && (trimmedLine.contains("H264/90000") || trimmedLine.contains("VP8/90000") || trimmedLine.contains("VP9/90000"))) { String payload = trimmedLine.split(":")[1].split(" ")[0]; - h264Payloads.add(payload); + videoPayloads.add(payload); } } - if (h264Payloads.isEmpty()) { + if (videoPayloads.isEmpty()) { return sdp; } @@ -386,17 +386,15 @@ public class WebRtcClient { if (trimmedLine.isEmpty()) continue; if (trimmedLine.startsWith("m=video")) { - // 将所有 H264 payload 移到最前面以优先使用 String[] parts = trimmedLine.split(" "); if (parts.length > 3) { StringBuilder mLine = new StringBuilder(parts[0] + " " + parts[1] + " " + parts[2]); - // 先添加所有的 H264 payload - for (String h264 : h264Payloads) { - mLine.append(" ").append(h264); + // 将 H264 放在最前面(如果存在) + for (String payload : videoPayloads) { + mLine.append(" ").append(payload); } - // 再添加其余的 payload for (int i = 3; i < parts.length; i++) { - if (!h264Payloads.contains(parts[i])) { + if (!videoPayloads.contains(parts[i])) { mLine.append(" ").append(parts[i]); } } @@ -405,25 +403,18 @@ public class WebRtcClient { newSdp.append(trimmedLine).append("\r\n"); } } else if (trimmedLine.startsWith("a=fmtp:")) { - boolean isH264 = false; - for (String h264 : h264Payloads) { - if (trimmedLine.startsWith("a=fmtp:" + h264)) { - isH264 = true; + boolean isVideoPayload = false; + for (String payload : videoPayloads) { + if (trimmedLine.startsWith("a=fmtp:" + payload)) { + isVideoPayload = true; break; } } - if (isH264) { - // 为 H264 注入码率优化参数。 - // 屏幕内容(文字/边缘)对码率敏感,过低的下限会产生色块; - // 提高下限可显著改善画质。若被控端上行带宽较弱,请适当下调 MIN。 + if (isVideoPayload) { String bonus = ";x-google-start-bitrate=" + START_BITRATE_KBPS + ";x-google-max-bitrate=" + MAX_BITRATE_KBPS + ";x-google-min-bitrate=" + MIN_BITRATE_KBPS; - if (!trimmedLine.contains("x-google-start-bitrate")) { - newSdp.append(trimmedLine).append(bonus).append("\r\n"); - } else { - newSdp.append(trimmedLine).append("\r\n"); - } + newSdp.append(trimmedLine).append(bonus).append("\r\n"); } else { newSdp.append(trimmedLine).append("\r\n"); } @@ -464,14 +455,17 @@ public class WebRtcClient { wsClient.sendMessage(msg); } - private void refreshVideoSource() { - if (videoCapturer != null) { - try { - Log.d(TAG, "Refreshing video capture to force a frame update for new connection"); - videoCapturer.stopCapture(); - videoCapturer.startCapture(videoWidth, videoHeight, videoFps); - } catch (Exception e) { - Log.e(TAG, "Error refreshing video capture: " + e.getMessage()); + /** + * 通过重新应用 RtpParameters 诱发编码器生成关键帧(I-Frame)。 + * 相比重启采集器,这种方法更平滑,不会导致 MediaProjection 管道断开。 + */ + public void triggerKeyFrame() { + if (videoSender != null) { + Log.d(TAG, "Triggering key frame via RtpSender parameters update"); + org.webrtc.RtpParameters parameters = videoSender.getParameters(); + if (parameters != null && !parameters.encodings.isEmpty()) { + // 即使不修改任何参数,重新设置一次 setParameters 也会诱发许多编码器产生 I-Frame + videoSender.setParameters(parameters); } } }