From 38df1883f5108ec1ce590ba52318815333fded38 Mon Sep 17 00:00:00 2001 From: liyi Date: Thu, 8 May 2025 10:13:44 +0800 Subject: [PATCH] =?UTF-8?q?feat:=E5=A2=9E=E5=8A=A0Android=E4=B8=8B?= =?UTF-8?q?=E6=8F=90=E5=8F=96P=E5=B8=A7nalu=E9=80=BB=E8=BE=91;=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E9=95=BF=E6=97=B6=E9=97=B4=E6=89=BE=E4=B8=8D=E5=88=B0?= =?UTF-8?q?I=E5=B8=A7=E6=97=B6=E7=9A=84reset=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/frame_dependency_manager.dart | 27 +++++++++++++++++++++++++-- lib/nalu_utils.dart | 11 +++++++++++ lib/video_decode_plugin.dart | 20 ++++++-------------- 3 files changed, 42 insertions(+), 16 deletions(-) diff --git a/lib/frame_dependency_manager.dart b/lib/frame_dependency_manager.dart index b564914..e08fafe 100644 --- a/lib/frame_dependency_manager.dart +++ b/lib/frame_dependency_manager.dart @@ -4,9 +4,19 @@ import 'dart:typed_data'; class FrameDependencyManager { Uint8List? _sps; Uint8List? _pps; - final int windowSize = 30; + final int windowSize = 10; final List _iFrameSeqWindow = []; + // 丢帧自愈相关 + int _dropCount = 0; + final int dropThreshold = 10; // 可根据需要调整 + + /// 重置依赖管理器(自愈) + void reset() { + _iFrameSeqWindow.clear(); + _dropCount = 0; + } + /// 更新SPS缓存 void updateSps(Uint8List? sps) { _sps = sps; @@ -21,15 +31,28 @@ class FrameDependencyManager { /// 判断是否有可用I帧 bool get hasIFrame => _iFrameSeqWindow.isNotEmpty; int? get lastIFrameSeq => _iFrameSeqWindow.isNotEmpty ? _iFrameSeqWindow.last : null; + int get dropCount => _dropCount; + int get dropThresholdValue => dropThreshold; void updateIFrameSeq(int seq) { _iFrameSeqWindow.add(seq); if (_iFrameSeqWindow.length > windowSize) { _iFrameSeqWindow.removeAt(0); } + _dropCount = 0; // I帧解码时重置丢帧计数 + // print('[FrameDependencyManager][调试] I帧解码成功,序号: $seq,当前I帧窗口: ${_iFrameSeqWindow.toString()}'); } /// 判断指定I帧序号是否在滑动窗口内 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; } } \ No newline at end of file diff --git a/lib/nalu_utils.dart b/lib/nalu_utils.dart index 1615081..ee09401 100644 --- a/lib/nalu_utils.dart +++ b/lib/nalu_utils.dart @@ -67,4 +67,15 @@ class NaluUtils { } return -1; } + + /// 提取所有指定type的NALU并拼接返回 + static List filterNalusByType(List frameData, int type) { + final nalus = splitNalus(frameData); + final filtered = nalus.where((nalu) => nalu.type == type).toList(); + final result = []; + for (final nalu in filtered) { + result.addAll(nalu.data); + } + return result; + } } \ No newline at end of file diff --git a/lib/video_decode_plugin.dart b/lib/video_decode_plugin.dart index 2356ed9..1fb9dc2 100644 --- a/lib/video_decode_plugin.dart +++ b/lib/video_decode_plugin.dart @@ -236,10 +236,11 @@ class VideoDecodePlugin { // P帧依赖链完整性校验(提前) if (frameType == 1) { if (!_depManager.isIFrameDecoded(refIFrameSeq)) { - print( - '[丢帧] P帧依赖的I帧未解码,丢弃 frameSeq=$frameSeq, refIFrameSeq=$refIFrameSeq'); + print('[丢帧] P帧依赖的I帧未解码,丢弃 frameSeq=$frameSeq, refIFrameSeq=$refIFrameSeq, dropCount=${_depManager.dropCount}, threshold=${_depManager.dropThresholdValue}'); return; } + // 仅推送type=1的NALU,提升平台一致性 + frameData = NaluUtils.filterNalusByType(frameData, 1); } await _decodeFrame( frameData: Uint8List.fromList(frameData), @@ -261,21 +262,12 @@ class VideoDecodePlugin { }) async { // 仅P帧做AnnexB起始码检测和修正 if (frameType == 1) { - // 分割NALU并只保留type=1的NALU - final nalus = NaluUtils.splitNalus(frameData); - final pNalus = nalus.where((nalu) => nalu.type == 1).toList(); - if (pNalus.isEmpty) { + // 仅推送type=1的NALU,提升平台一致性 + frameData = NaluUtils.filterNalusByType(frameData, 1); + if (frameData.isEmpty) { print('[VideoDecodePlugin][警告] iOS端P帧未找到type=1的NALU,已丢弃'); return; } - // 拼接所有type=1的NALU - final sendData = []; - 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; for (int i = 0; i < frameData.length - 3; i++) { if (frameData[i] == 0x00 &&