#PS:要转载请注明出处,本人版权所有

#PS:这个只是 《 我自己 》理解,如果和你的

#原则相冲突,请谅解,勿喷
开发环境:Linux 4.8.0-36-generic #36~16.04.1-Ubuntu SMP Sun Feb 5 09:39:57 UTC 2017 x86_64 x86_64 x86_64 GNU/Linux

1 首先直接下载源代码,查看其中的example1和2,里面有关于字符串的发送和接收的例子。先研究一下这个例子中JRTPlib的基本用法。然后就可以接着看下面的部分。(了解的忽略这一部分)
2 JRTPLib的使用心得。

  1. 首先我实现的所有的主要功能都来至于这些类,那就是RTPSession ,RTPSessionParams。其中RTPSession 是核心,所有的其他类都是为这个服务的。它的主要作用是管理一个rtp会话,而另外一个是设置这个会话的参数。
  2. 这个库使用的主要流程是建立一个会话对象,为这个会话设置参数,创建会话,循环等待响应和处理会话(也就是发送和接收)。所以这个库的核心就在循环等待这一块,其他的都是约定好的东西,照搬就可以。
  3. 现在我们进入正题,也就是循环等待部分(JRTPLib的核心)

    • 发送部分
      RTPSession .SendPacket(buf,len)用来发送数据
    • 接收部分
      RTPSession .BeginDataAccess();RTPSession .EndDataAccess();之间的部分就是接收和处理RTP数据。

3 正题,H264裸视频的传输,以实际代码为例。
a 首先是H264相关是设定,如果对RTP协议不太了解,可去找找相关资料来看一看就OK了。

    this->sess.SetDefaultPayloadType(96);//设置传输类型 ,来至于rtp协议规范 
    this->sess.SetDefaultMark(true);      //设置位,true标志此分包是最后一个包,false反之  
    this->sess.SetTimestampUnit(1.0/9000.0); //设置采样间隔  
    this->sess.SetDefaultTimestampIncrement(10);//设置时间戳增加间隔  

b 然后是传输参数的设定

    sessparams.SetOwnTimestampUnit(1.0/10.0); //时间戳单位  
    sessparams.SetAcceptOwnPackets(true);   //接收自己发送的数据包,这里必须设定为true,否则不能够接收到相关的包。 
    sessparams.SetUsePredefinedSSRC(true);  //设置使用预先定义的SSRC,同步源ID,不明白的去看看相关资料
    sessparams.SetPredefinedSSRC(SSRC);     //定义SSRC

c h264裸视频数据的实时传输(这里有一个非常重要的东西,叫做拆包,学过《计算机网络》的人都应该知道这么一个东西吧),宏CODE_M部分的拆包算法是我写的,另外一部分的是网上传播最宽的一个拆包算法,我没有找到最开始的出处,所以就见谅啦,那位大神,我用来做对照。此外对于H264的传输,一定要知道什么是NALU(header,data),如果不知道去查资料,同时最好去看看RTP协议发送相关的知识。这部分传入的参数为Libx264编码之后生成的Nalu

//Internet 对 pkg-len>1400丢包概率很大(出之于某一篇论文),RTP header 12bytes,RTP pkg-max-len <= 1388
//MTU 48~1500, UDP data max len = 1500 - 20(IP header) - 8(UDP header)
//上面的东西看不懂,请回去翻翻计算机网络
    if ( NaluSize <= MAX_RTP_PKT_LENGTH )  
    {    
        memcpy(Sbuf,p_Nalu,NaluSize);    
        status = this->sess.SendPacket((void *)Sbuf,NaluSize);  

        CheckError(status);  
    }   
    else if (NaluSize > MAX_RTP_PKT_LENGTH){

        this->sess.SetDefaultMark(false);//mark that this is not finall pkg
        int k=0,l=0;    
        k = NaluSize / MAX_RTP_PKT_LENGTH;  //k>=1
        l = NaluSize % MAX_RTP_PKT_LENGTH;  //l>=0
        int t=0;//用指示当前发送的是第几个分片RTP包
        int SendLen;
#ifdef CODE_M
        while( (t < k) || ((t == k) && (l > 0)) ){

            if ( t < k - 1){//0~k-2,total k - 1 pkgs

                memcpy(Sbuf,(p_Nalu+( t * MAX_RTP_PKT_LENGTH)),MAX_RTP_PKT_LENGTH);
                status = this->sess.SendPacket((void *)Sbuf,MAX_RTP_PKT_LENGTH);  
                CheckError(status);  

            }
            else {//last pkg, t == k,l > 0, or (k -1)'th pkg

                if ( (t == k-1) && ( l == 0 ) || (l > 0) && ( t == k)){

                    this->sess.SetDefaultMark(true);

                    if ( l > 0 )
                        SendLen = l;
                    else
                        SendLen = MAX_RTP_PKT_LENGTH;

                    memcpy(Sbuf,(p_Nalu+( t * MAX_RTP_PKT_LENGTH)),SendLen);
                    status = this->sess.SendPacket((void *)Sbuf,SendLen);  
                    CheckError(status);  

                }
                else {//(t == k-1) && ( l > 0 )

                    memcpy(Sbuf,(p_Nalu+( t * MAX_RTP_PKT_LENGTH)),MAX_RTP_PKT_LENGTH);
                    status = this->sess.SendPacket((void *)Sbuf,MAX_RTP_PKT_LENGTH);  
                    CheckError(status);  

                }
            }
            t++;

        }
#else
 while( t < k || ( t==k && l>0 ) )    
        {    
            if( (0 == t ) || ( t<k && 0!=t ) )//第一包到最后包的前一包  
            {  
                /*sendbuf[0] = (nalHeader & 0x60)|28;   
                sendbuf[1] = (nalHeader & 0x1f); 
                if ( 0 == t ) 
                { 
                    sendbuf[1] |= 0x80; 
                } 
                memcpy(sendbuf+2,&pSendbuf[t*MAX_RTP_PKT_LENGTH],MAX_RTP_PKT_LENGTH); 
                status = this->SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2);*/  
                memcpy(Sbuf,&p_Nalu[t*MAX_RTP_PKT_LENGTH],MAX_RTP_PKT_LENGTH);  
                status = this->sess.SendPacket((void *)Sbuf,MAX_RTP_PKT_LENGTH);  
                CheckError(status);  
                t++;  
            }  
            //最后一包  
            else if( ( k==t && l>0 ) || ( t== (k-1) && l==0 ))  
            {  
                //设置标志位Mark为1  
                this->sess.SetDefaultMark(true);  

                int iSendLen;  
                if ( l > 0)  
                {  
                    iSendLen = NaluSize - t*MAX_RTP_PKT_LENGTH;  
                }  
                else  
                    iSendLen = MAX_RTP_PKT_LENGTH;  

                //sendbuf[0] = (nalHeader & 0x60)|28;    
                //sendbuf[1] = (nalHeader & 0x1f);  
                //sendbuf[1] |= 0x40;  

                //memcpy(sendbuf+2,&pSendbuf[t*MAX_RTP_PKT_LENGTH],iSendLen);  
                //status = this->SendPacket((void *)sendbuf,iSendLen+2);  

                memcpy(Sbuf,&p_Nalu[t*MAX_RTP_PKT_LENGTH],iSendLen);  
                status = this->sess.SendPacket((void *)Sbuf,iSendLen);  

                CheckError(status);  
                t++;  
            }  

        }  
#endif
    }

d h264数据的接收部分,就是通过一个函数来对包进行组包操作,然后得到了传输数据,这部分代码和传输时拆包部分,息息相关。

    if(pack.GetPayloadType() == H264)  
    {  
        //std::cout<<"Got H264 packet:êo " << rtppack.GetExtendedSequenceNumber() << " from SSRC " << srcdat.GetSSRC() <<std::endl;  
        if(pack.HasMarker())//如果是最后一包则进行组包  
        {  

            //printf("Got a nal unit \n");
            memcpy(this->rframe->pframe + this->cur_size,pack.GetPayloadData(),pack.GetPayloadLength());        
            this->cur_size += pack.GetPayloadLength();
            this->rframe->use_len = this->cur_size;


//***************************
#define DO_WRITE_FILE
#ifdef DO_WRITE_FILE
            int fd_out = open("rec.h264", O_CREAT | O_RDWR,S_IRWXU|S_IRWXO|S_IRWXG);
            lseek(fd_out, 0, SEEK_END);
            write(fd_out, this->rframe->pframe,this->rframe->use_len); 
            close(fd_out);
#endif
//***************************

            while(1){
            if ( cir_buf.reserve() > this->rframe->use_len){

                for (long i = 0; i < this->rframe->use_len; i++){

                    cir_buf.push_back( *(this->rframe->pframe + i) );
                }
                break;
            }
            }



            memset(this->rframe->pframe,0,this->rframe->use_len);//清空缓存,为下次做准备  

            this->cur_size = 0;  
        }  
        else//放入缓冲区,在此必须确保有序  
        {  
            //unsigned char* p = rtppack.GetPayloadData();  


            memcpy(this->rframe->pframe + this->cur_size,pack.GetPayloadData(),pack.GetPayloadLength());  
            this->cur_size += pack.GetPayloadLength();  
        }  
    }  

这里只提供发送部分的代码,我的接收部分的代码和服务端写在一起的不好抽离。
y_jrtp.cpp

#include "y_jrtp.h"

static bool CheckError(int rtperr)
{
    if (rtperr < 0)
    {
        std::cout<<"ERROR: "<<RTPGetErrorString(rtperr)<<std::endl;
        return false;
    }
    return true;
}


yJRTPLIB::yJRTPLIB(void){

}
yJRTPLIB::~yJRTPLIB(void){

}

void yJRTPLIB::SendX264NalUnit(uint8_t * const p_payload, const int i_payload){

    uint8_t *p_Nalu;
    int NaluSize;
    uint8_t Sbuf[MAX_RTP_PKT_LENGTH];
    int status;


    p_Nalu = p_payload;
    NaluSize = i_payload;
    CLEAR_MEM(Sbuf);

    printf("Nal unit length is %d \n",NaluSize);
    //去除前导码0x000001 或者0x00000001
    //if( 0x01 == m_h264Buf[2] )
    //{
    //  pSendbuf = &m_h264Buf[3];
    //  buflen -= 3;
    //}
    //else
    //{
    //  pSendbuf = &m_h264Buf[4];
    //  buflen -= 4;
    //}

    if ( NaluSize <= MAX_RTP_PKT_LENGTH )
    {
        memcpy(Sbuf,p_Nalu,NaluSize);
        status = this->sess.SendPacket((void *)Sbuf,NaluSize);

        CheckError(status);
    }
    else if (NaluSize > MAX_RTP_PKT_LENGTH){

        this->sess.SetDefaultMark(false);//mark that this is not finall pkg
        int k=0,l=0;
        k = NaluSize / MAX_RTP_PKT_LENGTH;  //k>=1
        l = NaluSize % MAX_RTP_PKT_LENGTH;  //l>=0
        int t=0;//用指示当前发送的是第几个分片RTP包
        int SendLen;
#ifdef CODE_M
        while( (t < k) || ((t == k) && (l > 0)) ){

            if ( t < k - 1){//0~k-2,total k - 1 pkgs

                memcpy(Sbuf,(p_Nalu+( t * MAX_RTP_PKT_LENGTH)),MAX_RTP_PKT_LENGTH);
                status = this->sess.SendPacket((void *)Sbuf,MAX_RTP_PKT_LENGTH);
                CheckError(status);

            }
            else {//last pkg, t == k,l > 0, or (k -1)'th pkg

                if ( (t == k-1) && ( l == 0 ) || (l > 0) && ( t == k)){

                    this->sess.SetDefaultMark(true);

                    if ( l > 0 )
                        SendLen = l;
                    else
                        SendLen = MAX_RTP_PKT_LENGTH;

                    memcpy(Sbuf,(p_Nalu+( t * MAX_RTP_PKT_LENGTH)),SendLen);
                    status = this->sess.SendPacket((void *)Sbuf,SendLen);
                    CheckError(status);

                }
                else {//(t == k-1) && ( l > 0 )

                    memcpy(Sbuf,(p_Nalu+( t * MAX_RTP_PKT_LENGTH)),MAX_RTP_PKT_LENGTH);
                    status = this->sess.SendPacket((void *)Sbuf,MAX_RTP_PKT_LENGTH);
                    CheckError(status);

                }
            }
            t++;

        }
#else
 while( t < k || ( t==k && l>0 ) )
        {
            if( (0 == t ) || ( t<k && 0!=t ) )//第一包到最后包的前一包
            {
                /*sendbuf[0] = (nalHeader & 0x60)|28;
                sendbuf[1] = (nalHeader & 0x1f);
                if ( 0 == t )
                {
                    sendbuf[1] |= 0x80;
                }
                memcpy(sendbuf+2,&pSendbuf[t*MAX_RTP_PKT_LENGTH],MAX_RTP_PKT_LENGTH);
                status = this->SendPacket((void *)sendbuf,MAX_RTP_PKT_LENGTH+2);*/
                memcpy(Sbuf,&p_Nalu[t*MAX_RTP_PKT_LENGTH],MAX_RTP_PKT_LENGTH);
                status = this->sess.SendPacket((void *)Sbuf,MAX_RTP_PKT_LENGTH);
                CheckError(status);
                t++;
            }
            //最后一包
            else if( ( k==t && l>0 ) || ( t== (k-1) && l==0 ))
            {
                //设置标志位Mark为1
                this->sess.SetDefaultMark(true);

                int iSendLen;
                if ( l > 0)
                {
                    iSendLen = NaluSize - t*MAX_RTP_PKT_LENGTH;
                }
                else
                    iSendLen = MAX_RTP_PKT_LENGTH;

                //sendbuf[0] = (nalHeader & 0x60)|28;
                //sendbuf[1] = (nalHeader & 0x1f);
                //sendbuf[1] |= 0x40;

                //memcpy(sendbuf+2,&pSendbuf[t*MAX_RTP_PKT_LENGTH],iSendLen);
                //status = this->SendPacket((void *)sendbuf,iSendLen+2);

                memcpy(Sbuf,&p_Nalu[t*MAX_RTP_PKT_LENGTH],iSendLen);
                status = this->sess.SendPacket((void *)Sbuf,iSendLen);

                CheckError(status);
                t++;
            }

        }
#endif
    }
}

void yJRTPLIB::SetX264Parm()
{
    this->sess.SetDefaultPayloadType(H264);//设置传输类型
    this->sess.SetDefaultMark(true);      //设置位
    this->sess.SetTimestampUnit(1.0/9000.0); //设置采样间隔
    this->sess.SetDefaultTimestampIncrement(10);//设置时间戳增加间隔
}
int yJRTPLIB::SetRtpParm(std::string &d_ip){

    int status;
    std::string ipstr = d_ip;
    uint32_t destip;

    if ( (destip = inet_addr(ipstr.c_str())) == INADDR_NONE)
    {
        std::cerr << "Bad IP address specified" << std::endl;
        return -1;
    }

    RTPUDPv4TransmissionParams transparams;
    RTPSessionParams sessparams;

    //sessparams.SetOwnTimestampUnit(1.0/9000.0); //时间戳单位
    sessparams.SetOwnTimestampUnit(1.0/10.0); //时间戳单位
    sessparams.SetAcceptOwnPackets(true);   //接收自己发送的数据包
    sessparams.SetUsePredefinedSSRC(true);  //设置使用预先定义的SSRC
    sessparams.SetPredefinedSSRC(SSRC);     //定义SSRC

    transparams.SetPortbase(PORTBASE);

    status = sess.Create(sessparams,&transparams);
    CheckError(status);

    destip = ntohl(destip);
    RTPIPv4Address addr(destip,DESTPORT);
    status = sess.AddDestination(addr);
    CheckError(status);
}

y_jrtp.h

#ifndef Y_JRTP_H
#define Y_JRTP_H

#include <rtpsession.h>
#include <rtpudpv4transmitter.h>
#include <rtpipv4address.h>
#include <rtpsessionparams.h>
#include <rtperrors.h>
#include <rtplibraryversion.h>
#include <iostream>

//port必须是偶数
#define PORTBASE 6000
#define DESTPORT 6002
#define DESTIP "127.0.0.1"
#define H264 96

/*
SSRC:同步源标识。
*/

#define SSRC 1

//Internet 对 pkg-len>1400丢包概率很大,RTP header 12bytes,RTP pkg-max-len <= 1388
//MTU 48~1500, UDP data max len = 1500 - 20(IP header) - 8(UDP header)
#define MAX_RTP_PKT_LENGTH 1300

#ifndef CLEAR_MEM
#define CLEAR_MEM(mem) memset((mem),0,sizeof((mem)))
#endif

using namespace jrtplib;



class yJRTPLIB{

    public:
        yJRTPLIB(void);
        ~yJRTPLIB(void);
        void SendX264NalUnit(uint8_t * const p_payload, const int i_payload);
        void SetX264Parm(void);
        int SetRtpParm(std::string &d_ip);
    private:
        RTPSession sess;
};


#endif // Y_JRTP_H

首先,这里面的一些代码我直接使用的网上的某些教程,每一处用了一点,然后自己整理成这样的,这里再次说明,拆包部分的宏CODE_M是我的,另外一部分是网友的,不知道是谁,找不到最开始的出处。
好了JrtpLib就这些东西了,没有啥新鲜的,对于我这种小白用户,我也只需要了解这么多,如果想要优化RTP传输或者其他的想法,你就得好好的去读读这些代码,并深入的了解RTP协议。

#PS:请尊重原创,不喜勿喷

#PS:要转载请注明出处,本人版权所有.

有问题请留言,看到后我会第一时间回复

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐