Android Studio新手开发第三十三天

利用MediaRecorder录制视频

        上一次介绍了如何利用MediaRecorder录制音频,而MediaRecorder也可以用于录制视频,在录制视频前需要获取相机权限,要在AndroidManifest.xml中添加权限声明以及在代码中查看是否已经获取该权限,若没有就提示用户开启权限或者直接到设置中手动开启,下面的代码没有做这一部分,直接在系统设置中手动开启了。示例代码如下,在布局文件中添加两个按钮、一个文本视图以及一个SurfaceView用于实时展示相机获取的视频内容。

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MultiMedia.RecordVideoActivity">

    <Button
        android:id="@+id/startRecord"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="StartRecord" />

    <Button
        android:id="@+id/stopRecord"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="StopRecord" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <SurfaceView
        android:id="@+id/surfaceView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="center"/>


</androidx.appcompat.widget.LinearLayoutCompat>

        部分Java代码如下,主要看方法initializeCamera以及方法prepareMediaRecorder的内容。在方法initializeCamera中重点在于unlock方法的调用上,若想要调用方法setPreviewDisplay建议不要调用unlock方法,可能会报错如PreviewTexture failed,作者自己在学习使用时就出现这样的错误,后面将unlock方法去掉后就没问题了。

        在方法prepareMediaRecorder中若不想要使用方法setCamera是可以的但是在SurfaceView上显示的视频方向会颠倒与预期不符,这也是使用setCamera的原因之一,利用Camera类可以事先设置摄像头的一些配置然后再将它用于录制视频。若要调用setCamera方法则需要先将Camera解锁即调用unlock方法。在往后就是设置MediaRecorder的一些属性,如音频源,视频源,视频长度,视频规格,录制方向、输出格式以及编码器等。MediaRecorder的监听器中要实现Camera的初始化,若在onCreate方法中调用initializeCamera方法不能够实现实时展示视频内容,作者自己学的时候就出现该问题,不知道为什么。



public class RecordVideoActivity extends AppCompatActivity implements View.OnClickListener, MediaRecorder.OnInfoListener {

    private final String public_url = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString();
    private String private_url;
    private Button startRecord, stopRecord;
    private TextView textView;
    private SurfaceView surfaceView;
    private SurfaceHolder holder;
    private MediaRecorder mediaRecorder;
    private Camera camera;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_record_video);
        initView();
    }

    @Override
    public void onInfo(MediaRecorder mediaRecorder, int i, int i1) {
        //录制时长达到预设值停止录制并释放MediaRecorder
        if (i == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {
            stopRecording();
        }
    }

    @Override
    public void onClick(View view) {
        if (view.getId() == R.id.startRecord) {
            startRecording();
        } else if (view.getId() == R.id.stopRecord) {
            if (mediaRecorder != null) {
                stopRecording();
                releaseMediaRecorder();
            }
        }
    }

    //初始化页面
    private void initView() {
        startRecord = findViewById(R.id.startRecord);
        stopRecord = findViewById(R.id.stopRecord);
        textView = findViewById(R.id.textView);
        private_url = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();
        File recordFile = new File(private_url + "/audioRecord/");
        if (!recordFile.exists()) {
            boolean i = recordFile.mkdirs();
            if (i) {
                textView.setText("文件夹不存在,已新建!");
            }
        }
        // 初始化 SurfaceView 用于预览
        surfaceView = findViewById(R.id.surfaceView);
        holder = surfaceView.getHolder();
        holder.addCallback(new SurfaceHolder.Callback() {
            @Override
            public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {
                initializeCamera();//初始化Camera
            }

            @Override
            public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {

            }

            @Override
            public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {

            }
        });
        startRecord.setOnClickListener(this);
        stopRecord.setOnClickListener(this);
    }

    //初始化Camera
    private void initializeCamera() {
        // 初始化 Camera,0 通常代表后置摄像头
        camera = Camera.open(0);
        // 调整摄像头预览角度,例如设置为90度
        camera.setDisplayOrientation(90);
        // 设置SurfaceView不维护自己的缓冲区
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
        //若在这里先解锁Camera,那么在进入页面时将不能预览Camera即调用camera.setPreviewDisplay(holder)将会出错而不能调用camera.startPreview()进行预览
        //camera.unlock();
        // 将Camera的预览输出与SurfaceView关联
        try {
            camera.setPreviewDisplay(holder);
        } catch (IOException e) {
            Log.d(TAG, "initializeCamera: " + e.getMessage());
        }
        camera.startPreview(); // 开始预览
    }

    //准备录制视频,返回值为布尔类型,用于判断能否开始录制
    private boolean prepareMediaRecorder() {
        mediaRecorder = new MediaRecorder();
        camera.unlock(); // 解锁Camera以便MediaRecorder使用
        mediaRecorder.setCamera(camera); // 关联Camera
        Calendar calendar = Calendar.getInstance();
        String path = String.format("%s/audioRecord/%s%s%s%s%s.mp4", private_url, calendar.get(Calendar.YEAR), calendar.get(Calendar.MONTH),
                calendar.get(Calendar.DAY_OF_MONTH), calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE));
        // 设置音频和视频源
        mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 设置音频源为麦克风
        mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // 设置视频源为摄像头
        // 设置输出格式
        mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 设置输出格式为MPEG-4
        // 设置音频和视频编码器
        mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); // 设置音频编码器为AAC
        mediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); // 设置视频编码器为H.264
        // 设置输出文件
        mediaRecorder.setOutputFile(path);
        //录制5秒时间
        mediaRecorder.setMaxDuration(5000);
        // 设置视频参数
        mediaRecorder.setVideoSize(1280, 720); // 设置视频尺寸
        mediaRecorder.setVideoFrameRate(30); // 设置视频帧率
        // 设置视频编码比特率(可选)
        //mediaRecorder.setVideoEncodingBitRate(10000);
        // 设置录制方向,90度通常表示竖屏录制
        mediaRecorder.setOrientationHint(90);
        // 将预览设置关联到MediaRecorder
        mediaRecorder.setPreviewDisplay(surfaceView.getHolder().getSurface());
        //添加信息监听器
        mediaRecorder.setOnInfoListener(this);
        try {
            mediaRecorder.prepare(); // 准备录制
            textView.setText("准备录制!");
        } catch (IOException e) {
            Log.d(TAG, "prepareMediaRecorder: " + e.getMessage());
            releaseMediaRecorder();
            return false;
        }
        return true;
    }

    //开始录制
    public void startRecording() {
        if (prepareMediaRecorder()) {
            mediaRecorder.start(); // 开始录制
            textView.setText("正在录制...");
        }
    }

    //停止录制,释放MediaRecord以及锁定Camera
    public void stopRecording() {
        if (mediaRecorder != null) {
            mediaRecorder.stop(); // 停止录制
            releaseMediaRecorder(); // 释放MediaRecorder
            camera.lock(); // 锁定Camera
            textView.setText("停止录制!");
        }
    }

    //释放MediaRecorder
    private void releaseMediaRecorder() {
        if (mediaRecorder != null) {
            mediaRecorder.reset(); // 重置MediaRecorder
            mediaRecorder.release(); // 释放MediaRecorder
            mediaRecorder = null;
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseMediaRecorder();
        if (camera != null) {
            camera.release(); // 释放Camera
            camera = null;
        }
    }
}

        效果图如下,第一张是刚进入APP的页面,第二张是开始录制、第三张是结束录制、第四张是在保存路径上找到对应的视频。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值