AVMutableComposition - 如何将多个音频记录与 1 个视频记录合并
AVMutableComposition -How to Merge Multiple Audio Recordings with 1 Video Recording
我有几个音频剪辑是我用 AVAudioRecorder 在视频上录制的。使用 AVMutableComposition 我想在录制音频时将音频资产与视频合并。例如,视频有 1 分钟长,我分别录制了 5-10 秒、20-25 秒和 30-35 秒的 3 个音频片段。音频剪辑应在这些特定时间范围内与视频合并。当最终视频播放时,音频将在这些时间帧内播放。
型号:
class AudioModel {
var audioUrl: URL?
var startTime: Double?
var endTime: Double?
}
组合:
let mixComposition = AVMutableComposition()
guard let videoCompositionTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
guard let audioFromVideoCompositionTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
guard let audioModelCompositionTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
let videoAsset = AVURLAsset(url: videoURL)
guard let videoTrack = videoAsset.tracks(withMediaType: .video).first else { return }
for audioModel in audioModels {
let audioAsset = AVURLAsset(url: audioModel.url!)
let startTime = CMTime(seconds: audioModel.startTime!, preferredTimescale: 1000)
do {
try videoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: videoAsset.duration), of: videoTrack, at: .zero)
if let audioTrackFromAudioModel = audioAsset.tracks(withMediaType: .audio).first {
try audioModelCompositionTrack.insertTimeRange(CMTimeRangeMake(start: startTime, duration: audioAsset.duration),
of: audioTrackFromAudioModel, at: .zero)
}
if let audioFromVideoTrack = videoAsset.tracks(withMediaType: .audio).first {
try audioFromVideoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: videoAsset.duration),
of: audioFromVideoTrack, at: .zero)
}
} catch {
}
}
let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
// ... I know what to do from here
您的方法是正确的,但是您混淆了用于 insertTimeRange 的两个参数,并且多次从您的视频轨道添加视频和音频。
insertTimeRange中的第一个参数是指原始音频资产中的timeRange,而不是合成;因此假设对于您要添加整个剪辑的每个音频剪辑,时间范围应始终从 .zero 开始,而不是从 startTime 开始。 at: 参数不应为 .zero,而应为“startTime” - 合成中要添加音频的时间。
关于您的视频轨道和 audioFromVideoTrack,我不会将它们添加为循环的一部分,而只是在循环之前添加它们。否则,您将多次添加它们(每个音频项目一次),而不是一次,这可能会导致不必要的行为或导出会话完全失败。
我编辑了您的代码,但无法对其进行实际测试,所以对它持保留态度。
guard let videoCompositionTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
guard let audioFromVideoCompositionTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
guard let audioModelCompositionTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
let videoAsset = AVURLAsset(url: videoURL)
guard let videoTrack = videoAsset.tracks(withMediaType: .video).first else { return }
do {
try videoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: videoAsset.duration), of: videoTrack, at: .zero)
if let audioFromVideoTrack = videoAsset.tracks(withMediaType: .audio).first {
try audioFromVideoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: videoAsset.duration), of: audioFromVideoTrack, at: .zero)
}
} catch {
}
for audioModel in audioModels {
let audioAsset = AVURLAsset(url: audioModel.url!)
let startTime = CMTime(seconds: audioModel.startTime!, preferredTimescale: 1000)
do {
if let audioTrackFromAudioModel = audioAsset.tracks(withMediaType: .audio).first {
try audioModelCompositionTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: audioAsset.duration), of: audioTrackFromAudioModel, at: startTime)
}
} catch {
}
}
let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
// ... I know what to do from here
我有几个音频剪辑是我用 AVAudioRecorder 在视频上录制的。使用 AVMutableComposition 我想在录制音频时将音频资产与视频合并。例如,视频有 1 分钟长,我分别录制了 5-10 秒、20-25 秒和 30-35 秒的 3 个音频片段。音频剪辑应在这些特定时间范围内与视频合并。当最终视频播放时,音频将在这些时间帧内播放。
型号:
class AudioModel {
var audioUrl: URL?
var startTime: Double?
var endTime: Double?
}
组合:
let mixComposition = AVMutableComposition()
guard let videoCompositionTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
guard let audioFromVideoCompositionTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
guard let audioModelCompositionTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
let videoAsset = AVURLAsset(url: videoURL)
guard let videoTrack = videoAsset.tracks(withMediaType: .video).first else { return }
for audioModel in audioModels {
let audioAsset = AVURLAsset(url: audioModel.url!)
let startTime = CMTime(seconds: audioModel.startTime!, preferredTimescale: 1000)
do {
try videoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: videoAsset.duration), of: videoTrack, at: .zero)
if let audioTrackFromAudioModel = audioAsset.tracks(withMediaType: .audio).first {
try audioModelCompositionTrack.insertTimeRange(CMTimeRangeMake(start: startTime, duration: audioAsset.duration),
of: audioTrackFromAudioModel, at: .zero)
}
if let audioFromVideoTrack = videoAsset.tracks(withMediaType: .audio).first {
try audioFromVideoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: videoAsset.duration),
of: audioFromVideoTrack, at: .zero)
}
} catch {
}
}
let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
// ... I know what to do from here
您的方法是正确的,但是您混淆了用于 insertTimeRange 的两个参数,并且多次从您的视频轨道添加视频和音频。
insertTimeRange中的第一个参数是指原始音频资产中的timeRange,而不是合成;因此假设对于您要添加整个剪辑的每个音频剪辑,时间范围应始终从 .zero 开始,而不是从 startTime 开始。 at: 参数不应为 .zero,而应为“startTime” - 合成中要添加音频的时间。
关于您的视频轨道和 audioFromVideoTrack,我不会将它们添加为循环的一部分,而只是在循环之前添加它们。否则,您将多次添加它们(每个音频项目一次),而不是一次,这可能会导致不必要的行为或导出会话完全失败。
我编辑了您的代码,但无法对其进行实际测试,所以对它持保留态度。
guard let videoCompositionTrack = mixComposition.addMutableTrack(withMediaType: .video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
guard let audioFromVideoCompositionTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
guard let audioModelCompositionTrack = mixComposition.addMutableTrack(withMediaType: .audio, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return }
let videoAsset = AVURLAsset(url: videoURL)
guard let videoTrack = videoAsset.tracks(withMediaType: .video).first else { return }
do {
try videoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: videoAsset.duration), of: videoTrack, at: .zero)
if let audioFromVideoTrack = videoAsset.tracks(withMediaType: .audio).first {
try audioFromVideoCompositionTrack.insertTimeRange(CMTimeRangeMake(start: CMTime.zero, duration: videoAsset.duration), of: audioFromVideoTrack, at: .zero)
}
} catch {
}
for audioModel in audioModels {
let audioAsset = AVURLAsset(url: audioModel.url!)
let startTime = CMTime(seconds: audioModel.startTime!, preferredTimescale: 1000)
do {
if let audioTrackFromAudioModel = audioAsset.tracks(withMediaType: .audio).first {
try audioModelCompositionTrack.insertTimeRange(CMTimeRangeMake(start: .zero, duration: audioAsset.duration), of: audioTrackFromAudioModel, at: startTime)
}
} catch {
}
}
let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
// ... I know what to do from here