ios swift实现合并音视频教程

2024年11月19日
首页博客

在使用swift开发ios应用时有时候会实现一些音视频合并的工作,有以下方式可以实现。

AVFoundation库

swift自带的AVFoundation库可以开发ios全家桶关于多媒体相关的功能,可以用来实现音视频合并,具体代码如下

static func merge(videoURL: URL, audioURL: URL, outputURL: URL) async throws  {        // 创建一个可变组合对象        let mixComposition = AVMutableComposition()​        // 加载视频资产        guard let videoAsset = AVAsset(url: videoURL) as? AVAsset else {            throw NSError(domain: "Video asset error", code: -1, userInfo: nil)        }​        // 加载音频资产        guard let audioAsset = AVAsset(url: audioURL) as? AVAsset else {            throw NSError(domain: "Audio asset error", code: -2, userInfo: nil)        }​        // 添加视频轨道        guard let videoTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: .min) else {            throw NSError(domain: "Video track error", code: -3, userInfo: nil)        }​        let videoAssetTrack = videoAsset.tracks(withMediaType: .video)[0]        try videoTrack.insertTimeRange(CMTimeRange(start: .zero, duration: videoAsset.duration), of: videoAssetTrack, at: .zero)​        // 添加音频轨道        guard let audioTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: .min) else {            throw NSError(domain: "Audio track error", code: -4, userInfo: nil)        }​        let audioAssetTrack = audioAsset.tracks(withMediaType: .audio)[0]        try audioTrack.insertTimeRange(CMTimeRange(start: .zero, duration: audioAsset.duration), of: audioAssetTrack, at: .zero)​        // 设置输出文件格式        guard let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else {            throw NSError(domain: "Export session error", code: -5, userInfo: nil)        }​        exportSession.outputURL = outputURL        exportSession.outputFileType = .mp4​        // 导出视频        try await withCheckedThrowingContinuation { continuation in            exportSession.exportAsynchronously {                switch exportSession.status {                case .completed:                    continuation.resume(returning: outputURL)                case .failed:                    continuation.resume(throwing: exportSession.error ?? NSError(domain: "Export session failed", code: -6, userInfo: nil))                default:                    continuation.resume(throwing: NSError(domain: "Export session status error", code: -7, userInfo: nil))                }            }        }    }

不过我在使用过程中发现对于一些压缩过的视频可能会合并失败,比如我在对b站下载下来的音视频进行合并失败了,所以兼容性可能需要调整下。

ffmpeg

ffmpeg是目前音视频领域的王者,ios领域也不例外,使用ffmpeg来合并音视频基本不会有什么兼容性问题。如何使用,如果是swift项目使用spa包管理可以直接引入 https://github.com/tylerjonesio/ffmpeg-kit-spm

然后合并代码如下

import ffmpegkit​static func avMerge(videoUrl: URL, audioUrl: URL, destUrl: URL) async throws {        let result = try await withCheckedThrowingContinuation { continuation in                Task {                    do {                        // 构建 FFmpeg 命令                        let command = " -i \(videoUrl.path)  -i \(audioUrl.path)  -c copy \(destUrl.path)"                                                            var commands = command.split(separator: " ")                        // 执行 FFmpegKit 命令                        FFmpegKit.execute(withArguments: commands)                        // 成功完成,将结果传递给 continuation                        continuation.resume(returning: destUrl)                    } catch {                        // 捕获错误,将错误传递给 continuation                        continuation.resume(throwing: error)                    }                }            }    }​

如果遇到找不到包的情况,请检查你的配置,targets-》项目名称-〉General-》Framewarks,Library,and Embeded Content添加下该包即可。

多个视频合并

使用ffmpeg合并多个视频片段也是非常简单,直接上代码

 static func mergeTSFilesByFfmpeg(videoURLs: [URL], outputURL: URL) async throws {                        let result = try await withCheckedThrowingContinuation { continuation in                Task {                    do {                        // 构建 FFmpeg 命令                        let output = outputURL.path                                                let tempFile = try buildMergeFileCmd(videoURLs: videoURLs)                        let command = " -f concat -safe 0 -i \(tempFile.path)  -c copy \(output)"                                                            var commands = command.split(separator: " ")                        // 执行 FFmpegKit 命令                        FFmpegKit.execute(withArguments: commands)                        // 成功完成,将结果传递给 continuation                        continuation.resume(returning: output)                    } catch {                        // 捕获错误,将错误传递给 continuation                        continuation.resume(throwing: error)                    }                }            }            }        static func buildMergeFileCmd(videoURLs: [URL]) throws -> URL {                let destUrl = videoURLs[0].deletingLastPathComponent().appendingPathComponent("video_list.txt")​        // 创建并写入临时文件        // 遍历视频文件路径数组,生成格式化的字符串        let content = videoURLs.map { "file '\($0.path)'" }.joined(separator: "\r\n")                // 写入临时文件        try content.write(to: destUrl, atomically: true, encoding: .utf8)        return destUrl    }