IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> rtp发送h264和h265 -> 正文阅读

[网络协议]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);
	}
};
  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-02-07 14:01:51  更:2022-02-07 14:02:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年10日历 -2024/10/5 11:18:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码