发布时间:2025-12-10 23:07:57 浏览次数:1
Android是一种基于Linux内核的自由及开放源代码的操作系统,主要使用于移动设备,如智能手机和平板电脑,由美国Google公司和开放手机联盟领导及开发。
本文总结三种用于安卓录屏的解决方案:
adb shell命令screenrecord
MediaRecorder, MediaProjection
MediaProjection , MediaCodec和MediaMuxer
screenrecord命令
screenrecord是一个shell命令,支持Android4.4(API level 19)以上,录制的视频格式为mp4 ,存放到手机sd卡里,默认录制时间为180s
adb shell screenrecord --size 1280*720 --bit-rate 6000000 --time-limit 30 /sdcard/demo.mp4
--size 指定视频分辨率;
--bit-rate 指定视频比特率,默认为4M,该值越小,保存的视频文件越小;
--time-limit 指定录制时长,若设定大于180,命令不会被执行;
MediaRecorder
MediaProjection是Android5.0后才开放的屏幕采集接口,通过系统级服务MediaProjectionManager进行管理。
录屏过程可以分成两个部分,即通过MediaProjectionManage申请录屏权限,用户允许后开始录制屏幕;然后通过MediaRecorder对音视频数据进行处理。
获取MediaProjectionManager实例
MediaProjectionManager mProjectionManager = (MediaProjectionManager) getSystemService("media_projection");
申请权限
Intent captureIntent = mProjectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent, LOCAL_REQUEST_CODE);
createScreenCaptureIntent()这个方法会返回一个intent,你可以通过startActivityForResult方法来传递这个intent,为了能开始屏幕捕捉,activity会提示用户是否允许屏幕捕捉(为了防止开发者做一个木马,来捕获用户私人信息),你可以通过getMediaProjection来获取屏幕捕捉的结果。
在onActivityResult中获取结果
@OverridepublicvoidonActivityResult(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;//60fpsprivateVirtualDisplaymVirtualDisplay;publicMediaRecordService(intwidth,intheight,intbitrate,intdpi,MediaProjectionmp,StringdstPath){mWidth=width;mHeight=height;mBitRate=bitrate;mDpi=dpi;mMediaProjection=mp;mDstPath=dstPath;}@Overridepublicvoidrun(){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;//parametersfortheencoderprivatestaticfinalStringMIME_TYPE="video/avc";//H.264Advanced//VideoCodingprivatestaticfinalintFRAME_RATE=30;//30fpsprivatestaticfinalintIFRAME_INTERVAL=10;//10secondsbetween//I-framesprivatestaticfinalintTIMEOUT_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);}@Overridepublicvoidrun(){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{//wait10msThread.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//onceif(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方法。