import 'dart:async'; import 'dart:io'; import 'dart:typed_data'; import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; import 'video_decode_plugin_platform_interface.dart'; /// H.265/HEVC NAL单元类型定义 class HevcNalUnitType { static const int TRAIL_N = 0; // 尾随图片 - 非参考图片 static const int TRAIL_R = 1; // 尾随图片 - 参考图片 static const int TSA_N = 2; // 时间子层访问 - 非参考图片 static const int TSA_R = 3; // 时间子层访问 - 参考图片 static const int STSA_N = 4; // 分步时间子层访问 - 非参考图片 static const int STSA_R = 5; // 分步时间子层访问 - 参考图片 static const int RADL_N = 6; // 随机访问解码先导 - 非参考图片 static const int RADL_R = 7; // 随机访问解码先导 - 参考图片 static const int RASL_N = 8; // 随机访问跳过先导 - 非参考图片 static const int RASL_R = 9; // 随机访问跳过先导 - 参考图片 static const int RSV_VCL_N10 = 10; // 保留的非IRAP VCL NAL单元类型 static const int RSV_VCL_R11 = 11; // 保留的非IRAP VCL NAL单元类型 static const int RSV_VCL_N12 = 12; // 保留的非IRAP VCL NAL单元类型 static const int RSV_VCL_R13 = 13; // 保留的非IRAP VCL NAL单元类型 static const int RSV_VCL_N14 = 14; // 保留的非IRAP VCL NAL单元类型 static const int RSV_VCL_R15 = 15; // 保留的非IRAP VCL NAL单元类型 static const int BLA_W_LP = 16; // 有前导的无损拼接访问 static const int BLA_W_RADL = 17; // 有RADL的无损拼接访问 static const int BLA_N_LP = 18; // 无前导的无损拼接访问 static const int IDR_W_RADL = 19; // 有RADL的瞬时解码刷新 (IDR) static const int IDR_N_LP = 20; // 无前导的瞬时解码刷新 (IDR) static const int CRA_NUT = 21; // 清理随机访问 static const int RSV_IRAP_VCL22 = 22; // 保留的IRAP VCL NAL单元类型 static const int RSV_IRAP_VCL23 = 23; // 保留的IRAP VCL NAL单元类型 static const int RSV_VCL24 = 24; // 保留的VCL NAL单元类型 static const int RSV_VCL25 = 25; // 保留的VCL NAL单元类型 static const int RSV_VCL26 = 26; // 保留的VCL NAL单元类型 static const int RSV_VCL27 = 27; // 保留的VCL NAL单元类型 static const int RSV_VCL28 = 28; // 保留的VCL NAL单元类型 static const int RSV_VCL29 = 29; // 保留的VCL NAL单元类型 static const int RSV_VCL30 = 30; // 保留的VCL NAL单元类型 static const int RSV_VCL31 = 31; // 保留的VCL NAL单元类型 // 非VCL NAL单元类型 static const int VPS = 32; // 视频参数集 static const int SPS = 33; // 序列参数集 static const int PPS = 34; // 图像参数集 static const int AUD = 35; // 访问单元分隔符 static const int EOS = 36; // 序列结束 static const int EOB = 37; // 比特流结束 static const int FD = 38; // 填充数据 static const int PREFIX_SEI = 39; // 前缀辅助增强信息 static const int SUFFIX_SEI = 40; // 后缀辅助增强信息 static const int RSV_NVCL41 = 41; // 保留的非VCL NAL单元类型 static const int RSV_NVCL42 = 42; // 保留的非VCL NAL单元类型 static const int RSV_NVCL43 = 43; // 保留的非VCL NAL单元类型 static const int RSV_NVCL44 = 44; // 保留的非VCL NAL单元类型 static const int RSV_NVCL45 = 45; // 保留的非VCL NAL单元类型 static const int RSV_NVCL46 = 46; // 保留的非VCL NAL单元类型 static const int RSV_NVCL47 = 47; // 保留的非VCL NAL单元类型 static const int UNSPEC48 = 48; // 未指定的类型 static const int UNSPEC49 = 49; // 未指定的类型 static const int UNSPEC50 = 50; // 未指定的类型 static const int UNSPEC51 = 51; // 未指定的类型 static const int UNSPEC52 = 52; // 未指定的类型 static const int UNSPEC53 = 53; // 未指定的类型 static const int UNSPEC54 = 54; // 未指定的类型 static const int UNSPEC55 = 55; // 未指定的类型 static const int UNSPEC56 = 56; // 未指定的类型 static const int UNSPEC57 = 57; // 未指定的类型 static const int UNSPEC58 = 58; // 未指定的类型 static const int UNSPEC59 = 59; // 未指定的类型 static const int UNSPEC60 = 60; // 未指定的类型 static const int UNSPEC61 = 61; // 未指定的类型 static const int UNSPEC62 = 62; // 未指定的类型 static const int UNSPEC63 = 63; // 未指定的类型 // 帧类型别名,方便判断 // I帧类型:IDR_W_RADL, IDR_N_LP, BLA_W_LP, BLA_W_RADL, BLA_N_LP, CRA_NUT static const List KEY_FRAMES = [ IDR_W_RADL, IDR_N_LP, BLA_W_LP, BLA_W_RADL, BLA_N_LP, CRA_NUT ]; // 参数集类型:VPS, SPS, PPS static const List PARAMETER_SETS = [VPS, SPS, PPS]; /// 判断是否为关键帧NAL类型 static bool isKeyFrame(int nalUnitType) { return KEY_FRAMES.contains(nalUnitType); } /// 判断是否为参数集NAL类型 static bool isParameterSet(int nalUnitType) { return PARAMETER_SETS.contains(nalUnitType); } /// 判断是否为IDR帧 static bool isIdrFrame(int nalUnitType) { return nalUnitType == IDR_W_RADL || nalUnitType == IDR_N_LP; } /// 获取NAL单元类型名称 static String getName(int type) { switch (type) { case TRAIL_N: return "TRAIL_N"; case TRAIL_R: return "TRAIL_R"; case TSA_N: return "TSA_N"; case TSA_R: return "TSA_R"; case STSA_N: return "STSA_N"; case STSA_R: return "STSA_R"; case RADL_N: return "RADL_N"; case RADL_R: return "RADL_R"; case RASL_N: return "RASL_N"; case RASL_R: return "RASL_R"; case BLA_W_LP: return "BLA_W_LP"; case BLA_W_RADL: return "BLA_W_RADL"; case BLA_N_LP: return "BLA_N_LP"; case IDR_W_RADL: return "IDR_W_RADL"; case IDR_N_LP: return "IDR_N_LP"; case CRA_NUT: return "CRA_NUT"; case VPS: return "VPS"; case SPS: return "SPS"; case PPS: return "PPS"; case AUD: return "AUD"; case EOS: return "EOS"; case EOB: return "EOB"; case FD: return "FD"; case PREFIX_SEI: return "PREFIX_SEI"; case SUFFIX_SEI: return "SUFFIX_SEI"; default: if (type >= 10 && type <= 15) return "RSV_VCL_${type}"; if (type >= 22 && type <= 23) return "RSV_IRAP_VCL${type}"; if (type >= 24 && type <= 31) return "RSV_VCL${type}"; if (type >= 41 && type <= 47) return "RSV_NVCL${type}"; if (type >= 48 && type <= 63) return "UNSPEC${type}"; return "未知(${type})"; } } } /// 视频帧类型 enum FrameType { /// I帧 iFrame, /// P帧 pFrame, } /// 视频编码类型 enum CodecType { /// H.264编码 h264, /// H.265编码 h265, } /// 帧可用回调函数类型 typedef FrameAvailableCallback = void Function(int textureId); /// 解码器实例内部类 class _DecoderInstance { final int textureId; FrameAvailableCallback? frameCallback; _DecoderInstance(this.textureId); } /// 视频解码器配置 class VideoDecoderConfig { /// 视频宽度,默认640 final int width; /// 视频高度,默认360 final int height; /// 帧率,可为空 final int? frameRate; /// 编码类型,默认h264 final CodecType codecType; /// 缓冲区大小(帧数),默认25帧 final int bufferSize; /// 解码线程数,默认1线程 final int threadCount; /// 是否为调试模式,默认false final bool isDebug; /// 是否启用硬件解码,默认true final bool enableHardwareDecoder; /// 是否启用动态阈值,默认true final bool enableDynamicThresholds; /// 初始最大连续P帧数,默认10 final int initialMaxPFrames; /// 初始I帧超时时间(毫秒),默认500 final int initialIFrameTimeoutMs; /// 最小最大连续P帧数,默认5 final int minMaxPFrames; /// 最大最大连续P帧数,默认30 final int maxMaxPFrames; /// 构造函数 VideoDecoderConfig({ this.width = 640, this.height = 360, this.frameRate, this.codecType = CodecType.h264, this.bufferSize = 25, this.threadCount = 1, this.isDebug = false, this.enableHardwareDecoder = true, this.enableDynamicThresholds = true, this.initialMaxPFrames = 10, this.initialIFrameTimeoutMs = 500, this.minMaxPFrames = 5, this.maxMaxPFrames = 30, }); /// 转换为Map Map toMap() { return { 'width': width, 'height': height, 'frameRate': frameRate, 'codecType': codecType.toString().split('.').last, 'bufferSize': bufferSize, 'threadCount': threadCount, 'isDebug': isDebug, 'enableHardwareDecoder': enableHardwareDecoder, 'enableDynamicThresholds': enableDynamicThresholds, 'initialMaxPFrames': initialMaxPFrames, 'initialIFrameTimeoutMs': initialIFrameTimeoutMs, 'minMaxPFrames': minMaxPFrames, 'maxMaxPFrames': maxMaxPFrames, }; } } /// 视频解码插件主类 class VideoDecodePlugin { static const MethodChannel _channel = MethodChannel('video_decode_plugin'); // 解码器映射表,支持多实例 static final Map _decoders = {}; // 默认解码器ID static int? _defaultTextureId; // 监听器初始化标志 static bool _listenerInitialized = false; // 是否处于调试模式 static bool _isDebugMode = false; // 解码器状态跟踪 - 防止释放后继续使用 static final Map _isDecoderReleasing = {}; // 解码器状态锁 - 防止并发访问状态 static final _decoderStateLock = Object(); // 错误日志抑制 - 防止重复日志 static int _uninitializedErrorCount = 0; static int _lastErrorLogTime = 0; static const int _ERROR_LOG_THRESHOLD = 5; // 每5秒最多输出一次汇总 /// 日志输出控制 - 调试信息 static void _logDebug(String message) { if (_isDebugMode) { debugPrint('[VideoDecodePlugin] $message'); } } /// 日志输出控制 - 错误信息(总是输出) static void _logError(String message, {bool throttle = false}) { if (throttle) { // 增加计数 _uninitializedErrorCount++; // 检查是否需要输出汇总日志 final now = DateTime.now().millisecondsSinceEpoch; if (now - _lastErrorLogTime > 5000 || _uninitializedErrorCount >= 50) { debugPrint( '[VideoDecodePlugin] ERROR: $message (发生 $_uninitializedErrorCount 次)'); _lastErrorLogTime = now; _uninitializedErrorCount = 0; } } else { // 直接输出日志 debugPrint('[VideoDecodePlugin] ERROR: $message'); } } /// 初始化方法通道监听器 static void _initializeMethodCallHandler() { if (!_listenerInitialized) { _channel.setMethodCallHandler((call) async { switch (call.method) { case 'onFrameAvailable': final Map args = call.arguments; final int textureId = args['textureId']; // 检查解码器是否正在释放 bool isReleasing = false; // 同步访问解码器状态 _withLock(_decoderStateLock, () { isReleasing = _isDecoderReleasing[textureId] ?? false; }); if (isReleasing) { _logDebug('收到帧通知但解码器 $textureId 正在释放,忽略'); return null; } // 调用特定纹理ID的帧回调 final decoder = _decoders[textureId]; if (decoder != null && decoder.frameCallback != null) { // 获取解码器统计信息来检查是否是预通知 getDecoderStats(textureId).then((stats) { final renderedFrames = stats['renderedFrames'] ?? 0; if (renderedFrames == 0) { _logDebug('[预通知] 收到初始帧可用通知(无实际视频数据),纹理ID: $textureId'); } else { _logDebug('收到帧可用通知,纹理ID: $textureId,已渲染帧数: $renderedFrames'); } // 调用回调函数 decoder.frameCallback!(textureId); }).catchError((error) { // 如果无法获取统计信息,仍然调用回调但不区分类型 _logDebug('收到帧可用通知,纹理ID: $textureId'); decoder.frameCallback!(textureId); }); } return null; default: throw PlatformException( code: 'Unimplemented', details: 'The method ${call.method} is not implemented', ); } }); _listenerInitialized = true; } } /// 执行同步操作的辅助方法 static void _withLock(Object lock, Function() action) { // 在Dart中,Object实例可以直接用作锁对象 synchronized(lock, action); } /// 在锁保护下执行操作并返回结果 static T _withLockResult(Object lock, T Function() action) { return synchronizedWithResult(lock, action); } /// 检查解码器是否处于可用状态 static bool _isDecoderReady(int textureId) { bool isReleasing = false; _withLock(_decoderStateLock, () { isReleasing = _isDecoderReleasing[textureId] ?? false; }); return _decoders.containsKey(textureId) && !isReleasing; } /// 设置解码器释放状态 static void _setDecoderReleasing(int textureId, bool isReleasing) { _withLock(_decoderStateLock, () { if (isReleasing) { _isDecoderReleasing[textureId] = true; } else { _isDecoderReleasing.remove(textureId); } }); } /// 获取平台版本 static Future getPlatformVersion() { return VideoDecodePluginPlatform.instance.getPlatformVersion(); } /// 检查当前平台是否支持 static bool get isPlatformSupported { return Platform.isAndroid || Platform.isIOS; } /// 设置帧回调(默认解码器) static void setFrameCallback(FrameAvailableCallback callback) { if (_defaultTextureId != null) { setFrameCallbackForTexture(_defaultTextureId!, callback); } } /// 为特定纹理ID设置帧回调 static void setFrameCallbackForTexture( int textureId, FrameAvailableCallback callback) { _initializeMethodCallHandler(); final decoder = _decoders[textureId]; if (decoder != null) { decoder.frameCallback = callback; } } /// 初始化解码器 static Future initDecoder(VideoDecoderConfig config) async { // 设置调试模式 _isDebugMode = config.isDebug; // 重置错误计数 _uninitializedErrorCount = 0; // 先释放之前的默认解码器 if (_defaultTextureId != null) { await releaseDecoder(); } return await createDecoder(config); } /// 创建新的解码器实例(支持多实例) static Future createDecoder(VideoDecoderConfig config) async { // 更新调试模式 _isDebugMode = config.isDebug; // 重置错误计数 _uninitializedErrorCount = 0; if (!isPlatformSupported) { _logError('当前平台不支持视频解码插件'); return null; } // 确保监听器已初始化 _initializeMethodCallHandler(); try { _logDebug( '创建解码器: ${config.width}x${config.height}, 编码: ${config.codecType}'); final textureId = await _channel.invokeMethod('initDecoder', config.toMap()); if (textureId != null) { // 创建新解码器实例并保存 final decoder = _DecoderInstance(textureId); _decoders[textureId] = decoder; // 初始化解码器状态 _setDecoderReleasing(textureId, false); // 设置为默认解码器 _defaultTextureId = textureId; _logDebug('解码器创建成功,纹理ID: $textureId'); } return _defaultTextureId; } catch (e) { _logError('初始化解码器失败: $e'); return null; } } /// 获取默认纹理ID static int? get textureId => _defaultTextureId; /// 获取所有活跃的纹理ID static List get allTextureIds => _decoders.keys.toList(); /// 解码视频帧(默认解码器) static Future decodeFrame( Uint8List frameData, FrameType frameType) async { // 使用本地变量缓存ID,防止并发修改 final int? decoderId = _defaultTextureId; if (decoderId == null) { // 使用节流日志报告错误,避免日志爆炸 _logError('解码器未初始化', throttle: true); return false; } // 检查解码器是否正在释放 if (!_isDecoderReady(decoderId)) { _logDebug('解码器正在释放,忽略解码请求'); return false; } return decodeFrameForTexture(decoderId, frameData, frameType); } /// 为特定纹理ID解码视频帧 static Future decodeFrameForTexture( int textureId, Uint8List frameData, FrameType frameType) async { // 检查解码器是否存在且不在释放过程中 if (!_isDecoderReady(textureId)) { _logDebug('解码器不可用或正在释放,忽略解码请求'); return false; } try { final bool isIFrame = frameType == FrameType.iFrame; _logDebug( '解码帧: textureId=$textureId, 大小=${frameData.length}字节, 类型=${isIFrame ? "I帧" : "P帧"}'); final result = await _channel.invokeMethod('decodeFrame', { 'textureId': textureId, 'frameData': frameData, 'frameType': frameType.index, }) ?? false; if (!result) { _logDebug('解码帧失败'); } return result; } catch (e) { // 检查是否是因为解码器已释放导致的错误 if (!_decoders.containsKey(textureId)) { _logDebug('解码器已释放,忽略解码错误'); return false; } _logError('解码帧失败: $e'); return false; } } /// 释放默认解码器资源 static Future releaseDecoder() async { final int? decoderId = _defaultTextureId; if (decoderId == null) { return true; } final result = await releaseDecoderForTexture(decoderId); if (result) { _defaultTextureId = null; } return result; } /// 释放特定纹理ID的解码器资源 static Future releaseDecoderForTexture(int textureId) async { // 检查解码器是否存在 if (!_decoders.containsKey(textureId)) { return true; } // 标记解码器正在释放,防止新的解码请求 _setDecoderReleasing(textureId, true); try { _logDebug('释放解码器: textureId=$textureId'); // 清除回调,防止帧回调继续被调用 clearCallbackForTexture(textureId); final result = await _channel.invokeMethod('releaseDecoder', { 'textureId': textureId, }) ?? false; if (result) { // 从映射表中移除 _decoders.remove(textureId); // 如果释放的是默认解码器,重置默认ID if (_defaultTextureId == textureId) { _defaultTextureId = null; } // 移除释放状态 _setDecoderReleasing(textureId, false); // 重置错误计数 _uninitializedErrorCount = 0; _logDebug('解码器释放成功: textureId=$textureId'); } else { // 释放失败,恢复状态 _setDecoderReleasing(textureId, false); _logError('解码器释放失败: textureId=$textureId'); } return result; } catch (e) { // 发生异常,但仍然移除解码器,避免资源泄漏 _decoders.remove(textureId); if (_defaultTextureId == textureId) { _defaultTextureId = null; } _setDecoderReleasing(textureId, false); _logError('释放解码器失败: $e'); return false; } } /// 释放所有解码器 static Future releaseAllDecoders() async { bool allSuccess = true; // 复制键列表,因为我们会在迭代过程中修改映射 final textureIds = List.from(_decoders.keys); _logDebug('释放所有解码器: 共${textureIds.length}个'); // 释放每个解码器 for (final textureId in textureIds) { final success = await releaseDecoderForTexture(textureId); if (!success) { allSuccess = false; } } // 清空状态 _decoders.clear(); _defaultTextureId = null; // 清空所有释放状态 _withLock(_decoderStateLock, () { _isDecoderReleasing.clear(); }); // 重置错误计数 _uninitializedErrorCount = 0; return allSuccess; } /// 清除特定纹理ID的回调 static void clearCallbackForTexture(int textureId) { final decoder = _decoders[textureId]; if (decoder != null) { decoder.frameCallback = null; _logDebug('已清除纹理ID为$textureId的回调'); } } /// 清除所有回调 static void clearAllCallbacks() { for (final decoder in _decoders.values) { decoder.frameCallback = null; } _logDebug('已清除所有回调'); } /// 注册插件(不需要手动调用) static void registerWith() { // 仅用于插件注册 } /// 获取解码器统计信息 /// /// [textureId] 纹理ID /// 返回包含统计信息的Map,包括: /// - totalFramesReceived: 接收的总帧数 /// - framesRendered: 成功渲染的帧数 /// - framesDropped: 丢弃的帧数 /// - lastFrameTimestamp: 最后一帧时间戳 /// - averageProcessingTimeMs: 平均处理时间(毫秒) /// - decoderCount: 当前活跃的解码器数量 static Future> getDecoderStats(int textureId) async { // 检查解码器是否正在释放 if (!_isDecoderReady(textureId)) { _logDebug('解码器不可用或正在释放,无法获取统计信息'); return {}; } try { _logDebug('获取解码器统计信息: textureId=$textureId'); final params = { 'textureId': textureId, }; final result = await _channel.invokeMethod>( 'getDecoderStats', params); if (result == null) { return {}; } // 将Object?类型转换为明确的类型 final Map typedResult = {}; result.forEach((key, value) { if (key is String) { typedResult[key] = value; } }); _logDebug('获取解码器统计信息成功: $typedResult'); return typedResult; } catch (e) { _logError('获取解码器统计信息失败: $e'); return {}; } } /// 获取当前渲染FPS /// /// 返回当前解码器的实时渲染帧率 /// 如果解码器未初始化或获取失败,返回0.0 static Future getCurrentFps([int? textureId]) async { final targetTextureId = textureId ?? _defaultTextureId; if (targetTextureId == null) { return 0.0; } try { final stats = await getDecoderStats(targetTextureId); return stats['fps'] as double? ?? 0.0; } catch (e) { _logError('获取FPS失败: $e'); return 0.0; } } /// 获取动态阈值参数 /// /// 返回当前解码器使用的动态阈值参数 /// 包括检测到的GOP大小、最大连续P帧数限制、I帧超时时间等 static Future> getDynamicThresholdParams( [int? textureId]) async { final targetTextureId = textureId ?? _defaultTextureId; if (targetTextureId == null) { return {}; } try { final stats = await getDecoderStats(targetTextureId); return { 'detectedGopSize': stats['detectedGopSize'] as int? ?? 0, 'dynamicMaxConsecutivePFrames': stats['dynamicMaxConsecutivePFrames'] as int? ?? 0, 'dynamicIFrameTimeoutMs': stats['dynamicIFrameTimeoutMs'] as int? ?? 0, 'enableDynamicThresholds': stats['enableDynamicThresholds'] as bool? ?? false, }; } catch (e) { _logError('获取动态阈值参数失败: $e'); return {}; } } } /// 在Dart中实现简单的同步锁 void synchronized(Object lock, Function() action) { // 在单线程的Dart中,我们不需要真正的锁 // 但我们保留这个结构以便将来可能的改进 action(); } /// 在同步锁中执行并返回结果的版本 T synchronizedWithResult(Object lock, T Function() action) { return action(); }