zoukankan      html  css  js  c++  java
  • 使用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]

     
  • 相关阅读:
    Openssl和PKCS#11的故事
    SSL连接建立过程分析(5)
    SSL连接建立过程分析(1)
    关于OpenSSL支持USBKEY证书的尝试
    install the CLEARCASE with eclipse 3.4,duplicate location
    10种技巧可提升Android应用运行效果
    专访实战专家 揭秘iOS神奇开发之路
    win objc codeblocks
    redeclared as different kind of symbol ,undefined reference to `__objc_class_name_Rectangle12'
    201203NEWS
  • 原文地址:https://www.cnblogs.com/vczf/p/13884512.html
Copyright © 2011-2022 走看看