使用jrtp库发送RTP视频

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++;
            }
        }
    }

    
    
}
int Init(const std::string& ip, uint16_t destport);

初始化部分有几点需要注意的:

1.H264固定的采样率是90000HZ,对于时间戳单位应为

sessparams.SetOwnTimestampUnit(1.0/90000.0); //时间戳单位

2.如果视频帧率是25fps,那么时间戳间隔就是3600

this->SetDefaultTimestampIncrement(3600);
void SendData(unsigned char* pSendbuf, int buflen);
使用分片封包模式,这里大概介绍一下

当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]

 
原文地址:https://www.cnblogs.com/vczf/p/13884512.html