博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
关于开源的RTP——jrtplib的使用
阅读量:5146 次
发布时间:2019-06-13

本文共 7018 字,大约阅读时间需要 23 分钟。

session.BeginDataAccess(); if (session.GotoFirstSource()){ do{ RTPPacket *packet; while ((packet = session.GetNextPacket()) != 0){ cout << "Got packet with extended sequence number " << packet->GetExtendedSequenceNumber() << " from SSRC " << packet->GetSSRC() <<endl; session.DeletePacket(packet); } } while (session.GotoNextSource()); } session.EndDataAccess();

关于jrtplib环境的建立,能够见我曾经写的总结,如今我主要来谈谈jrtplib3.71下的几个example的学习。 一、sample是一个简单的IPv4的列子,它实现了RTP在本机上的数据的传输。 1、初始化。我们知道RTP是一般是使用UDP协议来实现数据的传输,在windows环境下,当然要用到我们熟悉的套接字的使用,所以我们先要进行初始化,载入套接字库。

函数RTPGetErrorString(…)依据出错返回的一个负数来推断,利用jrtplib採用的统一出错机制返回的是一个C++中标准的字符串std::string,表示出错的信息,比喻我们指定的Portbase不是一个偶数。(为什么,以下要讲)

2、关于RTPSession对象设置
   因为要使用套接字,故我们在使用之前一定要为我们的监听socket指定一个监听端口,也就是这里的portbase的值。我们能够通过调用RTPSession的第二个參数RTPUDPv4TransmissionParams的成员函数来设定,
   transparams.SetPortbase(portbase);
 
   还有就是我们要通过RTPSession的第一个參数来设定以下一些值:
  
sessparams.SetOwnTimestampUnit(1.0/10.0);
//注意这个值我们一定要设置,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)     
//设置时间戳是一件非常重要的事情,是RTP会话初始化过程所要进行的另外一项重要工
//作,他的单位是秒,如:当使用RTP会话传输8000Hz採样的音频数据时,因为时戳
//每秒钟将递增8000,所以时戳单元对应地应该被设置成1/8000:
sessparams.SetAcceptOwnPackets(true);
//通过这个函数我们能够设置是不是接收我们自己定义的数据包。
 
3、数据发送
   我想当我们要想建立连接的时候,我们就要让发送端知道要发送到的主机的IP地址,在jrtplib中我们能够通过RTPSession成员函数AddDestination()、DeleteDestination()和 ClearDestinations()来完毕。如以下是将数据发送给本机的6000号端口:
 
unsigned long addr = ntohl(inet_addr("127.0.0.1"));
sess.AddDestination(addr, 6000);
 
当然我们还能够这样来完毕增加一个client,
 
RTPIPv4Address addr(destip,destport);//destip为clientIP地址,destport为client//端口号
status = sess.AddDestination(addr);
checkerror(status);  
  
   目标地址全部指定之后,接着就能够调用RTPSession类的SendPacket()方法,向全部的目标地址发送流媒体数据。SendPacket()是RTPSession类提供的一个重载函数,它具有下列多种形式:
int SendPacket(void *data,int len)
int SendPacket(void *data,int len,unsigned char pt,bool mark,
unsigned long timestampinc)
int SendPacket(void *data,int len,unsigned short hdrextID,void *hdrextdata,
int numhdrextwords)
int SendPacket(void *data,int len,unsigned char pt,bool mark,
unsigned long timestampinc,unsigned short hdrextID,
void *hdrextdata,int numhdrextwords)
SendPacket()最典型的使用方法是相似于以下的语句,当中第一个參数是要被发送的数据,而第二个參数则指明将要发送数据的长度,再往后依次是RTP负载类型、标识和时戳增量。例如以下所看到的:
status = sess.SendPacket((void *)"1234567890",10,0,false,10);
checkerror(status);
对于同一个RTP会话来讲,负载类型、标识和时戳增量通常来讲都是同样的,JRTPLIB同意将它们设置为会话的默认參数,这是通过调用 RTPSession类的SetDefaultPayloadType()、SetDefaultMark()和 SetDefaultTimeStampIncrement()方法来完毕的。为RTP会话设置这些默认參数的优点是能够简化数据的发送,比如,假设为 RTP会话设置了默认參数:
session.SetDefaultPayloadType(96);//注意这个參数不能随便设置,參考RFC3551
session.SetDefaultMark(false);
session.SetDefaultTimestampIncrement(160);
在设置了以上的值后,我们能够这样来发送数据:
status = sess.SendPacket((void *)"1234567890",10);
3、数据接收
对于流媒体数据的接收端,首先须要调用RTPSession类的PollData()方法来接收发送过来的RTP或者RTCP数据报。因为同一个 RTP会话中同意有多个參与者(源),你既能够通过调用RTPSession类的GotoFirstSource()和GotoNextSource() 方法来遍历全部的源,也能够通过调用RTPSession类的GotoFirstSourceWithData()和 GotoNextSourceWithData()方法来遍历那些携带有数据的源。在从RTP会话中检測出有效的数据源之后,接下去就能够调用 RTPSession类的GetNextPacket()方法从中抽取RTP数据报,当接收到的RTP数据报处理完之后,一定要记得及时释放。以下的代码示范了该怎样对接收到的RTP数据报进行处理:

 

 

#ifdef WIN32 WSADATA dat; WSAStartup(MAKEWORD(2,2),&dat); #endif // WIN32 RTPSession sess; #ifdef WIN32 WSACleanup(); #endif // WIN32 #include "rtpsession.h" #include "rtppacket.h" #include "rtpudpv4transmitter.h" //RTPUDPv4TransmissionParams #include "rtpipv4address.h" #include "rtpsessionparams.h" #include "rtperrors.h" void checkerror(int rtperr) { if (rtperr < 0) { std::cout << "ERROR: " << RTPGetErrorString(rtperr) << std::endl; exit(-1); } }

 

JRTPLIB为RTP数据报定义了三种接收模式,当中每种接收模式都详细规定了哪些到达的RTP数据报将会被接受,而哪些到达的RTP数据报将会被拒绝。通过调用RTPSession类的SetReceiveMode()方法能够设置下列这些接收模式:

① RECEIVEMODE_ALL 缺省的接收模式,全部到达的RTP数据报都将被接受;
② RECEIVEMODE_IGNORESOME  除了某些特定的发送者之外,全部到达的RTP数据报都将被接受,而被拒绝的发送者列表能够通过调用AddToIgnoreList()、ClearIgnoreList()     和DeleteFromIgnoreList()方法来进行设置;
③ RECEIVEMODE_ACCEPTSOME  除了某些特定的发送者之外,全部到达的RTP数据报都将被拒绝,而被接受的发送者列表能够通过调用AddToAcceptList ()、ClearAcceptList ()和eleteFromAcceptList方法来进行设置。
4、执行程序
   在执行程序之前,我们要知道,这是基于UDP协议的数据报服务。我们的example1是在本机上实现的RTP传输,而且仅仅有一个RTPSession对象,所以我们的监听和接收的port是同一个,所以此时我们的portBase要和我们增加到RTPSession对象中的client的port一致,如上面我们能够都设置成6000.可是假设是在两个线程中,我们就要注意了,serverport和客户port一定不一样,这也是主要的知识,用的时候要注意,多思考。

 

1.设置基本參数 RTPSession m_RTPSocket; m_RTPSocket.Create(m_SPort); m_RTPSocket.SetTimestampUnit(1.0/25.0); m_RTPSocket.SetSessionBandwidth(900.0); m_RTPSocket.SetMaxPacketSize(65535); m_RTPSocket.SetDefaultPayloadType(0); m_RTPSocket.SetDefaultMark(false); m_RTPSocket.SetDefaultTimeStampIncrement(10); 2.发送 int status = m_RTPSocket.SendPacket(pFrame,wSize,1,name,10); 3,接收 int size = 0; SOCKET sockRtp,sockRtcp; struct timeval rttprev = {0,0},rtt,tv; fd_set fdset; bool frist = true; int port = ((CRNetVideoCtrl*)m_pMainClass)-> m_destport; if(port == -1) StopThread(); RTPSession m_RTPSocket; m_RTPSocket.Create(port); m_RTPSocket.SetSessionBandwidth(64); m_RTPSocket.GetRTPSocket(&sockRtp); m_RTPSocket.GetRTCPSocket(&sockRtcp); m_pDecoder = new CVideoDecode(); m_pDecoder-> dec_init(1); unsigned char* pBuffer = new unsigned char [320*240*2]; while(!m_bClose){ tv.tv_sec = 0; tv.tv_usec = 100000; FD_ZERO(&fdset); FD_SET(sockRtcp,&fdset); FD_SET(sockRtp,&fdset); int maxSize = max(sockRtp, sockRtcp); int selectRet = select(maxSize + 1,&fdset,NULL,NULL,&tv); if (-1 == selectRet || 0 == selectRet) continue; /* poll for incoming data *///调用这个,这样才可以获得数据 m_RTPSocket.PollData(); /* check incoming packets *///原来是rtp数据包 if (FD_ISSET(sockRtp, &fdset)){ if (m_RTPSocket.GotoFirstSourceWithData()){ RTPPacket *pack; while ((pack = m_RTPSocket.GetNextPacket()) != NULL){ if(frist){ //检查是否是关键帧 if(pack-> IsMarked()){ if((CRNetVideoCtrl*)m_pMainClass){ m_pDecoder-> dec_main(pack-> GetPayload(),pBuffer,pack-> GetPayloadLength(),&size); ((CRNetVideoCtrl*)m_pMainClass)-> DecodeVideo(pBuffer,size); TRACE( "Client Received IFrame/n "); frist = false; } } } else{ //解码并显示 m_pDecoder-> dec_main(pack-> GetPayload(),pBuffer,pack-> GetPayloadLength(),&size); ((CRNetVideoCtrl*)m_pMainClass)-> DecodeVideo(pBuffer,size); } delete pack; } } } if (FD_ISSET(sockRtcp, &fdset)){ if (m_RTPSocket.GotoFirstSource()) { do { RTPSourceData *srcdat; srcdat = m_RTPSocket.GetCurrentSourceInfo(); if (NULL != srcdat) { rtt = srcdat-> INF_GetRoundTripTime(); if (rtt.tv_sec != 0 || rtt.tv_usec != 0) { double t; t = (double)rtt.tv_sec; t += ((double)rtt.tv_usec)/1000000.0; t *= 1000.0; // we want milliseconds; TRACE( "rtt: %f ms/n ", (float)t); rttprev = rtt; } if (parent-> m_canSend) { sprintf(msg, "丢包: %f/n ", srcdat-> RR_GetPacketsLost()); sprintf(msg, "包序号: %d/n ", srcdat-> RR_GetExtendedHighestSequenceNumber()); sprintf(msg, "jitter: %d/n ", srcdat-> RR_GetJitter()); reportNum = 0; } srcdat-> FlushPackets(); // we don 't need the actual data } srcdat = NULL; } while (m_RTPSocket.GotoNextSource()); } } } if(m_pDecoder) delete m_pDecoder; m_pDecoder = NULL; delete []pBuffer; return 1L;

 

 

转载于:https://www.cnblogs.com/mengfanrong/p/4035016.html

你可能感兴趣的文章
laravel 没有route.php,关于Laravel Route重定向的一个注意点
查看>>
mysql中没有join吗,mysql – SQL Join,包括表a中的行,表b中没有匹配项
查看>>
php7.2 pdo简单应用,php pdo 简单使用 (一) - pdo
查看>>
字幕下载下来是php文件,剪映字幕导出为SRT文件网页版
查看>>
matlab在自动控制中...,实验一 Matlab在自动控制理论中的应用
查看>>
php随机生成文章,PHP生成随机字符串的技巧
查看>>
Linux内核存储接口,内存管理 – Linux内核API kcalloc
查看>>
c语言除法的数据类型,c语言数据类型运算的注意
查看>>
android模拟器游戏大全,安卓模拟器游戏大全_小鸡模拟器
查看>>
android%3cspan,Microsoft 365 Apps update channel name changes: iOS, Mac, and Android
查看>>
js生成GUID
查看>>
JdbcTemplate
查看>>
利用PreparedStatement预防SQL注入
查看>>
saltsack自动化配置day01:之SaltStack快速入门(一)
查看>>
剑指offer-----回溯和其他
查看>>
Java核心技术卷1-第三章-Java的基本程序设计结构
查看>>
从C++到Java的几点区别
查看>>
【牛客Wannafly挑战赛12】小H和圣诞树
查看>>
[AH2017/HNOI2017]单旋
查看>>
[SNOI2017]一个简单的询问
查看>>