[Android] Realization of screen recording function - MediaProjection


不积跬步,无以至千里;不积小流,无以成江海。要沉下心来,诗和远方的路费真的很贵!

Realization of Android screen recording function—MediaProjection

Reference blog: Android realizes screen recording MediaProjection and related exception resolution

Refer to GitHub: The most understandable version of Android screen recording in github

overview

AndroidThere are many ways to realize the screen recording function. The most common way to record here is to use the Androidbuilt-in one apkto realize it, that is, MediaProjectionto use it realize it.

Tools

The realized Androidscreen recording function needs to use some tools: MediaProjection, MediaProjectionManager, MediaRecoder, VirtualDisplay, DisplayMetricsetc.

effect

  • MediaProjection

for screen capture.

  • MediaProjectionManager

for creation MediaProjection.

  • MediaRecoder

For screen recording.

  • VirtualDisplay

Used to create virtual screens.

  • DisplayMetrics

Used to get screen parameters.

process

Access Request

To use the screen recording function, you must request permission for reading, writing and video recording.

  • Static permission application——Manifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission 
android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission 
android:name="android.permission.RECORD_AUDIO"/>
  • Android 6.0The above also requires dynamic application permissions
    //权限检查,连接录屏服务
    public void checkPermission() {
    
    
        //调用检查权限接口进行权限检查
        if ((ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)){
    
    
            //如果没有权限,获取权限
            //调用请求权限接口进行权限申请
            ActivityCompat.requestPermissions(this,new String[]{
    
    
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.RECORD_AUDIO},PERMISSION_REQUEST_CODE);
        }else{
    
    
            //有权限,连接录屏服务,进行录屏
            connectService();
        }
    }
  • After applying for permissions, it is also necessary to determine whether the user agrees
 //没有权限,去请求权限后,需要判断是否请求成功
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    
    
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == PERMISSION_REQUEST_CODE){
    
    
            //请求码相同
            if(grantResults.length != 0 &&
                    ((grantResults[0] != PackageManager.PERMISSION_GRANTED) ||
                    (grantResults[1] != PackageManager.PERMISSION_GRANTED))){
    
    
                //如果结果都存在,但是至少一个没请求成功,弹出提示
                Toast.makeText(MainActivity.this,"请同意必须的应用权限,否则无法正常使用该功能!", Toast.LENGTH_SHORT).show();
            }else if(grantResults.length != 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED &&
                     grantResults[1] == PackageManager.PERMISSION_GRANTED){
    
    
                //如果结果都存在,两个权限都申请成功,连接服务,启动录屏
                connectService();
            }
        }
    }

connection service

important point: Remember to Manifestdeclare the custom one in , otherwise the objects Serviceobtained later will be all .ServiceNULL

  • Manifest.xml
<service android:name=".ScreenRecordService"/>
  • connection service
 //连接服务
    public void connectService(){
    
    
        //通过intent为中介绑定Service,会自动create
        Intent intent = new Intent(this,ScreenRecordService.class);
        //绑定过程连接,选择绑定模式
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);
    }
  • Determine whether the service is connected successfully
  • If successful, send a screen recording request to the system
//连接服务成功与否,具体连接过程
    //调用连接接口,实现连接,回调连接结果
    private ServiceConnection serviceConnection = new ServiceConnection() {
    
    
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    
    
            //服务连接成功,需要通过Binder获取服务,达到Activity和Service通信的目的
            //获取Binder
            ScreenRecordService.ScreenRecordBinder binder = (ScreenRecordService.ScreenRecordBinder) iBinder;
            //通过Binder获取Service
            screenRecordService = binder.getScreenRecordService();
            //获取到服务,初始化录屏管理者
            mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
            //通过管理者,创建录屏请求,通过Intent
            Intent captureIntent = mediaProjectionManager.createScreenCaptureIntent();
            //将请求码作为标识一起发送,调用该接口,需有返回方法
            startActivityForResult(captureIntent,REQUEST_CODE);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
    
    
            //连接失败
            Toast.makeText(MainActivity.this,"录屏服务未连接成功,请重试!",Toast.LENGTH_SHORT).show();
        }
    };
  • After the request, get the returned information
@Override
    //返回方法,获取返回的信息
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    
    
        super.onActivityResult(requestCode, resultCode, data);
        //首先判断请求码是否一致,结果是否ok
        if(requestCode == REQUEST_CODE && resultCode == RESULT_OK){
    
    
            //录屏请求成功,使用工具MediaProjection录屏
            //从发送获得的数据和结果中获取该工具
            mediaProjection = mediaProjectionManager.getMediaProjection(resultCode,data);
            //将该工具给Service,并一起传过去需要录制的屏幕范围的参数
            if(screenRecordService != null){
    
    
                screenRecordService.setMediaProjection(mediaProjection);
                screenRecordService.setConfig(metrics.widthPixels,metrics.heightPixels,metrics.densityDpi);
            }
        }
    }

start recording

  • After getting the returned information, after sending all the parameters, start recording
//服务的两个主要逻辑
    //开始录屏
    public boolean startRecord() {
    
    
        //首先判断是否有录屏工具以及是否在录屏
        if (mediaProjection == null || running) {
    
    
            return false;
        }
        //有录屏工具,没有在录屏,就进行录屏
        //初始化录像机,录音机Recorder
        initRecorder();
        //根据获取的屏幕参数创建虚拟的录屏屏幕
        createVirtualDisplay();
        //本来不加异常也可以,但是这样就不知道是否start成功
        //万一start没有成功,但是running置为true了,就产生了错误也无提示
        //提示开始录屏了,但是并没有工作
        try{
    
    
            //准备工作都完成了,可以开始录屏了
            mediaRecorder.start();
            //标志位改为正在录屏
            running = true;
            return true;
        }catch (Exception e){
    
    
            e.printStackTrace();
            //有异常,start出错,没有开始录屏,弹出提示
            Toast.makeText(this,"开启失败,没有开始录屏",Toast.LENGTH_SHORT).show();
            //标志位变回没有录屏的状态
            running = false;
            return false;
        }
    }
  • Recording requires a VCR
//初始化Recorder录像机
    public void initRecorder() {
    
    
        //新建Recorder
        mediaRecorder = new MediaRecorder();
        //设置录像机的一系列参数
        //设置音频来源
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        //设置视频来源
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        //设置视频格式为mp4
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        //设置视频存储地址,返回的文件夹下的命名为当前系统事件的文件
        videoPath = getSaveDirectory() + System.currentTimeMillis() + ".mp4";
        //保存在该位置
        mediaRecorder.setOutputFile(videoPath);
        //设置视频大小,清晰度
        mediaRecorder.setVideoSize(width, height);
        //设置视频编码为H.264
        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        //设置音频编码
        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        //设置视频码率
        mediaRecorder.setVideoEncodingBitRate(2 * 1920 * 1080);
        mediaRecorder.setVideoFrameRate(18);
        //初始化完成,进入准备阶段,准备被使用
        //截获异常,处理
        try {
    
    
            mediaRecorder.prepare();
        } catch (IOException e) {
    
    
            e.printStackTrace();
            //异常提示
            Toast.makeText(this,
                    "Recorder录像机prepare失败,无法使用,请重新初始化!",
                    Toast.LENGTH_SHORT).show();
        }
    }
  • Recording requires a virtual screen
 public void createVirtualDisplay() {
    
    
        //虚拟屏幕通过MediaProjection获取,传入一系列传过来的参数
        //可能创建时会出错,捕获异常
        try {
    
    
            virtualDisplay = mediaProjection.createVirtualDisplay("VirtualScreen", width, height, dpi,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mediaRecorder.getSurface(), null, null);
        }catch (Exception e){
    
    
            e.printStackTrace();
            Toast.makeText(this,"virtualDisplay创建录屏异常,请退出重试!",Toast.LENGTH_SHORT).show();
        }
    }
  • Recorded video needs to be stored in one location
//获取存储文件夹的位置
    public String getSaveDirectory() {
    
    
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    
    
            //如果确认为视频类型,设置根目录,绝对路径下的自定义文件夹中
            String rootDir = Environment.getExternalStorageDirectory()
                    .getAbsolutePath() + "/" + "录屏文件" + "/";
            //创建该文件夹
            File file = new File(rootDir);
            if (!file.exists()) {
    
    
                //如果该文件夹不存在
                if (!file.mkdirs()) {
    
    
                    //如果没有创建成功
                    return null;
                }
            }
            //创建成功了,返回该目录
            return rootDir;
        } else {
    
    
            //不是音视频文件,不保存,无路径
            return null;
        }
    }

stop recording

  • Stop recording, restore all devices
//停止录屏
    public boolean stopRecord() {
    
    
        if (!running) {
    
    
            //没有在录屏,无法停止
            return false;
        }
        //无论设备是否还原或者有异常,但是确实录屏结束,修改标志位为未录屏
        running = false;
        //本来加不加捕获异常都可以,但是为了用户体验度,加入会更好
        try{
    
    
            //Recorder停止录像,重置还原,以便下一次使用
            mediaRecorder.stop();
            mediaRecorder.reset();
            //释放virtualDisplay的资源
            virtualDisplay.release();
        }catch (Exception e){
    
    
            e.printStackTrace();
            //有异常,保存失败,弹出提示
            Toast.makeText(this,"录屏出现异常,视频保存失败!",Toast.LENGTH_SHORT).show();
            return false;
        }
        //无异常,保存成功
        Toast.makeText(this,"录屏结束,保存成功!",Toast.LENGTH_SHORT).show();
        return true;
    }

Summarize

In general, there are only two files, one Activity, one Service, one is responsible for user operations, and the other is responsible for background implementation logic.

  • MainActivity
package com.example.screencap;

import android.Manifest;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageManager;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Bundle;
import android.os.IBinder;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
    
    //请求码
    private final static int REQUEST_CODE = 101;
    //权限请求码
    private final static int PERMISSION_REQUEST_CODE = 1101;
    //录屏工具
    MediaProjectionManager mediaProjectionManager;
    MediaProjection mediaProjection;
    //开始按钮,停止按钮
    Button btn_start_recorder;
    Button btn_stop_recorder;
    //获取录屏范围参数
    DisplayMetrics metrics;
    //录屏服务
    ScreenRecordService screenRecordService;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    
    
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //实例化按钮
        btn_start_recorder = findViewById(R.id.btn_start_recorder);
        btn_stop_recorder = findViewById(R.id.btn_stop_recorder);
        //点击按钮,请求录屏
        btn_start_recorder.setOnClickListener(this);
        btn_stop_recorder.setOnClickListener(this);
    }

    //权限检查,连接录屏服务
    public void checkPermission() {
    
    
        //调用检查权限接口进行权限检查
        if ((ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) && (ContextCompat.checkSelfPermission(MainActivity.this,
                Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED)){
    
    
            //如果没有权限,获取权限
            //调用请求权限接口进行权限申请
            ActivityCompat.requestPermissions(this,new String[]{
    
    
                    Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.RECORD_AUDIO},PERMISSION_REQUEST_CODE);
        }else{
    
    
            //有权限,连接录屏服务,进行录屏
            connectService();
        }
    }

    //没有权限,去请求权限后,需要判断用户是否同意权限请求
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    
    
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if(requestCode == PERMISSION_REQUEST_CODE){
    
    
            //请求码相同
            if(grantResults.length > 0 &&
                    (grantResults[0] != PackageManager.PERMISSION_GRANTED ||
                    grantResults[1] != PackageManager.PERMISSION_GRANTED)){
    
    
                //如果结果都存在,但是至少一个没请求成功,弹出提示
                Toast.makeText(MainActivity.this,"请同意必须的应用权限,否则无法正常使用该功能!", Toast.LENGTH_SHORT).show();
            }else if(grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED &&
                     grantResults[1] == PackageManager.PERMISSION_GRANTED){
    
    
                //如果结果都存在,两个权限都申请成功,连接服务,启动录屏
                Toast.makeText(MainActivity.this,"权限申请成功,用户同意!",Toast.LENGTH_SHORT).show();
                connectService();
            }
        }
    }

    //连接服务
    public void connectService(){
    
    
        //通过intent为中介绑定Service,会自动create
        Intent intent = new Intent(this,ScreenRecordService.class);
        //绑定过程连接,选择绑定模式
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);
    }

    //连接服务成功与否,具体连接过程
    //调用连接接口,实现连接,回调连接结果
    private ServiceConnection serviceConnection = new ServiceConnection() {
    
    
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
    
    
            //服务连接成功,需要通过Binder获取服务,达到Activity和Service通信的目的
            //获取Binder
            ScreenRecordService.ScreenRecordBinder binder = (ScreenRecordService.ScreenRecordBinder) iBinder;
            //通过Binder获取Service
            screenRecordService = binder.getScreenRecordService();
            //获取到服务,初始化录屏管理者
            mediaProjectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
            //通过管理者,创建录屏请求,通过Intent
            Intent captureIntent = mediaProjectionManager.createScreenCaptureIntent();
            //将请求码作为标识一起发送,调用该接口,需有返回方法
            startActivityForResult(captureIntent,REQUEST_CODE);
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
    
    
            //连接失败
            Toast.makeText(MainActivity.this,"录屏服务未连接成功,请重试!",Toast.LENGTH_SHORT).show();
        }
    };

    @Override
    //返回方法,获取返回的信息
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    
    
        super.onActivityResult(requestCode, resultCode, data);
        //首先判断请求码是否一致,结果是否ok
        if(requestCode == REQUEST_CODE && resultCode == RESULT_OK){
    
    
            //录屏请求成功,使用工具MediaProjection录屏
            //从发送获得的数据和结果中获取该工具
            mediaProjection = mediaProjectionManager.getMediaProjection(resultCode,data);
            //将该工具给Service,并一起传过去需要录制的屏幕范围的参数
            if(screenRecordService != null){
    
    
                screenRecordService.setMediaProjection(mediaProjection);
                //获取录屏屏幕范围参数
                metrics = new DisplayMetrics();
                getWindowManager().getDefaultDisplay().getMetrics(metrics);
                screenRecordService.setConfig(metrics.widthPixels,metrics.heightPixels,metrics.densityDpi);
            }
        }
    }

    @Override
    //点击事件
    public void onClick(View view) {
    
    
        switch (view.getId()) {
    
    
            case R.id.btn_start_recorder:
                //点击请求录屏后,第一件事,检查权限
                checkPermission();
                //参数传过去以后,如果在录制,提示
                if(screenRecordService != null && screenRecordService.isRunning()){
    
    
                    Toast.makeText(MainActivity.this,"当前正在录屏,请不要重复点击哦!",Toast.LENGTH_SHORT).show();
                } else if(screenRecordService != null && !screenRecordService.isRunning()){
    
    
                    //没有录制,就开始录制,弹出提示,返回主界面开始录制
                    screenRecordService.startRecord();
                    //返回主界面开始录制
                    setToBackground();
                } else if(screenRecordService == null){
    
    
                    connectService();
                }
                break;
            case R.id.btn_stop_recorder:
                if(screenRecordService != null && !screenRecordService.isRunning()){
    
    
                    //没有在录屏,无法停止,弹出提示
                    Toast.makeText(MainActivity.this,"您还没有录屏,无法停止,请先开始录屏吧!",Toast.LENGTH_SHORT).show();
                }else if(screenRecordService != null && screenRecordService.isRunning()){
    
    
                    //正在录屏,点击停止,停止录屏
                    screenRecordService.stopRecord();
                }
                break;
        }
    }

    //返回主界面开始录屏,相当于home键
    private void setToBackground(){
    
    
        //主页面的Intent
        Intent home = new Intent(Intent.ACTION_MAIN);
        //设置清除栈顶的启动模式
        home.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        //匹配符号
        home.addCategory(Intent.CATEGORY_HOME);
        //转换界面,隐式匹配,显示调用
        startActivity(home);
    }

    //当应用结束的时候,需要解除绑定服务,防止造成内存泄漏
    @Override
    protected void onDestroy() {
    
    
        super.onDestroy();
        unbindService(serviceConnection);
    }
}
  • activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/btn_start_recorder"
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:layout_marginTop="200dp"
        android:background="@drawable/btn_start_recorder"
        android:text="Start Recorder"
        android:textSize="25dp" />

    <Button
        android:id="@+id/btn_stop_recorder"
        android:layout_width="200dp"
        android:layout_height="100dp"
        android:layout_gravity="center"
        android:layout_marginTop="50dp"
        android:background="@drawable/btn_stop_recorder"
        android:text="Stop Recorder"
        android:textSize="25dp" />

</LinearLayout>
  • ScreenRecordService
package com.example.screencap;

import android.app.Service;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.os.Binder;
import android.os.Environment;
import android.os.HandlerThread;
import android.os.IBinder;
import android.widget.Toast;

import androidx.annotation.Nullable;

import java.io.File;
import java.io.IOException;

public class ScreenRecordService extends Service {
    
    
    //录屏工具MediaProjection
    private MediaProjection mediaProjection;
    //录像机MediaRecorder
    private MediaRecorder mediaRecorder;
    //用于录屏的虚拟屏幕
    private VirtualDisplay virtualDisplay;
    //声明录制屏幕的宽高像素
    private int width;
    private int height;
//    private int width = 720;
//    private int height = 1080;
    private int dpi;
    //标志,判断是否正在录屏
    private boolean running;
    //声明视频存储路径
    private String videoPath = "";

//    @Override
//    public void onCreate() {
    
    
//        super.onCreate();
        HandlerThread serviceThread = new HandlerThread("service_thread", android.os.Process.THREAD_PRIORITY_BACKGROUND);
        serviceThread.start();
//        running = false;
//    }


    @Override
    public void onCreate() {
    
    
        super.onCreate();
    }

//    @Override
//    public int onStartCommand(Intent intent, int flags, int startId) {
    
    
//        return START_STICKY;
//    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
    
    
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
    
    
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
    
    
        return super.onUnbind(intent);
    }

    //返回的Binder
    public class ScreenRecordBinder extends Binder {
    
    
        //返回Service的方法
        public ScreenRecordService getScreenRecordService() {
    
    
            return ScreenRecordService.this;
        }
    }

    @Nullable
    @Override
    //返回一个Binder用于通信,需要一个获取Service的方法
    public IBinder onBind(Intent intent) {
    
    
        return new ScreenRecordBinder();
    }

    //设置录屏工具MediaProjection
    public void setMediaProjection(MediaProjection projection) {
    
    
        mediaProjection = projection;
    }

    //设置需要录制的屏幕参数
    public void setConfig(int width, int height, int dpi) {
    
    
        this.width = width;
        this.height = height;
        this.dpi = dpi;
    }

    //返回判断,判断其是否在录屏
    public boolean isRunning() {
    
    
        return running;
    }

    //服务的两个主要逻辑
    //开始录屏
    public boolean startRecord() {
    
    
        //首先判断是否有录屏工具以及是否在录屏
        if (mediaProjection == null || running) {
    
    
            return false;
        }
        //有录屏工具,没有在录屏,就进行录屏
        //初始化录像机,录音机Recorder
        initRecorder();
        //根据获取的屏幕参数创建虚拟的录屏屏幕
        createVirtualDisplay();
        //本来不加异常也可以,但是这样就不知道是否start成功
        //万一start没有成功,但是running置为true了,就产生了错误也无提示
        //提示开始录屏了,但是并没有工作
        try{
    
    
            //准备工作都完成了,可以开始录屏了
            mediaRecorder.start();
            //标志位改为正在录屏
            running = true;
            return true;
        }catch (Exception e){
    
    
            e.printStackTrace();
            //有异常,start出错,没有开始录屏,弹出提示
            Toast.makeText(this,"开启失败,没有开始录屏",Toast.LENGTH_SHORT).show();
            //标志位变回没有录屏的状态
            running = false;
            return false;
        }
    }

    //停止录屏
    public boolean stopRecord() {
    
    
        if (!running) {
    
    
            //没有在录屏,无法停止
            return false;
        }
        //无论设备是否还原或者有异常,但是确实录屏结束,修改标志位为未录屏
        running = false;
        //本来加不加捕获异常都可以,但是为了用户体验度,加入会更好
        try{
    
    
            //Recorder停止录像,重置还原,以便下一次使用
            mediaRecorder.stop();
            mediaRecorder.reset();
            //释放virtualDisplay的资源
            virtualDisplay.release();
        }catch (Exception e){
    
    
            e.printStackTrace();
            //有异常,保存失败,弹出提示
            Toast.makeText(this,"录屏出现异常,视频保存失败!",Toast.LENGTH_SHORT).show();
            return false;
        }
        //无异常,保存成功
        Toast.makeText(this,"录屏结束,保存成功!",Toast.LENGTH_SHORT).show();
        return true;
    }

    //初始化Recorder录像机
    public void initRecorder() {
    
    
        //新建Recorder
        mediaRecorder = new MediaRecorder();
        //设置录像机的一系列参数
        //设置音频来源
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        //设置视频来源
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        //设置视频格式为mp4
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        //设置视频存储地址,返回的文件夹下的命名为当前系统事件的文件
        videoPath = getSaveDirectory() + System.currentTimeMillis() + ".mp4";
        //保存在该位置
        mediaRecorder.setOutputFile(videoPath);
        //设置视频大小,清晰度
        mediaRecorder.setVideoSize(width, height);
        //设置视频编码为H.264
        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        //设置音频编码
        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
        //设置视频码率
        mediaRecorder.setVideoEncodingBitRate(2 * 1920 * 1080);
        mediaRecorder.setVideoFrameRate(18);
        //初始化完成,进入准备阶段,准备被使用
        //截获异常,处理
        try {
    
    
            mediaRecorder.prepare();
        } catch (IOException e) {
    
    
            e.printStackTrace();
            //异常提示
            Toast.makeText(this,
                    "Recorder录像机prepare失败,无法使用,请重新初始化!",
                    Toast.LENGTH_SHORT).show();
        }
    }

    public void createVirtualDisplay() {
    
    
        //虚拟屏幕通过MediaProjection获取,传入一系列传过来的参数
        //可能创建时会出错,捕获异常
        try {
    
    
            virtualDisplay = mediaProjection.createVirtualDisplay("VirtualScreen", width, height, dpi,
                    DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mediaRecorder.getSurface(), null, null);
        }catch (Exception e){
    
    
            e.printStackTrace();
            Toast.makeText(this,"virtualDisplay创建录屏异常,请退出重试!",Toast.LENGTH_SHORT).show();
        }
    }


    //获取存储文件夹的位置
    public String getSaveDirectory() {
    
    
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    
    
            //如果确认为视频类型,设置根目录,绝对路径下的自定义文件夹中
            String rootDir = Environment.getExternalStorageDirectory()
                    .getAbsolutePath() + "/" + "录屏文件" + "/";
            //创建该文件夹
            File file = new File(rootDir);
            if (!file.exists()) {
    
    
                //如果该文件夹不存在
                if (!file.mkdirs()) {
    
    
                    //如果没有创建成功
                    return null;
                }
            }
            //创建成功了,返回该目录
            return rootDir;
        } else {
    
    
            //不是音视频文件,不保存,无路径
            return null;
        }
    }
}

Guess you like

Origin blog.csdn.net/qq_46546793/article/details/123279152