Android实现两台手机屏幕共享和远程控制
1 屏幕共享功能介绍
屏幕共享是指在视频通话或互动直播过程中将屏幕内容以视频的方式分享给其他的观众,以增强互动体验,提高沟通效率。屏幕共享解决方案提升了用户实时视频通话的沟通效率。
屏幕共享在如下场景中应用广泛:
- 视频会议场景中,屏幕共享可以将讲话者本地的文件、数据、网页、PPT 等画面分享给其他与会人;
- 在线课堂场景中,屏幕共享可以将老师的课件、笔记、讲课内容等画面展示给学生观看。

2 屏幕共享示例源码下载
请参考 下载示例源码 获取源码。
相关源码请查看 “/ZegoExpressExample/Others/src/main/java/com/example/others/screensharing” 目录下的文件。
others
...
├── screensharing
│ ├── CaptureScreenService.java //此文件实现了系统 Service 接口
│ ├── ScreenSharingActivity.java // 此文件主要完成了通过 ZegoExpress SDK 将屏幕画面数据流推送到远端的工作
│ ├── VideoCaptureScreen.java //此文件用于通过安卓系统接口创建 VirtualDisplay 实例,获取屏幕数据,并发送给 ZEGO Express SDK
│ └── ZegoVideoCaptureCallback.java //此文件实现了 ZegoExpress 的 IZegoCustomVideoCaptureHandler
...
3 屏幕共享功能实现准备工作-集成屏幕共享SDK
在实现屏幕共享功能之前,请确保:
- 已在项目中集成 ZEGO Express SDK,实现基本的实时音视频功能,详情请参考 快速开始 - 集成 和 快速开始 - 实现视频通话。
- 已在 ZEGO 控制台 创建项目,并申请有效的 AppID 和 AppSign,详情请参考 控制台 - 项目管理 中的“项目信息”。
4 屏幕共享实现流程-即构屏幕共享SDK
我们需要结合 Android 系统 API 和 ZEGO Express SDK 的自定义视频采集来进行屏幕分享。
下图展示了 Android 平台实现屏幕共享的数据流转:

4.1 获取用户录制屏幕授权
在录制屏幕前需要获取用户的授权,不同版本下需要获取的权限如下:
- Android 4.4 及之前版本必须获取到 root 权限后才能实现屏幕录制,由于目前大部分设备的系统版本都高于 4.4,该场景此处不做赘述。
- Android 5.0 及以上版本,可以使用系统提供的 MediaProjection 和 MediaProjectionManager 进行屏幕录制。该版本下可以不获取 root 权限,但会弹窗提示用户是否允许应用录制屏幕,需要用户授权。
- Android 10.0 及以上版本,屏幕录制使用系统 API 时需要用到前台服务,详情请参考 官方文档。
public static MediaProjectionManager mMediaProjectionManager;
if (Build.VERSION.SDK_INT < 21) {
Toast.makeText(ZGVideoCaptureOriginUI.this, getString(R.string.record_request), Toast.LENGTH_SHORT).show();
finish();
} else {
// 5.0及以上版本
// 请求录屏权限,等待用户授权
mMediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), REQUEST_CODE);
}
4.2 屏幕共享SDK-创建 MediaProjection 实例
- 在 AndroidManifest.xml 中添加相关配置。
为实现 Android 10.0 及以上版本应用的屏幕录制,需要在代码中开启前台服务,并在 AndroidManifest.xml 中注册 Service,添加 foregroundServiceType 属性。
<application>
<activity android:name="im.zego.videocapture.ui.ZGVideoCaptureDemoUI" />
<activity android:name="im.zego.videocapture.ui.ZGVideoCaptureOriginUI"></activity>
<service android:name=".service.CaptureScreenService"
android:enabled="true"
android:foregroundServiceType="mediaProjection"/>
</application>
- 用户授权后创建 MediaProjection 实例。
- 对于 Android 10.0 以下版,直接在授权成功后获取 MediaProjection
- 对于 Android 10.0 及以上版本,MediaProjection 实例的创建需要在前台服务的 onStartCommand 方法中执行。
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.Q){
//Target版本高于等于10.0需要使用前台服务,并在前台服务的onStartCommand方法中创建MediaProjection
service=new Intent(ZGVideoCaptureOriginUI.this, CaptureScreenService.class);
service.putExtra("code",resultCode);
service.putExtra("data",data);
startForegroundService(service);
}else {
//Target版本低于10.0直接获取MediaProjection
mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data);
}
}
}
创建一个类,实现 Service 接口,在 onStartCommand 中创建 MediaProjection 实例。
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class CaptureScreenService extends Service {
...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
···
//在这里获取MediaProjection
ZGVideoCaptureOriginUI.mMediaProjection = ZGVideoCaptureOriginUI.mMediaProjectionManager.getMediaProjection(mResultCode, Objects.requireNonNull(mResultData));
return super.onStartCommand(intent, flags, startId);
}
···
}
4.3 屏幕共享SDK-开启 ZegoExpress SDK 的自定义视频采集功能
调用 ZegoExpress SDK 的 enableCustomVideoCapture 开启自定义采集功能,详情请参考 自定义视频采集。
//VideoCaptureScreen继承IZegoCustomVideoCaptureHandler,用于监听自定义采集onStart和onStop回调
VideoCaptureScreen videoCapture = new VideoCaptureScreen(ZGVideoCaptureOriginUI.mMediaProjection, DEFAULT_VIDEO_WIDTH, DEFAULT_VIDEO_HEIGHT, mSDKEngine);
//监听自定义采集开始停止回调
mSDKEngine.setCustomVideoCaptureHandler(videoCapture);
ZegoCustomVideoCaptureConfig videoCaptureConfig=new ZegoCustomVideoCaptureConfig();
//使用SurfaceTexture类型进行自定义采集
videoCaptureConfig.bufferType=ZegoVideoBufferType.SURFACE_TEXTURE;
//开始自定义采集
mSDKEngine.enableCustomVideoCapture(true, videoCaptureConfig, ZegoPublishChannel.MAIN);
4.4 屏幕共享SDK-登录房间并开始推流
调用 loginRoom 接口,传入房间 ID 参数 “roomID” 和用户参数 “user”,登录房间。
调用 startPublishingStream 接口,传入流 ID 参数 “streamID”,向远端用户发送本端的音视频流。
/** 创建用户 */
ZegoUser user = new ZegoUser("user1");
/** 开始登录房间 */
mSDKEngine.loginRoom("room1", user);
/** 开始推流 */
mSDKEngine.startPublishingStream("stream1");
4.5 创建 VirtualDisplay 并给 ZEGO Express SDK 发送屏幕数据-
1、创建 ZegoVideoCaptureCallback 类继承 IZegoCustomVideoCaptureHandler。
2、创建 VideoCaptureScreen 类继承 ZegoVideoCaptureCallback。
当收到 onStart 回调后,开发者可以通过 MediaProjection 创建 VirtualDisplay 实例,用于获取屏幕数据,并发送给 ZEGO Express SDK。
3、通过 createVirtualDisplay 系统 API 将虚拟显示器的内容渲染到 Surface。
//ZegoVideoCaptureCallback继承于IZegoCustomVideoCaptureHandler
class VideoCaptureScreen extends ZegoVideoCaptureCallback {
@Override
//当收到onStart回调后,就可以通过MediaProjection创建VirtualDisplay,并给ZEGO SDK塞屏幕数据
public void onStart(ZegoPublishChannel channel) {
if (mZegoEngine != null && !mIsCapturing && mMediaProjection != null) {
mIsCapturing = true;
//通过ZEGO API getCustomVideoCaptureSurfaceTexture获取SurfaceTexture,该接口默认使用主路通道进行推流
SurfaceTexture texture = mZegoEngine.getCustomVideoCaptureSurfaceTexture();
texture.setDefaultBufferSize(mCaptureWidth, mCaptureHeight);
//通过获取的SurfaceTexture创建Surface
mSurface = new Surface(texture);
//通过mSurface,完成将录屏数据塞给ZEGO SDK
mVirtualDisplay = mMediaProjection.createVirtualDisplay("ScreenCapture",
mCaptureWidth, mCaptureHeight, 1,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mSurface, null, mHandler);
}
}
}
至此,我们已完成采集屏幕数据并通过 ZegoExpress SDK 分享到远端的操作。
5 观看远端屏幕共享-远程控制
完成以上步骤之后,其他用户可以使用 startPlayingStream 接口拉取屏幕共享流,详细步骤可以参考 快速开始。
// 同样的,拉流播放的用户首先需要初始化 SDK 并登陆同一个房间
...
...
// 拉流播放,需传入发起屏幕共享的用户推流时所用的 streamID
mSDKEngine.startPlayingStream(streamID, new ZegoCanvas(playView));
6 获取屏幕共享SDK更多帮助
获取本文的Demo、开发文档、技术支持,访问即构文档中心
近期有开发规划的开发者可上即构官网查看,恰逢即构七周年全线音视频产品1折的优惠,联系商务获取RTC产品优惠;
Android实现两台手机屏幕共享和远程控制的更多相关文章
- appium+pytest+allure+jenkins 如何实现多台手机连接
使用appium可以实现app自动化测试,我们之前是连接一台手机去运行,如何同时连接多台手机呢?很多人可能想到的是多线程(threading).今天分享一种比多线程更简单的方法,虽然不是多台手机同时运 ...
- Winpcap网络编程十之Winpcap实战,两台主机通过中间主机通信
注:源码等等的我不会全然公开的,此篇文章写出来为大家的网络编程或者课程设计提供一定的思路.. 好,本次我们须要完毕的任务是: 完毕两台主机通过中间主机的数据通信(网络层) 添加基于IP地址的转发功能 ...
- Appium同时连接多台手机进行测试(多线程)
作为测试小白,当时遇到了N多问题: 开启多线程后,发现app启动后,用例就停止了:且启动app对应的手机不能正确对应,用例中是A手机跑A用例,结果启动了B手机跑A用例报错. 主要原因:Appium S ...
- 利用Android Lost通过互联网或短信远程控制安卓设备
利用Android Lost通过互联网或短信远程控制安卓设备 作者:Jack Wallen| 杰克·瓦伦翻译:PurpleEndurer.2014-11-15第1版 使用智能手机要考虑的一个至关重要的 ...
- 使用TCP在同一台电脑上可以建立连接,在两台电脑上却连接失败的原因分析
最近在用unity做联机游戏,在网络方面费了不少劲,总是在代码没问题的时候出一些莫名奇妙的BUG,不过后来都决定了.如果感觉代码没问题,八成就是防火墙的问题. 用unity发布后的游戏,如果涉及网络, ...
- Android 实现两屏幕互相滑动
Android 实现两屏幕互相滑动 下文来自: http://blog.csdn.net/song_shi_chao/article/details/7081664 ----------------- ...
- ESXI和vSphere的安装配置-实现一台电脑硬件虚拟化为两台
本篇文章包含以下几个部分: 1EXSI软件和vSphere软件的安装 2在vSphere上安装虚拟系统 3对虚拟系统通过配置实现硬件虚拟化,实现硬件直通 1.EXSI安装 通过网上下载EXSI ISO ...
- 获取android的SDK或者手机目录路径
获取android的SDK或者手机目录路径 Google为我们提供了API来获取SDK或者手机目录路径: 1.获取SD卡目录 File file1 = Environment.getExternalS ...
- Android设置头像,手机拍照或从本地相冊选取图片作为头像
[Android设置头像,手机拍照或从本地相冊选取图片作为头像] 像微信.QQ.微博等社交类的APP,通常都有设置头像的功能,设置头像通常有两种方式: 1,让用户通过选择本地相冊之类的图片库中已 ...
- 两台win7电脑网线直连办法(共享文件夹形式)
一.背景 一台电脑需要测试,但要不停更新APP,可是该电脑没网络,用U盘太繁琐,即想到用网线将两台 电脑直连,一台电脑共享文件夹给另一台电脑,达到交换文件的目的. 感谢Tony(http://www. ...
随机推荐
- Redisson读写锁和分布式锁的项目实践
问题1:在修改分组时,有短链接正在访问会出现什么问题?怎么解决 假设:现有线程A正在修改短链a的分组gid1为gid2(还未修改成功) 同时有一个线程B获取了短链a分组gid1,要进行统计pv,uv, ...
- CAD如何使用 “库” 和 “打开文件菜单栏” 和 “项目管理器”
这个是一个简单的问题,就是库工具栏丢了怎么办? 点击 默认-块-插入-库中的块,这个菜单栏就会恢复了 打开文件菜单栏,有些同学的菜单栏默认是不存在的,需要特殊功能时可以使用命令行MANUBAR使其显现 ...
- AWK用法全解
一.awk介绍 awk是Linux自带的一个逐行扫描的文本处理工具,支持正则表达式.循环控制.条件判断.格式化输出.AWK自身带有一些变量,可以在书写脚本时调用. 二.基本语法格式 2.1.在shel ...
- 记一次SSD性能瓶颈排查之路——寿命与性能之间的取舍
1. 背景 我就职于一家轨道交通行业公司,负责的产品之一是日志记录板卡配套软件.有一天接到了现场报告,记录软件出现通信异常,将日志数据拉回来以后,发现出现异常时,CPU使用率接近100%,记录相关软 ...
- js判断一个变量是否存在值得简单方法
在编码过程中,有时候我们需要对一个变量判断其是否有值,这里有一种比较不错的方法判断: !!variable //返回True为存在值,返回False为不存在值 注意是双感叹号"!!" ...
- GitLab介绍及Docker部署GitLab
概述 官方文档:https://docs.gitlab.com/?tab=Use+GitLab GitHub地址:https://github.com/gitlabhq/gitlabhq GitLab ...
- MyBatisPlus逆向工程
MyBatisPlus逆向工程 一.创建Springboot工程 二.引入pom依赖 <?xml version="1.0" encoding="UTF-8&quo ...
- java中的mysql事务
mysql事务 如何进入事务处理? 开启事务 start transaction; 执行语句 增加.修改.删除 等业务处理的sql语句... 回滚事务 rollback; 相当于sql语句都没有执行 ...
- Raft论文(中英翻译)
In Search of an Understandable Consensus Algorithm(Extended Version) 寻找一种可理解的一致性算法(拓展版) 作者:斯坦福大学的Die ...
- 分时间段(年份或月份)统计,没有数字补0 Java(替代 MYSQL) 做法
需求如下~ 输入年份,表格第一行 1-12 月 输入年份和月份 表格第一行 1--31 具体天数 表格第二行就是统计数量,没有补0. 看完首先想到MYSQL查询出连续一段时间和数量,没有 就为0. ...