DubbingComposer.swift 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  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. let videoTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)
  27. //为合成器添加视频轨道
  28. if((videoUrl) != nil){
  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. let recordAssetTrack = recordAsset.tracks(withMediaType: AVMediaType.audio).first!
  64. let recordTrack = i % 2 == 0 ? recordTrackA : recordTrackB
  65. do {
  66. try recordTrack?.insertTimeRange(recordRange, of: recordAssetTrack, at: beginTime)
  67. }
  68. catch{
  69. print("Composer error on record: \(i)")
  70. }
  71. }
  72. let manager = FileManager.default
  73. do {
  74. try manager.removeItem(at: output)
  75. print("删除旧输出成功")
  76. }
  77. catch {
  78. print("删除旧输出失败")
  79. }
  80. //输出到文件
  81. if let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetMediumQuality) {
  82. assetExport.outputFileType = AVFileType.mp4
  83. assetExport.outputURL = output
  84. assetExport.shouldOptimizeForNetworkUse = true
  85. assetExport.exportAsynchronously(completionHandler: {
  86. successBlock?()
  87. return
  88. })
  89. }
  90. else {
  91. DispatchQueue.main.async(execute: {
  92. failBlock?("Something wrong on composition")
  93. })
  94. }
  95. }
  96. }