加载中 ...
首页 > 常见问题 正文

ShareREC for Android全系统手机录屏软件原理解析

2019-03-24 10:32:20 来源:沈阳软件公司 作者:沈阳软件开发

当录制完毕时,需要关闭MediaRecorder,并释放VirtualDisplay和MediaProjection,上面代码中的MediaProjection.Callback实例正是为了这个而界说的。下面的代码演示了怎样制止录制操作:

private void stop() {

if (mp != null) {

mp.stop();

if (cb != null) {

mp.unregisterCallback(cb);

}

mp = null;

}

}

方案二:自行实现媒体编码和输出

看完简朴的方案,现在来看一下庞大的方案。ShareREC在这个方案上的实现流程如下图:

移除点击此处添加图片说明文字

ShareREC将全系统录屏功效拆分为抓图、编码和输出3部门。在用户授权抓屏之后,抓图模块率先汇海,建立虚拟屏幕、建立图形缓存、建立回调等等。这内里的图形缓存是自安卓4.4以后提供的ImageReader。和MediaRecorder一样,它也提供了getSurface要领,返回用于更新缓存的surface实例。而且在缓存发生变换时,通过acquireLatestImage要领来获取最新的图片数据。不外由于我们并不知道什么时间缓存会发生变换,因此需要再挪用setOnImageAvailableListener要领设置一个OnImageAvailableListener实例,并通过它的onImageAvailable要领实时获得缓存更新的通知:

private MediaProjectionManager mpm;

private ImageReader ir;

private MediaProjection mp;

private VirtualDisplay vd;

/**

* @param screenSize 屏幕的现实分辨率

* @param videoSize 抓取图片的分辨率

*/

public void startCapturer(final int[] screenSize, final int[] videoSize, final Intent data) {

try {

float densityDpi = getResources().getDisplayMetrics().densityDpi;

int densityDpi = (int) (densityDpi * screenSize[0] / videoSize[0] + 0.5f);

ir = ImageReader.newInstance(videoSize[0], videoSize[1], PixelFormat.RGBA_8888, 4);

ir.setOnImageAvailableListener(this, null);

mp = mpm.getMediaProjection(Activity.RESULT_OK, data);

vd = mp.createVirtualDisplay("ShareREC",

videoSize[0], videoSize[1], (int) densityDpi,

DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,

ir.getSurface(), null, null);

} catch (Throwable t) {

t.printStackTrace();

}

}

public void onImageAvailable(ImageReader reader) {

Image image = reader.acquireLatestImage();

if (image != null) {

Image.Plane[] planes = image.getPlanes();

if (planes != null && planes.length > 0) {

int rowStride = planes[0].getRowStride();

ByteBuffer rgba = planes[0].getBuffer();

if (rgba != null) {

// 将rgba数据运送给编码器

offerFrame(rgba, rowStride);

}

}

image.close();

}

}

上面的代码演示了怎样通过组合VirtualDisplay和ImageReader来实现一连抓图。需要注重的一点是,凭据surface内部的实现原理(逾越本文的领域),我们获得的rgba数据,多数时间不仅包罗屏幕上的像素数据,还在图片的右侧包罗一条黑边,因此我们在将像素数据发送给编码器之前,还需要见告编码器,每一行有用像素的个数(本例子中用了字节数)。

然后说一下编码器MediaCodec。这工具从安卓4.1最先就有,一样平常是用来实现音视频编解码的。在它之前,市面上早已经有ffmpeg之类的工具,但MediaCodec的优势在于它还能调起硬件编解码模块,性能更高、效果更好。但它的早期版本功效很弱,只能支持像素数据作为输入源,而且多数是YUV花样数据,故而输入前还需要做一次RGB转YUV的操作。自安卓4.3最先,它支持surface作为输入源,因此这内里临一个看似理所应当的问题:既然我们的全系统手机录屏软件抓屏是基于安卓5.1的,而从安卓4.3最先,MediaCodec就支持以surface作为输入,那为什么不直接组合VirtualDisplay和MediaCodec就好,要中心插入一个ImageReader?这个问题怎么说呢,这是由于ShareREC不仅支持全系统录屏,还支持其它的应用内的录屏方式,如基于Cocos2d-x,Unity3D、libGDX等等引擎来做的录屏功效。而这些应用内的录屏方式,其抓取模块只能抓取到像素数据,思量到编码模块在ShareREC内是一个通用的模块,故而全系统录屏也将抓图输出处置惩罚为像素数据输出。

private BufferInfo bufferInfo;

private MediaCodec encoder;

public void startEncoder() throws Throwable {

// 获取硬件编码器支持的颜色花样,一样平常是I420或者NV12

int pixelFormat = getHWColorFormat();

MediaFormat format = MediaFormat.createVideoFormat(MIME, 1280, 720);

format.setInteger(MediaFormat.KEY_BIT_RATE, 1 * 1024 * 1024);

“沈阳软件公司”的新闻页面文章、图片、音频、视频等稿件均为自媒体人、第三方机构发布或转载。如稿件涉及版权等问题,请与

我们联系删除或处理,客服QQ:55506560,稿件内容仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同

其观点或证实其内容的真实性。