import Cocoa import ZIPFoundation import AVFoundation @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate, NSUserInterfaceValidations { var exporter: AVAssetExportSession? @IBAction func showInfoAction(_ sender: Any) { NSApplication.shared.keyWindow?.contentViewController?.performSegue(withIdentifier: "showInfo", sender: self) } @IBAction func showTimelapseAction(_ sender: Any) { NSApplication.shared.keyWindow?.contentViewController?.performSegue(withIdentifier: "showTimelapse", sender: self) } func validateUserInterfaceItem(_ item: NSValidatedUserInterfaceItem) -> Bool { // Show timelapse and show info buttons if(item.tag == 67 || item.tag == 68) { return NSApplication.shared.keyWindow != nil } return true } @IBAction func exportAction(_ sender: Any) { let document = NSApplication.shared.keyWindow?.windowController?.document as? Document; let savePanel = NSSavePanel() savePanel.title = "Save" savePanel.allowedFileTypes = ["public.png"] savePanel.begin { (result) in if result.rawValue == NSApplication.ModalResponse.OK.rawValue { let canvas = document?.makeComposite() let canvasTiff = canvas?.tiffRepresentation let bitmapImage = NSBitmapImageRep(data: canvasTiff!) let canvasPng = bitmapImage!.representation(using: .png, properties: [:]) try? canvasPng?.write(to: savePanel.url!) } } } @IBAction func exportThumbnailAction(_ sender: Any) { let document = NSApplication.shared.keyWindow?.windowController?.document as? Document; let savePanel = NSSavePanel() savePanel.title = "Save Thumbnail" savePanel.allowedFileTypes = ["public.png"] savePanel.begin { (result) in if result.rawValue == NSApplication.ModalResponse.OK.rawValue { let canvas = document?.makeThumbnail() let canvasTiff = canvas?.tiffRepresentation let bitmapImage = NSBitmapImageRep(data: canvasTiff!) let canvasPng = bitmapImage!.representation(using: .png, properties: [:]) try? canvasPng?.write(to: savePanel.url!) } } } @IBAction func exportTimelapseAction(_ sender: Any) { let document = NSApplication.shared.keyWindow?.windowController?.document as? Document; let savePanel = NSSavePanel() savePanel.title = "Save Timelapse" savePanel.allowedFileTypes = ["public.mpeg-4"] savePanel.begin { (result) in if result.rawValue == NSApplication.ModalResponse.OK.rawValue { guard let archive = Archive(data: (document?.data)!, accessMode: Archive.AccessMode.read) else { return } let directory = NSTemporaryDirectory() let mixComposition = AVMutableComposition() var duration = CMTime.zero var instructions: [AVMutableVideoCompositionLayerInstruction] = [] for entry in archive.makeIterator() { if entry.path.contains(VideoPath) { let fileName = NSUUID().uuidString + ".mp4" // This returns a URL? even though it is an NSURL class method let fullURL = NSURL.fileURL(withPathComponents: [directory, fileName])! let _ = try? archive.extract(entry, to: fullURL) let asset = AVAsset(url: fullURL) guard let firstTrack = mixComposition.addMutableTrack( withMediaType: .video, preferredTrackID: Int32(kCMPersistentTrackID_Invalid)) else { return } // 3 do { try firstTrack.insertTimeRange( CMTimeRangeMake(start: .zero, duration: asset.duration), of: asset.tracks(withMediaType: .video)[0], at: duration) } catch { print("Failed to load first track") return } duration = CMTimeAdd(duration, asset.duration) let firstInstruction = AVMutableVideoCompositionLayerInstruction( assetTrack: firstTrack) firstInstruction.setOpacity(0.0, at: duration + asset.duration) instructions.append(firstInstruction) } } let mainInstruction = AVMutableVideoCompositionInstruction() mainInstruction.timeRange = CMTimeRangeMake( start: .zero, duration: duration) mainInstruction.layerInstructions = instructions let mainComposition = AVMutableVideoComposition() mainComposition.instructions = [mainInstruction] mainComposition.frameDuration = CMTimeMake(value: 1, timescale: 30) mainComposition.renderSize = CGSize( width: document!.info.videoFrameWidth, height: document!.info.videoFrameHeight) self.exporter = AVAssetExportSession( asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) self.exporter?.outputURL = savePanel.url! self.exporter?.outputFileType = AVFileType.mp4 self.exporter?.shouldOptimizeForNetworkUse = true self.exporter?.videoComposition = mainComposition self.exporter?.exportAsynchronously { dump(self.exporter?.error?.localizedDescription); } } } } }