I've to admit that I'm quite inexperienced in Swift, but nonetheless I'm trying to build an OS X app to convert a video in a particular format and size with ffmpeg using Swift. My goal is to have the ffmpeg stdout in a separate window to show the progress to the user. Before posting here, I've read all that exist on the internet about the subject :-) and I've not found yet a solution to my problem of not having any output whatsoever in my textView but only in the Xcode console. I've found this post here: Real time NSTask output to NSTextView with Swift that seems very promising but is not working anyway. I've tried to use the command /bin/sh in the example with the provided arguments in my code and it works like a charm. Probably it's me and my inexperience, but I think that is something related to the way ffmpeg output his progress, that won't work. It seems that even removing the -v and -stat options the ffmpeg command still output to the Xcode console but not in my TextField. I hope that someone can shed a light in my swift coding darkness. Thanks in advance
STEFANO
UPDATE - SOLVED
I had an EUREKA moment and I've assigned the pipe to the standarderror and voilà it worked
import Cocoa
var videoLoadURL = ""
class ViewController: NSViewController {
@IBOutlet weak var filenameLabel: NSTextField!
@IBOutlet weak var progressText: NSTextField!
@IBOutlet weak var progressIndicator: NSProgressIndicator!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
progressIndicator.isHidden = true
}
@IBAction func loadVIdeo(_ sender: NSButton) {
let openPanel = NSOpenPanel()
openPanel.allowsMultipleSelection = false
openPanel.canChooseFiles = true
openPanel.runModal()
if let path = openPanel.url?.lastPathComponent {
filenameLabel.stringValue = path
}
if let path = openPanel.url?.absoluteURL {
videoLoadURL = path.absoluteString
}
}
@IBAction func convert(_ sender: NSButton) {
progressIndicator.isHidden = false
let savePanel = NSSavePanel()
var videoSaveURL: String = ""
savePanel.nameFieldStringValue = "Converted_\(filenameLabel.stringValue).mp4"
savePanel.runModal()
if let path = savePanel.url?.absoluteURL {
videoSaveURL = path.absoluteString
}
let startLaunch: CFTimeInterval = CACurrentMediaTime()
progressIndicator.startAnimation(self)
let task = Process()
task.launchPath = "/usr/local/bin/ffmpeg"
task.arguments = ["-i", "\(videoLoadURL)", "-y", "-g", "1", "-crf", "29","-b", "0", "-pix_fmt", "yuv420p", "-strict", "-2", "\(videoSaveURL)"]
let pipe = Pipe()
task.standardError = pipe
let outHandle = pipe.fileHandleForReading
outHandle.waitForDataInBackgroundAndNotify()
var observer1: NSObjectProtocol!
observer1 = NotificationCenter.default.addObserver(forName: NSNotification.Name.NSFileHandleDataAvailable, object: outHandle, queue: nil, using: { notification -> Void in
let data = outHandle.availableData
if data.count > 0 {
if let str = NSString(data: data, encoding: String.Encoding.utf8.rawValue) {
self.progressText.stringValue = str as String
}
outHandle.waitForDataInBackgroundAndNotify()
} else {
print("EOF on stdout from process")
NotificationCenter.default.removeObserver(observer1)
}
})
var observer2: NSObjectProtocol!
observer2 = NotificationCenter.default.addObserver(forName: Process.didTerminateNotification, object: task, queue: nil, using: { notification -> Void in
print("terminated")
NotificationCenter.default.removeObserver(observer2)
})
do {
try task.run()
}catch {
print("error")
}
task.waitUntilExit()
let elapsedTime: CFTimeInterval = CACurrentMediaTime() - startLaunch
NSSound.beep()
progressIndicator.stopAnimation(self)
progressIndicator.isHidden = true
if task.terminationStatus == 0 {
let alertOK = NSAlert()
alertOK.messageText = "Tutto bene"
alertOK.addButton(withTitle:"OK")
alertOK.addButton(withTitle: "Cancel")
alertOK.informativeText = "Conversione eseguita in \(elapsedTime) secondi"
alertOK.runModal()
} else {
let alertOK = NSAlert()
alertOK.messageText = "Errore"
alertOK.addButton(withTitle:"OK")
alertOK.informativeText = "La conversione è fallita"
alertOK.runModal()
}
}
}