最近写了个demo,有个功能是全屏录制视频,录视频的基本功能不算难,demo通过SurfaceView + MediaRecorder的方法, 这里不上代码了,只总结下一些问题:
1.Parameters参数问题
想通过getParameters获取Camera的参数,必须在Camera.unlock()方法前调用,否则会报错。
2.全屏录制
看了下网上的代码,大多数都是非全屏的,想要设置全屏,可以通过 MediaRecorder.setVideoSize(w, h)这个方法。但在使用时出现了挺多问题的。
1、如果设置的宽和高不是设备具备的尺寸时,一般会报错,提示说RuntimeException:start failed,所以在设置前先看下设备支持的尺寸。
2、查看设备尺寸可以通过getSupportedPreviewSizes的方法
Camera.Parameters parameters = mCamera.getParameters();
List.Size> sizeList = parameters.getSupportedPreviewSizes();
int width = sizeList.get(0).width;
int height = sizeList.get(0).height;
得到的是一串列表,从测试上看,只有使用里面的宽高才不会报错,不同设备得到的列表排列不尽相同,下面是按width&height格式输出来的列表数据,
从小到大排列; 从大到小排列; 最后多了个莫名项排列;
从输出结果可以看出一个问题,我要以宽&高的输出,理论上讲应该是宽比高小,但图片上看是宽比高大。这是因为摄像头默认捕获的画面是横向的,由于手机上一般想竖屏使用,所以一般在调用系统相机时,预览时会设置setDisplayOrientation(90)来保持竖屏预览,录制时使用setOrientationHint来保持竖屏录制。
if (mCurrentCamera == BACE_CAMERA) {
mMediaRecorder.setOrientationHint(90);// 如果是后置摄像头,输出旋转90度,保持竖屏录制
} else {
mMediaRecorder.setOrientationHint(270);// 如果是前置摄像头,输出旋转270度,保持竖屏录制
}
所以在使用setVideoSize (int width, int height)时,width也应该是使用默认的,也就是说全屏录制时,width应该是屏幕的高度,height是屏幕宽度。
3、所以要全屏录制,使用哪个参数呢?当然是用系统分辨率相同的宽高。测试的时候发现,基本上测试到的手机都支持系统分辨率的尺寸录制。我在application里已经先获取到设备的宽高了,所以在这里直接使用就行了。这里有个问题,我获取的宽高都是竖屏时的宽高,所以在用setVideoSize时,要互换一下,否则会报错,一般报start failed的错,但也会有手机是MediaRecorder的OnErrorListener里报error100的错误。
3.顺序问题
在初始化MediaRecorder时,设置参数需要按照一定的顺序。比说setVideoSize这个方法,看网上有些说要在setVideoEncoder之前调用,但我这样写时,会发现有些手机会直接卡死。这里放上我调用的顺序,测试上看暂时没发现什么问题
if (mCamera == null) {
return;
}
if (mMediaRecorder != null) {
return;
}
mMediaRecorder = new MediaRecorder();
mMediaRecorder.reset();
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); // 视频源
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC); // 音频源
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); // 视频输出格式
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB); // 音频格式
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP); // 视频录制格式
mMediaRecorder.setVideoEncodingBitRate(5 * Constant.WIDTH_OF_SCREEN * Constant.WIDTH_OF_SCREEN);// 设置帧频率
mMediaRecorder.setVideoFrameRate(30); // 视频帧频率
mMediaRecorder.setVideoSize(Constant.HEIGHT_OF_SCREEN, Constant.WIDTH_OF_SCREEN); // 视频分辨率
if (mCurrentCamera == BACE_CAMERA) {
mMediaRecorder.setOrientationHint(90);// 如果是后置摄像头,输出旋转90度,保持竖屏录制
} else {
mMediaRecorder.setOrientationHint(270);// 如果是前置摄像头,输出旋转270度,保持竖屏录制
}
mMediaRecorder.setOnErrorListener(new MediaRecorder.OnErrorListener() {
@Override
public void onError(MediaRecorder mr, int what, int extra) {
if (mr != null) {
mr.reset();
}
mListener.onFailed("MediaRecorder Error: what = " + what + ", extra = " + extra);
}
});
mMediaRecorder.setOutputFile(mRecordFile.getAbsolutePath()); // 输出文件
try {
mMediaRecorder.prepare();
} catch (IOException e) {
e.printStackTrace();
mListener.onFailed("MediaRecorder Error: " + e.toString());
}
mMediaRecorder.start();
录制出来的视频如果不清晰的话,可以通过setVideoEncodingBitRate这个方法,把参数调高就清晰了。
有些6.0的手机可能打不开录制界面,可以换成下面的方法。
CamcorderProfile profile = CamcorderProfile.get(CamcorderProfile.QUALITY_HIGH);
mMediaRecorder.setVideoSize(profile.videoFrameWidth, profile.videoFrameHeight);
4.适配问题
适配问题一向有点难弄,这里只说全屏的适配。当我调用系统分辨率当做录制尺寸时,发现在一些大部分测试机上没问题,录制出来的都是全屏的视频,查了下视频分辨率,也和系统分辨率一样,但在一台乐视机和一台魅蓝上,却只录制出640 x 480的视频。最开始我以为是代码问题,于是用Nexus测试了下,测试用的Nexus分辨率是2392 x 1440,没想到录制出的视频是1920 x 1080的。于是又研究了好一会,最后发现Nexus的支持列表里没有2392 x 1440的,但它竟然没有报错!所以我猜想是它有可能是当不匹配支持的数据时,会向下取一个宽高比相似的进行录制,个人想法。
所以这也暴露了一个问题,有可能设备不支持屏幕分辨率的尺寸,也不能指望其它手机也能像Nexus一样不报错。所以这里最好做一下最优适配,也就是拿屏幕分辨率和设备支持的尺寸进行比对,如果找不到屏幕分辨率的尺寸,那就向下取宽高比最相似的一个。
至于有些手机上没能录制出全屏视频的这个问题,暂时还没想出什么办法解决。
5.其它
录制完成后想用MediaMetadataRetriever拿视频的数据,结果死活都拿不到,在播放页面又没问题,最后发现是调用顺序错了。忘了先把MediaRecorder关掉再拿数据。