123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- //
- // DubbingComposer.swift
- // dubit
- //
- // Created by zack on 16/12/20.
- // Copyright © 2016年 Chengdu Aitu Education Technology Ltd. All rights reserved.
- //
- import Foundation
- import AVFoundation
- class DubbingComposer {
- var timeline: [Double]
- var videoUrl: URL?
- var musicUrl: URL
- var recordsUrl: [String]
- var preTime: Double = 0
-
- init(timeline: [Double], videoUrl: URL?, musicUrl: URL, recordsUrl: [String]){
- self.timeline = timeline
- self.videoUrl = videoUrl
- self.musicUrl = musicUrl
- self.recordsUrl = recordsUrl
- }
-
- func compose(_ output: URL, onSuccess successBlock: (()->())?, onFail failBlock:((_ message: String)->())? ) {
-
- //初始化合成器
- let composition = AVMutableComposition()
-
- var videoTimeRange:CMTimeRange?
-
- //为合成器添加视频轨道
- if((videoUrl) != nil){
- let videoTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: kCMPersistentTrackID_Invalid)
- let videoAsset = AVURLAsset(url: videoUrl!, options: nil)
- videoTimeRange = CMTimeRangeMake(start: CMTime.zero, duration: videoAsset.duration)
- do {
- try videoTrack?.insertTimeRange(videoTimeRange!, of: videoAsset.tracks(withMediaType: AVMediaType.video).first!, at: CMTime.zero)
- }
- catch {
- DispatchQueue.main.async(execute: {
- failBlock?("Fail on load video")
- })
- return
- }
- }
-
-
- //添加音乐轨道
- let musicTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
- let musicAsset = AVURLAsset(url: musicUrl, options: nil)
- let musicTimeRange = videoTimeRange ?? CMTimeRangeMake(start: CMTime.zero, duration: musicAsset.duration)
- do {
- try musicTrack?.insertTimeRange(musicTimeRange, of: musicAsset.tracks(withMediaType: AVMediaType.audio).first!, at: CMTime.zero)
- print("创建音乐音轨成功")
- }
- catch {
- print("创建音乐音轨失败")
- }
-
- //添加两条配音轨道
- let recordTrackA = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
- let recordTrackB = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: kCMPersistentTrackID_Invalid)
-
- //将配音交替放入两条配音轨道
- for (i, url) in recordsUrl.enumerated() {
- let recordUrl = URL(fileURLWithPath: url)
- let recordAsset = AVURLAsset(url: recordUrl, options: nil)
- let recordRange = CMTimeRangeMake(start: CMTime.zero, duration: recordAsset.duration)
- let currentSec = timeline[i]
- let beginSec = currentSec > preTime ? currentSec - preTime : currentSec
- let beginTime = CMTimeMakeWithSeconds(beginSec, preferredTimescale: 100)
- if(recordAsset.tracks(withMediaType: AVMediaType.audio).count > 0){
- let recordAssetTrack = recordAsset.tracks(withMediaType: AVMediaType.audio).first!
- let recordTrack = i % 2 == 0 ? recordTrackA : recordTrackB
- do {
- try recordTrack?.insertTimeRange(recordRange, of: recordAssetTrack, at: beginTime)
- }
- catch{
- print("Composer error on record: \(i)")
- }
- }
- }
-
- let manager = FileManager.default
- do {
- try manager.removeItem(at: output)
- print("删除旧输出成功")
- }
- catch {
- print("删除旧输出失败")
- }
-
- //输出到文件
- if let assetExport = AVAssetExportSession(asset: composition, presetName: AVAssetExportPresetMediumQuality) {
- assetExport.outputFileType = AVFileType.mp4
- assetExport.outputURL = output
- assetExport.shouldOptimizeForNetworkUse = true
- assetExport.exportAsynchronously(completionHandler: {
- if((assetExport.error) != nil){
- print(assetExport.error!)
- DispatchQueue.main.async(execute: {
- failBlock?("Something wrong on composition")
- })
- }else{
- successBlock?()
- }
- return
- })
- }
- else {
- DispatchQueue.main.async(execute: {
- failBlock?("Something wrong on composition")
- })
- }
- }
- }
|