公众号回复:666,领取学习资源大礼包
来源:svvvvvvvL
地址:https://www.jianshu.com/p/0d2f200ab374
Android的相机 Camera2 在 6.0M 的时候,出了一个支持高帧率预览和录像的功能。
就是创建一个新的 session
,叫做 mCameraDevice.createConstrainedHighSpeedCaptureSession
,通过这个,可以实现相机的高帧率(>120fps)的预览和录像(需要相机本身支持)。
根据相机的不同,实现的帧率也不同, 比如我手上这个华为v10的手机就是下图这个样子:
下面说一下大概步骤.
相机部分肯定得请求相关权限才能操作的,这里需要3个,分别为
相机 Manifest.permission.CAMERA
录音 Manifest.permission.RECORD_AUDIO 这个是为了录视频的时候录音用的
写入文件 Manifest.permission.WRITE_EXTERNAL_STORAGE 这个是拍好了视屏,存入到手机相册用的
除了运行时请求这些权限之外, 还需要在AndroidManifest.xml文件里面加入这3个权限的注册,否则请求权限的时候,不能触发Dialog提示
正确获取到权限了之后,按照常规方式打开相机.
先获取到
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
然后用这个manager打开相机,即
manager.openCamera(cameraId, mStateCallback, mBackgroundHandler);
在这中间,我们可以获取到相机的支持的各种参数信息, 也就是在这里才可以知道,当前的相机支不支持高帧率录制
使用 manager.getCameraCharacteristics(cameraId)
获取 CameraCharacteristics
。
然后使用 characteristics .get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
这个值可以获取到向前摄像头支持的参数 StreamConfigurationMap
。
最后用 map
调用 map.getHighSpeedVideoFpsRanges()
来获取支持搞帧率预览范围,代码部分为
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map = characteristics
.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
if (map == null) {
ErrorDialog.newInstance(getString(R.string.open_failed_of_map_null))
.show(getFragmentManager(), "TAG");
return;
}
for (Range fpsRange : map.getHighSpeedVideoFpsRanges()) {
Log.d(TAG, "openCamera: [width, height] = "+ fpsRange.toString());
}
比如我手上的这个华为V10测试机,获取到的参数就是
openCamera: [width, height] = [120, 120]
openCamera: [width, height] = [30, 120]
帧率范围Range的 Lower
和 Upper
一致才认为支持,所以这个log结果说明它支持120fps
的预览.
高帧率的视频录制和普通的camera.createCaptureSession要求不同,高帧率session的预览preview大小和设置的MediaRecorder的大小必须一致
上文获取出来的支持120fps,说明他支持预览,但是不意味着它支持录制视频。
是否支持视频录制,需要用另一个方法来查询, 就是 CamcorderProfile
的 public static boolean hasProfile(int cameraId, int quality)
方法,只有他返回true才意味着当前预览的帧率支持被录制视频。
直接在MediaRecorder的方法里set设置各个属性是否可行?
答案是不行, 虽然我也不知道为什么, 我用手边的机器坚果Pro 2s测试, 查询到支持的预览帧率支持1080p的240fps,但是在给MediaRecorder设置好各种参数后,比如mMediaRecorder.setVideoFrameRate(240),点击录制直接crash.
在得出手机支持高帧率录制后, 就需要配置MediaRecorder给下一步打开预览使用了. 具体的步骤如下:
CamcorderProfile profile = mVideoSize.getCamcorderProfile();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setProfile(profile);
mNextVideoFilePath = getVideoFile();
mMediaRecorder.setOutputFile(mNextVideoFilePath);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
int orientation = ORIENTATIONS.get(rotation);
mMediaRecorder.setOrientationHint(orientation);
mMediaRecorder.prepare();
这里面的关键就是setVideoFrameRate这一个属性, 这个只能由CamcorderProfile.videoFrameRate来设置, 如果CamcorderProfile说你不支持, 那么你手动设置了这个数值, 也不能正常录制.
其他的只需要将CamcorderProfile不包含的配置设置进去就好了. 其他的比如setOutputFile输出文件路径,setOrientationHint旋转方向这些, 根据需要配置就好了.
使用CameraDevice的如下方法开启预览
/*
* @param outputs The new set of Surfaces that should be made available as
* targets for captured high speed image data.
* @param callback The callback to notify about the status of the new capture session.
* @param handler The handler on which the callback should be invoked, or {@code null} to use
* the current thread's {@link android.os.Looper looper}.
*
* @throws IllegalArgumentException if the set of output Surfaces do not meet the requirements,
* the callback is null, or the handler is null but the current
* thread has no looper, or the camera device doesn't support
* high speed video capability.
* @throws CameraAccessException if the camera device is no longer connected or has
* encountered a fatal error
* @throws IllegalStateException if the camera device has been closed
*
* @see #createCaptureSession
* @see CaptureRequest#CONTROL_AE_TARGET_FPS_RANGE
* @see StreamConfigurationMap#getHighSpeedVideoSizes
* @see StreamConfigurationMap#getHighSpeedVideoFpsRangesFor
* @see CameraCharacteristics#REQUEST_AVAILABLE_CAPABILITIES
* @see CameraMetadata#REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO
* @see CameraCaptureSession#captureBurst
* @see CameraCaptureSession#setRepeatingBurst
* @see CameraConstrainedHighSpeedCaptureSession#createHighSpeedRequestList
*/
public abstract void createConstrainedHighSpeedCaptureSession(@NonNull List outputs,
@NonNull CameraCaptureSession.StateCallback callback,
@Nullable Handler handler)
throws CameraAccessException;
可以看到, 第一个参数是一个surface的List,代表着相机摄像头拍得到画面要输出到哪里去, 这里我们有2个目的地:
第一个是preview,需要输出到预览, 这样我们在屏幕上才看得到画面 第二个是mMediaRecorder.getSurface(), 这个是输出到MediaRecorder里面去,用来录制视频
所以这个地方的代码就类似
Surface previewSurface = new Surface(texture);
//添加预览输出
surfaces.add(previewSurface);
mPreviewBuilder.addTarget(previewSurface);
//配置MediaRecorder
setUpMediaRecorder();
//添加MediaRecorder输出
surfaces.add(mMediaRecorder.getSurface());
mPreviewBuilder.addTarget(mMediaRecorder.getSurface());
//开启预览
mCameraDevice.createConstrainedHighSpeedCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
//保存引用
mPreviewSessionHighSpeed = (CameraConstrainedHighSpeedCaptureSession) cameraCaptureSession;
//刷新预览, 使屏幕动起来
updatePreview();
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
Activity activity = getActivity();
if (null != activity) {
Toast.makeText(activity, "Failed", Toast.LENGTH_SHORT).show();
}
}
}, mBackgroundHandler);
没什么错误的话,这样就可以启动预览了.
因为MediaRecorder在之前已经准备就绪了, 所以开启录像只需要调用一下开始录像就好了
mMediaRecorder.start();
其他的一些UI操作看着办.
结束录制也是几句话:
//停止录制
mMediaRecorder.stop();
//重置状态,便于重用, 不需要每次都new MediaRecorder了
mMediaRecorder.reset();
//因为MediaRecorder被销毁了, 所以需要重新配置MediaRecorder,重新打开预览
startPreview();
对于录制生成的视频, 需要查看他的具体参数是不是真的是我们所设置的值. 我这里是macos环境, 就只演示我自己的操作:
用USB线连接手机和电脑,点击右下角的 Device File Explorer
在文件列表中找到自己录制的视频文件, 并把它保存到本地,比如Downloads
保存到本地的视频文件
打开终端Terminal,然后运行命令ffmpeg -i 视频文件名, 如果提示ffmpeg命令不存在或者找不到,就先安装这个东西
红线处是查询视频信息的命令,绿线是该视频的fps帧率信息. 虽然我们预览的是120fps,但是录制出来的不一定可以达到那么高, 比如图里面的就只有74.54fps.这是系统自己决定的.
关键就是2点:
预览尺寸和录制尺寸要一直
CamcorderProfile 说你支持你才支持.
所有Demo代码提交到Github.可以访问
https://github.com/ZhengShang/HighSpeedVideoDemo/tree/master
如果运行apk后发现crash,ANR或者黑屏等杂七杂八的问题, 这都很正常, Android相机开发受制于各种不同的手机和rom的差异化,确实挺难做到完美兼容的, 尤其是这随便写的Demo,就更难保证了.
但是大体流程是这个样子, 具体解决Bug那都是后事了.
推荐阅读:
音视频开发入门必备之基础知识
移动端技术交流喊你入群啦~~~
推荐几个堪称教科书级别的 Android 音视频入门项目
Android OpenGL ES 实现 3D 阿凡达效果
没想到,快手成了“生产力”
觉得不错,点个在看呗~