by fanxiushu 2019-12-06 转载或引用请注明原始作者。

xdisp_virt项目到目前为止,持续了两年多时间,几乎都是在windows平台下的实现各种功能,
因为持续时间比较长,能想到的功能都给添加到xdisp_virt中了,
尤其在windows截屏这部分,为了更好的截取windows桌面屏幕数据,能想到的都想办法实现了。
为了支持全屏3D游戏,添加了DXHOOK动态库,为了更好的控制鼠标键盘,添加了虚拟HID鼠标键盘驱动。
为了实现扩展显示器效果,也想法实现了Indirect Display驱动。
在对图像编码压缩方面,也是变着法的利用各种编码算法,什么H264,H264硬编码,H265,MPEG1/2/4, JPEG,VP8/9,
几乎是现有的通用各种编码算法都给实现了一个遍。
网络通讯方面也是,不单可以原生客户端可以链接进控制,还能在WEB页面上进行远程控制,通讯也添加了 (SSL) https 加密传输。
还专门开发了中转服务器解决通过公网访问处于局域网的机器。
各种复杂配置也直接简单的在WEB页面上进行配置,不再开发专门的配置界面程序。
而且还实现了 RTMP,RTSP推流,把桌面图像推流到 直播服务器。
不单截屏桌面数据,还能处理摄像头数据,还能处理电脑内部声音,以及麦克风声音,也能多路音频混音。
想想这两年的”战绩“确实有点多。。。
可是还算不够,最近心血来潮,想把xdisp_virt移植到 iOS,macOS和CentOS平台。
其实CentOS(linux)平台的桌面使用者不多,移植到linux没多大必要,
但是 iOS和macOS使用者还是比较多,尤其是iOS手机。
他们都属于UNIX类系统,只要移植到一个系统,很容易就能在别的系统上编译运行,因此这里也就统一解决了。
这里单单没有Android系统,不是我不想移植,只是因为手上暂时没有Android系统的设备,没法弄。

有兴趣可以直接到GITHUB上下载xdisp_virt试玩。
https://github.com/fanxiushu/xdisp_virt
(程序并未开放源代码,这可能会让人不爽。)

在 文章 “ Windows远程桌面实现之六(新版本框架更新,以及网页HTML5音频采集通讯)”
https://blog.csdn.net/fanxiushu/article/details/81905680
就曾简单介绍过xdisp_virt的网络通讯基本框架。
当时只考虑的是windows平台,采用的最高效的完成端口来作为底层基础通讯框架。
这次要移植,首先要解决的就是这个基础通讯框架。
程序所有通讯都是通过这个底层框架来完成的,比如程序内部集成的简单Web服务器,链接到中转服务器,
在此框架上实现的HTTPS加密传输等等,都是这个底层通讯框架的功劳。
(RTSP,RTMP 除外,这个是ffmpeg内部的通讯协议)
这个框架当时开发的时候,完全采用的是回调函数方式的异步通信的,什么意思呢?
当接受到指定长度数据包的时候,对应的回调函数会被调用,在这个回调函数中处理接收到的数据。
同样的,函数发送数据包也是立即返回,到数据包真正发送成功或者失败,对应的回调函数会被调用。
这种函数方式,在windows平台下的完成端口模型,是比较容易实现的
(当然,也并不是那么容易,这个得自己多去实践,)。
现在要移植到UNIX类平台下,linux有epoll,但是macos、ios对应的是kqueue,不是epoll,
我可不想在linux实现一套,又在macos,ios中另外实现一套,因此选择大家都通用的 select/poll 通讯模型,包括windows也能用。
根据select/poll模型的特点, 在 recv接收到数据时候,调用回调函数是容易实现的,但是 send发送数据却比较麻烦了。
windows平台完成端口模型中,调用WSASend异步发送数据,函数立即返回,数据发送完成后调用GetStatus函数就能获取到完成通知。
而select中,首先select来确定某个socket是否可以发送数据,
确定可以发送之后再调用 send发送数据,并且通常设置socket为非阻塞,防止send时候阻塞。
这与完成端口模型完全就是两个世界,而现在必须让select函数模拟这种send async方式:
send_async函数发送数据,并且立即返回 -> 数据发送完成,callback回调函数被调用。
其实这种方式也是可以模拟实现的,给每个socket套接字关联一个发送队列,send_async的时候,把数据包投递到这个发送队列。
然后采用某种通知机制通知socket有数据包需要发送,这时候send_async立即返回。
现在关键是如何通知这个socket,让它立即开发送数据。
根据select模型, select 的线程中往往有许多socket在 select中等待中,每个socket通常是只加到select的读事件中,
只有当有数据需要写的时候,才把socket添加到写事件中,否则每个socket添加select写事件,会造成select立即返回,白白浪费CPU。
假设添加到select中的所有socket 套接字都没有数据接收到,但是上面的send_async投递了一个数据包给某个socket。
因为select没有返回,这个数据包不能及时发送出去。这个时候,就必须采用某个办法,在send_async投递数据包之后,让select立即苏醒。
这样才能让这个数据包能立即被发送出去。
在这里也没找到更好更通用的办法,于是就给每个select线程创建一个UDP套接字,并且绑定到127.0.0.1,
select也把这个UDP套接字添加到读事件中, send_async在把数据包投递到发送队列之后,给这个UDP发送一个数据包。
这样 select就能立即苏醒。 这也是没办法中想到的一种办法。
不过总算是成功模拟了 异步发送行为。

在实现了跨平台的异步底层通讯框架之后,应该可以大刀阔斧的移植代码了。
可是开始之前,还有个麻烦要解决,那就是各种开源库的编译问题。
xdisp_virt使用了非常多的开源库,大致算下来,有十多种。大致:
ffmpeg, fdk-aac,x264, openh264, x265,turbojpeg,libyuv,unqlite,openssl,libvpx,jbig2,lzma,uuid
这些开源库在 linux平台和macOS平台都比较好编译,
通常都是 直接运行 configure, 然后make就可以了,使用cmake也差不多类似,按照每个开源库的编译说明,
这样直接使用系统自带的gcc编译就可以了。
比较麻烦的是 iOS的交叉编译。
但是熟悉嵌入式开发环境,以及经常编译这些嵌入式系统的人来说,也不算是个大麻烦。
编译这些开源库到iOS系统,只要不是非必要,不要使用Xcode开发环境来编译,而是直接采用命令行方式。
macOS系统中,C,C++,ObjC的编译器早就已经替换成 clang,
我们在终端敲入gcc也能运行,其实macos系把gcc作为 clang的一个软连接而已。
编译iOS的开源库,其实也是使用同样的clang,只是编译的时候,使用的编译参数不同而已。
这个与linux以及对应的linux嵌入式平台不同(Android也可以理解成其中一个linux嵌入式系统),
这些嵌入式系统往往需要下载自己的编译程序。
毕竟 macOS和iOS都是Apple自己的产品,完全可以把这些功能集成到一起。

现在我们就来处理这些参数问题。
网上也有现成的编译ffmpeg,x264这些库的编译脚本,如果只是其中一两个,使用脚本到无所谓,这里的开源库太多。
如果每个都去找脚本编译,那得累晕,再说有些开源库还没编译脚本下载,而且编译脚本往往把编译搞复杂了。
clang两个参数非常重要, -arch 直到编译成什么CPU芯片,因为现在iPhone手机基本都是arm64的,所以直接指定 -arch arm64 就可以。
还有一个 就是 -isysroot 这个是 编译的iOS 环境的SDK库的路径。 这个路径通常固定为:
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS{版本号}.sdk
基本上,只要指定这两个参数,使用clang编译的程序或者dylib库,就能在iOS上运行。
编译一些开源库,还得设置一些环境变量,最常用的就是 CC,CPP,CXXCPP,CFLAGS, CXXFLAGS,CPPFLAGS
这些环境变量的具体意思可以查阅网上的介绍。
在macOS系统中,打开terminal终端,
比如导出CC环境变量:
export CC="xcrun -sdk iphoneos clang"     //导出 C编译器
export CPP = "xcrun -sdk iphoneos clang -E" 导出 C预编译器
export CFLAGS="-arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS{版本号}.sdk     其他编译参数"  // 导出 C编译参数
.....
这样,预备工作就做好了,然后就是查看每个开源库对应的编译说明,这里以ffmpeg为例:
./configure \
--enable-static \
--enable-cross-compile \
--cc="xcrun -sdk iphoneos clang" \
--arch="arm64" \
--target-os=darwin
...其他参数

这样生成之后,然后直接 make,就能编译出iOS的ffmpeg静态库。其他开源库的编译这里不再罗嗦,都差不多。
是不是感觉比起 脚本方便多了,如果需要在iPhone模拟器中运行,对应的 arch 为 x86_64, 再把arm64和x86 用lipo打包在一起。
因为我直接使用真机调试开发,所以懒得再去折腾 x86_64的模拟器编译。

编译完全部的开源库,就可以开始移植xdisp_virt项目了,
xdisp_virt项目里边的源代码较多,还好当初开发的时候,虽然只想到了windows平台,但是使用的C、C++代码几乎都是跨平台的,
很少使用windows的自己框架,比如MFC,ATL在xdisp_virt项目里找不到影子。
即使跟平台相关的桌面图像采集,摄像头采集,音频采集。最终都实现成一个一个的标准C接口函数。

比如音频采集就简单浓缩成如下三个函数:

void* audio_capture_create(int is_capture_self, int audio_capture_index, audio_format_t* fmt, int* p_sleep_msec);
//创建音频采集接口, 其中audio_capture_index指定使用哪个音频设备,规定 0 表示采集电脑内部声音,从1开始是采集话筒声音。

void audio_capture_destroy(void* handle); //销毁采集接口

int audio_capture_capture(void* handle, unsigned char* buffer, int length); // 获取音频数据,

简单的说,在移植的时候,在macos,iOS,或者linux等平台,只要先写上面三个占位函数,有关音频编码,传输等都能编译。
之后把全部代码编译成功之后,再来具体实现跟这些平台相关的采集函数。

采用这种思路,很快就把macOS和linux系统平台下跟跟数据采集不相关的代码全部编译成功,留下20多个占位函数,
需要实现桌面采集,鼠标键盘模拟,摄像头采集,声音采集等这些跟具体系统相关的函数。
而且为了验证整个xdisp_virt工程除是否正常运行,在这些采集函数中随机模拟一些数据,看看运行起来是否正常。
目前基本都正常,还差实现这些具体的采集函数了。

linux和macOS平台都可以直接在命令行中编译,即使以后要编译跟系统相关的采集函数,我想也能在命令行中编译。
现在iOS需要创建Xcode工程,否则没法推送到iPhone上去调试运行。
为了尽量简单,在iOS编译中,我把已经编译的xdisp_virt导出一个xdisp_virt.dylib动态库,
并且跟windows,macos,linux一样的做法,
所有的开源库都静态编译进dylib中,这样,其实只要在iOS的Xcode工程中集中实现 对应的采集函数就可以了。

xdisp_virt.dylib导出
void* xdisp_virt_start(const char* conf_path, struct unix_capture_funcs_addr* addr); 函数,

struct unix_capture_funcs_addr结构就是全部的采集函数地址,
struct unix_capture_funcs_addr
{
    ///
    int(*fn_get_unix_screen_size)(int* cx, int *cy, int* bitcount);
    unsigned char* (*fn_get_unix_screen_data)(int cx, int cy, int bitcount);
    int(*fn_get_cursor_pos)(int* x, int* y);
   
    audio capture
    void* (*fn_audio_capture_create)(int is_capture_self, int audio_capture_index, struct audio_format_t* fmt, int* p_sleep_msec);
    void(*fn_audio_capture_destroy)(void* handle);
    int(*fn_audio_capture_capture)(void* handle, unsigned char* buffer, int length);

。。。。。
};

在macOS和iOS系统中,系统的采集函数基本都是 obj-c 的,而我们的xdisp_virt项目基本都是  C/C++的,
为了 又要使用c++,又要使用obj-c,直接使用 mm 后缀的文件名,实现 c,c++,objc混编,这也是感觉挺好玩的。
在Android中,要使用c++,还得实现java对应的jni接口,麻烦的要命。

文章未完待续,以后注意讲述如何在macos,ios,linux系统中实现跟采集相关的内容。

下图是移植xdisp_virt之后,首先在iOS系统实现的摄像头采集效果,
其实当时心血来潮,一个重要原因是发现iphone11Pro的浴霸摄像头拍照太清晰了,而电脑中的摄像头差的太远了。
本来是打算单独实现iPhone实现摄像头采集,后来想还是打算干脆移植 整个xdisp_virt工程好了。

图中,chrome浏览器显示的网页方式展现的摄像头图像,VLC播放器播放的是xdisp_virt 的 RTMP推流的摄像头图像。
图像很清晰吧,而且还是晚上,采光也很差。

Windows远程桌面实现之十 - 把xdisp_virt项目移植到iOS,macOS,linux平台(一)相关推荐

  1. [网络安全自学篇] 四十四.Windows远程桌面服务缺陷(CVE-2019-0708)复现及防御详解

    这是作者的网络安全自学教程系列,主要是关于安全工具和实践操作的在线笔记,特分享出来与博友们学习,希望您们喜欢,一起进步.前文分享了木马原理知识,并通过远程服务器IPC $ 漏洞实现木马植入及控制远程服 ...

  2. Windows远程桌面实现之六(新版本框架更新,以及网页HTML5音频采集通讯)

    by fanxiushu 2018-08-21 转载或引用请注明原始作者. 到目前为止,包括本文发布了六个系列,能坚持到现在也属不易. 第一篇: https://blog.csdn.net/fanxi ...

  3. 使用frp内网穿透,用windows远程桌面连接

    teamview 太黑心,莫名其妙检测商用劝退,实在是不得不脱坑.与其每个月给几百块RMB不如花几十块钱买台云服务.自己组建远程连接环境,使用windows远程桌面在公司连家里的电脑,安全无后门. 此 ...

  4. Windows远程桌面实现之十三:浏览器客户端使用WebRTC传输,以及WebRTC和MSE渲染显示(二)

    by fanxiushu 2022-03-12 转载或引用请注明原作者. 接上文. 我们先来编译kvswebrtc开源代码. 首先得从github下载 ksvwebrtc源码, 分别需要  amazo ...

  5. Windows远程桌面实现之十一:桌面屏幕通过各种直播服务端直播(RTSP, RTMP, HTTP-FLV, HLS)

    by fanxiushu 2020-01-23 转载或引用请注明原始作者. 此文还是基于xdisp_virt远程项目中的一个子功能.在把xdisp_virt移植到各种平台之后,就想着再做点什么新功能, ...

  6. 推荐三个Windows远程桌面客户端,mRemote、TSMMC.MSC、Terminals

    推荐非常好用的三个WINDOWS远程桌面客户端,mRemote.TSMMC.MSC.Terminals WINDOWS下大多都是直接用远程桌面进行远程管理,服务器稍微一多,有那十来多台以上的服务器,还 ...

  7. [Windows]如何查看和修改Windows远程桌面端口

    [Windows]如何查看和修改Windows远程桌面端口 查看Windows远程桌面端口 选择 开始 > 运行,输入 cmd,打开命令行窗口:执行: tasklist /svc | find ...

  8. 修改Windows远程桌面端口

    一.远程桌面是网络管理员最常用的工具之一,尤其是外网访问时非常的方便:但是其默认的3389端口容易受到攻击, 笔者一个客户的服务器就曾因为没有更改默认端口而遭到勒索病毒的攻击,幸亏提前在其它设备做了备 ...

  9. TeamViewer 替代品:使用 frp 实现 Windows 远程桌面连接教程

    最近在家办公,TeamViewer 又双叒叕认为我商用了,试了下 AnyDesk 替代,但是速度真的是远不如前者,所以干脆自己搭一个 frp 反向代理来当作 Windows 远程桌面,亲测速度还不错, ...

最新文章

  1. MinkowskiEngine多GPU训练
  2. 如何写好一篇科技论文?以Wiley科技刊为例(附视频)
  3. Boost.SmartPtr 的快速 (CI) 测试
  4. POSTMAN list参数传值
  5. Bloom Filter的基本原理和变种
  6. 你的灯还亮着吗阅读笔记之二
  7. linux 卸载opera,Ubuntu中安装Opera 55 浏览器
  8. ArcGIS 各版本产品补丁荟萃
  9. 最新淘汰服务器cpu,2019 最新 至强 Xeon E3服务器系列 CPU天梯图
  10. 公摊面积用计算机怎么计算,公摊面积计算(公摊面积计算器)
  11. Hbuilder X npx browserslist@latest --update-db
  12. 程序员值得关注的微信公众号
  13. Android 蓝牙 -- 还原网络设置 删除蓝牙所有存储配对信息流程分析---全网唯一
  14. 以影像技术为“桨“,荣耀如何讲好高端“新故事”?
  15. 思维导图(一):高效的思维工具
  16. java中感叹号啥意思_感叹号暗示什么意思
  17. JPMML调用PMML机器学习模型零基础总结(内含新版本jpmml解决方法)
  18. HP-UNIX上安装磁带库
  19. 趣拿洞察:盒马上海实现盈利背后的“玄机”
  20. 6.17.用100元人民币兑换10元,5元和一元的纸币共50张,请用穷举法编程计算共有几种兑换方法,每种方法各兑换多少张纸币。

热门文章

  1. 李瑞霖:4.21黄金诱多多单被套怎么办?黄金白银操作建议解套
  2. exp-00003 错误
  3. 为什么现在的人越来越不怕老板了? 老板和员工都要看
  4. 6.前端CSS之布局属性(盒子,float,overflow,opsition,z-index,opacity)
  5. android动态分区AB升级,Android A/B 系统升级简介
  6. 计算机网络:数据链路层:有线和无线网络(4)
  7. springboot集成Lean Cloud 及时通讯
  8. Hadoop生态系统的元数据管理和数据治理平台--Atlas 学习
  9. 改系统注册表 实现Windows XP自动登录
  10. 浪漫烟花——C/C++