说在前面
RTP,实时传输协议(Real-time Transport Protocol或简写RTP)是一个网络传输协议,基于UDP; jrtplib,基于C++、面向对象的RTP封装库; jthread,是一个基础库,jrtplib依赖于该库; jrtplib是对RTP协议的封装库,QT调用jrtplib库使用RTP协议; Qt根据编译器类型(msvc、mingw)不同,需要使用不同方式编译的jrtplib库和jthread库;
jrtplib库和jthread库的编译参见另一篇博文: https://blog.csdn.net/weixin_40355471/article/details/119574994
视频聊天说明
一般视频聊天需要识别摄像头、图像采集、图像转换为内存二进制数据并发送,对端需要接收二进制数据转换为图片并显示到界面; 当图片大于MTU(最大传输单元)时,一次数据包无法正常发送,需要做分包处理,接收端再进行组包,建议单个包大小1000字节; 使用RTP协议的优势:之前使用udp发送数据,当图片较大时丢包丢帧严重,无法进行高清视频聊天,RTP则能实现稳定的数据收发; 本博文只介绍RTP数据收发。
动态库引用
jrtplib库和jthread库编译完成后,把包含头文件的文件夹jrtplib3\和jthread\拷贝到工程pro文件同级目录的include文件夹,把编译好的库拷贝的构建目录(exe可执行文件所在文件夹),在pro文件添加:
LIBS += -lWs2_32
INCLUDEPATH += $$PWD/include
DEPENDPATH += $$PWD/include
LIBS +=-LD:\HSCompany\Qtcode\VideoCallHljVTM\source\build-VideoCallHljVTM_kf-Desktop_Qt_5_12_2_MinGW_64_bit-Release\bin -ljthread
LIBS +=-LD:\HSCompany\Qtcode\VideoCallHljVTM\source\build-VideoCallHljVTM_kf-Desktop_Qt_5_12_2_MinGW_64_bit-Release\bin -ljrtp
调用代码
首先初始化,设置参数,创建RTPSession会话,发送数据给对方,需要添加目标RtpAddDestination(QString ip, int port),可以添加多个目标,也可以删除目标,接收数据是通过while循环轮询方式。
初始化RTPSession会话
#include <WinSock2.h>
#include <jrtplib3/rtpconfig.h>
#include <jrtplib3/rtpsession.h>
#include <jrtplib3/rtpudpv4transmitter.h>
#include <jrtplib3/rtpipv4address.h>
#include <jrtplib3/rtpsessionparams.h>
#include <jrtplib3/rtperrors.h>
#include <jrtplib3/rtplibraryversion.h>
using namespace jrtplib;
using namespace std;
RTPSession sess;
sess.SetDefaultPayloadType(96);
sess.SetDefaultMark(true);
sess.SetTimestampUnit(1.0/9000.0);
sess.SetDefaultTimestampIncrement(10);
#ifdef RTP_SOCKETTYPE_WINSOCK
WSADATA dat;
WSAStartup(MAKEWORD(2,2),&dat);
#endif
uint16_t portbase = GlobalVar_VC::g_Video_SendLocal_Port.toInt();
int status;
RTPSessionParams sessparams;
sessparams.SetOwnTimestampUnit(1.0/10.0);
sessparams.SetAcceptOwnPackets(true);
sessparams.SetUsePredefinedSSRC(true);
sessparams.SetPredefinedSSRC(1);
RTPUDPv4TransmissionParams transparams;
transparams.SetPortbase(portbase);
status = sess.Create(sessparams,&transparams);
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<" "<<RTPGetErrorString(status).c_str();
发送数据,添加RTP目标端,删除RTP目标端
int ImageCaptureThre::sendDataRTP(char *data, int len)
{
int status = sess.SendPacket((void *)data,len,0,false,10);
return status;
}
void ImageCaptureThre::RtpAddDestination(QString ip, int port)
{
int status;
std::string ipstr;
ipstr = ip.toStdString();
uint16_t destport;
uint32_t destip;
destip = inet_addr(ipstr.c_str());
if (destip == INADDR_NONE)
{
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"Bad IP address specified ";
return;
}
destip = ntohl(destip);
destport = port;
RTPIPv4Address addr(destip,destport);
status = sess.AddDestination(addr);
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<" "<<RTPGetErrorString(status).c_str();
}
void ImageCaptureThre::RtpDelDestination(QString ip, int port)
{
int status;
std::string ipstr;
ipstr = ip.toStdString();
uint16_t destport;
uint32_t destip;
destip = inet_addr(ipstr.c_str());
if (destip == INADDR_NONE)
{
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<"Bad IP address specified ";
return;
}
destip = ntohl(destip);
destport = port;
RTPIPv4Address addr(destip,destport);
status = sess.DeleteDestination(addr);
qDebug()<<"["<<__FILE__<<"]"<<__LINE__<<__FUNCTION__<<" "<<RTPGetErrorString(status).c_str();
}
接收数据
void RcvImageUdp::revData()
{
while(isRevImage == true)
{
QThread::msleep(10);
sess.BeginDataAccess();
if (sess.GotoFirstSourceWithData())
{
do
{
RTPPacket *pack;
while ((pack = sess.GetNextPacket()) != NULL)
{
int recvSize = pack->GetPacketLength();
uint8_t * recvData=pack->GetPacketData();
sess.DeletePacket(pack);
}
} while (sess.GotoNextSourceWithData());
}
sess.EndDataAccess();
}
}
|