2025-05-07 15:20:54 +08:00

138 lines
5.6 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Video Decode Plugin
> 当前版本0.0.1
> 最低 Flutter SDK 要求:>=3.3.0
> 最低 Dart SDK 要求:>=3.3.4
## 安装
```yaml
dependencies:
video_decode_plugin: ^0.0.1 # 使用最新版本
```
## 插件简介与环境要求
本插件为Flutter平台下的裸流H.264/H.265实时视频解码渲染插件支持Android与iOS
- Flutter SDK: >=3.3.0
- Dart SDK: >=3.3.4
- Android: minSdkVersion 21+
- iOS: 11.0+
## 快速开始(单实例标准用法)
```dart
import 'package:video_decode_plugin/video_decode_plugin.dart';
// 1. 创建解码器配置
final config = VideoDecoderConfig(
width: 640, // 视频宽度
height: 480, // 视频高度
codecType: 'h264', // 编解码类型h264或h265
);
// 2. 初始化解码器获取纹理ID
final textureId = await VideoDecodePlugin.initDecoder(config);
// 3. 设置帧渲染回调(可选)
// 第一帧被渲染时触发的回调
VideoDecodePlugin.setOnFrameRenderedListener((textureId) {
// 新帧渲染时回调
setState(() {});
});
// 4. 渲染视频Flutter端
AspectRatio(
aspectRatio: 640/480, // 建议与视频源一致
child: Container(
color: Colors.transparent,
child: Texture(
textureId: textureId!,
filterQuality: FilterQuality.medium,
),
),
)
// 5. 推送视频帧SPS/PPS/I帧/P帧
await VideoDecodePlugin.sendFrame(
frameData: sps, frameType: 0, timestamp: ..., frameSeq: ...
);
await VideoDecodePlugin.sendFrame(
frameData: pps, frameType: 0, timestamp: ..., frameSeq: ...
);
await VideoDecodePlugin.sendFrame(
frameData: iFrame, frameType: 0, timestamp: ..., frameSeq: ...
);
await VideoDecodePlugin.sendFrame(
frameData: pFrame, frameType: 1, timestamp: ..., frameSeq: ..., refIFrameSeq: ...
);
// 6. 释放资源
await VideoDecodePlugin.releaseDecoder();
```
## 推荐推送流程(伪代码)
```dart
// 发送SPS和PPS
await VideoDecodePlugin.sendFrame(frameData: sps, frameType: 0, ...);
await VideoDecodePlugin.sendFrame(frameData: pps, frameType: 0, ...);
// 发送I帧
await VideoDecodePlugin.sendFrame(frameData: iFrame, frameType: 0, ...);
// 发送P帧
await VideoDecodePlugin.sendFrame(frameData: pFrame, frameType: 1, refIFrameSeq: iFrameSeq, ...);
```
## 必须做的前置操作
- **推送顺序**务必保证每个I帧前都已推送最新SPSNAL类型7、PPSNAL类型8再推送I帧NAL类型5最后推送P帧NAL类型1
- **依赖链完整性校验**业务端需实现I帧序号管理P帧推送前校验refIFrameSeq是否为已解码I帧否则丢弃。
- **丢帧策略**:强烈建议业务端实现丢帧与依赖链管理,避免无效帧流入原生端。
- **异常自愈**:检测到解码失败/花屏/长时间无I帧时建议主动reset解码器等待下一个I帧恢复链路。
- **日志监控**:建议业务端和原生端均输出详细日志,便于端到端排查依赖链断裂、丢包、乱序等问题。
## 重要注意事项
- **只推送标准NALU**仅推送type=1的P帧、type=5的I帧、type=7/8的SPS/PPS进入解码器SEI等异常NALU自动丢弃。
- **Flutter端布局建议**建议用AspectRatio、FittedBox等包裹Texture确保宽高比一致避免白边。父容器背景色建议设为透明。
- **iOS端Texture机制说明**Flutter Texture机制下无法直接控制原生UIView属性contentMode等如需更强原生控制力可考虑PlatformView方案。
- **Android/iOS平台差异**iOS端VideoToolbox对NALU格式、推送顺序要求更严格务必保证数据流规范。
## 常见问题与排查建议
- **花屏/马赛克**多因I帧依赖链断裂、SPS/PPS/I帧推送顺序错误、数据丢包、NALU内容异常导致。请重点排查推送顺序与依赖链。
- **白边/拉伸**多因Flutter端布局宽高比不一致或父容器背景色非透明。建议用AspectRatio/FittedBox包裹Texture。
- **P帧丢弃**如P帧依赖的I帧未解码P帧会被自动丢弃防止花屏。
- **iOS端解码失败**请确保推送NALU类型、顺序、内容均规范iOS端容错性低于Android。
- **Android帧率较低**:确保使用了硬解码、三层缓冲区被正常使用
## iOS端实现架构简述
- iOS端基于VideoToolbox实现H264/H265硬件解码输出CVPixelBuffer。
- 插件内部实现了解码输入缓冲区、输出缓冲区,解码与渲染完全解耦。
- 独立渲染线程定时从输出缓冲区取帧刷新Flutter纹理支持EMA自适应帧率平滑调整提升流畅度与健壮性。
- P帧推送前会校验依赖的I帧是否已解码若依赖链断裂则P帧直接丢弃避免马赛克。
- 仅推送标准NALUtype=1的P帧、type=5的I帧、type=7/8的SPS/PPS进入解码器SEI等异常NALU自动丢弃。
- 由于Flutter Texture机制无法直接控制原生UIView属性建议Flutter端容器背景色设为透明布局自适应。
- 如需更强原生控制力可考虑自定义PlatformView方案。
## 版本历史
### 0.0.1 - 2025.04
- ✨ 初始版本发布
- 🎯 基础功能实现:
- H.264 解码支持
- 实时视频流解码
- 基础错误处理
## H.264解码注意事项
### 马赛克/花屏出现的根因
- **依赖链断裂**P/B帧依赖的I帧未被解码成功或I帧本身丢失/损坏后续P/B帧全部解码失败必然花屏。
- **SPS/PPS/I帧推送顺序错误**未按SPS→PPS→I帧顺序推送或I帧前未收到最新SPS/PPS解码器无法正确解码I帧。
- **解码链断裂后未及时reset解码器**解码器内部状态异常后续即使收到新I帧也无法恢复需reset解码器。