编译jrtplib第一个例子

本文详细介绍了如何编译和使用JRTPLIB这个C++的RTP协议库,包括解决编译错误、链接错误的过程及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

RTP是实时 传输协议的简称. 

JRTPLIB是一个很牛的老外用C++写的一个开源的RTP协议库, 用它可以进行数据的实时传输.我现在用的是jrtplib-3.7.0版本,下载地址http://research.edm.uhasselt.be/~jori/page/index.php?n=CS.Jrtplib 

下载完后把它解压,首先需要提醒的是jrtplib只是一个库,不是可执行程序,我们只需要它编译好的jrtplib.lib文件放到我们的VC++编译环境中就可以在我们自己的程序中使用了. 


解压出来后打开jrtplib.dsw进行编译,发现了jrtplib.lib - 4 error(s), 13 warning(s), 看了一下错误的说明,都是缺少头文件jthread.h 和 jmutex.h. 因为jrtplib支持线程,所以还得去下另外一个开源的东西jthread.我现在用的是jthread-1.2.1,下载地址不记得了. 百度一下相信会有 :) . 


下载完后,打开src目录,把jmutex.h和jthread.h复制到jrtplib-3.7.0/src目录,再对其进行编译.发现还是找不到,什么原因呢,嘿嘿,别急,把#include <jmutex.h> 改成#include "jmutex.h",同样的#include <jthread.h> 改成#include "jthread.h", 这样编译就通过了. 


这里会有33个警告jrtplib.lib - 0 error(s), 33 warning(s),不用管它,算是编译器的一个BUG吧,用Win32 Debug才会出现的警告.成功以后就可以去Debug复制jrtplib.lib到D:/Program Files/Microsoft Visual Studio/VC98/Bin目录中,我的VC6.0是装在D盘. 


这时再把之前下载的jthread编译,直接编译就可以成功了,在debug目录中把jthread.lib也复制到里面.这样jrtplib就算编译完成了. 

我用的是VC6.0SP6环境 

用VC新建一个Win32的空工程,我命名为test3,把jrtplib-3.7.0/examples目录下的example1.cpp复制到该工程的目录下.在工程中建一个文件夹header专门用来保存头文件. 


把jrtplib-3.7.0/src目录中的头文件复制到header文件夹中.这时就可以在test3把example1.cpp和这些头文件加入到工程中去.(Add Files to Project..).
<!--[if !supportLineBreakNewLine]-->
<!--[endif]--> 

打开example1.cpp进行编译.发现错误e:/wu/c++/test3/example1.cpp(6) : fatal error C1083: Cannot open include file: 'rtpsession.h': No such file or directory Error executing cl.exe. 


找不到头文件 rtpsession.h, 我们把example1.cpp源码改一下#include "rtpsession.h" 改成#include "header/rtpsession.h", header就是我们建的那个文件夹名.同样的,把其它头文件包含的都改过来.有引号的改,其它像#include <stdlib.h> 是系统目录的,不用改. 


此时再次编译,又出了22个错误和5个警告.test3.exe - 22 error(s), 5 warning(s). 都是链接错误. 原因很简单了,因为我们还没把jrtplib.lib和jthread.lib引入到工程当中去,还有网络通信少不了的ws2_32.lib库,在源码的 头顶处加上 #pragma comment(lib,"jrtplib.lib")
#pragma comment(lib,"jthread.lib")
#pragma comment(lib,"ws2_32.lib")
也可以在Project -> setting -> Link 中把 jrtplib.lib jthread.lib ws2_32.lib 加进去,这样就不用在源码上改了 

加进去后,再编译,发现还是错test3.exe - 14 error(s), 6 warning(s),又是链接错误,这时还得改一个地方,也是最后一个地方,Project -> setting -> C/C++ ,Category中选择Code Generation,然后在Userun-time library中选择Multithreaded DLL,因为我们用到了多线程的链接库,得修改这个地方. 修改完后终于可以编译成功了.当时真的很高兴 呵呵.
<!--[if !supportLineBreakNewLine]-->
<!--[endif]--> 

怀着兴奋的心情运行第一个例子. 

第一个提示 Enter local portbase: ,意思很明白,一个主机端口,我输入了8989,然后回车,第二个提示 Enter the destination IP address 输入目标IP地址,我输入了127.0.0.1本机嘛. 第三个提示 Enter the destionation port 目标端口,直接输入8888.然后回车再来个提示Number of packets you wish to be sent: 你想发送多少个包,我想发送10个就输入10吧,回车. 此时错误来了:ERROR: The specified port base is not an even number,主机端口错了...怎么回事.关了再来 输入9999 127.0.0.1 8888 10 问题还是一样.这就郁闷了,源码的问题吗,看了一个源码貌似都没有错.然后就去百度了.找了很久,终于找到一个答案:jrtplib只能用偶数的端口号.... - -!汗. 重新试了一下,输入 6666 127.0.0.1 8888 10 ,终于可以发了,
<!--[if !supportLineBreakNewLine]-->
<!--[endif]--> 

但是还有一个问题,就是发的时候为什么收不到,源码中有接收的.这个问题百度了很久,都没有答案,最后自己发现的,原来把本地端口号和目标端口号一致就可以接收得到了.输入8888 127.0.0.1 8888 10,再测试,
Sending packet 1/10
Got packet !

Sending packet 2/10

Sending packet 3/10
Got packet !
Got packet !

Sending packet 4/10

Sending packet 5/10
Got packet !
Got packet !

Sending packet 6/10
Got packet !

Sending packet 7/10
Got packet !

Sending packet 8/10
Got packet !

Sending packet 9/10
Got packet !

Sending packet 10/10
Got packet !
Press any key to continue
<!--[if !supportLineBreakNewLine]-->
<!--[endif]--> 

是数据传输需要一定的时间吧,有时候发一次包并不能立即收到.第一个例子就这样顺利编译完成. 

--------------------------
找的资料和大家分享一下

 

 

转自 http://topic.youkuaiyun.com/u/20070902/14/6dca36ee-352c-42a5-bc97-67662faf6434.html

#include <jrtplib3/rtpsession.h> #include <jrtplib3/rtpudpv4transmitter.h> #include <jrtplib3/rtpipv4address.h> #include <jrtplib3/rtpipv6address.h> #include <jrtplib3/rtpudpv6transmitter.h> #include <jrtplib3/rtpsessionparams.h> #include <jrtplib3/rtperrors.h> #include <jrtplib3/rtplibraryversion.h> #include <jrtplib3/rtpsourcedata.h> #include <jrtplib3/rtprawpacket.h> #include <jrtplib3/rtcpcompoundpacket.h> #include <stdlib.h> #include <stdio.h> #include <iostream> #include <string> #include <list> #include <vector> #include <fstream> #include <arpa/inet.h> #include <cstdint> #include <net/if.h> #include "h264.h" #include <stdio.h> using namespace jrtplib; #define IPV4_ENABLE 1 //输出NALU长度和TYPE void dump(NALU_t *n) { if (!n)return; //printf("a new nal:"); printf(" len: %d ", n->len); printf("nal_unit_type: %x\n", n->nal_unit_type); } // // 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); } } int main(void) { std::cout << "JRTPLIB Using version " << RTPLibraryVersion::GetVersion().GetVersionString() << std::endl; int status = 0; RTPSession session; RTPSessionParams sessionparams; sessionparams.SetOwnTimestampUnit(1.0/90000.0); // 设置会话的时间戳单位,这里设置为 10Hz // sessionparams.SetAcceptOwnPackets(true); // 允许接收自己发送的数据包 // 启用 IPv6 地址族 sessionparams.SetAddressFamily(AF_INET6); #if IPV4_ENABLE //ipv4 RTPUDPv4TransmissionParams transparams; transparams.SetPortbase(BASE_PORT); status = session.Create(sessionparams,&transparams); #else //ipv6 RTPUDPv6TransmissionParams transparams; // 关键配置:接口绑定 unsigned int if_index = if_nametoindex("ens34"); if (if_index == 0) { std::cerr << "无效网络接口" << std::endl; return -1; } //transparams.SetMulticastInterfaceIndex(if_index); // 设置绑定的 IPv6 地址为任意地址 const char* bindAddr_str = "::1"; in6_addr bindAddr; if (inet_pton(AF_INET6, bindAddr_str, &bindAddr) != 1) { std::cerr << "无效的 IPv6 地址: " << bindAddr_str << std::endl; return 1; } transparams.SetBindIP(bindAddr); transparams.SetPortbase(BASE_PORT); status = session.Create(sessionparams, &transparams,RTPTransmitter::IPv6UDPProto);// 关键协议类型声明 #endif if (status < 0) { printf("Create session failed:\r\n"); std::cerr << RTPGetErrorString(status) << std::endl; exit(-1); } #if IPV4_ENABLE//ipv4 uint8_t localip[]={192,168,110,39}; RTPIPv4Address destAddr(localip,DEST_PORT); #else //ipv6 const char* dest_ipv6_str = "fe80::d67a:5e3c:9980"; //const char* dest_ipv6_str = "fe80::9f90:9964:b033:5c7a"; //const char* dest_ipv6_str = "::1"; // 将字符串形式的 IPv6 地址转换为 in6_addr 结构 in6_addr destIn6Addr; if (inet_pton(AF_INET6, dest_ipv6_str, &destIn6Addr) != 1) { std::cerr << "无效的 IPv6 地址: " << dest_ipv6_str << std::endl; return 1; } RTPIPv6Address destAddr(destIn6Addr, DEST_PORT); #endif status = session.AddDestination(destAddr); if (status < 0) { printf("AddDestination failed:\r\n"); std::cerr << RTPGetErrorString(status) << std::endl; exit(-1); } session.SetDefaultPayloadType(96); session.SetDefaultMark(false); session.SetDefaultTimestampIncrement(90000.0 /25.0); // 一秒25帧 RTPTime delay(0.035); //经本机测试,间隔35毫秒比较合适 //RTPTime starttime = RTPTime::CurrentTime(); NALU_HEADER *nalu_hdr; FU_INDICATOR *fu_ind; FU_HEADER *fu_hdr; char sendbuf[1500]; char* nalu_payload; unsigned int timestamp_increse=0,ts_current=0; OpenBitstreamFile((char*)"tom.h264");//打开264文件,并将文件指针赋给bits NALU_t *n; n = AllocNALU(200000);//为结构体nalu_t及其成员buf分配空间。返回值为指向nalu_t存储空间的指针 bool start=false; while(1) { if(feof(bits)) { // 当到达文件末尾时,再次偏移文件指针到开始 if (fseek(bits, 0, SEEK_SET) != 0) { std::cerr << "Failed to seek to the beginning of the file." << std::endl; // 可以选择退出程序或进行其他错误处理 exit(-1); } printf("end of file, restart from the beginning\r\n"); continue; } int size=GetAnnexbNALU(n);//每执行一次,文件的指针指向本次找到的NALU的末尾,下一个位置即为下个NALU的起始码0x000001 if(size<4) { printf("get nul error!\n"); continue; } dump(n);//输出NALU长度和TYPE if(!start) { if(n->nal_unit_type==5||n->nal_unit_type==6|| n->nal_unit_type==7||n->nal_unit_type==7) { printf("begin\n"); start=true; } } // 当一个NALU小于MAX_RTP_PKT_LENGTH字节的时候,采用一个单RTP包发送 if(n->len<=MAX_RTP_PKT_LENGTH) { //printf("ddd0\n"); //session.SetDefaultMark(false); //设置NALU HEADER,并将这个HEADER填入sendbuf[12] nalu_hdr =(NALU_HEADER*)&sendbuf[0]; //将sendbuf[12]的地址赋给nalu_hdr,之后对nalu_hdr的写入就将写入sendbuf中; nalu_hdr->F=n->forbidden_bit; nalu_hdr->NRI=n->nal_reference_idc>>5;//有效数据在n->nal_reference_idc的第6,7位,需要右移5位才能将其值赋给nalu_hdr->NRI。 nalu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[1];//同理将sendbuf[13]赋给nalu_payload memcpy(nalu_payload,n->buf+1,n->len-1);//去掉nalu头的nalu剩余内容写入sendbuf[13]开始的字符串。 ts_current=ts_current+timestamp_increse; //status = session.SendPacket((void *)sendbuf,n->len); if(n->nal_unit_type==1 || n->nal_unit_type==5) { status = session.SendPacket((void *)sendbuf,n->len,96,true,3600); } else { status = session.SendPacket((void *)sendbuf,n->len,96,true,0);\ //如果是6,7类型的包,不应该延时;之前有停顿,原因这在这 continue; } //发送RTP格式数据包并指定负载类型为96 if (status < 0) { std::cerr << RTPGetErrorString(status) << std::endl; exit(-1); } } else if(n->len>MAX_RTP_PKT_LENGTH) { //得到该nalu需要用多少长度为MAX_RTP_PKT_LENGTH字节的RTP包来发送 int k=0,l=0; k=n->len/MAX_RTP_PKT_LENGTH;//需要k个MAX_RTP_PKT_LENGTH字节的RTP包 l=n->len%MAX_RTP_PKT_LENGTH;//最后一个RTP包的需要装载的字节数 int t=0;//用于指示当前发送的是第几个分片RTP包 ts_current=ts_current+timestamp_increse; while(t<=k) { if(!t)//发送一个需要分片的NALU的第一个分片,置FU HEADER的S位 { //printf("dddd1\n"); memset(sendbuf,0,1500); //session.SetDefaultMark(false); //设置FU INDICATOR,并将这个HEADER填入sendbuf[12] fu_ind =(FU_INDICATOR*)&sendbuf[0]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中; fu_ind->F=n->forbidden_bit; fu_ind->NRI=n->nal_reference_idc>>5; fu_ind->TYPE=28; //设置FU HEADER,并将这个HEADER填入sendbuf[13] fu_hdr =(FU_HEADER*)&sendbuf[1]; fu_hdr->E=0; fu_hdr->R=0; fu_hdr->S=1; fu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[2];//同理将sendbuf[14]赋给nalu_payload memcpy(nalu_payload,n->buf+1,MAX_RTP_PKT_LENGTH);//去掉NALU头 //status = session.SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2); status = session.SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2,96,false,0); if (status < 0) { std::cerr << RTPGetErrorString(status) << std::endl; exit(-1); } t++; } //发送一个需要分片的NALU的非第一个分片,清零FU HEADER的S位,如果该分片是该NALU的最后一个分片,置FU HEADER的E位 else if(k==t)//发送的是最后一个分片,注意最后一个分片的长度可能超过MAX_RTP_PKT_LENGTH字节(当l>1386时)。 { //printf("dddd3\n"); memset(sendbuf,0,1500); //session.SetDefaultMark(true); //设置FU INDICATOR,并将这个HEADER填入sendbuf[12] fu_ind =(FU_INDICATOR*)&sendbuf[0]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中; fu_ind->F=n->forbidden_bit; fu_ind->NRI=n->nal_reference_idc>>5; fu_ind->TYPE=28; //设置FU HEADER,并将这个HEADER填入sendbuf[13] fu_hdr =(FU_HEADER*)&sendbuf[1]; fu_hdr->R=0; fu_hdr->S=0; fu_hdr->TYPE=n->nal_unit_type; fu_hdr->E=1; nalu_payload=&sendbuf[2];//同理将sendbuf[14]赋给nalu_payload memcpy(nalu_payload,n->buf+t*MAX_RTP_PKT_LENGTH+1,l-1);//将nalu最后剩余的l-1(去掉了一个字节的NALU头)字节内容写入sendbuf[14]开始的字符串。 //status = session.SendPacket((void *)sendbuf,l+1); status = session.SendPacket((void *)sendbuf,l+1,96,true,3600); if (status < 0) { std::cerr << RTPGetErrorString(status) << std::endl; exit(-1); } t++; // Sleep(100); } else if(t<k&&0!=t) { //printf("dddd2"); memset(sendbuf,0,1500); //session.SetDefaultMark(false); //设置FU INDICATOR,并将这个HEADER填入sendbuf[12] fu_ind =(FU_INDICATOR*)&sendbuf[0]; //将sendbuf[12]的地址赋给fu_ind,之后对fu_ind的写入就将写入sendbuf中; fu_ind->F=n->forbidden_bit; fu_ind->NRI=n->nal_reference_idc>>5; fu_ind->TYPE=28; //设置FU HEADER,并将这个HEADER填入sendbuf[13] fu_hdr =(FU_HEADER*)&sendbuf[1]; //fu_hdr->E=0; fu_hdr->R=0; fu_hdr->S=0; fu_hdr->E=0; fu_hdr->TYPE=n->nal_unit_type; nalu_payload=&sendbuf[2];//同理将sendbuf[14]的地址赋给nalu_payload memcpy(nalu_payload,n->buf+t*MAX_RTP_PKT_LENGTH+1,MAX_RTP_PKT_LENGTH);//去掉起始前缀的nalu剩余内容写入sendbuf[14]开始的字符串。 //status = session.SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2); status = session.SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2,96,false,0); if (status < 0) { std::cerr << RTPGetErrorString(status) << std::endl; exit(-1); } t++; } } } RTPTime::Wait(delay); } // while 结束 printf("over\n"); delay = RTPTime(3.0); session.BYEDestroy(delay,"Time's up",9); //释放资源 FreeNALU(n); return 0; } 它无法实现ipv6通信,请分析原因。程序运行结果为:hou@jhou-VirtualBox:live555_demo$ ./Vt_Main.out JRTPLIB Using version 3.11.2 this no is 0x00000001 test run 1!!! [19] len: 11 nal_unit_type: 7 begin this no is 0x00000001 test run 1!!! [28] len: 20 nal_unit_type: 8
最新发布
05-30
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值