import 'dart:async'; import 'dart:math' as math; import 'package:fis_common/logger/logger.dart'; import 'package:fis_vid/data_channel/channel.dart'; /// Vid缓冲等待 class VidBufferWaiter { VidBufferWaiter( this.channel, { this.timeout = 10 * 1000, }); // ignore: constant_identifier_names static const _FIRST_FRAME_LOAD_TIME_LIMIT = 60 * 1000; // 一分钟 /// 数据通道 final VidDataChannel channel; /// 等待超时时间,默认10秒 final int timeout; double _frameRate = 0; int _frameCount = 0; int _framePerSize = 0; int _vidHeadSize = 0; int _maxDuration = 0; Timer? _timer; /// vid头信息尺寸(含扩展) int get vidHeaderSize => _vidHeadSize; /// 初始化 void init() { _frameRate = channel.probe.frameRate; _frameCount = channel.imageCount; _vidHeadSize = _calcHeaderSize(); _framePerSize = _calcFramePerSize(); } /// 等待首帧 Future waitSingleVid() async { logger.i("VidPlayer - Wait single vid start."); final completer = Completer(); const int waitInterval = 100; const int waitCountLimit = _FIRST_FRAME_LOAD_TIME_LIMIT ~/ waitInterval; int waitCount = 0; _timer = Timer.periodic( const Duration(milliseconds: waitInterval), (timer) { waitCount++; if (channel.isBufferedDone) { // 全部缓冲完成 timer.cancel(); completer.complete(); logger.i("VidPlayer - Wait single vid end."); return; } if (waitCount >= waitCountLimit) { completer.completeError(Exception("First frame load timeout")); logger.i("VidPlayer - Wait single vid: timeout."); } }, ); return completer.future; } /// 等待缓冲 Future waitBuffer(int frameIndex) { logger.i("VidPlayer - Wait buffer start."); final completer = Completer(); final bufferredSize = channel.getBufferSize(); final needBufferSize = _calcNeedWaitSize(frameIndex); const int waitInterval = 100; final int waitCountLimit = timeout ~/ waitInterval; int waitCount = 0; final startTime = DateTime.now(); _timer = Timer.periodic( const Duration(milliseconds: waitInterval), (timer) { // 函数 - 完成等待 void finishFn() { timer.cancel(); completer.complete(); final endTime = DateTime.now(); final spendTime = endTime.difference(startTime).inMilliseconds; logger.i("VidPlayer - Wait buffer end, spend time: $spendTime ms."); } waitCount++; final bufferedSize = channel.getBufferSize(); if (bufferedSize >= needBufferSize) { // 缓存足够 return finishFn(); } if (waitCount >= waitCountLimit) { // 等待超时 final bufferredSizeNow = channel.getBufferSize(); if (bufferredSizeNow - bufferredSize < 1024) { timer.cancel(); // 缓冲小于1k,报超时 completer.completeError(Exception("Wait buffer timeout")); logger.i("VidPlayer - Wait buffer timeout."); } else { // 放行 finishFn(); } } }, ); return completer.future; } /// 记录单帧耗时 void recordFrameSpendTime(int millseconds) { _maxDuration = math.max(_maxDuration, millseconds); } /// 取消等待 void cancel() { _timer?.cancel(); } int _calcNeedWaitSize(int frameIndex) { int needWaitSize = 0; final int bufferedSize = channel.getBufferSize(); final int totalSize = channel.getFileSize(); // 已缓冲帧数 final int bufferredFrameCount = bufferedSize ~/ _framePerSize; // 未缓冲帧数 final int unbufferredFrameCount = _frameCount - bufferredFrameCount; // 未缓冲数据字节数 final int unbufferredSize = totalSize - bufferedSize; // 未缓冲数据字节数 所需耗时 final int needBufferTime = unbufferredFrameCount * _maxDuration; // 未缓冲播放时长 final int unbufferredDuration = ((1000 / _frameRate) * unbufferredFrameCount).toInt(); // 每秒需要字节数 final int perSecondNeedSize = (_framePerSize * _frameRate).toInt(); // 缓存和播放同时结束所需耗时 int needWaitTime = needBufferTime - unbufferredDuration; if (needWaitTime < 0) { // 预计缓冲比播放快(可能是网络波动大导致出现加载) // 缓存1秒 needWaitSize = perSecondNeedSize; } else { needWaitTime += 1; //多缓存一秒 needWaitSize = needWaitTime * perSecondNeedSize; if (frameIndex > bufferredFrameCount) { // 手动定位到了后面指定帧,需要额外缓存相差的帧数 final needWaitExtraCount = frameIndex - bufferredFrameCount; final needWaitExtraSize = _framePerSize * needWaitExtraCount; needWaitSize += needWaitExtraSize; } } return math.max(unbufferredSize, needWaitSize); } int _calcFramePerSize() { final size = _calcFramesBytesSize(); final perSize = size ~/ channel.imageCount; return perSize; } int _calcFramesBytesSize() { int size = channel.getFileSize(); size -= _vidHeadSize; return size; } int _calcHeaderSize() { int size = 0; size += channel.probe.toBytes().length; size += channel.extendedData.length; size += channel.imageCount * 8; size += 16; // Header Size size += 4; // Version Size size += 4; // ImageFormat return size; } }