zoukankan      html  css  js  c++  java
  • C# 广播TS流精确计时发送

    广播传输相关的项目,需求是UDP发送TS到IP/ASI网关,网关经过ASI输出到激励器,再由激励器通过射频天线输出,接收端为终端机顶盒。

      因为以前没有怎么接触过广播相关的东西,一开始认为用C#写个UDP的程序将TS发出即可。TS的规范是每188个字节为一个packet,我用固定码率计算出100毫秒要发送的packet的个数,发送一定量的packet后如果还未到100毫秒则进行等待,超过100毫秒则重新发送下一组packet。但测试发现,发送的数据可以到达网关,但激励器却接收不到。在网关上看到有ASI输出的码率但不是太精确。所幸手头上有一个伙伴公司用VC写的TS发送测试工具,该工具发送的码率可以正确到达激励器并被机顶盒接收。于是想到用wireshark抓包工具抓包看一下和我的程序有什么区别。

      经过抓发发现伙伴公司写的TS发送工具发送packet十分均匀,而我的程序发送的时间间隔则有跳跃。后来想到,发送的时间如果不均匀,可能会造成设备缓冲区溢出发生,所以无法正确接收。我的程序是一下子吧固定的一组包全部发出去,然后等待,但伙伴公司的工具则是每个包固定的发送时间,可以肯定我写的程序,发送TS时,packet没有均匀的离开发送端,造成码率错误。知道了问题,那么如何修改呢?抓包工具看到,每个包发送的时间及间隔的最小单位是微秒,然而sleep及普通的定时器是无法做到微秒级的精确计时的。

       于是求助度娘了解到,要像达到精确计时就需要获取CPU的相关计时信息进行精确计算。于是参考VC上精确计时和别人的文章找到了精确到微妙级计时的方法。【参考[.NET] 如何用C#做高精度计时器>http://blog.csdn.net/cloudhsu/article/details/5773043】。 

    namespace AccurateTimer{
        /// <summary>
        /// 消息结构体
        /// </summary>
        [StructLayout(LayoutKind.Sequential)]
        public struct MSG
        {
            public IntPtr handle;
            public uint msg;
            public IntPtr wParam;
            public IntPtr lParam;
            public uint time;
            public System.Drawing.Point p;
        } 
    
        /// <summary>
        /// 定时函数
        /// </summary>
        public class AccurateTimer
        {
    
            //调用系统API获取CPU时钟相关信息
            public static bool IsTimeBeginPeriod = false;
    
            const int PM_REMOVE = 0x0001;
    
            [DllImport("user32.dll", SetLastError = true)]
            static extern bool PeekMessage(out MSG lpMsg, IntPtr hWnd, uint wMsgFilterMin,
               uint wMsgFilterMax, uint wRemoveMsg);
    
            [DllImport("user32.dll", SetLastError = true)]
            static extern bool TranslateMessage(ref MSG lpMsg);
    
            [DllImport("user32.dll", SetLastError = true)]
            static extern bool DispatchMessage(ref MSG lpMsg);
    
    
            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern bool QueryPerformanceCounter(ref Int64 count);
    
            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern bool QueryPerformanceFrequency(ref Int64 frequency);
    
    
            public static int GetTimeTick()
            {
                return Environment.TickCount;
            }
    
     
    
            //参数_wait_count:要等待的时间,根据时间内部换算可换成秒,毫秒或微秒,_wait_count2:实际回传的等待时间,精度不大于1微妙
    
     
    
            public static void AccurateSleep(int _wait_count, ref double _wait_count2)
            {
                Int64 t_i8Frequency = 0;
                Int64 t_i8StartTime = 0;
                Int64 t_i8EndTime = 0;
                //double t_r8PassedMSec = 0;
                MSG msg;
                AccurateTimer.QueryPerformanceCounter(ref t_i8StartTime);
                AccurateTimer.QueryPerformanceFrequency(ref t_i8Frequency);
                do
                {
                    if (AccurateTimer.PeekMessage(out msg, IntPtr.Zero, 0, 0, PM_REMOVE))
                    {
                        AccurateTimer.TranslateMessage(ref msg);
                        AccurateTimer.DispatchMessage(ref msg);
                    }
                    AccurateTimer.QueryPerformanceCounter(ref t_i8EndTime);
                    //*1000000 微妙 1000毫秒  1秒=1000毫秒=1000000微妙
                    //等待时计算,乘于1000是毫秒,乘于1000000则是微妙
                    _wait_count2= ((double)(t_i8EndTime - t_i8StartTime) / (double)t_i8Frequency) * 1000000;
                } while (t_r8PassedMSec <=_wait_count);
            } 
        }
    }

      于是修改程序,根据码率计算出平均每个packet发送使用的微妙数:          

    //100毫秒内需要发送的packet数量,4000000为发送的码率,TS_OVER_IP_SIZE为固定的常量188
    
              double _packet_count = 4000000 / 10) / (TS_OVER_IP_SIZE * 8);
    
              //发送间隔计算,每个包发送的毫秒数乘1000即是微秒
    
              int _duration = (int)((100 / _packet_count)*1000);

          编译程序,测试通过。不过要说C#本身的托管机制,代码执行效率自然比不上VC,所以实际到达设备的码率还是有一定误差的,但对我来说是不影响业务的使用。

  • 相关阅读:
    CMake 从文件路径中提取文件名
    std::multimap 按照key遍历---
    Windows / Linux 一件编译zlib库
    C++ 11 可变模板参数的两种展开方式
    cmake 生成VS项目文件夹
    C++ 利用文件流复制文件
    利用 getsockname 和 getpeername 来获取某一个链接的本地地址和远端地址
    Windows 用VS编译libevent源码
    揭示同步块索引(上):从lock开始
    C手写一个多线程,供java调用
  • 原文地址:https://www.cnblogs.com/HappyEDay/p/5780328.html
Copyright © 2011-2022 走看看