feat:增加Android下提取P帧nalu逻辑;增加长时间找不到I帧时的reset逻辑

This commit is contained in:
liyi 2025-05-08 10:13:44 +08:00
parent a8bb1f12f4
commit 38df1883f5
3 changed files with 42 additions and 16 deletions

View File

@ -4,9 +4,19 @@ import 'dart:typed_data';
class FrameDependencyManager { class FrameDependencyManager {
Uint8List? _sps; Uint8List? _sps;
Uint8List? _pps; Uint8List? _pps;
final int windowSize = 30; final int windowSize = 10;
final List<int> _iFrameSeqWindow = []; final List<int> _iFrameSeqWindow = [];
//
int _dropCount = 0;
final int dropThreshold = 10; //
///
void reset() {
_iFrameSeqWindow.clear();
_dropCount = 0;
}
/// SPS缓存 /// SPS缓存
void updateSps(Uint8List? sps) { void updateSps(Uint8List? sps) {
_sps = sps; _sps = sps;
@ -21,15 +31,28 @@ class FrameDependencyManager {
/// I帧 /// I帧
bool get hasIFrame => _iFrameSeqWindow.isNotEmpty; bool get hasIFrame => _iFrameSeqWindow.isNotEmpty;
int? get lastIFrameSeq => _iFrameSeqWindow.isNotEmpty ? _iFrameSeqWindow.last : null; int? get lastIFrameSeq => _iFrameSeqWindow.isNotEmpty ? _iFrameSeqWindow.last : null;
int get dropCount => _dropCount;
int get dropThresholdValue => dropThreshold;
void updateIFrameSeq(int seq) { void updateIFrameSeq(int seq) {
_iFrameSeqWindow.add(seq); _iFrameSeqWindow.add(seq);
if (_iFrameSeqWindow.length > windowSize) { if (_iFrameSeqWindow.length > windowSize) {
_iFrameSeqWindow.removeAt(0); _iFrameSeqWindow.removeAt(0);
} }
_dropCount = 0; // I帧解码时重置丢帧计数
// print('[FrameDependencyManager][调试] I帧解码成功序号: $seq当前I帧窗口: ${_iFrameSeqWindow.toString()}');
} }
/// I帧序号是否在滑动窗口内 /// I帧序号是否在滑动窗口内
bool isIFrameDecoded(int? seq) { bool isIFrameDecoded(int? seq) {
return seq != null && _iFrameSeqWindow.contains(seq); if (seq == null || !_iFrameSeqWindow.contains(seq)) {
_dropCount++;
// print('[FrameDependencyManager][调试] 当前I帧窗口: ${_iFrameSeqWindow.toString()}待查找I帧序号: $seq');
if (_dropCount >= dropThreshold) {
print('[FrameDependencyManager][自愈] 连续丢帧$_dropCount次自动reset依赖窗口');
reset();
}
return false;
}
return true;
} }
} }

View File

@ -67,4 +67,15 @@ class NaluUtils {
} }
return -1; return -1;
} }
/// type的NALU并拼接返回
static List<int> filterNalusByType(List<int> frameData, int type) {
final nalus = splitNalus(frameData);
final filtered = nalus.where((nalu) => nalu.type == type).toList();
final result = <int>[];
for (final nalu in filtered) {
result.addAll(nalu.data);
}
return result;
}
} }

View File

@ -236,10 +236,11 @@ class VideoDecodePlugin {
// P帧依赖链完整性校验 // P帧依赖链完整性校验
if (frameType == 1) { if (frameType == 1) {
if (!_depManager.isIFrameDecoded(refIFrameSeq)) { if (!_depManager.isIFrameDecoded(refIFrameSeq)) {
print( print('[丢帧] P帧依赖的I帧未解码丢弃 frameSeq=$frameSeq, refIFrameSeq=$refIFrameSeq, dropCount=${_depManager.dropCount}, threshold=${_depManager.dropThresholdValue}');
'[丢帧] P帧依赖的I帧未解码丢弃 frameSeq=$frameSeq, refIFrameSeq=$refIFrameSeq');
return; return;
} }
// type=1NALU
frameData = NaluUtils.filterNalusByType(frameData, 1);
} }
await _decodeFrame( await _decodeFrame(
frameData: Uint8List.fromList(frameData), frameData: Uint8List.fromList(frameData),
@ -261,21 +262,12 @@ class VideoDecodePlugin {
}) async { }) async {
// P帧做AnnexB起始码检测和修正 // P帧做AnnexB起始码检测和修正
if (frameType == 1) { if (frameType == 1) {
// NALU并只保留type=1NALU // type=1NALU
final nalus = NaluUtils.splitNalus(frameData); frameData = NaluUtils.filterNalusByType(frameData, 1);
final pNalus = nalus.where((nalu) => nalu.type == 1).toList(); if (frameData.isEmpty) {
if (pNalus.isEmpty) {
print('[VideoDecodePlugin][警告] iOS端P帧未找到type=1的NALU已丢弃'); print('[VideoDecodePlugin][警告] iOS端P帧未找到type=1的NALU已丢弃');
return; return;
} }
// type=1NALU
final sendData = <int>[];
for (final nalu in pNalus) {
sendData.addAll(nalu.data);
}
// print(
// '[VideoDecodePlugin][调试] iOS端P帧仅推送type=1 NALU, count=${pNalus.length}, 总长度=${sendData.length}');
frameData = sendData;
int startIndex = -1; int startIndex = -1;
for (int i = 0; i < frameData.length - 3; i++) { for (int i = 0; i < frameData.length - 3; i++) {
if (frameData[i] == 0x00 && if (frameData[i] == 0x00 &&