DubbingComposer.swift 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  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. var videoTimeRange:CMTimeRange?
  26. //为合成器添加视频轨道
  27. if((videoUrl) != nil){
  28. let videoTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)
  29. let videoAsset = AVURLAsset(url: videoUrl!, options: nil)
  30. videoTimeRange = CMTimeRangeMake(start: CMTime.zero, duration: videoAsset.duration)
  31. do {
  32. try videoTrack?.insertTimeRange(videoTimeRange!, of: videoAsset.tracks(withMediaType: AVMediaType.video).first!, at: CMTime.zero)
  33. }
  34. catch {
  35. DispatchQueue.main.async(execute: {
  36. failBlock?("Fail on load video")
  37. })
  38. return
  39. }
  40. }
  41. //添加音乐轨道
  42. let musicTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
  43. let musicAsset = AVURLAsset(url: musicUrl, options: nil)
  44. let musicTimeRange = videoTimeRange ?? CMTimeRangeMake(start: CMTime.zero, duration: musicAsset.duration)
  45. do {
  46. try musicTrack?.insertTimeRange(musicTimeRange, of: musicAsset.tracks(withMediaType: AVMediaType.audio).first!, at: CMTime.zero)
  47. print("创建音乐音轨成功")
  48. }
  49. catch {
  50. print("创建音乐音轨失败")
  51. }
  52. //添加两条配音轨道
  53. let recordTrackA = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
  54. let recordTrackB = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
  55. //将配音交替放入两条配音轨道
  56. for (i, url) in recordsUrl.enumerated() {
  57. let recordUrl = URL(fileURLWithPath: url)
  58. let recordAsset = AVURLAsset(url: recordUrl, options: nil)
  59. let recordRange = CMTimeRangeMake(start: CMTime.zero, duration: recordAsset.duration)
  60. let currentSec = timeline[i]
  61. let beginSec = currentSec > preTime ? currentSec - preTime : currentSec
  62. let beginTime = CMTimeMakeWithSeconds(beginSec, preferredTimescale: 100)
  63. if(recordAsset.tracks(withMediaType: AVMediaType.audio).count > 0){
  64. let recordAssetTrack = recordAsset.tracks(withMediaType: AVMediaType.audio).first!
  65. let recordTrack = i % 2 == 0 ? recordTrackA : recordTrackB
  66. do {
  67. try recordTrack?.insertTimeRange(recordRange, of: recordAssetTrack, at: beginTime)
  68. }
  69. catch{
  70. print("Composer error on record: \(i)")
  71. }
  72. }
  73. }
  74. let manager = FileManager.default
  75. do {
  76. try manager.removeItem(at: output)
  77. print("删除旧输出成功")
  78. }
  79. catch {
  80. print("删除旧输出失败")
  81. }
  82. //输出到文件
  83. if let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetMediumQuality) {
  84. assetExport.outputFileType = AVFileType.mp4
  85. assetExport.outputURL = output
  86. assetExport.shouldOptimizeForNetworkUse = true
  87. assetExport.exportAsynchronously(completionHandler: {
  88. if((assetExport.error) != nil){
  89. print(assetExport.error!)
  90. DispatchQueue.main.async(execute: {
  91. failBlock?("Something wrong on composition")
  92. })
  93. }else{
  94. successBlock?()
  95. }
  96. return
  97. })
  98. }
  99. else {
  100. DispatchQueue.main.async(execute: {
  101. failBlock?("Something wrong on composition")
  102. })
  103. }
  104. }
  105. }