我要求用户有多个剪辑,应用程序提供3个功能。用户可以进行慢动作,用户可以加快速度。更改速度后,用户可以合并它们以制作单个视频并保存到设备。
例如,剪辑1,剪辑2和剪辑3以正常速度记录,然后以慢动作转换剪辑1,剪辑2处于正常速度,剪辑3以快速速度进行,然后当用户合并时,这三个剪辑将组合在一起并使其成为可能在一个剪辑中,用户可以在社交网络中分享。
使用AVFoundation录制视频或从图库中选择视频
func convertVideoWithSpeed(completion:()->()) {
if RecordedSegment.segments.count > 0 {
self.exportVideoWithMode(RecordedSegment.segments.first!,title: "clip_\(counter).mp4", completion: { [unowned self] (path) in
RecordedSegment.segments.removeFirst()
self.counter = self.counter + 1
self.mergedVideArray.append("clip_\(self.counter).mp4")
self.convertVideoWithSpeed(completion)
})
} else {
var arr1 = [NSURL]()
for track in self.mergedVideArray {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
var finalURL = documentsURL.URLByAppendingPathComponent(track)
arr1.append(finalURL)
}
self.mergeVideos(self.mergedVideArray, completion: {
completion()
})
}
}
转换不同剪辑的视频帧速率
func exportVideoWithMode(segment:RecordedSegment,title:String,completion:(path:String)->()) {
let mixComposition = AVMutableComposition()
let videoTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
let audioTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
let startTimer = kCMTimeZero
print(RecordedSegment.segments)
var size = CGSizeZero
let astTrack = AVAsset(URL: NSURL(string: segment.path!)!)
size = astTrack.tracksWithMediaType(AVMediaTypeVideo)[0].naturalSize
do {
try videoTrack.insertTimeRange(CMTimeRangeMake(startTimer, astTrack.duration), ofTrack: astTrack.tracksWithMediaType(AVMediaTypeVideo)[0] , atTime: startTimer)
try audioTrack.insertTimeRange(CMTimeRangeMake(startTimer, astTrack.duration), ofTrack: astTrack.tracksWithMediaType(AVMediaTypeAudio)[0] , atTime: startTimer)
} catch _ {
print("Failed to load first track")
}
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let finalURL = documentsURL.URLByAppendingPathComponent(title)
let instruction = AVMutableVideoCompositionInstruction()
let layerInstruct = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
instruction.layerInstructions = [layerInstruct]
let videoComposition = AVMutableVideoComposition()
videoComposition.instructions = [instruction]
if segment.mode == .Slow {
videoComposition.frameDuration = CMTimeMake(1, 15)
} else if segment.mode == .Fast {
videoComposition.frameDuration = CMTimeMake(1, 30)
} else {
videoComposition.frameDuration = CMTimeMake(1, 30)
}
videoComposition.renderSize = size
videoComposition.renderScale = 1
instruction.timeRange = CMTimeRangeMake(kCMTimeZero, astTrack.duration)
guard let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else { return }
exportSession.outputURL = finalURL
exportSession.outputFileType = AVFileTypeQuickTimeMovie
exportSession.shouldOptimizeForNetworkUse = true
exportSession.videoComposition = videoComposition
if NSFileManager.defaultManager().fileExistsAtPath(finalURL.path!) {
do {
try NSFileManager.defaultManager().removeItemAtURL(finalURL)
} catch {
}
}
// 6 - Perform the Export
exportSession.exportAsynchronouslyWithCompletionHandler() {
let error = exportSession.error?.code
print(exportSession.error)
if exportSession.status == .Cancelled {
print("Export was cancelled")
GlobalUtility.hideActivityIndi(self)
} else if exportSession.status == .Completed {
print("completed")
let asset = AVAsset(URL: finalURL)
let track = asset.tracksWithMediaType(AVMediaTypeVideo)[0]
print("==============\(track.nominalFrameRate)")
completion(path: finalURL.path!)
} else if error == nil{
completion(path: finalURL.path!)
}else{
if exportSession.status == .Cancelled {
print("Export was cancelled")
GlobalUtility.hideActivityIndi(self)
}
GlobalUtility.hideActivityIndi(self)
}
}
}
将它们合并到一个视频
func mergeVideos(mergePaths:[String],completion:()->()) {
var count = 0
let mixComposition = AVMutableComposition()
let videoTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID())
let audioTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID())
var startTimer = kCMTimeZero
print(RecordedSegment.segments)
for track in mergePaths {
let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
var finalURL = documentsURL.URLByAppendingPathComponent(track)
if NSFileManager.defaultManager().fileExistsAtPath(finalURL.path!) {
let astTrack = AVAsset(URL: finalURL)
let video = astTrack.tracksWithMediaType(AVMediaTypeVideo)
let audio = astTrack.tracksWithMediaType(AVMediaTypeAudio)
if audio.count > 0 && video.count > 0 {
do {
try videoTrack.insertTimeRange(CMTimeRangeMake(startTimer, astTrack.duration), ofTrack: video[0] , atTime: startTimer)
try audioTrack.insertTimeRange(CMTimeRangeMake(startTimer, astTrack.duration), ofTrack: audio[0] , atTime: startTimer)
startTimer = (videoTrack.asset?.duration)!
} catch _ {
print("Failed to load first track")
}
} else {
print("file not exist")
}
} else {
print("tracks not exist")
}
}
//let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let finalURL = self.recordSession.outputUrl
count = count + 1
guard let exportSession = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else { return }
exportSession.outputURL = finalURL
exportSession.outputFileType = AVFileTypeQuickTimeMovie
exportSession.shouldOptimizeForNetworkUse = true
if NSFileManager.defaultManager().fileExistsAtPath(self.recordSession.outputUrl.path!) {
do {
try NSFileManager.defaultManager().removeItemAtURL(self.recordSession.outputUrl)
} catch {
}
}
// 6 - Perform the Export
exportSession.exportAsynchronouslyWithCompletionHandler() {
let error = exportSession.error?.code
print(exportSession.error)
if exportSession.status == .Cancelled {
print("Export was cancelled")
GlobalUtility.hideActivityIndi(self)
} else if exportSession.status == .Completed {
print("completed")
completion()
} else if error == nil{
completion()
}else{
if exportSession.status == .Cancelled {
print("Export was cancelled")
GlobalUtility.hideActivityIndi(self)
}
GlobalUtility.hideActivityIndi(self)
}
}
}