如何分割视频的前15秒并将其快速保存到相机胶卷中

时间:2018-06-21 00:02:24

标签: ios swift video

我正在尝试从UIImagePicker中选择任何视频的前15秒,然后将其保存到照片库。我正在使用AVFoundation抓紧前15秒,然后另存为新文件。我认为问题是我没有正确保存它,但不确定。此时,代码似乎可以正常运行。

import UIKit
import MobileCoreServices
import AVFoundation
import Photos

class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate {
    var spliceSeconds = 15.0
    var preferredPreset = AVAssetExportPresetPassthrough

    @IBOutlet weak var bnLibrary: UIButton!
    @IBOutlet weak var bnCamera: UIButton!

    override func viewDidLoad() {
        super.viewDidLoad()
        authVideoAccess()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func authVideoAccess() {

        let status = PHPhotoLibrary.authorizationStatus()
        switch status {
        case .authorized:
            print("authorized")
            break
        case .denied:
            //alert
            print("denied")
            break
        case .notDetermined:
            PHPhotoLibrary.requestAuthorization { (newStatus) in
                print("status is \(newStatus)")
                if newStatus ==  PHAuthorizationStatus.authorized {
                    /* do stuff here */
                    print("success")
                }
            }
            print("It is not determined until now")
            break
        case .restricted:
            print("permission restricted")
            break
        default:
            // show something eles
            print("default")
            break
        }


    }

    @IBAction func getVideo(_ sender: UIButton) {
        if sender == bnCamera {
            self.openCamera()
        } else {
            self.openVideoLibrary()
        }
    }

    func openCamera() {
        if UIImagePickerController.isSourceTypeAvailable(.camera) {
            let myPickerController = UIImagePickerController()
            myPickerController.delegate = self
            myPickerController.sourceType = .camera
            myPickerController.mediaTypes = [kUTTypeMovie as String]
            self.present(myPickerController, animated: true, completion: nil)
        }
    }

    func openVideoLibrary() {
        if UIImagePickerController.isSourceTypeAvailable(.photoLibrary) {
            let myPickerController = UIImagePickerController()
            myPickerController.delegate = self
            myPickerController.sourceType = .photoLibrary
            myPickerController.mediaTypes = [kUTTypeMovie as String, kUTTypeVideo as String]
            self.present(myPickerController, animated: true, completion: nil)
        }

    }

    //pragmark - delegate methods
    func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : Any]) {

        let vidUrl:URL = info[UIImagePickerControllerMediaURL]! as! URL
        let options = [ AVURLAssetPreferPreciseDurationAndTimingKey: true ]
        let asset = AVURLAsset(url: vidUrl, options: options)


        if verifyPresetForAsset(preset: self.preferredPreset, asset: asset) {
            let assetVideoTrack: AVAssetTrack = asset.tracks(withMediaType: AVMediaType.video).first!
            let assetAudioTrack: AVAssetTrack = asset.tracks(withMediaType: AVMediaType.audio).first!
            let startTime = assetAudioTrack.timeRange.start
            let endTime = CMTimeMakeWithSeconds(spliceSeconds, startTime.timescale)

            if assetVideoTrack.timeRange.duration.seconds > spliceSeconds {
                trimVideo(assetVideoTrack: assetVideoTrack, assetAudioTrack: assetAudioTrack, startTime: startTime, endTime: endTime )
            } else {
                //show a message
            }
        }


        //print(info[UIImagePickerControllerMediaURL]!)
    }



    func numberOfComponents(in pickerView: UIPickerView) -> Int {
        return 1
    }

    func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
        return 1
    }

    //pragmamark - trim stuff
    func verifyPresetForAsset(preset: String, asset: AVAsset) -> Bool {
        let compatiblePresets = AVAssetExportSession.exportPresets(compatibleWith: asset)
        let filteredPresets = compatiblePresets.filter { $0 == preset }
        return filteredPresets.count > 0 || preset == AVAssetExportPresetPassthrough
    }


    func getNewFilePath() -> String {
        let paths = FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask)
        return paths[0].absoluteString + "StorySplitter-" + UUID().uuidString + ".mp4"
    }

    func trimVideo(assetVideoTrack: AVAssetTrack, assetAudioTrack: AVAssetTrack, startTime: CMTime, endTime: CMTime) -> Void {

        var accumulatedTime = kCMTimeZero

        let durationOfCurrentSlice = CMTimeSubtract(endTime, startTime)
        let timeRangeForCurrentSlice = CMTimeRangeMake(startTime, endTime)

        let composition = AVMutableComposition()
        let videoCompTrack = composition.addMutableTrack(withMediaType: AVMediaType.video, preferredTrackID: CMPersistentTrackID())
        let audioCompTrack = composition.addMutableTrack(withMediaType: AVMediaType.audio, preferredTrackID: CMPersistentTrackID())



        do {
            try videoCompTrack?.insertTimeRange(timeRangeForCurrentSlice, of: assetVideoTrack, at: accumulatedTime)
            try audioCompTrack?.insertTimeRange(timeRangeForCurrentSlice, of: assetAudioTrack, at: accumulatedTime)
            accumulatedTime = CMTimeAdd(accumulatedTime, durationOfCurrentSlice)
        }
        catch let compError {
            print("TrimVideo: error during composition: \(compError)")
        }

        accumulatedTime = CMTimeAdd(accumulatedTime, durationOfCurrentSlice)

        let exportSession = AVAssetExportSession(asset: composition, presetName: self.preferredPreset)
        exportSession?.outputURL = URL(fileURLWithPath: getNewFilePath())
        exportSession?.outputFileType = AVFileType.mp4
        exportSession?.shouldOptimizeForNetworkUse = true

        exportSession?.exportAsynchronously {
            print("finished saving")
        }


    }

}

1 个答案:

答案 0 :(得分:0)

exportSession?.exportAsynchronously 

不会保存到照片库,而只是保存到您指定的outputURL,它位于应用程序的沙箱中。如果您打印出网址,则类似

file:///var/mobile/Containers/Data/Application/520D8354-B1D2-465F-93C2-54F2528D1AA9/Pictures/StorySplitter-528229F9-4D4F-4E28-82C9-3B1F2FDB215B.mp4

需要做一些额外的工作,以将上述mp4文件保存在图片Libraray中。

exportSession?.exportAsynchronously {
     PHPhotoLibrary.shared().performChanges({
        PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: newURL)
     }) { saved, error in
             // handle here
        }
}

此外,您需要将func getNewFilePath() -> String更改为FileManager.default.urls(for: .picturesDirectory, in: .userDomainMask)

,而不是使用NSTemporaryDirectory().appendingFormat("yourVideoName.mov")

PHPPhotoLibrary似乎无法从FileManager访问URL,但可以通过NSTemporaryDirectory()进行访问

仅供参考:以上两个网址 fileManagerPath:

file:///var/mobile/Containers/Data/Application/520D8354-B1D2-465F-93C2-54F2528D1AA9/Pictures/StorySplitter-528229F9-4D4F-4E28-82C9-3B1F2FDB215B.mp4

tempPath:

/private/var/mobile/Containers/Data/Application/520D8354-B1D2-465F-93C2-54F2528D1AA9/tmp/video.mov

主要区别在于files:///privatefiles:///将使该URL成为绝对URL,因此在trimVideo函数中,您必须使用{{1 }}作为导出URL。

由于相同的URL必须使用两次,并且URL(string: getNewFilePath())每次都获得唯一的URL,因此您需要将其保存在变量中,以便在getNewFilePath())中,您仍然可以引用相同的URL exportSession?.exportAsynchronously