diff --git a/HarmonyOSNEXTApp b/HarmonyOSNEXTApp new file mode 160000 index 0000000..a7a4a9f --- /dev/null +++ b/HarmonyOSNEXTApp @@ -0,0 +1 @@ +Subproject commit a7a4a9f94c069706f1a32d7fccfaa0cac4a5e387 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 cb61355..bdca88e 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 @@ -12,8 +12,15 @@ import android.os.Build; import android.os.IBinder; import android.provider.Settings; import android.util.Log; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; import android.widget.Toast; +import java.util.ArrayList; +import java.util.List; + import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; @@ -56,6 +63,7 @@ public class MainActivity extends BaseMvvmActivity presets = new ArrayList<>(); + presets.add(new ResolutionPreset("原始 (" + nativeLong + ")", nativeLong)); + presets.add(new ResolutionPreset("1080P", 1920)); + presets.add(new ResolutionPreset("720P", 1280)); + presets.add(new ResolutionPreset("480P", 854)); + + ArrayAdapter adapter = new ArrayAdapter<>( + this, android.R.layout.simple_spinner_item, presets); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + binding.spinnerResolution.setAdapter(adapter); + + // 选中当前采集分辨率对应的预设 + int[] cur = screenCaptureService.getCurrentCaptureResolution(); + int curLong = Math.max(cur[0], cur[1]); + int selectIdx = 0; + int bestDiff = Integer.MAX_VALUE; + for (int i = 0; i < presets.size(); i++) { + int diff = Math.abs(presets.get(i).longEdge - curLong); + if (diff < bestDiff) { + bestDiff = diff; + selectIdx = i; + } + } + binding.spinnerResolution.setSelection(selectIdx); + updateCurrentResolutionText(cur[0], cur[1], cur[2]); + + // 注册监听:本地或远端切换分辨率后刷新当前分辨率文本 + screenCaptureService.setResolutionListener((w, h, fps, fromRemote) -> + runOnUiThread(() -> updateCurrentResolutionText(w, h, fps))); + + // 注意:先 setSelection 再设置监听,避免初始化时触发一次多余的切换。 + binding.spinnerResolution.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (screenCaptureService == null) return; + ResolutionPreset preset = (ResolutionPreset) parent.getItemAtPosition(position); + int[] now = screenCaptureService.getCurrentCaptureResolution(); + int nowLong = Math.max(now[0], now[1]); + // 与当前一致则不重复切换,避免无谓的提示与编码重启 + if (preset.longEdge == nowLong) return; + screenCaptureService.changeResolutionByLongEdge(preset.longEdge); + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + } + + private void updateCurrentResolutionText(int w, int h, int fps) { + binding.tvCurrentResolution.setText( + String.format("当前: %dx%d @ %dfps", w, h, fps)); + } } diff --git a/WebRTCControlled/app/src/main/java/com/ttstd/controlled/input/InputCommandHandler.java b/WebRTCControlled/app/src/main/java/com/ttstd/controlled/input/InputCommandHandler.java index d38d94f..43f51c0 100644 --- a/WebRTCControlled/app/src/main/java/com/ttstd/controlled/input/InputCommandHandler.java +++ b/WebRTCControlled/app/src/main/java/com/ttstd/controlled/input/InputCommandHandler.java @@ -18,6 +18,13 @@ public class InputCommandHandler { private final int screenHeight; private final InputExecutor inputExecutor; + /** 控制端请求切换屏幕采集分辨率时的回调(由 ScreenCaptureService 实现)。 */ + public interface ResolutionRequestListener { + void onResolutionRequested(int width, int height, int fps); + } + + private ResolutionRequestListener resolutionListener; + public InputCommandHandler(int screenWidth, int screenHeight) { this(screenWidth, screenHeight, new SystemInputUtils()); } @@ -35,6 +42,10 @@ public class InputCommandHandler { this.inputExecutor = inputExecutor; } + public void setResolutionRequestListener(ResolutionRequestListener listener) { + this.resolutionListener = listener; + } + public void handleCommand(byte[] data) { try { ControlMessage command = ControlMessage.parseFrom(data); @@ -56,6 +67,15 @@ public class InputCommandHandler { case MOTION_EVENT: handleMotionEvent(command); break; + case SET_RESOLUTION: + // 分辨率切换不依赖输入执行器,转发给屏幕采集服务处理。 + if (resolutionListener != null) { + resolutionListener.onResolutionRequested( + command.getWidth(), command.getHeight(), command.getFps()); + } else { + Log.w(TAG, "SET_RESOLUTION received but no listener registered"); + } + break; default: Log.w(TAG, "Unknown action: " + action); } diff --git a/WebRTCControlled/app/src/main/java/com/ttstd/controlled/service/ScreenCaptureService.java b/WebRTCControlled/app/src/main/java/com/ttstd/controlled/service/ScreenCaptureService.java index 7f4a5fc..3ed45a4 100644 --- a/WebRTCControlled/app/src/main/java/com/ttstd/controlled/service/ScreenCaptureService.java +++ b/WebRTCControlled/app/src/main/java/com/ttstd/controlled/service/ScreenCaptureService.java @@ -83,6 +83,13 @@ public class ScreenCaptureService extends Service { private String pendingOfferSdp; /** 当前正在控制本设备的控制端名称(用户名或设备 ID),用于断开提示。 */ private String controllingName; + /** 设备真实屏幕分辨率,用于分辨率切换时按宽高比计算采集尺寸。 */ + private int realScreenWidth; + private int realScreenHeight; + /** 当前实际采集分辨率与帧率(运行时可被本地/远端请求切换)。 */ + private int currentCaptureWidth; + private int currentCaptureHeight; + private int currentCaptureFps; /** 主线程 Handler,用于从 WebRTC 回调线程切回主线程执行 UI 提示。 */ private final Handler mainHandler = new Handler(Looper.getMainLooper()); /** 标记服务是否正在关闭,避免重复停止/重复弹窗。 */ @@ -137,6 +144,8 @@ public class ScreenCaptureService extends Service { } int realWidth = dm.widthPixels; int realHeight = dm.heightPixels; + this.realScreenWidth = realWidth; + this.realScreenHeight = realHeight; // 分辨率缩放逻辑:如果设备分辨率大于 1080p,则等比例缩小到 1080p 以内 int captureWidth = realWidth; @@ -167,8 +176,15 @@ public class ScreenCaptureService extends Service { Log.i(TAG, "Real resolution: " + realWidth + "x" + realHeight); Log.i(TAG, "Capture resolution: " + captureWidth + "x" + captureHeight + " @ " + fps + "fps"); + // 记录初始采集分辨率,供 UI 与运行时切换使用 + this.currentCaptureWidth = captureWidth; + this.currentCaptureHeight = captureHeight; + this.currentCaptureFps = fps; + // 输入处理器必须使用真实分辨率进行坐标映射 inputHandler = new InputCommandHandler(realWidth, realHeight, createInputExecutor()); + // 控制端发来的 SET_RESOLUTION 指令转交本服务处理(坐标映射与输入执行器无关)。 + inputHandler.setResolutionRequestListener(this::onRemoteResolutionRequested); // 开启屏幕捕获,使用计算出的 capture 分辨率 startScreenCapture(resultCode, resultData, serverUrl, deviceId, captureWidth, captureHeight, fps); @@ -216,6 +232,90 @@ public class ScreenCaptureService extends Service { return webRtcClient; } + /** + * 切换屏幕采集分辨率(本地 UI 调用)。 + * @param longEdge 目标长边像素;<=0 表示使用设备原生分辨率(按屏幕宽高比缩放)。 + */ + public void changeResolutionByLongEdge(int longEdge) { + requestResolutionChange(longEdge, 0, 0, false); + } + + /** 返回当前采集分辨率(宽/高/帧率)。 */ + public int[] getCurrentCaptureResolution() { + return new int[]{ currentCaptureWidth, currentCaptureHeight, currentCaptureFps }; + } + + /** 返回设备原生分辨率的长边(像素),供 UI 生成“原始画质”选项。 */ + public int getNativeLongEdge() { + return Math.max(realScreenWidth, realScreenHeight); + } + + /** 控制端经 DataChannel 发来的 SET_RESOLUTION 指令入口。 */ + private void onRemoteResolutionRequested(int width, int height, int fps) { + requestResolutionChange(width, height, fps, true); + } + + /** + * 解析并应用目标采集分辨率。 + * 语义(与控制端约定一致): + * - width <= 0:使用设备原生分辨率。 + * - height <= 0:仅按 width 指定长边,按屏幕宽高比计算另一维度。 + * - 否则使用精确 width x height。 + * 最终宽高会偶数对齐(部分 H264 编码器要求),并触发一次关键帧刷新远端画面。 + * + * @param width 目标宽度/长边(像素) + * @param height 目标高度(像素,<=0 时按宽高比计算) + * @param fps 目标帧率(<=0 时沿用当前帧率) + * @param fromRemote 是否由远端控制端触发(仅用于 UI 提示区分) + */ + public void requestResolutionChange(int width, int height, int fps, boolean fromRemote) { + int[] size = resolveCaptureSize(width, height); + int targetW = size[0]; + int targetH = size[1]; + int targetFps = fps > 0 ? fps : currentCaptureFps; + if (targetFps <= 0) targetFps = 30; + + if (webRtcClient == null) { + Log.w(TAG, "requestResolutionChange ignored: WebRTC not ready"); + return; + } + + webRtcClient.changeCaptureFormat(targetW, targetH, targetFps); + currentCaptureWidth = targetW; + currentCaptureHeight = targetH; + currentCaptureFps = targetFps; + + Log.i(TAG, "Capture resolution changed -> " + targetW + "x" + targetH + " @ " + targetFps + "fps (remote=" + fromRemote + ")"); + showToast(getString(R.string.resolution_changed_message, targetW, targetH)); + + if (resolutionListener != null) { + resolutionListener.onResolutionChanged(targetW, targetH, targetFps, fromRemote); + } + } + + /** 根据指令参数解析出最终采集尺寸(保留屏幕宽高比、偶数对齐)。 */ + private int[] resolveCaptureSize(int width, int height) { + int w, h; + if (width <= 0) { + w = realScreenWidth; + h = realScreenHeight; + } else if (height <= 0) { + int longEdge = Math.max(realScreenWidth, realScreenHeight); + float scale = (float) width / longEdge; + w = Math.round(realScreenWidth * scale); + h = Math.round(realScreenHeight * scale); + } else { + w = width; + h = height; + } + // 偶数对齐(部分 H264 编码器要求宽高为偶数) + if (w % 2 != 0) w--; + if (h % 2 != 0) h--; + if (w <= 0) w = 2; + if (h <= 0) h = 2; + return new int[]{ w, h }; + } + /** * 状态回调。由已 bind 的 MainActivity 注册,用于把连接状态同步到界面。 */ @@ -232,6 +332,19 @@ public class ScreenCaptureService extends Service { this.stateListener = listener; } + /** + * 分辨率变化回调。由已 bind 的 MainActivity 注册,用于在界面上同步当前采集分辨率。 + */ + public interface ResolutionChangeListener { + void onResolutionChanged(int width, int height, int fps, boolean fromRemote); + } + + private ResolutionChangeListener resolutionListener; + + public void setResolutionListener(ResolutionChangeListener listener) { + this.resolutionListener = listener; + } + @Override public void onDestroy() { super.onDestroy(); diff --git a/WebRTCControlled/app/src/main/java/com/ttstd/controlled/webrtc/WebRtcClient.java b/WebRTCControlled/app/src/main/java/com/ttstd/controlled/webrtc/WebRtcClient.java index 002e8a8..5366bfc 100644 --- a/WebRTCControlled/app/src/main/java/com/ttstd/controlled/webrtc/WebRtcClient.java +++ b/WebRTCControlled/app/src/main/java/com/ttstd/controlled/webrtc/WebRtcClient.java @@ -6,6 +6,8 @@ import android.util.Log; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.JsonObject; +import com.ttstd.control.Action; +import com.ttstd.control.ControlMessage; import com.ttstd.controlled.signaling.SignalMessage; import com.ttstd.controlled.signaling.WebSocketClient; @@ -148,6 +150,69 @@ public class WebRtcClient { } } + /** + * 运行时切换屏幕采集分辨率(无需重建采集器/连接)。 + * 通过 {@link org.webrtc.VideoCapturer#changeCaptureFormat} 平滑改变编码尺寸, + * 并触发一次关键帧,避免远端解码器出现花屏/卡顿。 + * + * @param width 目标宽度(像素,需为偶数) + * @param height 目标高度(像素,需为偶数) + * @param fps 目标帧率,<=0 时沿用当前帧率 + */ + public void changeCaptureFormat(int width, int height, int fps) { + if (videoCapturer == null) { + Log.w(TAG, "changeCaptureFormat ignored: capturer not ready"); + return; + } + int targetFps = fps > 0 ? fps : this.videoFps; + if (targetFps <= 0) targetFps = 30; + Log.i(TAG, "Changing capture format -> " + width + "x" + height + " @ " + targetFps + "fps"); + try { + videoCapturer.changeCaptureFormat(width, height, targetFps); + } catch (Exception e) { + Log.e(TAG, "changeCaptureFormat failed", e); + return; + } + this.videoWidth = width; + this.videoHeight = height; + this.videoFps = targetFps; + // 编码尺寸变化后诱发关键帧,使远端立即刷新画面。 + triggerKeyFrame(); + // 主动把最新采集分辨率上报给控制端,使其 UI 与实际保持一致。 + sendResolutionReport(width, height, targetFps); + } + + /** + * 通过 DataChannel 把当前实际采集分辨率上报给控制端(action=REPORT_RESOLUTION)。 + * 控制端(网页/Android/Flutter)据此同步展示当前分辨率,避免 UI 与实际不一致。 + */ + public void sendResolutionReport(int width, int height, int fps) { + if (dataChannel == null || dataChannel.state() != DataChannel.State.OPEN) { + Log.d(TAG, "sendResolutionReport skipped: dataChannel not open"); + return; + } + try { + ControlMessage report = ControlMessage.newBuilder() + .setAction(Action.REPORT_RESOLUTION) + .setWidth(width) + .setHeight(height) + .setFps(fps) + .build(); + ByteBuffer buffer = ByteBuffer.wrap(report.toByteArray()); + // binary=true:必须作为二进制发送,控制端按 protobuf 二进制解析; + // 若传 false 会被当作文本,Web 端收到的将是字符串导致解码失败。 + dataChannel.send(new DataChannel.Buffer(buffer, true)); + Log.i(TAG, "Reported capture resolution -> " + width + "x" + height + " @ " + fps + "fps"); + } catch (Exception e) { + Log.e(TAG, "sendResolutionReport failed", e); + } + } + + /** 返回当前采集分辨率(像素),用于 UI 展示。 */ + public int[] getCurrentResolution() { + return new int[]{ videoWidth, videoHeight, videoFps }; + } + private VideoSink localSink; public void setLocalSink(VideoSink sink) { @@ -372,7 +437,10 @@ public class WebRtcClient { @Override public void onStateChange() { Log.d(TAG, "DataChannel state: " + dc.state()); - if (dc.state() == DataChannel.State.CLOSED) { + if (dc.state() == DataChannel.State.OPEN) { + // 通道建立后,立即把当前实际采集分辨率上报给控制端(初始值或最近一次切换值)。 + sendResolutionReport(videoWidth, videoHeight, videoFps); + } else if (dc.state() == DataChannel.State.CLOSED) { notifyRemoteDisconnected(); } } diff --git a/WebRTCControlled/app/src/main/proto/control_message.proto b/WebRTCControlled/app/src/main/proto/control_message.proto index e72212d..99c6720 100644 --- a/WebRTCControlled/app/src/main/proto/control_message.proto +++ b/WebRTCControlled/app/src/main/proto/control_message.proto @@ -13,6 +13,8 @@ enum Action { KEY = 3; LONG_PRESS = 4; MOTION_EVENT = 5; + SET_RESOLUTION = 6; // 控制端请求被控端切换屏幕采集分辨率 + REPORT_RESOLUTION = 7; // 被控端上报当前实际采集分辨率(含连接建立后的初始值) } // 通过 RTCDataChannel 传输的控制指令(protobuf 二进制)。 @@ -38,4 +40,12 @@ message ControlMessage { // 原始动作(MOTION_EVENT):对应 Android MotionEvent ACTION_*(0=DOWN 1=UP 2=MOVE) int32 motion_action = 11; + + // 分辨率切换(SET_RESOLUTION): + // width 为目标长边/宽度;<=0 表示使用被控端原始(native)分辨率。 + // height 为目标高度;<=0 时按被控端屏幕宽高比基于 width 计算(保留原始比例)。 + // fps 为目标帧率;<=0 表示沿用当前帧率。 + int32 width = 12; + int32 height = 13; + int32 fps = 14; } diff --git a/WebRTCControlled/app/src/main/res/layout/activity_main.xml b/WebRTCControlled/app/src/main/res/layout/activity_main.xml index 93caa4c..e736a90 100644 --- a/WebRTCControlled/app/src/main/res/layout/activity_main.xml +++ b/WebRTCControlled/app/src/main/res/layout/activity_main.xml @@ -76,6 +76,30 @@ android:enabled="false" android:text="停止屏幕共享" /> + + + + + + 当前应用未使用系统签名,也未获取 Root 权限。如需在远程控制时模拟键盘与触摸输入,请前往系统设置开启本应用的无障碍服务。 去开启 取消 + 采集分辨率 + 采集分辨率已切换为 %1$d×%2$d diff --git a/WebRTCController/app/src/main/java/com/ttstd/controller/MainActivity.java b/WebRTCController/app/src/main/java/com/ttstd/controller/MainActivity.java index f06cca3..d6aad34 100644 --- a/WebRTCController/app/src/main/java/com/ttstd/controller/MainActivity.java +++ b/WebRTCController/app/src/main/java/com/ttstd/controller/MainActivity.java @@ -11,6 +11,13 @@ import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Spinner; + +import java.util.ArrayList; +import java.util.List; import androidx.appcompat.app.AppCompatActivity; @@ -47,6 +54,7 @@ public class MainActivity extends AppCompatActivity { private LinearLayout setupPanel; private TextView tvStatusControl; private TextView tvStats; + private Spinner spinnerResolution; private WebSocketClient wsClient; private WebRtcClient webRtcClient; @@ -89,6 +97,7 @@ public class MainActivity extends AppCompatActivity { setupPanel = findViewById(R.id.setup_panel); tvStatusControl = findViewById(R.id.tv_status_control); tvStats = findViewById(R.id.tv_stats); + spinnerResolution = findViewById(R.id.spinner_resolution); // 默认服务器地址 etServerUrl.setText("ws://175.178.213.60:8088/ws/signal"); @@ -147,6 +156,7 @@ public class MainActivity extends AppCompatActivity { } }); + setupResolutionSpinner(); updateUI(false); } @@ -458,11 +468,64 @@ public class MainActivity extends AppCompatActivity { tvStatus.setText("状态: 已停止"); } + /** 分辨率预设(发送给被控端的 SET_RESOLUTION 参数)。 */ + private static class ResolutionPreset { + final String label; + final int width; + final int height; + final int fps; + + ResolutionPreset(String label, int width, int height, int fps) { + this.label = label; + this.width = width; + this.height = height; + this.fps = fps; + } + + @Override + public String toString() { + return label; + } + } + + /** 构建分辨率下拉框,选择后请求被控端切换采集分辨率。 */ + private void setupResolutionSpinner() { + List presets = new ArrayList<>(); + presets.add(new ResolutionPreset("原始画质", 0, 0, 0)); // 被控端原生分辨率 + presets.add(new ResolutionPreset("1080P", 1920, 0, 0)); + presets.add(new ResolutionPreset("720P", 1280, 0, 0)); + presets.add(new ResolutionPreset("480P", 854, 0, 0)); + + ArrayAdapter adapter = new ArrayAdapter<>( + this, android.R.layout.simple_spinner_item, presets); + adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); + spinnerResolution.setAdapter(adapter); + spinnerResolution.setSelection(0); + + // 先选中默认项再设置监听,避免初始化即触发一次切换。 + spinnerResolution.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + ResolutionPreset preset = (ResolutionPreset) parent.getItemAtPosition(position); + if (webRtcClient != null) { + webRtcClient.requestResolutionChange(preset.width, preset.height, preset.fps); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + } + }); + } + private void updateUI(boolean connected) { setupPanel.setVisibility(connected ? View.GONE : View.VISIBLE); controlPanel.setVisibility(connected ? View.VISIBLE : View.GONE); btnConnect.setEnabled(!connected); btnDisconnect.setEnabled(connected); + if (spinnerResolution != null) { + spinnerResolution.setEnabled(connected); + } if (tvStatusControl != null) { tvStatusControl.setText(connected ? "远程控制中" : "未连接"); } diff --git a/WebRTCController/app/src/main/java/com/ttstd/controller/webrtc/WebRtcClient.java b/WebRTCController/app/src/main/java/com/ttstd/controller/webrtc/WebRtcClient.java index da6c387..0c08a1b 100644 --- a/WebRTCController/app/src/main/java/com/ttstd/controller/webrtc/WebRtcClient.java +++ b/WebRTCController/app/src/main/java/com/ttstd/controller/webrtc/WebRtcClient.java @@ -5,6 +5,7 @@ import android.util.Log; import com.google.gson.Gson; import com.google.gson.JsonObject; +import com.ttstd.control.Action; import com.ttstd.controller.signaling.SignalMessage; import com.ttstd.controller.signaling.WebSocketClient; import com.ttstd.control.ControlMessage; @@ -239,6 +240,23 @@ public class WebRtcClient { } } + /** + * 请求被控端切换屏幕采集分辨率(通过 DataChannel 发送 SET_RESOLUTION 指令)。 + * 语义:width <= 0 表示被控端原生分辨率;height <= 0 时按 width 指定长边依屏幕宽高比缩放; + * fps <= 0 表示沿用当前帧率。 + */ + public void requestResolutionChange(int width, int height, int fps) { + if (dataChannel != null && dataChannel.state() == DataChannel.State.OPEN) { + ControlMessage msg = ControlMessage.newBuilder() + .setAction(Action.SET_RESOLUTION) + .setWidth(width) + .setHeight(height) + .setFps(fps) + .build(); + sendControlCommand(msg); + } + } + public void close() { if (remoteVideoTrack != null && remoteSurfaceView != null) { remoteVideoTrack.removeSink(remoteSurfaceView); diff --git a/WebRTCController/app/src/main/proto/control_message.proto b/WebRTCController/app/src/main/proto/control_message.proto index e72212d..99c6720 100644 --- a/WebRTCController/app/src/main/proto/control_message.proto +++ b/WebRTCController/app/src/main/proto/control_message.proto @@ -13,6 +13,8 @@ enum Action { KEY = 3; LONG_PRESS = 4; MOTION_EVENT = 5; + SET_RESOLUTION = 6; // 控制端请求被控端切换屏幕采集分辨率 + REPORT_RESOLUTION = 7; // 被控端上报当前实际采集分辨率(含连接建立后的初始值) } // 通过 RTCDataChannel 传输的控制指令(protobuf 二进制)。 @@ -38,4 +40,12 @@ message ControlMessage { // 原始动作(MOTION_EVENT):对应 Android MotionEvent ACTION_*(0=DOWN 1=UP 2=MOVE) int32 motion_action = 11; + + // 分辨率切换(SET_RESOLUTION): + // width 为目标长边/宽度;<=0 表示使用被控端原始(native)分辨率。 + // height 为目标高度;<=0 时按被控端屏幕宽高比基于 width 计算(保留原始比例)。 + // fps 为目标帧率;<=0 表示沿用当前帧率。 + int32 width = 12; + int32 height = 13; + int32 fps = 14; } diff --git a/WebRTCController/app/src/main/res/layout/activity_main.xml b/WebRTCController/app/src/main/res/layout/activity_main.xml index 6806094..47e8885 100644 --- a/WebRTCController/app/src/main/res/layout/activity_main.xml +++ b/WebRTCController/app/src/main/res/layout/activity_main.xml @@ -136,6 +136,13 @@ android:textColor="@android:color/white" android:textSize="12sp" /> + + diff --git a/WebRTCControllerWeb/public/control_message.proto b/WebRTCControllerWeb/public/control_message.proto index e72212d..99c6720 100644 --- a/WebRTCControllerWeb/public/control_message.proto +++ b/WebRTCControllerWeb/public/control_message.proto @@ -13,6 +13,8 @@ enum Action { KEY = 3; LONG_PRESS = 4; MOTION_EVENT = 5; + SET_RESOLUTION = 6; // 控制端请求被控端切换屏幕采集分辨率 + REPORT_RESOLUTION = 7; // 被控端上报当前实际采集分辨率(含连接建立后的初始值) } // 通过 RTCDataChannel 传输的控制指令(protobuf 二进制)。 @@ -38,4 +40,12 @@ message ControlMessage { // 原始动作(MOTION_EVENT):对应 Android MotionEvent ACTION_*(0=DOWN 1=UP 2=MOVE) int32 motion_action = 11; + + // 分辨率切换(SET_RESOLUTION): + // width 为目标长边/宽度;<=0 表示使用被控端原始(native)分辨率。 + // height 为目标高度;<=0 时按被控端屏幕宽高比基于 width 计算(保留原始比例)。 + // fps 为目标帧率;<=0 表示沿用当前帧率。 + int32 width = 12; + int32 height = 13; + int32 fps = 14; } diff --git a/WebRTCControllerWeb/src/components/ControlBar.vue b/WebRTCControllerWeb/src/components/ControlBar.vue index 2e4e0d4..0adf0f5 100644 --- a/WebRTCControllerWeb/src/components/ControlBar.vue +++ b/WebRTCControllerWeb/src/components/ControlBar.vue @@ -1,5 +1,6 @@