jrtp使用起来比较简单,这里使用分片封包模式
#ifndef RTP_RAW_SESSION_H #define RTP_RAW_SESSION_H #include "jrtplib3/rtpsession.h" #include <string> using namespace jrtplib; class RawSession : public RTPSession { public: RawSession(); ~RawSession(); int Init(const std::string& ip, uint16_t destport); void SendData(unsigned char* pSendbuf, int buflen); void SendData(unsigned char* pSendbuf, int buflen, uint32_t timestampinc, bool mark, uint8_t pt = 96); protected: void OnAPPPacket(RTCPAPPPacket *apppacket,const RTPTime &receivetime,const RTPAddress *senderaddress); void OnBYEPacket(RTPSourceData *srcdat); void OnBYETimeout(RTPSourceData *srcdat); }; #endif
#include "RawSession.h" #include <iostream> #include <arpa/inet.h> #include "jrtplib3/rtpudpv4transmitter.h" #include "jrtplib3/rtpipv4address.h" #include "jrtplib3/rtpsessionparams.h" #include "jrtplib3/rtperrors.h" #include "jrtplib3/rtplibraryversion.h" #include "jrtplib3/rtcpapppacket.h" namespace { uint32_t SSRC = 100; uint16_t BASE_PORT = 2222; uint32_t MAX_RTP_PKT_LENGTH = 1360; uint32_t H264 = 96; } #define FU bool CheckError(int rtperr) { if (rtperr < 0) { std::cout << "ERROR: "<<RTPGetErrorString(rtperr) << std::endl; return false; } return true; } RawSession::RawSession() { } RawSession::~RawSession() { } int RawSession::Init(const std::string& ip, uint16_t destport) { uint32_t destip = inet_addr(ip.c_str()); if (destip == INADDR_NONE) { std::cerr << "Bad IP address specified" << std::endl; return -1; } // The inet_addr function returns a value in network byte order, but // we need the IP address in host byte order, so we use a call to // ntohl destip = ntohl(destip); int status; //RTP+RTCP库初始化SOCKET环境 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 sending 10 samples each second, so we'll // put the timestamp unit to (1.0/10.0) sessparams.SetOwnTimestampUnit(1.0/90000.0); //时间戳单位 sessparams.SetAcceptOwnPackets(true); //接收自己发送的数据包 sessparams.SetUsePredefinedSSRC(true); //设置使用预先定义的SSRC sessparams.SetPredefinedSSRC(SSRC); //定义SSRC transparams.SetPortbase(BASE_PORT); status = this->Create(sessparams, &transparams); this->SetDefaultPayloadType(96); this->SetDefaultTimestampIncrement(3600); this->SetDefaultMark(true); CheckError(status); RTPIPv4Address addr(destip, destport); status = this->AddDestination(addr); CheckError(status); std::cout << "RawSession::Init ip:" << destip << " status:" << status << std::endl; return status; } // 收到RTCP报文 void RawSession::OnAPPPacket(RTCPAPPPacket *apppacket,const RTPTime &receivetime,const RTPAddress *senderaddress) { std::cout << "Got RTCP packet from: " << senderaddress << std::endl; std::cout << "Got RTCP subtype: " << apppacket->GetSubType() << std::endl; std::cout << "Got RTCP subtype: " << (char *)apppacket->GetAPPData() << std::endl; return ; } void RawSession::OnBYEPacket(RTPSourceData *srcdat) { std::cout << "OnBYEPacket" << std::endl; } void RawSession::OnBYETimeout(RTPSourceData *srcdat) { std::cout << "OnBYETimeout" << std::endl; } void RawSession::SendData(unsigned char* pSendbuf, int buflen) { //std::cout << "send packet length : " << buflen << std::endl; char sendbuf[1430]; //发送的数据缓冲 memset(sendbuf, 0, 1430); int status; if ( buflen <= MAX_RTP_PKT_LENGTH ) { memcpy(sendbuf, pSendbuf, buflen); status = this->SendPacket((void *)sendbuf, buflen); CheckError(status); } else if(buflen > MAX_RTP_PKT_LENGTH) { //设置标志位Mark为0 this->SetDefaultMark(false); //printf("buflen = %d ",buflen); //得到该需要用多少长度为MAX_RTP_PKT_LENGTH字节的RTP包来发送 int k = 0, l = 0; #ifdef FU buflen = buflen -1; #endif k = buflen / MAX_RTP_PKT_LENGTH; l = buflen % MAX_RTP_PKT_LENGTH; int t = 0;//用指示当前发送的是第几个分片RTP包 char nalHeader = pSendbuf[0]; // NALU 头ª¡¤ while( t < k || ( t==k && l>0 ) ) { if ( ( t < (k-1) ) || ( t== (k-1) && l!=0 ) )//第一包到最后包的前一包 { #ifdef FU sendbuf[0] = (nalHeader & 0x60)|28; sendbuf[1] = (nalHeader & 0x1f); if ( 0 == t ) { sendbuf[1] |= 0x80; } memcpy(sendbuf+2, &pSendbuf[t*MAX_RTP_PKT_LENGTH + 1], MAX_RTP_PKT_LENGTH); status = this->SendPacket((void *)sendbuf, MAX_RTP_PKT_LENGTH+2); #else memcpy(sendbuf, &pSendbuf[t*MAX_RTP_PKT_LENGTH], MAX_RTP_PKT_LENGTH); status = this->SendPacket((void *)sendbuf, MAX_RTP_PKT_LENGTH); #endif CheckError(status); t++; } //最后一包 else if( ( k==t && l>0 ) || ( t== (k-1) && l==0 )) { //设置标志位Mark为1 this->SetDefaultMark(true); int iSendLen; if ( l > 0) { iSendLen = buflen - t * MAX_RTP_PKT_LENGTH; } else { iSendLen = MAX_RTP_PKT_LENGTH; } #ifdef FU sendbuf[0] = (nalHeader & 0x60)|28; sendbuf[1] = (nalHeader & 0x1f); sendbuf[1] |= 0x40; memcpy(sendbuf+2, &pSendbuf[t*MAX_RTP_PKT_LENGTH + 1], iSendLen); status = this->SendPacket((void *)sendbuf, iSendLen+2); #else memcpy(sendbuf, &pSendbuf[t*MAX_RTP_PKT_LENGTH], iSendLen); status = this->SendPacket((void *)sendbuf, iSendLen); #endif CheckError(status); t++; } } } } void RawSession::SendData(unsigned char* pSendbuf, int buflen, uint32_t timestampinc, bool mark, uint8_t pt) { char sendbuf[1430]; //发送的数据缓冲 memset(sendbuf, 0, 1430); timestampinc = timestampinc * 90; int status; if ( buflen <= MAX_RTP_PKT_LENGTH ) { memcpy(sendbuf, pSendbuf, buflen); status = this->SendPacket((void *)sendbuf, buflen, pt, mark, timestampinc); CheckError(status); } else if(buflen > MAX_RTP_PKT_LENGTH) { //设置标志位Mark为0 this->SetDefaultMark(false); //printf("buflen = %d ",buflen); //得到该需要用多少长度为MAX_RTP_PKT_LENGTH字节的RTP包来发送 int k = 0, l = 0; #ifdef FU buflen = buflen -1; #endif k = buflen / MAX_RTP_PKT_LENGTH; l = buflen % MAX_RTP_PKT_LENGTH; int t = 0;//用指示当前发送的是第几个分片RTP包 char nalHeader = pSendbuf[0]; // NALU 头ª¡¤ while( t < k || ( t==k && l>0 ) ) { if ( ( t < (k-1) ) || ( t== (k-1) && l!=0 ) )//第一包到最后包的前一包 { #ifdef FU sendbuf[0] = (nalHeader & 0x60)|28; sendbuf[1] = (nalHeader & 0x1f); if ( 0 == t ) { sendbuf[1] |= 0x80; } memcpy(sendbuf+2, &pSendbuf[t*MAX_RTP_PKT_LENGTH + 1], MAX_RTP_PKT_LENGTH); status = this->SendPacket((void *)sendbuf, MAX_RTP_PKT_LENGTH + 2, pt, false, 0); #else memcpy(sendbuf, &pSendbuf[t*MAX_RTP_PKT_LENGTH], MAX_RTP_PKT_LENGTH); status = this->SendPacket((void *)sendbuf, MAX_RTP_PKT_LENGTH, pt, false, 0); #endif CheckError(status); t++; } //最后一包 else if( ( k==t && l>0 ) || ( t== (k-1) && l==0 )) { //设置标志位Mark为1 this->SetDefaultMark(true); int iSendLen; if ( l > 0) { iSendLen = buflen - t * MAX_RTP_PKT_LENGTH; } else { iSendLen = MAX_RTP_PKT_LENGTH; } #ifdef FU sendbuf[0] = (nalHeader & 0x60)|28; sendbuf[1] = (nalHeader & 0x1f); sendbuf[1] |= 0x40; memcpy(sendbuf+2, &pSendbuf[t*MAX_RTP_PKT_LENGTH + 1], iSendLen); status = this->SendPacket((void *)sendbuf, iSendLen+2, pt, true, timestampinc); #else memcpy(sendbuf, &pSendbuf[t*MAX_RTP_PKT_LENGTH], iSendLen); status = this->SendPacket((void *)sendbuf, iSendLen, pt, true, timestampinc); #endif CheckError(status); t++; } } } }
初始化部分有几点需要注意的:
1.H264固定的采样率是90000HZ,对于时间戳单位应为
sessparams.SetOwnTimestampUnit(1.0/90000.0); //时间戳单位
2.如果视频帧率是25fps,那么时间戳间隔就是3600
this->SetDefaultTimestampIncrement(3600);
当NALU的长度超过MTU时,就必须对NALU单元进行分片封包.也称为Fragmentation Units(FUs).
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| FU indicator | FU header | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
| |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Figure 14. RTP payload format for FU-A
The FU indicator octet has the following format:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|F|NRI| Type |
+---------------+
别被名字吓到这个格式就是上面提到的RTP h264负载类型,Type为FU-A
The FU header has the following format:
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E|R| Type |
+---------------+
S bit为1表示分片的NAL开始,当它为1时,E不能为1
E bit为1表示结束,当它为1,S不能为1
R bit保留位
Type就是NALU头中的Type,取1-23的那个值
这里有几个问题需要强调一下:
1.就是长度小于MTU时候,一个RTP发送一个nalu
2.每个I帧前面发送一个sps pps,这两个RTP包和接下来的I帧RTP包的时间戳相同
3.当nalu大于MTU,添加FU头,同时注意,要去掉nalu的header,就是第一个字节。FU indicator + FU header + nalu[1]