rtp发送h264和h265
Posted qianbo_insist
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了rtp发送h264和h265相关的知识,希望对你有一定的参考价值。
rtp
实时传输协议,可以使用udp和tcp
h264 和 h265
h264有sps pps,se 帧,h265多一些,有sps pps vps等,不过不是很重要,接收的时候用的多一些,发送稍简单,以下是发送程序
mtu
最大传输单元,不管是tcp还是udp都必须要分包发送,因为超过mtu实际上被交换机等设备分帧,所以必须小于交换机等设备的最大传输单元,这样效率比较高。一般我们可以定义mtu为1400字节,当然可以再小一点,加上rtp协议头部的12字节,最多为1412字节,各位可以自己拟定。
网络发送
这一部分可以使用各类封装程序,也可以自己调用简单的socket api函数发送,具体不细节写了,读者可以自己用喜欢的程序来完成
void rtp_send_data(uint8_t *buf, int len, int last_packet_of_frame,uint32_t timestamp)
发送rtp for h264
分为h264和h265
void send_rtp_h264(uint8_t * data, int dlen,uint32_t timestamp)
int iSize = max_payload_size- RTP_HEADERS_SIZE;
uint8_t buf[max_payload_size];
uint8_t *pos = &buf[12];//0 -11 是rtp头部
if (dlen > iSize) //超过MTU
const uint8_t s_e_r_Start = 0x80;
const uint8_t s_e_r_Mid = 0x00;
const uint8_t s_e_r_End = 0x40;
//获取帧头数据,1byte
unsigned char naluType = (*data) & 0x1f; //获取NALU的5bit 帧类型
unsigned char nal_ref_idc = (*data) & 0x60; //获取NALU的2bit 帧重要程度 00 可以丢 11不能丢
//nal_ref_idc = 0x60;
//组装FU-A帧头数据 2byte
unsigned char f_nri_type = nal_ref_idc + 28;//F为0 1bit,nri上面获取到2bit,28为FU-A分片类型5bit
unsigned char s_e_r_type = naluType;
bool bFirst = true;
bool mark = false;
int nOffset = 1;
while (!mark)
if (dlen < nOffset + iSize) //是否拆分结束
iSize = dlen - nOffset;
mark = true;
s_e_r_type = s_e_r_End + naluType;
else
if (bFirst == true) //set the start
s_e_r_type = s_e_r_Start + naluType;
bFirst = false;
else
s_e_r_type = s_e_r_Mid + naluType;
*pos = f_nri_type;
*(pos + 1) = s_e_r_type;
memcpy(pos + 2, data + nOffset, iSize);
nOffset += iSize;
rtp_send_data(buf, iSize + 2, mark, timestamp);
else
rtp_send_data(data, dlen, true, timestamp);
h265
h265 稍微简单一些
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 = max_payload_size - RTP_HEVC_HEADERS_SIZE;
int nal_type = (buf[0] >> 1) & 0x3F;
/* send it as one single NAL unit? */
if (len <= max_payload_size) //小于对定的最大值时,直接发送(最大值一般小于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[RTP_HEVC_HEADERS_SIZE], 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[RTP_HEVC_HEADERS_SIZE], buf, len);
rtp_send_data(buf, len + 2, last_packet_of_frame,ts);
其他方式
这里建议的其他方式为使用ffmpeg,事实上ffmpeg封装了各类编码格式的rtp,可以直接调用,不详细解释,可以直接使用以下代码,当然,需要变格式时请自行改变
extern "C"
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
class RTP_Wrapper
AVFormatContext * m_fmtctx = NULL;
AVStream * m_st_v = NULL;
AVStream * m_st_a = NULL;
AVCodecContext *m_p_v = NULL;
AVCodecContext *m_p_a = NULL;
public:
//推RTP
//ffmpeg - re - i cw.ts - vcodec copy - acodec copy - f rtp_mpegts rtp ://238.123.46.66:8001 推rtp
//推UDP
//ffmpeg - re - i cw.ts - vcodec copy - acodec copy - f mpegts udp ://238.123.46.66:8001 推udp
int init_rtp(AVCodecContext *v, AVCodecContext *a,const char * url, unsigned short port)
int ret = -1;
m_p_v = v;
m_p_a = a;
//AVFrame * pFrame = av_frame_alloc();
//m_fmtctx = avformat_alloc_context();
AVOutputFormat* fmt = av_guess_format("rtp", NULL, NULL);
//AVOutputFormat* fmt = av_guess_format("rtp_mpegts", NULL, NULL);
char rtp[128];
sprintf(rtp, "%s:%d", url, port);
ret = avformat_alloc_output_context2(&m_fmtctx, fmt, fmt->name,
rtp);
//printf("Writing to %s\\n", m_fmtctx->filename);
AVDictionary *options = NULL;
av_dict_set(&options, "buffer_size", "102400", 0); //设置缓存大小,1080p可将值调大
av_dict_set(&options, "send_buffer_size", "102400", 0); //设置缓存大小,1080p可将值调大
avio_open(&m_fmtctx->pb, rtp, AVIO_FLAG_WRITE);
//初始化AVStream
if (v != NULL)
//查找编码器
AVCodec *cv = avcodec_find_encoder(AV_CODEC_ID_H264);
m_st_v = avformat_new_stream(m_fmtctx, cv);
avcodec_parameters_from_context(m_st_v->codecpar, v);
if (a != NULL)
AVCodec *ca = avcodec_find_encoder(AV_CODEC_ID_AAC);
m_st_a = avformat_new_stream(m_fmtctx, ca);
avcodec_parameters_from_context(m_st_a->codecpar, a);
#if 0
//打开输出流
char buffer[128];
//设置流格式为RTP
sprintf(buffer, "rtp://%s:%d", "127.0.0.1", 6666);
m_fmtctx->oformat = av_guess_format("rtp", buffer, NULL);
if (avio_open(&m_fmtctx->pb, buffer, AVIO_FLAG_WRITE) < 0)
return -1;
#endif
avformat_write_header(m_fmtctx, NULL);
char sdp[256];
av_sdp_create(&m_fmtctx, 1, sdp, sizeof(sdp));
FILE * fp = fopen("c:\\\\test.sdp", "wb");
fwrite(sdp, 1, strlen(sdp), fp);
fclose(fp);
printf("%s\\n", sdp);
return 0;
//设置AVCodecContext编码参数
void send(AVPacket * pkt)
//写文件头
pkt->stream_index = m_st_v->index;
pkt->pts = av_rescale_q(pkt->pts, m_p_v->time_base,m_st_v->time_base);
pkt->dts = av_rescale_q(pkt->dts, m_p_v->time_base, m_st_v->time_base);
pkt->pos = -1;
av_interleaved_write_frame(m_fmtctx, pkt);
av_packet_free(&pkt);
void UnInit()
//销毁资源
if (!(m_fmtctx->oformat->flags & AVFMT_NOFILE))
avio_close(m_fmtctx->pb);
avformat_free_context(m_fmtctx);
;
以上是关于rtp发送h264和h265的主要内容,如果未能解决你的问题,请参考以下文章