2025-05-15 16:47:25 +08:00
2025-05-14 09:09:27 +08:00
2025-05-07 15:07:36 +08:00
2025-05-14 09:09:27 +08:00
2025-05-15 16:47:25 +08:00
2025-04-28 09:11:51 +08:00
2025-04-28 09:11:51 +08:00
2025-04-28 09:11:51 +08:00
2025-04-28 09:11:51 +08:00
2025-04-28 09:11:51 +08:00
2025-04-28 09:11:51 +08:00
2025-05-07 15:20:54 +08:00

Video Decode Plugin

当前版本0.0.1
最低 Flutter SDK 要求:>=3.3.0
最低 Dart SDK 要求:>=3.3.4

安装

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+

快速开始(单实例标准用法)

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();

推荐推送流程(伪代码)

// 发送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解码器。
Description
可视对讲的原生端解码插件
Readme 48 MiB
Languages
Dart 46.8%
Swift 28.3%
Kotlin 22.9%
Ruby 2%