这里演示了在linux环境下使用JRTPlib库完成rtp协议的封装,此例程可以作为流媒体传输的基本例程。
这里只给出源码(这些在JRtplib的官方文件中都可以找到)
发送端:
/*
* 发送端程序(windows,linux通用)
* 基于IPv4的传输例程,需要提供一个端口号和目的地址
* 参考:http://blog.csdn.net/ipromiseu/article/details/4531656
*/
#include "rtpsession.h" //定义了rtpsession的一些实现
#include "rtppacket.h" //定义了rtppacket数据包
#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>
//检查是否出现rtp错误,如果出错就打印出错信息
void checkerror(int rtperr)
{
if (rtperr < 0)
{
std::cout << "ERROR: " << RTPGetErrorString(rtperr) << std::endl;
exit(-1);
}
}
//主循环
int main(void)
{
//windows环境下我们需要首次加载套接字
#ifdef WIN32
WSADATA dat;
WSAStartup(MAKEWORD(2,2),&dat);
#endif // WIN32
RTPSession sess;
//RTPSession类来实例化此次的RTP会话
uint16_t portbase,destport;
//本机端口号,目的端口号
uint32_t destip;
//目的IP
std::string ipstr;
int status,i,num;
//首先获得必须的信息
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;
}
//inet_addr返回一个基于网络字节序列,我们需要的是一个基于本机字节序列的,
//还需要使用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;
//下面创建一个RTP会话,发送传入的数据包
//这是rtpsession的第二个参数类,他的成员函数可以设定监听端口
RTPUDPv4TransmissionParams transparams;
//这是rtpsession的第一个参数类,他的成员函数可以设置恰当的时戳单元
RTPSessionParams sessparams;
//设置恰当的时戳单元,每秒我们需要发送10次,故参数为1.0/10
sessparams.SetOwnTimestampUnit(1.0/10.0);
//下面设置是不是接收我们自定义的数据包,这里选是
sessparams.SetAcceptOwnPackets(true);
//设置本机端口
transparams.SetPortbase(portbase);
//初始化,创建rtp会话
status = sess.Create(sessparams,&transparams);
checkerror(status);
//组合目的地址
RTPIPv4Address addr(destip,destport);
//设置目的地址,增加发送的目的地址。当然可以增加很多地址完成多播的功能
//另外还可以使用DeleteDestination()和 ClearDestinations()来删除和清楚目的地址
//也可以写成: unsigned long addr = ntohl(inet_addr("127.0.0.1"));
// sess.AddDestination(addr,6000);
status = sess.AddDestination(addr);
checkerror(status);
for (i = 1 ; i <= num ; i++)
{
printf("\nSending packet %d/%d\n",i,num);
//发送流媒体数据.第一个参数是发送的数据,二个是数据长度,往后是rtp负载类型,标识,时戳量
//当然JRTPLIB允许将他们设置成会话的默认参数,这是调用RTPSession类的setDefaultpayloadtype(),setDefaultmark,等方法类完成的,如果那样设置之后,我们可以这样来发送数据:
//status = sess.SendPacket((void *)"1234567890",10);
status = sess.SendPacket((void *)"1234567890",10,0,false,10);
checkerror(status);
/*
sess.BeginDataAccess();
//收到的报文,遍历所有携带数据的源(因为一个rtp会话允许有多个参与者(源))
if (sess.GotoFirstSourceWithData())
{
do
{
RTPPacket *pack;
while ((pack = sess.GetNextPacket()) != NULL)
{
// 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);
}
} while (sess.GotoNextSourceWithData());
}
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);
//windows环境下我们最后还有卸载套接字
#ifdef WIN32
WSACleanup();
#endif // WIN32
return 0;
}
接收端:
/*
* JRTPLIB接收数据例程
* http://blog.sina.com.cn/s/blog_57e2e18901008s4h.html
*/
#include "rtpsession.h"
#include "rtppacket.h"
#include "rtpudpv4transmitter.h"
#include "rtpipv4address.h"
#include "rtpsessionparams.h"
#include "rtperrors.h"
#ifndef WIN32
#include <netinet/in.h>
#include <arpa/inet.h>
#else
#include <winsock2.h>
#endif // WIN32
#include "rtpsourcedata.h"
#include <stdlib.h>
#include <stdio.h>
#include <iostream>
#include <string>
//检查错误
void checkerror(int rtperr)
{
if (rtperr < 0)
{
std::cout << "ERROR: " << RTPGetErrorString(rtperr) << std::endl;
exit(-1);
}
}
//重新封装一个类
class MyRTPSession : public RTPSession
{
protected:
void OnNewSource(RTPSourceData *dat)
{
if (dat->IsOwnSSRC())
return;
uint32_t ip;
uint16_t port;
if (dat->GetRTPDataAddress() != 0)
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());
ip = addr->GetIP();
port = addr->GetPort();
}
else if (dat->GetRTCPDataAddress() != 0)
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());
ip = addr->GetIP();
port = addr->GetPort()-1;
}
else
return;
RTPIPv4Address dest(ip,port);
AddDestination(dest);
struct in_addr inaddr;
inaddr.s_addr = htonl(ip);
std::cout << "Adding destination " << std::string(inet_ntoa(inaddr)) << ":" << port << std::endl;
}
void OnBYEPacket(RTPSourceData *dat)
{
if (dat->IsOwnSSRC())
return;
uint32_t ip;
uint16_t port;
if (dat->GetRTPDataAddress() != 0)
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());
ip = addr->GetIP();
port = addr->GetPort();
}
else if (dat->GetRTCPDataAddress() != 0)
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());
ip = addr->GetIP();
port = addr->GetPort()-1;
}
else
return;
RTPIPv4Address dest(ip,port);
DeleteDestination(dest);
struct in_addr inaddr;
inaddr.s_addr = htonl(ip);
std::cout << "Deleting destination " << std::string(inet_ntoa(inaddr)) << ":" << port << std::endl;
}
void OnRemoveSource(RTPSourceData *dat)
{
if (dat->IsOwnSSRC())
return;
if (dat->ReceivedBYE())
return;
uint32_t ip;
uint16_t port;
if (dat->GetRTPDataAddress() != 0)
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTPDataAddress());
ip = addr->GetIP();
port = addr->GetPort();
}
else if (dat->GetRTCPDataAddress() != 0)
{
const RTPIPv4Address *addr = (const RTPIPv4Address *)(dat->GetRTCPDataAddress());
ip = addr->GetIP();
port = addr->GetPort()-1;
}
else
return;
RTPIPv4Address dest(ip,port);
DeleteDestination(dest);
struct in_addr inaddr;
inaddr.s_addr = htonl(ip);
std::cout << "Deleting destination " << std::string(inet_ntoa(inaddr)) << ":" << port << std::endl;
}
};
//
// The main routine
//
int main(void)
{
#ifdef WIN32
WSADATA dat;
WSAStartup(MAKEWORD(2,2),&dat);
#endif // WIN32
MyRTPSession sess;
uint16_t portbase;
std::string ipstr;
int status,i,num;
unsigned char *payloadpointer;
int j,len;
// First, we'll ask for the necessary information
std::cout << "Enter local portbase:" << std::endl;
std::cin >> portbase;
std::cout << std::endl;
std::cout << std::endl;
std::cout << "Number of seconds you wish to wait:" << std::endl;
std::cin >> num;
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 just use 8000 samples per second.
sessparams.SetOwnTimestampUnit(1.0/8000.0);
sessparams.SetAcceptOwnPackets(true);
transparams.SetPortbase(portbase);
status = sess.Create(sessparams,&transparams);
checkerror(status);
for (i = 1 ; i <= num ; i++)
{
sess.BeginDataAccess();
//遍历所有的带数据的源
if (sess.GotoFirstSourceWithData())
{
do
{
RTPPacket *pack;
while ((pack = sess.GetNextPacket()) != NULL)
{
// You can examine the data here
printf("Got packet !\n");
payloadpointer = pack->GetPayloadData();//获得数据指针
len = pack->GetPayloadLength();//获得数据长度
for(j=0;j<len;j++)
{
printf("%c ",*(payloadpointer+j));//打印数据
}
printf("\n");
//删除获得的数据
sess.DeletePacket(pack);
}
} while (sess.GotoNextSourceWithData());
}
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
WSACleanup();
#endif // WIN32
return 0;
}
编译:g++ -o example1 example1.cpp -I /usr/local/include/jrtplib3/ -ljrtp