Skip to content

Commit 4f6dca3

Browse files
committed
Add better time stamp guessing for ffmpeg
1 parent 930d808 commit 4f6dca3

File tree

1 file changed

+61
-2
lines changed

1 file changed

+61
-2
lines changed

main.go

+61-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"path/filepath"
2424
"regexp"
2525
"sort"
26+
"strconv"
2627
"strings"
2728
"sync"
2829
"time"
@@ -77,7 +78,7 @@ type Args struct {
7778
FontBg *colors.Color `ox:"font background color,default:white"`
7879
FontDPI int `ox:"font dpi,default:100,name:font-dpi"`
7980
FontMargin int `ox:"margin,default:5"`
80-
TimeCode time.Duration `ox:"time code,short:t"`
81+
TimeCode time.Duration `ox:"video time code,short:t"`
8182
VipsConcurrency int `ox:"vips concurrency,default:$NUMCPU"`
8283

8384
ctx context.Context
@@ -299,6 +300,7 @@ func (args *Args) renderVips(r io.Reader, name string) (image.Image, error) {
299300
func (args *Args) renderFfmpeg(_ io.Reader, pathName string) (image.Image, error) {
300301
var err error
301302
ffmpegOnce.Do(func() {
303+
ffprobePath, _ = exec.LookPath("ffprobe")
302304
ffmpegPath, err = exec.LookPath("ffmpeg")
303305
})
304306
switch {
@@ -310,7 +312,7 @@ func (args *Args) renderFfmpeg(_ io.Reader, pathName string) (image.Image, error
310312
// ffmpeg -loglevel panic -hide_banner -ss $t -i *.mkv -vframes 1 -q:v 1
311313
params := []string{
312314
`-hide_banner`,
313-
// `-ss`, ``,
315+
`-ss`, args.ffprobeTimecode(pathName),
314316
`-i`, pathName,
315317
`-vframes`, `1`,
316318
`-q:v`, `1`,
@@ -337,6 +339,62 @@ func (args *Args) renderFfmpeg(_ io.Reader, pathName string) (image.Image, error
337339
return png.Decode(&buf)
338340
}
339341

342+
func (args *Args) ffprobeTimecode(pathName string) string {
343+
switch {
344+
case ffprobePath == "":
345+
return "00:00"
346+
case args.TimeCode != 0:
347+
return formatTimecode(args.TimeCode)
348+
}
349+
params := []string{
350+
`-loglevel`, `quiet`,
351+
`-show_format`,
352+
pathName,
353+
}
354+
args.logger("ffprobe: executing %s %s", ffprobePath, strings.Join(params, " "))
355+
cmd := exec.CommandContext(args.ctx, ffprobePath, params...)
356+
buf, err := cmd.CombinedOutput()
357+
if err != nil {
358+
return "00:00"
359+
}
360+
m := durationRE.FindStringSubmatch(string(buf))
361+
if m == nil {
362+
return "00:00"
363+
}
364+
f, err := strconv.ParseFloat(m[1], 64)
365+
if err != nil {
366+
return "00:00"
367+
}
368+
switch dur := time.Duration(f * float64(time.Second)); {
369+
case dur >= 1*time.Hour:
370+
return "10:00"
371+
case dur >= 30*time.Minute:
372+
return "05:00"
373+
case dur >= 15*time.Minute:
374+
return "03:00"
375+
case dur >= 5*time.Minute:
376+
return "02:00"
377+
case dur > 1*time.Minute:
378+
return "00:30"
379+
case dur > 30*time.Second:
380+
return "00:10"
381+
case dur > 5*time.Second:
382+
return "00:02"
383+
}
384+
return "00:00"
385+
}
386+
387+
var durationRE = regexp.MustCompile(`(?m)^duration=(.*)$`)
388+
389+
func formatTimecode(d time.Duration) string {
390+
if d == 0 {
391+
return "00:00"
392+
}
393+
secs := int64(float64(d) / float64(time.Minute))
394+
rem := int64((float64(d) / float64(time.Minute)) * float64(time.Minute))
395+
return fmt.Sprintf("%02d:%02d", secs, rem)
396+
}
397+
340398
// renderLibreOffice renders the image using the `soffice` command.
341399
func (args *Args) renderLibreOffice(_ io.Reader, pathName string) (image.Image, error) {
342400
var err error
@@ -706,6 +764,7 @@ var (
706764

707765
var (
708766
sofficePath string
767+
ffprobePath string
709768
ffmpegPath string
710769
)
711770

0 commit comments

Comments
 (0)