Android录屏 MediaRecorder介绍

Android录屏 MediaRecorder介绍

Android录屏的三种方案

1、adb shell命令screenrecord
2、MediaRecorder, MediaProjection
3、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

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

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

你可能感兴趣的:(android)