首页 > 代码库 > 基于JRTPLib的rtp编程例程

基于JRTPLib的rtp编程例程

这里演示了在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