您当前的位置: 首页 >  音视频

qianbo_insist

暂无认证

  • 0浏览

    0关注

    399博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

协议圣经(二) RTP组播音视频技巧

qianbo_insist 发布时间:2021-06-20 22:10:56 ,浏览量:0

协议圣经

协议圣经一是基础,还没有写,先出2

RTP组播

RTP为半应用层,半传输层协议,可以使用tcp,也可以使用udp,组播为D类地址,为何要使用组播,组播对什么有效,如224.3.4.5,端口选择一个9200,组播的好处是: 1 由交换机和路由器来确定发包 2 不用服务器转发 3 在udp基础上发送方便

缺点:不能跨网,不能到外网上去组播,也就是一旦确定组播方式,一般也就确定了是局域网程序,这里说一般,是因为是有mbone这种internet组播的,但是是实验爱好者组成的网,并不适合实际情况。

1、h264 h265 视频组播

h264编码和h265编码使用libx264 libx265 或者直接ffmpeg来编码,ffmpeg作为世界一等一的开源项目,封装了各类硬件和软件音视频编码和解码。

2、aac opus 音频组播

aac,opus 相对视频简单,不用分包,都小于MTU的大小

3、界面编写:

使用MFC c++来编写实例代码,这个相对简陋,有能力的可以使用任何界面,甚至跨个平台去编写,这里是示例,以方便调试展示为主。

rtp组播发送

3.1 采集

使用directshow去采集音频和视频,这个在windows上已经成熟彻底,也可以使用桌面来作为视频源,这个不做要求,主要是讲原理

3.2 设置

设置相对简单,后期示例需要加上窗口来写入码率,帧率设置,音频的一些设置

播放

其实播放是比较难做的,但是我们并不是为了制作播放器,我们是为了说明协议的发送,所以播放我们使用了一个非常简单的方法,vlc,写一个sdp文件,这里暂时只用视频来说明

m=video 9200 RTP/AVP 96 a=rtpmap:96 H264/90000 a=framerate:20 c=IN IP4 234.5.6.7

就这么简单,很奇特吧,端口在9200上面,映射payload为96 ,时间戳基数为 90000,如果你不想为90000,没问题的,改程序就可以,比如帧率是整数的20帧,你完全可以用一个绝对时间戳来作为RTP时间戳。组播IP地址为234.5.6.7,ok,启动程序后,使用vlc打开这个sdp,视频出现,播放器省了。一个好的benifit是,不用服务器就可以让跟多网内的人接收

rtp组播接收

m=video 9200 RTP/AVP 96 指明视频端口9200 a= framerate:20 指明帧率20 c=IN IP4 234.5.6.7 指明组播地址234.5.6.7

rtp协议

毋庸置疑,rtp,real time protocol 实时传输协议是传输协议的基础,使用RTP over UDP,如webrtc等都是使用这些东西来做的,rtp协议RFC文档:RFC 3550,不过还有很多其他文档,一般我们按照一个基础来做。

一般我们使用12个字节头部来说明rtp,比较重要的是时间戳和ssrc,注意,rtp头部并不一定是12字节,可以扩展。ssrc代表着一个唯一的流号码,如果多路流使用同样一组ssrc,那图像就要错了。但是同一路音频和视频是同一个ssrc的,否则谁也不知道哪个视频和哪个音频是要做合流。注意不能靠IP地址,因为一个IP地址可以发出多路流。

组播程序

我们只使用发送程序,使用asio来制作发送程序,并且使用同步发送,UDP其实非常奇怪,如果没有接收,bind 以后使用send其实是会有问题的,缓冲区满会停滞,所以我们使用sendto,即使是block也是不会停滞的,使用者放心不用担心发送阻塞的问题。以下给出组播发送的程序,组播接收的设置是不一样的,组播接收必须绑定组播的端口。

#define _CRT_SECURE_NO_WARNINGS
#include 
#include 
#include 

using asio::ip::udp;


class c_multisock
{
public:
	c_multisock():v_socket(v_service)
	{}
	void read()
	{
		asio::ip::udp::endpoint sender;
		std::vector buffer;
		std::size_t bytes_readable = 0;

		asio::socket_base::bytes_readable num_of_bytes_readable(true);
		v_socket.io_control(num_of_bytes_readable);
		// Get the value from the command.
		bytes_readable = num_of_bytes_readable.get();
		// If there is no data available, then sleep.
		if (!bytes_readable)
		{
			return;
		}
		// Resize the buffer to store all available data.
		buffer.resize(bytes_readable);
		// Read available data.
		v_socket.receive_from(
			asio::buffer(buffer, bytes_readable),
			sender);

		// Extract data from the buffer.
		std::string message(buffer.begin(), buffer.end());

		// Output data.
		std::cout NRI=NRI>>5;。
		nalu_hdr->TYPE=TYPE;

		unsigned char *nalu_payload=&sendbuf[13];
		memcpy(nalu_payload,data+1,len-1);

		rtp_hdr->timestamp= little2big(_ts_current);
		int bytes=len + 12 ;						
		SendPacket(sendbuf, bytes);
	}

	else if(len>1400)
	{
		//得到该nalu需要用多少长度为1400字节的RTP包来发送
		int k=0,l=0; 
		 pagenum = (len+1400-1)/1400
		k=len/1400;
		l=len%1400;
		int t=0;
		rtp_hdr->timestamp= little2big(_ts_current);
		while(tmarker=0;

				fu_ind =(FU_INDICATOR*)&sendbuf[12]; 
				fu_ind->F=F;
				fu_ind->NRI=NRI>>5;
				fu_ind->TYPE=28;
				//设置FU HEADER,并将这个HEADER填入sendbuf[13]
				fu_hdr =(FU_HEADER*)&sendbuf[13];
				fu_hdr->E=0;
				fu_hdr->R=0;
				fu_hdr->S=1;
				fu_hdr->TYPE=TYPE;
				rtp_hdr->seq_no = little2bigs(_seq_num ++);
				//	printf("head nalu\n");
				unsigned char *nalu_payload=&sendbuf[14];//同理将sendbuf[14]赋给nalu_payload
				memcpy(nalu_payload, data +1,1400);//去掉NALU头
				int bytes=1400+14;						
				SendPacket(sendbuf, bytes);
				t++;

			}
			else if(k==t)
			{
				rtp_hdr->marker=1;
				fu_ind =(FU_INDICATOR*)&sendbuf[12]; 
				fu_ind->F=F;
				fu_ind->NRI=NRI>>5;
				fu_ind->TYPE=28;

				//设置FU HEADER,并将这个HEADER填入sendbuf[13]
				fu_hdr =(FU_HEADER*)&sendbuf[13];
				fu_hdr->R=0;
				fu_hdr->S=0;
				fu_hdr->TYPE=TYPE;
				fu_hdr->E=1;
				if(l-1>=0)
				{

					rtp_hdr->seq_no = little2bigs(_seq_num ++);
					unsigned char *nalu_payload=&sendbuf[14];
					memcpy(nalu_payload, data +t*1400+1,l-1);
					int bytes = l+14-1;
					SendPacket(sendbuf, bytes);
				}

				t++;

			}
			else if(tmarker=0;

				fu_ind =(FU_INDICATOR*)&sendbuf[12]; 
				fu_ind->F=F;
				fu_ind->NRI=NRI>>5;
				fu_ind->TYPE=28;

				//设置FU HEADER,并将这个HEADER填入sendbuf[13]
				fu_hdr =(FU_HEADER*)&sendbuf[13];
				//fu_hdr->E=0;
				fu_hdr->R=0;
				fu_hdr->S=0;
				fu_hdr->E=0;
				fu_hdr->TYPE=TYPE;
				rtp_hdr->seq_no = little2bigs(_seq_num ++);
				unsigned char *nalu_payload=&sendbuf[14];
				//	printf("length - %u\n",t*1400+1);
				memcpy(nalu_payload, data +t*1400+1,1400);
				int bytes=1400+14;						
				SendPacket(sendbuf, bytes);
				t++;
			}
		}
	}
}

以上使用h264,rtp,asio写的组播程序来描述协议,实际上,要写一个完整的代码,还是需要一定的时间的,如果读者有什么需求,可以随时和作者交流,未完待续。

关注
打赏
1663161521
查看更多评论
立即登录/注册

微信扫码登录

0.0694s