采用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的实现类,里面是详细的实现方法。核心的代码有下面几个地方:

  public void Start(int frameRate, bool mouseDisplay, int port)
{
_udpService = new UdpBroadcastService(port,
bytes =>
{
//deskShareFrm.ShowImg(bytes);
}); _desktopCapturer = CapturerFactory.CreateDesktopCapturer(frameRate, mouseDisplay);
_desktopCapturer.ImageCaptured += ImageCaptured;
_desktopCapturer.Start(); //队列循环处理积压的图片
//Task.Factory.StartNew(() =>
//{
// while (true)
// {
// if (_bitmapQueue.Count > 0)
// {
// Bitmap bitmap = _bitmapQueue.Dequeue();
// SendCommand(bitmap);
// } // }
//});
}

开始屏幕分享,_udpService 初始化,屏幕抓取设置以及开启屏幕抓取。

  /// <summary>
/// 屏幕截屏结果
/// </summary>
/// <param name="bitmap"></param>
private void ImageCaptured(Bitmap bitmap)
{
//_bitmapQueue.Enqueue(bitmap);
SendCommand(bitmap);
}
  /// <summary>
/// 发送命令
/// </summary>
/// <param name="bitmap"></param>
private void SendCommand(Bitmap bitmap)
{
//Bitmap newBitmap = ESBasic.Helpers.ImageHelper.RoundSizeByNumber(bitmap, 4);
//bitmap.Dispose();
//bitmap = null; string uid = Guid.NewGuid().ToString();
Console.WriteLine(string.Format("图片获取时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); byte[] bytes = ImgConvert.ConvertByte(bitmap);
Console.WriteLine(string.Format("缓存转换时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid));
bitmap.Dispose();
bitmap = null; _redisCacheHelper.Add<byte[]>(uid, bytes, TimeSpan.FromMilliseconds());
Console.WriteLine(string.Format("缓存插入时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); byte[] sendBytes = StringToBytes(uid);
_udpService.Send(sendBytes, sendBytes.Length);
Console.WriteLine(string.Format("图片发送时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); sendBytes = null;
bytes = null;
}

抓取到屏幕图片的结果是Bitmap,这里需要将Bitmap转换为byte[],因为在测试中Redis直接存入Bitmap取出时候有些异常。待Redis存入图片数据后,再通过UDP发送图片数据。来看下测试数据,图片获取到发送的耗时情况。

主要的耗时在图片转换这个地方,耗时约10毫秒,redis存入时间是很快的6毫秒左右,UDP发送时间基本为0.

 public void Recive(int port)
{
_imgDisplyFrm = new ImgDisplyFrm();
_imgDisplyFrm.Closed += (e, o) =>
{
if (_udpService != null)
{
_udpService.Dispose();
_udpService = null;
}
}; _udpService = new UdpBroadcastService(port,
bytes =>
{
ShowImg(bytes);
});
_imgDisplyFrm.ShowDialog();
}
 /// <summary>
/// 显示图像
/// </summary>
/// <param name="reciveBytes"></param>
private void ShowImg(byte[] reciveBytes)
{
Bitmap bitmap = ParseImg(reciveBytes); if (_imgDisplyFrm != null)
_imgDisplyFrm.ShowImg(bitmap);
}
 /// <summary>
/// 解析图片
/// </summary>
/// <param name="bytes"></param>
/// <returns></returns>
private Bitmap ParseImg(byte[] bytes)
{
string uid = BytesToString(bytes);
Console.WriteLine(string.Format("消息接收时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); byte[] imgBytes = _redisCacheHelper.Get<byte[]>(uid);
Console.WriteLine(string.Format("缓存获取时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); if (imgBytes != null)
{
Bitmap bitmap = ImgConvert.ConvertImg(imgBytes);
Console.WriteLine(string.Format("转码完成时间:{0}-{1}", DateTime.Now.ToString("mm:ss:ffff"), uid)); return bitmap;
}
else
return null;
}

接收端同样也是开启一个_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

windows下实现屏幕分享(C#)的更多相关文章

  1. Windows 下 tail 查看日志命令工具分享

    以前在公司时服务器上面可以实现tail 命令查看程序运行日志,感觉相当不错,上网查了下这些命令是linux 下的,还好有好心人开发了一个可以在Windows下的运行的小工具,来给分享一下: 使用方法: ...

  2. WINDOWS下搭建SVN服务器端的步骤分享(Subversion)

    1.获取svn程序 2.安装 Subversion(以下简称SVN)的服务器端和客户端.下载下来的服务器端是个 zip压缩包,直接解压缩即可,比如我解压到 E:\subversion .客户端安装文件 ...

  3. [分享]windows下编译squid的经验(转)

    squid是什么我这里就不说了,这不是本文的重点,总之它是一个集:代理.加速.缓存.负载均衡.防盗链.访问控制等多功能的一个超牛X开源软件,如今已经广泛应用于很多领域.对于缓存和加速这一领域,如今各大 ...

  4. 在 Linux/windows下 命令行中使用和执行 PHP 代码[交互式php]

    [注释]在ubuntu下,升级php到7.1版本,虽然提示的是Interactive mode enabled, 但实际上可以直接书写命令,和interactive shell效果一样. 一:wind ...

  5. windows下的Mysql安装与基本使用(zip)

    一.安装过程 Mysql社区版下载地址:http://dev.mysql.com/downloads/mysql/ --注意,已管理员身份运行cmd.exe,很重要!!目录在,c:\windows\s ...

  6. 让 windows 下的命令行程序 cmd.exe 用起来更顺手

    在 Windows 下使用 Larave 框架做开发,从 Composer 到 artisan 总是避免不了和 cmd.exe 打交道,系统默认的命令行界面却是不怎么好看,且每行显示的字符数是做了限制 ...

  7. Windows下构建ASP.NET Core+Code First+Docker

    背景介绍 本文将会示范如何在Windows系统下基于ASP.NET Core构建跨平台服务,并通过Docker容器运行发布. 首先说一下为什么选择这一套组合: 我本人和我们Code4Thought团队 ...

  8. windows下nginx安装、配置与使用(转载)

    目前国内各大门户网站已经部署了Nginx,如新浪.网易.腾讯等:国内几个重要的视频分享网站也部署了Nginx,如六房间.酷6等.新近发现Nginx 技术在国内日趋火热,越来越多的网站开始部署Nginx ...

  9. 用脚本如何实现将Linux下的txt文件批量转化为Windows下的txt文件?

    众所周知,Windows和Linux的文件换行回车格式不同,Windows下换行格式是\r\n(回车+换行),Linux下换行格式为\n(只是换行),因此,其中一个操作系统的文本文件若需要在另外一个中 ...

随机推荐

  1. 让你大开眼界的10款Android界面设计

    根据调查显示, iOS与Android的市场份额差距正越来越大.Android设备正在成为手机应用市场的主力军.如何从设计层面创造一个优美的app界面来吸引用户已然成为广大App开发者们必做的功课之一 ...

  2. Mcrosoft中间语言的主要特征

    Mcrosoft中间语言显然在.NET FrameWork中起着非常重要的作用.现在讨论一下IL(Intermideate Language)的主要特征.因为面向.NET的所有语言在逻辑上都需要支持I ...

  3. HBase预分区方法

    (what)什么是预分区? HBase表在刚刚被创建时,只有1个分区(region),当一个region过大(达到hbase.hregion.max.filesize属性中定义的阈值,默认10GB)时 ...

  4. 3.1.2 condition 条件

    package 第三章.重入锁; import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Reent ...

  5. mvc中使用Hangfire处理后台任务

    考虑下如下代码,在数据保存后,需要发送邮件,发送邮件是个耗时的工作. 我们的目的是,数据保存成功后,就可以返回响应了,发送邮件不重要,不需要等待邮件发送成功 [HttpPost] public Act ...

  6. 编写高质量代码改善C#程序的157个建议——建议77: 正确停止线程

    建议77: 正确停止线程 开发者总尝试对自己的代码有更多的控制.例如,“让那个还在工作的线程马上停止下来”.然而,并非我们想怎样就可以怎样的,这至少涉及两个问题. 第一个问题 正如线程不能立即启动一样 ...

  7. APUE(4)---文件和目录 (2)

    七.函数umask umask函数为进程设置文件模式创建屏蔽字,并返回之前的值,这是少数几个没有出错返回函数中的一个.其中cmask是9个常量(S_IR/W/XUSR.S_IR/W/XGRP.S_IR ...

  8. 第几天——第九届蓝桥杯C语言B组(省赛)第一题

    原创 标题:第几天 2000年的1月1日,是那一年的第1天. 那么,2000年的5月4日,是那一年的第几天? 注意:需要提交的是一个整数,不要填写任何多余内容. 这题是送分题,只需要注意一下2000年 ...

  9. jsp乱码的问题

    大家在JSP的开发过程中,经常出现中文乱码的问题,可能一至困扰着大家,现把JSP开发中遇到的中文乱码的问题及解决办法写出来供大家参考.首先了解一下Java中文问题的由来: Java的内核和class文 ...

  10. 一个自定义MVP .net框架 AngelFrame

    摘要:本篇是本人在完成.net平台下一个项目时,对于MVP框架引发的一些思考,以及开发了一个小型的配置型框架,名字叫作AngelFrame.这个项目属于前端桌面管理系统的一部分,最终要集成进去. 关键 ...