Reading ffmpeg output for progress.
This commit is contained in:
parent
545a474559
commit
e9d896427c
@ -4,7 +4,10 @@ import (
|
|||||||
// stdlib
|
// stdlib
|
||||||
"bufio"
|
"bufio"
|
||||||
"log"
|
"log"
|
||||||
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -17,12 +20,19 @@ type Task struct {
|
|||||||
// Filed in conversion.
|
// Filed in conversion.
|
||||||
totalFrames int
|
totalFrames int
|
||||||
|
|
||||||
// State information.
|
// Initial calculation state information.
|
||||||
|
previousOutput string
|
||||||
gotInput bool
|
gotInput bool
|
||||||
gotDuration bool
|
gotDuration bool
|
||||||
|
gotTimeOrFPSParsingError bool
|
||||||
|
|
||||||
|
// After totalFrames will be filled we will use these variables
|
||||||
|
// to work with output.
|
||||||
|
gotFrame bool
|
||||||
|
|
||||||
// File info.
|
// File info.
|
||||||
duration string
|
duration string
|
||||||
|
fps string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert launches conversion procedure. Should be launched in separate
|
// Convert launches conversion procedure. Should be launched in separate
|
||||||
@ -85,7 +95,11 @@ func (t *Task) Convert() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
stderrScanner.Scan()
|
proceeding := stderrScanner.Scan()
|
||||||
|
if proceeding == false {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
//log.Println(stderrScanner.Text())
|
||||||
t.workWithOutput(stderrScanner.Text())
|
t.workWithOutput(stderrScanner.Text())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,8 +108,110 @@ func (t *Task) Convert() {
|
|||||||
|
|
||||||
// Printing progress for this task.
|
// Printing progress for this task.
|
||||||
func (t *Task) workWithOutput(output string) {
|
func (t *Task) workWithOutput(output string) {
|
||||||
if output == "" {
|
// Do nothing if we have empty output string or if we're not ready.
|
||||||
|
if output == "" || t.gotTimeOrFPSParsingError {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we have totalFrames defined, which is the very final state
|
||||||
|
// for calculations below, we should work with output in different
|
||||||
|
// manner :)
|
||||||
|
if t.totalFrames != 0 {
|
||||||
|
// We should look for current frame count.
|
||||||
|
// If we have "frame=" there - then output for next function
|
||||||
|
// call will be current frame count.
|
||||||
|
if strings.Contains(output, "frame=") {
|
||||||
|
t.gotFrame = true
|
||||||
|
// If we have only "frame" here - then actual current frame
|
||||||
|
// will be in next output. Otherwise we should fix output
|
||||||
|
// to contain only actual current frame.
|
||||||
|
// We have ASCII here, not runes, len() is fine.
|
||||||
|
if len(output) == 6 {
|
||||||
|
// Current frame will be in next output.
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
output = strings.Split(output, "frame=")[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ... which we should properly use.
|
||||||
|
if t.gotFrame {
|
||||||
|
currentFrame, err := strconv.Atoi(output)
|
||||||
|
if err != nil {
|
||||||
|
log.Println("Failed to convert current frame value to int ("+output+"):", err.Error())
|
||||||
|
t.gotFrame = false
|
||||||
|
return
|
||||||
|
}
|
||||||
|
percentage := currentFrame / int(t.totalFrames/100)
|
||||||
|
// What if... we mistaken with totalFrames prediction?
|
||||||
|
if percentage > 100 {
|
||||||
|
percentage = 100
|
||||||
|
}
|
||||||
|
os.Stdout.Write([]byte("\rConverting " + t.InputFile + ": " + strconv.Itoa(percentage) + "% done (" + output + " frame of " + strconv.Itoa(t.totalFrames) + ")"))
|
||||||
|
|
||||||
|
// ... and reset it's state so next "frame=" will be the
|
||||||
|
// next stop.
|
||||||
|
t.gotFrame = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// We got input keyword. Next function runs will look for duration.
|
||||||
|
if output == "Input" {
|
||||||
|
t.gotInput = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.gotInput && output == "Duration:" {
|
||||||
|
t.gotDuration = true
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.gotDuration && t.duration == "" {
|
||||||
|
t.duration = output
|
||||||
|
log.Println("File duration:", t.duration)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if t.duration != "" && output == "fps," {
|
||||||
|
t.fps = t.previousOutput
|
||||||
|
log.Println("Got FPS value:", t.fps)
|
||||||
|
|
||||||
|
// Calculate total frames approximately, because even if ffmpeg
|
||||||
|
// writes that there is 29.97 fps, it actually might be something
|
||||||
|
// like 29.971872638217638216.
|
||||||
|
// BTW, this is a duration, not a time, and to avoid all
|
||||||
|
// kind of type pr0n we will just fix gathered duration to
|
||||||
|
// be parsable by time.ParseDuration()
|
||||||
|
fileDuration := strings.Replace(t.duration, ":", "h", 1)
|
||||||
|
fileDuration = strings.Replace(fileDuration, ":", "m", 1)
|
||||||
|
fileDuration = strings.Replace(fileDuration, ".", "s", 1)
|
||||||
|
fileDuration = strings.Replace(fileDuration, ",", "", 1)
|
||||||
|
fileDuration += "ms"
|
||||||
|
totalTime, err := time.ParseDuration(fileDuration)
|
||||||
|
log.Println("Got file duration parsed:", totalTime)
|
||||||
|
seconds := totalTime.Seconds()
|
||||||
|
if err != nil {
|
||||||
|
log.Println("ERROR: failed to parse video file total time value. No progress output will be produced!")
|
||||||
|
t.gotTimeOrFPSParsingError = true
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Println("Got file duration in seconds:", seconds)
|
||||||
|
fps, err1 := strconv.ParseFloat(t.fps, 64)
|
||||||
|
if err1 != nil {
|
||||||
|
log.Println("ERROR: failed to parse frames per second value: '" + t.fps + "', no progress output will be produced!")
|
||||||
|
t.gotTimeOrFPSParsingError = true
|
||||||
|
}
|
||||||
|
// We don't mind to loose 1 or 2 fps from total fps counter,
|
||||||
|
// yea? :)
|
||||||
|
t.totalFrames = int(float64(seconds) * fps)
|
||||||
|
log.Println("Total frames calculated:", t.totalFrames)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save previous output for good unless we have everything we need
|
||||||
|
// to print progress.
|
||||||
|
if t.totalFrames == 0 {
|
||||||
|
t.previousOutput = output
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user