windows下实现屏幕分享(C#)

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
全局流量管理 GTM,标准版 1个月
简介: 采用UDP广播进行数据的传输,实现windows下进行低延迟的屏幕共享。 开发语言:C# 第三方组件:Redis   1.实现思路   总体流程图 DGIS.DesktopShare实现windows下屏幕分享低延迟功能,按照服务执行位置由三部分构成:发起端、接收端、缓存端。

采用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  /// 
2         /// 屏幕截屏结果
3         /// 
4         /// 
5 private void ImageCaptured(Bitmap bitmap) 6 { 7 // _bitmapQueue.Enqueue(bitmap); 8 SendCommand(bitmap); 9 }
 1  /// 
 2         /// 发送命令
 3         /// 
 4         /// 
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 /// 
 2         /// 显示图像
 3         /// 
 4         /// 
5 private void ShowImg( byte [] reciveBytes) 6 { 7 Bitmap bitmap = ParseImg(reciveBytes); 8 9 if (_imgDisplyFrm != null ) 10 _imgDisplyFrm.ShowImg(bitmap); 11 }
 1 /// 
 2         /// 解析图片
 3         /// 
 4         /// 
5 /// 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 下载地址: 

 要是没有分的请移步去百度网盘下载吧,载地址:https://pan.baidu.com/s/1i5XlLnZ

 

代码开源地址

2018.1.30更新:增加了声卡声音采集同步传输

 

时间仓促,代码和文章都写的不好,只希望这种思路的提出能为有这方面需求的同行提供点参考。

更多个人工作中的项目请访问我的个人网站:www.88gis.cn

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
消息中间件 安全 API
C#实现操作Windows窗口句柄:SendMessage/PostMessage发送系统消息、事件和数据【窗口句柄总结之二】
SendMessage/PostMessage API 可以实现发送系统消息,这些消息可以定义为常见的鼠标或键盘事件、数据的发送等各种系统操作......
5886 1
C#实现操作Windows窗口句柄:SendMessage/PostMessage发送系统消息、事件和数据【窗口句柄总结之二】
|
6天前
|
Linux C# iOS开发
开源GTKSystem.Windows.Forms框架让C# Winform支持跨平台运行
开源GTKSystem.Windows.Forms框架让C# Winform支持跨平台运行
41 12
|
2月前
|
数据库连接 数据库 C#
Windows下C# 通过ADO.NET方式连接南大通用GBase 8s数据库(上)
Windows下C# 通过ADO.NET方式连接南大通用GBase 8s数据库(上)
|
2月前
|
数据库连接 数据库 C#
Windows下C# 通过ADO.NET方式连接南大通用GBase 8s数据库(下)
本文接续前文,深入讲解了在Windows环境下使用C#和ADO.NET操作南大通用GBase 8s数据库的方法。通过Visual Studio 2022创建项目,添加GBase 8s的DLL引用,并提供了详细的C#代码示例,涵盖数据库连接、表的创建与修改、数据的增删查改等操作,旨在帮助开发者提高数据库管理效率。
|
5月前
|
XML 监控 C#
Windows平台C#版RTSP转RTMP直播推送定制版
前几年我们发布了C++版的多路RTMP/RTSP转RTMP转发官方定制版。在秉承低延迟、灵活稳定、低资源占用的前提下,客户无需关注开发细节,只需图形化配置转发等各类参数,实现产品快速上线目的。如监控类摄像机、NVR等,通过厂商说明或Onvif工具,获取拉流的RTSP地址,图形化配置,完成拉流转发等操作,轻松实现标准RTMP服务器对接。
|
5月前
|
开发者 iOS开发 C#
Uno Platform 入门超详细指南:从零开始教你打造兼容 Web、Windows、iOS 和 Android 的跨平台应用,轻松掌握 XAML 与 C# 开发技巧,快速上手示例代码助你迈出第一步
【8月更文挑战第31天】Uno Platform 是一个基于 Microsoft .NET 的开源框架,支持使用 C# 和 XAML 构建跨平台应用,适用于 Web(WebAssembly)、Windows、Linux、macOS、iOS 和 Android。它允许开发者共享几乎全部的业务逻辑和 UI 代码,同时保持原生性能。选择 Uno Platform 可以统一开发体验,减少代码重复,降低开发成本。安装时需先配置好 Visual Studio 或 Visual Studio for Mac,并通过 NuGet 或官网下载工具包。
466 0
|
消息中间件 监控 数据可视化
【时序数据库InfluxDB】Windows环境下配置InfluxDB+数据可视化,以及使用 C#进行简单操作的代码实例
influxDB的官网下载地址 https://portal.influxdata.com/downloads/打开以后,如下图所示,可以选择版本号,以及平台。此处咱们选择windows平台。不过此处没有实际的可以下载的地方,着实比较过分,不过咱们可以另辟蹊径。
1211 0
【时序数据库InfluxDB】Windows环境下配置InfluxDB+数据可视化,以及使用 C#进行简单操作的代码实例
|
API C# Windows
C#实现操作Windows窗口句柄:常用窗口句柄相关API、Winform中句柄属性和Process的MainWindowHandle问题【窗口句柄总结之三】
本篇主要介绍一些与窗口句柄相关的一些API,比如设置窗口状态、当前激活的窗口、窗口客户区的大小、鼠标位置、禁用控件等,以及介绍Winform中的句柄属性,便于直接获取控件或窗体句柄,以及不推荐...
3391 0
C#实现操作Windows窗口句柄:常用窗口句柄相关API、Winform中句柄属性和Process的MainWindowHandle问题【窗口句柄总结之三】
|
8月前
|
人工智能 机器人 C#
Windows编程课设(C#)——基于WPF和.net的即时通讯系统(仿微信)
一款参考QQ、微信的即时通讯软件。采用CS结构,客户端基于.Net与WPF开发,服务端使用Java开发。
|
API C# Windows
C#实现操作Windows窗口句柄:遍历、查找窗体和控件【窗口句柄最全总结之一】
C#对Windows窗口或窗口句柄的操作,都是通过 P/Invoke Win32 API 实现的,DllImport引入Windows API操作窗口(句柄),可以实现枚举已打开的窗口、向窗口...
4075 0
C#实现操作Windows窗口句柄:遍历、查找窗体和控件【窗口句柄最全总结之一】