之前我们说了音频的录制和播放,那么现在说说视频的录制。其实视频的录制还是用的MediaRecorder,并且配合Camera进行操作。所以这里我们详细说明一下视频录制的做法。至于视频播放,由于涉及到全屏切换,生命周期管理等一系列问题,这里不写博客说了,感兴趣的同学可以在我的资源中找到我的框架,里面有关于视频的播放案例。
好了,我们开始录制视频的学习。
1.媒体播放器MediaRecorder常用方法(录音与录像通用)
-
reset:重置录制资源。
-
prepare:准备录制。
-
start:开始录制。
-
stop:结束录制。
-
release:释放录制资源。
-
setOnErrorListener:设置错误监听器。可监听服务器异常和未知错误的事件。需要实现接口MediaRecorder.OnErrorListener的onError方法。
-
setOnInfoListener:设置信息监听器。可监听录制结束事件,包括达到录制时长或达到录制大小。需要实现接口MediaRecorder.OnInfoListener的onInfo方法。
-
setMaxDuration:设置可录制的最大时长,单位毫秒。
-
setMaxFileSize:设置可录制的最大文件大小,单位字节。
-
setOutputFile:设置输出文件的路径。
2.MeidaRecorder用于录像的常用方法
-
setCamera:设置相机对象。
-
setPreviewDisplay:设置预览界面。预览界面对象可通过SurfaceHolder对象的getSurface方法获得。
-
setOrientationHint:设置预览的角度。跟拍照一样设置为90,表示界面从水平方向到垂直方向旋转90度。
-
setVideoSource:设置视频来源。一般使用VideoSource.CAMERA表示摄像头。
-
setOutputFormat:设置媒体输出格式。媒体输出格式的取值见表1。
表1
|
OutputFormat类的输出格式
|
格式分类
|
扩展名
|
格式说明
|
AMR_NB
|
音频
|
.amr
|
窄带格式
|
AMR_WB
|
音频
|
.amr
|
宽带格式
|
AAC_ADTS
|
音频
|
.aac
|
高级的音频传输流格式
|
MPEG_4
|
视频
|
.mp4
|
MPEG4格式
|
THREE_GPP
|
视频
|
.3gp
|
3GP格式
|
-
setVideoEncoder:设置视频编码器。一般使用VideoEncoder.MPEG_4_SP表示MPEG4编码。该方法在setOutputFormat方法之后调用,否则会报错java.lang.IllegalStateException。
-
setVideoSize:设置视频的分辨率。
-
setVideoFrameRate:设置视频每秒录制的帧数。越大视频越连贯,当然最终生成的视频文件也越大。
-
setVideoEncodingBitRate:设置视频每秒录制的字节数。越大视频越清晰,setVideoFrameRate与setVideoEncodingBitRate设置一个即可。
3.代码示例
<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"/>
布局代码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;
同样的,我们录制出来的视频由于没有进行处理,视频质量不是很高,很不清晰。所以如果大家想更加清晰的话,可以考虑把设置的Camera部分换成Camera2或者更好用的CameraX。我这里就不进行具体操作了,原理都是一样的,大家感兴趣可以进行尝试。
Android 5.0以上,无需ROOT;
2、案例实现方式:
使用MediaProjection构建虚拟VirtualDisplay,完成屏幕录制生成mp4文件保存到本地sdcard。
3、案例源码:
案例源码以实现录屏最小集的方式实现,注释充分,通俗易懂。
具体表现:调用MediaRecorder的start()与stop()间隔不能小于1秒(有时候大于1秒也崩),否则必崩。错误信息:java.lang.RuntimeException: stop Failed.at android.media.MediaRecorder.stop(Native Method)解决办法:在stop以前调用setOnErrorListener(null);就行了!相关...
public void startRecord() {
mediarecorder = new MediaRecorder();// 创建mediarecorder对象
mCamera = getCameraInstance();
Parameters parameters = mCamera.getParameters();
用MediaRecorder类实现录像功能的基本步骤是:创建MediaRecorder对象、设置音视频源、设置输出格式和音视频编码格式、设置预览控件和输出文件、准备、开始录制、停止录制、释放资源。停止录制后可以重置,然后开始另一个录制过程。
final int CODE_VIDEO_SYS = 0x001 ;
Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);
startActivityForResult(intent, CODE_VIDEO_SYS);
@Override
public vo...
通过CameraManager拿到对应相机的参数
调用openCamera打开相机。
在回调中创建CaptureRequestBuilder与CameraCaptureSession。其中,要将我们的Surface添加到CaptureRequestBuilder中,这里我们还是使用TextureView,通过其SurfaceTextur
之前讲了camera的相关问题,今天把mediaRecorder总结一下
1.视频的videoSize设置
视频的录制尺寸设置考虑的方面比较多,说下我在这里碰到的一些坑。初次以camera的预览尺寸作为videoSize的,经过测试发现,部分手机(酷派)录制时直接崩溃了,原因是得到的bestPreviewSize不匹配该手机支持的videoSize。后来找到这个方法:getSu
一旦你建立了预览类和显示预览类的viewlayout,你就已准备好开始使用你的应用获取图像了.在你的应用代码中,你还必须要建立起那些用于控制的控件们的侦听器,来响应用户的动作.
要取得图像,使用Camera.takePicture()方法.此方法有三个参数.要获得JPEG图像,你必须实现一个Camera.PictureCallback接口来接收图像数据然后写入文件中.下面的代码...
为了将MediaRecorder用于视频捕获,必须采用与音频捕获相同的步骤,同时加上一些视频的特定的步骤。除此之外,MediaRecord是一个状态机,因此必须遵从实例化到录制的特定步骤序列。
首先将实例化MediaRecorder,然后一次进行其他的步骤。
MediaRecorder recorder=new MediaRecorder();
1.音频和视频源