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"><Buttonandroid:id="@+id/startRecord"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="StartRecord" /><Buttonandroid:id="@+id/stopRecord"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="StopRecord" /><TextViewandroid:id="@+id/textView"android:layout_width="match_parent"android:layout_height="wrap_content"/><SurfaceViewandroid: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;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_record_video);initView();}@Overridepublic void onInfo(MediaRecorder mediaRecorder, int i, int i1) {//录制时长达到预设值停止录制并释放MediaRecorderif (i == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED) {stopRecording();}}@Overridepublic 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() {@Overridepublic void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) {initializeCamera();//初始化Camera}@Overridepublic void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int i, int i1, int i2) {}@Overridepublic void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) {}});startRecord.setOnClickListener(this);stopRecord.setOnClickListener(this);}//初始化Cameraprivate 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); // 关联CameraCalendar 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); // 设置音频编码器为AACmediaRecorder.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);// 将预览设置关联到MediaRecordermediaRecorder.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以及锁定Camerapublic void stopRecording() {if (mediaRecorder != null) {mediaRecorder.stop(); // 停止录制releaseMediaRecorder(); // 释放MediaRecordercamera.lock(); // 锁定CameratextView.setText("停止录制!");}}//释放MediaRecorderprivate void releaseMediaRecorder() {if (mediaRecorder != null) {mediaRecorder.reset(); // 重置MediaRecordermediaRecorder.release(); // 释放MediaRecordermediaRecorder = null;}}@Overrideprotected void onDestroy() {super.onDestroy();releaseMediaRecorder();if (camera != null) {camera.release(); // 释放Cameracamera = null;}}
}效果图如下,第一张是刚进入APP的页面,第二张是开始录制、第三张是结束录制、第四张是在保存路径上找到对应的视频。




