iOS 实时录音并将其分割成文件

3

我正在制作一款需要向服务器流媒体音频的应用程序。我想要做的是将录制的音频分成块并在录制时上传它们。

我使用了两个录音机来实现这个目标,但效果不佳;我可以听到块之间的差异(停顿几毫秒)。

我该如何解决这个问题?

1个回答

10
你的问题可以分为两个部分:录音和分块(以及上传,但谁在乎呢)。
对于从麦克风录制并写入文件,你可以快速开始使用 AVAudioEngineAVAudioFile。下面是一个示例,它以设备默认输入采样率记录块(你可能需要进行速率转换)。
当你谈论“块之间的差异”时,你指的是将音频数据分成片段的能力,以便在连接它们时不会听到不连续性。例如,LPCM 音频数据可以在样本级别上分成块,但 LPCM 比特率很高,因此你更有可能使用类似 ADPCM(在 iOS 上称为 ima4?)、mp3 或 aac 的分组格式。这些格式只能在分组边界上进行分割,例如 64、576 或 1024 个样本。如果你的块没有头文件(对于 mp3 和 aac 通常如此,ima4 不确定),那么连接就很简单:只需将块端对端放置,就像命令行工具 cat 一样。遗憾的是,在 iOS 上没有 mp3 编码器,所以这使得 aac 成为你可能的格式,但这取决于你的播放要求。iOS 设备和 Mac 可以肯定地播放它。
import AVFoundation

class ViewController: UIViewController {

    let engine = AVAudioEngine()

    struct K {
        static let secondsPerChunk: Float64 = 10
    }

    var chunkFile: AVAudioFile! = nil
    var outputFramesPerSecond: Float64 = 0  // aka input sample rate
    var chunkFrames: AVAudioFrameCount = 0
    var chunkFileNumber: Int = 0

    func writeBuffer(_ buffer: AVAudioPCMBuffer) {
        let samplesPerSecond = buffer.format.sampleRate

        if chunkFile == nil {
            createNewChunkFile(numChannels: buffer.format.channelCount, samplesPerSecond: samplesPerSecond)
        }

        try! chunkFile.write(from: buffer)
        chunkFrames += buffer.frameLength

        if chunkFrames > AVAudioFrameCount(K.secondsPerChunk * samplesPerSecond) {
            chunkFile = nil // close file
        }
    }

    func createNewChunkFile(numChannels: AVAudioChannelCount, samplesPerSecond: Float64) {
        let fileUrl = NSURL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("chunk-\(chunkFileNumber).aac")!
        print("writing chunk to \(fileUrl)")

        let settings: [String: Any] = [
            AVFormatIDKey: kAudioFormatMPEG4AAC,
            AVEncoderBitRateKey: 64000,
            AVNumberOfChannelsKey: numChannels,
            AVSampleRateKey: samplesPerSecond
        ]

        chunkFile = try! AVAudioFile(forWriting: fileUrl, settings: settings)

        chunkFileNumber += 1
        chunkFrames = 0
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        let input = engine.inputNode!

        let bus = 0
        let inputFormat = input.inputFormat(forBus: bus)

        input.installTap(onBus: bus, bufferSize: 512, format: inputFormat) { (buffer, time) -> Void in
            DispatchQueue.main.async {
                self.writeBuffer(buffer)
            }
        }

        try! engine.start()
    }
}

超级解释兄弟 :) - undefined
我查看了代码,当我调用写入缓冲区函数时,它会崩溃,并显示以下错误:'com.apple.coreaudio.avfaudio',原因是'error -50'。 - undefined
@RhythmicFistman 我搜索了很多,但是找不到使用 AVAudioEngine 录制音频的示例!希望你能找到一些相关资料。谢谢! - undefined
@RhythmicFistman,我尝试了你的代码,它可以正常工作,记录音频并将其写入aac文件目录。但是当我尝试读取该文件并从中获取缓冲区时,返回的是空缓冲区。我正在使用AVAudioFile来读取文件。你能否给予一些解释? - undefined
奇怪 - 或许可以开一个新的问题,询问如何使用AVAudioFile读取AAC文件。 - undefined
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接