Android实现两台手机屏幕共享和远程控制

1 屏幕共享功能介绍

屏幕共享是指在视频通话或互动直播过程中将屏幕内容以视频的方式分享给其他的观众,以增强互动体验,提高沟通效率。屏幕共享解决方案提升了用户实时视频通话的沟通效率。

屏幕共享在如下场景中应用广泛:

  • 视频会议场景中,屏幕共享可以将讲话者本地的文件、数据、网页、PPT 等画面分享给其他与会人;
  • 在线课堂场景中,屏幕共享可以将老师的课件、笔记、讲课内容等画面展示给学生观看。
微信图片_20220803172428.png

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

在实现屏幕共享功能之前,请确保:

4 屏幕共享实现流程-即构屏幕共享SDK

我们需要结合 Android 系统 API 和 ZEGO Express SDK 的自定义视频采集来进行屏幕分享。

下图展示了 Android 平台实现屏幕共享的数据流转:

微信图片_20220803172447.png

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 实例

  1. 在 AndroidManifest.xml 中添加相关配置。

<div class="mk-hint">

为实现 Android 10.0 及以上版本应用的屏幕录制,需要在代码中开启前台服务,并在 AndroidManifest.xml 中注册 Service,添加 foregroundServiceType 属性。
</div>

<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>
  1. 用户授权后创建 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产品优惠;

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容