Search code examples
macosffmpegswiftuinsopenpanel

Unable to access user selected file via NSOpenPanel in FFMPEG process in macOS app


I am new to macOS development via SwiftUI. I'm trying to run a FFMPEG process after I selected a MP4 file via NSOpenPanel. However, the FFMPEG responded with:

file:///Users/MyUsername/Documents/Video.mp4: No such file or directory

Here is my simple codes:

import SwiftUI

struct ContentView: View {

    @State var selectedURL: URL?

    var body: some View {
        VStack {
            if selectedURL != nil {
                Text("Selected: \(selectedURL!.absoluteString)")
            } else {
                Text("Nothing selected")
            }
            Button(action: {
                let panel = NSOpenPanel()
                panel.allowedFileTypes = ["mp4"]
                panel.canChooseDirectories = false
                panel.canCreateDirectories = false
                panel.allowsMultipleSelection = false
                let result = panel.runModal()
                if result == .OK {
                    self.selectedURL = panel.url

                    let savePath = self.getDownloadDirectory().appendingPathComponent("video.webm")

                    self.convertVideo(inputFilePath: self.selectedURL!.absoluteString, outputFilePath: savePath.absoluteString, callback: {(s) in
                        // omit the callback at this moment
                    })
                }
            }) {
                Text("Select File")
            }
        }
        .frame(width: 640, height: 480)
    }

    func getDownloadDirectory() -> URL {
        let paths = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask)
        return paths[0]
    }

    func convertVideo(inputFilePath: String, outputFilePath: String,
                     callback: @escaping (Bool) -> Void) -> (Process, DispatchWorkItem)? {
        guard let launchPath = Bundle.main.path(forResource: "ffmpeg", ofType: "") else {
            return nil
        }
        let process = Process()
        let task = DispatchWorkItem {
            process.launchPath = launchPath
            process.arguments = [
                "-y",
                "-i", inputFilePath,
                "-vcodec", "vp8",
                "-acodec", "libvorbis",
                "-pix_fmt", "yuva420p",
                "-metadata:s:v:0",
                "alpha_mode=\"1\"",
                "-auto-alt-ref", "0",
                outputFilePath
            ]
            process.standardInput = FileHandle.nullDevice
            process.launch()
            process.terminationHandler = { process in
                callback(process.terminationStatus == 0)
            }
        }
        DispatchQueue.global(qos: .userInitiated).async(execute: task)

        return (process, task)
    }
}

What did I miss to allow FFMPEG process to access my selected file? Thanks!


Solution

  • Try to use .path instead of .absoluteString

    self.convertVideo(inputFilePath: self.selectedURL!.path, 
                      outputFilePath: savePath.absoluteString, callback: {(s) in