写点什么

深入浅出,Andorid 端屏幕采集技术实践 (1),android 面试题整理最新

用户头像
Android架构
关注
发布于: 2021 年 11 月 06 日

随着全球产业链线上化和数字化的加速,移动端实时屏幕共享在各行各业场景下都有了广泛的应用,比如在线教育、视频会议、远程业务咨询、手游直播。而屏幕采集则是实现实时屏幕共享流程中的第一步,本篇技术分享就来跟大家讲讲拍乐云在 Andorid 端屏幕采集的经验实践。

背景

Android 从 4.0 开始就提供了手机录屏方法,但是需要 root 权限。从 5.0 开始,Google 开放了系统录屏 API:MediaProjection 和 MediaProjectionManager,不需要 root 权限,但是会弹出录屏权限申请框,用户同意后才能开始录屏,类似 Android6.0 之后权限申请流程。 鉴于目前市面上 5.0 以下的 Android 手机占比很低且屏幕采集需要 root 权限实现复杂,接下来我们主要介绍 Android5.0 及以上版本的屏幕采集原理。 试想一下,一套完整的屏幕采集流程应该是怎样的?屏幕数据源(生产者)在缓冲区产生数据,屏幕数据消费者从缓冲区提取数据使用。不同的消费者可以实现不同的功能,比如录屏保存和录屏直播(屏幕共享)。这些关键的角色在 Android 端又是由谁来扮演呢? VirtualDisplayVirtualDisplay 是 Android 上的虚拟显示器。本文里 VirtualDisplay 的作用就是抓取屏幕上显示的内容,是屏幕数据的生产者。 Surface 在 Android 的窗口实现里,Surface 对应了一块屏幕数据缓冲区,屏幕数据生产者可以在 Surface 上生产数据,消费者则从 Surface 中提取数据使用。 屏幕采集流程


介绍完以上关键角色,我们大致可以画出一套屏幕采集流程图:



下面逐步介绍代码实现。


一、获取 MediaProjection


首先需要获取 MediaProjectionManager 服务,然后通过 MediaProjectionManager 服务,获取一个申请屏幕采集权限的 Intent 并启动屏幕采集申请权限界面: mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE); Intent intent = mediaProjectionManager.createScreenCaptureIntent(); startActivityForResult(intent, SCREEN_CAPTURE_REQUEST_CODE); 启动的屏幕采集权限申请界面如下:



用户允许(点击立即开始)后,在 onActivityResult 回调里根据返回的 resultCode 和 data 获取 MediaProjection:


protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data);


if (requestCode == SCREEN_CAPTURE_REQUEST_CODE && resultCode == Activity.RESULT_OK) { mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data); } } 需要特别注意的是,在 targetSdkVersion 大于等于 29 时,系统加强了对屏幕采集的限制,必须先启动相应的前台 Service,才能正常调用 getMediaProjection 方法,否则会抛异常: java.lang.SecurityException: Media projections require a foreground service of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION 查看系统源码发现以下条件语句如果都为 true 则抛出以上异常: if (REQUIRE_FG_SERVICE_FOR_PROJECTION //1.默认为 true && requiresForegroundService() //2.当前 APP 需要启动前台 Service && !mActivityManagerInternal.hasRunningForegroundService( //3.当前应用没有启动前台 service uid, ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECTION)) { throw new SecurityException("Media projections require a foreground service" + " of type ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PROJECT


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


ION"); }


//APP TargetSdkVersion 大于等于 29 并且不是特权应用(特权应用一般是系统应用),则返回 true(需要启动前台 service) boolean requiresForegroundService () { return mTargetSdkVersion >= Build.VERSION_CODES.Q && !mIsPrivileged; } 前台 Service 配置参考如下:


二、构造 Surface


1.如果屏幕采集数据用来录制视频,那么消费者可以是 MediaRecoder,相应地 Surface 由 MediaRecoder 提供: Surface surface = mediaRecorder.getSurface(); 2.如果屏幕采集数据用来屏幕共享(录屏直播),那么消费者可以是类似 MediaCodec 这样的编码器,相应地 Surface 由 MediaCodec 提供: Surface surface = mediaCodec.createInputSurface(); 3.如果需要将屏幕采集数据显示在 UI 界面 SurfaceView 上的话,Surface 可以通过以下方式生成: SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surface); Surface surface = surfaceView.getHolder().getSurface(); 4.如果想要更加灵活的掌控整个屏幕采集流程,Surface 还可以通过 SurfaceTexture 生成: SurfaceTexture surfaceTexture = new SurfaceTexture(textureId); surfaceTexture.setOnFrameAvailableListener(new OnFrameAvailableListener() {


@Override public void onFrameAvailable(SurfaceTexture surfaceTexture) {


} }, handler); Surface surface = new Surface(surfaceTexture); 这里简单介绍下 SurfaceTexture 。SurfaceTexture 可以用来捕获视频流中的图像帧,当 SurfaceTexture 中有数据更新时,会触发 onFrameAvailable 回调,此时可以调用 updateTexImage 方法从视频流数据中更新当前数据帧。


三、创建 VirtualDisplay


MediaProjection 有现成的 API 可以调用: public VirtualDisplay createVirtualDisplay(String name, int width, int height, int dpi, int flags, Surface surface, VirtualDisplay.Callback callback, Handler handler) {


DisplayManager dm = (DisplayManager) mContext.getSystemService(Context.DISPLAY_SERVICE); return dm.createVirtualDisplay(this, name, width, height, dpi, surface, flags, callback, handler, null /* uniqueId */); } 参数说明文档如下:



各参数 Android 官方文档都有较详细的说明,其中 flag 和 surface 这里再额外说明下:


flag 是 VirtualDisplay 的标记位,一般取 VIRTUAL_DISPLAY_FLAG_PUBLIC 即可; surface 也就是上文提到的屏幕数据缓冲区,一般由消费者提供。


四、屏幕采集数据处理

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
深入浅出,Andorid 端屏幕采集技术实践(1),android面试题整理最新