RTP 发送 和接收 h265

Posted qianbo_insist

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了RTP 发送 和接收 h265相关的知识,希望对你有一定的参考价值。

h265 发送RTP

首先RTP头部不变,其次,h265 头部为三字节,假定最大传输单元MTU为1400字节,并且包含头部12字节

/*F type
layerId TID
SE futype
*/
void send_rtp_h265(uint8_t *buf, int len, int last_packet_of_frame, uint32_t ts)

	//RTPMuxContext *rtp_ctx = ctx->priv_data;
	int rtp_payload_size = 1400 - 12;
	int nal_type = (buf[0] >> 1) & 0x3F;

	/* send it as one single NAL unit? */
	if (len <= 1400) //小于对定的最大值时,直接发送(最大值一般小于mtu)  
	
		/* use the original NAL unit buffer and transmit it as RTP payload */
		//rtp_send_data(buf, len, 0, ts);

	
	else //大于最大值时进行fu分组发送  
	
		/*
					create the HEVC payload header and transmit the buffer as fragmentation units (FU)

					0                   1
					0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
					+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
					|F|   Type    |  LayerId  | TID |
					+-------------+-----------------+

						F       = 0
						Type    = 49 (fragmentation unit (FU))
						LayerId = 0
						TID     = 1
		*/
		buf[0] = 49 << 1;
		buf[1] = 1;
		//此处为paylaodhdr,规范赋值应该是替换hevc数据nal 的payloadhdr的type  
		 //rtp_ctx->buf[0] = (buf[0] &0x81) | (49<<1);  
	//rtp_ctx->buf[1] = buf[1]  
	 /*
		 create the FU header

		 0 1 2 3 4 5 6 7
		 +-+-+-+-+-+-+-+-+
		 |S|E|  FuType   |
		 +---------------+

			 S       = variable
			 E       = variable
			 FuType  = NAL unit type
	 */
		buf[2] = nal_type;
		/* set the S bit: mark as start fragment */
		buf[2] |= 1 << 7;
		/* pass the original NAL header */
		//此处要注意,当是分组的第一报数据时,应该覆盖掉前两个字节的数据,h264要覆盖前一个字节的数据,即是第一包要去除hevc帧数据的paylaodhdr  
		buf += 2;
		len -= 2;
		while (len > rtp_payload_size)
		
			/* complete and send current RTP packet */
			memcpy(&buf[12], buf, rtp_payload_size);
			//rtp_send_data(buf, max_payload_size, 0,ts);

			buf += rtp_payload_size;
			len -= rtp_payload_size;

			/* reset the S bit */
			buf[2] &= ~(1 << 7);

		

		/* set the E bit: mark as last fragment */
		buf[2] |= 1 << 6;

		/* complete and send last RTP packet */
		memcpy(&buf[12], buf, len);
		//rtp_send_data(buf, len + 2, last_packet_of_frame,ts);

	

下面看ffmpeg 如何封包

static void nal_send(AVFormatContext *s1, const uint8_t *buf, int size, int last)

    RTPMuxContext *s = s1->priv_data;
    enum AVCodecID codec = s1->streams[0]->codecpar->codec_id;

    av_log(s1, AV_LOG_DEBUG, "Sending NAL %x of len %d M=%d\\n", buf[0] & 0x1F, size, last);
    if (size <= s->max_payload_size)             //max_payload_size一般<= 1500
        int buffered_size = s->buf_ptr - s->buf;
        int header_size;
        int skip_aggregate = 0;

        if (codec == AV_CODEC_ID_H264) 
            header_size = 1;
            skip_aggregate = s->flags & FF_RTP_FLAG_H264_MODE0;
         else 
        //h265 头两个字节,第二个字节是0x01;第一个字节表示NAL Type,具体哪些类型可以参考Hevc.h
            header_size = 2; 
        

        // Flush buffered NAL units if the current unit doesn't fit
        if (buffered_size + 2 + size > s->max_payload_size) 
            flush_buffered(s1, 0);
            buffered_size = 0;
        
        // If we aren't using mode 0, and the NAL unit fits including the
        // framing (2 bytes length, plus 1/2 bytes for the STAP-A/AP marker),
        // write the unit to the buffer as a STAP-A/AP packet, otherwise flush
        // and send as single NAL.
        if (buffered_size + 2 + header_size + size <= s->max_payload_size &&
            !skip_aggregate) 
            if (buffered_size == 0)  //发送第一个数据包
                if (codec == AV_CODEC_ID_H264) 
                    *s->buf_ptr++ = 24;
                 else 
                    *s->buf_ptr++ = 48 << 1;   //hevc head flag  Header 为96 后面一般会跟VPS,PPS,SPS等信息,这些包字节少,可以放在一起一次发送VPS,PPS,SSP之间插入两个字节表示各类型包长度。
                    *s->buf_ptr++ = 1;
                
            
            AV_WB16(s->buf_ptr, size);
            s->buf_ptr += 2; //数据包长度(两个字节表示)
            memcpy(s->buf_ptr, buf, size);
            s->buf_ptr += size;
            s->buffered_nals++;
         else 
            flush_buffered(s1, 0);
            ff_rtp_send_data(s1, buf, size, last);
        
     else 
        int flag_byte, header_size;
        flush_buffered(s1, 0);
        if (codec == AV_CODEC_ID_H264 && (s->flags & FF_RTP_FLAG_H264_MODE0)) 
            av_log(s1, AV_LOG_ERROR,
                   "NAL size %d > %d, try -slice-max-size %d\\n", size,
                   s->max_payload_size, s->max_payload_size);
            return;
        
        av_log(s1, AV_LOG_DEBUG, "NAL size %d > %d\\n", size, s->max_payload_size);
        if (codec == AV_CODEC_ID_H264)     //视频类型为H264
            uint8_t type = buf[0] & 0x1F;
            uint8_t nri = buf[0] & 0x60;

            s->buf[0] = 28;        /* FU Indicator; Type = 28 ---> FU-A */
            s->buf[0] |= nri;
            s->buf[1] = type;
            s->buf[1] |= 1 << 7;
            buf  += 1;
            size -= 1;

            flag_byte   = 1;
            header_size = 2;
         else 
            uint8_t nal_type = (buf[0] >> 1) & 0x3F;  // For hevc 
            /*
             * create the HEVC payload header and transmit the buffer as fragmentation units (FU)
             *
             *    0                   1
             *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
             *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
             *   |F|   Type    |  LayerId  | TID |
             *   +-------------+-----------------+
             *
             *      F       = 0
             *      Type    = 49 (fragmentation unit (FU))
             *      LayerId = 0
             *      TID     = 1
             */
            s->buf[0] = 49 << 1; //98只是Header,并不是真正意义上视频包类型。
            s->buf[1] = 1; //固定不变

            /*
             *     create the FU header
             *
             *     0 1 2 3 4 5 6 7
             *    +-+-+-+-+-+-+-+-+
             *    |S|E|  FuType   |
             *    +---------------+
             *
             *       S       = variable
             *       E       = variable
             *       FuType  = NAL unit type
             */
            s->buf[2]  = nal_type; //该字节才表示真正意义上的数据类型
            /* set the S bit: mark as start fragment */
            s->buf[2] |= 1 << 7;  //第一个数据包分片

            /* pass the original NAL header */
            buf  += 2; //去掉两个字节的头
            size -= 2;

            flag_byte   = 2;
            header_size = 3;
        

        while (size + header_size > s->max_payload_size) 
      //中间数据包分片
            memcpy(&s->buf[header_size], buf, s->max_payload_size - header_size);
            ff_rtp_send_data(s1, s->buf, s->max_payload_size, 0);
            buf  += s->max_payload_size - header_size;
            size -= s->max_payload_size - header_size;
            s->buf[flag_byte] &= ~(1 << 7);
        
        s->buf[flag_byte] |= 1 << 6;//最后一个数据包分片
        memcpy(&s->buf[header_size], buf, size);
        ff_rtp_send_data(s1, s->buf, size + header_size, last);
    

ffmpeg 把h264 和 h265 放在了一起,可以作为参考,ff_rtp_send_data 读者可以自己写出来,就是rtp 发送,udp 和 TCP 自行选择

解RTP包h265

#include <stdint.h>
#include <iostream>

#define AV_RB16(x) ((((const uint8_t*)(x))[0] << 8)|((const uint8_t*)(x))[1])

class H265Frame

public:
  unsigned char m_firstfrag;
  unsigned char m_lastfrag;
  unsigned int m_buflen;
  unsigned char *m_buf;
public:
  H265Frame();
  ~H265Frame();
  int handleHevcFrame(uint16_t seq,uint32_t timestamp,const uint8_t *buf, int len);
private:
  int handleHevcRtpPackage(uint16_t seq,uint32_t timestamp,const uint8_t *buf, int len,uint8_t *outbuf,int *outlen);
  void handleFragPackage(const uint8_t *buf, int len,int start_bit,const uint8_t *nal_header,int nal_header_len,uint8_t *outbuf,int *outlen);
  int handleAggregatedPacket(const uint8_t *buf, int len,uint8_t *outbuf, int *outlen);
;

实现:

using namespace std;

static const uint8_t start_sequence[] =  0x00, 0x00, 0x00, 0x01 ;

H265Frame::H265Frame ()

  m_firstfrag = 0;
  m_lastfrag = 0;
  m_buflen = 0;
  m_buf = new unsigned char [1*1024*1024];


H265Frame::~H265Frame ()

	delete [] m_buf;


int H265Frame::handleHevcFrame(uint16_t seq,uint32_t timestamp,const uint8_t *buf, int len)
	int nal_type = 0;
	unsigned char outbuf[2048]=0;
	int outlen=0;
	nal_type = handleHevcRtpPackage(seq,timestamp,buf,len,outbuf,&outlen);
	if(nal_type < 0)
		return -1;
	
	if(nal_type == 49)
		//first
		if(m_firstfrag)
			memcpy(m_buf,outbuf,outlen);
			m_buflen = outlen;
			return 0;
		
		//end
		else if(m_lastfrag)
			memcpy(m_buf+m_buflen,outbuf,outlen);
			m_buflen += outlen;
			return 1;
		
		//mid
		else
			memcpy(m_buf+m_buflen,outbuf,outlen);
			m_buflen += outlen;
			return 0;
		
	
	memcpy(m_buf,outbuf,outlen);
	m_buflen = outlen;
	return 1;


/*
   handle one hevc rtp package
 */
int H265Frame::handleHevcRtpPackage(uint16_t seq,uint32_t timestamp,const uint8_t *buf, int len,uint8_t *outbuf,int *outlen)
	int ret = -1;
	int tid, lid, nal_type;
    int first_fragment, last_fragment, fu_type;
    uint8_t new_nal_header[2];

	//ori
    const uint8_t *rtp_pl = buf;

    /*
     * decode the HEVC payload header according to section 4 of draft version 6:
     *
     *    0                   1
     *    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
     *   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
     *   |F|   Type    |  LayerId  | TID |
     *   +-------------+-----------------+
     *
     *      Forbidden zero (F): 1 bit
     *      NAL unit type (Type): 6 bits
     *      NUH layer ID (LayerId): 6 bits
     *      NUH temporal ID plus 1 (TID): 3 bits
     */

    nal_type =  (buf[0] >> 1) & 0x3f;
    lid  = ((buf[0] << 5) & 0x20) | ((buf[1] >> 3) & 0x1f);
    tid  =   buf[1] & 0x07;

    /* sanity check for correct layer ID */
    if (lid) 
        /* future scalable or 3D video coding extensions */
        cout << "Multi-layer HEVC coding\\n";
        return ret;
    

    /* sanity check for correct temporal ID */
    if (!tid) 
        cout << "Illegal temporal ID in RTP/HEVC packet\\n";
        return ret;
    

    /* sanity check for correct NAL unit type */
    if (nal_type > 50) 
        cout << "Unsupported (HEVC) NAL type (" << nal_type << ")\\n";
        return ret;
    

	//process
    switch (nal_type) 
    /* video parameter set (VPS) */
    case 32:
    /* sequence parameter set (SPS) */
    case 33:
    /* picture parameter set (PPS) */
    case 34:
    /*  supplemental enhancement information (SEI) */
    case 39:
    /* single NAL unit packet */
    default:
		//cout << "nal_type:" << nal_type << "\\n";
    	ret = nal_type;
    	/* the outlen */
		*outlen = sizeof(start_sequence) + len;
        /* A/V packet: copy start sequence */
        memcpy(outbuf, start_sequence, sizeof(start_sequence));
        /* A/V packet: copy NAL unit data */
        memcpy(outbuf+ sizeof(start_sequence), buf, len);
        break;
		
    /* aggregated packet (AP) - with two or more NAL units */
    case 48:
		//cout << "nal_type:" << nal_type << "\\n";
        buf += 2;
        len -= 2;
		//handl aggregated p
        if(handleAggregatedPacket(buf, len,outbuf,outlen) == 0)
			ret = nal_type;
		
        break;
    /* fragmentation unit (FU) */
    case 49:
        /* pass the HEVC payload header (two bytes) */
        buf += 2;
        len -= 2;

        /*
         *    decode the FU header
         *
         *     0 1 2 3 4 5 6 7
         *    +-+-+-+-+-+-+-+-+
         *    |S|E|  FuType   |
         *    +---------------+
         *
         *       Start fragment (S): 1 bit
         *       End fragment (E): 1 bit
         *       FuType: 6 bits
         */
        first_fragment = buf[0] & 0x80;
        last_fragment  = buf[0] & 0x40;
        fu_type        = buf[0] & 0x3f;

		m_firstfrag = first_fragment;
		m_lastfrag = last_fragment;

        /* pass the HEVC FU header (one byte) */
        buf += 1;
        len -= 1;

        //cout << "nal_type:" << nal_type << " FU type:" << fu_type << " first_frag:" << first_fragment << " last_frag:" << last_fragment << " with " << len <<" bytes\\n";

        /* sanity check for size of input packet: 1 byte payload at least */
        if (len <= 0) 
			return -1;
        

        if (first_fragment && last_fragment) 
			return -1;
        

    	ret = nal_type;

		/*modify nal header*/
        new_nal_header[0] = (rtp_pl[0] & 0x81) | (fu_type << 1);
        new_nal_header[1] = rtp_pl[1];

        handleFragPackage(buf, len, first_fragment,new_nal_header, sizeof(new_nal_header), outbuf,outlen);

        break;
    /* PACI packet */
    case 50:
        /* Temporal scalability control information (TSCI) */
		cout << "****[hevc] unhandled hevc package : " << nal_type << "\\n";
        cout << "PACI packets for RTP/HEVC\\n";
        break;
    

	return 	ret;


/*
 * process one hevc frag package,add the startcode and nal header only when the package set start_bit 
 */
void H265Frame::handleFragPackage(const uint8_t *buf, int len,int start_bit,const uint8_t *nal_header,int nal_header_len,uint8_t *outbuf,int *outlen)
    int tot_len = len;
    int pos = 0;
    if (start_bit)
        tot_len += sizeof(start_sequence) + nal_header_len;
    if (start_bit) 
        memcpy(outbuf + pos, start_sequence, sizeof(start_

以上是关于RTP 发送 和接收 h265的主要内容,如果未能解决你的问题,请参考以下文章

异步udpserver接收rtp转html5

rtp发送和接收开源地址

RTP 接发ps流工具改进

c++ h264RTP接收和发送程序

使用Jrtplib实现RTP视频数据发送接收

尝试通过RTP cpp使用ffmpeg库发送视频流时数据包丢失