查了不少网页,自己又折腾了一段时间,终于可以实现视频的传输了。我用的是jrtplib3.7.1发送和接收rtp包
发送端:
#include
#include
#include
#include
#include
#include
//#include "jrtplib3/rtpsession.h"
#include "rtpsession.h"
#include "rtppacket.h"
#include "rtpudpv4transmitter.h"
#include "rtpipv4address.h"
#include "rtpsessionparams.h"
#include "rtperrors.h"
#include "qdebug.h"
#include "rtpsender.h"
RTPTime delay (0.0001);
#define PacketMaxsize 1400
//#define PacketMaxsize 64000
RtpSender::RtpSender()
{
portbase=8000;//本地端口
destIP="127.0.0.1";
destip=inet_addr( destIP.toStdString().c_str() );
destip = ntohl(destip);
destport=9000;
ret =0;
done= false;
sendbyte=0;
isCreat = false;
printf("send init ok/n");
}
RtpSender::~RtpSender()
{
}
// 错误处理函数
void RtpSender::checkerror(int err)
{
if (err < 0)
{
std::cout << "ERROR: " << RTPGetErrorString(err) << std::endl;
exit(-1);
}
}
void RtpSender::initRtp()
{
if(isCreat=true)
sess.BYEDestroy(RTPTime(1,0),0,0);
RTPSessionParams sessParams;
sessParams.SetOwnTimestampUnit(1.0 / 90000.0); //90000 samples per second
sessParams.SetUsePollThread(1); //background thread to call virtual callbacks - set by default, but just to be sure
sessParams.SetMaximumPacketSize(64000);
//setup transmission parameters
RTPUDPv4TransmissionParams transParams;
transParams.SetPortbase(portbase);
// 创建RTP会话
err_status = sess.Create(sessParams,&transParams);
checkerror(err_status);
// 指定RTP数据接收端
RTPIPv4Address addr(destip,destport);
err_status = sess.AddDestination(addr);
checkerror(err_status);
isCreat = true;
/*
qDebug() << "destIP:" << destIP;
qDebug() << "destort: " << destport;
qDebug() << "portbase: " << portbase;
*/
}
int RtpSender::m_sendPacket(unsigned char*data,int framelength)
{
//RTPSession sess;
// 发送流媒体数据
sendbyte=0;
done= false;
do
{
//printf("here?/n");
if(framelength>PacketMaxsize)//需要分包
{
ret = sess.SendPacket(data,PacketMaxsize,96,0,1000);
checkerror(ret);
data = data + PacketMaxsize; //update发送指针
framelength = framelength - PacketMaxsize;
sendbyte = sendbyte + PacketMaxsize;
//printf("divide packet/n");
}
else
{
ret= sess.SendPacket(data,framelength,96,1,1000); //发送函数 第四个参数marker决定是否是该frame最后小于1400的数据
checkerror(ret);
done = true; //如果是 表示完成
sendbyte = framelength;
//printf("send a paket/n");
}
RTPTime::Wait (delay);
// RTPTime::Wait(RTPTime(1,0));
#ifndef RTP_SUPPORT_THREAD
status = sess.Poll();
checkerror(status);
#endif // RTP_SUPPORT_THREAD
} while(!done);
return sendbyte;
}
初始化rtp参考jrtplib自带的example和网页上的方法,我自己分包发送的,分包好处是接收时可以减少丢包。
接收端主要代码:
void RtpReceiver::m_receiPacket(unsigned char* recvpointer)
{
RTPIPv4Address* addr;
int nPort;
#ifndef RTP_SUPPORT_THREAD
err_status = sess.Poll();//必要时发送rtcp
checkerror(err_status);
#endif
//标志位应放在循环外,保证每次都被设置
packetflage = false;
DecodeEnable = false;
recvlength =0; //初始化接收数据 以及数据接收标示
// 检索RTP数据源
sess.BeginDataAccess();
if (sess.GotoFirstSourceWithData() )
{
//printf("Begin receive/n");
do
{
RTPPacket* packet;
// RTPSourceData *srcdata;
// 获取RTP数据报
while ((packet = sess.GetNextPacket()) != NULL && packetflage==false) //标示为零 接收同一packet的剩余数据
{
if(processpacket(recvpointer,*packet))//processpacket() 返回1 已经接受到所有的packet 可以调用解码
{
packetflage =true;
recv_one_length=recvlength;
DecodeEnable =true;
}
else
{
packetflage = false; //返回0,packet还没接受完 继续sess.GetNextPacket()
DecodeEnable = false;
}
sess.DeletePacket(packet);// 删除RTP数据报
RTPTime::Wait(RTPTime(0,1000));
}
/*
//定义信号
emit receive_done();
*/
} while (sess.GotoNextSourceWithData()); //接收另一个packet
}//end if()
sess.EndDataAccess();
// 接受RTP数据
//printf("receive end/n");
}
int RtpReceiver::processpacket(unsigned char* recvpointer,const RTPPacket &rtppack)
{
unsigned char* payloadpointer = rtppack.GetPayloadData(); //接收该数据包数据
bool packetmarker = rtppack.HasMarker(); //察看部否是已经传完该数据包 Returns true is the marker bit was set
if(!packetmarker) //未传完数据包
{
memcpy(recvpointer+recvoffset,payloadpointer,rtppack.GetPayloadLength());
recvlength += rtppack.GetPayloadLength();
recvoffset += rtppack.GetPayloadLength(); //更新接收数据保存的指针
//printf("not one packet/n");
RecvOneflage = 0; //标示接受位 继续执行sess.GetNextPacket()
}
else
{
memcpy(recvpointer +recvoffset,payloadpointer,rtppack.GetPayloadLength());
recvlength += rtppack.GetPayloadLength();
recvoffset = 0; //传完,初始化
//printf("one packet received/n");
RecvOneflage =1;
}
return RecvOneflage;
}
接收端我用一个线程不停调用m_receiPacket()函数,接收到一个完整的RTP包即一帧图像后,设置解码标志,在线程中解码,保证
接收->完整RTP->解码的顺序。一开始,我的标志位没设好,结果接到的图像花屏很严重。还以为是丢包很严重造成的。实际上没有满足上述顺序,解码了多次。
最后的效果不错,局域网内很清楚,下一步移植到板子上试试。