Android屏幕投影及反向控制原理

这一周过的是够有意思的,先停两天电,然后感冒了,然后项目出Bug了,然后发烧了,呵呵哒,赶紧只能过来写点东西压压惊。鉴于最近正好在研究Android投屏及反像控制和Android双开的技术原理,本周就先写写Android投影以及反向控制的原理了。

1 目标

  • Android投影屏幕到电脑
  • 电脑端反向控制Android手机(如QQ,微信,淘宝…)

2 背景

最近在项目小组中遇到一件事,小组有时候需要演示demo供大家参考,当演示Android手机投屏时,就需要借助第三方软件进行投屏,比如说360手机管家的演示功能还有一个神器Vysor(通过Google浏览器投屏并控制手机),但是随之也会带来问题,通过反编译Vysor的Apk可以看到它是使用adb命令截屏然后通过Async网络库传输屏幕投影给后台,既然有网络操作,如果是公司比较重要的东西,万一第三方在后面偷偷保留了演示录屏(我相信这些应用应该都不会,有职业操守),然后可能就会有自己去做投屏的需求。

3 预览图

今天写的原理都是经过本人实现过的,目前PC端已经正常工作,并且可以投屏多台Android。Web端通过node.js websocket webrtc HTML实现的目前还在开发中,鉴于之前没怎么用过前端,所以写的比较慢。

目前测试实时投影在真实机上还可以。
PC端的动态截图如下。

《Android屏幕投影及反向控制原理》 时间.gif
《Android屏幕投影及反向控制原理》 p2
《Android屏幕投影及反向控制原理》 地图.gif

4 原理图

《Android屏幕投影及反向控制原理》 原理.png

5 投屏

投影屏幕,可以去传输图像也可以去传输视频,具体使用哪一种就去看你的需求。而投影图像又分为通过ADB命令去截取图像以及通过Android的ImageReader获取图像然后通过网络传输两种方式,所以投屏的实现是有很多种的,你想使用哪一种都是可以的。

图像流

现在的产品看到他们都是借助手机连线到电脑端的,通过adb直接去截取图片,这样的话就会很快,如果你只是在公司内部用,使用公司的局域网进行通信我觉得也已经够用了,因此也可以实现通过网络Socket直接去传输图像的字节码。但是现在手机分辨率可高了,因此如果你不对图像进行处理直接通过Socket传输的话那么会让PC端投屏变得很卡,因此AndroidClient可以先对图像进行压缩裁剪之后再去传输。

Android端这块我是开启了一个Service,然后通过ImageReader获取屏幕的图像,之后对图片进行裁剪压缩之后再利用Socket传输图像数据信息。其中的基本代码流程如下:

virtualDisplay = mediaProjection.createVirtualDisplay("MainScreen",width,height,dpi
                ,DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,imageReader.getSurface()
                ,null,screenHandler);
        imageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() {
            @Override
            public void onImageAvailable(ImageReader imageReader) {
                Log.i(TAG, "call onImageAvailable");
                try {
                    //如果有图片那么就获取
                    img = imageReader.acquireLatestImage();
                    if (img != null) {
                     //图像处理
                     //send 数据
                    }
            }
  }

视频流

起初我采用的就是图片传输,后来想想其实还是有其他方案的,其实可以通过获取Android手机的屏幕视频流通过H264进行编码进行传输给后台Server,这样可以让画面显示的更加流畅。Android Client里面有一个MediaCodec的类以及VirtualDisplay类可以去读取Android的屏幕流,然后转化为H264视频流。

Android端依然是开启一个Service去获取屏幕流,但是编码H264时会有一个坑,就是需要你去手动加入SPS和PPS,之后才是视频帧。此处的处理代码如下

//1,get SPS and PPS
MediaFormat outputFormat = codec.getOutputFormat();
ByteBuffer sps = outputFormat.getByteBuffer("csd-0");    // SPS
ByteBuffer pps outputFormat.getByteBuffer("csd-1");    // PPS
//2, change ByteBuffer to byte[]
...
//3, send byte[] to server by socket
...

6 反向控制

PC端去控制手机有如下两大块技术:

  • Android通过USB数据线或者Wifi连接打开ADB,通过本地执行ADB command
  • Android手机root掉,通过Android客户端执行ADB command

Adb连接方式有如下两种:

  • Usb数据线
  • Wifi: adb tcpip 5555, adb connect android_ip_address

PC应用程序:
Server端的代码主题逻辑不复杂:通过Socket接收Android 客户端传过来的图像数据信息解压缩显示到Ui上面,当用户点击UI时获取鼠标点击的坐标,通过比例换算转化成实际Android真机的坐标,之后通过ADB执行对应的Command命令,然后Android图像的变化再通过Socket实时传输给Server端记住坐标系变化不要忘记了,一开始我忘记了转化坐标结果显示就不对。

当然PC端也可以读取Android Client端的H264编码视频流,然后PC端使用FFMPEG这个库去解码,关于FFMPEG库的相关使用,我推荐大家去看看雷霄骅的技术博客,此人在音视频方面给予大家很大的帮助。

Web应用程序:
首先通过在node.js上通过socket获取Android Client端的H264视频流,然后通过WebSocket实时将字节数组传输给WebRtc,通过WebRtc的video标签去显示,题外话:WebRtc也是个好东西,你可以基于它去做很多有意思的东西比如网络视屏以及现在挺热的Android直播,程序员去多折腾折腾还是很有意思的。之后通过js获取鼠标点击的坐标事件,之后的操作和PC很相似了,都是得到命令然后执行,然后AndroidClient再投屏图像,如此循环。

这大概就是屏幕投影的原理了。并不复杂。关键你要有一颗折腾的心。最近准备把Web端的这块实现完了就来写一篇双开的原理实现。欢迎其他程序员一起入坑一起交流,不管你是学习Android的还是后端的还是前端开发的,都欢迎大家一起交流原理思想,一起学习,一起进步。
还有本人写博客并不多,所以语法表述之类的还尚待提高,而且今天写的时候烧还没退头一直很懵,还请见谅,欢迎大家提出其他的实现想法以及意见。
也喜欢你可以加入QQ群大家一起交流交流:94839608

这是我一开始写的最基本的Android屏幕投影以及反向控制代码,里面很多代码都是硬编码进去的,可能需要你们手动改一些IP或者通过Socket去传输屏幕宽高之类的,毕竟只是基本测试使用的。这是链接
http://download.csdn.net/detail/zhangkai1992/9809903
最近换了工作方向,我还是很喜欢Android的,不过不是我最喜欢的,不过我还是有空会去研究研究Android的。

    原文作者:kason_zhang
    原文地址: https://www.jianshu.com/p/279682621a01
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞