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

C#/.Net Core下按指定码率UDP组播推MPEG TS文件及PCR延迟计算问题

固定码率推流实现方案

你之前的问题核心是Thread.Sleep的精度不足,Windows系统默认线程调度时间片为10~15ms,直接调用1ms等待的实际误差远大于你的2641tick(约264微秒)需求,凑出来的计数阈值只适配当前运行环境,换机器就会出现码率偏差。
推荐使用混合等待方案,兼顾精度和CPU占用:

// 初始化高精度计时器
Stopwatch sw = Stopwatch.StartNew();
long lastSendTicks = 0;
// 每个UDP包的发送间隔,单位是100ns tick,你计算的2641可以直接用
const long SendIntervalTicks = 2641;

for(int i = 1316; i < bytes.Length; i+=1316)
{
    int begin = i-1316;
    var sendBytes = bytes[begin..i];
    
    // 等待到指定发送时间
    long currentTicks = sw.ElapsedTicks * 10000000 / Stopwatch.Frequency; // 转换为100ns单位的tick
    while (currentTicks - lastSendTicks < SendIntervalTicks)
    {
        // 差超过1ms就Sleep让出CPU,差不足1ms就忙等保证精度
        if (SendIntervalTicks - (currentTicks - lastSendTicks) > 10000)
        {
            Thread.Sleep(1);
        }
        currentTicks = sw.ElapsedTicks * 10000000 / Stopwatch.Frequency;
    }
    lastSendTicks = currentTicks;
    
    try
    {
        outPutUdpClient.Send(sendBytes, sendBytes.Length);
    }
    catch (Exception ex)
    {
        Debug.WriteLine(ex);
    }
}

如果你的运行环境允许少量CPU占用,也可以直接用Stopwatch忙等,精度更高,适合微秒级的间隔控制。

基于PCR的精准推流算法

PCR是TS流自带的同步时钟,完全还原原始卫星流的播出时序,比固定码率计算更准确,不会因为录制文件的微小码率波动出现时序偏差,算法逻辑如下:

  1. 预解析TS文件,提取所有PCR值以及对应的文件偏移位置,存储为有序列表,PCR的时钟频率为90kHz,1个PCR单位等于1/90000秒
  2. 推流启动时,记录第一个PCR值为基准PCR,同时记录本地启动时间戳
  3. 推流过程中,每读取到一个PCR节点时,计算理论流逝时间:(当前PCR - 基准PCR) / 90000.0 秒
  4. 对比本地实际流逝时间,如果本地时间小于理论流逝时间,等待对应的时间差后再继续发送后续数据
  5. 两个PCR节点之间的数据包按固定间隔均匀发送即可,不需要额外调整,靠PCR节点定期校准整体时序

对应的简化实现代码参考:

// 预解析得到的PCR列表,Item1为PCR值,Item2为对应文件偏移
List<Tuple<long, int>> pcrList = ParsePcrFromTsFile(tsFile);
int pcrIndex = 0;
Stopwatch sw = Stopwatch.StartNew();
long basePcr = pcrList[0].Item1;
long baseTimeMs = 0;

for(int i = 1316; i < bytes.Length; i+=1316)
{
    // 到达下一个PCR节点,校准时间
    if (pcrIndex < pcrList.Count && i >= pcrList[pcrIndex].Item2)
    {
        long currentPcr = pcrList[pcrIndex].Item1;
        double targetElapsedMs = (currentPcr - basePcr) / 90.0; // 转换为毫秒
        double actualElapsedMs = sw.Elapsed.TotalMilliseconds;
        if (actualElapsedMs < targetElapsedMs)
        {
            // 等待差值,用之前的混合等待方案保证精度
            Thread.Sleep((int)(targetElapsedMs - actualElapsedMs));
        }
        pcrIndex++;
    }
    
    // 发送当前UDP包
    var sendBytes = bytes[(i-1316)..i];
    outPutUdpClient.Send(sendBytes, sendBytes.Length);
}

注意解析PCR时选择固定的参考PID即可,不需要处理TS流里的所有PID。

内容的提问来源于stack exchange,提问作者Deim

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 19:06:00