机器人论坛
直播中

王颖

8年用户 144经验值
私信 关注
[问答]

如何使用jrtp库发送RTP视频

jrtp使用起来比较简单,这里使用分片封包模式
#ifndef RTP_RAW_SESSION_H#define RTP_RAW_SESSION_H#include "jrtplib3/rtpsession.h"#include 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 #include #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 FUbool CheckError(int rtperr){    if (rtperr < 0)    {        std::cout << "ERROR: "<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

回帖(1)

刘超

2020-11-4 11:30:05
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/n",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/n",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]




举报

更多回帖

发帖
×
20
完善资料,
赚取积分