【Android5.0以上实现对手机屏幕录制并将视频实时保存到本地(亦可实时传输)】丈夫欲遂平生志,一载寒窗一举汤。这篇文章主要讲述Android5.0以上实现对手机屏幕录制并将视频实时保存到本地(亦可实时传输)相关的知识,希望能为你提供帮助。
前言:
经过很长的学习很研究,在实现了支持安卓4.3以上可实现屏幕录制并保存或实时发送后,根据手机系统的版本号,在android5.0以上利用原生的接口效率更高更省CPU,内存。
5.0以上编码传输可达到每90帧/s,且在手机画面不刷新时不传输数据,更省内存电量,因此在5.0以上优先使用原生接口更为合适。
设计逻辑:
初始化
1.MediaProjectionManager,
2.Intent启动mMediaProjectionManager.createScreenCaptureIntent();
,
3.启动成功后开启线程
1)创建H264编码器MediaCodec,
2)启动VirtualDisplay,将MediaCodec创建的inputsurface通过创建mVirtualDisplay = mediaProjection.createVirtualDisplay,传入,使编码器开始不断记录并开始解码。
基础代码如下:
/** * 5.0屏幕录制 */ @SuppressWarnings("ALL") @TargetApi(Build.VERSION_CODES.LOLLIPOP) public class ScreenAboveLollRecordManager { private static final int REQUEST_CODE = 12580; private MediaProjectionManager mMediaProjectionManager; private ScreenRecorderThread mScreenRecorderThread; private int mScreenWidth; private int mScreenHeight; private MediaProjection mMediaProjection; private MediaCodec mMediaCodec; private final MediaCodec.BufferInfo mBufferInfo = new MediaCodec.BufferInfo(); private VirtualDisplay mVirtualDisplay; private RandomAccessFile mH264DataFile = null; public ScreenRecorderManager(Activity activity, int width, int height) { if (activity != null) { mScreenWidth = width; mScreenHeight = height; mMediaProjectionManager = (MediaProjectionManager) activity .getSystemService(Context.MEDIA_PROJECTION_SERVICE); File file = new File(Environment.getExternalStorageDirectory().getPath() + "/videoTest.264"); try { mH264DataFile = new RandomAccessFile(file, "rw"); } catch (FileNotFoundException e) { } } }//退出线程,停止录制 public void destroy() { mIsRun = false; mMediaProjectionManager = null; }//开始屏幕录制public void startScreenRecorde(Activity activity) { if (mMediaProjectionManager != null) { if (activity != null) { Intent intent = mMediaProjectionManager.createScreenCaptureIntent(); activity.startActivityForResult(intent, REQUEST_CODE); } } }/* * 在Acitvity的onActivityResult调用 */ public void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode != REQUEST_CODE) { return; }if (mScreenRecorderThread != null || mIsRun) { // 5.0的编码已经开启了 // activity被销毁了 return; }if (resultCode == Activity.RESULT_OK) { mMediaProjection = mMediaProjectionManager.getMediaProjection(resultCode, data); if (mMediaProjection == null) { // TODO 通知启动失败 return; } mIsRun = true; mScreenRecorderThread = new ScreenRecorderThread(); mScreenRecorderThread.start(); // TODO 通知开始屏幕录制 } else { // TODO 通知启动失败 } }//录制视频线程 private class ScreenRecorderThread extends Thread { @Override public void run() { super.run(); createMediaCodec(mMediaProjection); recordVirtualDisplay(); release(); } }//创建编码器 private void createMediaCodec(MediaProjection mediaProjection) { if (mediaProjection != null) { MediaCodecInfo codecInfo = selectCodec(); if (codecInfo == null) { return; }MediaCodecInfo.CodecCapabilities capabilities = codecInfo .getCapabilitiesForType(MediaFormat.MIMETYPE_VIDEO_AVC); MediaCodecInfo.CodecProfileLevel[] profileLevels = capabilities.profileLevels; MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, mScreenWidth, mScreenHeight); // 设置颜色格式 format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface); // TODO 可根据不同分辨率设置比特率 format.setInteger(MediaFormat.KEY_BIT_RATE, 4000000); // 设置帧率 format.setInteger(MediaFormat.KEY_FRAME_RATE, 10); // 设置关键帧间隔时间,单位s format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1); format.setInteger(MediaFormat.KEY_PROFILE, MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline); int maxCodecProfileLevel = MediaCodecInfo.CodecProfileLevel.AVCLevel1; for (MediaCodecInfo.CodecProfileLevel codecProfileLevel : profileLevels) { if (codecProfileLevel.profile != MediaCodecInfo.CodecProfileLevel.AVCProfileBaseline) { continue; } if (codecProfileLevel.level > maxCodecProfileLevel) { maxCodecProfileLevel = codecProfileLevel.level; } } format.setInteger("level", maxCodecProfileLevel); try { // 实例化一个支持给定MIME类型的数据输出的编码器 mMediaCodec = MediaCodec.createByCodecName(codecInfo.getName()); // 配置好格式参数 mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); // 请求一个surface用于编码器的输入 Surface encoderSurface = mMediaCodec.createInputSurface(); mVirtualDisplay = mediaProjection.createVirtualDisplay("display", mScreenWidth, mScreenHeight, 1, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, encoderSurface, null, null); mMediaCodec.start(); } catch (Exception e) { } } }//选择编码器级别 @SuppressWarnings("deprecation") private static MediaCodecInfo selectCodec() { int numCodecs = MediaCodecList.getCodecCount(); for (int i = 0; i < numCodecs; i++) { MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i); if (!codecInfo.isEncoder()) { continue; } String[] types = codecInfo.getSupportedTypes(); for (String type : types) { if (MediaFormat.MIMETYPE_VIDEO_AVC.equalsIgnoreCase(type)) { return codecInfo; } } } return null; }private boolean mIsRun = false; //录制虚拟视频 @SuppressWarnings("deprecation") private void recordVirtualDisplay() { while (mIsRun) { try { if (mMediaCodec == null) { return; } ByteBuffer[] encoderOutputBuffers = mMediaCodec.getOutputBuffers(); while (mIsRun & & mIsOnStart) { int encoderStatus = mMediaCodec.dequeueOutputBuffer(mBufferInfo, 0); if (encoderStatus == MediaCodec.INFO_TRY_AGAIN_LATER) { break; } else if (encoderStatus == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) { encoderOutputBuffers = mMediaCodec.getOutputBuffers(); } else if (encoderStatus == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) { } else if (encoderStatus < 0) { // let‘s ignore it } else { if (mBufferInfo.size != 0) { byte[] dataToWrite = new byte[mBufferInfo.size]; ByteBuffer encodedData = https://www.songbingjia.com/android/encoderOutputBuffers[encoderStatus]; encodedData.position(mBufferInfo.offset); encodedData.limit(mBufferInfo.offset + mBufferInfo.size); encodedData.get(dataToWrite, 0, mBufferInfo.size); saveH264DataToFile(dataToWrite); } mMediaCodec.releaseOutputBuffer(encoderStatus, false); } } } catch (Exception e) { } try { Thread.sleep(20); } catch (InterruptedException e) { } } else{ // 非横屏睡眠500毫秒等待 try { Thread.sleep(500); } catch (InterruptedException e) { } } } else{ // app不在前台,睡眠500毫秒,较少cpu的消耗 try { Thread.sleep(500); } catch (InterruptedException e) { } }//保存H264视频到本地 private void saveH264DataToFile(byte[] dataToWrite) { try { mH264DataFile.write(dataToWrite, 0, dataToWrite.length); } catch (FileNotFoundException e) { } catch (IOException e) { } }//释放所有资源 private void release() { if (mMediaCodec != null) { mMediaCodec.stop(); mMediaCodec.release(); mMediaCodec = null; } if (mVirtualDisplay != null) { mVirtualDisplay.release(); mVirtualDisplay = null; } if (mMediaProjection != null) { mMediaProjection.stop(); mMediaProjection = null; } mScreenRecorderThread = null; } }
推荐阅读
- 安卓app开发-03-项目的基本开发步骤
- android 实现屏幕录制功能
- Android Studio无法连接真机的问题()
- 贯穿全栈React Native开发App
- eureka... is an unknown property 在 application.properties 中
- Androidadb调试
- JNI on Chromium for Android
- Android刷机的一般步骤
- Appscan使用说明