ShareREC for Android全系统手机录屏软件原理解析
format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, pixelFormat);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 0);
encoder = MediaCodec.createEncoderByType("video/avc");
encoder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
encoder.start();
bufferInfo = new BufferInfo();
}
上面的代码演示了怎样初始化一个MediaCodec实例。需要注重的一点是,虽然我们设置了MediaCodec的帧率,但由于抓图时,图片数据不是匀速输入的,因此这个字段在此处形同虚设,可是又不能不填。上面的例子并不演示怎样获取硬件编码器支持的颜色花样类型,详细的实现方式可以搜索一下,不难找。
然后我们来实现上面抓图模块中遗留的offerFrame要领:
public void offerFrame(ByteBuffer frame, int rowStride) throws Throwable {
long framePreTimeUs = System.nanoTime() / 1000;
ByteBuffer[] inputBuffers = encoder.getInputBuffers();
int inputBufferIndex = encoder.dequeueInputBuffer(-1);
if (inputBufferIndex >= 0) {
ByteBuffer ibb = inputBuffers[inputBufferIndex];
(0);YUVConverter.rgbaToI420(frame, ibb, 1280, 720, rowStride);
encoder.queueInputBuffer(inputBufferIndex, 0, ibb.limit(), framePreTimeUs, 0);
}
ByteBuffer[] outputBuffers = encoder.getOutputBuffers();
int outputBufferIndex = encoder.dequeueOutputBuffer(bufferInfo, 0);
while (outputBufferIndex >= 0) {
ByteBuffer obb = outputBuffers[outputBufferIndex];
if (obb != null) {
int frameType = 0;
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) == 1) {
frameType = 1;
} else if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) == 2) {
frameType = 2;
}
// 将编码好的H264帧输出给mp4合并模块
offerVideoTrack(obb, bufferInfo.size, bufferInfo.presentationTimeUs, frameType);
}
encoder.releaseOutputBuffer(outputBufferIndex, false);
outputBufferIndex = encoder.dequeueOutputBuffer(bufferInfo, 0);
}
}
MediaCodec的输入输出都有缓存行列,我们要给它输入数据,需要先获取其输入缓存行列,然后在空闲的位置复制像素数据。由于我们抓取到的数据是RGBA花样,必须转为YUV花样才气别准确编码,这里ShareREC使用了libYUV,将RGBA转为I420。此外,并不是一输入图片就连忙会有输出h264帧,MediaCodec一样平常会缓存3-7张图片。
最后是视频合并模块,ShareREC使用了mp4v2来实现。实在在安卓平台同样自4.3以后系统自带了视频合并工具MediaMuxer。但这个工具似乎必须与MediaCodec一同使用,由于的用户要求ShareREC至少支持4.0以上的系统,故除了MediaCodec,实在我们还具备优化过的软件编码器。为了同时兼容两种编码器,我们放弃了MediaMuxer而接纳兼容性更好的mp4v2。
本文不先容mp4v2的使用,由于这凌驾java代码的领域(libYUV也是)。但它的事情原理很简朴,无非就是打开文件;在内存中生存视频轨道和音频轨道的信息;接着一帧帧写入视频或者音频数据,不用在意写入顺序,可以混在一起;在完成合并时,将内存内里的音视频信息组合为mp4形貌信息,追加到文件尾部,之后关闭文件。这个流程网上的文档许多,随便搜索就有了。但使用时有一些可能需要注重的,包罗多线程同步和图片出现时间的问题。
关于多线程同步,是指由于我们在现实录屏时,音频和视频是离开两条线程来编码的,但最后往mp4v2写入时,是写入统一个文件的,但由于mp4v2没有做好同步,因此若是写入音视频帧的时间,差池mp4v2自己做好同步锁,会泛起音视频写乱了的问题,导致最后视频无法播放。
至于图片出现的问题,请回首一下上面代码例子中的framePreTimeUs,这个是这一张图片被送入编码器的时间,合并视频时,需要将这个字段带给mp4v2。由于mp4v2默认是以为图片匀速输入的,以是它不剖析我们这个字段,只在意一最先设置的帧率。但由于抓图不是匀速的,因此若是只遵照牢固的帧率来显示,未来视频就会时快时慢,甚至声音图片差别步。因此在添加视频帧时,务须要设置出现的时间偏移。ShareREC以TimeScale为基准,会将framePreTimeUs凭据TimeScale做一次转换,然后在MP4WriteSample的时间,renderingOffset参数通报进去。
“沈阳软件公司”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与
我们联系删除或处理,客服QQ:55506560,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同
其观点或证实其内容的真实性。
热门文章
使用“扫一扫”即可将网页分享至朋友圈。