您的位置:首页 > 移动开发 > Android开发

Android5.0以上使用MediaProjection截图和录屏

2016-03-24 14:43 459 查看

MediaProjection介绍

MediaProjection可以用来捕捉屏幕,具体来说可以截取当前屏幕和录制屏幕视频。MediaProjection由MediaProjectionManager来管理和获取。

使用步骤

首先获取MediaProjectionManager,和其他的Manager一样通过 Context.getSystemService() 传入参数MEDIA_PROJECTION_SERVICE获得实例。

接着调用MediaProjectionManager.createScreenCaptureIntent()弹出dialog询问用户是否授权应用捕捉屏幕,同时覆写onActivityResult()获取授权结果。

如果授权成功,通过MediaProjectionManager.getMediaProjection(int resultCode, Intent resultData)获取MediaProjection实例,通过MediaProjection.createVirtualDisplay(String name, int width, int height, int dpi, int flags, Surface surface, VirtualDisplay.Callback callback, Handler handler)创建VirtualDisplay实例。实际上在上述方法中传入的surface参数,是真正用来截屏或者录屏的。

截屏

截屏这里用到ImageReader类,这个类的getSurface()方法获取到surface直接传入MediaProjection.createVirtualDisplay()方法中,此时就可以执行截取。通过ImageReader.acquireLatestImage()方法即可获取当前屏幕的Image,经过简单处理之后即可保存为Bitmap。

private void startCapture() {

mImageName = System.currentTimeMillis() + ".png";

Log.e(TAG, "image name is : " + mImageName);

Image image = mImageReader.acquireLatestImage();

int width = image.getWidth();

int height = image.getHeight();

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

final ByteBuffer buffer = planes[0].getBuffer();

int pixelStride = planes[0].getPixelStride();

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

int rowPadding = rowStride - pixelStride * width;

Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);

bitmap.copyPixelsFromBuffer(buffer);

bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);

image.close();

if (bitmap != null) {

Log.e(TAG, "bitmap  create success ");

try {

File fileFolder = new File(mImagePath);

if (!fileFolder.exists())

fileFolder.mkdirs();

File file = new File(mImagePath, mImageName);

if (!file.exists()) {

Log.e(TAG, "file create success ");

file.createNewFile();

}

FileOutputStream out = new FileOutputStream(file);

bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);

out.flush();

out.close();

Log.e(TAG, "file save success ");

Toast.makeText(this.getApplicationContext(), "截图成功", Toast.LENGTH_SHORT).show();

} catch (IOException e) {

Log.e(TAG, e.toString());

e.printStackTrace();

}

}

}


录屏

录屏需要用到上篇博文中的MediaCadec,这个类将原始的屏幕数据编码,在通过MediaMuxer分装为mp4格式保存。MediaCodec.createInputSurface()获取一个surface对象讲起传入MediaProjection.createVirtualDisplay()即可获取屏幕原始多媒体数据,之后读取MediaCodec编码输出数据经过MediaMuxer封装处理为mp4即可播放,实现录屏。

private void recordVirtualDisplay() {//循环多去编解码器输出数据经过处理保存为mp4

while (!mIsQuit.get()) {

int index = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 10000);

Log.i(TAG, "dequeue output buffer index=" + index);

if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {//后续输出格式变化

resetOutputFormat();

} else if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {//请求超时

Log.d(TAG, "retrieving buffers time out!");

try {

// wait 10ms

Thread.sleep(10);

} catch (InterruptedException e) {

}

} else if (index >= 0) {//有效输出

if (!mMuxerStarted) {

throw new IllegalStateException("MediaMuxer dose not call addTrack(format) ");

}

encodeToVideoTrack(index);

mMediaCodec.releaseOutputBuffer(index, false);

}

}

}

private void encodeToVideoTrack(int index) {//输出数据为mp4文件

ByteBuffer encodedData = mMediaCodec.getOutputBuffer(index);

if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {//是特定格式信息等配置数据,不是媒体数据

// The codec config data was pulled out and fed to the muxer when we got

// the INFO_OUTPUT_FORMAT_CHANGED status.

// Ignore it.

Log.d(TAG, "ignoring BUFFER_FLAG_CODEC_CONFIG");

mBufferInfo.size = 0;

}

if (mBufferInfo.size == 0) {

Log.d(TAG, "info.size == 0, drop it.");

encodedData = null;

} else {

Log.d(TAG, "got buffer, info: size=" + mBufferInfo.size

+ ", presentationTimeUs=" + mBufferInfo.presentationTimeUs

+ ", offset=" + mBufferInfo.offset);

}

if (encodedData != null) {//存在编码数据

encodedData.position(mBufferInfo.offset);

encodedData.limit(mBufferInfo.offset + mBufferInfo.size);

mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo);//写入

Log.i(TAG, "sent " + mBufferInfo.size + " bytes to muxer...");

}

}

private void resetOutputFormat() {

// should happen before receiving buffers, and should only happen once

if (mMuxerStarted) {

throw new IllegalStateException("output format already changed!");

}

MediaFormat newFormat = mMediaCodec.getOutputFormat();

Log.i(TAG, "output format changed.\n new format: " + newFormat.toString());

mVideoTrackIndex = mMuxer.addTrack(newFormat);

mMuxer.start();

mMuxerStarted = true;

Log.i(TAG, "started media muxer, videoIndex=" + mVideoTrackIndex);

}


附录参考

官方文档

Android视频录制

Android 5.0截屏

Android录屏代码

本文Demo
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: