You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

AVFoundation 240fps录制无法返回相机内参技术咨询

解决方案:240fps录制时无法获取相机内参的问题

我之前做iOS慢动作录制项目时也碰到过一模一样的问题,核心原因要么是硬件对高帧率(240fps)模式下的内参交付支持有限,要么是你的配置顺序搞错了导致的。下面是几个经过验证的可行方案:

1. 先锁定240fps格式,再检查内参支持性

你之前的代码大概率是先检查了connection的内参支持,再设置240fps格式——这是错误的!isCameraIntrinsicMatrixDeliverySupported的返回值完全依赖当前激活的相机格式(帧率、分辨率)。正确的流程应该是先指定240fps格式,再获取connection并检查支持性。

示例代码:

// 1. 获取后置广角相机
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
NSError *configError;

if ([videoDevice lockForConfiguration:&configError]) {
    // 2. 找到支持240fps的目标格式(以1080p为例,可根据设备调整分辨率)
    AVCaptureDeviceFormat *target240fpsFormat = nil;
    for (AVCaptureDeviceFormat *format in videoDevice.formats) {
        CMVideoDimensions dims = CMVideoFormatDescriptionGetDimensions(format.formatDescription);
        if (dims.width == 1920 && dims.height == 1080) {
            for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
                if (range.maxFrameRate >= 240.0 && range.minFrameRate <= 240.0) {
                    target240fpsFormat = format;
                    break;
                }
            }
        }
        if (target240fpsFormat) break;
    }

    // 3. 激活240fps格式
    if (target240fpsFormat) {
        [videoDevice setActiveFormat:target240fpsFormat];
        [videoDevice setActiveVideoMinFrameDuration:CMTimeMake(1, 240)];
        [videoDevice setActiveVideoMaxFrameDuration:CMTimeMake(1, 240)];
    }
    [videoDevice unlockForConfiguration];
}

// 4. 创建输入并添加到会话
AVCaptureDeviceInput *videoInput = [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:&configError];
if ([self.session canAddInput:videoInput]) {
    [self.session addInput:videoInput];
}

// 5. 此时再检查并启用内参交付
AVCaptureConnection *videoConnection = [self.videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
if (@available(iOS 11.0, *)) {
    if (videoConnection.isCameraIntrinsicMatrixDeliverySupported) {
        videoConnection.cameraIntrinsicMatrixDeliveryEnabled = YES;
        NSLog(@"已成功启用240fps模式下的内参交付");
    } else {
        NSLog(@"当前设备的240fps格式不支持内参交付,需要使用替代方案");
    }
}

2. 硬件不支持时的替代方案:提前缓存内参

如果你的设备(比如部分旧款iPhone)在240fps模式下确实不支持内参交付,不用慌——相机内参在光学焦距不变的情况下是固定的(除非你使用了光学变焦)。你可以提前在常规帧率(比如30fps)下获取一次内参,然后和录制的视频文件关联存储。

示例代码:

__block CMatrixFloat3x3 cachedIntrinsicMatrix = CMatrixFloat3x3Identity;
AVCaptureDevice *videoDevice = [AVCaptureDevice defaultDeviceWithDeviceType:AVCaptureDeviceTypeBuiltInWideAngleCamera mediaType:AVMediaTypeVideo position:AVCaptureDevicePositionBack];
NSError *error;

// 临时切换到30fps格式获取内参
if ([videoDevice lockForConfiguration:&error]) {
    AVCaptureDeviceFormat *temp30fpsFormat = nil;
    for (AVCaptureDeviceFormat *format in videoDevice.formats) {
        for (AVFrameRateRange *range in format.videoSupportedFrameRateRanges) {
            if (range.maxFrameRate >= 30.0 && range.minFrameRate <= 30.0) {
                temp30fpsFormat = format;
                break;
            }
        }
        if (temp30fpsFormat) break;
    }

    if (temp30fpsFormat) {
        [videoDevice setActiveFormat:temp30fpsFormat];
        [videoDevice setActiveVideoMinFrameDuration:CMTimeMake(1, 30)];
        [videoDevice setActiveVideoMaxFrameDuration:CMTimeMake(1, 30)];
    }
    [videoDevice unlockForConfiguration];
}

// 启动会话捕获一帧获取内参
[self.session startRunning];
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

id tempDelegate = [[NSObject alloc] initWithBlock:^(AVCaptureOutput *output, CMSampleBufferRef sampleBuffer, AVCaptureConnection *connection) {
    if (@available(iOS 11.0, *)) {
        cachedIntrinsicMatrix = CMGetAttachment(sampleBuffer, kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix, NULL);
    }
    dispatch_semaphore_signal(semaphore);
    [self.videoDataOutput setSampleBufferDelegate:nil queue:nil];
}];
[self.videoDataOutput setSampleBufferDelegate:tempDelegate queue:dispatch_get_main_queue()];
dispatch_semaphore_wait(semaphore, dispatch_time(DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC));
[self.session stopRunning];

// 切换回240fps格式开始录制(重复之前的240fps配置代码)
// 录制完成后,将cachedIntrinsicMatrix写入视频元数据或单独保存

3. 确保AVAssetWriter正确保留元数据

即使sampleBuffer中包含内参,如果你没正确配置AVAssetWriterInput,写入文件时可能会丢失元数据。需要注意:

  • 务必设置videoInput.expectsMediaDataInRealTime = YES
  • 直接调用[videoInput appendSampleBuffer:sampleBuffer],该方法会自动保留sampleBuffer中的附件(包括内参)
  • 如需手动管理元数据,可使用AVAssetWriterInputMetadataAdaptor

示例代码片段:

// 创建AVAssetWriterInput
NSDictionary *videoOutputSettings = @{
    AVVideoCodecKey: AVVideoCodecTypeH264,
    AVVideoWidthKey: @(1920),
    AVVideoHeightKey: @(1080)
};
AVAssetWriterInput *videoInput = [[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo outputSettings:videoOutputSettings];
videoInput.expectsMediaDataInRealTime = YES;

// 添加到AVAssetWriter
if ([self.assetWriter canAddInput:videoInput]) {
    [self.assetWriter addInput:videoInput];
}

// 在captureOutput回调中写入缓冲区
- (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer fromConnection:(AVCaptureConnection *)connection {
    if (self.assetWriter.status == AVAssetWriterStatusWriting && videoInput.isReadyForMoreMediaData) {
        [videoInput appendSampleBuffer:sampleBuffer];
        // 如果需要手动保存内参,这里可以从sampleBuffer中读取并存储
        if (@available(iOS 11.0, *)) {
            CMatrixFloat3x3 intrinsic = CMGetAttachment(sampleBuffer, kCMSampleBufferAttachmentKey_CameraIntrinsicMatrix, NULL);
            // 保存intrinsic到文件或内存
        }
    }
}

内容的提问来源于stack exchange,提问作者Robert Turrall

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:30:16