基于Int16数组立体声通道生成.mov/.m4a文件的技术问题
解决PCM转M4A/AAC文件损坏、杂音慢放的问题
我来帮你排查下这个问题,你的代码里有几个关键的设置和数据处理错误,导致生成的音频文件出现损坏、杂音或慢放的情况,下面是具体的问题分析和修正方案:
问题分析
- AVAudioFile初始化参数错误:创建用于写入的
AVAudioFile时,你传入了PCM格式(.pcmFormatInt16),但我们要输出的是AAC编码的M4A文件,这个参数应该匹配输出格式,而非输入的原始PCM格式。 - 输出设置冗余/错误:
AVLinearPCMIsBigEndianKey是PCM原始数据的属性,不是AAC编码的输出设置,而且iOS平台的Int16 PCM默认是小端模式(little-endian),你设置为true会导致数据解析错乱。AVAudioFileTypeKey与输出文件类型不匹配:生成M4A文件时应该使用kAudioFileM4AType,而非kAudioFileAAC_ADTSType(后者是纯AAC文件的类型)。
- PCM缓冲区帧数设置不准确:你的
frameCapacity计算有误,左右声道的帧数应该是相同的,直接用leftBuffer.count即可,不需要相加再除以2。
修正后的代码
private func convertToM4a(leftChannel leftPath: URL, rightChannel rightPath: URL, converterCallback: ConverterCallback) { let m4aUrl = FileManagerUtil.getTempFileName(parentFolder: FrameExtractor.PCM_ENCODE_FOLDER, fileNameWithExtension: "encodedAudio.m4a") // 先删除已存在的文件 if FileManager.default.fileExists(atPath: m4aUrl.path) { try? FileManager.default.removeItem(atPath: m4aUrl.path) } do { // 读取左右声道的Int16 PCM数据 let leftBuffer = try NSArray(contentsOf: leftPath) as! [Int16] let rightBuffer = try NSArray(contentsOf: rightPath) as! [Int16] guard leftBuffer.count == rightBuffer.count else { print("左右声道帧数不一致") return } let sampleRate = 44100 let channels = 2 let frameCount = leftBuffer.count // 修正后的输出设置 let outputSettings: [String: Any] = [ AVFormatIDKey: kAudioFormatMPEG4AAC, AVSampleRateKey: sampleRate, AVNumberOfChannelsKey: channels, // 设置正确的文件类型:M4A对应kAudioFileM4AType AVAudioFileTypeKey: kAudioFileM4AType, // 可选:添加AAC编码的比特率设置,提升音质 AVEncoderBitRateKey: 128000 ] // 根据输出设置创建AVAudioFormat guard let outputFormat = AVAudioFormat(settings: outputSettings) else { print("无法创建输出格式") return } // 初始化用于写入的AVAudioFile,不需要指定commonFormat,由outputFormat决定 let audioFile = try AVAudioFile(forWriting: m4aUrl, format: outputFormat) // 创建PCM缓冲区,匹配输入的PCM格式(Int16、双声道、非交错) guard let pcmFormat = AVAudioFormat(commonFormat: .pcmFormatInt16, sampleRate: Double(sampleRate), channels: AVAudioChannelCount(channels), interleaved: false), let pcmBuffer = AVAudioPCMBuffer(pcmFormat: pcmFormat, frameCapacity: AVAudioFrameCount(frameCount)) else { print("无法创建PCM缓冲区") return } pcmBuffer.frameLength = AVAudioFrameCount(frameCount) // 填充左右声道数据 let leftChannelData = pcmBuffer.int16ChannelData![0] let rightChannelData = pcmBuffer.int16ChannelData![1] for i in 0..<frameCount { leftChannelData[i] = leftBuffer[i] rightChannelData[i] = rightBuffer[i] } // 写入PCM数据到文件 try audioFile.write(from: pcmBuffer) converterCallback.m4aEncoded(to: m4aUrl) } catch { print("编码错误:\(error.localizedDescription)") } }
额外说明
- 如果需要生成纯AAC文件(.aac后缀),只需要把
AVAudioFileTypeKey改为kAudioFileAAC_ADTSType,同时输出文件的后缀改为.aac即可。 - 建议添加左右声道帧数的校验,避免因数据不一致导致的异常。
- 可选添加
AVEncoderBitRateKey设置编码比特率,根据需求调整(比如128kbps、256kbps),平衡音质和文件大小。
内容的提问来源于stack exchange,提问作者Mohammed Atif
相关产品推荐
相关产品推荐

