第29讲 Android Camera2 API SlowMotion实战

本讲是Android Camera专题系列的第29讲,我们介绍Android Camera2 API专题的SlowMotion实战,包括如下内容:

  • 判断是否支持Slow Motion
  • Slow Motion支持的Size和FPS
  • Camera流程控制
  • MediaRecorder流程控制

视频在线观看:

判断是否支持Slow Motion

Camera方面

Capability

  • 是否支持REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO

StreamConfigurationMap

  • getHighSpeedVideoSizes()不为NULL
  • 每一种Size对应的getHighSpeedVideoFpsRangesFor不为NULL

实战代码

GeekCamera2\app\src\main\java\com\deepinout\geekcamera\cts\helpers\StaticMetadata.java#isHighSpeedVideoSupported

public boolean isHighSpeedVideoSupported() {
    if (!isCapabilitySupported(
            CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_CONSTRAINED_HIGH_SPEED_VIDEO)) {
        return false;
    }
    StreamConfigurationMap config =
            getValueFromKeyNonNull(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
    if (config == null) {
        return false;
    }
    Size[] availableSizes = config.getHighSpeedVideoSizes();
    if (availableSizes.length == 0) {
        return false;
    }

    for (Size size : availableSizes) {
        Range<Integer>[] availableFpsRanges = config.getHighSpeedVideoFpsRangesFor(size);
        if (availableFpsRanges.length == 0) {
            return false;
        }
    }

    return true;
}

Video Encoder方面

从CamcorderProfile判断是否支持High Speed Quality的Profile

实战代码

GeekCamera2\app\src\main\java\com\deepinout\geekcamera\cameracontroller\CameraController2.java#getFpsFromHighSpeedProfileForSize

private int getFpsFromHighSpeedProfileForSize(android.util.Size size) {
    for (int quality = CamcorderProfile.QUALITY_HIGH_SPEED_LOW;
         quality <= CamcorderProfile.QUALITY_HIGH_SPEED_2160P; quality++) {
        if (CamcorderProfile.hasProfile(quality)) {
            CamcorderProfile profile = CamcorderProfile.get(quality);
            if (size.equals(new android.util.Size(profile.videoFrameWidth, profile.videoFrameHeight))){
                return profile.videoFrameRate;
            }
        }
    }

    return 0;
}

Slow Motion支持的Size和FPS

遍历Camera支持的Size和FPS,对每一种Size和FPS的组合去检查CamcorderProfile是否支持

实战代码

GeekCamera2\app\src\main\java\com\deepinout\geekcamera\cameracontroller\CameraController2.java#doInitSupportedHighSpeedVideoSizes

private void doInitSupportedHighSpeedVideoSizes(CameraFeatures camera_features,
                                                StreamConfigurationMap configs) {
    Log.i(TAG, "[Slow_Motion] doInitSupportedHighSpeedVideoSizes");
    hs_fps_ranges = new ArrayList<>();
    camera_features.mSupportedVideoSizesHighSpeed = new ArrayList<>();

    for (Range<Integer> r : configs.getHighSpeedVideoFpsRanges()) {
        hs_fps_ranges.add(new int[] {r.getLower(), r.getUpper()});
    }
    Collections.sort(hs_fps_ranges, new CameraController.RangeSorter());
    if( MyDebug.LOG ) {
        Log.i(TAG, "[Slow_Motion] Camera Supported high speed video fps ranges: ");
        for (int[] f : hs_fps_ranges) {
            Log.i(TAG, "[Slow_Motion]   camera hs range: [" + f[0] + "-" + f[1] + "]");
        }
        Log.i(TAG, "[Slow_Motion] Video Supported high speed video camcorder profiles: ");
        for (int quality = CamcorderProfile.QUALITY_HIGH_SPEED_LOW;
             quality <= CamcorderProfile.QUALITY_HIGH_SPEED_2160P; quality++) {
            if (CamcorderProfile.hasProfile(quality)) {
                CamcorderProfile profile = CamcorderProfile.get(quality);
                Log.i(TAG, "[Slow_Motion]   video hs camcorder profile:" + profile.videoFrameWidth
                                + "x" + profile.videoFrameHeight + "@" + profile.videoFrameRate + " fps" +
                                   ",quality:" + quality);
            }
        }
    }

    android.util.Size[] camera_video_sizes_high_speed = configs.getHighSpeedVideoSizes();
    for(android.util.Size camera_size : camera_video_sizes_high_speed) {
        for (Range<Integer> r : configs.getHighSpeedVideoFpsRangesFor(camera_size)) {
            int profile_fps = getFpsFromHighSpeedProfileForSize(camera_size);
            if (r.getUpper() > r.getLower()) {
                Log.w(TAG, "[Slow_Motion] skip " + camera_size + "@fps range:" + r.toString());
                continue;
            }
            if (r.getUpper() != profile_fps) {
                Log.w(TAG, "[Slow_Motion] high speed recording " + camera_size + "@" + r.getUpper() + "fps"
                        + " is not supported by CamcorderProfile");
                continue;
            }
            ArrayList<int[]> fr = new ArrayList<>();
            fr.add(new int[] { r.getLower(), r.getUpper()});

            CameraController.Size hs_video_size = new CameraController.Size(
                    camera_size.getWidth(),
                    camera_size.getHeight(),
                    fr,
                    true);
            if (MyDebug.LOG) {
                Log.i(TAG, "[Slow_Motion] added high speed video size: " +
                        hs_video_size +
                        ", fps range:" + r.toString());
            }
            camera_features.mSupportedVideoSizesHighSpeed.add(hs_video_size);
        }
    }
    Collections.sort(camera_features.mSupportedVideoSizesHighSpeed, new CameraController.SizeSorter());
}

Camera流程控制

Session创建

  • 创建SessionConfiguration时,Session Type要设置为SessionConfiguration.SESSION_HIGH_SPEED
  • 只配置一个Preview Surface + 一个Video Recording Surface,不支持Video Snapshot

实战代码

GeekCamera2\app\src\main\java\com\deepinout\geekcamera\cameracontroller\CameraController2.java#createCaptureSession

mSessionConfiguration = new SessionConfiguration(
        is_video_high_speed ?
                SessionConfiguration.SESSION_HIGH_SPEED : SessionConfiguration.SESSION_REGULAR,
        outputConfigurations,
        new CameraTestUtils.HandlerExecutor(mCameraBackgroundHandler),
        myStateCallback
);

Repeating burst request创建

  • 调用CameraConstrainedHighSpeedCaptureSession# createHighSpeedRequestList,根据一个request产生一组CaptureRequest

实战代码

GeekCamera2\app\src\main\java\com\deepinout\geekcamera\cameracontroller\CameraController2.java#setRepeatingRequest

if( is_video_high_speed && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M ) {
    CameraConstrainedHighSpeedCaptureSession captureSessionHighSpeed = (CameraConstrainedHighSpeedCaptureSession) mCameraCaptureSession;
    List<CaptureRequest> mPreviewBuilderBurst = captureSessionHighSpeed.createHighSpeedRequestList(request);
    Log.i(TAG, "[Slow_Motion] setRepeatingBurst createHighSpeedRequestList size:" + mPreviewBuilderBurst.size());
    captureSessionHighSpeed.setRepeatingBurst(mPreviewBuilderBurst, previewCaptureCallback, mCameraBackgroundHandler);
}

Repeating burst CaptureRequest的个数(Batch Size)是如何决定决定的

  • 录像过程中预览一般30FPS就足够了,因此Batch Size的计算会想办法将预览帧率限制在30FPS

    MediaRecorder流程控制

  • 举例240FPS的CaptureRequest List有8个Batch Size

    MediaRecorder流程控制

MediaRecorder流程控制

将CamcorderProfile中的值设置给MediaRecorder

setVideoFrameRate

  • Slow Motion
    • 建议不要录制Audio
    • 通过setVideoFrameRate设置的video frame rate要小于capture rate,建议为30

  • 高帧率视频

    • Video frame rate与capture rate相等

实战代码

GeekCamera2\app\src\main\java\com\deepinout\geekcamera\preview\VideoProfile.java#copyToMediaRecorder

public void copyToMediaRecorder(MediaRecorder media_recorder, boolean slow_motion) {
    if( MyDebug.LOG )
        Log.d(TAG, "copyToMediaRecorder: " + media_recorder + toString());
    if( record_audio && !slow_motion) {
        if( MyDebug.LOG )
            Log.d(TAG, "record audio");
        media_recorder.setAudioSource(this.audioSource);
    }
    media_recorder.setVideoSource(this.videoSource);
    // n.b., order may be important - output format should be first, at least
    // also match order of MediaRecorder.setProfile() just to be safe, see https://stackoverflow.com/questions/5524672/is-it-possible-to-use-camcorderprofile-without-audio-source
    media_recorder.setOutputFormat(this.fileFormat);
    if (slow_motion) {
        media_recorder.setVideoFrameRate(30);
    } else {
        media_recorder.setVideoFrameRate(this.videoFrameRate);
    }
    media_recorder.setCaptureRate(this.videoCaptureRate);
    media_recorder.setVideoSize(this.videoFrameWidth, this.videoFrameHeight);
    media_recorder.setVideoEncodingBitRate(this.videoBitRate);
    media_recorder.setVideoEncoder(this.videoCodec);
    if( record_audio && !slow_motion) {
        media_recorder.setAudioEncodingBitRate(this.audioBitRate);
        media_recorder.setAudioChannels(this.audioChannels);
        media_recorder.setAudioSamplingRate(this.audioSampleRate);
        media_recorder.setAudioEncoder(this.audioCodec);
    }
    if( MyDebug.LOG )
        Log.d(TAG, "done: " + media_recorder);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

deepinout_camera

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值