2025-04-28 01:08:21 +00:00
|
|
|
|
# video_decode_plugin
|
|
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
一个高性能的 Flutter 插件,用于在 Android 原生层解码 H.264 裸流数据,并支持两种渲染模式。
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
## 功能特点
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
- 支持 H.264 Annex B 格式裸流解码(含 NALU 单元)
|
|
|
|
|
|
- 使用 Android MediaCodec 硬解码,提供高性能解码能力
|
|
|
|
|
|
- 支持两种渲染模式:
|
|
|
|
|
|
- Flutter 纹理渲染:将解码后的帧通过 Flutter Texture 传递到 Flutter UI
|
|
|
|
|
|
- 原生 SurfaceView 渲染:在原生 Android 层直接渲染
|
|
|
|
|
|
- 提供完善的配置管理和性能监控
|
|
|
|
|
|
- 支持动态丢帧策略,优化内存使用
|
|
|
|
|
|
- 适配低端设备的性能优化措施
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
## 安装
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
在 `pubspec.yaml` 文件中添加依赖:
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
```yaml
|
|
|
|
|
|
dependencies:
|
|
|
|
|
|
video_decode_plugin: ^0.0.1
|
|
|
|
|
|
```
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
## 使用方法
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
### 基本用法
|
|
|
|
|
|
|
|
|
|
|
|
```dart
|
|
|
|
|
|
import 'package:video_decode_plugin/video_decode_plugin.dart';
|
|
|
|
|
|
|
|
|
|
|
|
// 创建解码器(Flutter 渲染模式)
|
|
|
|
|
|
final decoder = H264Decoder(renderMode: RenderMode.flutter);
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
// 初始化解码器
|
|
|
|
|
|
await decoder.init(
|
|
|
|
|
|
const H264DecoderConfig(
|
|
|
|
|
|
bufferSize: 10,
|
|
|
|
|
|
maxWidth: 1280,
|
|
|
|
|
|
maxHeight: 720,
|
|
|
|
|
|
useDropFrameStrategy: true,
|
|
|
|
|
|
debugMode: true,
|
|
|
|
|
|
),
|
|
|
|
|
|
);
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
// 开始解码
|
|
|
|
|
|
await decoder.start();
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
// 输入 H.264 数据
|
|
|
|
|
|
await decoder.feedData(h264Data);
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
// 暂停解码
|
|
|
|
|
|
await decoder.pause();
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
// 恢复解码
|
|
|
|
|
|
await decoder.resume();
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
// 释放资源
|
|
|
|
|
|
await decoder.release();
|
|
|
|
|
|
```
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
### 渲染视图
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
#### Flutter 渲染模式
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
```dart
|
|
|
|
|
|
// 使用 Flutter 渲染模式显示视频
|
|
|
|
|
|
H264VideoPlayerWidget(
|
|
|
|
|
|
decoder: decoder,
|
|
|
|
|
|
width: 640,
|
|
|
|
|
|
height: 360,
|
|
|
|
|
|
backgroundColor: Colors.black,
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
#### 原生渲染模式
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
```dart
|
|
|
|
|
|
// 使用原生渲染模式显示视频
|
|
|
|
|
|
const H264NativePlayerWidget(
|
|
|
|
|
|
width: 640,
|
|
|
|
|
|
height: 360,
|
|
|
|
|
|
backgroundColor: Colors.black,
|
|
|
|
|
|
)
|
|
|
|
|
|
```
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
### 监听事件
|
|
|
|
|
|
|
|
|
|
|
|
```dart
|
|
|
|
|
|
// 订阅解码器事件
|
|
|
|
|
|
decoder.eventStream.listen((event) {
|
|
|
|
|
|
switch (event.type) {
|
|
|
|
|
|
case H264DecoderEventType.frameAvailable:
|
|
|
|
|
|
// 新帧可用
|
|
|
|
|
|
break;
|
|
|
|
|
|
case H264DecoderEventType.stats:
|
|
|
|
|
|
// 性能统计信息
|
|
|
|
|
|
final stats = event.data as Map<String, dynamic>;
|
|
|
|
|
|
print('总帧数: ${stats['totalFrames']}');
|
|
|
|
|
|
print('丢弃帧数: ${stats['droppedFrames']}');
|
|
|
|
|
|
print('缓冲区使用: ${stats['bufferUsage']}');
|
|
|
|
|
|
print('解码耗时: ${stats['lastDecodingTimeMs']}ms');
|
|
|
|
|
|
break;
|
|
|
|
|
|
case H264DecoderEventType.error:
|
|
|
|
|
|
// 解码错误
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
```
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
## 配置选项
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
`H264DecoderConfig` 类提供以下配置选项:
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
| 参数 | 类型 | 默认值 | 说明 |
|
|
|
|
|
|
|-----|------|-------|-----|
|
|
|
|
|
|
| bufferSize | int | 5 | 缓冲区大小(帧数) |
|
|
|
|
|
|
| maxWidth | int | 1280 | 最大解码宽度 |
|
|
|
|
|
|
| maxHeight | int | 720 | 最大解码高度 |
|
|
|
|
|
|
| useDropFrameStrategy | bool | true | 是否启用丢帧策略 |
|
|
|
|
|
|
| debugMode | bool | false | 是否启用调试模式 |
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
## 性能优化
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
本插件提供多项性能优化措施:
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
1. **动态丢帧策略**:缓冲区满时,优先保留 I 帧,丢弃 P 帧,确保解码连续性。
|
|
|
|
|
|
2. **零拷贝传输**:使用 Surface 和 SurfaceTexture 直接渲染,避免内存拷贝。
|
|
|
|
|
|
3. **异步处理**:解码和渲染在独立线程进行,不阻塞主线程。
|
|
|
|
|
|
4. **低端设备适配**:可设置最大解码分辨率,避免低端设备性能问题。
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
## 示例应用
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
本项目自带一个完整的示例应用,演示如何使用这个插件播放 H.264 视频流。
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
运行示例:
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
```shell
|
|
|
|
|
|
cd example
|
|
|
|
|
|
flutter run
|
|
|
|
|
|
```
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
## 注意事项
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
- 目前仅支持 Android 平台
|
|
|
|
|
|
- 需要确保输入的 H.264 数据是有效的 Annex B 格式(含 NALU 开始码)
|
|
|
|
|
|
- 建议在实际项目中根据设备性能动态调整解码配置
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
## 许可证
|
2025-04-28 01:08:21 +00:00
|
|
|
|
|
2025-04-21 10:56:28 +08:00
|
|
|
|
MIT
|