Android提供了MediaProjection来实现录屏,通过MediaProjection可以获取当前屏幕的视频流,而视频流需要通过编解码来压缩进行传输,通过MediaCodec可实现视频的编码和解码,这篇文章主要介绍了android设备间实现无线投屏,需要的朋友可以参考下。
前言
Android提供了MediaProjection来实现录屏,通过MediaProjection可以获取当前屏幕的视频流,而视频流需要通过编解码来压缩进行传输,通过MediaCodec可实现视频的编码和解码。视频流的推送和接收可通过Socket或WebSocket来实现,服务端推送编码的视频流,客户端接收视频流并进行解码,然后渲染在SurfaceView上即可显示服务端的画面。
投屏服务端的实现
服务端主入口负责请求录屏和启动录屏服务,服务端主入口的实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
|
Android8.0后录屏需要开启前台服务通知,录屏服务开启后同时启动WebSocketServer服务端,前台服务代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
|
SocketManager为WebSocketServer的控制类,并同时将录屏对象MediaProjection与视频编码器ScreenEncoder绑定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
|
推流服务端ScreenSocketServer代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
|
WebSocketServer启动成功会回调onStart方法。
1 2 3 4 |
|
当有客户端连接回调onOpen方法,通过回调的WebSocket对象即可向客户端发送数据。
1 2 3 4 5 |
|
视频编码器ScreenEncoder采用采用H.265编码(H.265/HEVC),需要注意不同手机支持的编码最大分辨率不同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
|
注意点:
1.需要依赖WebSocket相关jar。
implementation “org.java-websocket:Java-WebSocket:1.5.3”
2.AndroidManifest配置网络和前台服务权限。
1 2 |
|
3.配置前台录屏服务foregroundServiceType属性。
1 2 3 4 5 |
|
投屏客户端的实现
客户端入口负责创建SurfaceView并初始化WebSocket客户端管理器SocketClientManager。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
|
SocketClientManager将Surface与解码器绑定,创建WebSocket客户端并接收服务端推送的视频流。其中URI 需要修改为服务端的IP地址和端口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
|
ScreenSocketClient代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
|
不同手机支持的解码最大分辨率不同,需要设置正确的分辨率才不会报错。投屏解码器ScreenDecoder代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
|
注意点:
1.需要依赖WebSocket相关jar。
implementation “org.java-websocket:Java-WebSocket:1.5.3”
2.AndroidManifest配置网络权限。
1 |
|
效果
左手边为服务端,右手边为客户端,服务端投屏到客户端的效果如下:
遇到的错误
1.WebSocket出现Permission denied的报错:
onerror =java.net.SocketException: socket failed: EACCES (Permission denied)
问题原因:需要网络权限。
1 |
|
2.WebSocket出现Host unreachable报错:
onerror =java.net.NoRouteToHostException: Host unreachable
问题原因:不在同一局域网内或服务端未正常启动。
3.WebSocket出现failed to connect报错:
failed to connect to /192.168.1.3 (port 50000) from /:: (port 38262):,不在同一局域网内或服务端未正常启动
问题原因:不在同一局域网内或服务端未正常启动。
客户端连接成功回调onOpen方法
1 2 3 4 |
|
4.初始化MediaFormat时报IllegalStateException异常:
Caused by: java.lang.IllegalArgumentException
at android.media.MediaCodec.native_configure(Native Method)
at android.media.MediaCodec.configure(MediaCodec.java:2023)
at android.media.MediaCodec.configure(MediaCodec.java:1951)
at com.example.screenprojection.ScreenEncoder.startEncode(ScreenEncoder.java:56)
问题原因:不同手机支持的最大编解码分辨率不同,可通过adb shell cat /system/etc/media_codecs.xml
的adb命令查看手机支持的编解码分辨率。未设置编码强制要求的一些配置 会抛出 IllegalStateException。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
|