协议圣经一是基础,还没有写,先出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++来编写实例代码,这个相对简陋,有能力的可以使用任何界面,甚至跨个平台去编写,这里是示例,以方便调试展示为主。
使用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是,不用服务器就可以让跟多网内的人接收
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写的组播程序来描述协议,实际上,要写一个完整的代码,还是需要一定的时间的,如果读者有什么需求,可以随时和作者交流,未完待续。