3986.net
小网站 大容量 大智慧
当前位置:首页 >> 计算机软件及应用 >>

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


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

1. /// <summary> 2. 3. 4. 5. 6. 7. 1 8. -+ 9. | 10. -+ 11. | 12. -+ 13. | 14. =+ 15. | 16. | 17. -+ 18. 19. /// </remarks> public class RtpPacket /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ /// | .... /// | contributing source (CSRC) identifiers /// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ /// | synchronization source (SSRC) identifier /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ /// | timestamp /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ /// |V=2|P|X| CC |M| PT | sequence number /// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ /// RTP(RFC3550)协定数据报 /// </summary> /// <remarks> /// The RTP header has the following format: /// /// 0 1 2 3

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

20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52.

{ /// <summary> /// version (V): 2 bits /// RTP 版本标识,当前规范定义值为 2. /// This field identifies the version of RTP. The version defined by this specification is two (2). /// (The value 1 is used by the first draft version of RTP and th e value 0 is used by the protocol /// initially implemented in the \vat" audio tool.) /// </summary> public int Version { get { return 2; } } /// <summary> /// padding (P):1 bit /// 如果设定 padding,在报文的末端就会包含一个或者多个 padding 字节,这不属 于 payload。 /// 最后一个字节的 padding 有一个计数器,标识需要忽略多少个 padding 位元组 (包括自己)。 /// 一些加密算法可能需要固定块长度的 padding,或者是为了在更低层数据单元中携 带一些 RTP 报文。 /// If the padding bit is set, the packet contains one or more ad ditional padding octets at the /// end which are not part of the payload. The last octet of the padding contains a count of /// how many padding octets should be ignored, including itself. Padding may be needed by /// some encryption algorithms with fixed block sizes or for carr ying several RTP packets in a /// lower-layer protocol data unit. /// </summary> public int Padding { get { return 0; } } /// <summary> /// extension (X):1 bit /// 如果设定了 extension 位,定长头字段后面会有一个头扩展。 /// If the extension bit is set, the fixed header must be followe d by exactly one header extensio. /// </summary> public int Extension { get { return 0; } } /// <summary> /// CSRC count (CC):4 bits /// CSRC count 标识了定长头字段中包含的 CSRC identifier 的数量。

53. 54. 55. 56. 57. 58. 59. 件。 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. 79. 80. 81.

/// The CSRC count contains the number of CSRC identifiers that f ollow the fixed header. /// </summary> public int CC { get { return 0; } } /// <summary> /// marker (M):1 bit /// marker 是由一个 profile 定义的。用来允许标识在像报文流中界定帧界等的事 /// 一个 profile 可能定义了附加的标识位或者通过修改 payload type 域中的位 数量来指定没有标识位. /// The interpretation of the marker is defined by a profile. It is intended to allow significant /// events such as frame boundaries to be marked in the packet st ream. A profile may define /// additional marker bits or specify that there is no marker bit by changing the number of bits /// in the payload type field. /// </summary> public int Marker { get { return 0; } } /// <summary> /// payload type (PT):7 bits /// 这个字段定一个 RTPpayload 的格式和在应用中定义解释。 /// profile 可能指定一个从 payload type 码字到 payload format 的默认静 态映像。 /// 也可以通过 non-RTP 方法来定义附加的 payload type 码字(见第 3 章)。 /// 在 RFC 3551[1]中定义了一系列的默认音视频映射。 /// 一个 RTP 源有可能在会话中改变 payload type, 但是这个域在复用独立的媒体时 是不同的。(见 5.2 节)。 /// 接收者必须忽略它不识别的 payload type。 /// This field identifies the format of the RTP payload and deter mines its interpretation by the /// application. A profile may specify a default static mapping o f payload type codes to payload /// formats. Additional payload type codes may be defined dynamic ally through non-RTP means /// (see Section 3). A set of default mappings for audio and vide o is specified in the companion /// RFC 3551 [1]. An RTP source may change the payload type durin g a session, but this field /// should not be used for multiplexing separate media streams (s ee Section 5.2).

82. 83. 84. 85. 86. 87. 88. 序列。 89. 90. 91. 92. 93. 94. 95. 96. 97. 98. 99. 100. 101. 102. 值。 103. 104. 105. 106. 107. 108. 109.

/// A receiver must ignore packets with payload types that it doe s not understand. /// </summary> public RtpPayloadType PayloadType { get; private set; } /// <summary> /// sequence number:16 bits /// 每发送一个 RTP 数据报文序号值加一,接收者也可用来检测丢失的包或者重建报文 /// 初始的值是随机的,这样就使得 known-plaintext 攻击更加困难, 即使源并没 有加密(见 9。1), /// 因为要通过的 translator 会做这些事情。 关于选择随机数方面的技术见[17]。 /// The sequence number increments by one for each RTP data packe t sent, and may be used /// by the receiver to detect packet loss and to restore packet s equence. The initial value of the /// sequence number should be random (unpredictable) to make know n-plaintext attacks on /// encryption more dificult, even if the source itself does not encrypt according to the method /// in Section 9.1, because the packets may flow through a transl ator that does. Techniques for /// choosing unpredictable numbers are discussed in [17]. /// </summary> public int SequenceNumber { get; private set; } /// <summary> /// timestamp:32 bits /// timestamp 反映的是 RTP 数据报文中的第一个字段的采样时刻的时间瞬时 /// 采样时间值必须是从恒定的和线性的时间中得到以便于同步和 jitter 计算(见 /// 必须保证同步和测量保温 jitter 到来所需要的时间精度(一帧一个 tick 一般 /// 时钟频率是与 payload 所携带的数据格式有关的, profile 中静态的定义或 在 /// 或通过 non-RTP 方法所定义的 payload format 中动态的定义。 如果 RTP 报 /// 采样时钟而不是从系统时钟读数。 例如, 在固定位元速率的音频中, timestamp 时 /// 如果音讯应用中从输入设备中读入 160 个采样周期的块, the timestamp 就会 /// 而不管块是否传输了或是丢弃了。

第 6.4.1 节)。 情况下是不够的)。 是在定义格式的 payload format 中, 文周期的生成,就采用虚拟的(nominal) 钟会在每个采样周期时加一。 每一块增加 160,

110. 成的, 111. 112. 113. 114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. 129. 130. 131. 132. 133. 134. 率很小, 135. 136.

/// 对于序号来说,timestamp 初始值是随机的。只要它们是同时(逻辑上)同时生 /// 这些连续的的 RTP 报文就会有相同的 timestamp, /// 例如,同属一个视频帧。正像在 MPEG 中内插视频帧一样, /// 连续的但不是按顺序发送的 RTP 报文可能含有相同的 timestamp。 /// The timestamp reflects the sampling instant of the first oc /// sampling instant must be derived from a clock that incremen /// in time to allow synchronization and jitter calculations (s /// of the clock must be suficient for the desired synchronizat /// packet arrival jitter (one tick per video frame is typicall /// is dependent on the format of data carried as payload and i /// or payload format specification that defines the format, or may be specified dynamically for /// payload formats defined through non-RTP means. If RTP packe /// the nominal sampling instant as determined from the samplin /// reading of the system clock. As an example, for fixed-rate /// likely increment by one for each sampling period. If an aud /// 160 sampling periods from the input device, the timestamp w /// each such block, regardless of whether the block is transmi /// </summary> public long Timestamp { get; private set; } /// <summary> /// SSRC:32 bits /// SSRC 域识别同步源。为了防止在一个会话中有相同的同步源有相同的 /// 这个 identifier 必须随机选取。 /// 生成随机 identifier 的算法见目录 A.6 。虽然选择相同的 identifier 概 /// 但是所有的 RTP implementation 必须检测和解决冲突。 /// 第 8 章描述了冲突的概率和解决机制和 RTP 级的检测机制,根据唯一

tet in the RTP data packet. The ts monotonically and linearly ee Section 6.4.1). The resolution ion accuracy and for measuring y not suficient). The clock frequency s specified statically in the profile

ts are generated periodically, g clock is to be used, not a audio the timestamp clock would io application reads blocks covering ould be increased by 160 for tted in a packet or dropped as silent.

SSRC identifier,

的 SSRCidentifier 前向循环。

137. 138. 139. 140. 141. 142. 143. 144. 145. 146. 147. ). 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. 159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170.

/// 如果有源改变了它的源传输地址, /// 就必须为它选择一个新的 SSRCidentifier 来避免被识别为循环过的源(见第 /// The SSRC field identifies the synchronization source. This /// randomly, with the intent that no two synchronization sourc /// will have the same SSRC identifier. An example algorithm fo /// is presented in Appendix A.6. Although the probability of m /// identifier is low, all RTP implementations must be prepared to detect and resolve collisions. /// Section 8 describes the probability of collision along with a mechanism for resolving collisions /// and detecting RTP-level forwarding loops based on the uniqu /// a source changes its source transport address, it must also choose a new SSRC identifier to /// avoid being interpreted as a looped source (see Section 8.2 /// </summary> public int SSRC { get { return 0; } } /// <summary> /// 每一个 RTP 包中都有前 12 个字节定长的头字段 /// The first twelve octets are present in every RTP packet /// </summary> public const int HeaderSize = 12; /// <summary> /// RTP 消息头 /// </summary> private byte[] _header; /// <summary> /// RTP 消息头 /// </summary> public byte[] Header { get { return _header; } } /// <summary> /// RTP 有效载荷长度 /// </summary> private int _payloadSize; /// <summary> /// RTP 有效载荷长度

8.2 节)。 identifier should be chosen es within the same RTP session r generating a random identifier ultiple sources choosing the same

eness of the SSRC identifier. If

171. 172. 173. 174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. 189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203. 204. 205. 206. 207. 208. 209. 210. 211. 212.

/// </summary> public int PayloadSize { get { return _payloadSize; } } /// <summary> /// RTP 有效载荷 /// </summary> private byte[] _payload; /// <summary> /// RTP 有效载荷 /// </summary> public byte[] Payload { get { return _payload; } } /// <summary> /// RTP 消息总长度,包括 Header 和 Payload /// </summary> public int Length { get { return HeaderSize + PayloadSize; } }

/// <summary> /// RTP(RFC3550)协定数据报 /// </summary> /// <param name="playloadType">数据报文有效载荷类型</param> /// <param name="sequenceNumber">数据报文序号值</param> /// <param name="timestamp">数据报文采样时刻</param> /// <param name="data">数据</param> /// <param name="dataSize">数据长度</param> public RtpPacket( RtpPayloadType playloadType, int sequenceNumber, long timestamp, byte[] data, int dataSize) { // fill changing header fields SequenceNumber = sequenceNumber; Timestamp = timestamp; PayloadType = playloadType; // build the header bistream _header = new byte[HeaderSize]; // fill the header array of byte with RTP header fields _header[0] = (byte)((Version << 6) | (Padding << 5) | (Extens

ion << 4) | CC);

213. 214. 215. 216. 217. 218. 219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. 234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. 249. 250. 251. 252. 253. 254. { }

_header[1] = (byte)((Marker << 7) | (int)PayloadType); _header[2] = (byte)(SequenceNumber >> 8); _header[3] = (byte)(SequenceNumber); for (int i = 0; i < 4; i++) { _header[7 - i] = (byte)(Timestamp >> (8 * i)); } for (int i = 0; i < 4; i++) { _header[11 - i] = (byte)(SSRC >> (8 * i)); } // fill the payload bitstream _payload = new byte[dataSize]; _payloadSize = dataSize; // fill payload array of byte from data (given in parameter o Array.Copy(data, 0, _payload, 0, dataSize);

f the constructor)

/// <summary> /// RTP(RFC3550)协定数据报 /// </summary> /// <param name="playloadType">数据报文有效载荷类型</param> /// <param name="sequenceNumber">数据报文序号值</param> /// <param name="timestamp">数据报文采样时刻</param> /// <param name="frame">图片</param> public RtpPacket( RtpPayloadType playloadType, int sequenceNumber, long timestamp, Image frame) // fill changing header fields SequenceNumber = sequenceNumber; Timestamp = timestamp; PayloadType = playloadType; // build the header bistream _header = new byte[HeaderSize]; // fill the header array of byte with RTP header fields

255. 256. 257. 258. 259. 260. 261. 262. 263. 264. 265. 266. 267. 268. 269. 270. 271. 272. 273. 274. 275. 276. 277. 278. 279. 280. 281. 282. 283. 284. 285. 286. 287. 288. 289. 290. 291. 292. 293. 294. 295. 296. 297. }

_header[0] = (byte)((Version << 6) | (Padding << 5) | (Extens _header[1] = (byte)((Marker << 7) | (int)PayloadType); _header[2] = (byte)(SequenceNumber >> 8); _header[3] = (byte)(SequenceNumber); for (int i = 0; i < 4; i++) { _header[7 - i] = (byte)(Timestamp >> (8 * i)); } for (int i = 0; i < 4; i++) { _header[11 - i] = (byte)(SSRC >> (8 * i)); } // fill the payload bitstream using (MemoryStream ms = new MemoryStream()) { frame.Save(ms, ImageFormat.Jpeg); _payload = ms.ToArray(); _payloadSize = _payload.Length; }

ion << 4) | CC);

/// <summary> /// RTP(RFC3550)协定数据报 /// </summary> /// <param name="packet">数据包</param> /// <param name="packetSize">数据包长度</param> public RtpPacket(byte[] packet, int packetSize) { //check if total packet size is lower than the header size if (packetSize >= HeaderSize) { //get the header bitsream _header = new byte[HeaderSize]; for (int i = 0; i < HeaderSize; i++) { _header[i] = packet[i]; } //get the payload bitstream _payloadSize = packetSize - HeaderSize; _payload = new byte[_payloadSize]; for (int i = HeaderSize; i < packetSize; i++)

298. 299. 300. 301. 302. 303. 304. 305. 306. 307. 308. 309. 310. 311. 312. 313. 314. 315. 316. 317. 318. 319. 320. 321. 322. 323. 324. 325. 326. 327. 328. 329. 330. 331. 332. 333. 334. 335. 336. 337. 338. 339. 340. } } } }

{ _payload[i - HeaderSize] = packet[i]; } //interpret the changing fields of the header PayloadType = (RtpPayloadType)(_header[1] & 127); SequenceNumber = UnsignedInt(_header[3]) + 256 * UnsignedIn Timestamp = UnsignedInt(_header[7]) + 256 * UnsignedInt(_header[6]) + 65536 * UnsignedInt(_header[5]) + 16777216 * UnsignedInt(_header[4]);

t(_header[2]);

/// <summary> /// 将消息转换成 byte 数组 /// </summary> /// <returns>消息 byte 数组</returns> public byte[] ToArray() { byte[] packet = new byte[Length]; Array.Copy(_header, 0, packet, 0, HeaderSize); Array.Copy(_payload, 0, packet, HeaderSize, PayloadSize); return packet;

/// <summary> /// 将消息体转换成图片 /// </summary> /// <returns>图片</returns> public Bitmap ToBitmap() { return new Bitmap(new MemoryStream(_payload));

/// <summary> /// 将消息体转换成图片 /// </summary> /// <returns>图片</returns> public Image ToImage() {

341. 342. 343. 344. 345. 346. 347. 348. 349. 350. 351. 352. 353. 354. 355. 356. 357. 358. 359. 360. frame); 361. 362. 363. 364. 365. 366. 367. 368. 369. 370. 371. 372. 373. 374. 375. } } } { }

return Image.FromStream(new MemoryStream(_payload));

/// <summary> /// 将图片转换成消息 /// </summary> /// <param name="playloadType">数据报文有效载荷类型</param> /// <param name="sequenceNumber">数据报文序号值</param> /// <param name="timestamp">数据报文采样时刻</param> /// <param name="frame">图片帧</param> /// <returns> /// RTP 消息 /// </returns> public static RtpPacket FromImage( RtpPayloadType playloadType, int sequenceNumber, long timestamp, Image frame) return new RtpPacket(playloadType, sequenceNumber, timestamp,

/// <summary> /// return the unsigned value of 8-bit integer nb /// </summary> /// <param name="nb"></param> /// <returns></returns> private static int UnsignedInt(int nb) { if (nb >= 0) return (nb); else return (256 + nb);


推荐相关:

RTP-RFC 3550中文版_信息与通信_工程科技_专业资料。RTP-RFC 3550中文版RFC...C#实现RTP数据报传输参照... 暂无评价 11页 1下载券 RFC 3550 中文版 96页...


本文档是对RTP协议的分析,包括其产生背景,原理及工作机制,数据包格式等,关键...数 据流的一个传输协议, 由 IETF 作为 RFC1889 发布,现在最新的为 RFC3550...


传输提供了拥塞控制和流控制,它的具体包 结构和各字段的含义可参考 RFC3550,...NALU 前添加相应的 RTP 包头,然后将包含 RTP 包头和 NALU 的数据 包发送出去...


由 IETF 作为 RFC1889 发布,现在最新的为 RFC3550...进行数据包传输,具体见本文 2.2.1RTP 数据格式...2014年12月大学英语四级经典参考范文 104份文档 2014...


数据流的一 个传输协议, 由 IETF 作为 RFC1889 发布,现在最新的为 RFC3550。...·SR(Sender Report) 发送端报告,所谓发送端是指发出 RTP 数据报的应用 程序...


RTCP 包括五种数据包类型(RFC3550 Page69) : 表 ...数据块通过接收来自单同步 源的 RTP 数据包传输统计...六、参考文献: 【1】 协议分析网,http://www.cn...


RTP 参考文档 RFC3550/RFC3551 Real-time Transport Protocol)是用于 Internet 上针对多媒体数据流的一 种传输层协议。RTP 协议详细说明了在互联网上传递音频和视频...


RFC3550_RTP协议中文版 33页 1财富值 RTSP流媒体协议...并实现媒体流的实时传输,每一个 RTP 数据 报都由...状态码 描述了与 RTSP 在制定时较多地参考了 HTTP...


RTP 是 IETF 提出的适合实时数据传输的协议,支持在...当检测到数据包丢失或错误时,要求发送端重新发送,但...RTP RFC3550中文版 21页 免费 RTP管道现状 6页 免费...


RFC3550 不仅定义了 RTP,而且定义了配套的相关协议 ...要实现流式传输,就是要从降低延迟和恢复数据包时序...小异,下面只讲述 SR 类型,而其它类型请参考 RFC...

网站首页 | 网站地图
3986 3986.net
文档资料库内容来自网络,如有侵犯请联系客服。zhit325@qq.com