You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 01:15:03