一、概述

在视频会议、线上课堂、游戏直播等场景下,屏幕共享是一个最常被用到的功能。要实现对屏幕画面的实时共享,端到端主要有几个步骤:录屏采集、视频编码、实时传输、视频解码、视频渲染。

一般来说,实时屏幕共享时,共享发起端以固定采样频率(一般 8 - 15帧)抓取到屏幕中指定源的画面(包括指定屏幕、指定区域、指定程序等),经过视频编码压缩(选择保持文本/图形边缘信息不失真的方案)后,在实时网络上以相应的帧率分发。

因此,录屏采集是实现实时屏幕共享的基础。即构作为专业的音视频云服务厂商,对于实时屏幕共享有一套完整的流程体系和 API 封装,让开发者可以更加方便快捷地拥有录屏直播的能力。

下面我们将介绍基于不同端,实现录屏采集的方法。这是第一篇,Android端录屏采集实现教程。

二、原理

在分享如何实现Android系统录屏采集前,我们先来看看其背后的原理。

Android 在 4.4 版本前要实现屏幕录制必须获取到 root 权限,但目前大部分设备的系统版本都高于4.4,因此这种情况在此就不作赘述。

在 5.0及以上版本,我们可以利用系统提供的 MediaProjection 和 MediaProjectionManager 进行屏幕录制,可以不需要获取 root 权限,但会弹窗获取权限,需要用户同意才行。

那么在Android5.0及以上版本,我们使用 MedaProjection 是如何把屏幕的数据录制下来呢?

这里我们就要说到两个“助攻的小伙伴”了——Surface 和 VirtualDisplay。

1、Surface

Handle onto a raw buffer that is being managed by the screen
compositor.A Surface is generally created by or from a consumer of
image buffers (such as a SurfaceTexture ,MediaRecorder , or Allocation
), and is handed to some kind of producer (such as OpenGL ,MediaPlayer
, or CameraDevice ) to draw into.

Google 官网对 Surface 的定义是:Surface 就是屏幕数据消费者(如 SurfaceTexture,MediaRecorder,Allocation)提供给屏幕数据的生产者(如 OpenGL,MediaPlayer,CameraDevice)的一块数据缓冲区,生产者们可以在 Surface 上进行图像内容的生产,消费者们会把生产出来的数据消费到屏幕上面(绘制出来)或者是转换成消费者所希望的数据。

2、VirtualDisplay

顾名思义,这个便是系统提供的一个虚拟屏幕,我们采用 MediaProjection 进行录制,就需要创建这样一个 VirturalDisplay 。那么,这个 VirturalDisplay 和 Surface 有什么关联呢?属于生产者还是消费者呢?

答案非常明显,VirturalDisplay 属于生产者,因为 VirturalDisplay 是系统的一个虚拟屏幕,其内容可以理解为手机物理屏幕的拷贝,只是仅存在于内存中,而没有绘制出来,所以我们无法看到这个屏幕而已,那么既然是手机屏幕的镜像,相对于屏幕录制的整个架构来说,自然就是生产者了。

OK,现在清楚了这两个助攻的小伙伴的特点,我们还要思考一个问题,现在缓冲区有了,生产者有了,那消费者呢??屏幕数据应该给谁消费呢?

这就涉及到了场景问题。Android 允许我们把屏幕数据通过 MediaRecorder 录制下来然后保存,也允许我们把屏幕数据录制下来通过 MediaCodec 进行编码,然后传输出去。

因此根据上面的原理,我们可以画出以下屏幕采集的整体架构图:

三、实现

上面我们已经清楚了整个屏幕录制的原理,那么在代码层面,我们应当如何实现呢?主要分为以下几步:

第一步,申请权限。在 AndroidManifest 加上申请权限的代码,因为我们需要用到音频录制。

<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

第二步,获取系统服务。通过 MediaProjectionManager 获取一个系统服务,这个系统服务需要获取用户授权:

mMediaProjectionManager = (MediaProjectionManager)
getSystemService(MEDIA_PROJECTION_SERVICE);

MediaProjectionManager 是系统提供的一个录屏服务,在使用上和其他的系统服务没有太大的区别,都是通过 getSystemService 获取对应的服务。

第三步,创建 Intent 跳转服务。MediaProjectManager 已经封装了获取 Intent 的方法 createScreenCaptureIntent, 拿到 Intent 之后,当调用 startActivityForResult 方法时,会触发一个请求授权的弹窗,当用户同意授权或者拒绝授权,都会通过 onActivityResult 返回。

Intent captureIntent= mMediaProjectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent, REQUEST_CODE);

第四步,监听 onActivityResult 根据用户授权返回的结果获取

MediaProjection
protected void onActivityResult(int requestCode, int resultCode, Intent data) {if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode,
data);}
}

在这里我们才是获取到了真正的屏幕录制操作对象—— MediaProjection,接下来我们就需要通过这个对象去开启屏幕录制。

第五步,创建虚拟屏幕。我们已经获取到了 MediaProjection,接下来就是要创建一个虚拟屏幕——VirtualDisplay,这一步是屏幕录制的关键所在,我们先来看看 MediaProject 官网的 API 是如何创建一个 VirtualDIsplay的,重点看看参数的定义。

public VirtualDisplay createVirtualDisplay(@NonNull String name,int width, int height, int dpi, int flags, @Nullable Surface surface,@Nullable VirtualDisplay.Callback callback, @Nullable Handler handler)

对于创建虚拟屏幕的 API ,其他参数可以先忽略,但其中有两个参数我们需要注意,一个是 Surface surface ,一个是 int Flag 。

首先是 int Flag ,从这个参数的命名上来看,我们知道这是一个标志位,从 Android 的习惯来看,这个标志位可以传递什么参数呢?我们看看注释。

* @param flags A combination of virtual display flags. See {@link DisplayManager}
for the full
* list of flags.

根据注释,我们可以看到 DisplayManager 提供了以下相关的 Flag:

那么,提供的这几个 Flag 有什么区别呢?

  • VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR:当没有内容显示时,允许将内容镜像到专用显示器上。

  • VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY:仅显示此屏幕的内容,不镜像显示其他屏幕的内容。

  • VIRTUAL_DISPLAY_FLAG_PRESENTATION:创建演示文稿的屏幕。

  • VIRTUAL_DISPLAY_FLAG_PUBLIC:创建公开的屏幕。

  • VIRTUAL_DISPLAY_FLAG_SECURE:创建一个安全的屏幕

一般如果没有特殊的需求,我们将这个 Flag 设置为 VIRTUAL_DISPLAY_FLAG_PUBLIC 就可以了,这样就可以获取到屏幕的数据了。

然后是Surface ,这不就是我们前面说的助攻小伙伴嘛。我们前面说过了,这个 Surface 是由消费者去创建的。因此,这时候就要想想我们的消费者是什么?我们的场景是什么?是要录制成文件还是编码成数据传输出去实现录屏直播呢?

当然…… 这个终极问题最后可能是要产品经理来决定……o(╯□╰)o

1、屏幕录制保存(MediaRecoder)

好了,假设现在产品经理已经明确表示,需求场景是把屏幕录制成文件保存下来,就像现在很多市面上的屏幕录制 APP 一样。那我们应该怎么做呢?

其实很简单,我们只需要想一下,有没有什么 API 是可以将图像数据录制保存成文件的呢?

Android 官方就已经有提供了一个工具供我们使用,那就是 MediaRecoder ,重点是 MediaRecoder 可以通过 getSurface 对外提供一个 Surface,而这个 Surface 刚好是 VirtualDisplay 所需要的,所以整个调用链和 API 我们可以理清楚了,如下图。而数据的流向则是相反的,从 VirturalDisplay -> Surface -> MediaRecoder(绿色箭头表示数据的流向)。

那么 MediaRecoder 要怎么使用呢?MediaRecoder 不仅可以录制视频画面,还可以录制音频。下面提供了如何设置 MediaRecoder 的代码。最后,只要调用一下 mediaRecorder.start() 就会启动录制,并将录制好的视频画面和 MIC 采集到的声音保存到我们定义的文件中。

private void initRecorder() {File file = new File(Environment.getExternalStorageDirectory(),
System.currentTimeMillis() + ".mp4");mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);mediaRecorder.setOutputFile(file.getAbsolutePath());mediaRecorder.setVideoSize(width, height);mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);mediaRecorder.setVideoEncodingBitRate(5 * 1024 * 1024);mediaRecorder.setVideoFrameRate(30);try {mediaRecorder.prepare();} catch (IOException e) {e.printStackTrace();}
}

2、录屏直播

如果此时产品经理突然要改需求,想把录制成文件改成录屏直播(那也是没办法的事o(╯□╰)o)。那我们就要改变方案了,把数据的消费者 MediaRecorder 换成其他可以编码的工具,比如 Android 自带的硬件编码 MediaCodec或者大名鼎鼎的FFmpeg。但是数据的生产者不会变,依然是VirtualDisplay,数据缓冲依旧是Surface。

以 MediaCodec 为例,关于 MediaRecoder 的流程图则变为:

MediaCodec 作为 Android 系统提供的硬编/硬解能力,本身便可作为一次专题进行分享,因此,这里不会太深入的分享关于 MediaCodec 的功能和使用方式,只是作为一个消费者的角度和我们的屏幕录制直播方案进行分享。

mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
mEncoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mInputSurface = mEncoder.createInputSurface(); //这⾥输出的 Surface 可以输⼊给
VirtualDisplay
//直接开启编码器
mEncoder.start();

四、延伸

通过以上内容我们知道,MediaRecoder 支持录制 MIC 采集的音频数据和MediaProjection 提供的屏幕画面数据。然而 MediaProjection 不能提供音频数据,如果我们想通过 MediaRecoder 录制 MediaProjection 提供的屏幕画面数据加上非 MediaRecoder 指定的音频源呢?比如我们录制一个游戏视频,但是想加入对应的音频,类似于王者荣耀的精彩片段加上特定音效,要如何实现嗯?

其实只要我们在一开始录制的时候,不设置 MediaRecoder 的音频源,然后再利用其他工具,把音频源剪辑进去就可以了。比如大名鼎鼎的FFmpeg就是音视频剪辑的好手,但是呢,FFmpeg对于上手是有一定的门槛和难度的,想要自己编译一个稳定可靠好用的FFmpeg库可不是那么简单的,并且为了加上一个录制音频的功能,大大增加我们 APK 的体积,也是因小失大的。

那么,还有其他的办法可以实现吗?答案是肯定的。

Android 系统提供了原生的 MediaExtractor 类,给音视频混合提供了相对比较简单易操作的方法,那么,使用 MediaExtractor 应该注意什么呢?

MediaExtractor 可以把音频和视频源剪辑到一起,我们可以理解为两条不同的轨道——音频轨和视频轨,把他们混在一起,其中最重要的自然是混合在一起的时间戳。因此,在剪辑的时候,除非可以明确的确定音频的开始时间在视频的某个详细时间点,否则,建议将音频和视频全部置回开始的时候,然后再开始混合。

五、总结

最后我们来总结一下本篇的主要内容。

首先我们介绍了原理,说明了MediaProjectionManager和MediaProjection两个安卓系统用来提供录屏能力的系统服务,以及两个助攻的小伙伴——数据缓冲区Surface 和虚拟屏幕 VirtualDisplay;

其次介绍了如何实现录屏采集的两个使用场景:录制并保存(屏幕录制)和录制并编码(屏幕直播);

最后延伸了如何在屏幕录制并保存的同时,混入非环境背景音的音频。

最后的最后,记得在不使用的时候,释放使用到的 API 哦!!

作者:即构科技-陈少锋,本文视频独家发布于极客时间app,视频地址:https://time.geekbang.org/dailylesson/detail/100056832

如何实现Android端的录屏采集相关推荐

  1. Windows桌面端录屏采集实现

    实时屏幕共享功能,在视频会议.游戏直播.在线教育等场景中已广泛被应用.近日,主打屏幕分享的社交应用「Squad」被Twitter收购,让我们看到了实时屏幕共享融于更多行业,开启丰富玩法的趋势. 作为实 ...

  2. 技术干货 | 录屏采集实现教程 —— iOS端

    实时屏幕共享功能,在视频会议.游戏直播.在线教育等场景中已广泛被应用.近日,主打屏幕分享的社交应用「Squad」被Twitter收购,让我们看到了实时屏幕共享融于更多行业,开启丰富玩法的趋势. 作为实 ...

  3. 技术干货 | 录屏采集实现教程 —— macOS桌面端

    实时屏幕共享功能,在视频会议.游戏直播.在线教育等场景中已广泛被应用.近日,主打屏幕分享的社交应用「Squad」被Twitter收购,让我们看到了实时屏幕共享融于更多行业,开启丰富玩法的趋势. 作为实 ...

  4. 技术干货 | 录屏采集实现教程 —— Windows桌面端

    在进入具体的方式讨论前,我们先看看 Windows 桌面图形界面的简化架构,如下图: 在 Windows Vista 之前,Windows 界面的复合画面经由 Graphics Device Inte ...

  5. android 屏幕录制方案,ShareREC for Android全系统录屏原理解析

    本文是Mob开发者平台技术副总监余勋杰基于MediaProjection实现Android全系统录屏功能的原理解析,包括了结合MediaRecorder和MediaCodec两套方案. 文 / 余勋杰 ...

  6. Android端恶意锁屏勒索应用分析

    一.前言 5月12日,一场全球性互联网灾难悄然而至,一款名为WannaCRY的PC端恶意勒索软件利用NSA泄漏的危险漏洞"永恒之蓝",给100多个国家和地区10万台电脑造成了巨大的 ...

  7. Android中手机录屏及数据解析

    Android中手机录屏及数据解析: 获取手机录屏数据: 01.手机权限获取 //动态请求权限的数组 //请求权限的数组,可以在数组中添加你需要动态获取的权限 private val PERMISSI ...

  8. 移动端和PC端的录屏软件汇总,需要的朋友速看

    有朋友有屏幕录制的需求吗?我在这里汇总了移动端和PC端的录屏软件,有需要的朋友就看看吧. 移动端录屏软件: 1.嘿录录屏 它是一款全新的手机录屏软件,支持录制游戏.直播.课程.会议.网课等内容.操作简 ...

  9. Android iOS防录屏截屏

    目录 Android防录屏和截屏 关于WindowManager.LayoutParams.FLAG_SECURE 关于Display.FLAG_SECURE iOS防录屏和截屏 监听截屏 录屏监听 ...

  10. 抛砖引玉系列:Android简易实现录屏软件。

    以前在工作中碰到一个问题,领导总是要求我给他路一段实现的功能效果或者当问题出现时,客户没办法描述清楚但是又找不到好用的免费录屏软件,所以昨天刚好有时间整理了一下,写了一个简易的Android录屏软件出 ...

最新文章

  1. 未获得计算机访问权限,如何获取文件夹的访问权限
  2. Oracle 12C -- 清空audit记录
  3. 应用程序的数据库从Sql Server迁移到Oracle
  4. X Window概述
  5. 面试准备——Struts2相关问题
  6. python课程ppt_Python电子教学课件12程序设计基本方法.ppt
  7. 旋转校正原理_【干货】全站仪水准器的检校原理及方法,值得学习!
  8. [Lab1]-EIGRP试验
  9. 计算机歌曲带歌词,mp3上怎么看歌词?我的机子说是可以显示的啊
  10. 【渝粤题库】陕西师范大学164109人力资源管理 作业(高起专)
  11. 接口调用频繁限制,接口限制流量
  12. Constraints --- transition(clock transition、input transition、max transition)
  13. Java中巧算年龄的代码,巧算年龄 - 寂寞暴走伤的个人空间 - OSCHINA - 中文开源技术交流社区...
  14. python爬取58同城二手房信息_动手写爬虫(2):爬取58同城二手物品信息
  15. C语言定义数组起始地址对齐方式(IAR C99 Kinetis K66)
  16. 鸿蒙基于linux系统,鸿蒙操作系统(HarmonyOS)是基于Linux的吗?尽管已知道它是基于微内核的...
  17. Android长截图与长图分享
  18. 2019联想创新科技大会:“端边云网智”一切就绪
  19. 最近笔记本电脑开机启动正常,进入黑屏?
  20. c语言程序设计呼克佑第二版课后答案,《程序设计技术》实验大纲-呼克佑

热门文章

  1. SQLSTATE[23000]: Integrity constraint violation:1062 Duplicate entry1664187678631531497821000‘ 解决办法
  2. 安装Go语言开发工具
  3. 用form表单提交时,出现415错误
  4. python:修改图片的尺寸
  5. OMNeT++理论算法仿真详述
  6. 国外服务器直播网站,海外直播服务器搭建
  7. nginx 文件服务器 文件类型,如何配置Nginx限制文件类型?
  8. Redis 常见面试题(带答案)110道
  9. Excel: 批量去除空格的函数——trim函数, substitute函数,clean函数
  10. Python连接SQL Server 之pyodbc