当前位置:网站首页>RTP 发送 和接收 h265
RTP 发送 和接收 h265
2022-07-29 03:22:00 【qianbo_insist】
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_sequence));
pos += sizeof(start_sequence);
memcpy(outbuf + pos, nal_header, nal_header_len);
pos += nal_header_len;
}
memcpy(outbuf + pos, buf, len);
*outlen = tot_len;
}
/* aggregated packet (AP) - with two or more NAL units * we add start_sequence_code_header for every NAL unit */
int H265Frame::handleAggregatedPacket(const uint8_t *buf, int len,uint8_t *outbuf, int *outlen)
{
int pass = 0;
int total_length = 0;
uint8_t *dst = NULL;
// first we are going to figure out the total size
for (pass = 0; pass < 2; pass++) {
const uint8_t *src = buf;
int src_len = len;
while (src_len > 2) {
uint16_t nal_size = AV_RB16(src);
// consume the length of the aggregate
src += 2;
src_len -= 2;
if (nal_size <= src_len) {
if (pass == 0) {
// counting
total_length += sizeof(start_sequence) + nal_size;
} else {
// copying
memcpy(dst, start_sequence, sizeof(start_sequence));
dst += sizeof(start_sequence);
memcpy(dst, src, nal_size);
dst += nal_size;
}
} else {
cout << "[HEVC] Aggregated error,nal size exceeds length: " << nal_size << " " << src_len << "\n";
return -1;
}
// eat what we handled
src += nal_size;
src_len -= nal_size;
}
if (pass == 0) {
dst = outbuf;
*outlen = total_length;
}
}
return 0;
}
以上代码可以作为参考
边栏推荐
- Incremental real-time disaster recovery notes
- Three military product baselines (functional baseline, distribution baseline, product baseline) and the documents contained in the baseline
- C traps and defects Chapter 3 semantic "traps" 3.9 integer overflow
- 后缀自动机(sam)板子 from jly
- Redis之sentinel哨兵集群怎么部署
- 13_ UE4 advanced_ Montage animation realizes attack while walking
- How dare you write a resume that is proficient in concurrent programming? Why do you use a two-way linked list in AQS?
- How to realize shortcut keys for interface scaling by vscade
- Producer consumer model of concurrent model
- Ten thousand words detailed Google play online application standard package format AAB
猜你喜欢
照片比例校正工具:DxO ViewPoint 3 直装版
ShardingSphere之水平分表实战(三)
Apache文件管理自学笔记——映射文件夹和基于单ip多域名配置apache虚拟机
Leetcode 1331 array sequence number conversion [map] the leetcode path of heroding
Arm architecture and neural network
MySQL installation and configuration super detailed tutorial and simple database and table building method
Detailed steps for installing MySQL 8.0 under Linux
Configure vscade to realize ROS writing
暴力递归到动态规划 01 (机器人移动)
Learn more than 4000 words, understand the problem of this pointing in JS, and handwrite to realize call, apply and bind
随机推荐
01-sdram: Code of initialization module
腾讯云使用pem登录
How to realize multi line annotation in MATLAB
Sanzi chess (player + computer)
ROS-Errror:Did you forget to specify generate_ messages(DEPENDENCIES ...)?
[freeswitch development practice] unimrcp compilation and installation
How close can QA be to business code QA conducts testability transformation on business code
The Federal Reserve raised interest rates again, Powell "let go of doves" at 75 basis points, and US stocks reveled
Plato Farm在Elephant Swap上铸造的ePLATO是什么?为何具备高溢价?
"PHP Basics" output approximate value of PI
shell脚本总结
Redis configuration cache expiration listening event trigger
C traps and defects Chapter 2 syntax "traps" 2.6 problems caused by "hanging" else
「PHP基础知识」输出圆周率的近似值
2022-07-28 第四小组 修身课 学习笔记(every day)
Score addition and subtraction of force deduction and brushing questions (one question per day 7/27)
军品技术文件划分及说明
Arm architecture and neural network
Shortcut key for adjusting terminal size in ROS
简历竟然敢写精通并发编程,那你说说AQS为什么要用双向链表?