从视频文件中读取音频数据并获取AVAudioPCMBuffer的方法

ChatGPT4o 使用 ChatGPT 4o 回答

问题:

如何从视频文件中,读取音频数据,并获取 AVAudioPCMBuffer

回答:

要从视频文件中读取音频数据并获取 AVAudioPCMBuffer,你可以使用 Apple 的 AVFoundation 框架。以下是一个示例代码,展示了如何从视频文件中提取音频数据并将其转换为 AVAudioPCMBuffer

首先,确保你已经导入了必要的框架:

import AVFoundation

然后,你可以使用以下代码来实现这个功能:

import AVFoundation

func extractAudioPCMBuffer(from videoURL: URL) -> AVAudioPCMBuffer? {
    // 创建 AVAsset 实例
    let asset = AVAsset(url: videoURL)
    
    // 创建 AVAssetReader 实例
    guard let assetReader = try? AVAssetReader(asset: asset) else {
        print("Failed to create AVAssetReader")
        return nil
    }
    
    // 获取音频轨道
    guard let audioTrack = asset.tracks(withMediaType: .audio).first else {
        print("No audio track found")
        return nil
    }
    
    // 创建 AVAssetReaderTrackOutput 实例
    let outputSettings: [String: Any] = [
        AVFormatIDKey: kAudioFormatLinearPCM,
        AVLinearPCMIsNonInterleaved: true,
        AVLinearPCMBitDepthKey: 16,
        AVLinearPCMIsFloatKey: false,
        AVLinearPCMIsBigEndianKey: false
    ]
    let trackOutput = AVAssetReaderTrackOutput(track: audioTrack, outputSettings: outputSettings)
    assetReader.add(trackOutput)
    
    // 开始读取
    assetReader.startReading()
    
    // 创建 AVAudioFormat 实例
    let audioFormat = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: audioTrack.naturalTimeScale, channels: 1, interleaved: false)
    
    // 创建 AVAudioPCMBuffer 实例
    guard let buffer = AVAudioPCMBuffer(pcmFormat: audioFormat!, frameCapacity: AVAudioFrameCount(audioTrack.naturalTimeScale)) else {
        print("Failed to create AVAudioPCMBuffer")
        return nil
    }
    
    // 读取音频数据
    while assetReader.status == .reading {
        if let sampleBuffer = trackOutput.copyNextSampleBuffer(),
           let blockBuffer = CMSampleBufferGetDataBuffer(sampleBuffer) {
            var length = 0
            var dataPointer: UnsafeMutablePointer<Int8>?
            CMBlockBufferGetDataPointer(blockBuffer, atOffset: 0, lengthAtOffsetOut: &length, totalLengthOut: &length, dataPointerOut: &dataPointer)
            
            if let dataPointer = dataPointer {
                let audioBuffer = buffer.audioBufferList.pointee.mBuffers
                memcpy(audioBuffer.mData, dataPointer, length)
                buffer.frameLength = AVAudioFrameCount(length) / audioFormat!.streamDescription.pointee.mBytesPerFrame
            }
        }
    }
    
    if assetReader.status == .completed {
        return buffer
    } else {
        print("Failed to read audio data: \(assetReader.error?.localizedDescription ?? "Unknown error")")
        return nil
    }
}

// 使用示例
if let videoURL = Bundle.main.url(forResource: "example", withExtension: "mp4") {
    if let audioBuffer = extractAudioPCMBuffer(from: videoURL) {
        print("Successfully extracted audio buffer")
    } else {
        print("Failed to extract audio buffer")
    }
}

请注意,这段代码假设音频轨道是单声道(channels: 1)并且使用 16 位 PCM 格式。如果你的音频轨道有不同的配置,你需要相应地调整 AVAudioFormatoutputSettings

此外,这段代码是一个基本示例,可能需要根据具体需求进行调整和优化。