Android录屏 MediaRecorder介绍

Android录屏的三种方案

1、adb shell命令screenrecord2、MediaRecorder, MediaProjection3、MediaCodec和MediaMuxer, MediaProjection , 

一、screenrecord命令

screenrecord是一个shell命令,支持Android4.4(API level 19)以上,
录制的视频格式为mp4 ,存放到手机sd卡里,默认录制时间为180s

adb shell screenrecord --size 1280x720 --bit-rate 6000000 --time-limit 30 /sdcard/demo.mp4 --size 指定视频分辨率,根据手机情况决定 --bit-rate 指定视频比特率,默认为4M,该值越小,保存的视频文件越小; --time-limit 指定录制时长,若设定大于180,命令不会被执行;

并不是所以手机都执行screenrecord命令,部分手机不识别。
我在几款华为手机就没执行成功报错:
/system/bin/sh: screenrecord: inaccessible or not found

二、 MediaRecorder

MediaProjection是Android5.0后才开放的屏幕采集接口,通过系统级服务MediaProjectionManager进行管理。

这里先整体说一下屏幕录制的流程,不然看起来费劲。

1、通过startActivityForResult(Intent intent)判断是否录屏授权的Activity其中intent对象就需要MediaProjectionManager.createScreenCaptureIntent();获取2、在onActivityResult回调方法中做具体录屏工作比如:创建MediaRecorder,设置MP4文件路径创建VirtualDisplay,设置屏幕相关参数如果不在onActivityResult回调中执行会有问题。3、开始录屏MediaRecorder.start()4、停止录屏MediaRecorder.reset();MediaRecorder.release();

录屏过程用到录音权限和数据读写权限。

三、MediaCodec和MediaMuxer

MediaCodec提供对音视频压缩编码和解码功能,MediaMuxer可以将音视频混合生成多媒体文件,生成MP4文件。

这个录屏的方式和MediaRecorder是类似的,只是流程第二部有点不同,这里不做介绍。

详情可以参考:https://www.jianshu.com/p/8b313692ac85

四、MediaRecorder项目示例的主要代码

这里只做了录制和停止录制,没有做相关适配,比如横竖屏切换后尺寸变化。

简单效果:

生成的MP4文件会在sdcard目录下,并且以录屏时间为文件名。

1、MainActivity

package com.liwenzhi.screen;import android.Manifest;import android.app.Activity;import android.content.Context;import android.content.Intent;import android.content.pm.PackageManager;import android.media.projection.MediaProjection;import android.media.projection.MediaProjectionManager;import android.os.Handler;import android.os.Message;import android.support.v4.app.ActivityCompat;import android.support.v4.content.ContextCompat;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;import android.widget.Toast;import java.io.File;public class MainActivity extends AppCompatActivity implements View.OnClickListener {    String TAG = "MainActivity";    MediaProjectionManager mProjectionManager;    MediaRecordService mediaRecord;    int displayWidth = 1280;    int displayHeight = 1920;    Button btn_start_record;    Button btn_stop_record;    TextView tv_time;    int REQUEST_CODE_PERMISSIONS = 99;    int REQUEST_CODE_SCREEN = 100;    //录音权限和数据读写权限    String[] permissions = new String[]{Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE};    boolean mPassPermissions = true;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();        initData();        initEvent();    }    private void initView() {        btn_start_record = findViewById(R.id.btn_start_record);        btn_stop_record = findViewById(R.id.btn_stop_record);        tv_time = findViewById(R.id.tv_time);    }    private void initData() {        //权限申请        //逐个判断你要的权限是否已经通过        judgePermissions();        if (!mPassPermissions) {            ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE_PERMISSIONS);        }    }    private void initEvent() {        btn_start_record.setOnClickListener(this);        btn_stop_record.setOnClickListener(this);    }    // 申请权限后的回调    // 1、录音和读写    // 2、录屏startActivityForResult后的回调    @Override    protected void onActivityResult(int requestCode, int resultCode, Intent data) {        super.onActivityResult(requestCode, resultCode, data);        //录音和读写权限        if (requestCode == REQUEST_CODE_PERMISSIONS) {            if (resultCode != Activity.RESULT_OK) {                mPassPermissions = false;            }        } else            // 录屏权限            if (requestCode == REQUEST_CODE_SCREEN) {                if (resultCode == Activity.RESULT_OK) {                    try {                        // mediaProjection 如果不在权限申请中回调,获取到的对象为空                        MediaProjection mediaProjection = mProjectionManager.getMediaProjection(resultCode, data);                        if (mediaProjection == null) {                            Log.e(TAG, "media projection is null");                            return;                        }                        File file = new File("/sdcard/" + MyTimeUtils.getTimeString("yyyy-MM-dd_HH:mm:ss") + ".mp4");  //录屏生成文件                        if (!file.exists()) {                            file.createNewFile();                        }                        mediaRecord = new MediaRecordService(displayWidth, displayHeight, 6000000, 1,                                mediaProjection, file.getAbsolutePath());                        mediaRecord.start();                        second = 0;                        mHandler.sendEmptyMessageDelayed(10, 1000);                    } catch (Exception e) {                        e.printStackTrace();                    }                } else {                    Toast.makeText(MainActivity.this, "录屏失败", Toast.LENGTH_LONG).show();                }            }    }    @Override    public void onBackPressed() {        //super.onBackPressed();    }    @Override    public void onClick(View v) {        switch (v.getId()) {            case R.id.btn_start_record:                startRecord();                break;            case R.id.btn_stop_record:                stopRecord();                break;        }    }    // 开始录屏    private void startRecord() {        judgePermissions();        if (!mPassPermissions) {            Toast.makeText(this, "基础权限没通过!", Toast.LENGTH_LONG).show();            return;        }        mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);        if (mProjectionManager != null) {            Intent intent = mProjectionManager.createScreenCaptureIntent();            PackageManager packageManager = getPackageManager();            if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {                //存在录屏授权的Activity                startActivityForResult(intent, REQUEST_CODE_SCREEN);            } else {                Toast.makeText(this, "没有录屏权限!", Toast.LENGTH_LONG).show();            }        }    }    // 停止录屏    private void stopRecord() {        mediaRecord.release();        mHandler.removeMessages(10);    }    int second;    // 计算时间    Handler mHandler = new Handler() {        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what) {                case 10:                    second++;                    tv_time.setText(second + "");                    mHandler.sendEmptyMessageDelayed(10, 1000);                    break;            }        }    };    //判断每个权限是否通过    private void judgePermissions() {        boolean permission = true;        for (int i = 0; i < permissions.length; i++) {            if (ContextCompat.checkSelfPermission(this, permissions[i]) != PackageManager.PERMISSION_GRANTED) {                // 未授予的权限                permission = false;            }        }        mPassPermissions = permission;    }}

2、MediaRecordService

package com.liwenzhi.screen;import android.hardware.display.DisplayManager;import android.hardware.display.VirtualDisplay;import android.media.MediaRecorder;import android.media.projection.MediaProjection;import android.util.Log;public class MediaRecordService extends Thread {    private static final String TAG = "MediaRecordService";    private int mWidth;    private int mHeight;    private int mBitRate;    private int mDpi;    private String mDstPath;    private MediaRecorder mMediaRecorder;    private MediaProjection mMediaProjection;    private static final int FRAME_RATE = 60; // 60 fps    private VirtualDisplay mVirtualDisplay;    public MediaRecordService(int width, int height, int bitrate, int dpi, MediaProjection mp, String dstPath) {        mWidth = width;        mHeight = height;        mBitRate = bitrate;        mDpi = dpi;        mMediaProjection = mp;        mDstPath = dstPath;    }    @Override    public void run() {        try {            initMediaRecorder();            //在mediarecorder.prepare()方法后调用            mVirtualDisplay = mMediaProjection.createVirtualDisplay(TAG + "-display", mWidth, mHeight, mDpi,                    DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mMediaRecorder.getSurface(), null, null);            Log.i(TAG, "created virtual display: " + mVirtualDisplay);            mMediaRecorder.start();            Log.i(TAG, "mediarecorder start");        } catch (Exception e) {            e.printStackTrace();        }    }    /**     * 初始化MediaRecorder     *     * @return     */    public void initMediaRecorder() {        mMediaRecorder = new MediaRecorder();        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 (Exception e) {            e.printStackTrace();        }        Log.i(TAG, "media recorder" + mBitRate + "kps");    }    public void release() {        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");    }}

3、MyTimeUtils

package com.liwenzhi.screen;import java.text.SimpleDateFormat;import java.util.Date;/** * a Utils for time * 

* identify the following letter: :"yyyy-MM-dd DD HH:mm:ss SSS" * * method1: long getTimeLong() * * method2: int getTimeInt(String filter) * * method3: String getTimeString() * * method4: String getTimeString(long time) * * method5: String getTimeString(long time, String filter) * * method6: String getTimeString( String filter) */public class MyTimeUtils { public static long getTimeLong() { return System.currentTimeMillis(); } public static int getTimeInt(String filter) { SimpleDateFormat format = new SimpleDateFormat(filter); String time = format.format(new Date()); return Integer.parseInt(time); } public static final String getTimeString() { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return format.format(new Date(getTimeLong())); } public static final String getTimeString(long time) { SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return format.format(new Date(time)); } public static final String getTimeString(long time, String filter) { SimpleDateFormat format = new SimpleDateFormat(filter); return format.format(new Date(time)); } public static final String getTimeString(String filter) { SimpleDateFormat format = new SimpleDateFormat(filter); return format.format(new Date(getTimeLong())); } public static Long getTimeLong(String filter, String date) { try { SimpleDateFormat format = new SimpleDateFormat(filter); Date dateTime = format.parse(date); return dateTime.getTime(); } catch (Exception e) { e.printStackTrace(); } return 0L; }}

测试apk和项目源码下载:
https://download.csdn.net/download/wenzhi20102321/12262262

这个项目只是简单录屏,如果要做得好,
最好是正在通知栏/悬浮框里面控制停止,
并且可以按退出键回到主界面,
要用到服务来控制录屏屏幕的开始和停止。

共勉:时间总会过去,多做一下有意义的事情。

更多相关文章

  1. Android的所有权限说明
  2. android 权限汇集
  3. android 模拟器获取root权限
  4. android Mainifest权限设置清单
  5. 获得Android(安卓)Linux系统增删文件的权限
  6. android 笔记 --- Android应用程序的权限列表
  7. Android(安卓)UI LinearLayout权限级别与TableLayout混合使用,
  8. Android(安卓)4.4 SD卡文件读写变化
  9. 2010.12.16(2)——— android 关于录制视频时方向的问题

随机推荐

  1. android(25)(android下实现多线程断点下载)
  2. Android: How to download the latest zi
  3. 单独编译android模块
  4. Android(安卓)Studio新建项目Rendering P
  5. 笔记:Android(安卓)Studio发布项目到Bintr
  6. Android编译系统详解(三)
  7. android studio3导入opencv4人脸识别例程
  8. Android(安卓)Socket Posix 实现
  9. Android(安卓)4.4报错,Android(安卓)7.0运
  10. Android传感器