转自:
jrtplib学习之example1例程分析 - CSDN博客
https://blog.csdn.net/czh52911/article/details/7673209
本文主要讲解jrtplib源码中的example1例程,以及在jrtplib中常见的一些类的含义
example1:
- /
- Here’s a small IPv4 example: it asks for a portbase and a destination and
- starts sending packets to that destination.
- /
- <span style=”color:#000000;”>#include “rtpsession.h”
- //</span><span style=”background-color:#f0f0f0;”><span style=”color:#000000;”><span class=”t_tag”>定义</span>了RTPSession
- #include “rtpudpv4transmitter.h”
- //定义了RTPSession的第二个</span><span style=”color:#000000;”><span class=”t_tag”>参数
- </span>#include ”rtpipv4address.h”
- //定义了rtpipv4address
- #include “rtpsessionparams.h”
- //定义了RTPSession的第一个参数
- #include “rtperrors.h”
- </span></span><span style=”background-color:#f0f0f0;”><span style=“color:#000000;”>//定义了RTP中的错误</span><span class=”t_tag”><span style=”color:#000000;”>信息
- </span></span></span><span style=”color:#000000;”>#ifndef WIN32
- #include <netinet/in.h>
- #include <arpa/inet.h>
- #else
- #include <winsock2.h>
- #endif // WIN32
- #include <stdlib.h>
- #include <stdio.h>
- #include <iostream>
- #include <string>
- using namespace jrtplib;
- </span>//
- // This function checks if there was a RTP error. If so, it displays an error
- // message and exists.
- //
- //获得出错信息
- void checkerror(int rtperr)
- {
- if (rtperr < 0)
- {
- std::cout << ”ERROR: ” << RTPGetErrorString(rtperr) << std::endl;
- exit(-1);
- }
- }
- //
- // The main routine
- //
- int main(void)
- {
- #ifdef WIN32 //windows下编程加载套接字库
- WSADATA dat;
- WSAStartup(MAKEWORD(2,2),&dat);
- #endif // WIN32
- RTPSession sess;
- uint16_t portbase,destport;
- uint32_t destip;
- std::string ipstr;
- int status,i,num;
- // First, we’ll ask for the necessary information
- std::cout << ”Enter local portbase:” << std::endl;
- std::cin >> portbase;
- std::cout << std::endl;
- std::cout << ”Enter the destination IP address” << std::endl;
- std::cin >> ipstr;
- destip = inet_addr(ipstr.c_str());
- if (destip == INADDR_NONE)
- {
- std::cerr << ”Bad IP address specified” << std::endl;
- return -1;
- }
- // The inet_addr function returns a value in network byte order, but
- // we need the IP address in host byte order, so we use a call to
- // ntohl
- destip = ntohl(destip);
- std::cout << ”Enter the destination port” << std::endl;
- std::cin >> destport;
- std::cout << std::endl;
- std::cout << ”Number of packets you wish to be sent:” << std::endl;
- std::cin >> num;
- // Now, we’ll create a RTP session, set the destination, send some
- // packets and poll for incoming data.
- RTPUDPv4TransmissionParams transparams;
- RTPSessionParams sessparams;
- // IMPORTANT: The local timestamp unit MUST be set, otherwise
- // RTCP Sender Report info will be calculated wrong
- // In this case, we’ll be sending 10 samples each second, so we’ll
- // put the timestamp unit to (1.0/10.0)
- sessparams.SetOwnTimestampUnit(1.0/10.0); //设置时间戳
- sessparams.SetAcceptOwnPackets(true); //通过这个函数我们可以设置是不是接收我们自定义的数据包
- transparams.SetPortbase(portbase); //设置监听端口
- status = sess.Create(sessparams,&transparams); //创建会话
- checkerror(status);
- RTPIPv4Address addr(destip,destport); //
- status = sess.AddDestination(addr); <span style=”color:#000000;”>// 指定RTP数据接收端
- </span> checkerror(status);
- for (i = 1 ; i <= num ; i++)
- {
- printf(”\nSending packet %d/%d\n”,i,num);
- // send the packet
- status = sess.SendPacket((void )“1234567890”,10,0,false,10); //向目标地址发送流媒体数据
- checkerror(status);
- sess.BeginDataAccess(); //数据包接收开始
- // check incoming packets
- if (sess.GotoFirstSourceWithData()) //开始递归参与者中第一个有RTP数据的流,如果找到了,就返回tree,否则返回false。在接收数据是我们常用的是这套函数,因为如果没有数据要来都没用
/
Here’s a small IPv4 example: it asks for a portbase and a destination and
starts sending packets to that destination.
*/
#include “rtpsession.h”
//定义了RTPSession
include "rtpudpv4transmitter.h"
//定义了RTPSession的第二个参数
#include "rtpipv4address.h"
//定义了rtpipv4address
include "rtpsessionparams.h"
//定义了RTPSession的第一个参数
include "rtperrors.h"
//定义了RTP中的错误信息
#ifndef WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
else
#include <winsock2.h>
endif // WIN32
include <stdlib.h>
include <stdio.h>
include <iostream>
include <string>
using namespace jrtplib; // // This function checks if there was a RTP error. If so, it displays an error // message and exists. // //获得出错信息 void checkerror(int rtperr) { if (rtperr < 0) { std::cout << “ERROR: ” << RTPGetErrorString(rtperr) << std::endl; exit(-1); } } // // The main routine // int main(void) {ifdef WIN32 //windows下编程加载套接字库
WSADATA dat;
WSAStartup(MAKEWORD(2,2),&dat);
endif // WIN32
RTPSession sess;
uint16_t portbase,destport;
uint32_t destip;
std::string ipstr;
int status,i,num;
// First, we'll ask for the necessary information
std::cout << "Enter local portbase:" << std::endl;
std::cin >> portbase;
std::cout << std::endl;
std::cout << "Enter the destination IP address" << std::endl;
std::cin >> ipstr;
destip = inet_addr(ipstr.c_str());
if (destip == INADDR_NONE)
{
std::cerr << "Bad IP address specified" << std::endl;
return -1;
}
// The inet_addr function returns a value in network byte order, but
// we need the IP address in host byte order, so we use a call to
// ntohl
destip = ntohl(destip);
std::cout << "Enter the destination port" << std::endl;
std::cin >> destport;
std::cout << std::endl;
std::cout << "Number of packets you wish to be sent:" << std::endl;
std::cin >> num;
// Now, we'll create a RTP session, set the destination, send some
// packets and poll for incoming data.
RTPUDPv4TransmissionParams transparams;
RTPSessionParams sessparams;
// IMPORTANT: The local timestamp unit MUST be set, otherwise
// RTCP Sender Report info will be calculated wrong
// In this case, we'll be sending 10 samples each second, so we'll
// put the timestamp unit to (1.0/10.0)
sessparams.SetOwnTimestampUnit(1.0/10.0); //设置时间戳
sessparams.SetAcceptOwnPackets(true); //通过这个函数我们可以设置是不是接收我们自定义的数据包
transparams.SetPortbase(portbase); //设置监听端口
status = sess.Create(sessparams,&transparams); //创建会话
checkerror(status);
RTPIPv4Address addr(destip,destport); //
status = sess.AddDestination(addr); <span style="color:#000000;">// 指定RTP数据接收端
checkerror(status);
for (i = 1 ; i <= num ; i++)
{
printf("\nSending packet %d/%d\n",i,num);
// send the packet
status = sess.SendPacket((void *)"1234567890",10,0,false,10); //向目标地址发送流媒体数据
checkerror(status);
sess.BeginDataAccess(); //数据包接收开始
// check incoming packets
if (sess.GotoFirstSourceWithData()) //开始递归参与者中第一个有RTP数据的流,如果找到了,就返回tree,否则返回false。在接收数据是我们常用的是这套函数,因为如果没有数据要来都没用</pre><div class="dp-highlighter bg_cpp"><div class="bar"><div class="tools"><b>[cpp]</b> <a href="#" class="ViewSource" title="view plain" onclick="dp.sh.Toolbar.Command('ViewSource',this);return false;">view plain</a><span data-mod="popu_168"> <a href="#" class="CopyToClipboard" title="copy" onclick="dp.sh.Toolbar.Command('CopyToClipboard',this);return false;">copy</a><div style="position: absolute; left: 375px; top: 2763px; width: 16px; height: 16px; z-index: 99;"><embed id="ZeroClipboardMovie_2" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_2" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=2&width=16&height=16" wmode="transparent"></div><div style="position: absolute; left: 375px; top: 2763px; width: 16px; height: 16px; z-index: 99;"><embed id="ZeroClipboardMovie_4" src="https://csdnimg.cn/public/highlighter/ZeroClipboard.swf" loop="false" menu="false" quality="best" bgcolor="#ffffff" width="16" height="16" name="ZeroClipboardMovie_4" align="middle" allowscriptaccess="always" allowfullscreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer" flashvars="id=4&width=16&height=16" wmode="transparent"></div></span><span data-mod="popu_169"> <a href="#" class="PrintSource" title="print" onclick="dp.sh.Toolbar.Command('PrintSource',this);return false;">print</a></span><a href="#" class="About" title="?" onclick="dp.sh.Toolbar.Command('About',this);return false;">?</a></div></div><ol start="1" class="dp-cpp"><li class="alt"><span><span> { </span></span></li><li class=""><span> <span class="keyword">do</span><span> </span></span></li><li class="alt"><span> { </span></li><li class=""><span> RTPPacket *pack; </span></li><li class="alt"><span> </span></li><li class=""><span> <span class="keyword">while</span><span> ((pack = sess.GetNextPacket()) != NULL) </span><span class="comment">//得到当前参与者当前媒体流的下一个RTP数据包。 {</span><span> </span></span></li><li class="alt"><span> <span class="comment">// You can examine the data here</span><span> </span></span></li><li class=""><span> printf(<span class="string">"Got packet !\n"</span><span>); </span></span></li><li class="alt"><span> </span></li><li class=""><span> <span class="comment">// we don't longer need the packet, so</span><span> </span></span></li><li class="alt"><span> <span class="comment">// we'll delete it</span><span> </span></span></li><li class=""><span> sess.DeletePacket(pack); <span class="comment">//删除rtp数据包</span><span> </span></span></li><li class="alt"><span> } </span></li><li class=""><span> } <span class="keyword">while</span><span> (sess.GotoNextSourceWithData()); </span><span class="comment">//设置当前的源(source)为source table中有RTP数据的下一个源。如果已经到尾部了就返回false.</span><span> </span></span></li><li class="alt"><span> } </span></li><li class=""><span> </span></li><li class="alt"><span> sess.EndDataAccess(); <span class="comment">//数据包接收结束</span><span> </span></span></li><li class=""><span> </span></li><li class="alt"><span><span class="preprocessor">#ifndef RTP_SUPPORT_THREAD</span><span> </span></span></li><li class=""><span> status = sess.Poll(); <span class="comment">//轮询线程</span><span> </span></span></li><li class="alt"><span> checkerror(status); </span></li><li class=""><span><span class="preprocessor">#endif // RTP_SUPPORT_THREAD</span><span> </span></span></li><li class="alt"><span> </span></li><li class=""><span> RTPTime::Wait(RTPTime(1,0)); <span class="comment">//发完一个包后等待一定时间(其中括号中第一个参数表示秒,第二个表示微秒)</span><span> </span></span></li><li class="alt"><span> } </span></li><li class=""><span> </span></li><li class="alt"><span> sess.BYEDestroy(RTPTime(10,0),0,0); <span class="comment">//释放资源最多等待十秒超时,然后释放所有占有资源</span><span> </span></span></li><li class=""><span> </span></li><li class="alt"><span><span class="preprocessor">#ifdef WIN32 //windows下编程清理套接字库</span><span> </span></span></li><li class=""><span> WSACleanup(); </span></li><li class="alt"><span><span class="preprocessor">#endif // WIN32</span><span> </span></span></li><li class=""><span> <span class="keyword">return</span><span> 0; </span></span></li><li class="alt"><span>} </span></li></ol></div><pre class="cpp" name="code" style="display: none;"> {
do
{
RTPPacket *pack;
while ((pack = sess.GetNextPacket()) != NULL) //得到当前参与者当前媒体流的下一个RTP数据包。 {
// You can examine the data here
printf("Got packet !\n");
// we don't longer need the packet, so
// we'll delete it
sess.DeletePacket(pack); //删除rtp数据包
}
} while (sess.GotoNextSourceWithData()); //设置当前的源(source)为source table中有RTP数据的下一个源。如果已经到尾部了就返回false.
}
sess.EndDataAccess(); //数据包接收结束
ifndef RTP_SUPPORT_THREAD
status = sess.Poll(); //轮询线程
checkerror(status);
endif // RTP_SUPPORT_THREAD
RTPTime::Wait(RTPTime(1,0)); //发完一个包后等待一定时间(其中括号中第一个参数表示秒,第二个表示微秒)
}
sess.BYEDestroy(RTPTime(10,0),0,0); //释放资源最多等待十秒超时,然后释放所有占有资源
ifdef WIN32 //windows下编程清理套接字库
WSACleanup();
endif // WIN32
return 0;
}
因为我使用的是linux平台,所以,不考虑win32相关的代码。
重要类的含义:
class RTPSession
该类属于RTP使用的高级层。客户最终通过该类或派生类使用RTP。该类完全把RTCP的实现细节封装内部,并提供读取和发送数据的接口,使用者只需关心实际数据的收发。
class RTPSessionParams是对其参数控制类。
class RTPPollThread
RTP线程类。
线程功能:接收 RTP和RTCP数据包,然后自动发送RTCP包。
class RTPUDPv4Transmitter
UDP OVER IPV4的传输模块。 此类继承了RTPTransmitter 类,并实现了在UDP over IPv4 上的 RTP和RTCP数据的收发。该模块的参数由RTPUDPv4TransmissionParams 类设置。该模块用RTPRawPacket 类来存储到达的RTCP和RTP数据。收到的数据包都放到rawpacketlist对列中。
class RTPSources
该类维护一个哈希散列表,列表里包含参加者的源信息。该类提供函数 用来轮询处理每个参与者的RTCP和RTCP 数据包。每次收到包 都会根据该包的SSRC 把该包塞入到哈希表中,而进一步的数据处理则交给更下层的RTPInternalSourceData类。该类的设计是为了更好的管理来自不同SSRC的数据。
class RTPSessionSources : public RTPSources
该类继承了RTPSources,重载了RTPSources中的一些纯虚函数。为RTPSession会话类提供数据服务。即类RTPSession通过 类RTPSessionSources 操作内部数据。
RTPInternalSourceData RTPSourceData RTPSourceStats
这些类管理 数据包的存储以及 参数信息:包括RTP数据包和包的参数、RTCP 的发送者报告信息、接收者报告信息、源描述符信息,数据源发送的地址,时间等。
RTPSourceStats管理接收到包的状态参数。对它的操作被封装在RTPSourceData的接口函数中。
RTPSourceData是管理接收到的源数据包的具体实现类。
RTPInternalSourceData 继承了RTPSourceData,上层是他通过它来配置参数的以及读取数据的。
class RTPRawPacket
传输模块此类用来存储管理到达的RTP和RTCP数据。其实只是存储了指向实际数据的在指针,因此数据实体还是在传输模块里面。该类在传输模块中用std::list<RTPRawPacket*> rawpacketlist 容器队列的方式使用。
class RTPPacket
该类是RTP包的完整描述类,该类完全对应RFC3550协议里的RTP包格式。RTP包的生成有两种方式:
1 从原始的RTPRawPacket实例中粘贴。
int ParseRawPacket(RTPRawPacket &rawpack);
2根据RTP格式所需的所有参数生成。
int BuildPacket(所有参数)
class RTCPCompoundPacket(复合包)
一个实例代表一个RTCP复合包。
class RTCPCompoundPacketBuilder(创建RTCP复合包)
此类用于构建RTCP复合包,从基类RTCPCompoundPacket继承。该类维维系了RTCP复合包所需的全部信息,如各种包信息队列:报告包(发送、接收)、源描述包、离开包、应用包。等需要创建的时候组合成完整的RTCPCompoundPacket。
class RTCPPacketBuilder
该类是RTCPCompoundPacketBuilder的更高层封装。该类根据RTPPacketBuilder实例 和RTPSources实例 来自动生成下一个要发送的复合包;另外,也提供函数 决定什么时候该发送 SDES选项 ,而不是 规范名。
1.RTPSession
对于大多数的RTP应用程序,RTPSession类可能是JRTPLIB唯一使用的类。它能完全处理RTCP部份的数据包,所以用户可以把精力集中在真正的数据收发。要知道RTPSession类在多线程下并不是安全的,因此,用户要通过某些锁同步机制来保证不会出现在不同线程当中调用同一个RTPSession实例。
该类属于RTP使用的高级层。客户最终通过该类或派生类使用RTP。该类完全把RTCP的实现细节封装内部,使用者只需关系实际数据的收发。要注意的是该层不提供线程安全机制,这意味如果在上层的多线程环境对同一个RTPSession类进行操作的话,需要提供互斥保护机制。
2.inet_addr
inet_addr(const char *cp); 函数将网络地址转换为二进制的数字。网络地址字符串是以数字和点组成的字符串,如:” 192.168.1.1 ” ,返回值,成功则返回对应的网络二进制的数字,失败返回-1.
3.c_str()函数,返回const char*类型(可读不可改)的指向字符数组的指针,内容与本string串相同
4.INADDR_NONE 不合法的ip地址
5.ntohl 将32位网络字符顺序转换成主机字符顺序,将一个无符号长整形数从网络字节顺序转换为主机字节顺序。
6.RTPUDPv4TransmissionParams
可以把RTPUDPv4TransmissionParams类看作是网络接口类,它主要有以下功能:
设置/获取绑定的IP地址
设置/获取绑定的端口号
设置/获取广播TTL
获取本地IP地址串
设置/获取RTP/RTCP发送/接收缓冲区的大小
7.RTPSessionParams
是对其参数的控制类
8.sessparams.SetOwnTimestampUnit(1.0/10.0);
设置时间戳单元,根据传输的负载类型设置相应的时间戳单元,每秒10个采样。