如何通过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
相关产品推荐
相关产品推荐

