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端的动态截图如下。
4 原理图
原理.png5 投屏
投影屏幕,可以去传输图像也可以去传输视频,具体使用哪一种就去看你的需求。而投影图像又分为通过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的。