当前位置: 首页 > article >正文

使用 Swift 完成FFmpeg音频录制、播放和视频格式转换应用

使用 Swift 构建音频录制、播放和视频格式转换应用

在这篇博客中,我们介绍如何用ffmpeg在swift上实现音频录制、音频播放、通过ffmpeg命令实现视频格式转换

  1. 音频录制:通过 AVAudioRecorder 实现音频录制功能。
  2. 音频播放:通过 AVAudioPlayer 实现录制音频的播放。
  3. 视频格式转换:通过 FFmpegKit 实现视频格式的转换。

这段代码展示了如何结合 iOS 的音频和视频处理框架,以及第三方库 FFmpegKit,来构建一个功能丰富的多媒体应用。
完整代码:

import AVFoundation
import Foundation

// 定义协议,用于通知录音状态的变化
protocol AudioRecorderDelegate: AnyObject {
    func customAudioRecorderDidFinishRecording(successfully flag: Bool)
    func customAudioRecorderDidEncounterError(_ error: Error)
}

class AudioRecorder: NSObject {
    private var audioRecorder: AVAudioRecorder?
    private var recordingSession: AVAudioSession!
    weak var delegate: AudioRecorderDelegate?
    
    private var isRecording: Bool = false
    
    // 录音文件保存路径(可自定义)
    private var recordingFileURL: URL {
        // 获取 Documents 目录
        let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        // 创建文件名,修改扩展名为 .wav
        let audioFilename = documentsPath.appendingPathComponent("recording.wav")
        return audioFilename
    }
    
    // 请求麦克风权限并设置音频会话排
    func requestPermissionAndSetupSession(completion: @escaping (Bool) -> Void) {
        recordingSession = AVAudioSession.sharedInstance()
        
        // 请求麦克风权限
        AVAudioSession.sharedInstance().requestRecordPermission { [unowned self] allowed in
            DispatchQueue.main.async {
                if allowed {
                    do {
                        // 设置音频会话类别和模式
                        try recordingSession.setCategory(.playAndRecord, mode: .default)
                        try recordingSession.setActive(true)
                        completion(true)
                    } catch {
                        print("音频会话配置失败:\(error.localizedDescription)")
                        self.delegate?.customAudioRecorderDidEncounterError(error)
                        completion(false)
                    }
                } else {
                    print("麦克风权限被拒绝")
                    completion(false)
                }
            }
        }
    }
    
    // 开始录音
    func startRecording() {
        // 设置录音参数
        let settings: [String: Any] = [
            AVFormatIDKey: Int(kAudioFormatLinearPCM), // 音频格式改为 PCM
            AVSampleRateKey: 44100, // 采样率
            AVNumberOfChannelsKey: 2, // 声道数
            AVLinearPCMBitDepthKey: 16, // 位深度(常用 16 位), 使用多少个二进制来存储一个采样点的样本值,位深度越高,表示振幅越精确
            AVLinearPCMIsBigEndianKey: false, // 是否大端字节序
            AVLinearPCMIsFloatKey: false, // 是否浮点型
            AVLinearPCMIsNonInterleaved: false, // 是否非交错
        ]
        
        do {
            audioRecorder = try AVAudioRecorder(url: recordingFileURL, settings: settings)
            audioRecorder?.delegate = self
            audioRecorder?.record()
            isRecording = true
            print("开始录音")
        } catch {
            print("录音器初始化失败:\(error.localizedDescription)")
            delegate?.customAudioRecorderDidEncounterError(error)
        }
    }
    
    // 停止录音
    func stopRecording() {
        audioRecorder?.stop()
        isRecording = false
        print("停止录音")
    }
    
    // 判断是否正在录音
    func isRecordingActive() -> Bool {
        return isRecording
    }
    
    // 获取录音文件的 URL
    func getRecordingFileURL() -> URL {
        return recordingFileURL
    }
}

// MARK: - AVAudioRecorderDelegate

extension AudioRecorder: AVAudioRecorderDelegate {
    // 录音完成后的回调
    func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
        delegate?.customAudioRecorderDidFinishRecording(successfully: flag)
    }
    
    // 录音发生错误的回调
    func audioRecorderEncodeErrorDidOccur(_ recorder: AVAudioRecorder, error: Error?) {
        if let error = error {
            print("录音发生错误:\(error.localizedDescription)")
            delegate?.customAudioRecorderDidEncounterError(error)
        }
    }
}


功能概述

1. 音频录制

通过 AVAudioRecorder 实现音频录制功能,录制的音频保存为 .wav 格式。

2. 音频播放

通过 AVAudioPlayer 播放录制的音频文件。

3. 视频格式转换

通过 FFmpegKit 将视频文件从 .mp4 格式转换为另一个 .mp4 文件(可以自定义编码器和参数)。


代码分析

1. 音频录制功能

AudioRecorder 类

AudioRecorder 类封装了音频录制的逻辑,使用 AVAudioRecorder 进行录音,并通过代理通知录音状态的变化。

关键代码
  • 录音文件保存路径

    private var recordingFileURL: URL {
        let documentsPath = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)[0]
        let audioFilename = documentsPath.appendingPathComponent("recording.wav")
        return audioFilename
    }
    

    录音文件保存在应用的 Documents 目录下,文件名为 recording.wav

  • 请求麦克风权限

    func requestPermissionAndSetupSession(completion: @escaping (Bool) -> Void) {
        recordingSession = AVAudioSession.sharedInstance()
        AVAudioSession.sharedInstance().requestRecordPermission { [unowned self] allowed in
            DispatchQueue.main.async {
                if allowed {
                    do {
                        try recordingSession.setCategory(.playAndRecord, mode: .default)
                        try recordingSession.setActive(true)
                        completion(true)
                    } catch {
                        self.delegate?.customAudioRecorderDidEncounterError(error)
                        completion(false)
                    }
                } else {
                    completion(false)
                }
            }
        }
    }
    

    通过 AVAudioSession 请求麦克风权限,并设置音频会话的类别为 .playAndRecord

  • 开始录音

    func startRecording() {
        let settings: [String: Any] = [
            AVFormatIDKey: Int(kAudioFormatLinearPCM),
            AVSampleRateKey: 44100,
            AVNumberOfChannelsKey: 2,
            AVLinearPCMBitDepthKey: 16,
            AVLinearPCMIsBigEndianKey: false,
            AVLinearPCMIsFloatKey: false,
            AVLinearPCMIsNonInterleaved: false,
        ]
        
        do {
            audioRecorder = try AVAudioRecorder(url: recordingFileURL, settings: settings)
            audioRecorder?.delegate = self
            audioRecorder?.record()
            isRecording = true
        } catch {
            delegate?.customAudioRecorderDidEncounterError(error)
        }
    }
    

    设置录音参数(如采样率、声道数、位深度等),并启动录音。

  • 停止录音

    func stopRecording() {
        audioRecorder?.stop()
        isRecording = false
    }
    
  • 代理回调

    func audioRecorderDidFinishRecording(_ recorder: AVAudioRecorder, successfully flag: Bool) {
        delegate?.customAudioRecorderDidFinishRecording(successfully: flag)
    }
    

2. 音频播放功能

音频播放逻辑

通过 AVAudioPlayer 播放录制的 .wav 文件。

关键代码
  • 播放音频

    @objc func playAudio() {
        let audioURL = audioRecorder.getRecordingFileURL()
        do {
            audioPlayer = try AVAudioPlayer(contentsOf: audioURL)
            audioPlayer?.delegate = self
            audioPlayer?.play()
        } catch {
            print("音频播放失败:\(error.localizedDescription)")
        }
    }
    
  • 播放完成回调

    func audioPlayerDidFinishPlaying(_ player: AVAudioPlayer, successfully flag: Bool) {
        print("音频播放完成")
    }
    

3. 视频格式转换功能

FFmpegKit

FFmpegKit 是一个强大的多媒体处理库,支持音视频的编码、解码、转换等操作。

关键代码
  • FFmpeg 命令

    let ffmpegCommand = "\(overwriteOption) -i \"\(inputFile)\" -c:v libx264 -c:a aac \"\(outputFile)\""
    

    该命令将输入文件转换为 H.264 视频编码和 AAC 音频编码的 .mp4 文件。

  • 执行转换

    FFmpegKit.executeAsync(ffmpegCommand) { session in
        let state = session?.getState()
        let returnCode = session?.getReturnCode()
        
        if ReturnCode.isSuccess(returnCode) {
            print("视频转换成功!输出文件位于:\(outputFile)")
        } else {
            if let output = session?.getAllLogsAsString() {
                print("转换失败,输出日志:\n\(output)")
            }
        }
    }
    

    使用 FFmpegKit.executeAsync 异步执行转换命令,并通过回调处理结果。


4. 用户交互

按钮操作
  • 开始录音

    @objc func startRecording() {
        if !audioRecorder.isRecordingActive() {
            audioRecorder.startRecording()
        } else {
            print("正在录音中")
        }
    }
    
  • 停止录音

    @objc func stopRecording() {
        if audioRecorder.isRecordingActive() {
            audioRecorder.stopRecording()
        } else {
            print("当前未在录音")
        }
    }
    
  • 播放音频

    @objc func playAudio() {
        let audioURL = audioRecorder.getRecordingFileURL()
        do {
            audioPlayer = try AVAudioPlayer(contentsOf: audioURL)
            audioPlayer?.delegate = self
            audioPlayer?.play()
        } catch {
            print("音频播放失败:\(error.localizedDescription)")
        }
    }
    
  • 视频格式转换

    @objc func convertVideoFormat() {
        FFmpegKit.executeAsync(ffmpegCommand) { session in
            let state = session?.getState()
            let returnCode = session?.getReturnCode()
            
            if ReturnCode.isSuccess(returnCode) {
                print("视频转换成功!输出文件位于:\(outputFile)")
            } else {
                if let output = session?.getAllLogsAsString() {
                    print("转换失败,输出日志:\n\(output)")
                }
            }
        }
    }
    

疑点说明

在代码中,录音文件的保存路径使用了 .wav 扩展名,但音频格式设置为 kAudioFormatLinearPCM。这可能会让人感到困惑,因为 .wav 通常被认为是 WAV 文件格式,而 kAudioFormatLinearPCM 是一种原始的未压缩音频格式。为了理解为什么这可以工作,我们需要了解 WAV 文件的结构和 PCM 数据的关系。

WAV 文件和 PCM 数据的关系

  1. WAV 文件格式:

    • WAV 文件是一种音频文件格式,它实际上是一个容器格式。
    • WAV 文件的核心内容是 PCM 数据(Pulse Code Modulation,脉冲编码调制),这是一种未压缩的音频数据格式。
    • 除了 PCM 数据,WAV 文件还包含一个文件头(Header),用于描述音频数据的格式(如采样率、声道数、位深度等)。
  2. PCM 数据:

    • PCM 是一种原始的音频数据格式,不包含任何文件头信息。
    • 它只包含音频的采样值,无法单独描述音频的格式。
  3. 为什么可以保存为 .wav 文件:

    • 当你使用 AVAudioRecorder 录制音频时,即使你指定了 kAudioFormatLinearPCMAVAudioRecorder 会自动为录音文件添加 WAV 文件头,使其成为一个合法的 WAV 文件。
    • 这意味着,虽然音频数据本身是 PCM 格式,但由于文件头的存在,最终保存的文件是一个合法的 WAV 文件。

代码中的行为

在你的代码中,以下设置指定了音频格式为 PCM:

AVFormatIDKey: Int(kAudioFormatLinearPCM)

但录音文件的保存路径使用了 .wav 扩展名:

let audioFilename = documentsPath.appendingPathComponent("recording.wav")

AVAudioRecorder 会根据录音设置和文件扩展名自动处理文件格式。在这种情况下,它会将录制的 PCM 数据封装为一个合法的 WAV 文件,并保存到指定路径。

注意事项

  1. 文件扩展名的选择:

    • 虽然 .wav 是一个常见的扩展名,但它只是一个约定,真正决定文件格式的是文件内容。
    • 如果你将文件扩展名改为 .pcm,文件内容仍然是合法的 WAV 文件,只是扩展名可能会让人误解。
  2. 兼容性:

    • 如果你需要与其他程序或设备共享录音文件,确保它们支持 WAV 格式。
    • 如果你需要保存为纯 PCM 数据(没有文件头),你需要手动处理文件的写入。
  3. 自定义文件格式:

    • 如果你需要更灵活的文件格式(如 MP3、AAC 等),可以更改 AVFormatIDKey 的值,并选择合适的文件扩展名。

http://www.kler.cn/a/533557.html

相关文章:

  • 人工智能第2章-知识点与学习笔记
  • 院校联合以项目驱动联合培养医工计算机AI人才路径探析
  • [吾爱出品]CursorWorkshop V6.33 专业鼠标光标制作工具-简体中文汉化绿色版
  • 【大数据技术】教程05:本机DataGrip远程连接虚拟机MySQL/Hive
  • 实战:如何快速让新网站被百度收录?
  • Starrocks 对比 Clickhouse
  • RabbitMQ 从入门到精通:从工作模式到集群部署实战(一)
  • 【OpenCV实战】基于 OpenCV 的多尺度与模板匹配目标跟踪设计与实现
  • 简易C语言矩阵运算库
  • 【C语言】指针详细解读3
  • 激光工控机在自动化领域中有哪些作用?
  • vim modeline
  • CTP查询资金费率和手续费没响应
  • 零基础Vue入门6——Vue router
  • 【CPP】CPP经典面试题
  • Ollama:一站式 AI 模型管理与交互平台,什么是 Ollama,Ollama 的核心功能,Ollama 的使用场景
  • AWS上设计可图形化创建处理逻辑的智能电话语音客服程序的流程和关键代码
  • Junit5使用教程(3)
  • 3.Pandas库
  • oracle ORA-27054报错处理
  • 深入理解和使用定时线程池ScheduledThreadPoolExecutor
  • 智能编码在前端研发的创新应用
  • python爬取网页数据为json该用什么方法?
  • 验证工具:GVIM和VIM
  • java s7接收Byte字节,接收word转16位二进制
  • 当大模型遇上Spark:解锁大数据处理新姿势