好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

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# ,  .NET ,  RTP

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于C#实现RTP数据包传输参照RFC3550的详细内容...

  阅读:49次

上一篇: 在线商城表结构

下一篇:相似字符串