LivePhotoUtilSwift

 avatar
unknown
swift
11 days ago
4.5 kB
5
No Index
import Photos
import UIKit
import ImageIO
import MobileCoreServices

class LivePhotoUtilSwift: NSObject {
    static func convertVideo(_ path: String, complete: @escaping (Bool, String?) -> Void) {

        let metaURL = Bundle.main.url(forResource: "metadata", withExtension: "mov")
        let livePhotoSize = CGSize(width: 1080, height: 1920)
        let livePhotoDuration = CMTime(value: 550, timescale: 600)
        let assetIdentifier = UUID().uuidString
        
        let documentPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
        let durationPath = documentPath + "/duration.mp4"
        let acceleratePath = documentPath + "/accelerate.mp4"
        let resizePath = documentPath + "/resize.mp4"
        
        try? FileManager.default.removeItem(atPath: durationPath)
        try? FileManager.default.removeItem(atPath: acceleratePath)
        try? FileManager.default.removeItem(atPath: resizePath)

        let finalPath = resizePath

        let converter = Converter4Video(path: finalPath)

        converter.durationVideo(at: path, outputPath: durationPath, targetDuration: 3) { success, error in
            converter.accelerateVideo(at: durationPath, to: livePhotoDuration, outputPath: acceleratePath) { success, error in
                converter.resizeVideo(at: acceleratePath, outputPath: resizePath, outputSize: livePhotoSize) { success, error in

                    let asset = AVURLAsset(url: URL(filePath: finalPath))
                    let generator = AVAssetImageGenerator(asset: asset)
                    generator.appliesPreferredTrackTransform = true
                    generator.requestedTimeToleranceAfter = .zero
                    generator.requestedTimeToleranceBefore = .zero

                    var times = [NSValue]()
                    let time = CMTime(value: CMTimeValue(0.5), timescale: asset.duration.timescale)
                    times.append(NSValue(time: time))


                    // serial queues are the default. No need to specify this.
                    let q = DispatchQueue(label: "image")

                    var index = 0
                    generator .generateCGImagesAsynchronously(forTimes: times) { requestedTime, image, actualTime, result, error in
                        guard let image else { return }
                        let picturePath = documentPath.appending(String(format: "/%@%d.heic", "live", index))
                        let videoPath = documentPath.appending(String(format: "/%@%d.mov", "live", index))
                        index += 1

                        try? FileManager.default.removeItem(atPath: picturePath)
                        try? FileManager.default.removeItem(atPath: videoPath)

                        let converter4Image = Converter4Image(image: UIImage(cgImage: image))
                        q.async {
                            print(picturePath)
                            print(videoPath)

                            converter4Image.write(dest: picturePath, assetIdentifier: assetIdentifier)
                            converter.write(dest: videoPath, assetIdentifier: assetIdentifier, metaURL: metaURL!) { success, error in
                                if !success {
                                    print("merge failed: \(error)")
                                    complete(false, error?.localizedDescription)
                                    return
                                }

                                PHPhotoLibrary.shared().performChanges {
                                    let request = PHAssetCreationRequest.forAsset()
                                    let photoURL = URL(fileURLWithPath: picturePath)
                                    let pairedVideoURL = URL(fileURLWithPath: videoPath)
                                    request.addResource(with: .photo, fileURL: photoURL, options: PHAssetResourceCreationOptions())
                                    request.addResource(with: .pairedVideo, fileURL: pairedVideoURL, options: PHAssetResourceCreationOptions())
                                } completionHandler: { success, error in
                                    DispatchQueue.main.async {
                                        complete(error == nil, error?.localizedDescription)
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
Editor is loading...