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

如何在Swift中将CMSampleBuffer转换为视频Data?

嘿,我来帮你搞定把CMSampleBuffer转换成视频Data的事儿!从相机捕获的CMSampleBuffer是未编码的原始视频帧,要得到可复用的视频Data,我们需要用AVAssetWriter把这些帧编码成标准视频格式(比如MP4),下面是具体实现步骤和代码:

核心思路

相机输出的每一帧CMSampleBuffer都是原始像素数据,我们通过AVAssetWriter将这些帧编码为H.264格式的视频流,最终读取编码后的临时文件内容得到视频Data。

步骤1:初始化编码工具

首先准备AVAssetWriter(负责编码)和AVAssetWriterInput(负责接收帧数据),因为AVAssetWriter需要写入文件,我们用临时文件存储编码结果,最后再转成Data:

import AVFoundation

private var assetWriter: AVAssetWriter?
private var videoInput: AVAssetWriterInput?
private var tempVideoURL: URL?

func setupAssetWriter(with firstSampleBuffer: CMSampleBuffer) {
    do {
        // 创建唯一的临时文件URL
        tempVideoURL = FileManager.default.temporaryDirectory.appendingPathComponent("temp_\(UUID().uuidString).mp4")
        guard let tempURL = tempVideoURL else { return }
        
        // 清理已存在的同名临时文件
        if FileManager.default.fileExists(atPath: tempURL.path) {
            try FileManager.default.removeItem(at: tempURL)
        }
        
        // 初始化AVAssetWriter,指定输出格式为MP4
        assetWriter = try AVAssetWriter(outputURL: tempURL, fileType: .mp4)
        
        // 从第一帧获取视频基础信息(分辨率、格式)
        let formatDesc = CMSampleBufferGetFormatDescription(firstSampleBuffer)!
        let videoDimensions = CMVideoFormatDescriptionGetDimensions(formatDesc)
        let frameRate = 30.0 // 可根据相机实际帧率调整,比如从AVCaptureDevice获取
        
        // 配置视频编码参数
        let videoSettings: [String: Any] = [
            AVVideoCodecKey: AVVideoCodecType.h264,
            AVVideoWidthKey: videoDimensions.width,
            AVVideoHeightKey: videoDimensions.height,
            AVVideoCompressionPropertiesKey: [
                AVVideoAverageBitRateKey: 5_000_000, // 比特率,数值越大画质越好但文件越大
                AVVideoMaxKeyFrameIntervalKey: Int(frameRate) // 关键帧间隔,通常设为帧率值
            ]
        ]
        
        // 创建视频输入,开启实时数据接收模式
        videoInput = AVAssetWriterInput(mediaType: .video, outputSettings: videoSettings)
        videoInput?.expectsMediaDataInRealTime = true
        
        // 将视频输入添加到编码工具
        if let input = videoInput, assetWriter?.canAdd(input) == true {
            assetWriter?.add(input)
        }
        
        // 开始编码会话
        assetWriter?.startWriting()
        assetWriter?.startSession(atSourceTime: CMSampleBufferGetPresentationTimeStamp(firstSampleBuffer))
    } catch {
        print("初始化编码工具失败:\(error.localizedDescription)")
    }
}

步骤2:在捕获回调中写入帧数据

在你现有的captureOutput方法里,把每一帧CMSampleBuffer喂给编码工具:

public func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
    // 首次捕获帧时初始化编码工具
    if assetWriter == nil {
        setupAssetWriter(with: sampleBuffer)
        return
    }
    
    guard let assetWriter = assetWriter, let videoInput = videoInput else { return }
    
    // 确保编码工具处于写入状态,且输入端口准备好接收数据
    if assetWriter.status == .writing && videoInput.isReadyForMoreMediaData {
        videoInput.append(sampleBuffer)
    }
    
    // 你可以根据业务逻辑,在合适时机调用stopRecording()结束录制
    // 比如用户点击停止按钮时触发
}

步骤3:停止录制并获取视频Data

当需要结束录制时,调用下面的方法完成编码并提取视频Data:

func stopRecording(completion: @escaping (Data?) -> Void) {
    guard let assetWriter = assetWriter, let videoInput = videoInput else {
        completion(nil)
        return
    }
    
    // 标记输入数据结束
    videoInput.markAsFinished()
    
    // 完成编码并处理结果
    assetWriter.finishWriting { [weak self] in
        guard let self = self, assetWriter.status == .completed else {
            print("视频编码失败:\(assetWriter.error?.localizedDescription ?? "未知错误")")
            completion(nil)
            return
        }
        
        do {
            // 读取临时文件内容为Data
            let videoData = try Data(contentsOf: self.tempVideoURL!)
            completion(videoData)
            
            // 清理临时文件
            try FileManager.default.removeItem(at: self.tempVideoURL!)
        } catch {
            print("读取视频Data失败:\(error.localizedDescription)")
            completion(nil)
        }
    }
}

注意事项

  • 格式兼容性:相机输出的CMSampleBuffer通常是YUV格式(如kCVPixelFormatType_420YpCbCr8BiPlanarFullRange),H.264编码原生支持这种格式,无需额外转换。
  • 状态监听:要关注assetWriter.status的变化,及时处理编码过程中的错误(比如磁盘空间不足、格式不支持等)。
  • 实时性保障:因为是相机实时捕获,videoInput的expectsMediaDataInRealTime必须设为true,确保能及时接收帧数据。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 08:13:07