Recording of video files in Android

Before we talked about audio recording and playback, now we talk about video recording. In fact, the video recording still uses MediaRecorder, and cooperates with Camera to operate. So here we explain in detail the practice of video recording. As for video playback, due to a series of issues related to full-screen switching, life cycle management, etc., I will not write a blog here. Interested students can find my framework in my resources, which contains video playback cases.

Well, we start to record the video learning.

1. Common methods of media player MediaRecorder (common for recording and recording)

  • reset: Reset the recording resource.
  • prepare: prepare to record.
  • start: Start recording.
  • stop: End recording.
  • release: Release recording resources.
  • setOnErrorListener: Set the error listener. Can monitor server exceptions and unknown error events. Need to implement the onError method of the interface MediaRecorder.OnErrorListener.
  • setOnInfoListener: Set the information listener. You can monitor the recording end event, including reaching the recording duration or reaching the recording size. Need to implement the onInfo method of the interface MediaRecorder.OnInfoListener.
  • setMaxDuration: Set the maximum recording duration, in milliseconds.
  • setMaxFileSize: Set the maximum recordable file size, in bytes.
  • setOutputFile: Set the path of the output file.

2. Common methods used by MeidaRecorder for recording

  • setCamera: Set the camera object.
  • setPreviewDisplay: Set the preview interface. The preview interface object can be obtained through the getSurface method of the SurfaceHolder object.
  • setOrientationHint: Set the angle of the preview. Set to 90 like taking photos, which means that the interface is rotated 90 degrees from horizontal to vertical.
  • setVideoSource: Set the video source. Generally use VideoSource.CAMERA to represent the camera.
  • setOutputFormat: Set the media output format. The value of the media output format is shown in Table 1.
Table 1
The output format of the OutputFormat class Format classification extension name Format description
AMR_NB Audio .amr Narrowband format
AMR_WB Audio .amr Wideband format
AAC_ADTS Audio .aac Advanced audio transport stream format
MPEG_4 video .mp4 MPEG4 format
THREE_GPP video .3gp 3GP format
  • setVideoEncoder: Set the video encoder. Generally, VideoEncoder.MPEG_4_SP is used to represent MPEG4 encoding. This method is called after the setOutputFormat method, otherwise an error java.lang.IllegalStateException will be reported.
  • setVideoSize: Set the resolution of the video.
  • setVideoFrameRate: Set the number of video frames recorded per second. The larger the video, the more coherent the video, of course, the larger the final video file.
  • setVideoEncodingBitRate: Set the number of bytes of video recorded per second. The larger the video, the clearer the video, just set one of setVideoFrameRate and setVideoEncodingBitRate.

3. Code example

Access Request

<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.FLASHLIGHT"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>

Layout code activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <FrameLayout
        android:id="@+id/fr_root"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <Button
        android:id="@+id/bt_record"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="录制视频"/>
</RelativeLayout>

MainActivity.java

public class MainActivity extends WaterPermissionActivity implements MediaRecorder.OnErrorListener, MediaRecorder.OnInfoListener {

    private TextView bt_record;
    private FrameLayout fr_root;
    private SurfaceHolder mHolder; // 声明一个表面持有者对象
    private Camera mCamera; // 声明一个相机对象
    private MediaRecorder mMediaRecorder;
    private String moviePath;//视频路径

    @Override
    protected MvcBaseModel getModelImp() {
        return null;
    }

    @Override
    protected int getContentLayoutId() {
        return R.layout.activity_main;
    }

    @Override
    protected void initWidget() {
        fr_root = findViewById(R.id.fr_root);
        bt_record = findViewById(R.id.bt_record);
        bt_record.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if (event.getAction() == MotionEvent.ACTION_DOWN) {
                    //按下,开始录制
                    getPath();//初始化视频路径
                    initRecord();//开始录制
                } else if (event.getAction() == MotionEvent.ACTION_UP) {
                    //松开,停止录制
                    cancelRecord(); // 取消录制操作
                    freeCamera(); // 释放相机资源
                }
                return true;
            }
        });
        requestPermission(WRITE_EXTERNAL_STORAGE);
    }

    @Override
    protected void doSDWrite() {
        requestPermission(READ_EXTERNAL_STORAGE);
    }

    @Override
    protected void doSDRead() {
        requestPermission(CAMERA);
    }

    @Override
    protected void doCamera() {
        requestPermission(RECORD);
    }

    @Override
    protected void doRecord() {
        //这里为了请求好动态权限再让Surface初始化才这么弄得,可以在进入录制页面就请求好动态权限,就不用这么写了。
        SurfaceView surfaceView = new SurfaceView(this);
        FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
        surfaceView.setLayoutParams(params);
        fr_root.addView(surfaceView);
        // 获取表面视图的表面持有者
        mHolder = surfaceView.getHolder();
        // 给表面持有者添加表面变更监听器
        mHolder.addCallback(mSurfaceCallback);
    }

    /**
     * 初始化录制操作,开始录制调用这个方法
     */
    private void initRecord() {
        mMediaRecorder = new MediaRecorder(); // 创建一个媒体录制器
        mMediaRecorder.setCamera(mCamera); // 设置媒体录制器的摄像头
        mMediaRecorder.setOnErrorListener(this); // 设置媒体录制器的错误监听器
        mMediaRecorder.setOnInfoListener(this); // 设置媒体录制器的信息监听器
        mMediaRecorder.setPreviewDisplay(mHolder.getSurface()); // 设置媒体录制器的预览界面
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // 设置视频源为摄像头
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频源为麦克风
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 设置媒体的输出格式
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 设置媒体的音频编码器
        // 如果录像报错:MediaRecorder start failed: -19
        // 试试把setVideoSize和setVideoFrameRate注释掉,因为尺寸设置必须为摄像头所支持,否则报错
         mMediaRecorder.setVideoSize(2280, 1080); // 设置视频的分辨率
        // mMediaRecorder.setVideoFrameRate(16); // 设置视频每秒录制的帧数
        // setVideoFrameRate与setVideoEncodingBitRate设置其一即可
        mMediaRecorder.setVideoEncodingBitRate(1 * 1024 * 512); // 设置视频每秒录制的字节数
        mMediaRecorder.setOrientationHint(90); // 输出旋转90度,也就是保持竖屏录制
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP); // 设置媒体的视频编码器
        mMediaRecorder.setMaxDuration(10 * 1000); // 设置媒体的最大录制时长
        // mMediaRecorder.setMaxFileSize(1024*1024*10); // 设置媒体的最大文件大小
        // setMaxFileSize与setMaxDuration设置其一即可
        mMediaRecorder.setOutputFile(moviePath); // 设置媒体文件的保存路径
        try {
            mMediaRecorder.prepare(); // 媒体录制器准备就绪
            mMediaRecorder.start(); // 媒体录制器开始录制
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 定义一个表面持有者的变更监听器
     */
    private SurfaceHolder.Callback mSurfaceCallback = new SurfaceHolder.Callback() {
        // 在表面视图创建时触发
        public void surfaceCreated(SurfaceHolder holder) {
            initCamera(); // 初始化相机
        }

        // 在表面视图变更时触发
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        }

        // 在表面视图销毁时触发
        public void surfaceDestroyed(SurfaceHolder holder) {
            freeCamera(); // 释放相机资源
        }
    };

    /**
     * 释放相机资源
     */
    private void freeCamera() {
        if (mCamera != null) {
            mCamera.stopPreview(); // 停止预览
            mCamera.lock(); // 锁定相机,即关闭相机
            mCamera.release(); // 释放相机资源
            mCamera = null;
        }
    }

    /**
     * 初始化相机操作
     */
    private void initCamera() {
        if (mCamera != null) {
            freeCamera();
        }
        try {
            // 打开摄像头,默认后置摄像头
            mCamera = Camera.open();
            // 设置相机的展示角度
            mCamera.setDisplayOrientation(90);
            // 设置相机的预览界面
            mCamera.setPreviewDisplay(mHolder);
            // 开始预览画面
            mCamera.startPreview();
            // 解锁相机,即打开相机
            mCamera.unlock();
        } catch (Exception e) {
            e.printStackTrace();
            freeCamera();
        }
    }

    /**
     * 录制前创建一个空文件并获取路径
     */
    private void getPath() {
        List<String> list = new ArrayList<>();
        list.add("record");
        String dirPath = PathGetUtil.getLongwayPath(this, list);
        File fileDir = new File(dirPath);
        if (!fileDir.exists()) {
            fileDir.mkdirs();
        }
        File fileVoice = new File(dirPath, "movie" + System.currentTimeMillis() + ".mp4");
        if (!fileVoice.exists()) {
            try {
                fileVoice.createNewFile();
                moviePath = fileVoice.getPath();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onError(MediaRecorder mr, int what, int extra) {
        if (mr != null) {
            mr.reset();  // 重置媒体录制器
        }
    }

    @Override
    public void onInfo(MediaRecorder mr, int what, int extra) {
        // 录制达到最大时长,或者达到文件大小限制,都停止录制
        if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
                || what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED) {
            cancelRecord();
        }
    }

    /**
     * 取消录制操作,停止录制的时候调用该方法
     */
    private void cancelRecord() {
        if (mMediaRecorder != null) {
            mMediaRecorder.setOnErrorListener(null); // 错误监听器置空
            mMediaRecorder.setPreviewDisplay(null); // 预览界面置空
            try {
                mMediaRecorder.stop(); // 媒体录制器停止录制
            } catch (Exception e) {
                e.printStackTrace();
            }
            mMediaRecorder.release(); // 媒体录制器释放资源
            mMediaRecorder = null;
        }
    }
}

Similarly, since the video we recorded was not processed, the video quality was not very high and it was very unclear. So if you want to be more clear, you can consider changing the Camera part of the setting to Camera2 or a better CameraX. I will not perform specific operations here, the principles are the same, you can try it if you are interested.

Guess you like

Origin blog.csdn.net/weixin_38322371/article/details/115249593