diff --git a/pkg/utils/vtt.go b/pkg/utils/vtt.go index af07a3801..7af094c49 100644 --- a/pkg/utils/vtt.go +++ b/pkg/utils/vtt.go @@ -1,41 +1,37 @@ package utils import ( - "strconv" - "time" + "fmt" + "math" ) -// GetVTTTime returns a timestamp appropriate for VTT files (hh:mm:ss.mmm) -func GetVTTTime(totalSeconds float64) (s string) { - totalSecondsString := strconv.FormatFloat(totalSeconds, 'f', -1, 64) - secondsDuration, _ := time.ParseDuration(totalSecondsString + "s") - - // Hours - var hours = int(secondsDuration / time.Hour) - var n = secondsDuration % time.Hour - if hours < 10 { - s += "0" +// from stdlib's time.go +func norm(hi, lo, base int) (nhi, nlo int) { + if lo < 0 { + n := (-lo-1)/base + 1 + hi -= n + lo += n * base } - s += strconv.Itoa(hours) + ":" - - // Minutes - var minutes = int(n / time.Minute) - n = secondsDuration % time.Minute - if minutes < 10 { - s += "0" + if lo >= base { + n := lo / base + hi += n + lo -= n * base } - s += strconv.Itoa(minutes) + ":" - - // Seconds - var seconds = int(n / time.Second) - n = secondsDuration % time.Second - if seconds < 10 { - s += "0" - } - s += strconv.Itoa(seconds) - - // videojs requires milliseconds - s += ".000" - - return + return hi, lo +} + +// GetVTTTime returns a timestamp appropriate for VTT files (hh:mm:ss.mmm) +func GetVTTTime(fracSeconds float64) string { + if fracSeconds < 0 || math.IsNaN(fracSeconds) || math.IsInf(fracSeconds, 0) { + return "00:00:00.000" + } + + var msec, sec, min, hour int + msec = int(fracSeconds * 1000) + sec, msec = norm(sec, msec, 1000) + min, sec = norm(min, sec, 60) + hour, min = norm(hour, min, 60) + + return fmt.Sprintf("%02d:%02d:%02d.%03d", hour, min, sec, msec) + } diff --git a/pkg/utils/vtt_test.go b/pkg/utils/vtt_test.go new file mode 100644 index 000000000..091cdd3be --- /dev/null +++ b/pkg/utils/vtt_test.go @@ -0,0 +1,42 @@ +package utils + +import ( + "math" + "testing" +) + +func TestZeroTimestamp(t *testing.T) { + if want, got := "00:00:00.000", GetVTTTime(0); want != got { + t.Errorf("TestZeroTimestamp: GetVTTTime(0) = %v; want %v", got, want) + } +} + +func TestValidTimestamp(t *testing.T) { + s := 0.1 + if want, got := "00:00:00.100", GetVTTTime(s); want != got { + t.Errorf("TestValidTimestamp: GetVTTTime(%v) = %v; want %v", s, got, want) + } + s = ((24+1)*60+1)*60 + 1 + 0.1 + if want, got := "25:01:01.100", GetVTTTime(s); want != got { + t.Errorf("TestValidTimestamp: GetVTTTime(%v) = %v; want %v", s, got, want) + } +} + +// Negative timestamps are not defined by WebVTT. +func TestNegativeTimestamp(t *testing.T) { + if want, got := "00:00:00.000", GetVTTTime(-1); want != got { + t.Errorf("TestNegativeTimestamp: GetVTTTime(-1) = %v; want %v", got, want) + } +} + +func TestInvalidTimestamp(t *testing.T) { + if want, got := "00:00:00.000", GetVTTTime(math.NaN()); want != got { + t.Errorf("TestInvalidTimestamp: GetVTTTime(NaN) = %v; want %v", got, want) + } + if want, got := "00:00:00.000", GetVTTTime(math.Inf(1)); want != got { + t.Errorf("TestInvalidTimestamp: GetVTTTime(Inf) = %v; want %v", got, want) + } + if want, got := "00:00:00.000", GetVTTTime(math.Inf(-1)); want != got { + t.Errorf("TestInvalidTimestamp: GetVTTTime(-Inf) = %v; want %v", got, want) + } +}