Android实现录屏直播(一)ScreenRecorder的简单分析
Android实现录屏直播(二)需求才是硬道理之产品功能调研
Android实现录屏直播(三)MediaProjection + VirtualDisplay + librtmp + MediaCodec实现视频编码并推流到rtmp服务器
应项目需求瞄准了Bilibili的录屏直播功能,基本就仿着做一个吧。研究后发现Bilibili是使用的MediaProjection 与 VirtualDisplay结合实现的,需要 Android 5.0 Lollipop API 21以上的系统才能使用。
其实官方提供的android-ScreenCapture这个Sample中已经有了MediaRecorder的实现与使用方式,还有使用MediaRecorder实现的录制屏幕到本地文件的Demo,从中我们都能了解这些API的使用。
而如果需要直播推流的话就需要自定义MediaCodec,再从MediaCodec进行编码后获取编码后的帧,免去了我们进行原始帧的采集的步骤省了不少事。可是问题来了,因为之前没有仔细了解H264文件的结构与FLV封装的相关技术,其中爬了不少坑,此后我会一一记录下来,希望对用到的朋友有帮助。
项目中对我参考意义最大的一个Demo是网友Yrom的GitHub项目ScreenRecorder,Demo中实现了录屏并将视频流存为本地的MP4文件(咳咳,其实Yrom就是Bilibili的员工吧?( ゜- ゜)つロ)😏。在此先大致分析一下该Demo的实现,之后我会再说明我的实现方式。
ScreenRecorder
具体的原理在Demo的README中已经说得很明白了:
Display
可以“投影”到一个VirtualDisplay
- 通过
MediaProjectionManager
取得的MediaProjection
创建VirtualDisplay
VirtualDisplay
会将图像渲染到Surface
中,而这个Surface
是由MediaCodec
所创建的
1
2
3
4
5
6 > mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
> ...
> mSurface = mEncoder.createInputSurface();
> ...
> mVirtualDisplay = mMediaProjection.createVirtualDisplay(name, mWidth, mHeight, mDpi, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mSurface, null, null);
>
>
MediaMuxer
将从MediaCodec
得到的图像元数据封装并输出到MP4文件中
1
2
3
4
5
6
7 > int index = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
> ...
> ByteBuffer encodedData = mEncoder.getOutputBuffer(index);
> ...
> mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo);
>
>
>
所以其实在Android 4.4上可以通过
DisplayManager
来创建VirtualDisplay
也是可以实现录屏,但因为权限限制需要ROOT。 (see DisplayManager.createVirtualDisplay())
Demo很简单,两个Java文件:
- MainActivity.java
- ScreenRecorder.java
MainActivity
类中仅仅是实现的入口,最重要的方法是onActivityResult
,因为MediaProjection就需要从该方法开启。但是别忘了先进行MediaProjectionManager的初始化
1 |
|
ScreenRecorder
这是一个线程,结构很清晰,run()
方法中完成了MediaCodec的初始化,VirtualDisplay的创建,以及循环进行编码的全部实现。
线程主体
1 |
|
MediaCodec的初始化
方法中进行了编码器的参数配置与启动、Surface的创建两个关键的步骤
1 | private void prepareEncoder() throws IOException { |
编码器实现循环编码
下面的代码就是编码过程,由于作者使用的是Muxer来进行视频的采集,所以在resetOutputFormat方法中实际意义是将编码后的视频参数信息传递给Muxer并启动Muxer。
1 | private void recordVirtualDisplay() { |
1 | private void resetOutputFormat() { |
获取sps pps的ByteBuffer,注意此处的sps pps都是read-only只读状态
1 | private void getSpsPpsByteBuffer(MediaFormat newFormat) { |
录屏视频帧的编码过程
BufferInfo.flags表示当前编码的信息,如源码注释:
1 | /** |
实现编码:
1 | private void encodeToVideoTrack(int index) { |
以上就是对ScreenRecorder这个Demo的大体分析,由于总结时间仓促,很多细节部分我也没有进行深入的发掘研究,所以请大家抱着怀疑的态度阅读,如果说明有误或是理解不到位的地方,希望大家帮忙指出,谢谢!
参考文档
在功能的开发中还参考了很多有价值的资料与文章: