fix: 修复自编码模式黑屏问题

- 调整包序号过期检查逻辑,避免同一帧分片被误删
- 适配 Android 14,在创建 VirtualDisplay 前注册 MediaProjection.Callback
- 启动后检查编码器实际运行状态,失败时进行清理
This commit is contained in:
TongTongStudio
2026-07-24 20:08:17 +08:00
parent 23698d0be7
commit 3792b75244
3 changed files with 39 additions and 3 deletions

View File

@@ -476,6 +476,13 @@ public class ScreenCaptureService extends Service {
}
});
selfEncoder.start();
// start() 内部捕获异常后 running 仍为 false需据此判断真实启动结果
// 否则会误报 SELF_CODEC 模式已生效而实际无任何画面(黑屏)。
if (!selfEncoder.isRunning()) {
Log.e(TAG, "self codec encoder failed to start, cleaning up");
stopSelfCodecEncoder();
return false;
}
return true;
}

View File

@@ -45,6 +45,7 @@ public class SelfCodecEncoder {
private MediaCodec encoder;
private Surface inputSurface;
private VirtualDisplay virtualDisplay;
private MediaProjection.Callback projectionCallback;
private boolean running = false;
private int frameSeq = 0;
private final Object sendLock = new Object();
@@ -88,6 +89,20 @@ public class SelfCodecEncoder {
inputSurface = encoder.createInputSurface();
encoder.start();
// Android 14+ (API 34) 强制要求:在 createVirtualDisplay 之前必须先注册
// MediaProjection.Callback否则会抛
// IllegalStateException("Must register a callback before starting capture...")
// 导致自编码模式无法启动、控制端黑屏。onStop 时释放资源。
projectionCallback = new MediaProjection.Callback() {
@Override
public void onStop() {
Log.e(TAG, "MediaProjection stopped, releasing encoder");
release();
if (callback != null) callback.onEncoderError("MediaProjection stopped");
}
};
mediaProjection.registerCallback(projectionCallback, null);
virtualDisplay = mediaProjection.createVirtualDisplay(
"SelfCodec", width, height, dpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, inputSurface, null, null);
@@ -246,6 +261,11 @@ public class SelfCodecEncoder {
}
}
/** 返回编码器是否正在运行(供上层判断启动结果)。 */
public boolean isRunning() {
return running;
}
/** 即时请求一个关键帧(强求 IDR。 */
public void requestKeyFrame() {
if (encoder == null) return;
@@ -260,6 +280,13 @@ public class SelfCodecEncoder {
public void release() {
running = false;
if (mediaProjection != null && projectionCallback != null) {
try {
mediaProjection.unregisterCallback(projectionCallback);
} catch (Exception ignored) {
}
projectionCallback = null;
}
if (virtualDisplay != null) {
try {
virtualDisplay.release();