添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • 录音 Manifest.permission.RECORD_AUDIO 这个是为了录视频的时候录音用的
  • 写入文件 Manifest.permission.WRITE_EXTERNAL_STORAGE 这个是拍好了视屏,存入到手机相册用的
  • 除了运行时请求这些权限之外, 还需要在 AndroidManifest.xml 文件里面加入这3个权限的注册,否则请求权限的时候,不能触发Dialog提示

        <uses-permission android:name="android.permission.CAMERA" />
        <uses-permission android:name="android.permission.RECORD_AUDIO" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    

    第二步,打开相机

    正确获取到权限了之后,按照常规方式打开相机.

    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<Integer> fpsRange : map.getHighSpeedVideoFpsRanges()) { Log.d(TAG, "openCamera: [width, height] = "+ fpsRange.toString());

    比如我手上的这个华为V10测试机,获取到的参数就是

    openCamera: [width, height] = [120, 120]
    openCamera: [width, height] = [30, 120]
    

    帧率范围RangeLowerUpper一致才认为支持,所以这个log结果说明它支持120fps的预览.

    支持高帧率的视频录制的条件

  • 高帧率的视频录制和普通的camera.createCaptureSession要求不同,高帧率session的预览preview大小和设置的MediaRecorder的大小必须一致
  • 上文获取出来的支持120fps,说明他支持预览,但是不意味着它支持录制视频.是否支持视频录制,需要用另一个方法来查询, 就是CamcorderProfilepublic static boolean hasProfile(int cameraId, int quality)方法,只有他返回true才意味着当前预览的帧率支持被录制视频.
  • 为什么一定要用CamcorderProfile来判断是否支持?

    直接在MediaRecorder的方法里set设置各个属性是否可行?
    答案是不行, 虽然我也不知道为什么, 我用手边的机器坚果Pro 2s测试, 查询到支持的预览帧率支持1080p240fps,但是在给MediaRecorder设置好各种参数后,比如mMediaRecorder.setVideoFrameRate(240),点击录制直接crash.

    配置MediaRecorder

    在得出手机支持高帧率录制后, 就需要配置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<Surface> 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环境, 就只演示我自己的操作:
    1.用USB线连接手机和电脑,点击右下角的 Device File Explorer
    红线处是查询视频信息的命令,绿线是该视频的fps帧率信息. 虽然我们预览的是120fps,但是录制出来的不一定可以达到那么高, 比如图里面的就只有74.54fps.这是系统自己决定的.

    关键就是2点:
    1.预览尺寸和录制尺寸要一直
    2.CamcorderProfile说你支持你才支持.

    所有Demo代码提交到Github.可以访问https://github.com/ZhengShang/HighSpeedVideoDemo/tree/master

    Demo Apk 下载