最近在制作一个基于UDP的屏幕广播,这个屏幕广播的需求是:
(1)仅仅实现电脑屏幕的画面传播即可,不需要进行音视频的同步
(2)屏幕广播实现后需要延时达到500ms以内。
一、屏幕广播的整体框架实现思路
电脑端的画面采集(rgb)->图像数据的编码压缩(rgb->yuv->h264)->采集端发送数据->渲染播放端接收数据->图像数据解码(h264->yuv)->图像渲染播放(yuv)
二、技术选型
(1)电脑端的画面采集目前选用dxgi或者gdi,优先选用dxgi(效率比gdi高)
(2)图像编解码使用开源的ffmpeg进行实现。
(3)选用udp进行传输:udp的传输时延低以及udp能够进行广播数据,不需要额外手段进行转发数据。
(4)图像渲染:目前选用sdl,因为sdl开源、跨平台的特性可移植性强,因此为将来移植到安卓端做好准备。
三、技术难点
(1)udp每次传输的数据大小比较小,最大是1472字节,如果每次发送数据大于这个值时,udp是无法广播出去的,导致接收端接收不到数据,最终导致屏幕广播无法正常运行。
==》解决的方法:解决方法目前我想到的是模仿rtsp和sip的方式,对udp数据进行分片处理,例如发送一帧200k的h264数据时需要将其拆分为大概20个数据碎片发送,然后再在接收端接收和 拼接组装成一帧再播放。
(2)由于使用了(1)的方式,因此在传输过程中很可能会存在丢帧的情况而导致存在有些帧的数据不完整,存在丢帧的原因一般有如下:
a)发送端的发送速度过快导致接收端还没处理好上一次数据的情况下,本次的数据已经来了,导致本次的数据丢失,造成数据不完整。
b)接收端处理数据的效率过低,导致本次数据来了,但是还在处理上一次的数据而导致数据丢失。
针对上述两个原因给出如下做法建议:
a)如果音视频传输的延时性要求不高的话可以适当降低每次发送数据的频率来缓解数据丢失的问题。
b)如果音视频传输的延时性要求较高时,以本项目为例:要求为500ms以内,此时根本不可能说需要降低发送端的发送频率的,应该需要发送端尽快处理完数据后马上发送才是合理的;因此此时应该需要做的是尽可能的提高接收端接收数据后的处理效率,尽可能做到接收完数据后马上能够进行下一次等待接收数据的状态,以此保证能够尽可能减少丢失数据,本项目的做法接收完数据后在最短的时间拼接完数据后,数据插入解码链表中,然后马上转到接收数据的状态。
==>尽管我们此时已经将接收数据和接收数据后的处理效率提高到极限了,但此时还是很有可能存在数据丢失的问题,因为对应不同的硬件cpu处理的效率是不一样的,有的cpu性能好 点,它就处理的快点,而不会出现花屏情况;但是有些性能没那么好的cpu在代码最优的情况下时效率仍无法满足实际的需求时,就会出现花屏的情况,针对这个问题,也是有解决方法的,如下将讲述。
c)针对不同cpu处理效率不同,还存在的花屏的情况的话,可以想清楚一个问题,究竟为什么使用udp传输时存在丢失数据呢?
==>原因是udp接收端会有自己的一个数据缓存区的,这个缓存区的容量如果我们不设置会有一个默认值的,对于我的windows的系统而言,默认的udp接收缓冲区的大小为64K字节,在使用 后setsockopt()函数将udp接收缓冲区的大小设置为1024K字节大小后,查询到的是1048576字节即1024K,证明设置成功了。
//设置该套接字为广播类型,
bool bOpt = true;
setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char*)&bOpt, sizeof(bOpt));
//获取udp默认缓存区大小
int optVal = 0;
int optLen = sizeof(optVal);
//optoptLen_len = sizeof(optVal);
getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&optVal, &optLen);
printf("SockOpt Sendbuff BeforeSet Value:%d, optLen:%d\n", optVal, optLen);
//设置udp接收端缓存区大小
int nRecvBuf = 1024 * 1024;//设置为500K
ret = setsockopt(s, SOL_SOCKET, SO_RCVBUF, (const char*)&nRecvBuf, sizeof(int));
if (ret != 0)
{
printf("setsockopt ret:%d\n", ret);
}
//设置后的查询
getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char*)&optVal, &optLen);
printf("SockOpt Sendbuff BeforeSet Value:%d, optLen:%d\n", optVal, optLen);
图1.1 udp设置缓存区大小前后情况
d)如下图1.1为所示缓存区没有增大时使用cpu性能较低的电脑接收到的图像,由于存在数据帧数据不完整情况导致解码失败而导致花屏现象出现,而对于图1.2所示的为将udp缓存区大小增大后图像显示的情况,无花屏的现象。
综上所述在对于cpu性能有差异的情况,基于udp分片传输数据时可以使用在尽量降低接收端处理效率的情况下,进一步将udp接收缓存区的大小增大,能够对花屏现象有缓解作用。
图1.2没有增大缓存时的接收端图像及解码情况
图1.3为增大了udp缓存后的接收端图像的显示情况
四、本文总结后续的期望
本文通过增大udp缓存后能够有效解决花屏的问题,但是解决这类问题的方法应该会有其他更有效的方式,例如将在未来的日子里进一步验证的方法:
(1)丢包重传
(2)使用硬件解码和渲染降低cpu然后提升处理数据速度等。
如遇到技术问题也可以私聊我一块讨论学习。
每写一篇文章都不容易,尊重别人的知识产权才是对自己和技术的尊重。为了避免发生知识产权被侵权的情况,我决定做出以下声明:
1.博客中标注原创的文章,版权归原作者 吴豪乐工作室 所有;
2.未经原作者允许不得转载本文内容,否则将视为侵权;
3.转载或者引用本文内容请注明来源及原作者;
4.对于不遵守此声明或者其他违法使用本文内容者,本人依法保留追究权等。