DubbingComposer.swift 3.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  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 musicUrl: URL
  13. var recordsUrl: [String]
  14. var preTime: Double = 0
  15. init(timeline: [Double], musicUrl: URL, recordsUrl: [String]){
  16. self.timeline = timeline
  17. self.musicUrl = musicUrl
  18. self.recordsUrl = recordsUrl
  19. }
  20. func compose(_ output: URL, onSuccess successBlock: (()->())?, onFail failBlock:((_ message: String)->())? ) {
  21. //初始化合成器
  22. let composition = AVMutableComposition()
  23. //添加音乐轨道
  24. let musicTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
  25. let musicAsset = AVURLAsset(url: musicUrl, options: nil)
  26. let musicTimeRange = CMTimeRangeMake(start: CMTime.zero, duration: musicAsset.duration)
  27. do {
  28. try musicTrack?.insertTimeRange(musicTimeRange, of: musicAsset.tracks(withMediaType: AVMediaType.audio).first!, at: CMTime.zero)
  29. print("创建音乐音轨成功")
  30. }
  31. catch {
  32. print("创建音乐音轨失败")
  33. }
  34. //添加两条配音轨道
  35. let recordTrackA = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
  36. let recordTrackB = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
  37. //将配音交替放入两条配音轨道
  38. for (i, url) in recordsUrl.enumerated() {
  39. let recordUrl = URL(fileURLWithPath: url)
  40. let recordAsset = AVURLAsset(url: recordUrl, options: nil)
  41. let recordRange = CMTimeRangeMake(start: CMTime.zero, duration: recordAsset.duration)
  42. let currentSec = timeline[i]
  43. let beginSec = currentSec > preTime ? currentSec - preTime : currentSec
  44. let beginTime = CMTimeMakeWithSeconds(beginSec, preferredTimescale: 100)
  45. if(recordAsset.tracks(withMediaType: AVMediaType.audio).count > 0){
  46. let recordAssetTrack = recordAsset.tracks(withMediaType: AVMediaType.audio).first!
  47. let recordTrack = i % 2 == 0 ? recordTrackA : recordTrackB
  48. do {
  49. try recordTrack?.insertTimeRange(recordRange, of: recordAssetTrack, at: beginTime)
  50. }
  51. catch{
  52. print("Composer error on record: \(i)")
  53. }
  54. }
  55. }
  56. // let manager = FileManager.default
  57. // do {
  58. // try manager.removeItem(at: output)
  59. // print("删除旧输出成功")
  60. // }
  61. // catch {
  62. // print("删除旧输出失败")
  63. // }
  64. //输出到文件
  65. if let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetHighestQuality) {
  66. assetExport.outputFileType = AVFileType.mp4
  67. assetExport.outputURL = output
  68. assetExport.shouldOptimizeForNetworkUse = true
  69. assetExport.exportAsynchronously(completionHandler: {
  70. if((assetExport.error) != nil){
  71. print(assetExport.error!)
  72. DispatchQueue.main.async(execute: {
  73. failBlock?("Something wrong on composition")
  74. })
  75. }else{
  76. successBlock?()
  77. }
  78. return
  79. })
  80. }
  81. else {
  82. DispatchQueue.main.async(execute: {
  83. failBlock?("Something wrong on composition")
  84. })
  85. }
  86. }
  87. }