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 = 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;
}
}







  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值