当前位置:网站首页>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;
}
以上代码可以作为参考
边栏推荐
- 生产部署zabbix5.0笔记
- [freeswitch development practice] media bug obtains call voice flow
- Implement Lmax disruptor queue from scratch (VI) analysis of the principle of disruptor solving pseudo sharing and consumers' elegant stopping
- xxxxx
- Score addition and subtraction of force deduction and brushing questions (one question per day 7/27)
- Learn more than 4000 words, understand the problem of this pointing in JS, and handwrite to realize call, apply and bind
- What is eplato cast by Plato farm on elephant swap? Why is there a high premium?
- Self study notes on Apache file management -- mapping folders and configuring Apache virtual machines based on single IP and multi domain names
- Three military product baselines (functional baseline, distribution baseline, product baseline) and the documents contained in the baseline
- 2022-07-28 顾宇佳 学习笔记
猜你喜欢

Apache文件管理自学笔记——映射文件夹和基于单ip多域名配置apache虚拟机

年内首个“三连跌” 95号汽油回归“8元时代“

Shell programming specifications and variables

2022-07-28 第四小组 修身课 学习笔记(every day)
![[freeswitch development practice] media bug obtains call voice flow](/img/14/9a359403606c312b30733d4a015fa5.png)
[freeswitch development practice] media bug obtains call voice flow

Summary of basic knowledge points of C language

ROS-Errror:Did you forget to specify generate_ messages(DEPENDENCIES ...)?

Tonight at 7:30 | is the AI world in the eyes of Lianjie, Jiangmen, Baidu and country garden venture capital continue to be advanced or return to the essence of business

Unity game special effects

C语言基础知识点汇总
随机推荐
04 | background login: login method based on account and password (Part 1)
Makefile details
Unity game special effects
A case of gradually analyzing the splitting of classes -- colorful ball collisions
How to realize shortcut keys for interface scaling by vscade
国产ERP有没有机会击败SAP ?
一种简单通用的获取函数栈空间大小的方法
MYSQL入门与进阶(十三)
「PHP基础知识」输出圆周率的近似值
Unity 之游戏特效
What if MySQL forgets the password
Alibaba Sentinel - workflow and principle analysis
MYSQL入门与进阶(十二)
Shell script summary
Calculation of array serial number of force deduction questions (daily question 7/28)
军品三大基线(功能基线、分配基线、产品基线)及基线包含的文件
Arm architecture and neural network
Flask creation process day05-06 creation project
Production deployment zabbix5.0 notes
Does domestic ERP have a chance to beat sap?