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的主要内容,如果未能解决你的问题,请参考以下文章