DubbingComposer.swift 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. //
  2. // DubbingComposer.swift
  3. // dubit
  4. //
  5. // Created by zack on 16/12/20.
  6. // Copyright © 2016年 Chengdu Aitu Education Technology Ltd. All rights reserved.
  7. //
  8. import Foundation
  9. import AVFoundation
  10. class DubbingComposer {
  11. var timeline: [Double]
  12. var videoUrl: URL
  13. var musicUrl: URL
  14. var recordsUrl: [String]
  15. var preTime: Double = 0
  16. init(timeline: [Double], videoUrl: URL, musicUrl: URL, recordsUrl: [String]){
  17. self.timeline = timeline
  18. self.videoUrl = videoUrl
  19. self.musicUrl = musicUrl
  20. self.recordsUrl = recordsUrl
  21. }
  22. func compose(_ output: URL, onSuccess successBlock: (()->())?, onFail failBlock:((_ message: String)->())? ) {
  23. //初始化合成器
  24. let composition = AVMutableComposition()
  25. //为合成器添加视频轨道
  26. let videoTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)
  27. let videoAsset = AVURLAsset(url: videoUrl, options: nil)
  28. let videoTimeRange = CMTimeRangeMake(start: CMTime.zero, duration: videoAsset.duration)
  29. do {
  30. try videoTrack?.insertTimeRange(videoTimeRange, of: videoAsset.tracks(withMediaType: AVMediaType.video).first!, at: CMTime.zero)
  31. }
  32. catch {
  33. DispatchQueue.main.async(execute: {
  34. failBlock?("Fail on load video")
  35. })
  36. return
  37. }
  38. //添加音乐轨道
  39. let musicTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
  40. let musicAsset = AVURLAsset(url: musicUrl, options: nil)
  41. let musicTimeRange = videoTimeRange
  42. do {
  43. try musicTrack?.insertTimeRange(musicTimeRange, of: musicAsset.tracks(withMediaType: AVMediaType.audio).first!, at: CMTime.zero)
  44. print("创建音乐音轨成功")
  45. }
  46. catch {
  47. print("创建音乐音轨失败")
  48. }
  49. //添加两条配音轨道
  50. let recordTrackA = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
  51. let recordTrackB = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
  52. //将配音交替放入两条配音轨道
  53. for (i, url) in recordsUrl.enumerated() {
  54. let recordUrl = URL(fileURLWithPath: url)
  55. let recordAsset = AVURLAsset(url: recordUrl, options: nil)
  56. let recordRange = CMTimeRangeMake(start: CMTime.zero, duration: recordAsset.duration)
  57. let currentSec = timeline[i]
  58. let beginSec = currentSec > preTime ? currentSec - preTime : currentSec
  59. let beginTime = CMTimeMakeWithSeconds(beginSec, preferredTimescale: 100)
  60. let recordAssetTrack = recordAsset.tracks(withMediaType: AVMediaType.audio).first!
  61. let recordTrack = i % 2 == 0 ? recordTrackA : recordTrackB
  62. do {
  63. try recordTrack?.insertTimeRange(recordRange, of: recordAssetTrack, at: beginTime)
  64. }
  65. catch{
  66. print("Composer error on record: \(i)")
  67. }
  68. }
  69. let manager = FileManager.default
  70. do {
  71. try manager.removeItem(at: output)
  72. print("删除旧输出成功")
  73. }
  74. catch {
  75. print("删除旧输出失败")
  76. }
  77. //输出到文件
  78. if let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetMediumQuality) {
  79. assetExport.outputFileType = AVFileType.mp4
  80. assetExport.outputURL = output
  81. assetExport.shouldOptimizeForNetworkUse = true
  82. assetExport.exportAsynchronously(completionHandler: {
  83. successBlock?()
  84. return
  85. })
  86. }
  87. else {
  88. DispatchQueue.main.async(execute: {
  89. failBlock?("Something wrong on composition")
  90. })
  91. }
  92. }
  93. }