0

I'm still quite new to coding and Swift. Forgive my doubtlessly clumsy code.

I am trying to run a process with ffprobe from the main resource bundle. Actually, I need to run it twice, each time with different arguments, once to get the duration of the audio file I'm inputting, and once to get the full output so that I can parse it for a tidbit of data that I can't isolate in a single argument the way I can the file duration. (I have to do it twice because the full output doesn't actually list the duration in seconds the way that I need.)

To avoid a lot of repetitive code, I'd like to do this in one function. This is what I have:

func ffprobeOperations() {
    var probeArguments = [String]()

    // get full ffprobe output to parse
    let probeArguments1 = [
        "-hide_banner",
        "\(inputFilePath)"]

    // get just file duration in seconds
    let probeArguments2 = [
        "-hide_banner",
        "-v",
        "0",
        "-show_entries",
        "format=duration",
        "-of",
        "compact=p=0:nk=1",
        "\(inputFilePath)"]

    var probePass: Int = 0
    if probePass == 1 {
        probeArguments = probeArguments2
    } else if probePass == 0 {
        probeArguments = probeArguments1
    }

    guard let launchPath = Bundle.main.path(forResource: "ffprobe", ofType: "") else { return }
    do {
        let probeTask: Process = Process()
        probeTask.launchPath = launchPath
        probeTask.arguments = probeArguments
        probeTask.standardInput = FileHandle.nullDevice
        let pipe = Pipe()
        probeTask.standardError = pipe
        probeTask.standardOutput = pipe
        let outHandle = pipe.fileHandleForReading
        outHandle.waitForDataInBackgroundAndNotify()

        var obs1 : NSObjectProtocol!
        obs1 = NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable,
                                                                                                    object: outHandle, queue: nil) {  notification -> Void in
                                                                                                        let data = outHandle.availableData
                                                                                                        if data.count > 0 {
                                                                                                            if let str = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
                                                                                                                self.ffmpegLogOutput.string += ("\(str)")
                                                                                                                let range = NSRange(location:self.ffmpegLogOutput.string.count,length:0)
                                                                                                                self.ffmpegLogOutput.scrollRangeToVisible(range)
                                                                                                            }
                                                                                                            outHandle.waitForDataInBackgroundAndNotify()
                                                                                                        } else {
                                                                                                            print("EOF on stderr from process")
                                                                                                            NotificationCenter.default.removeObserver(obs1!)
                                                                                                        }
        }
        var obs2 : NSObjectProtocol!
        obs2 = NotificationCenter.default.addObserver(forName: Process.didTerminateNotification,
                                                                                                    object: probeTask, queue: nil) { notification -> Void in
                                                                                                        print("terminated")
                                                                                                        NotificationCenter.default.removeObserver(obs2!)
        }
        probeTask.launch()
        probeTask.waitUntilExit()
        probePass += 1
    }
}

But no matter where I position probePass += 1 in the function, XCode still gives me a warning that the condition probePass == 1 will never be true and thus the pass with the second set of arguments will never be executed.

Where should I place probePass += 1, or is there a better way to do this altogether?

NCrusher
  • 93
  • 1
  • 10
  • well you have set `probePass` equal to `0` in the line above the comparison. What did you expect? – Leo Dabus Oct 29 '19 at 00:09
  • Well, I expected that the probePass == 0 condition would be met, and then probePass += 1 would happen at the end of the function and then the probePass == 1 condition would be met. Clearly it's not working that way and I can't figure out why. – NCrusher Oct 29 '19 at 00:15
  • *parse it for a tidbit of data that I can't isolate in a single argument* --> which tidbit? – Gyan Oct 29 '19 at 06:05
  • part of an error message that prints out at the very beginning, right after the banner (which I hide using -hide_banner.) There doesn't seem to be an argument for isolating it. If there were, my life would easier tonight. – NCrusher Oct 29 '19 at 06:13

1 Answers1

0

The scope of the variable probePass is only the function ffProbeOptions, since it's created in there. This means that once ffProbeOptions is finished running, the variable will be uninitialised. Try defining probePass outside of the function, so it has a larger scope. This way the variable will not be destroyed each time your function is finished, and it will keep track of its value (e.g. 0 or 1).

Like so:

var probePass = 0
func ffprobeOperations() {
    var probeArguments = [String]()

    // get full ffprobe output to parse
    let probeArguments1 = [
        "-hide_banner",
        "\(inputFilePath)"]

    // get just file duration in seconds
    let probeArguments2 = [
        "-hide_banner",
        "-v",
        "0",
        "-show_entries",
        "format=duration",
        "-of",
        "compact=p=0:nk=1",
        "\(inputFilePath)"]

    if probePass == 1 {
        probeArguments = probeArguments2
    } else if probePass == 0 {
        probeArguments = probeArguments1
    }

    guard let launchPath = Bundle.main.path(forResource: "ffprobe", ofType: "") else { return }
    do {
        let probeTask: Process = Process()
        probeTask.launchPath = launchPath
        probeTask.arguments = probeArguments
        probeTask.standardInput = FileHandle.nullDevice
        let pipe = Pipe()
        probeTask.standardError = pipe
        probeTask.standardOutput = pipe
        let outHandle = pipe.fileHandleForReading
        outHandle.waitForDataInBackgroundAndNotify()

        var obs1 : NSObjectProtocol!
        obs1 = NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable,
                                                                                                    object: outHandle, queue: nil) {  notification -> Void in
                                                                                                        let data = outHandle.availableData
                                                                                                        if data.count > 0 {
                                                                                                            if let str = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
                                                                                                                self.ffmpegLogOutput.string += ("\(str)")
                                                                                                                let range = NSRange(location:self.ffmpegLogOutput.string.count,length:0)
                                                                                                                self.ffmpegLogOutput.scrollRangeToVisible(range)
                                                                                                            }
                                                                                                            outHandle.waitForDataInBackgroundAndNotify()
                                                                                                        } else {
                                                                                                            print("EOF on stderr from process")
                                                                                                            NotificationCenter.default.removeObserver(obs1!)
                                                                                                        }
        }
        var obs2 : NSObjectProtocol!
        obs2 = NotificationCenter.default.addObserver(forName: Process.didTerminateNotification,
                                                                                                    object: probeTask, queue: nil) { notification -> Void in
                                                                                                        print("terminated")
                                                                                                        NotificationCenter.default.removeObserver(obs2!)
        }
        probeTask.launch()
        probeTask.waitUntilExit()
        probePass += 1
    }
}

Maybe a more elegant way to reach your goal is to define a list of probeArguments:

let probeArgumentList: [[String]] = [
    [   
        "-hide_banner",
        "\(inputFilePath)"
    ],
    [
        "-hide_banner",
        "-v",
        "0",
        "-show_entries",
        "format=duration",
        "-of",
        "compact=p=0:nk=1",
        "\(inputFilePath)"
    ]
]

and then loop over the list

for probeArguments in probeArgumentList {
    ## Perform operation
}
laurensvm
  • 143
  • 8