feat: 支持手动切换采集帧率
在 Android、Web 及 Flutter 控制端新增帧率选择 UI,被控端按屏幕刷新率生成支持的帧率档位并上报,允许在保持分辨率不变的情况下仅切换帧率。 另附带更新信号服务器的数据库配置。
This commit is contained in:
@@ -69,6 +69,13 @@ public class MainActivity extends AppCompatActivity {
|
||||
private TextView tvStatusControl;
|
||||
private TextView tvStats;
|
||||
private Spinner spinnerResolution;
|
||||
private Spinner spinnerFps;
|
||||
/** 帧率下拉框当前档位(以被控端上报的 supported_fps 为准)。 */
|
||||
private final List<Integer> fpsOptions = new ArrayList<>();
|
||||
/** 被控端最近上报的实际采集分辨率/帧率(仅切帧率时保持分辨率不变)。 */
|
||||
private int lastReportedWidth = 0;
|
||||
private int lastReportedHeight = 0;
|
||||
private int lastKnownFps = 0;
|
||||
private Button btnDisconnectControl;
|
||||
|
||||
// 屏幕串流"自编码"模式相关 UI 与解码器
|
||||
@@ -131,6 +138,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
tvStatusControl = findViewById(R.id.tv_status_control);
|
||||
tvStats = findViewById(R.id.tv_stats);
|
||||
spinnerResolution = findViewById(R.id.spinner_resolution);
|
||||
spinnerFps = findViewById(R.id.spinner_fps);
|
||||
btnDisconnectControl = findViewById(R.id.btn_disconnect_control);
|
||||
|
||||
// 默认服务器地址
|
||||
@@ -192,6 +200,7 @@ public class MainActivity extends AppCompatActivity {
|
||||
});
|
||||
|
||||
setupResolutionSpinner();
|
||||
setupFpsSpinner();
|
||||
updateUI(false);
|
||||
|
||||
setupSelfCodecUi();
|
||||
@@ -414,6 +423,13 @@ public class MainActivity extends AppCompatActivity {
|
||||
webRtcClient.setSelfCodecDecoder(selfCodecDecoder);
|
||||
webRtcClient.setRecorder(videoRecorder);
|
||||
webRtcClient.setStreamModeReportListener(mode -> runOnUiThread(() -> syncStreamModeUi(mode)));
|
||||
// 被控端上报当前分辨率/帧率与支持的帧率档位 -> 同步帧率下拉框
|
||||
webRtcClient.setResolutionReportListener((w, h, fps, supportedFps) -> runOnUiThread(() -> {
|
||||
if (w > 0) lastReportedWidth = w;
|
||||
if (h > 0) lastReportedHeight = h;
|
||||
if (fps > 0) lastKnownFps = fps;
|
||||
applyFpsOptions(supportedFps, fps);
|
||||
}));
|
||||
webRtcClient.setConnectionListener(new WebRtcClient.ConnectionListener() {
|
||||
@Override
|
||||
public void onConnectionEstablished() {
|
||||
@@ -689,6 +705,61 @@ public class MainActivity extends AppCompatActivity {
|
||||
});
|
||||
}
|
||||
|
||||
/** 构建帧率下拉框;档位以被控端上报(REPORT_RESOLUTION.supported_fps)为准,未上报前使用常用档位。 */
|
||||
private void setupFpsSpinner() {
|
||||
List<Integer> defaults = new ArrayList<>();
|
||||
defaults.add(15);
|
||||
defaults.add(24);
|
||||
defaults.add(30);
|
||||
defaults.add(60);
|
||||
applyFpsOptions(defaults, 0);
|
||||
|
||||
spinnerFps.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (position < 0 || position >= fpsOptions.size()) return;
|
||||
int fps = fpsOptions.get(position);
|
||||
// 与被控端当前帧率一致时不重复发送(含上报同步 setSelection 触发的回调)
|
||||
if (fps <= 0 || fps == lastKnownFps || webRtcClient == null) return;
|
||||
// 仅切换帧率:分辨率沿用被控端最近上报的实际采集尺寸
|
||||
webRtcClient.requestResolutionChange(lastReportedWidth, lastReportedHeight, fps);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> parent) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** 更新帧率档位并把选中项同步为最接近 currentFps 的档位。 */
|
||||
private void applyFpsOptions(List<Integer> options, int currentFps) {
|
||||
if (spinnerFps == null) return;
|
||||
if (options != null && !options.isEmpty() && !options.equals(fpsOptions)) {
|
||||
fpsOptions.clear();
|
||||
fpsOptions.addAll(options);
|
||||
List<String> labels = new ArrayList<>();
|
||||
for (int f : fpsOptions) labels.add(f + "fps");
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(
|
||||
this, android.R.layout.simple_spinner_item, labels);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
spinnerFps.setAdapter(adapter);
|
||||
}
|
||||
if (currentFps > 0 && !fpsOptions.isEmpty()) {
|
||||
int idx = 0;
|
||||
int best = Integer.MAX_VALUE;
|
||||
for (int i = 0; i < fpsOptions.size(); i++) {
|
||||
int diff = Math.abs(fpsOptions.get(i) - currentFps);
|
||||
if (diff < best) {
|
||||
best = diff;
|
||||
idx = i;
|
||||
}
|
||||
}
|
||||
if (spinnerFps.getSelectedItemPosition() != idx) {
|
||||
spinnerFps.setSelection(idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateUI(boolean connected) {
|
||||
setupPanel.setVisibility(connected ? View.GONE : View.VISIBLE);
|
||||
controlPanel.setVisibility(connected ? View.VISIBLE : View.GONE);
|
||||
@@ -697,6 +768,9 @@ public class MainActivity extends AppCompatActivity {
|
||||
if (spinnerResolution != null) {
|
||||
spinnerResolution.setEnabled(connected);
|
||||
}
|
||||
if (spinnerFps != null) {
|
||||
spinnerFps.setEnabled(connected);
|
||||
}
|
||||
if (tvStatusControl != null) {
|
||||
tvStatusControl.setText(connected ? "远程控制中" : "未连接");
|
||||
}
|
||||
|
||||
@@ -71,6 +71,17 @@ public class WebRtcClient {
|
||||
void onStreamModeReported(int mode);
|
||||
}
|
||||
|
||||
/** 被控端上报当前采集分辨率/帧率与支持的帧率档位(REPORT_RESOLUTION)。 */
|
||||
public interface ResolutionReportListener {
|
||||
void onResolutionReported(int width, int height, int fps, List<Integer> supportedFps);
|
||||
}
|
||||
|
||||
private ResolutionReportListener resolutionReportListener;
|
||||
|
||||
public void setResolutionReportListener(ResolutionReportListener l) {
|
||||
this.resolutionReportListener = l;
|
||||
}
|
||||
|
||||
private ConnectionListener connectionListener;
|
||||
|
||||
public WebRtcClient(Context context, WebSocketClient wsClient, String deviceId) {
|
||||
@@ -371,10 +382,16 @@ public class WebRtcClient {
|
||||
streamModeReportListener.onStreamModeReported(msg.getStreamMode());
|
||||
}
|
||||
} else if (msg.getAction() == Action.REPORT_RESOLUTION) {
|
||||
Log.i(TAG, "Received resolution report: " + msg.getWidth() + "x" + msg.getHeight());
|
||||
Log.i(TAG, "Received resolution report: " + msg.getWidth() + "x" + msg.getHeight()
|
||||
+ " @ " + msg.getFps() + "fps, supportedFps=" + msg.getSupportedFpsList());
|
||||
if (selfCodecDecoder != null) {
|
||||
selfCodecDecoder.updateResolution(msg.getWidth(), msg.getHeight());
|
||||
}
|
||||
if (resolutionReportListener != null) {
|
||||
resolutionReportListener.onResolutionReported(
|
||||
msg.getWidth(), msg.getHeight(), msg.getFps(),
|
||||
new ArrayList<>(msg.getSupportedFpsList()));
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
|
||||
@@ -51,4 +51,7 @@ message ControlMessage {
|
||||
int32 height = 13;
|
||||
int32 fps = 14;
|
||||
int32 stream_mode = 15; // 串流模式:0=WebRTC 内置媒体流,1=自编码(MediaCodec 硬编 + DataChannel 透传)
|
||||
|
||||
// 被控端支持的帧率档位列表(REPORT_RESOLUTION 上报,按屏幕刷新率筛选,升序)。
|
||||
repeated int32 supported_fps = 16;
|
||||
}
|
||||
|
||||
@@ -152,6 +152,13 @@
|
||||
android:layout_marginTop="4dp"
|
||||
android:backgroundTint="#80FFFFFF" />
|
||||
|
||||
<Spinner
|
||||
android:id="@+id/spinner_fps"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:backgroundTint="#80FFFFFF" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_disconnect_control"
|
||||
android:layout_width="wrap_content"
|
||||
|
||||
Reference in New Issue
Block a user