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

如何使用Swift将加载到AVAsset中的视频音轨写入AVAudioPCMBuffer

Swift 实现从AVAsset提取音频轨道写入AVAudioPCMBuffer

实现逻辑

核心依赖AVFoundation框架原生API实现,无需调用C语言接口,流程如下:

  • 通过AVAssetReader读取asset中的音频轨道
  • 配置线性PCM格式的轨道输出,与AVAudioPCMBuffer格式兼容
  • 循环读取CMSampleBuffer并将数据写入目标AVAudioPCMBuffer

完整实现代码

import AVFoundation

func readAudioBuffer(from asset: AVAsset) async throws -> AVAudioPCMBuffer {
    // 读取asset的首个音频轨道
    guard let audioTrack = try await asset.loadTracks(withMediaType: .audio).first else {
        throw NSError(domain: "AudioReaderError", code: -1, userInfo: [NSLocalizedDescriptionKey: "Asset无可用音频轨道"])
    }
    
    // 初始化资源读取器
    let assetReader = try AVAssetReader(asset: asset)
    
    // 配置输出为兼容AVAudioPCMBuffer的线性PCM格式
    let outputSettings: [String: Any] = [
        AVFormatIDKey: kAudioFormatLinearPCM,
        AVSampleRateKey: 44100,
        AVNumberOfChannelsKey: 2,
        AVLinearPCMBitDepthKey: 16,
        AVLinearPCMIsBigEndianKey: false,
        AVLinearPCMIsFloatKey: false,
        AVLinearPCMIsNonInterleavedKey: false
    ]
    
    let trackOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: outputSettings)
    guard assetReader.canAdd(trackOutput) else {
        throw NSError(domain: "AudioReaderError", code: -2, userInfo: [NSLocalizedDescriptionKey: "无法添加音频轨道输出"])
    }
    assetReader.add(trackOutput)
    
    // 启动读取
    guard assetReader.startReading() else {
        throw NSError(domain: "AudioReaderError", code: -3, userInfo: [NSLocalizedDescriptionKey: "读取器启动失败: \(assetReader.error?.localizedDescription ?? "未知错误")"])
    }
    
    // 初始化目标PCMBuffer
    let audioFormat = AVAudioFormat(settings: outputSettings)!
    let duration = try await asset.load(.duration)
    let totalFrames = AVAudioFrameCount(duration.seconds * 44100)
    guard let buffer = AVAudioPCMBuffer(pcmFormat: audioFormat, frameCapacity: totalFrames) else {
        throw NSError(domain: "AudioReaderError", code: -4, userInfo: [NSLocalizedDescriptionKey: "PCMBuffer初始化失败"])
    }
    
    // 循环读取采样数据写入buffer
    while assetReader.status == .reading {
        guard let sampleBuffer = trackOutput.copyNextSampleBuffer() else { break }
        defer { CMSampleBufferInvalidate(sampleBuffer) }
        
        guard let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) else { continue }
        let dataLength = CMBlockBufferGetDataLength(blockBuffer)
        let targetOffset = Int(buffer.frameLength) * Int(audioFormat.streamDescription.pointee.mBytesPerFrame)
        
        // 将采样数据写入buffer
        CMBlockBufferCopyDataBytes(
            blockBuffer,
            atOffset: 0,
            dataLength: dataLength,
            destination: buffer.audioBufferList.pointee.mBuffers.mData!.advanced(by: targetOffset)
        )
        buffer.frameLength += AVAudioFrameCount(dataLength / Int(audioFormat.streamDescription.pointee.mBytesPerFrame))
    }
    
    // 处理读取异常
    if assetReader.status == .failed {
        throw assetReader.error!
    }
    
    return buffer
}

注意事项

  • 示例代码使用iOS 15+推出的AVAsset异步加载API,如需兼容更低版本,可替换为传统的闭包式加载接口
  • 音频输出格式可根据实际需求调整,需保证输出格式与AVAudioPCMBuffer的格式完全匹配,避免写入失败
  • 大体积音视频的读取操作建议放在子线程执行,避免阻塞主线程
  • 读取失败时可通过AVAssetReader.error获取具体错误信息定位问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.06 10:36:05