利用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的页面,第二张是开始录制、第三张是结束录制、第四张是在保存路径上找到对应的视频。




1647

被折叠的 条评论
为什么被折叠?



