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

如何用AVFoundation提升视频帧率?时间信息为零问题求解

AVFoundation提升视频帧率问题排查与解决方案

一、方案可行性分析

复制帧并缩短单帧时长的方案是可行的,属于帧率提升中的「帧复制」(Frame Duplication)方法,适合对画质要求不高、追求简单实现的场景。但该方案没有帧插值逻辑,运动画面会出现卡顿感,流畅度不如帧插值类方案(如AVVideoComposition硬件插值或第三方算法)。

二、时间信息为零的原因

你通过CMSampleBufferGetSampleTimingInfo获取到大量零值时间信息,核心原因有两点:

  1. 视频帧存储特性:MP4等容器中,I帧(关键帧)会携带完整时间信息,但P帧/B帧的时间依赖轨道全局时序,不会单独存储在SampleBuffer中,直接读取会返回默认零值。
  2. AVAssetReader配置问题:当outputSettings设为nil时,输出的是原始压缩格式SampleBuffer,这类Buffer的时间信息不会直接暴露在CMSampleTimingInfo中,需通过轨道属性计算。

三、正确获取时间信息的方法

放弃直接从SampleBuffer读取时序,改用以下两种可靠方式:

方法1:通过轨道加载帧时间范围

let asset = AVURLAsset(url: url)
let videoTrack = asset.tracks(withMediaType: .video)[0]
let totalDuration = asset.duration

videoTrack.loadTrackTimeRange(CMTimeRangeMake(start: .zero, duration: totalDuration), forMediaType: .video) { timeRanges in
    for (index, timeRange) in timeRanges.enumerated() {
        print("\(index):")
        print("时长:\(timeRange.duration)")
        print("时间戳:\(timeRange.start)")
    }
}

方法2:修正SampleBuffer读取逻辑

如果坚持使用AVAssetReader,改用更可靠的API读取时间,当返回零值时用轨道标称帧率补全:

if let sampleBuffer {
    let presentationTime = CMSampleBufferGetPresentationTimeStamp(sampleBuffer)
    var duration = CMSampleBufferGetDuration(sampleBuffer)
    
    // 若时长为零,用轨道标称帧率计算单帧时长
    if CMTimeCompare(duration, .zero) == 0 {
        let nominalFrameRate = videoTrack.nominalFrameRate
        duration = CMTimeMake(value: 1, timescale: Int32(nominalFrameRate))
    }
    
    print("\(nFrames):")
    print("时长:\(duration)")
    print("时间戳:\(presentationTime)")
    
    nFrames += 1
}

四、完整帧率翻倍实现步骤

  1. 读取原始帧:用上述方法正确获取每帧的时间戳和时长。
  2. 复制并调整时序:对每帧复制一份,将原帧与复制帧的时长均设为原时长的1/2;复制帧的时间戳设为原帧时间戳 + 原时长的1/2。
  3. 写入新视频:用AVAssetWriter输出处理后的帧,设置新的帧率参数(原帧率×2)。

写入环节示例代码:

let outputURL = // 输出文件路径
let writer = try AVAssetWriter(outputURL: outputURL, fileType: .mp4)
let videoTrack = asset.tracks(withMediaType: .video)[0]

let videoInput = AVAssetWriterInput(mediaType: .video, outputSettings: [
    AVVideoCodecKey: AVVideoCodecType.h264,
    AVVideoWidthKey: videoTrack.naturalSize.width,
    AVVideoHeightKey: videoTrack.naturalSize.height,
    AVVideoCompressionPropertiesKey: [
        AVVideoAverageBitRateKey: videoTrack.estimatedDataRate * 2
    ]
])
writer.add(videoInput)
writer.startWriting()
writer.startSession(atSourceTime: .zero)

// 假设processedSampleBuffers是处理后的帧数组(原帧+复制帧)
videoInput.requestMediaDataWhenReady(on: .global()) {
    while videoInput.isReadyForMoreMediaData {
        guard let sampleBuffer = processedSampleBuffers.popFirst() else {
            videoInput.markAsFinished()
            writer.finishWriting {
                // 处理完成回调
            }
            return
        }
        videoInput.append(sampleBuffer)
    }
}

五、进阶方案与资源

如果追求更流畅的帧率提升,推荐:

  • AVVideoComposition硬件插值:iOS 13+支持通过AVMutableVideoComposition设置frameDuration,系统会自动完成硬件加速的帧插值。
  • 第三方库:使用FFmpegKit(支持多种帧率提升算法)或MetalPetal(基于Metal的实时帧插值)实现更自然的运动画面。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 13:07:47