Skip to content

Commit 930d808

Browse files
committed
Switching to ffmpeg command for videos
1 parent fe250a9 commit 930d808

File tree

1 file changed

+57
-56
lines changed

1 file changed

+57
-56
lines changed

main.go

+57-56
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import (
2828
"time"
2929
"unicode"
3030

31-
"github.com/asticode/go-astiav"
3231
"github.com/davidbyttow/govips/v2/vips"
3332
"github.com/gabriel-vasile/mimetype"
3433
"github.com/kenshaw/colors"
@@ -78,6 +77,7 @@ type Args struct {
7877
FontBg *colors.Color `ox:"font background color,default:white"`
7978
FontDPI int `ox:"font dpi,default:100,name:font-dpi"`
8079
FontMargin int `ox:"margin,default:5"`
80+
TimeCode time.Duration `ox:"time code,short:t"`
8181
VipsConcurrency int `ox:"vips concurrency,default:$NUMCPU"`
8282

8383
ctx context.Context
@@ -200,7 +200,7 @@ func (args *Args) renderFile(name string) (image.Image, string, error) {
200200
case strings.HasPrefix(typ, "font/"):
201201
g = args.renderFont
202202
case strings.HasPrefix(typ, "video/"):
203-
g, notStream = args.renderAstiav, true
203+
g, notStream = args.renderFfmpeg, true
204204
default:
205205
return nil, "", fmt.Errorf("mime type %q not supported", typ)
206206
}
@@ -295,40 +295,58 @@ func (args *Args) renderVips(r io.Reader, name string) (image.Image, error) {
295295
return args.vipsExport(v)
296296
}
297297

298-
// renderAstiv renders the image using the astiav (ffmpeg) library.
299-
func (args *Args) renderAstiav(_ io.Reader, name string) (image.Image, error) {
300-
astiavOnce.Do(astiavInit(args.logger, args.Verbose))
301-
in := astiav.AllocFormatContext()
302-
defer in.Free()
303-
if err := in.OpenInput(name, nil, nil); err != nil {
304-
return nil, err
305-
}
306-
defer in.CloseInput()
307-
if err := in.FindStreamInfo(nil); err != nil {
298+
// renderFfmpeg renders the image using the ffmpeg command.
299+
func (args *Args) renderFfmpeg(_ io.Reader, pathName string) (image.Image, error) {
300+
var err error
301+
ffmpegOnce.Do(func() {
302+
ffmpegPath, err = exec.LookPath("ffmpeg")
303+
})
304+
switch {
305+
case err != nil:
308306
return nil, err
307+
case ffmpegPath == "":
308+
return nil, errors.New("ffmpeg not in path")
309309
}
310-
for i, is := range in.Streams() {
311-
p := is.CodecParameters()
312-
typ := p.MediaType()
313-
if typ != astiav.MediaTypeVideo {
314-
continue
310+
// ffmpeg -loglevel panic -hide_banner -ss $t -i *.mkv -vframes 1 -q:v 1
311+
params := []string{
312+
`-hide_banner`,
313+
// `-ss`, ``,
314+
`-i`, pathName,
315+
`-vframes`, `1`,
316+
`-q:v`, `1`,
317+
`-f`, `apng`,
318+
`-`,
319+
}
320+
args.logger("executing: %s %s", ffmpegPath, strings.Join(params, " "))
321+
start := time.Now()
322+
cmd := exec.CommandContext(
323+
args.ctx,
324+
ffmpegPath,
325+
params...,
326+
)
327+
var buf, stderr bytes.Buffer
328+
cmd.Stdout, cmd.Stderr = &buf, &stderr
329+
if err := cmd.Run(); err != nil {
330+
errstr := stderr.String()
331+
if len(errstr) > 100 {
332+
errstr = errstr[:100]
315333
}
316-
args.logger("stream %d: %s", i, typ)
317-
rate := ox.NewRate(p.BitRate(), time.Second)
318-
args.logger(
319-
" bit rate: %s, pixel format: %v, time base: %v, duration: %v, frames: %v",
320-
rate, p.PixelFormat(), is.TimeBase(), is.Duration(), is.NbFrames(),
321-
)
334+
return nil, fmt.Errorf("%w: %s", err, errstr)
322335
}
323-
return nil, errors.New("oops!")
336+
args.logger("ffmpeg render: %v", time.Now().Sub(start))
337+
return png.Decode(&buf)
324338
}
325339

326340
// renderLibreOffice renders the image using the `soffice` command.
327341
func (args *Args) renderLibreOffice(_ io.Reader, pathName string) (image.Image, error) {
342+
var err error
328343
sofficeOnce.Do(func() {
329-
sofficePath, _ = exec.LookPath("soffice")
344+
sofficePath, err = exec.LookPath("soffice")
330345
})
331-
if sofficePath == "" {
346+
switch {
347+
case err != nil:
348+
return nil, err
349+
case sofficePath == "":
332350
return nil, errors.New("soffice not in path")
333351
}
334352
tmpDir, err := os.MkdirTemp("", name+".")
@@ -526,26 +544,6 @@ func vipsLevel(level vips.LogLevel) string {
526544
return fmt.Sprintf("(%d)", level)
527545
}
528546

529-
// astiavInit initializes the astiav package.
530-
func astiavInit(logger func(string, ...any), verbose bool) func() {
531-
return func() {
532-
level := astiav.LogLevelQuiet
533-
if verbose {
534-
level = astiav.LogLevelDebug
535-
}
536-
astiav.SetLogLevel(level)
537-
astiav.SetLogCallback(func(c astiav.Classer, l astiav.LogLevel, fmt, msg string) {
538-
var cs string
539-
if c != nil {
540-
if cl := c.Class(); cl != nil {
541-
cs = "class: " + cl.String()
542-
}
543-
}
544-
logger("astiav %d: %s%s", l, strings.TrimSpace(msg), cs)
545-
})
546-
}
547-
}
548-
549547
type files struct {
550548
args *Args
551549
}
@@ -698,6 +696,19 @@ func isLibreOffice(typ, ext string) bool {
698696
return false
699697
}
700698

699+
var urlRE = regexp.MustCompile(`(?i)^https?://`)
700+
701+
var (
702+
vipsOnce sync.Once
703+
sofficeOnce sync.Once
704+
ffmpegOnce sync.Once
705+
)
706+
707+
var (
708+
sofficePath string
709+
ffmpegPath string
710+
)
711+
701712
// extensions are the extensions to check for directories.
702713
var extensions = map[string]bool{
703714
"3g2": true,
@@ -764,13 +775,3 @@ var extensions = map[string]bool{
764775
"xlsx": true,
765776
"xpm": true,
766777
}
767-
768-
var urlRE = regexp.MustCompile(`(?i)^https?://`)
769-
770-
var (
771-
vipsOnce sync.Once
772-
astiavOnce sync.Once
773-
sofficeOnce sync.Once
774-
)
775-
776-
var sofficePath string

0 commit comments

Comments
 (0)