| 
					
				 | 
			
			
				@@ -1,778 +0,0 @@ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//  PQPHAssetVideoParaseUtil.swift 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//  PQSpeed 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//  Created by SanW on 2020/8/3. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//  Copyright © 2020 BytesFlow. All rights reserved. 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import CoreServices 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import Photos 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-import UIKit 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-var currentExportSession: AVAssetExportSession? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-open class PQPHAssetVideoParaseUtil: NSObject { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    static var imagesOptions: PHImageRequestOptions = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let imagesOptions = PHImageRequestOptions() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        imagesOptions.isSynchronous = false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        imagesOptions.deliveryMode = .fastFormat 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        imagesOptions.resizeMode = .fast 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        imagesOptions.version = .current 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return imagesOptions 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    static var singleImageOptions: PHImageRequestOptions = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let singleImageOptions = PHImageRequestOptions() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        singleImageOptions.isSynchronous = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        singleImageOptions.isNetworkAccessAllowed = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        singleImageOptions.deliveryMode = .highQualityFormat 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        singleImageOptions.resizeMode = .none 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        singleImageOptions.version = .current 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return singleImageOptions 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    static var videoRequestOptions: PHVideoRequestOptions = { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let videoRequestOptions = PHVideoRequestOptions() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // 解决慢动作视频返回AVComposition而不是AVURLAsset 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//        videoRequestOptions.version = .original 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        videoRequestOptions.version = .current 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // 下载iCloud视频 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        videoRequestOptions.isNetworkAccessAllowed = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        videoRequestOptions.deliveryMode = .mediumQualityFormat 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return videoRequestOptions 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    }() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// PHAsset解析为AVPlayerItem 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Parameters: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - asset: <#asset description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - resultHandler: <#resultHandler description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Returns: <#description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public class func parasToAVPlayerItem(phAsset: PHAsset, isHighQuality: Bool = false, resultHandler: @escaping (AVPlayerItem?, Float64, [AnyHashable: Any]?) -> Void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        PHImageManager().requestPlayerItem(forVideo: phAsset, options: videoRequestOptions) { playerItem, info in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if isHighQuality, (playerItem?.asset as? AVURLAsset)?.url.absoluteString.components(separatedBy: "/").last?.contains(".medium.") ?? false { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let tempVideoOptions = PHVideoRequestOptions() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                tempVideoOptions.version = .original 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 下载iCloud视频 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                tempVideoOptions.isNetworkAccessAllowed = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                tempVideoOptions.deliveryMode = .highQualityFormat 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                tempVideoOptions.progressHandler = { progress, error, pointer, info in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "导出playerItem-progress = \(progress),error = \(String(describing: error)),pointer = \(pointer),info = \(String(describing: info))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                PHImageManager().requestPlayerItem(forVideo: phAsset, options: tempVideoOptions) { playerItem, info in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    let size = try! (playerItem?.asset as? AVURLAsset)?.url.resourceValues(forKeys: [.fileSizeKey]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "size = \(String(describing: size))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    resultHandler(playerItem, Float64(size?.fileSize ?? 0), info) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let size = try! (playerItem?.asset as? AVURLAsset)?.url.resourceValues(forKeys: [.fileSizeKey]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                BFLog(message: "size = \(String(describing: size))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                resultHandler(playerItem, Float64(size?.fileSize ?? 0), info) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// PHAsset解析为AVAsset 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Parameters: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - asset: <#asset description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - resultHandler: <#resultHandler description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Returns: <#description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public class func parasToAVAsset(phAsset: PHAsset, isHighQuality: Bool = true, resultHandler: @escaping (AVAsset?, Int, AVAudioMix?, [AnyHashable: Any]?) -> Void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        PHImageManager.default().requestAVAsset(forVideo: phAsset, options: videoRequestOptions) { avAsset, audioMix, info in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if isHighQuality, (avAsset as? AVURLAsset)?.url.absoluteString.components(separatedBy: "/").last?.contains(".medium.") ?? false { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let tempVideoOptions = PHVideoRequestOptions() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                tempVideoOptions.version = .original 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 下载iCloud视频 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                tempVideoOptions.isNetworkAccessAllowed = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                tempVideoOptions.deliveryMode = .highQualityFormat 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                tempVideoOptions.progressHandler = { progress, error, pointer, info in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "导出playerItem-progress = \(progress),error = \(String(describing: error)),pointer = \(pointer),info = \(String(describing: info))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                PHImageManager.default().requestAVAsset(forVideo: phAsset, options: tempVideoOptions) { tempAvAsset, tempAudioMix, tempInfo in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    let size = try! (tempAvAsset as? AVURLAsset)?.url.resourceValues(forKeys: [.fileSizeKey]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "size = \(String(describing: size))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    resultHandler(tempAvAsset, size?.fileSize ?? 0, tempAudioMix, tempInfo) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let size = try! (avAsset as? AVURLAsset)?.url.resourceValues(forKeys: [.fileSizeKey]) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                resultHandler(avAsset, size?.fileSize ?? 0, audioMix, info) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                BFLog(message: "size = \(String(describing: size))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// PHAsset 转码为.mp4保存本地 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Parameters: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - phAsset: <#phAsset description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - isAdjustRotationAngle: 是否调整旋转角度 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - resultHandler: <#resultHandler description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Returns: <#description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public class func exportPHAssetToMP4(phAsset: PHAsset, isAdjustRotationAngle: Bool = true, isCancelCurrentExport: Bool = false, deliveryMode: PHVideoRequestOptionsDeliveryMode? = .automatic, resultHandler: @escaping (_ phAsset: PHAsset, _ aVAsset: AVAsset?, _ filePath: String?, _ errorMsg: String?) -> Void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        BFLog(message: "导出相册视频-开始导出:phAsset = \(phAsset)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if isCancelCurrentExport { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            currentExportSession?.cancelExport() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        PQPHAssetVideoParaseUtil.parasToAVAsset(phAsset: phAsset) { avAsset, fileSize, _, _ in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if avAsset is AVURLAsset { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 创建目录 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                createDirectory(path: photoLibraryDirectory) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let fileName = (avAsset as! AVURLAsset).url.absoluteString 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let filePath = photoLibraryDirectory + fileName.md5.md5 + ".mp4" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let data = try? Data(contentsOf: NSURL.fileURL(withPath: filePath)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if FileManager.default.fileExists(atPath: filePath) && (data?.count ?? 0) > fileSize / 40 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "导出相册视频-已经导出完成:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        resultHandler(phAsset, avAsset, filePath, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    let tempExportSession = PQSingletoMemoryUtil.shared.allExportSession[phAsset] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if tempExportSession != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        BFLog(message: "导出相册视频-正在导出") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "导出相册视频-未导出视频过,开始导出:phAsset = \(phAsset)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // 删除以创建地址 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if FileManager.default.fileExists(atPath: filePath) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        do { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            try FileManager.default.removeItem(at: NSURL.fileURL(withPath: filePath)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } catch { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            BFLog(message: "导出相册视频-error == \(error)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    let requestOptions = PHVideoRequestOptions() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // 解决慢动作视频返回AVComposition而不是AVURLAsset 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    //        videoRequestOptions.version = .original 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    requestOptions.version = .current 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // 下载iCloud视频 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    requestOptions.isNetworkAccessAllowed = false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    requestOptions.progressHandler = { progress, error, pointer, info in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        BFLog(message: "导出相册视频-progress = \(progress),error = \(String(describing: error)),pointer = \(pointer),info = \(String(describing: info))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    requestOptions.deliveryMode = deliveryMode ?? .automatic 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    PHImageManager.default().requestExportSession(forVideo: phAsset, options: requestOptions, exportPreset: (deliveryMode == .automatic || deliveryMode == .mediumQualityFormat) ? AVAssetExportPresetMediumQuality : (deliveryMode == .highQualityFormat ? AVAssetExportPresetHighestQuality : AVAssetExportPresetLowQuality), resultHandler: { avAssetExportSession, _ in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        BFLog(message: "导出相册视频-请求到导出 avAssetExportSession = \(String(describing: avAssetExportSession))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        currentExportSession = avAssetExportSession 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        if avAssetExportSession != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            PQSingletoMemoryUtil.shared.allExportSession[phAsset] = avAssetExportSession! 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        avAssetExportSession?.outputURL = NSURL(fileURLWithPath: filePath) as URL 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        avAssetExportSession?.shouldOptimizeForNetworkUse = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        avAssetExportSession?.outputFileType = .mp4 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        if isAdjustRotationAngle { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            let rotationAngle = PQPHAssetVideoParaseUtil.videoRotationAngle(assert: avAsset!) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            // mdf by ak 统一导出的视频为30FPS 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            var centerTranslate: CGAffineTransform = CGAffineTransform(translationX: 0, y: 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            var mixedTransform: CGAffineTransform = CGAffineTransform() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            let videoComposition = AVMutableVideoComposition() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            videoComposition.frameDuration = CMTime(value: 1, timescale: 30) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            let tracks = avAsset?.tracks(withMediaType: .video) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            let firstTrack = tracks?.first 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            videoComposition.renderSize = CGSize(width: firstTrack?.naturalSize.width ?? 0, height: firstTrack?.naturalSize.height ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            mixedTransform = centerTranslate.rotated(by: 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            if rotationAngle == 90 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                centerTranslate = CGAffineTransform(translationX: firstTrack?.naturalSize.height ?? 0, y: 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                mixedTransform = centerTranslate.rotated(by: .pi / 2) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                videoComposition.renderSize = CGSize(width: firstTrack?.naturalSize.height ?? 0, height: firstTrack?.naturalSize.width ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } else if rotationAngle == 180 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                centerTranslate = CGAffineTransform(translationX: firstTrack?.naturalSize.width ?? 0, y: firstTrack?.naturalSize.height ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                mixedTransform = centerTranslate.rotated(by: .pi) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                videoComposition.renderSize = CGSize(width: firstTrack?.naturalSize.width ?? 0, height: firstTrack?.naturalSize.height ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } else if rotationAngle == 270 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                centerTranslate = CGAffineTransform(translationX: 0, y: firstTrack?.naturalSize.width ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                mixedTransform = centerTranslate.rotated(by: .pi / 2 * 3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                videoComposition.renderSize = CGSize(width: firstTrack?.naturalSize.height ?? 0, height: firstTrack?.naturalSize.width ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            let roateInstruction = AVMutableVideoCompositionInstruction() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            roateInstruction.timeRange = CMTimeRange(start: CMTime.zero, end: avAsset?.duration ?? CMTime.zero) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            if firstTrack != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                let layRoateInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: firstTrack!) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                layRoateInstruction.setTransform(mixedTransform, at: CMTime.zero) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                roateInstruction.layerInstructions = [layRoateInstruction] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                videoComposition.instructions = [roateInstruction] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                avAssetExportSession?.videoComposition = videoComposition 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                BFLog(message: "firstTrack is error !!!") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        avAssetExportSession?.exportAsynchronously(completionHandler: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            BFLog(message: "导出相册视频-progress = \(avAssetExportSession?.progress ?? 0),status = \(String(describing: avAssetExportSession?.status))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            switch avAssetExportSession?.status { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            case .unknown: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                    resultHandler(phAsset, avAsset, nil, avAssetExportSession?.error?.localizedDescription) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                avAssetExportSession?.cancelExport() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                PQSingletoMemoryUtil.shared.allExportSession.removeValue(forKey: phAsset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                BFLog(message: "导出相册视频-发生未知错误:\(filePath),\(avAssetExportSession?.error?.localizedDescription ?? "")") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            case .waiting: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                BFLog(message: "导出相册视频-等待导出mp4:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            case .exporting: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                BFLog(message: "导出相册视频-导出相册视频中...:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            case .completed: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                    resultHandler(phAsset, avAsset, filePath, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                avAssetExportSession?.cancelExport() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                PQSingletoMemoryUtil.shared.allExportSession.removeValue(forKey: phAsset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                BFLog(message: "导出相册视频-导出完成:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            case .failed: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                    resultHandler(phAsset, avAsset, nil, avAssetExportSession?.error?.localizedDescription) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                avAssetExportSession?.cancelExport() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                PQSingletoMemoryUtil.shared.allExportSession.removeValue(forKey: phAsset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                BFLog(message: "导出相册视频-导出失败:\(filePath),\(avAssetExportSession?.error?.localizedDescription ?? "")") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            case .cancelled: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                    resultHandler(phAsset, avAsset, nil, avAssetExportSession?.error?.localizedDescription) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                avAssetExportSession?.cancelExport() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                PQSingletoMemoryUtil.shared.allExportSession.removeValue(forKey: phAsset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                BFLog(message: "导出相册视频-取消导出:\(filePath),\(avAssetExportSession?.error?.localizedDescription ?? "")") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else if avAsset is AVComposition { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                BFLog(message: "导出相册视频-是AVComposition = \(String(describing: avAsset))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let assetResources = PHAssetResource.assetResources(for: phAsset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var resource: PHAssetResource? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                for assetRes in assetResources { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if assetRes.type == .video || assetRes.type == .pairedVideo { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        resource = assetRes 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if phAsset.mediaType == .video, resource != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    let fileName = (resource?.originalFilename ?? "") + (resource?.assetLocalIdentifier ?? "") + (resource?.uniformTypeIdentifier ?? "") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    let filePath = photoLibraryDirectory + fileName.md5 + ".mp4" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    let data = try? Data(contentsOf: NSURL.fileURL(withPath: filePath)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if FileManager.default.fileExists(atPath: filePath) && (data?.count ?? 0) > fileSize / 40 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            resultHandler(phAsset, avAsset, filePath, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        PHAssetResourceManager.default().writeData(for: resource!, toFile: URL(fileURLWithPath: filePath), options: nil) { error in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                resultHandler(phAsset, avAsset, error == nil ? filePath : nil, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        resultHandler(phAsset, avAsset, nil, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    resultHandler(phAsset, avAsset, nil, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// PHAsset 转码为.mp4保存本地 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Parameters: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - phAsset: <#phAsset description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - isAdjustRotationAngle: 是否调整旋转角度 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - resultHandler: <#resultHandler description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Returns: <#description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public class func writePHAssetDataToMP4(phAsset: PHAsset, isAdjustRotationAngle _: Bool = true, isCancelCurrentExport: Bool = false, deliveryMode _: PHVideoRequestOptionsDeliveryMode? = .automatic, resultHandler: @escaping (_ phAsset: PHAsset, _ aVAsset: AVAsset?, _ filePath: String?, _ errorMsg: String?) -> Void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        BFLog(message: "导出相册视频-开始导出:phAsset = \(phAsset)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if isCancelCurrentExport { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            currentExportSession?.cancelExport() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        PQPHAssetVideoParaseUtil.parasToAVAsset(phAsset: phAsset) { avAsset, fileSize, _, _ in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if avAsset is AVURLAsset { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 创建目录 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                createDirectory(path: photoLibraryDirectory) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let fileName = (avAsset as! AVURLAsset).url.absoluteString 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let filePath = photoLibraryDirectory + fileName.md5 + ".mp4" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let data = try? Data(contentsOf: NSURL.fileURL(withPath: filePath)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if FileManager.default.fileExists(atPath: filePath) && (data?.count ?? 0) > fileSize / 40 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "导出相册视频-已经导出完成:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        resultHandler(phAsset, avAsset, filePath, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    let tempExportSession = PQSingletoMemoryUtil.shared.allExportSession[phAsset] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if tempExportSession != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        BFLog(message: "导出相册视频-正在导出") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "导出相册视频-未导出视频过,开始导出:phAsset = \(phAsset)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // 删除以创建地址 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if FileManager.default.fileExists(atPath: filePath) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        do { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            try FileManager.default.removeItem(at: NSURL.fileURL(withPath: filePath)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } catch { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            BFLog(message: "导出相册视频-error == \(error)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    do { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        try FileManager.default.copyItem(at: (avAsset as! AVURLAsset).url, to: URL(fileURLWithPath: filePath)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } catch { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        BFLog(message: "导出相册视频-error == \(error)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    NSError *error; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            AVURLAsset *avurlasset = (AVURLAsset*)asset; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            NSURL *fileURL = [NSURL fileURLWithPath:savePath]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            if ([[NSFileManager defaultManager] copyItemAtURL:avurlasset.URL toURL:fileURL error:&error]) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                CBLog(@"保存成功"); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                dispatch_async(dispatch_get_main_queue(), ^{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                    if (result) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                        result(savePath,[savePath lastPathComponent]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            }else{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                CBLog(@"error=%@",error); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                [[NSFileManager defaultManager]removeItemAtPath:savePath error:nil]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                dispatch_async(dispatch_get_main_queue(), ^{ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                    failure(error.description); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    let requestOptions = PHVideoRequestOptions() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    // 解决慢动作视频返回AVComposition而不是AVURLAsset 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    //        videoRequestOptions.version = .original 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    requestOptions.version = .current 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    // 下载iCloud视频 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    requestOptions.isNetworkAccessAllowed = false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    requestOptions.progressHandler = { progress, error, pointer, info in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        BFLog(message: "导出相册视频-progress = \(progress),error = \(String(describing: error)),pointer = \(pointer),info = \(String(describing: info))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    requestOptions.deliveryMode = deliveryMode ?? .automatic 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    PHImageManager.default().requestExportSession(forVideo: phAsset, options: requestOptions, exportPreset: (deliveryMode == .automatic || deliveryMode == .mediumQualityFormat) ? AVAssetExportPreset1920x1080 :(deliveryMode == .highQualityFormat ? AVAssetExportPresetHighestQuality : AVAssetExportPresetLowQuality) , resultHandler: { avAssetExportSession, _ in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        BFLog(message: "导出相册视频-请求到导出 avAssetExportSession = \(avAssetExportSession)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        currentExportSession = avAssetExportSession 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        if avAssetExportSession != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            PQSingletoMemoryUtil.shared.allExportSession[phAsset] = avAssetExportSession! 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        avAssetExportSession?.outputURL = NSURL(fileURLWithPath: filePath) as URL 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        avAssetExportSession?.shouldOptimizeForNetworkUse = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        avAssetExportSession?.outputFileType = .mp4 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        if isAdjustRotationAngle { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            let rotationAngle = PQPHAssetVideoParaseUtil.videoRotationAngle(assert: avAsset!) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            // mdf by ak 统一导出的视频为30FPS 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            var centerTranslate: CGAffineTransform = CGAffineTransform(translationX: 0, y: 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            var mixedTransform: CGAffineTransform = CGAffineTransform() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            let videoComposition = AVMutableVideoComposition() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            videoComposition.frameDuration = CMTime(value: 1, timescale: 30) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            let tracks = avAsset?.tracks(withMediaType: .video) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            let firstTrack = tracks?.first 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            videoComposition.renderSize = CGSize(width: firstTrack?.naturalSize.width ?? 0, height: firstTrack?.naturalSize.height ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            mixedTransform = centerTranslate.rotated(by: 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-// 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            if rotationAngle == 90 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                centerTranslate = CGAffineTransform(translationX: firstTrack?.naturalSize.height ?? 0, y: 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                mixedTransform = centerTranslate.rotated(by: .pi / 2) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                videoComposition.renderSize = CGSize(width: firstTrack?.naturalSize.height ?? 0, height: firstTrack?.naturalSize.width ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            } else if rotationAngle == 180 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                centerTranslate = CGAffineTransform(translationX: firstTrack?.naturalSize.width ?? 0, y: firstTrack?.naturalSize.height ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                mixedTransform = centerTranslate.rotated(by: .pi) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                videoComposition.renderSize = CGSize(width: firstTrack?.naturalSize.width ?? 0, height: firstTrack?.naturalSize.height ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            } else if rotationAngle == 270 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                centerTranslate = CGAffineTransform(translationX: 0, y: firstTrack?.naturalSize.width ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                mixedTransform = centerTranslate.rotated(by: .pi / 2 * 3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                videoComposition.renderSize = CGSize(width: firstTrack?.naturalSize.height ?? 0, height: firstTrack?.naturalSize.width ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            let roateInstruction = AVMutableVideoCompositionInstruction() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            roateInstruction.timeRange = CMTimeRange(start: CMTime.zero, end: avAsset?.duration ?? CMTime.zero) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            if firstTrack != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                let layRoateInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: firstTrack!) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                layRoateInstruction.setTransform(mixedTransform, at: CMTime.zero) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                roateInstruction.layerInstructions = [layRoateInstruction] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                videoComposition.instructions = [roateInstruction] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                avAssetExportSession?.videoComposition = videoComposition 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                BFLog(message: "firstTrack is error !!!") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        avAssetExportSession?.shouldOptimizeForNetworkUse = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        avAssetExportSession?.exportAsynchronously(completionHandler: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            BFLog(message: "导出相册视频-progress = \(avAssetExportSession?.progress ?? 0),status = \(String(describing: avAssetExportSession?.status))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            switch avAssetExportSession?.status { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            case .unknown: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                    resultHandler(phAsset, avAsset, nil, avAssetExportSession?.error?.localizedDescription) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                avAssetExportSession?.cancelExport() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                PQSingletoMemoryUtil.shared.allExportSession.removeValue(forKey: phAsset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                BFLog(message: "导出相册视频-发生未知错误:\(filePath),\(avAssetExportSession?.error?.localizedDescription ?? "")") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            case .waiting: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                BFLog(message: "导出相册视频-等待导出mp4:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            case .exporting: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                BFLog(message: "导出相册视频-导出相册视频中...:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            case .completed: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                    resultHandler(phAsset, avAsset, filePath, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                avAssetExportSession?.cancelExport() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                PQSingletoMemoryUtil.shared.allExportSession.removeValue(forKey: phAsset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                BFLog(message: "导出相册视频-导出完成:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            case .failed: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                    resultHandler(phAsset, avAsset, nil, avAssetExportSession?.error?.localizedDescription) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                avAssetExportSession?.cancelExport() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                PQSingletoMemoryUtil.shared.allExportSession.removeValue(forKey: phAsset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                BFLog(message: "导出相册视频-导出失败:\(filePath),\(avAssetExportSession?.error?.localizedDescription ?? "")") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            case .cancelled: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                    resultHandler(phAsset, avAsset, nil, avAssetExportSession?.error?.localizedDescription) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                avAssetExportSession?.cancelExport() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                PQSingletoMemoryUtil.shared.allExportSession.removeValue(forKey: phAsset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                BFLog(message: "导出相册视频-取消导出:\(filePath),\(avAssetExportSession?.error?.localizedDescription ?? "")") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                                break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                        }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-//                    }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else if avAsset is AVComposition { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                BFLog(message: "导出相册视频-是AVComposition = \(String(describing: avAsset))") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let assetResources = PHAssetResource.assetResources(for: phAsset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var resource: PHAssetResource? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                for assetRes in assetResources { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if assetRes.type == .video || assetRes.type == .pairedVideo { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        resource = assetRes 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if phAsset.mediaType == .video, resource != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    let fileName = (resource?.originalFilename ?? "") + (resource?.assetLocalIdentifier ?? "") + (resource?.uniformTypeIdentifier ?? "") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    let filePath = photoLibraryDirectory + fileName.md5 + ".mp4" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    let data = try? Data(contentsOf: NSURL.fileURL(withPath: filePath)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    if FileManager.default.fileExists(atPath: filePath) && (data?.count ?? 0) > fileSize / 40 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            resultHandler(phAsset, avAsset, filePath, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        PHAssetResourceManager.default().writeData(for: resource!, toFile: URL(fileURLWithPath: filePath), options: nil) { error in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                resultHandler(phAsset, avAsset, error == nil ? filePath : nil, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        resultHandler(phAsset, avAsset, nil, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    resultHandler(phAsset, avAsset, nil, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// 导出相册视频 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Parameters: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - aVAsset: <#aVAsset description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - isAdjustRotationAngle: <#isAdjustRotationAngle description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - resultHandler: <#resultHandler description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public class func exportAVAssetToMP4(aVAsset: AVURLAsset, isAdjustRotationAngle: Bool = true, resultHandler: @escaping (_ aVAsset: AVURLAsset?, _ filePath: String?, _ errorMsg: String?) -> Void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        currentExportSession?.cancelExport() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        BFLog(message: "开始导出相册视频:url = \(aVAsset.url.absoluteString)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // 创建目录 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        createDirectory(path: photoLibraryDirectory) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let fileName = aVAsset.url.absoluteString 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let filePath = photoLibraryDirectory + fileName.md5 + ".mp4" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let data = try? Data(contentsOf: NSURL.fileURL(withPath: filePath)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let fileSize = try! aVAsset.url.resourceValues(forKeys: [.fileSizeKey]).fileSize ?? 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if FileManager.default.fileExists(atPath: filePath) && (data?.count ?? 0) > fileSize / 40 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                resultHandler(aVAsset, filePath, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            BFLog(message: "未导出视频过,开始导出:aVAsset = \(aVAsset)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 删除以创建地址 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            try? FileManager.default.removeItem(at: NSURL.fileURL(withPath: filePath)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let avAssetExportSession = AVAssetExportSession(asset: aVAsset, presetName: AVAssetExportPreset1280x720) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            currentExportSession = avAssetExportSession 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            avAssetExportSession?.outputURL = NSURL(fileURLWithPath: filePath) as URL 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            avAssetExportSession?.shouldOptimizeForNetworkUse = false 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            avAssetExportSession?.outputFileType = .mp4 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if isAdjustRotationAngle { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let rotationAngle = PQPHAssetVideoParaseUtil.videoRotationAngle(assert: aVAsset) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // mdf by ak 统一导出的视频为30FPS 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var centerTranslate: CGAffineTransform = CGAffineTransform(translationX: 0, y: 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var mixedTransform: CGAffineTransform = CGAffineTransform() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let videoComposition = AVMutableVideoComposition() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                videoComposition.frameDuration = CMTime(value: 1, timescale: 30) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let tracks = aVAsset.tracks(withMediaType: .video) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let firstTrack = tracks.first 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                videoComposition.renderSize = CGSize(width: firstTrack?.naturalSize.width ?? 0, height: firstTrack?.naturalSize.height ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                mixedTransform = centerTranslate.rotated(by: 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if rotationAngle == 90 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    centerTranslate = CGAffineTransform(translationX: firstTrack?.naturalSize.height ?? 0, y: 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    mixedTransform = centerTranslate.rotated(by: .pi / 2) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    videoComposition.renderSize = CGSize(width: firstTrack?.naturalSize.height ?? 0, height: firstTrack?.naturalSize.width ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } else if rotationAngle == 180 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    centerTranslate = CGAffineTransform(translationX: firstTrack?.naturalSize.width ?? 0, y: firstTrack?.naturalSize.height ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    mixedTransform = centerTranslate.rotated(by: .pi) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    videoComposition.renderSize = CGSize(width: firstTrack?.naturalSize.width ?? 0, height: firstTrack?.naturalSize.height ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } else if rotationAngle == 270 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    centerTranslate = CGAffineTransform(translationX: 0, y: firstTrack?.naturalSize.width ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    mixedTransform = centerTranslate.rotated(by: .pi / 2 * 3) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    videoComposition.renderSize = CGSize(width: firstTrack?.naturalSize.height ?? 0, height: firstTrack?.naturalSize.width ?? 0) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let roateInstruction = AVMutableVideoCompositionInstruction() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                roateInstruction.timeRange = CMTimeRange(start: CMTime.zero, end: aVAsset.duration) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let layRoateInstruction = AVMutableVideoCompositionLayerInstruction(assetTrack: firstTrack!) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                layRoateInstruction.setTransform(mixedTransform, at: CMTime.zero) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                roateInstruction.layerInstructions = [layRoateInstruction] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                videoComposition.instructions = [roateInstruction] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                avAssetExportSession?.videoComposition = videoComposition 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            avAssetExportSession?.shouldOptimizeForNetworkUse = true 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            avAssetExportSession?.exportAsynchronously(completionHandler: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                BFLog(message: "导出相册视频progress = \(avAssetExportSession?.progress ?? 0)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                switch avAssetExportSession?.status { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case .unknown: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        resultHandler(aVAsset, nil, avAssetExportSession?.error?.localizedDescription) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "导出相册视频发生未知错误:\(filePath),\(avAssetExportSession?.error?.localizedDescription ?? "")") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case .waiting: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "等待导出mp4:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case .exporting: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "导出相册视频中...:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case .completed: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        resultHandler(aVAsset, filePath, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "导出相册视频完成:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case .failed: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        resultHandler(aVAsset, nil, avAssetExportSession?.error?.localizedDescription) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "导出相册视频失败:\(filePath),\(avAssetExportSession?.error?.localizedDescription ?? "")") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case .cancelled: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        resultHandler(aVAsset, nil, avAssetExportSession?.error?.localizedDescription) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "取消导出相册视频:\(filePath),\(avAssetExportSession?.error?.localizedDescription ?? "")") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// 获取视频资源的旋转角度 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Parameter assert: <#assert description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Returns: <#description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public class func videoRotationAngle(assert: AVAsset) -> Int { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        var rotationAngle: Int = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let tracks = assert.tracks(withMediaType: .video) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if tracks.count > 0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let firstTrack = tracks.first 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let transform = firstTrack?.preferredTransform 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if transform?.a == 0, transform?.b == 1.0, transform?.c == -1.0, transform?.d == 0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                rotationAngle = 90 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else if transform?.a == -1.0, transform?.b == 0, transform?.c == 0, transform?.d == -1.0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                rotationAngle = 180 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else if transform?.a == 0, transform?.b == -1.0, transform?.c == 1.0, transform?.d == 0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                rotationAngle = 270 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else if transform?.a == 1.0, transform?.b == 0, transform?.c == 0, transform?.d == 1.0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                rotationAngle = 0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        return rotationAngle 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// 裁剪背景音乐并导出 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Parameters: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - url: 原始地址 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - startTime: 开始时间 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - endTime: 结束时间 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - resultHandler: <#resultHandler description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Returns: <#description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public class func cutAudioToLocal(url: String, startTime: Float, endTime: Float, resultHandler: @escaping (_ url: String, _ filePath: String?, _ startTime: Float, _ endTime: Float, _ errorMsg: String?) -> Void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // 创建目录 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        createDirectory(path: bgMusicDirectory) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let filePath = bgMusicDirectory + url.md5 + ".mp3" 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let data = try? Data(contentsOf: NSURL.fileURL(withPath: filePath)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if FileManager.default.fileExists(atPath: filePath) && (data?.count ?? 0) > 0 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                resultHandler(url, filePath, startTime, endTime, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 删除以创建地址 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            try? FileManager.default.removeItem(at: NSURL.fileURL(withPath: filePath)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let audioAsset = AVURLAsset(url: URL(string: url)!) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            audioAsset.loadValuesAsynchronously(forKeys: ["duration", "tracks"]) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let status = audioAsset.statusOfValue(forKey: "tracks", error: nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                switch status { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case .loaded: // 加载完成 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    // AVAssetExportPresetPassthrough /AVAssetExportPresetAppleM4A 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    let exportSession = AVAssetExportSession(asset: audioAsset, presetName: AVAssetExportPresetHighestQuality) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    exportSession?.outputURL = URL(fileURLWithPath: filePath) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    exportSession?.outputFileType = .mp3 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    exportSession?.timeRange = CMTimeRange(start: CMTime(seconds: Double(startTime), preferredTimescale: 1000), end: CMTime(seconds: Double(endTime), preferredTimescale: 1000)) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    exportSession?.exportAsynchronously(completionHandler: { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        switch exportSession?.status { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        case .waiting: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            BFLog(message: "等待导出mp3:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        case .exporting: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            BFLog(message: "导出中...:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        case .completed: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                resultHandler(url, filePath, startTime, endTime, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            BFLog(message: "导出完成:\(filePath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        case .cancelled, .failed, .unknown: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                                resultHandler(url, nil, startTime, endTime, exportSession?.error?.localizedDescription) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            BFLog(message: "导出失败:\(filePath),\(exportSession?.error?.localizedDescription ?? "")") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                            break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case .loading: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "加载中...:\(url)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                case .failed, .cancelled, .unknown: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    DispatchQueue.main.async { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                        resultHandler(url, nil, startTime, endTime, "导出失败") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                default: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    break 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// 创建本地保存地址 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Parameters: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - sourceFilePath: <#sourceFilePath description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - completeHandle: <#completeHandle description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Returns: <#description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public class func createLocalFile(sourceFilePath: String, completeHandle: (_ isFileExists: Bool, _ isCreateSuccess: Bool, _ filePath: String) -> Void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let cLocalPath = NSString(string: NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!).appendingPathComponent("\(sourceFilePath.md5).mp4") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        if FileManager.default.fileExists(atPath: cLocalPath) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            BFLog(message: "文件已经存在:\(cLocalPath)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            completeHandle(true, false, cLocalPath) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            let result = FileManager.default.createFile(atPath: cLocalPath, contents: nil, attributes: nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            BFLog(message: "文件创建:\(cLocalPath),\(result)") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            completeHandle(false, result, cLocalPath) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// 获取图库图片 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Parameters: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - asset: <#asset description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - itemSize: <#itemSize description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - resultHandler: <#resultHandler description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Returns: <#description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public class func requestAssetImage(asset: PHAsset, itemSize: CGSize, resultHandler: @escaping (UIImage?, [AnyHashable: Any]?) -> Void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        PHCachingImageManager().requestImage(for: asset, targetSize: itemSize, contentMode: .aspectFill, options: imagesOptions, resultHandler: { image, info in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            BFLog(message: "info = \(info ?? [:])") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if info?.keys.contains("PHImageResultIsDegradedKey") ?? false, "\(info?["PHImageResultIsDegradedKey"] ?? "0")" == "0" { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                resultHandler(image, info) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        }) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// 获取图库原图 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Parameters: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - asset: <#asset description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - resultHandler: <#resultHandler description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Returns: <#description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public class func requestAssetOringinImage(asset: PHAsset, resultHandler: @escaping (_ isGIF: Bool, _ data: Data?, UIImage?, [AnyHashable: Any]?) -> Void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        PHCachingImageManager().requestImageData(for: asset, options: singleImageOptions) { data, _, _, info in 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            var image: UIImage? 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if data != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                image = UIImage(data: data!) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if info?.keys.contains("PHImageFileUTIKey") ?? false, "\(info?["PHImageFileUTIKey"] ?? "")" == "com.compuserve.gif" { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                resultHandler(true, data, image, info) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                resultHandler(false, data, image, info) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				- 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// 获取gif帧跟时长 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Parameters: 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - data: <#data description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - isRenderingTemplate 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    ///   - resultHandler: <#resultHandler description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    /// - Returns: <#description#> 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    public class func parasGIFImage(data: Data, isRenderingColor: UIColor? = nil, resultHandler: @escaping (_ data: Data, _ images: [UIImage]?, _ duration: Double?) -> Void) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let info: [String: Any] = [ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            kCGImageSourceShouldCache as String: true, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            kCGImageSourceTypeIdentifierHint as String: kUTTypeGIF, 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        ] 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        guard let imageSource = CGImageSourceCreateWithData(data as CFData, info as CFDictionary) else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            resultHandler(data, nil, nil) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            BFLog(message: "获取gifimageSource 失败") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        // 获取帧数 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        let frameCount = CGImageSourceGetCount(imageSource) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        var gifDuration = 0.0 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        var images = [UIImage]() 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        for i in 0 ..< frameCount { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            // 取出索引对应的图片 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            guard let imageRef = CGImageSourceCreateImageAtIndex(imageSource, i, info as CFDictionary) else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                BFLog(message: "取出对应的图片失败") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            if frameCount == 1 { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 单帧 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                gifDuration = .infinity 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 1.获取gif没帧的时间间隔 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 获取到该帧图片的属性字典 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                guard let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, i, nil) as? [String: Any] else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "取出对应的图片属性失败") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 获取该帧图片中的GIF相关的属性字典 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                guard let gifInfo = properties[kCGImagePropertyGIFDictionary as String] as? [String: Any] else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "取出对应的图片属性失败") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let defaultFrameDuration = 0.1 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 获取该帧图片的播放时间 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let unclampedDelayTime = gifInfo[kCGImagePropertyGIFUnclampedDelayTime as String] as? NSNumber 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 如果通过kCGImagePropertyGIFUnclampedDelayTime没有获取到播放时长,就通过kCGImagePropertyGIFDelayTime来获取,两者的含义是相同的; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let delayTime = gifInfo[kCGImagePropertyGIFDelayTime as String] as? NSNumber 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let duration = unclampedDelayTime ?? delayTime 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                guard let frameDuration = duration else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    BFLog(message: "获取帧时间间隔失败") 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    return 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 对于播放时间低于0.011s的,重新指定时长为0.100s; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                let gifFrameDuration = frameDuration.doubleValue > 0.011 ? frameDuration.doubleValue : defaultFrameDuration 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 计算总时间 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                gifDuration += gifFrameDuration 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                // 2.图片 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                var frameImage: UIImage? = UIImage(cgImage: imageRef, scale: 1.0, orientation: .up) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if isRenderingColor != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    frameImage = frameImage?.tintImage(color: isRenderingColor!, blendMode: .destinationIn) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                if frameImage != nil { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                    images.append(frameImage!) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-        resultHandler(data, images, gifDuration) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				-} 
			 |