使用C# Emgu.CV查找.ts文件指定字节位置对应的最近视频帧索引
基于Emgu.CV实现TS文件指定字节位置对应最近帧索引查询
实现思路
Emgu.CV底层封装的FFmpeg提供了读取当前流字节位置的属性,只需先构建所有帧的起始字节偏移与帧索引的映射表,再通过二分查找即可快速得到目标位置对应的最近帧索引。
具体实现步骤
- 第一步:预构建偏移-帧索引映射表
遍历TS文件所有视频帧,记录每帧读取前的字节偏移量与对应帧索引,存入有序集合。 - 第二步:查询最近帧索引
对有序偏移集合做二分查找,找到与目标字节位置差值最小的偏移项,对应的索引即为所求。
代码示例
using Emgu.CV; using Emgu.CV.CvEnum; public static class TsFrameQuery { // 预生成帧偏移映射表 public static List<(long byteOffset, int frameIndex)> BuildFrameOffsetMap(string tsFilePath, int videoStreamIndex = 0) { var offsetMap = new List<(long, int)>(); using var capture = new VideoCapture(tsFilePath); // 指定视频流索引,避免读取到音频/字幕流的偏移 capture.SetCaptureProperty(CapProp.StreamIndex, videoStreamIndex); int frameIndex = 0; using var mat = new Mat(); // 先取初始偏移,再读取帧 long currentOffset = (long)capture.GetCaptureProperty(CapProp.PosBytes); while (capture.Read(mat)) { if (mat.IsEmpty) break; offsetMap.Add((currentOffset, frameIndex)); frameIndex++; // 更新为下一帧的起始偏移 currentOffset = (long)capture.GetCaptureProperty(CapProp.PosBytes); } return offsetMap; } // 查询最近帧索引 public static int GetNearestFrameIndex(List<(long byteOffset, int frameIndex)> offsetMap, long targetBytePos) { // 二分查找第一个大于等于目标位置的偏移下标 int left = 0, right = offsetMap.Count - 1; while (left < right) { int mid = (left + right) / 2; if (offsetMap[mid].byteOffset < targetBytePos) left = mid + 1; else right = mid; } // 对比相邻两个偏移的差值,取更近的 if (left == 0) return offsetMap[0].frameIndex; var prevDiff = targetBytePos - offsetMap[left - 1].byteOffset; var currDiff = offsetMap[left].byteOffset - targetBytePos; return prevDiff < currDiff ? offsetMap[left - 1].frameIndex : offsetMap[left].frameIndex; } // 调用示例 public static void RunDemo() { string tsPath = "你的测试文件路径.ts"; long targetPos = 515486; // 目标字节位置 var offsetMap = BuildFrameOffsetMap(tsPath); int nearestFrameIndex = GetNearestFrameIndex(offsetMap, targetPos); Console.WriteLine($"最近帧索引为:{nearestFrameIndex}"); } }
注意事项
- CapProp.PosBytes 属性的准确性依赖底层FFmpeg对MPEG-TS封装的适配,主流FFmpeg版本均支持该格式的字节位置查询。
- 如果TS文件存在多个视频流,需先确认目标流的索引,传入
BuildFrameOffsetMap方法即可。 - 若仅需单次查询,无需完整生成映射表,可在遍历过程中实时对比字节偏移,找到首个大于目标位置的偏移项后直接对比相邻项即可,降低大文件的遍历开销。
内容的提问来源于stack exchange,提问作者mirij
相关产品推荐
相关产品推荐

