windows下实现屏幕分享(C#)
采用UDP广播进行数据的传输,实现windows下进行低延迟的屏幕共享。
开发语言:C#
第三方组件:Redis
1.实现思路
总体流程图
DGIS.DesktopShare实现windows下屏幕分享低延迟功能,按照服务执行位置由三部分构成:发起端、接收端、缓存端。
通过UDP广播实现发起端和接收端的通讯,是为了尽量的减少通讯负载和降低延迟。众所周知UDP是所有通讯协议中延迟最低的(但也有受网络因素丢包的问题,这里作为局域网同屏,暂不考虑丢包问题),而采用广播的方式可以有效的降低发起端的性能负担。
增加一个Redis服务,是为了减少UDP广播数据,按照1920*1080分辨率的截屏数据来算,单张图片已经超过了UDP单包的最大数据量1472字节,倘若直接使用UDP传输截屏图片,需要额外的进行封包拆包,这样不仅浪费了程序执行时间,也增加了发送端和接收端的代码复杂度。本着最低延迟的目的,将真实的图片数据存入Redis缓存,只通过UDP广播Redis中对应的UID信息即可,这也是本程序最核心的地方。接收端接收到UID的数据后,再自行去获取Redis中真实的数据进行解析。
2.代码结构
DGIS.DesktopShare.Service的代码结构分为Frm(窗体)、IService(接口)、Service(实现)三个部分。引用的第三方dll有Redis相关操作库、屏幕获取相关库和DGIS开头的辅助操作库。
Frm:
ImgDisplyFrm是接收端的默认显示界面,包含一个PictureBox控件,显示接收到的屏幕图像。
IService:
IDesktopShareService屏幕共享操作接口,主要方法有4个。
注释的已经很明确了,这里不多说。
Service:
DesktopShareService:IDesktopShareService的实现类,里面是详细的实现方法。核心的代码有下面几个地方:
1 public void Start(int frameRate, bool mouseDisplay, int port) 2 { 3 _udpService = new UdpBroadcastService(port, 4 bytes => 5 { 6 //deskShareFrm.ShowImg(bytes); 7 }); 8 9 _desktopCapturer = CapturerFactory.CreateDesktopCapturer(frameRate, mouseDisplay); 10 _desktopCapturer.ImageCaptured += ImageCaptured; 11 _desktopCapturer.Start(); 12 13 //队列循环处理积压的图片 14 //Task.Factory.StartNew(() => 15 //{ 16 // while (true) 17 // { 18 // if (_bitmapQueue.Count > 0) 19 // { 20 // Bitmap bitmap = _bitmapQueue.Dequeue(); 21 // SendCommand(bitmap); 22 // } 23 24 // } 25 //}); 26 }
开始屏幕分享,_udpService 初始化,屏幕抓取设置以及开启屏幕抓取。
1 /// <summary> 2 /// 屏幕截屏结果 3 /// </summary> 4 /// <param name="bitmap"></param> 5 private void ImageCaptured(Bitmap bitmap) 6 { 7 //_bitmapQueue.Enqueue(bitmap); 8 SendCommand(bitmap); 9 }
1 /// <summary> 2 /// 发送命令 3 /// </summary> 4 /// <param name="bitmap"></param> 5 private void SendCommand(Bitmap bitmap) 6 { 7 //Bitmap newBitmap = ESBasic.Helpers.ImageHelper.RoundSizeByNumber(bitmap, 4); 8 //bitmap.Dispose(); 9 //bitmap = null; 10 11 string uid = Guid.NewGuid().ToString(); 12 Console.WriteLine(string.Format("图片获取时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); 13 14 byte[] bytes = ImgConvert.ConvertByte(bitmap); 15 Console.WriteLine(string.Format("缓存转换时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); 16 bitmap.Dispose(); 17 bitmap = null; 18 19 _redisCacheHelper.Add<byte[]>(uid, bytes, TimeSpan.FromMilliseconds(5000)); 20 Console.WriteLine(string.Format("缓存插入时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); 21 22 byte[] sendBytes = StringToBytes(uid); 23 _udpService.Send(sendBytes, sendBytes.Length); 24 Console.WriteLine(string.Format("图片发送时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); 25 26 sendBytes = null; 27 bytes = null; 28 }
抓取到屏幕图片的结果是Bitmap,这里需要将Bitmap转换为byte[],因为在测试中Redis直接存入Bitmap取出时候有些异常。待Redis存入图片数据后,再通过UDP发送图片数据。来看下测试数据,图片获取到发送的耗时情况。
主要的耗时在图片转换这个地方,耗时约10毫秒,redis存入时间是很快的6毫秒左右,UDP发送时间基本为0.
1 public void Recive(int port) 2 { 3 _imgDisplyFrm = new ImgDisplyFrm(); 4 _imgDisplyFrm.Closed += (e, o) => 5 { 6 if (_udpService != null) 7 { 8 _udpService.Dispose(); 9 _udpService = null; 10 } 11 }; 12 13 _udpService = new UdpBroadcastService(port, 14 bytes => 15 { 16 ShowImg(bytes); 17 }); 18 _imgDisplyFrm.ShowDialog(); 19 }
1 /// <summary> 2 /// 显示图像 3 /// </summary> 4 /// <param name="reciveBytes"></param> 5 private void ShowImg(byte[] reciveBytes) 6 { 7 Bitmap bitmap = ParseImg(reciveBytes); 8 9 if (_imgDisplyFrm != null) 10 _imgDisplyFrm.ShowImg(bitmap); 11 }
1 /// <summary> 2 /// 解析图片 3 /// </summary> 4 /// <param name="bytes"></param> 5 /// <returns></returns> 6 private Bitmap ParseImg(byte[] bytes) 7 { 8 string uid = BytesToString(bytes); 9 Console.WriteLine(string.Format("消息接收时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); 10 11 byte[] imgBytes = _redisCacheHelper.Get<byte[]>(uid); 12 Console.WriteLine(string.Format("缓存获取时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); 13 14 if (imgBytes != null) 15 { 16 Bitmap bitmap = ImgConvert.ConvertImg(imgBytes); 17 Console.WriteLine(string.Format("转码完成时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); 18 19 return bitmap; 20 } 21 else 22 return null; 23 }
接收端同样也是开启一个_udpService ,初始化,进行消息监听,接收到消息后进入ShowImg(显示图像)方法,中间主要代码是ParseImg(图片获取解析)这部分,这部分实现将真实图片数据从redis中获取。看下耗时情况。
这里能看到,消息的发送和接收之间耗时约为0(这里考虑本机没有经过网卡,实际应该有100毫秒左右延迟),redis缓存获取时间非常的快4毫秒左右,转码耗时30毫秒。
以上综合计算,从屏幕采集到终端接收解析总共耗时50毫秒左右,加上UDP广播理论上的100毫秒延迟,150毫秒是能够达到的。
下图是真实环境中两台电脑的屏幕分享延迟图片。
从截图上看,延迟在170毫秒,由此看出这种方案是比较可行的。
放一张测试效果图。
3.源码
源码放到了CSDN上,博客园不知道如何上传附件。万恶的CSDN居然至少需要设置2分了。
GIS.DesktopShare 下载地址:http://download.csdn.net/download/jiangfei200809/10229866
要是没有分的请移步去百度网盘下载吧,载地址:https://pan.baidu.com/s/1i5XlLnZ
代码开源地址:https://gitee.com/JFly/DGIS.BigEye.git
2018.1.30更新:增加了声卡声音采集同步传输
时间仓促,代码和文章都写的不好,只希望这种思路的提出能为有这方面需求的同行提供点参考。
更多个人工作中的项目请访问我的个人网站:www.88gis.cn
转载于:https://www.cnblogs.com/tracyjfly/p/8377961.html
windows下实现屏幕分享(C#)相关推荐
- Windows 下 tail 查看日志命令工具分享
以前在公司时服务器上面可以实现tail 命令查看程序运行日志,感觉相当不错,上网查了下这些命令是linux 下的,还好有好心人开发了一个可以在Windows下的运行的小工具,来给分享一下: 概述:ta ...
- 学习分享:Windows下大型文件 CRC 校验
学习分享:Windows下cmd窗口生成MDx与SHAx校验和 一.在 windows 下打开 cmd.exe 窗口 方法一:操作步骤: 同时按下键盘的 win + R 两个键,进入运行窗口: 在输入 ...
- win centos php语法,linux(centos5.5)/windows下nginx开启phpinfo模式功能的配置方法分享
经过志文工作室测试有效的相关配置主要内容如下: location ~ \.php(.*)$ { fastcgi_pass unix:/tmp/php-cgi.sock; fastcgi_index ...
- 软件:推荐七款Windows下宝藏软件,值得收藏!
今天给大家推荐七款Windows下软件,每一个都值得拥有. 一.uTools 效率神器 一个可以帮你提高生产率的工具. uTools是一个极简.插件化.跨平台的现在桌面软件.通过选配丰富的插件,可以打 ...
- windows打开的winform当前焦点 是否是hwnd_推介几款 windows 下非常好用的工具
在下工具控一枚,平时会留意收集各种各样给我们生活生产带来便捷的工具,毕竟人生苦短:下面主要介绍一些我在 Windows 系统上发现的一些好用的工具,并且会一笔带过主要优点特点,具体详细用法可以搜一下, ...
- SVN+SSH协议工作方式全解析,以Sourceforge为例讲解如何在Windows下配置TortoiseSVN和Su
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 简单地说 ...
- 快捷键实现MAC或者WINDOWS下快速切换显示器输入源
目录 通过键盘实现mac或者Windows显示器输入信号源快捷键切换(电脑怎么切换屏幕的信号输入) 前言 一.通过显示器菜单按钮控制 二.通过快捷键设置 1.window系统 对ControlMyMo ...
- Windows下ppt放映获取不到窗口的问题
一.问题 在屏幕分享或者投屏,选择单个应用窗口进行分享或者投屏时,当我们选择了PPT的窗口,如果在分享的中途,点击了放映PPT,此时会新创建一个窗口,句柄HWND也会不一样,但是此时远端看到的还是我们 ...
- Windows下的150款免费软件
WINDOWS XP GOODIES Agent 可以编辑微软帮助中的精灵动画人物所说的话. Alt-Tab Replacement 使用这个Alt-Tab程序,不仅可看到对应于可供切换应用程序窗口的 ...
最新文章
- 新建域用户添加提示密码策略受限
- 青少年软件编程等级考试 python-中国电子学会、北大等推出青少年软件编程等级标准升级版...
- 学python买什么电脑-程序员,买了台破Apple电脑,用来学Python
- [收集]Visual C#中调用Windows API
- [YTU]_2354 (实现复数类中的加运算符重载【C++运算符重载】)
- java中String和char的区别
- Android各种dialog
- java中InvocationHandler 用于实现代理。
- sqlsever	创建一个通用分页查询
- python怎么打印路径,python 打印路径的几种结果
- ReentrantLock和AbstractQueuedSynchronizer的整体结构
- Python学习笔记之os模块
- 地壳中元素含量排名记忆口诀_【化学好好玩】用口诀帮你速记忆化学知识
- 南阳oj STL分类练习
- 男生拍照姿势大全,这样拍才帅
- 2021年危险化学品生产单位安全生产管理人员报名考试及危险化学品生产单位安全生产管理人员模拟考试
- android手机不能发短信,小米4手机收短信正常但无法发送短信怎么办?
- 实习笔记Day11(2022.8.19)
- Win11 PowerShell字体大小设置
- 智能语音外呼系统 OKCC没有电脑的坐席能不能开展工作
热门文章
- 计算机提示无法识别优盘,win7电脑无法识别u盘的四种解决方法
- 国内电子数据取证鉴定标准最新合集(2020版)
- Java银行卡归属地校验查询
- 20200215惠普(HP)星14(R5-3500U)在ubuntu20.04下启动通过dmesg打印的内核信息
- 阿里P7测试员晒工资条,看完狠狠扎我心
- Timed out waiting for transform from base_footprint to map to become available before running costm
- 关于pdf和其zip格式的导出
- ZigBee的无线通信与网络组建
- NFS共享存储服务介绍与案例详细配置过程
- JavaScript函数(看这个就可以了)