fix: 修复自编码模式黑屏问题
- 调整包序号过期检查逻辑,避免同一帧分片被误删 - 适配 Android 14,在创建 VirtualDisplay 前注册 MediaProjection.Callback - 启动后检查编码器实际运行状态,失败时进行清理
This commit is contained in:
@@ -476,6 +476,13 @@ public class ScreenCaptureService extends Service {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
selfEncoder.start();
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ public class SelfCodecEncoder {
|
|||||||
private MediaCodec encoder;
|
private MediaCodec encoder;
|
||||||
private Surface inputSurface;
|
private Surface inputSurface;
|
||||||
private VirtualDisplay virtualDisplay;
|
private VirtualDisplay virtualDisplay;
|
||||||
|
private MediaProjection.Callback projectionCallback;
|
||||||
private boolean running = false;
|
private boolean running = false;
|
||||||
private int frameSeq = 0;
|
private int frameSeq = 0;
|
||||||
private final Object sendLock = new Object();
|
private final Object sendLock = new Object();
|
||||||
@@ -88,6 +89,20 @@ public class SelfCodecEncoder {
|
|||||||
inputSurface = encoder.createInputSurface();
|
inputSurface = encoder.createInputSurface();
|
||||||
encoder.start();
|
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(
|
virtualDisplay = mediaProjection.createVirtualDisplay(
|
||||||
"SelfCodec", width, height, dpi,
|
"SelfCodec", width, height, dpi,
|
||||||
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, inputSurface, null, null);
|
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, inputSurface, null, null);
|
||||||
@@ -246,6 +261,11 @@ public class SelfCodecEncoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 返回编码器是否正在运行(供上层判断启动结果)。 */
|
||||||
|
public boolean isRunning() {
|
||||||
|
return running;
|
||||||
|
}
|
||||||
|
|
||||||
/** 即时请求一个关键帧(强求 IDR)。 */
|
/** 即时请求一个关键帧(强求 IDR)。 */
|
||||||
public void requestKeyFrame() {
|
public void requestKeyFrame() {
|
||||||
if (encoder == null) return;
|
if (encoder == null) return;
|
||||||
@@ -260,6 +280,13 @@ public class SelfCodecEncoder {
|
|||||||
|
|
||||||
public void release() {
|
public void release() {
|
||||||
running = false;
|
running = false;
|
||||||
|
if (mediaProjection != null && projectionCallback != null) {
|
||||||
|
try {
|
||||||
|
mediaProjection.unregisterCallback(projectionCallback);
|
||||||
|
} catch (Exception ignored) {
|
||||||
|
}
|
||||||
|
projectionCallback = null;
|
||||||
|
}
|
||||||
if (virtualDisplay != null) {
|
if (virtualDisplay != null) {
|
||||||
try {
|
try {
|
||||||
virtualDisplay.release();
|
virtualDisplay.release();
|
||||||
|
|||||||
@@ -98,9 +98,11 @@ public class SelfCodecDecoder {
|
|||||||
byte magic = b.get();
|
byte magic = b.get();
|
||||||
if (magic != MAGIC) return;
|
if (magic != MAGIC) return;
|
||||||
int seq = b.getInt();
|
int seq = b.getInt();
|
||||||
// 关键修复:增加包序号过期检查(处理包乱序或重复)。
|
// 过期检查:被控端每个“单元”共用同一个 seq(同一单元的所有分片 seq 相同、以 idx 区分),
|
||||||
// 实时视频流中,如果收到了序号更旧的包,直接丢弃以保证延迟和状态一致性。
|
// 因此不能用 seq<=lastSeq 来丢弃——否则同一单元除首片外的分片都会被误删,
|
||||||
if (lastSeq != -1 && seq <= lastSeq && (lastSeq - seq) < 1000) {
|
// 导致大帧永远无法重组、自编码串流黑屏。这里只在 seq 明显小于 lastSeq(旧单元)时丢弃,
|
||||||
|
// 相同 seq(同单元的其它分片)放行。
|
||||||
|
if (lastSeq != -1 && seq < lastSeq && (lastSeq - seq) < 1000) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
short total = b.getShort();
|
short total = b.getShort();
|
||||||
|
|||||||
Reference in New Issue
Block a user