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

c++ h264RTP接收和发送程序

H264关于RTP协议的实现

H264和H265的nalu介绍

H264 NALU 使用PS封装 RTP发送

用Python生成10秒H264彩色视频

流媒体专家(10)rtp传输H264