I'm trying to find the midpoint of a video, in other words divide the total length by 2
. ffmpeg
gives time in format HOURS:MM:SS.MICROSECONDS
, so ignoring microseconds, how could I get the half way point? I've tried dividing each number (hours/minutes/seconds
) by 2
but this fails in the case of 0:01:00
, for example.
This can be much simpler (and maybe even faster) if we use the parsing and formatting logic provided by the time
package.
func getDividedTime(times string, n int) (string, error) {
t, err := time.Parse("15:04:05.000000", times)
if err != nil {
return "", err
}
d := t.Sub(time.Time{}) / time.Duration(n)
return time.Time{}.Add(d).Format("15:04:05"), nil
}
Try it on the Go Playground. The weakness of this solution is that it "rejects" inputs having hours > 23
.
Essentially, you want to:
(and do some formatting for ffmpeg
). Also note that this answer can be generalized to dividing by n
, instead of just halving.
import (
"fmt"
"strconv"
"strings"
)
// ffmpeg uses the format HOURS:MM:SS.MICROSECONDS
func getDividedTime(time string, n int) string {
times := strings.Split(time, ":")
// get rid of microseconds
times[2] = strings.Split(times[2], ".")[0]
// conversions
minutesToSeconds := 60
hoursToSeconds := 60 * minutesToSeconds
// convert everything to seconds
seconds, _ := strconv.Atoi(times[2])
minutes, _ := strconv.Atoi(times[1])
hours, _ := strconv.Atoi(times[0])
secMinutes := minutes * minutesToSeconds
secHours := hours * hoursToSeconds
totalSeconds := seconds + secHours + secMinutes
totalSeconds /= n
// update time and grab seconds
newSeconds := totalSeconds % 60
totalSeconds /= 60
// update time and grab minutes
newMinutes := totalSeconds % 60
totalSeconds /= 60
newHours := totalSeconds % 3600
times[0] = strconv.Itoa(newHours)
times[1] = strconv.Itoa(newMinutes)
times[2] = strconv.Itoa(newSeconds)
// zero padding for seconds and minutes (not hours)
if newMinutes < 10 {
times[1] = "0" + times[1]
}
if newSeconds < 10 {
times[2] = "0" + times[2]
}
dividedTime := strings.Join(times[:], ":")
return dividedTime
}
func main() {
// create some tests
tables := []struct {
inputTime string
correctTime string
}{
{"0:11:28.956000", "0:05:44"},
{"1:00:00.111999", "0:30:00"},
{"1:15:00.111999", "0:37:30"},
{"1:45:00.111999", "0:52:30"},
{"0:59:00.000000", "0:29:30"},
{"2:04:22.123151", "1:02:11"},
{"0:00:00.000000", "0:00:00"},
{"0:00:03.000000", "0:00:01"},
{"0:00:04.000000", "0:00:02"},
{"0:20:00.000000", "0:10:00"},
{"0:02:00.000000", "0:01:00"},
{"99:00:00.000000", "49:30:00"},
}
// ensure output matches truth values
for _, table := range tables {
output := getDividedTime(table.inputTime, 2) // cut time in half
if output != table.correctTime {
fmt.Printf("failure: expected: %s, got: %s
", table.correctTime, output)
}
}
}
An even simpler solution that uses Duration
and not Time
. It allows parsing of hours > 24
. This is usually desired when dealing with timers or in your case, video duration!
func getDividedTime(st string, div int) string {
var h, m, s int
fmt.Sscanf(st, "%d:%d:%d", &h, &m, &s)
seconds := (h*60*60 + m*60 + s) / div
d := time.Duration(int64(seconds) * int64(1e9))
hh, mm, ss := int(d.Hours()), int(d.Minutes()), int(d.Seconds())
return fmt.Sprintf("%d:%02d:%02d", hh, mm-(hh*60), ss-(mm*60))
}
Try it in Go Playground! Feel free to check for invalid input errors returned by fmt.Sscanf()
!
Avoid parsing the stderr output of ffmpeg
to get duration (that's what I'm assuming you're doing): it's not meant for machine parsing and is prone to breakage. That's what ffprobe
is for and you won't have to perform additional processing to isolate the duration.
You can use ffprobe
to get duration in seconds which is much easier to divide:
ffprobe -v error -show_entries format=duration -of csv=p=0 input.mp4
Example result:
48.012000
Then use your favorite function or tool to divide by 2. (Example using bc
in a Bash script.)
If you require HOURS:MM:SS.MICROSECONDS
then add the -sexagesimal
option.