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

如何通过CMSampleBuffer准确计算AVAssetWriter录制的视频容量?

问题描述

我通过AVCaptureVideoDataOutputSampleBufferDelegate的captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)方法录制视频,使用AVAssetWriter将接收到的CMSampleBuffer保存至指定URL。需要让用户看到实时更新的录制视频容量,但目前通过累加CMSampleBuffer原始像素大小的方式计算结果不准确,请问如何通过CMSampleBuffer正确计算该容量?

用户实现代码:

func writeBufferVideoToFile(assetWriter: AVAssetWriter,
                            sampleBuffer: CMSampleBuffer) {
    guard isRecoding else {
        return
    }
    // Setup start record
    if assetWriter.status == .unknown {
        totalBytesWritten = 0
        assetWriter.startWriting()
        assetWriter.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(sampleBuffer))
    }
    // Record data
    if let assetWriterInput = assetWriter.currentInput,
       assetWriterInput.isReadyForMoreMediaData {
        // Calculate the total number of bytes written
        totalBytesWritten += Double(calculateSampleBufferSize(sampleBuffer: sampleBuffer))
        // Record
        assetWriterInput.append(sampleBuffer)
    }
}

func calculateSampleBufferSize(sampleBuffer: CMSampleBuffer) -> Int {
    guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else {
        return 0
    }
    
    CVPixelBufferLockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0))
    let size = CVPixelBufferGetDataSize(imageBuffer)
    CVPixelBufferUnlockBaseAddress(imageBuffer, CVPixelBufferLockFlags(rawValue: 0))
    
    return size
}
正确计算方案

问题根源

你当前计算的是原始未压缩的像素缓冲区大小,但AVAssetWriter写入文件的是编码后的压缩媒体数据(如H.264/H.265),两者体积差异极大,所以累加原始数据的结果会远大于实际文件大小。

解决方案1:获取CMSampleBuffer中的编码数据大小

直接从CMSampleBuffer的CMBlockBuffer中提取编码后的实际数据长度:

func calculateEncodedSampleBufferSize(sampleBuffer: CMSampleBuffer) -> Int {
    guard let dataBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) else {
        return 0
    }
    var totalLength = 0
    var dataPointer: UnsafeMutablePointer<UInt8>?
    let status = CMBlockBufferGetDataPointer(dataBuffer, 
                                             atOffset: 0, 
                                             lengthAtOffsetOut: nil, 
                                             totalLengthOut: &totalLength, 
                                             dataPointerOut: &dataPointer)
    guard status == kCMBlockBufferNoErr else {
        return 0
    }
    return totalLength
}

替换原代码中的calculateSampleBufferSize调用即可:

totalBytesWritten += Double(calculateEncodedSampleBufferSize(sampleBuffer: sampleBuffer))

解决方案2:直接读取目标文件的实际大小

如果需要最精准的实时数值,可以直接读取磁盘上目标文件的属性,这种方式完全匹配实际文件大小:

func getCurrentRecordedFileSize(url: URL) -> Double {
    do {
        let fileAttributes = try FileManager.default.attributesOfItem(atPath: url.path)
        return fileAttributes[.size] as? Double ?? 0
    } catch {
        return 0
    }
}

在每次调用assetWriterInput.append(sampleBuffer)之后,调用该方法获取最新文件大小并更新显示即可。


内容的提问来源于stack exchange,提问作者Tú Trần

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 01:40:12