美文网首页
VideoUtils

VideoUtils

作者: devchena | 来源:发表于2018-03-12 13:23 被阅读98次
    //
    //  MediaUtils.swift
    //  QingkCloudPhoneBackground
    //
    //  Created by user on 2018/1/15.
    //  Copyright © 2018年 devchena. All rights reserved.
    //
    
    import Photos
    import AVFoundation
    import AssetsLibrary
    import AVKit
    
    let defaultFolderName = "文件夹名称"
    
    class VideoUtils {
        
        /* 临时文件夹路径(先创建) */
        class func tempPath() -> String? {
            let tempPath = NSTemporaryDirectory()
            let tempMediaURL = URL(fileURLWithPath: tempPath).appendingPathComponent("tempMedia")
            
            let fileManager = FileManager.default
            let isExisting = fileManager.fileExists(atPath: tempMediaURL.path)
            if !isExisting {
                do {
                    try fileManager.createDirectory(at: tempMediaURL, withIntermediateDirectories: true, attributes: nil)
                } catch let error {
                    print("TempMedia directory creation failed:\(error)")
                    return nil
                }
            }
            
            return tempMediaURL.path
        }
        
        /* 默认文件输出路径 */
        class func defaultOutputURL(with fileType: String = "mov") -> URL? {
            guard let tempPath = tempPath() else {
                return nil
            }
            
            let dateFormatter = DateFormatter()
            dateFormatter.dateFormat = "YYYY-MM-dd-hh:mm:ss:SS"
            let fileName = dateFormatter.string(from: Date()) + String(format: ".%@", fileType)
            return URL(fileURLWithPath: tempPath).appendingPathComponent(fileName)
        }
    
        /* 删除临时文件夹 */
        class func deleteTempPath() {
            guard let path = tempPath() else {
                return
            }
            
            let fileManager = FileManager.default
            if fileManager.fileExists(atPath: path) {
                do {
                    try fileManager.removeItem(atPath: path)
                } catch let error {
                    print("Delete tempPath failed:\(error)")
                }
            }
        }
    
        /* 删除指定路径文件 */
        class func deleteFile(atPath path: String) {
            let fileManager = FileManager.default
            if fileManager.fileExists(atPath: path) {
                do {
                    try fileManager.removeItem(atPath: path)
                } catch let error {
                    print("Delete File failed:\(error)")
                }
            }
        }
        
        /* 指定路径文件大小 */
        class func fileSize(atPath path: String) -> UInt64 {
            let fileHandle = FileHandle(forReadingAtPath: path)
            return fileHandle?.seekToEndOfFile() ?? 0
        }
        
        /* 获取指定路径下的二进制数据 */
        class func fileData(atPath path: String) -> Data? {
            guard let data = FileManager.default.contents(atPath: path) else {
                return nil
            }
            return data
        }
    }
    
    extension VideoUtils {
        
        /* 是否存在相册文件夹 */
        class func isExistFolder(with folderName: String = defaultFolderName) -> Bool {
            var isExisted = false
    
            let collectonResults = PHCollectionList.fetchTopLevelUserCollections(with: nil)
            collectonResults.enumerateObjects({ (obj, index, stop) -> Void in
                if let assetCollection = obj as? PHAssetCollection, assetCollection.localizedTitle == folderName {
                    isExisted = true
                }
            })
            
            return isExisted
        }
        
        /* 创建相册文件夹 */
        class func createFolder(with folderName: String = defaultFolderName) {
            if !isExistFolder(with: folderName) {
                PHPhotoLibrary.shared().performChanges({
                    PHAssetCollectionChangeRequest.creationRequestForAssetCollection(withTitle: folderName)
                }, completionHandler: { (success, error) in
                    if !success {
                        print("创建相册文件夹失败:\(String(describing: error))")
                    }
                })
            }
        }
        
        /* 保存视频至指定相册文件夹 */
        class func saveVideoToFolder(videoPathURL: URL,
                                     folderName: String = defaultFolderName,
                                     completion: @escaping (_ localIdentifier: String) -> Void,
                                     failure: ((_ error: Error?) -> Void)? = nil) {
            var localIdentifier: String?
            let collectonResults = PHCollectionList.fetchTopLevelUserCollections(with: nil)
            collectonResults.enumerateObjects({ (obj, index, stop) -> Void in
                if let assetCollection = obj as? PHAssetCollection, assetCollection.localizedTitle == folderName {
                    PHPhotoLibrary.shared().performChanges({
                        // 请求创建一个Asset
                        let assetRequest = PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: videoPathURL)
                        // 请求编辑相册
                        let collectonRequest = PHAssetCollectionChangeRequest(for: assetCollection)
                        // 为Asset创建一个占位符,放到相册编辑请求中
                        if let placeHolder = assetRequest?.placeholderForCreatedAsset {
                            // 相册中添加视频
                            collectonRequest?.addAssets([placeHolder] as NSFastEnumeration)
                            localIdentifier = placeHolder.localIdentifier
                        } else {
                            failure?(nil)
                            return
                        }
                        
                    }, completionHandler: { (success, error) in
                        if success && localIdentifier != nil {
                            completion(localIdentifier!)
                        } else {
                            failure?(error)
                        }
                    })
                }
            })
        }
        
        class func requestVideo(localIdentifier: String,
                                 folderName: String = defaultFolderName,
                                 completion: @escaping (_ asset: PHAsset) -> Void,
                                 failure: (() -> Void)? = nil) {
            DispatchQueue.global().async(execute: { () -> Void in
                let result1 = PHAssetCollection.fetchAssetCollections(with: .smartAlbum, subtype: .albumRegular, options: nil)
                let result2 = PHAssetCollection.fetchAssetCollections(with: .album, subtype: .albumRegular, options: nil)
                let results = [result1, result2]
                
                let fetchOptions = PHFetchOptions()
                fetchOptions.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
                fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.video.rawValue)
                
                var asset: PHAsset?
                for result in results {
                    for i in 0..<result.count {
                        let assetCollection = result[i]
                        if assetCollection.localizedTitle == folderName {
                            let fetchResult = PHAsset.fetchAssets(in: assetCollection, options: fetchOptions)
                            fetchResult.enumerateObjects({ (obj, index, stop) -> Void in
                                if obj.localIdentifier == localIdentifier {
                                    asset = obj
                                }
                            })
                        }
                    }
                }
                
                if asset != nil {
                    completion(asset!)
                } else {
                    failure?()
                }
            })
        }
        
        class func requestVideoInformation(for asset: PHAsset,
                                           completion: @escaping (_ entity: MTVideoEntity) -> Void,
                                           failure: ((Error?) -> Void)? = nil) {
            let options = PHVideoRequestOptions()
            options.version = .current
            options.deliveryMode = .automatic
            PHImageManager.default().requestAVAsset(forVideo: asset, options: options, resultHandler: { (obj, audioMix, info) in
                guard let avAsset = obj, let avURLAsset = avAsset as? AVURLAsset else {
                    failure?(nil)
                    return
                }
                
                let url = avURLAsset.url
                let time = avAsset.duration
                let seconds = UInt(ceil(Double(time.value)/Double(time.timescale)))
                
                let entity = MTVideoEntity()
                entity.duration = seconds
                entity.videoURL = url
                entity.size = fileSize(atPath: url.path)
                entity.thumbnail = extractThumbnail(with: avURLAsset)
    
                completion(entity)
            })
        }
        
        class func requestVideoInformation(localIdentifier: String,
                                           folderName: String = defaultFolderName,
                                           completion: @escaping (_ entity: MTVideoEntity) -> Void,
                                           failure: ((Error?) -> Void)? = nil) {
            requestVideo(localIdentifier: localIdentifier, folderName: folderName, completion: { (asset) in
                requestVideoInformation(for: asset, completion: { (entity) in
                    completion(entity)
                }, failure: { (error) in
                    failure?(error)
                })
            }, failure: {
                failure?(nil)
            })
        }
    }
    
    extension VideoUtils {
        
        /* 请求录制权限 */
        class func requestRecordingPermission(completionHandler handler: @escaping (_ videoAllowed: Bool, _ audioAllowed: Bool) -> Void) {
            AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeVideo) { (videoAllowed) in
                AVCaptureDevice.requestAccess(forMediaType: AVMediaTypeAudio, completionHandler: { (audioAllowed) in
                    DispatchQueue.main.async {
                        handler(videoAllowed, audioAllowed)
                    }
                })
            }
        }
    
        // 转换assetURL为路径
        class func convertToVideoURL(with assetURL: URL, completion: @escaping (URL?) -> Void) {
            DispatchQueue.global().async {
                let fetchOptions = PHFetchOptions()
                fetchOptions.predicate = NSPredicate(format: "mediaType = %d", PHAssetMediaType.video.rawValue)
                let fetchResult = PHAsset.fetchAssets(withALAssetURLs: [assetURL], options: fetchOptions)
                guard let phAsset = fetchResult.firstObject else {
                    completion(nil)
                    return
                }
                
                let options = PHVideoRequestOptions()
                options.version = .current
                options.deliveryMode = .automatic
                PHImageManager.default().requestAVAsset(forVideo: phAsset, options: options, resultHandler: { (avAsset, audioMix, info) in
                    guard let url = (avAsset as? AVURLAsset)?.url else {
                        completion(nil)
                        return
                    }
                    completion(url)
                })
            }
        }
    
        /* 提取指定路径的本地视频封面图 */
        class func extractThumbnail(with asset: AVURLAsset) -> UIImage? {
            let imageGenerator = AVAssetImageGenerator(asset: asset)
            imageGenerator.appliesPreferredTrackTransform = true
            imageGenerator.apertureMode = AVAssetImageGeneratorApertureModeEncodedPixels
            let time = CMTime(value: 0, timescale: asset.duration.timescale)
            var cgImage: CGImage?
            do {
                try cgImage = imageGenerator.copyCGImage(at: time, actualTime: nil)
            } catch let error {
                print("Copy cgImage failed! Error:\(error)")
                return nil
            }
            return (cgImage != nil) ? UIImage(cgImage: cgImage!) : nil
        }
        
        class func loaclVideoInfo(with fileURL: URL) -> MTVideoEntity {
            let asset = AVURLAsset(url: fileURL)
            let thumbnail = extractThumbnail(with: asset)
            let time = asset.duration
            let seconds = UInt(ceil(Double(time.value)/Double(time.timescale)))
            
            let entity = MTVideoEntity()
            entity.duration = seconds
            entity.thumbnail = thumbnail
            entity.size = fileSize(atPath: fileURL.path)
            entity.videoURL = fileURL
            entity.durationString = self.getFormatPlayTime(secounds: TimeInterval(entity.duration))
            let sizeFloat = Float(entity.size)/1024.0/1024.0
            entity.sizeString = String(format: "%.2fM", sizeFloat)
            return entity
        }
        
        class func getFormatPlayTime(secounds:TimeInterval)->String{
            if secounds.isNaN{
                return "00:00"
            }
            var Min = Int(secounds / 60)
            let Sec = Int(secounds.truncatingRemainder(dividingBy: 60))
            var Hour = 0
            if Min>=60 {
                Hour = Int(Min / 60)
                Min = Min - Hour*60
                return String(format: "%02d:%02d:%02d", Hour, Min, Sec)
            }
            return String(format: "%02d:%02d", Min, Sec)
        }
    }
    
    extension VideoUtils {
        
        /* 保存视频至相册 */
        class func saveVideoToPhotosAlbum(videoPathURL: URL,
                                          completion: @escaping (_ videoPathURLInPhotosAlbum: URL) -> Void,
                                          failure: ((_ error: Error?, _ message: String?) -> Void)? = nil) {
            PhotoAssetUtils.authorizationStatus({ (isValid) -> Void in
                if isValid {
                    ALAssetsLibrary().writeVideoAtPath(toSavedPhotosAlbum: videoPathURL, completionBlock: { (assetURL, error) in
                        if let url = assetURL, error == nil {
                            convertToVideoURL(with: url, completion: { (fileURL) in
                                if fileURL != nil {
                                    completion(fileURL!)
                                } else {
                                    failure?(nil, "获取视频路径异常")
                                }
                            })
                        } else {
                            failure?(error, "保存视频至相册失败")
                        }
                    })
                } else {
                    failure?(nil, nil)
                    let message = "请在【设置】-【隐私】-【照片】里打开允许访问相册的权限"
                    UIApplication.shared.keyWindow?.showAlert(type: .Alert, title: "您没有开启相册权限", message: message, sourceView: nil, actions: [
                        AlertAction(title: "取消", type: .Cancel, handler: nil),
                        AlertAction(title: "去设置", type: .Default, handler: { () -> Void in
                            if let url = URL(string: UIApplicationOpenSettingsURLString) {
                                UIApplication.shared.openURL(url)
                            }
                        })
                    ])
                }
            })
        }
    
        /*!
         @abstract                      写入视频文件的二进制数据到指定路径
         @param        toFile           写入的视频路径
         @param        completion       完成回调
         @param        failure          失败回调
         @discussion                    不影响源文件
         */
        class func writeVideo(for asset: PHAsset,
                              toFile fileURL: URL,
                              completion: @escaping (_ fileURL: URL) -> Void,
                              failure: ((Error?) -> Void)? = nil) {
            guard asset.mediaType == .video else {
                failure?(nil)
                return
            }
            
            if #available(iOS 9.0, *) {
                var assetResource: PHAssetResource?
                let assetResources = PHAssetResource.assetResources(for: asset)
                for assetRes in assetResources {
                    if assetRes.type == .video { assetResource = assetRes }
                }
                
                if let assetRes = assetResource {
                    let fileManager = FileManager.default
                    if fileManager.fileExists(atPath: fileURL.path) {
                        do {
                            try fileManager.removeItem(atPath: fileURL.path)
                        } catch let error {
                            print("Remove the existing item failure:\(error)")
                            failure?(error)
                            return
                        }
                    }
                    
                    PHAssetResourceManager.default().writeData(for: assetRes, toFile: fileURL, options: nil, completionHandler: { (error) in
                        error == nil ? completion(fileURL) : failure?(error)
                    })
                } else { failure?(nil) }
            } else {
                let options = PHVideoRequestOptions()
                options.version = .current
                options.deliveryMode = .automatic
                PHImageManager.default().requestAVAsset(forVideo: asset, options: options, resultHandler: { (avAsset, audioMix, info) in
                    guard let url = (avAsset as? AVURLAsset)?.url else {
                        failure?(nil)
                        return
                    }
                
                    var data: Data?
                    do {
                        try data = Data(contentsOf: url)
                    } catch let error {
                        failure?(error)
                        return
                    }
                    
                    guard let videoData = data else {
                        failure?(nil)
                        return
                    }
                    do {
                        try videoData.write(to: fileURL)
                    } catch let error {
                        failure?(error)
                        return
                    }
                    completion(fileURL)
                })
            }
        }
        
        /*!
         @abstract                      转换视频质量
         @param        inputURL         源视频文件地址(二进制数据地址,非PHAssetURL)
         @param        outputURL        转换后视频文件输出地址
         @param        fileLengthLimit  转换后的文件大小限制
         @param        fileLengthLimit  转换进度
         @param        completion       完成回调
         @param        failure          失败回调
         @discussion                    默认将原视频文件转换为中质量MP4格式并输出到指定路径(不影响源文件)
         */
        class func convertVideoQuailty(with inputURL: URL,
                                       outputURL: URL? = nil,
                                       fileLengthLimit: Int64? = nil,
                                       progress: ((Float) -> Void)? = nil,
                                       completion: @escaping (_ exportSession: AVAssetExportSession, _ compressedOutputURL: URL) -> Void,
                                       failure: ((Error?) -> Void)? = nil) {
            guard let compressedOutputURL = (outputURL ?? defaultOutputURL(with: "mp4")) else {
                failure?(nil)
                return
            }
            
            let fileManager = FileManager.default
            if fileManager.fileExists(atPath: compressedOutputURL.path) {
                do {
                    try fileManager.removeItem(atPath: compressedOutputURL.path)
                } catch let error {
                    print("Remove the existing item failure:\(error)")
                    failure?(error)
                    return
                }
            }
            
            let asset = AVURLAsset(url: inputURL, options: nil)
            guard let exportSession = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset960x540) else {
                failure?(nil)
                return
            }
            exportSession.outputURL = compressedOutputURL
            exportSession.shouldOptimizeForNetworkUse = true
            exportSession.outputFileType = AVFileTypeMPEG4
            if fileLengthLimit != nil {
                exportSession.fileLengthLimit = fileLengthLimit!
            }
            exportSession.exportAsynchronously {
                progress?(exportSession.progress)
                let exportStatus = exportSession.status
                switch exportStatus {
                case .unknown:
                    break
                case .waiting:
                    break
                case .exporting:
                    break
                case .cancelled:
                    break
                case .failed:
                    print("AVAssetExportSessionStatusFailed:\(String(describing: exportSession.error))!")
                    failure?(exportSession.error)
                case .completed:
                    completion(exportSession, compressedOutputURL)
                }
            }
        }
    }
    
    
    
    

    相关文章

      网友评论

          本文标题:VideoUtils

          本文链接:https://www.haomeiwen.com/subject/ojenfftx.html