Python使用UDP实现720p视频传输的操作


Posted in Python onApril 24, 2021

1. 项目背景

视频传输: 在一台电脑上播放视频(捕捉摄像头画面),同局域网内另一台电脑上实时播放,尽量不卡顿。

先放最后的照片,和用gif展示一下视频效果。

Python使用UDP实现720p视频传输的操作

Python使用UDP实现720p视频传输的操作

传输视频可以采取图片或者流的形式,本文采取传输图片的形式,在1s之内显示多张图片从而形成连续的视频画面。

经费有限,所有实验均基于笔记本电脑。

使用的视频源是本机摄像头,以及进击的巨人720p资源。

2. 解决方案

1. 使用Python的Socket,使用opencv捕捉摄像头/视频的画面。

2. 原始的图片很大(720p的大小是1920*1080*3),整图就算压缩成jpg格式其大小也非常大。而UDP最大只能传输65535字节大小的数据区,故对图片进行分块,分块过后的数据压缩成jpg格式,并对图片分块数据进行编号。

3. 实验检测表明,本文实验环境发送端不需要使用发送队列,基本上新生成的帧很快就能被socket传输掉。

4. 接收端使用多线程接收,每个线程是一个socket,接收过后的数据存储于数据片池。

5. 接收端另开一个线程,用于反复从数据片池 读取数据片,根据数据片的编号更新幕布,这里幕布是专门用于图像显示的一个数组,其维度是720p(1920*1080*3)。更新过后的结果暂存于图片池

6. 主线程反复从图片池读取图片,并显示。

3. 实现细节

3.1 TCP/UDP的选择

为了实现低延迟,毫无疑问选取无连接的UDP传输。

3.2 图片分片算法

这里其实也谈不上什么算法,就是将图片水平分割。这种做法的好处在于,分割后图片的编号可以和区域一一对应。本文没有探索更为复杂的图片分片算法。

Python使用UDP实现720p视频传输的操作

经过处理,图片变为一个个分片,如下:

Python使用UDP实现720p视频传输的操作

Python使用UDP实现720p视频传输的操作

Python使用UDP实现720p视频传输的操作

Python使用UDP实现720p视频传输的操作

对上述图片进行编号,很显然可以编号0,1,2,3,对于任意分块(例如2)在图像数组中对应的区域是frame[2*piece_size:(2+1)*piece_size],其中piece_size表示一片数据的大小。

这种对应关系方便解压后的图像还原操作。

3.3 JPG压缩

这其实是个很小的技术点,因为使用的压缩算法都是现成的。但是值得一提的是,JPG的压缩率是真的高,在实验数据上实现了10-20倍的压缩率。

使用了多线程压缩,压缩完过后,更新对应的桶,这里的桶实际上就是数据片。

Python使用UDP实现720p视频传输的操作

由主线程Main Thread反复从桶里取数据片(t1),每取1片发送一次,然后再取下一片(t2),直到所有桶都被取了一次(例子中有10片)。

至此,一张图片的分片数据被全部取完,于是开始统计一些FPS相关信息。

3.4 接收队列

接收端开了10个线程用于异步socket接收数据片。

为了保证接收端产生丝滑的视频效果,使用接收队列是个不错的选择。本文使用了2个队列的设计。实现数据接收的二级缓冲。示意图如下:

Python使用UDP实现720p视频传输的操作

这样一来,视频效果明显丝滑了很多。

4. 遇到的坑及解决办法

4.1. Windows防火墙

巨坑,最好都关了。

Python使用UDP实现720p视频传输的操作

4.2. 路由器网络频段

同一台路由器的5G和2.4G频段有时候不能互相ping通,要确保两个电脑连接在同一频段上。

4.3. Wifi配置

如果上述设置都对了,但是还是ping不通。将wifi连接设置成专用网络,也许就能解决问题。

Python使用UDP实现720p视频传输的操作

4.4. 硬件瓶颈

个人PC的性能是较大瓶颈,尤其是单机测验的时候(本地两个终端,一个发送、一个接收),CPU使用率分分钟到100%。听某个技术大哥说要使用GPU压缩。

Python使用UDP实现720p视频传输的操作

用两台电脑,一台接收一台发送之后,效果要好很多。

4.5. OpenCV读取摄像头大坑

由于摄像头驱动的关系,在我的电脑上需要设置以下两个变量,才能成功启用外置的720p摄像头。

os.environ["OPENCV_VIDEOIO_DEBUG"] = "1"
	os.environ["OPENCV_VIDEOIO_PRIORITY_MSMF"] = "0"

即使如此,如果不做额外的设置,读出来的图片将是480p的(看起来很像是720p被压缩过后的)。所以如果要传输真·720p,还需要设置读出的图像大小,如下:

self.stream = cv2.VideoCapture(1) # 读取第一个外置摄像头
	self.stream.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)   # float
	self.stream.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)   # float

4.6. Socket卡顿

不知道是不是我写的有问题,感觉多线程的socket会争抢资源(发送和接收的线程间,对应5.1节功能),造成接收端的画面显示将变得卡顿。

5. 尚未Bug Free的功能

5.1 使用TCP回传帧率信息

为了计算网络时延,采取类似伽利略测光速的方法。从数据包打包之前,到对方收到数据包之后,再将这个数据回传到发送方。

这样就不存在两台机器时间差校准的问题。

该算法的大致流程如下图所示。

Python使用UDP实现720p视频传输的操作

Python使用UDP实现720p视频传输的操作

这种计算方式应该是自己的实验环境下比较准确的方法了。

时延信息的反馈不需要特别快(比如200-500ms发送一次),所以使用TCP技术

其实TCP和UDP在使用Python编程的时候代码差距可以说极小…

但是!!!

自己目前在实现信息回传的时候,会莫名卡顿起来。

接收端建立回传的socket之后,甚至还没传输数据,整个程序运行起来就变得非常卡顿,这个让我比较苦恼,目前正在找bug.

5.2 拥塞控制 (流量控制)的算法

这部分的思想是流量控制,感谢评论区指正。

5.1节如果一并回传接收端队列状态信息。如果接收端队列太满,说明来不及处理视频帧了,从而对发送端的发送速度进行控制,才是“拥塞控制”

这个本来是想着和5.1综合起来用的,已经写好了,但是还没能真正展现价值,设计是否合理也值得商榷。

控制的是发送端的发送频率,从而实现接收端的流畅播放

思想和TCP的拥塞控制一样慢增长,快下降。如果接收端的队列一直处于较空的状态,则表明还有一定的性能剩余,此时可以缓慢加快发送的频率;如果检测到接收端队列中数据较多,表明发送速度太快来不及显示,这时候就大幅下降发送的频率。

这个拥塞控制的算法基于几个假设:

1.网络情况良好,丢包率比较低;

2接收端电脑的性能足够高,来得及处理解包、显示图像。

如果5.1能够正确实现,则应该根据网络时延Python使用UDP实现720p视频传输的操作的大小来控制发送的频率。

6. 总结

这个项目是一周的时间内完成的,目前还有点bug。小组内的成员分别在不同技术方向上进行了探索,收获都还挺大的。这篇博客就当一个项目总结吧,写的难免有纰漏之处。

github地址:https://github.com/820fans/UDP-Video-Transfer

以上为个人经验,希望能给大家一个参考,也希望大家多多支持三水点靠木。如有错误或未考虑完全的地方,望不吝赐教。

Python 相关文章推荐
跟老齐学Python之集合的关系
Sep 24 Python
python脚本爬取字体文件的实现方法
Apr 29 Python
在python中使用正则表达式查找可嵌套字符串组
Oct 24 Python
Python中装饰器学习总结
Feb 10 Python
利用python修改json文件的value方法
Dec 31 Python
Python3.5 Pandas模块之DataFrame用法实例分析
Apr 23 Python
Python完成毫秒级抢淘宝大单功能
Jun 06 Python
ML神器:sklearn的快速使用及入门
Jul 11 Python
Python Opencv任意形状目标检测并绘制框图
Jul 23 Python
Python 装饰器@,对函数进行功能扩展操作示例【开闭原则】
Oct 17 Python
python多线程高级锁condition简单用法示例
Nov 07 Python
jupyter使用自动补全和切换默认浏览器的方法
Nov 18 Python
python通配符之glob模块的使用详解
Apr 24 #Python
Django debug为True时,css加载失败的解决方案
Apr 24 #Python
python 模块重载的五种方法
Apr 24 #Python
写一个Python脚本自动爬取Bilibili小视频
python实现图片批量压缩
Apr 24 #Python
如何用python绘制雷达图
两行代码解决Jupyter Notebook中文不能显示的问题
You might like
php自动注册登录验证机制实现代码
2011/12/20 PHP
PHP中SESSION使用中的一点经验总结
2012/03/30 PHP
PHP中使用json数据格式定义字面量对象的方法
2014/08/20 PHP
php实现可运算的验证码
2015/11/10 PHP
ThinkPHP框架里隐藏index.php
2016/04/12 PHP
Yii2学习笔记之汉化yii设置表单的描述(属性标签attributeLabels)
2017/02/07 PHP
[原创]php token使用与验证示例【测试可用】
2017/08/30 PHP
JS 分号引起的一段调试问题
2009/06/18 Javascript
jquery select(列表)的操作(取值/赋值)
2009/08/06 Javascript
jquery事件机制扩展插件 jquery鼠标右键事件
2011/12/21 Javascript
Vue.js数据绑定之data属性
2017/07/07 Javascript
Node.js Express安装与使用教程
2018/05/11 Javascript
使用JS实现导航切换时高亮显示的示例讲解
2018/08/22 Javascript
详解CommonJS和ES6模块循环加载处理的区别
2018/12/26 Javascript
AngularJS实现的鼠标拖动画矩形框示例【可兼容IE8】
2019/05/17 Javascript
mock.js模拟数据实现前后端分离
2019/07/24 Javascript
vue实现按钮切换图片
2021/01/20 Vue.js
Pycharm 安装 idea VIM插件的图文教程详解
2020/02/21 Python
Ubuntu 20.04安装Pycharm2020.2及锁定到任务栏的问题(小白级操作)
2020/10/29 Python
浅谈盘点5种基于Python生成的个性化语音方法
2021/02/05 Python
div或img图片高度随宽度自适应的方法
2020/02/06 HTML / CSS
La Senza官网:北美顶尖性感内衣品牌
2018/08/03 全球购物
Nebula美国官网:便携式投影仪
2019/03/15 全球购物
Elemental Herbology官网:英国美容品牌
2019/04/27 全球购物
英国健康和美容技术产品购物网站:CurrentBody
2019/07/17 全球购物
新西兰第一的行李箱网站:luggage.co.nz
2019/07/22 全球购物
EJB2和EJB3在架构上的不同点
2014/09/29 面试题
出生公证委托书
2014/04/03 职场文书
法制宣传标语集锦
2014/06/25 职场文书
防灾减灾标语
2014/10/07 职场文书
副检察长四风问题对照检查材料思想汇报
2014/10/07 职场文书
经典导游欢迎词
2015/01/26 职场文书
法学专业求职信范文
2015/03/19 职场文书
pandas求平均数和中位数的方法实例
2021/08/04 Python
MySQL磁盘碎片整理实例演示
2022/04/03 MySQL
从原生JavaScript到React深入理解
2022/07/23 Javascript