Android录屏的三种解决方案
本文总结三种用于安卓录屏的解决方案:
adbshell命令screenrecord
MediaRecorder,MediaProjection
MediaProjection,MediaCodec和MediaMuxer
screenrecord命令
screenrecord是一个shell命令,支持Android4.4(APIlevel19)以上,录制的视频格式为mp4,存放到手机sd卡里,默认录制时间为180s
adbshellscreenrecord--size1280*720--bit-rate6000000--time-limit30/sdcard/demo.mp4
--size指定视频分辨率;
--bit-rate指定视频比特率,默认为4M,该值越小,保存的视频文件越小;
--time-limit指定录制时长,若设定大于180,命令不会被执行;
MediaRecorder
MediaProjection是Android5.0后才开放的屏幕采集接口,通过系统级服务MediaProjectionManager进行管理。
录屏过程可以分成两个部分,即通过MediaProjectionManage申请录屏权限,用户允许后开始录制屏幕;然后通过MediaRecorder对音视频数据进行处理。
获取MediaProjectionManager实例
MediaProjectionManagermProjectionManager=(MediaProjectionManager)getSystemService("media_projection");
申请权限
IntentcaptureIntent=mProjectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent,LOCAL_REQUEST_CODE);
createScreenCaptureIntent()这个方法会返回一个intent,你可以通过startActivityForResult方法来传递这个intent,为了能开始屏幕捕捉,activity会提示用户是否允许屏幕捕捉(为了防止开发者做一个木马,来捕获用户私人信息),你可以通过getMediaProjection来获取屏幕捕捉的结果。
在onActivityResult中获取结果
@Override
publicvoidonActivityResult(intrequestCode,intresultCode,Intentdata){
MediaProjectionmediaProjection=mProjectionManager.getMediaProjection(resultCode,data);
if(mediaProjection==null){
Log.e(TAG,"mediaprojectionisnull");
return;
}
Filefile=newFile("xx.mp4");//录屏生成文件
mediaRecord=newMediaRecordService(displayWidth,displayHeight,6000000,1,
mediaProjection,file.getAbsolutePath());
mediaRecord.start();
}
创建MediaRecorder进程
packagecom.unionpay.service;
importandroid.hardware.display.DisplayManager;
importandroid.hardware.display.VirtualDisplay;
importandroid.media.MediaRecorder;
importandroid.media.projection.MediaProjection;
importandroid.util.Log;
publicclassMediaRecordServiceextendsThread{
privatestaticfinalStringTAG="MediaRecordService";
privateintmWidth;
privateintmHeight;
privateintmBitRate;
privateintmDpi;
privateStringmDstPath;
privateMediaRecordermMediaRecorder;
privateMediaProjectionmMediaProjection;
privatestaticfinalintFRAME_RATE=60;//60fps
privateVirtualDisplaymVirtualDisplay;
publicMediaRecordService(intwidth,intheight,intbitrate,intdpi,MediaProjectionmp,StringdstPath){
mWidth=width;
mHeight=height;
mBitRate=bitrate;
mDpi=dpi;
mMediaProjection=mp;
mDstPath=dstPath;
}
@Override
publicvoidrun(){
try{
initMediaRecorder();
//在mediarecorder.prepare()方法后调用
mVirtualDisplay=mMediaProjection.createVirtualDisplay(TAG+"-display",mWidth,mHeight,mDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,mMediaRecorder.getSurface(),null,null);
Log.i(TAG,"createdvirtualdisplay:"+mVirtualDisplay);
mMediaRecorder.start();
Log.i(TAG,"mediarecorderstart");
}catch(Exceptione){
e.printStackTrace();
}
}
/**
*初始化MediaRecorder
*
*@return
*/
publicvoidinitMediaRecorder(){
mMediaRecorder=newMediaRecorder();
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setOutputFile(mDstPath);
mMediaRecorder.setVideoSize(mWidth,mHeight);
mMediaRecorder.setVideoFrameRate(FRAME_RATE);
mMediaRecorder.setVideoEncodingBitRate(mBitRate);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
try{
mMediaRecorder.prepare();
}catch(Exceptione){
e.printStackTrace();
}
Log.i(TAG,"mediarecorder"+mBitRate+"kps");
}
publicvoidrelease(){
if(mVirtualDisplay!=null){
mVirtualDisplay.release();
mVirtualDisplay=null;
}
if(mMediaRecorder!=null){
mMediaRecorder.setOnErrorListener(null);
mMediaProjection.stop();
mMediaRecorder.reset();
mMediaRecorder.release();
}
if(mMediaProjection!=null){
mMediaProjection.stop();
mMediaProjection=null;
}
Log.i(TAG,"release");
}
}
MediaCodec与MediaMuxer
MediaCodec提供对音视频压缩编码和解码功能,MediaMuxer可以将音视频混合生成多媒体文件,生成MP4文件。
与MediaRecorder类似,都需要先通过MediaProjectionManager获取录屏权限,在回调中进行屏幕数据处理。
这里创建另一个进程:
packagecom.unionpay.service;
importjava.io.IOException;
importjava.nio.ByteBuffer;
importjava.util.concurrent.atomic.AtomicBoolean;
importandroid.hardware.display.DisplayManager;
importandroid.hardware.display.VirtualDisplay;
importandroid.media.MediaCodec;
importandroid.media.MediaCodecInfo;
importandroid.media.MediaFormat;
importandroid.media.MediaMuxer;
importandroid.media.projection.MediaProjection;
importandroid.util.Log;
importandroid.view.Surface;
publicclassScreenRecordServiceextendsThread{
privatestaticfinalStringTAG="ScreenRecordService";
privateintmWidth;
privateintmHeight;
privateintmBitRate;
privateintmDpi;
privateStringmDstPath;
privateMediaProjectionmMediaProjection;
//parametersfortheencoder
privatestaticfinalStringMIME_TYPE="video/avc";//H.264Advanced
//VideoCoding
privatestaticfinalintFRAME_RATE=30;//30fps
privatestaticfinalintIFRAME_INTERVAL=10;//10secondsbetween
//I-frames
privatestaticfinalintTIMEOUT_US=10000;
privateMediaCodecmEncoder;
privateSurfacemSurface;
privateMediaMuxermMuxer;
privatebooleanmMuxerStarted=false;
privateintmVideoTrackIndex=-1;
privateAtomicBooleanmQuit=newAtomicBoolean(false);
privateMediaCodec.BufferInfomBufferInfo=newMediaCodec.BufferInfo();
privateVirtualDisplaymVirtualDisplay;
publicScreenRecordService(intwidth,intheight,intbitrate,intdpi,MediaProjectionmp,StringdstPath){
super(TAG);
mWidth=width;
mHeight=height;
mBitRate=bitrate;
mDpi=dpi;
mMediaProjection=mp;
mDstPath=dstPath;
}
/**
*stoptask
*/
publicfinalvoidquit(){
mQuit.set(true);
}
@Override
publicvoidrun(){
try{
try{
prepareEncoder();
mMuxer=newMediaMuxer(mDstPath,MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
}catch(IOExceptione){
thrownewRuntimeException(e);
}
mVirtualDisplay=mMediaProjection.createVirtualDisplay(TAG+"-display",mWidth,mHeight,mDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,mSurface,null,null);
Log.d(TAG,"createdvirtualdisplay:"+mVirtualDisplay);
recordVirtualDisplay();
}finally{
release();
}
}
privatevoidrecordVirtualDisplay(){
while(!mQuit.get()){
intindex=mEncoder.dequeueOutputBuffer(mBufferInfo,TIMEOUT_US);
//Log.i(TAG,"dequeueoutputbufferindex="+index);
if(index==MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
//后续输出格式变化
resetOutputFormat();
}elseif(index==MediaCodec.INFO_TRY_AGAIN_LATER){
//请求超时
//Log.d(TAG,"retrievingbufferstimeout!");
try{
//wait10ms
Thread.sleep(10);
}catch(InterruptedExceptione){
}
}elseif(index>=0){
//有效输出
if(!mMuxerStarted){
thrownewIllegalStateException("MediaMuxerdosenotcalladdTrack(format)");
}
encodeToVideoTrack(index);
mEncoder.releaseOutputBuffer(index,false);
}
}
}
/**
*硬解码获取实时帧数据并写入mp4文件
*
*@paramindex
*/
privatevoidencodeToVideoTrack(intindex){
//获取到的实时帧视频数据
ByteBufferencodedData=mEncoder.getOutputBuffer(index);
if((mBufferInfo.flags&MediaCodec.BUFFER_FLAG_CODEC_CONFIG)!=0){
//Thecodecconfigdatawaspulledoutandfedtothemuxer
//whenwegot
//theINFO_OUTPUT_FORMAT_CHANGEDstatus.
//Ignoreit.
Log.d(TAG,"ignoringBUFFER_FLAG_CODEC_CONFIG");
mBufferInfo.size=0;
}
if(mBufferInfo.size==0){
Log.d(TAG,"info.size==0,dropit.");
encodedData=null;
}else{
//Log.d(TAG,"gotbuffer,info:size="+mBufferInfo.size+",presentationTimeUs="
//+mBufferInfo.presentationTimeUs+",offset="+mBufferInfo.offset);
}
if(encodedData!=null){
mMuxer.writeSampleData(mVideoTrackIndex,encodedData,mBufferInfo);
}
}
privatevoidresetOutputFormat(){
//shouldhappenbeforereceivingbuffers,andshouldonlyhappen
//once
if(mMuxerStarted){
thrownewIllegalStateException("outputformatalreadychanged!");
}
MediaFormatnewFormat=mEncoder.getOutputFormat();
mVideoTrackIndex=mMuxer.addTrack(newFormat);
mMuxer.start();
mMuxerStarted=true;
Log.i(TAG,"startedmediamuxer,videoIndex="+mVideoTrackIndex);
}
privatevoidprepareEncoder()throwsIOException{
MediaFormatformat=MediaFormat.createVideoFormat(MIME_TYPE,mWidth,mHeight);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE,mBitRate);
format.setInteger(MediaFormat.KEY_FRAME_RATE,FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,IFRAME_INTERVAL);
Log.d(TAG,"createdvideoformat:"+format);
mEncoder=MediaCodec.createEncoderByType(MIME_TYPE);
mEncoder.configure(format,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
mSurface=mEncoder.createInputSurface();
Log.d(TAG,"createdinputsurface:"+mSurface);
mEncoder.start();
}
privatevoidrelease(){
if(mEncoder!=null){
mEncoder.stop();
mEncoder.release();
mEncoder=null;
}
if(mVirtualDisplay!=null){
mVirtualDisplay.release();
}
if(mMediaProjection!=null){
mMediaProjection.stop();
}
if(mMuxer!=null){
mMuxer.stop();
mMuxer.release();
mMuxer=null;
}
}
}
该进程只实现了视频录制,调用该进程只需修改主进程中的onActivityResult方法。
总结
MediaProjection似乎只有在屏幕发生变化时才传输,因此录屏推流的画面显得不够流畅
到此这篇关于Android录屏的三种方案的文章就介绍到这了,更多相关Android录屏的三种方案内容请搜索毛票票以前的文章或继续浏览下面的相关文章希望大家以后多多支持毛票票!