ios - 视频的 scaleTimeRange 防止在 Xcode 7 上以 Swift 2.0+ 导出视频

标签 ios swift video avplayer

我正在使用 AVFoundation 在自定义相机应用程序中录制视频。以下代码成功地从 previewView 捕获视频并将其转换为保存到设备照片库的视频 Assets 。我想让应用程序使用效果将视频转换为慢动作视频,并且听人说 AVMutableCompositionscaleTimeRange 可以做到这一点。但是当我尝试实现如下面的代码所示时,视频没有导出,效果也不是慢动作。

func captureOutput(captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAtURL outputFileURL: NSURL!, fromConnections connections: [AnyObject]!, error: NSError!) {

    if(error != nil){
        print(error)
    }

    self.lockInterfaceRotation = false

    let backgroundRecordId: UIBackgroundTaskIdentifier = self.backgroundRecordId
    self.backgroundRecordId = UIBackgroundTaskInvalid

    //create mutable composition of video and slow it down

    let videoAsset = AVURLAsset(URL: outputFileURL, options: nil)
    let mixComposition = AVMutableComposition()
    let videoTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))
    let audioTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: CMPersistentTrackID(kCMPersistentTrackID_Invalid))

    let sourceVideoTrack = videoAsset.tracksWithMediaType(AVMediaTypeVideo)[0] 
    let sourceAudioTrack = videoAsset.tracksWithMediaType(AVMediaTypeAudio)[0]
    do{
        try videoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), ofTrack: sourceVideoTrack, atTime: kCMTimeZero)

        try audioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), ofTrack: sourceAudioTrack, atTime: kCMTimeZero)
    }catch _ {
        print("ERROR could not insert time range")
        return
    }

    /* ** THE PART THAT ISN'T WORKING - When I add this section everything breaks **
    //slow video down for slow mo effect
    let videoDuration = videoAsset.duration;
    let videoScaleFactor = 2 * videoDuration.value

    videoTrack.scaleTimeRange(CMTimeRangeMake(kCMTimeZero, videoDuration), toDuration: CMTimeMake(videoScaleFactor, videoDuration.timescale))
    */

    // Make mutable combination
    // -- Create instruction
    let instruction = AVMutableVideoCompositionInstruction()
    instruction.timeRange = CMTimeRangeMake(kCMTimeZero, videoAsset.duration)
    let videoLayerInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: videoTrack)
    instruction.layerInstructions = [videoLayerInstruction]

    let mutableComposition = AVMutableVideoComposition()
    mutableComposition.renderSize = videoTrack.naturalSize
    mutableComposition.frameDuration = CMTimeMake(1, 20)
    mutableComposition.instructions = [instruction]

    // -- Get path
    let fileName = "/editedVideo-\(arc4random() % 10000).mp4"
    let allPaths = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)
    let docsPath = allPaths[0] as NSString
    let exportPath = docsPath.stringByAppendingFormat(fileName)
    let exportUrl = NSURL.fileURLWithPath(exportPath as String)

    print("Tracks before export: \(mixComposition.tracks.count). File URL: \(exportUrl)")

    // -- Remove old video if exists
    if NSFileManager.defaultManager().fileExistsAtPath(exportPath as String) {
        print("Deleting existing file\n")
        do{
            try NSFileManager.defaultManager().removeItemAtPath(exportPath as String)
        }catch _ {
            print("Error deleting old video file")
        }

    }

    // -- Create exporter
    let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
    exporter!.videoComposition = mutableComposition
    exporter!.outputFileType = AVFileTypeMPEG4
    exporter!.outputURL = exportUrl
    exporter!.shouldOptimizeForNetworkUse = true

    // -- Export video
    exporter!.exportAsynchronouslyWithCompletionHandler({
        self.exportDidFinish(exporter!)
    })

}

func exportDidFinish(exporter: AVAssetExportSession) {

    // Save video to photo album
    let assetLibrary = ALAssetsLibrary()
    assetLibrary.writeVideoAtPathToSavedPhotosAlbum(exporter.outputURL, completionBlock: {(url: NSURL!, error: NSError!) in
        print("Saved video to album \(exporter.outputURL)")
        if (error != nil) {
            print("Error saving video")
        }
    })

    // Check asset tracks
    print("SUCCESS exporting video")
}

任何想法将不胜感激!谢谢

最佳答案

我最终通过取出音频并执行以下操作使其正常工作:

let videoAsset = AVURLAsset(URL: NSURL(fileURLWithPath: self.pathToMovie), options: nil)
let mixComposition = AVMutableComposition()
let compositionVideoTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid)

do {
  try compositionVideoTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, videoAsset.duration), ofTrack: (videoAsset.tracksWithMediaType(AVMediaTypeVideo).first)!, atTime: kCMTimeZero)
} catch {
  print("handle insert error")
  return
}

let videoDuration = videoAsset.duration

//slow down the video to half speed (change multiplier to desired value)
let finalTimeScale : Int64 = videoDuration.value * 2

compositionVideoTrack.scaleTimeRange(CMTimeRangeMake(kCMTimeZero, videoDuration), toDuration: CMTimeMake(finalTimeScale, videoDuration.timescale))

let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
let finalURL = documentsURL.URLByAppendingPathComponent("what_you_want_to_call_your_video.mp4")

//make sure no other file where this one will be saved
let manager = NSFileManager()
do {
  try manager.removeItemAtPath(finalURL.path!)
} catch {
  print("file doesn't exist or couldn't remove file at path")
}

let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality)
exporter!.outputURL = finalURL
exporter!.outputFileType = AVFileTypeMPEG4 //AVFileTypeQuickTimeMovie, whatever format you want that AVFoundation does
exporter!.exportAsynchronouslyWithCompletionHandler({
                switch exporter!.status{
                case  AVAssetExportSessionStatus.Failed:
                    print("failed \(exporter!.error)")

                    dispatch_async(dispatch_get_main_queue(), {

                        print("Error saving video is \(exporter!.error!.description)")

                        //Show an alert
                        let alertController: UIAlertController = UIAlertController(title: "Error Exporting Video", message: "\(exporter!.error) \(exporter!.error!.description). Please try again", preferredStyle: .Alert)

                        //Create and add the Cancel action
                        let okayAction: UIAlertAction = UIAlertAction(title: "Okay", style: .Cancel) { action -> Void in
                            //Do some stuff
                        }
                        alertController.addAction(okayAction)

                        //Present the AlertController
                        self.presentViewController(alertController, animated: true, completion: nil)
                    })

                case AVAssetExportSessionStatus.Cancelled:
                    print("cancelled \(exporter!.error)")

                    dispatch_async(dispatch_get_main_queue(), {

                        print("Error saving video is \(exporter!.error!.description)")

                        //Show an alert
                        let alertController: UIAlertController = UIAlertController(title: "Error Exporting Video", message: "\(exporter!.error) \(exporter!.error!.description). Please try again", preferredStyle: .Alert)

                        //Create and add the Cancel action
                        let okayAction: UIAlertAction = UIAlertAction(title: "Okay", style: .Cancel) { action -> Void in
                            //Do some stuff
                        }
                        alertController.addAction(okayAction)

                        //Present the AlertController
                        self.presentViewController(alertController, animated: true, completion: nil)
                    })
                default:
                    print("complete, now presenting confirmation")

                    dispatch_async(dispatch_get_main_queue(), {

                        //everything is done, do what you want to do now that video is created
                    })
                }

            })

关于ios - 视频的 scaleTimeRange 防止在 Xcode 7 上以 Swift 2.0+ 导出视频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34365266/

相关文章:

ios - 在 iOS 中播放加密的 mp3

ios - Xcode 构建错误,arm-apple-darwin11-gcc-4.2.1 execvp : No such file or directory

android - 我应该如何定位适用于 Android 和 iOS 的基于 Web 的管理工具?

swift - MacOS/OSx - 以编程方式更改 iTunes 库歌曲/项目上的元数据

Swift-创建助手时发现的问题

ios - 我如何在 Swift 中打印一个整数

ios - 我如何在 Swift 中使用 CFArrayRef?

html - HTML内容背后的YouTube全屏视频

javascript - 当我将鼠标悬停在 slider html5 视频上时的当前时间工具提示

php - ffmpeg 不从图像创建视频