如何在 .NET 中使用托管 RTP API 类创建组播系统
创建一个 RTP 组播演示器(带运动检测)。
引言
在本文中,我将描述 RTP(实时传输协议)的架构,并讨论 Microsoft Conference XP 项目中用于组播 JPEG 图像的 RTP 托管类。有关 RTP 编程的更多示例,请参见 www.SocketCoder.com。
IP 网络中数据音频/视频传输的关键标准是实时传输协议 (RTP),以及其相关的配置文件和有效负载格式。RTP 旨在提供用于在 IP 网络上传输实时媒体(如音频和视频)的服务。这些服务包括时序恢复、丢失检测和纠正、有效负载和源识别、接收质量反馈、媒体同步以及成员管理。RTP 最初设计用于使用轻量级会话模型的组播会议。从那时起,它已被证明适用于各种其他应用程序:H.323 视频会议、网络广播和电视分发;以及有线和蜂窝电话。该协议已被证明可以从点对点使用扩展到拥有数千用户的组播会话。
背景
RTP 如何工作?
发送方负责捕获和转换用于传输的视听数据,以及生成 RTP 数据包。它还可以通过响应接收方反馈来调整传输的媒体流,从而参与错误纠正和拥塞控制。帧将被加载到 RTP 数据包中,准备发送。如果帧很大,它们可能会被分割成几个 RTP 数据包;如果帧很小,几个帧可能会捆绑到一个 RTP 数据包中。根据使用的错误纠正方案,可以使用信道编码器生成错误纠正数据包或在传输前重新排序数据包。RTP 数据包发送后,对应于这些数据包的缓冲媒体数据最终会被释放。发送方不得丢弃可能用于错误纠正或编码过程的数据。此要求可能意味着发送方必须在发送相应数据包后缓冲数据一段时间,具体取决于使用的编解码器和错误纠正方案。发送方负责为它正在生成的媒体流生成周期性状态报告,包括那些唇同步所需的报告。它还接收来自其他参与者的接收质量反馈,并可以使用该信息来调整其传输。接收方负责从网络收集 RTP 数据包、纠正任何丢失、恢复时序、解压缩媒体并将结果呈现给用户。它还发送接收质量反馈,允许发送方调整传输以适应接收方,并维护会话参与者数据库。下图显示了接收过程可能的框图;实现有时会根据其需求以不同的顺序执行操作。
在我的下一篇文章中,我将提供有关 RTP 架构以传输音频和视频的更多信息。有关 RTP 的更多信息,请参阅以下链接
使用代码
微软在其 Conference XP 项目中实现了 RTP。下图展示了 Conference XP 3.0 中 RTP 的架构设计
这些步骤将为您简要介绍如何在组播项目中使用 RTP
- 使用
RtpSession
和RtpParticipant
:会话由一群使用 RTP 通信的参与者组成。一个参与者可以活跃在多个 RTP 会话中——例如,一个会话用于交换音频数据,另一个会话用于交换视频数据。对于每个参与者,会话由网络地址和一对端口(用于发送数据和接收数据)标识。发送和接收端口可以相同。每个端口对都包含两个相邻的端口:一个偶数端口用于 RTP 数据包,下一个更高(奇数)端口用于 RTCP 控制包。默认端口对是 UDP/IP 的 5004 和 5005,但许多应用程序在会话设置期间动态分配端口并忽略默认值。RTP 会话旨在传输单一类型的媒体;在多媒体通信中,每种媒体类型都应在单独的 RTP 会话中传输。我们将使用RtpSession
和RtpParticipant
类来- 管理所有 RTP 对象和数据。
- 保存有关用户的信息。
- 发送或接收 RTP 数据。
我们首先需要连接一些 RTP 事件,以传达 RTP API 进程中发生的情况。这些事件是
// Manage the join to the session Ex.Add/Remove a User To/From the RTP Session RtpEvents.RtpParticipantAdded += new RtpEvents.RtpParticipantAddedEventHandler(RtpParticipantAdded); RtpEvents.RtpParticipantRemoved += new RtpEvents.RtpParticipantRemovedEventHandler(RtpParticipantRemoved);
使用上述事件声明的示例
private void RtpParticipantAdded(object sender, RtpEvents.RtpParticipantEventArgs ea) { MessageBox.Show (ea.RtpParticipant.Name + " has joined to the session"); } private void RtpParticipantRemoved(object sender, RtpEvents.RtpParticipantEventArgs ea) { MessageBox.Show(ea.RtpParticipant.Name + " has left the session"); } // Manage (Add/Remove)Sessions Ex.Activeate multiple RTP sessions RtpEvents.RtpStreamAdded += new RtpEvents.RtpStreamAddedEventHandler(RtpStreamAdded); RtpEvents.RtpStreamRemoved += new RtpEvents.RtpStreamRemovedEventHandler(RtpStreamRemoved); private void RtpStreamAdded(object sender, RtpEvents.RtpStreamEventArgs ea) { ea.RtpStream.FrameReceived += new RtpStream.FrameReceivedEventHandler(FrameReceived); } private void RtpStreamRemoved(object sender, RtpEvents.RtpStreamEventArgs ea) { ea.RtpStream.FrameReceived -= new RtpStream.FrameReceivedEventHandler(FrameReceived); }
然后要加入 RTP 会话,我们必须指定 RTP 数据包有效负载的类型。每个会话只能包含一种有效负载类型
RtpSession rtpSession; private void JoinRtpSession(string SessionName,string name) { rtpSession = new RtpSession(ep, new RtpParticipant(SessionName, name), true, true); rtpSender = rtpSession.CreateRtpSenderFec(name, PayloadType.JPEG, null, 0, 200); } private void LeaveRtpSession() { // Clean up all outstanding objects owned by the RtpSession rtpSession.Dispose(); }
最后,加入会话后,我们可以获取 RTP 流缓冲区,如下所示
private void FrameReceived(object sender, RtpStream.FrameReceivedEventArgs ea) { System.IO.MemoryStream ms = new MemoryStream(ea.Frame.Buffer); pictureBox_Receive.Image = Image.FromStream(ms); }
- 使用
RtpSender
和RtpListener
:发送方负责发送捕获的数据并生成 RTP 数据包——数据可以来自实时捕获或文件——并对其进行压缩以进行传输。例如,将位图图像转换为 JPEG 压缩图像,就像我在示例中使用的那样。发送方首先将媒体数据(例如视频帧)读入缓冲区,从中生成编码帧。在托管 RTP 库中,RtpSender
用于- 通过网络发送数据。
- 发送数据时可选择是否进行前向纠错。
并且,在
RTPListener
中- 一个线程从网络接收数据。
- 一个线程将数据包分发到适当的流进行处理。
如下所示使用
RTPSender
RtpSender rtpSender; MemoryStream ms = new MemoryStream(); // Compressed the captured image as JPEG image format pictureBox_sender.Image.Save(ms, ImageFormat.Jpeg); // Send The The Comressed Image as Bytes stream rtpSender.Send(ms.GetBuffer());
RTCP 管理:RTCP 数据包在 RTP 规范中定义:接收方报告 (RR)、发送方报告 (SR)、源描述 (SDES)、成员管理 (BYE) 和应用程序定义 (APP)。RTCP 发送方用于传出 RTCP 数据,其中
- 本地参与者和流正在加入或离开。
- 用于网络状态的发送方和接收方报告。
RtcpListener
用于- 处理传入的 RTCP(RTP 控制协议)数据。
- 参与者和流加入或离开。
- 用于网络状态的发送方和接收方报告。
- 性能计数器
用于跟踪有趣的实时网络统计信息,例如字节、数据包、每秒帧数、丢失字节和恢复字节。所有这些都包含在 RTP API 类属性中。
提高网络使用性能
为了提高我的示例中的性能,我使用了一种机制,仅发送与之前捕获的图像不同的新屏幕捕获图像。这将减少网络资源的使用,因此我在发送图像之前比较图像的像素,为了加快比较速度,我将图像大小调整为 100x100,然后逐像素比较,如下所示
public float difference(Image OrginalImage, Image SecoundImage) { float percent = 0; try { float counter = 0; Bitmap bt1 = new Bitmap(OrginalImage); Bitmap bt2 = new Bitmap(SecoundImage); int size_H = bt1.Size.Height; int size_W = bt1.Size.Width; float total = size_H * size_W; Color pixel_image1; Color pixel_image2; for (int x = 0; x != size_W; x++) { for (int y = 0; y != size_H; y++) { pixel_image1 = bt1.GetPixel(x, y); pixel_image2 = bt2.GetPixel(x, y); if (pixel_image1 != pixel_image2) {counter++; } } } percent = (counter / total) * 100; } catch (Exception) {percent=100;} return percent; }
上述方法将计算新捕获图像中的差异像素,以便我们可以根据返回的差异百分比决定是否发送它。
参考文献
- 微软会议 XP 项目
- RTP:互联网的音频和视频,Addison Wesley 2003。
- 网络编程完整参考,FADI Abdelqader(撰写中)。参见:www.SocketCoder.Com。
- RFC3550.