feat:增加Android下提取P帧nalu逻辑;增加长时间找不到I帧时的reset逻辑
This commit is contained in:
parent
a8bb1f12f4
commit
38df1883f5
@ -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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -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=1的NALU,提升平台一致性
|
||||||
|
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=1的NALU
|
// 仅推送type=1的NALU,提升平台一致性
|
||||||
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=1的NALU
|
|
||||||
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 &&
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user