在使用swift开发ios应用时有时候会实现一些音视频合并的工作,有以下方式可以实现。
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是目前音视频领域的王者,ios领域也不例外,使用ffmpeg来合并音视频基本不会有什么兼容性问题。如何使用,如果是swift项目使用spa包管理可以直接引入 https://github.com/tylerjonesio/ffmpeg-kit-spm
然后合并代码如下
import ffmpegkitstatic 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 }