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流自带的同步时钟,完全还原原始卫星流的播出时序,比固定码率计算更准确,不会因为录制文件的微小码率波动出现时序偏差,算法逻辑如下:
- 预解析TS文件,提取所有PCR值以及对应的文件偏移位置,存储为有序列表,PCR的时钟频率为90kHz,1个PCR单位等于1/90000秒
- 推流启动时,记录第一个PCR值为基准PCR,同时记录本地启动时间戳
- 推流过程中,每读取到一个PCR节点时,计算理论流逝时间:
(当前PCR - 基准PCR) / 90000.0秒 - 对比本地实际流逝时间,如果本地时间小于理论流逝时间,等待对应的时间差后再继续发送后续数据
- 两个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
相关产品推荐
相关产品推荐

