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
相关产品推荐
相关产品推荐

