zoukankan      html  css  js  c++  java
  • C#实现RTP数据包传输参照RFC3550

    C#实现RTP数据包传输参照RFC3550

    闲暇时折腾IP网络视频监控系统,需要支持视频帧数据包在网络内的传输。
    未采用H.264或MPEG4等编码压缩方式,直接使用Bitmap图片。
    由于对帧的准确到达要求不好,所以采用UDP传输。如果发生网络丢包现象则直接将帧丢弃。
    为了记录数据包的传输顺序和帧的时间戳,所以研究了下RFC3550协议,采用RTP包封装视频帧。
    并未全面深究,所以未使用SSRC和CSRC,因为不确切了解其用意。不过目前的实现情况已经足够了。

    复制代码
      1   /// <summary>
      2   /// RTP(RFC3550)协议数据包
      3   /// </summary>
      4   /// <remarks>
      5   /// The RTP header has the following format:
      6   ///  0                   1                   2                   3
      7   ///  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
      8   /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
      9   /// |V=2|P|X| CC    |M| PT          | sequence number               |
     10   /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     11   /// | timestamp                                                     |
     12   /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     13   /// | synchronization source (SSRC) identifier                      |
     14   /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
     15   /// | contributing source (CSRC) identifiers                        |
     16   /// | ....                                                          |
     17   /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     18   /// </remarks>
     19   public class RtpPacket
     20   {
     21     /// <summary>
     22     /// version (V): 2 bits
     23     /// RTP版本标识,当前规范定义值为2.
     24     /// This field identifies the version of RTP. The version defined by this specification is two (2).
     25     /// (The value 1 is used by the first draft version of RTP and the value 0 is used by the protocol
     26     /// initially implemented in the \vat" audio tool.)
     27     /// </summary>
     28     public int Version { get { return 2; } }
     29 
     30     /// <summary>
     31     /// padding (P):1 bit
     32     /// 如果设定padding,在报文的末端就会包含一个或者多个padding 字节,这不属于payload。
     33     /// 最后一个字节的padding 有一个计数器,标识需要忽略多少个padding 字节(包括自己)。
     34     /// 一些加密算法可能需要固定块长度的padding,或者是为了在更低层数据单元中携带一些RTP 报文。
     35     /// If the padding bit is set, the packet contains one or more additional padding octets at the
     36     /// end which are not part of the payload. The last octet of the padding contains a count of
     37     /// how many padding octets should be ignored, including itself. Padding may be needed by
     38     /// some encryption algorithms with fixed block sizes or for carrying several RTP packets in a
     39     /// lower-layer protocol data unit.
     40     /// </summary>
     41     public int Padding { get { return 0; } }
     42 
     43     /// <summary>
     44     /// extension (X):1 bit 
     45     /// 如果设定了extension 位,定长头字段后面会有一个头扩展。
     46     /// If the extension bit is set, the fixed header must be followed by exactly one header extensio.
     47     /// </summary>
     48     public int Extension { get { return 0; } }
     49 
     50     /// <summary>
     51     /// CSRC count (CC):4 bits 
     52     /// CSRC count 标识了定长头字段中包含的CSRC identifier 的数量。
     53     /// The CSRC count contains the number of CSRC identifiers that follow the fixed header.
     54     /// </summary>
     55     public int CC { get { return 0; } }
     56 
     57     /// <summary>
     58     /// marker (M):1 bit 
     59     /// marker 是由一个profile 定义的。用来允许标识在像报文流中界定帧界等的事件。
     60     /// 一个profile 可能定义了附加的标识位或者通过修改payload type 域中的位数量来指定没有标识位.
     61     /// The interpretation of the marker is defined by a profile. It is intended to allow significant
     62     /// events such as frame boundaries to be marked in the packet stream. A profile may define
     63     /// additional marker bits or specify that there is no marker bit by changing the number of bits
     64     /// in the payload type field.
     65     /// </summary>
     66     public int Marker { get { return 0; } }
     67 
     68     /// <summary>
     69     /// payload type (PT):7 bits
     70     /// 这个字段定一个RTPpayload 的格式和在应用中定义解释。
     71     /// profile 可能指定一个从payload type 码字到payload format 的默认静态映射。
     72     /// 也可以通过non-RTP 方法来定义附加的payload type 码字(见第3 章)。
     73     /// 在 RFC 3551[1]中定义了一系列的默认音视频映射。
     74     /// 一个RTP 源有可能在会话中改变payload type,但是这个域在复用独立的媒体时是不同的。(见5.2 节)。
     75     /// 接收者必须忽略它不识别的payload type。
     76     /// This field identifies the format of the RTP payload and determines its interpretation by the
     77     /// application. A profile may specify a default static mapping of payload type codes to payload
     78     /// formats. Additional payload type codes may be defined dynamically through non-RTP means
     79     /// (see Section 3). A set of default mappings for audio and video is specified in the companion
     80     /// RFC 3551 [1]. An RTP source may change the payload type during a session, but this field
     81     /// should not be used for multiplexing separate media streams (see Section 5.2).
     82     /// A receiver must ignore packets with payload types that it does not understand.
     83     /// </summary>
     84     public RtpPayloadType PayloadType { get; private set; }
     85 
     86     /// <summary>
     87     /// sequence number:16 bits
     88     /// 每发送一个RTP 数据报文序列号值加一,接收者也可用来检测丢失的包或者重建报文序列。
     89     /// 初始的值是随机的,这样就使得known-plaintext 攻击更加困难, 即使源并没有加密(见9。1),
     90     /// 因为要通过的translator 会做这些事情。关于选择随机数方面的技术见[17]。
     91     /// The sequence number increments by one for each RTP data packet sent, and may be used
     92     /// by the receiver to detect packet loss and to restore packet sequence. The initial value of the
     93     /// sequence number should be random (unpredictable) to make known-plaintext attacks on
     94     /// encryption more dificult, even if the source itself does not encrypt according to the method
     95     /// in Section 9.1, because the packets may flow through a translator that does. Techniques for
     96     /// choosing unpredictable numbers are discussed in [17].
     97     /// </summary>
     98     public int SequenceNumber { get; private set; }
     99 
    100     /// <summary>
    101     /// timestamp:32 bits
    102     /// timestamp 反映的是RTP 数据报文中的第一个字段的采样时刻的时间瞬时值。
    103     /// 采样时间值必须是从恒定的和线性的时间中得到以便于同步和jitter 计算(见第6.4.1 节)。
    104     /// 必须保证同步和测量保温jitter 到来所需要的时间精度(一帧一个tick 一般情况下是不够的)。
    105     /// 时钟频率是与payload 所携带的数据格式有关的,在profile 中静态的定义或是在定义格式的payload format 中,
    106     /// 或通过non-RTP 方法所定义的payload format 中动态的定义。如果RTP 报文周期的生成,就采用虚拟的(nominal) 
    107     /// 采样时钟而不是从系统时钟读数。例如,在固定比特率的音频中,timestamp 时钟会在每个采样周期时加一。
    108     /// 如果音频应用中从输入设备中读入160 个采样周期的块,the timestamp 就会每一块增加160,
    109     /// 而不管块是否传输了或是丢弃了。
    110     /// 对于序列号来说,timestamp 初始值是随机的。只要它们是同时(逻辑上)同时生成的,
    111     /// 这些连续的的 RTP 报文就会有相同的timestamp,
    112     /// 例如,同属一个视频帧。正像在MPEG 中内插视频帧一样,
    113     /// 连续的但不是按顺序发送的RTP 报文可能含有相同的timestamp。
    114     /// The timestamp reflects the sampling instant of the first octet in the RTP data packet. The
    115     /// sampling instant must be derived from a clock that increments monotonically and linearly
    116     /// in time to allow synchronization and jitter calculations (see Section 6.4.1). The resolution
    117     /// of the clock must be suficient for the desired synchronization accuracy and for measuring
    118     /// packet arrival jitter (one tick per video frame is typically not suficient). The clock frequency
    119     /// is dependent on the format of data carried as payload and is specified statically in the profile
    120     /// or payload format specification that defines the format, or may be specified dynamically for
    121     /// payload formats defined through non-RTP means. If RTP packets are generated periodically,
    122     /// the nominal sampling instant as determined from the sampling clock is to be used, not a
    123     /// reading of the system clock. As an example, for fixed-rate audio the timestamp clock would
    124     /// likely increment by one for each sampling period. If an audio application reads blocks covering
    125     /// 160 sampling periods from the input device, the timestamp would be increased by 160 for
    126     /// each such block, regardless of whether the block is transmitted in a packet or dropped as silent.
    127     /// </summary>
    128     public long Timestamp { get; private set; }
    129 
    130     /// <summary>
    131     /// SSRC:32 bits
    132     /// SSRC 域识别同步源。为了防止在一个会话中有相同的同步源有相同的SSRC identifier, 
    133     /// 这个identifier 必须随机选取。
    134     /// 生成随机 identifier 的算法见目录A.6 。虽然选择相同的identifier 概率很小,
    135     /// 但是所有的RTP implementation 必须检测和解决冲突。
    136     /// 第8 章描述了冲突的概率和解决机制和RTP 级的检测机制,根据唯一的 SSRCidentifier 前向循环。
    137     /// 如果有源改变了它的源传输地址,
    138     /// 就必须为它选择一个新的SSRCidentifier 来避免被识别为循环过的源(见第8.2 节)。
    139     /// The SSRC field identifies the synchronization source. This identifier should be chosen
    140     /// randomly, with the intent that no two synchronization sources within the same RTP session
    141     /// will have the same SSRC identifier. An example algorithm for generating a random identifier
    142     /// is presented in Appendix A.6. Although the probability of multiple sources choosing the same
    143     /// identifier is low, all RTP implementations must be prepared to detect and resolve collisions.
    144     /// Section 8 describes the probability of collision along with a mechanism for resolving collisions
    145     /// and detecting RTP-level forwarding loops based on the uniqueness of the SSRC identifier. If
    146     /// a source changes its source transport address, it must also choose a new SSRC identifier to
    147     /// avoid being interpreted as a looped source (see Section 8.2).
    148     /// </summary>
    149     public int SSRC { get { return 0; } }
    150 
    151     /// <summary>
    152     /// 每一个RTP包中都有前12个字节定长的头字段
    153     /// The first twelve octets are present in every RTP packet
    154     /// </summary>
    155     public const int HeaderSize = 12;
    156     /// <summary>
    157     /// RTP消息头
    158     /// </summary>
    159     private byte[] _header;
    160     /// <summary>
    161     /// RTP消息头
    162     /// </summary>
    163     public byte[] Header { get { return _header; } }
    164 
    165     /// <summary>
    166     /// RTP有效载荷长度
    167     /// </summary>
    168     private int _payloadSize;
    169     /// <summary>
    170     /// RTP有效载荷长度
    171     /// </summary>
    172     public int PayloadSize { get { return _payloadSize; } }
    173 
    174     /// <summary>
    175     /// RTP有效载荷
    176     /// </summary>
    177     private byte[] _payload;
    178     /// <summary>
    179     /// RTP有效载荷
    180     /// </summary>
    181     public byte[] Payload { get { return _payload; } }
    182 
    183     /// <summary>
    184     /// RTP消息总长度,包括Header和Payload
    185     /// </summary>
    186     public int Length { get { return HeaderSize + PayloadSize; } }
    187 
    188     /// <summary>
    189     /// RTP(RFC3550)协议数据包
    190     /// </summary>
    191     /// <param name="playloadType">数据报文有效载荷类型</param>
    192     /// <param name="sequenceNumber">数据报文序列号值</param>
    193     /// <param name="timestamp">数据报文采样时刻</param>
    194     /// <param name="data">数据</param>
    195     /// <param name="dataSize">数据长度</param>
    196     public RtpPacket(
    197       RtpPayloadType playloadType, 
    198       int sequenceNumber, 
    199       long timestamp, 
    200       byte[] data, 
    201       int dataSize)
    202     {
    203       // fill changing header fields
    204       SequenceNumber = sequenceNumber;
    205       Timestamp = timestamp;
    206       PayloadType = playloadType;
    207 
    208       // build the header bistream
    209       _header = new byte[HeaderSize];
    210 
    211       // fill the header array of byte with RTP header fields
    212       _header[0] = (byte)((Version << 6) | (Padding << 5) | (Extension << 4) | CC);
    213       _header[1] = (byte)((Marker << 7) | (int)PayloadType);
    214       _header[2] = (byte)(SequenceNumber >> 8);
    215       _header[3] = (byte)(SequenceNumber);
    216       for (int i = 0; i < 4; i++)
    217       {
    218         _header[7 - i] = (byte)(Timestamp >> (8 * i));
    219       }
    220       for (int i = 0; i < 4; i++)
    221       {
    222         _header[11 - i] = (byte)(SSRC >> (8 * i));
    223       }
    224 
    225       // fill the payload bitstream
    226       _payload = new byte[dataSize];
    227       _payloadSize = dataSize;
    228 
    229       // fill payload array of byte from data (given in parameter of the constructor)
    230       Array.Copy(data, 0, _payload, 0, dataSize);
    231     }
    232 
    233     /// <summary>
    234     /// RTP(RFC3550)协议数据包
    235     /// </summary>
    236     /// <param name="playloadType">数据报文有效载荷类型</param>
    237     /// <param name="sequenceNumber">数据报文序列号值</param>
    238     /// <param name="timestamp">数据报文采样时刻</param>
    239     /// <param name="frame">图片</param>
    240     public RtpPacket(
    241       RtpPayloadType playloadType, 
    242       int sequenceNumber, 
    243       long timestamp, 
    244       Image frame)
    245     {
    246       // fill changing header fields
    247       SequenceNumber = sequenceNumber;
    248       Timestamp = timestamp;
    249       PayloadType = playloadType;
    250 
    251       // build the header bistream
    252       _header = new byte[HeaderSize];
    253 
    254       // fill the header array of byte with RTP header fields
    255       _header[0] = (byte)((Version << 6) | (Padding << 5) | (Extension << 4) | CC);
    256       _header[1] = (byte)((Marker << 7) | (int)PayloadType);
    257       _header[2] = (byte)(SequenceNumber >> 8);
    258       _header[3] = (byte)(SequenceNumber);
    259       for (int i = 0; i < 4; i++)
    260       {
    261         _header[7 - i] = (byte)(Timestamp >> (8 * i));
    262       }
    263       for (int i = 0; i < 4; i++)
    264       {
    265         _header[11 - i] = (byte)(SSRC >> (8 * i));
    266       }
    267 
    268       // fill the payload bitstream
    269       using (MemoryStream ms = new MemoryStream())
    270       {
    271         frame.Save(ms, ImageFormat.Jpeg);
    272         _payload = ms.ToArray();
    273         _payloadSize = _payload.Length;
    274       }
    275     }
    276 
    277     /// <summary>
    278     /// RTP(RFC3550)协议数据包
    279     /// </summary>
    280     /// <param name="packet">数据包</param>
    281     /// <param name="packetSize">数据包长度</param>
    282     public RtpPacket(byte[] packet, int packetSize)
    283     {
    284       //check if total packet size is lower than the header size
    285       if (packetSize >= HeaderSize)
    286       {
    287         //get the header bitsream
    288         _header = new byte[HeaderSize];
    289         for (int i = 0; i < HeaderSize; i++)
    290         {
    291           _header[i] = packet[i];
    292         }
    293 
    294         //get the payload bitstream
    295         _payloadSize = packetSize - HeaderSize;
    296         _payload = new byte[_payloadSize];
    297         for (int i = HeaderSize; i < packetSize; i++)
    298         {
    299           _payload[i - HeaderSize] = packet[i];
    300         }
    301 
    302         //interpret the changing fields of the header
    303         PayloadType = (RtpPayloadType)(_header[1] & 127);
    304         SequenceNumber = UnsignedInt(_header[3]) + 256 * UnsignedInt(_header[2]);
    305         Timestamp = UnsignedInt(_header[7]) 
    306           + 256 * UnsignedInt(_header[6]) 
    307           + 65536 * UnsignedInt(_header[5]) 
    308           + 16777216 * UnsignedInt(_header[4]);
    309       }
    310     }
    311 
    312     /// <summary>
    313     /// 将消息转换成byte数组
    314     /// </summary>
    315     /// <returns>消息byte数组</returns>
    316     public byte[] ToArray()
    317     {
    318       byte[] packet = new byte[Length];
    319 
    320       Array.Copy(_header, 0, packet, 0, HeaderSize);
    321       Array.Copy(_payload, 0, packet, HeaderSize, PayloadSize);
    322 
    323       return packet;
    324     }
    325 
    326     /// <summary>
    327     /// 将消息体转换成图片
    328     /// </summary>
    329     /// <returns>图片</returns>
    330     public Bitmap ToBitmap()
    331     {
    332       return new Bitmap(new MemoryStream(_payload));
    333     }
    334 
    335     /// <summary>
    336     /// 将消息体转换成图片
    337     /// </summary>
    338     /// <returns>图片</returns>
    339     public Image ToImage()
    340     {
    341       return Image.FromStream(new MemoryStream(_payload));
    342     }
    343 
    344     /// <summary>
    345     /// 将图片转换成消息
    346     /// </summary>
    347     /// <param name="playloadType">数据报文有效载荷类型</param>
    348     /// <param name="sequenceNumber">数据报文序列号值</param>
    349     /// <param name="timestamp">数据报文采样时刻</param>
    350     /// <param name="frame">图片帧</param>
    351     /// <returns>
    352     /// RTP消息
    353     /// </returns>
    354     public static RtpPacket FromImage(
    355       RtpPayloadType playloadType, 
    356       int sequenceNumber, 
    357       long timestamp, 
    358       Image frame)
    359     {
    360       return new RtpPacket(playloadType, sequenceNumber, timestamp, frame);
    361     }
    362 
    363     /// <summary>
    364     /// return the unsigned value of 8-bit integer nb
    365     /// </summary>
    366     /// <param name="nb"></param>
    367     /// <returns></returns>
    368     private static int UnsignedInt(int nb)
    369     {
    370       if (nb >= 0)
    371         return (nb);
    372       else
    373         return (256 + nb);
    374     }
    375   }
    复制代码
     
     
     
    标签: C#.NETRTP
  • 相关阅读:
    【NOIP 2003】 加分二叉树
    【POJ 1655】 Balancing Act
    【HDU 3613】Best Reward
    【POJ 3461】 Oulipo
    【POJ 2752】 Seek the Name, Seek the Fame
    【POJ 1961】 Period
    【POJ 2406】 Power Strings
    BZOJ3028 食物(生成函数)
    BZOJ5372 PKUSC2018神仙的游戏(NTT)
    BZOJ4836 二元运算(分治FFT)
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/3020402.html
Copyright © 2011-2022 走看看