package utilities import ( "bufio" "fmt" "github.com/nethruster/go-fraction" "io" "strings" "time" ) // Timecodes Entries in milliseconds. First entry includes fallback duration, in time.Duration format type Timecodes []int64 func (tc Timecodes) FallbackDuration() time.Duration { return time.Duration(tc[0]) } func (tc Timecodes) PTS(n int) int64 { if n >= 0 && (n+1) < len(tc) { return tc[n+1] } return -1 } const timecodesv1Header = "# timecode format v1" func ParseTimecodesV1(reader io.Reader) (Timecodes, error) { var tc Timecodes scanner := bufio.NewScanner(reader) // Read header if !scanner.Scan() { return nil, io.ErrUnexpectedEOF } switch strings.TrimSpace(scanner.Text()) { case timecodesv1Header: default: return nil, fmt.Errorf("unexpected line: %s", strings.TrimSpace(scanner.Text())) } // Read assume line if !scanner.Scan() { return nil, io.ErrUnexpectedEOF } var fps float64 var from, to int64 _, err := fmt.Sscanf(strings.ToLower(strings.TrimSpace(scanner.Text())), "assume %f", &fps) if err != nil { return nil, err } assumed := FPSToRatio(fps) assumedFraction, err := fraction.New(assumed.Denominator, assumed.Numerator) if err != nil { return nil, err } if assumed.Denominator == 1000 { assumedFraction, err = fraction.FromFloat64(1 / fps) if err != nil { return nil, err } } running, err := fraction.New(0, 1000) if err != nil { return nil, err } var currentFrame int64 //fallback tc = append(tc, (int64(time.Second)*assumedFraction.Numerator())/assumedFraction.Denominator()) for scanner.Scan() { line := strings.ReplaceAll(strings.TrimSpace(scanner.Text()), " ", "") if line == "" || line[0] == '#' { continue } _, err := fmt.Sscanf(strings.ReplaceAll(strings.TrimSpace(scanner.Text()), " ", ""), "%d,%d,%f", &from, &to, &fps) if err != nil { return nil, err } ratio := FPSToRatio(fps) frac, err := fraction.New(ratio.Denominator, ratio.Numerator) if err != nil { return nil, err } if ratio.Denominator == 1000 { frac, err = fraction.FromFloat64(1 / fps) if err != nil { return nil, err } } for ; currentFrame < from; currentFrame++ { tc = append(tc, (running.Numerator()*1000)/running.Denominator()) running = running.Add(assumedFraction) } for ; currentFrame <= to; currentFrame++ { tc = append(tc, (running.Numerator()*1000)/running.Denominator()) running = running.Add(frac) } } return tc, nil }