131 lines
2.6 KiB
Go
131 lines
2.6 KiB
Go
|
// +build !sharedffmpeg
|
||
|
|
||
|
package thumbnail
|
||
|
|
||
|
import (
|
||
|
"encoding/json"
|
||
|
"fmt"
|
||
|
"os/exec"
|
||
|
"strconv"
|
||
|
)
|
||
|
|
||
|
type ExtDependencySupport struct {
|
||
|
FFMpeg bool
|
||
|
MediaInfo bool
|
||
|
}
|
||
|
|
||
|
func Parse(filename string, time uint64) (*string, *VidInfo, error) {
|
||
|
// check if the extern dependencies are available
|
||
|
mExtDepsAvailable := checkExtDependencySupport()
|
||
|
fmt.Printf("FFMPEG support: %t\n", mExtDepsAvailable.FFMpeg)
|
||
|
fmt.Printf("MediaInfo support: %t\n", mExtDepsAvailable.MediaInfo)
|
||
|
|
||
|
var pic *string = nil
|
||
|
var info *VidInfo = nil
|
||
|
|
||
|
if mExtDepsAvailable.FFMpeg {
|
||
|
p, err := parseFFmpegPic(filename, time)
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
pic = EncodeBase64(p, "image/jpeg")
|
||
|
}
|
||
|
|
||
|
if mExtDepsAvailable.MediaInfo {
|
||
|
i, err := getVideoAttributes(filename)
|
||
|
if err != nil {
|
||
|
return nil, nil, err
|
||
|
}
|
||
|
info = i
|
||
|
}
|
||
|
return pic, info, nil
|
||
|
}
|
||
|
|
||
|
// check if a specific system command is available
|
||
|
func commandExists(cmd string) bool {
|
||
|
_, err := exec.LookPath(cmd)
|
||
|
return err == nil
|
||
|
}
|
||
|
|
||
|
// ext dependency support check
|
||
|
func checkExtDependencySupport() *ExtDependencySupport {
|
||
|
var extDepsAvailable ExtDependencySupport
|
||
|
|
||
|
extDepsAvailable.FFMpeg = commandExists("ffmpeg")
|
||
|
extDepsAvailable.MediaInfo = commandExists("mediainfo")
|
||
|
|
||
|
return &extDepsAvailable
|
||
|
}
|
||
|
|
||
|
func secToString(time uint64) string {
|
||
|
return fmt.Sprintf("%02d:%02d:%02d", time/3600, (time/60)%60, time%60)
|
||
|
}
|
||
|
|
||
|
// parse the thumbail picture from video file
|
||
|
func parseFFmpegPic(path string, time uint64) (*[]byte, error) {
|
||
|
app := "ffmpeg"
|
||
|
|
||
|
cmd := exec.Command(app,
|
||
|
"-hide_banner",
|
||
|
"-loglevel", "panic",
|
||
|
"-ss", secToString(time),
|
||
|
"-i", path,
|
||
|
"-vframes", "1",
|
||
|
"-q:v", "2",
|
||
|
"-f", "singlejpeg",
|
||
|
"pipe:1")
|
||
|
|
||
|
stdout, err := cmd.Output()
|
||
|
|
||
|
if err != nil {
|
||
|
fmt.Println(err.Error())
|
||
|
fmt.Println(string(err.(*exec.ExitError).Stderr))
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
return &stdout, nil
|
||
|
}
|
||
|
|
||
|
func getVideoAttributes(path string) (*VidInfo, error) {
|
||
|
app := "mediainfo"
|
||
|
|
||
|
arg0 := path
|
||
|
arg1 := "--Output=JSON"
|
||
|
|
||
|
cmd := exec.Command(app, arg1, "-f", arg0)
|
||
|
stdout, err := cmd.Output()
|
||
|
|
||
|
var t struct {
|
||
|
Media struct {
|
||
|
Track []struct {
|
||
|
Duration string
|
||
|
FileSize string
|
||
|
Width string
|
||
|
Height string
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
err = json.Unmarshal(stdout, &t)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
duration, err := strconv.ParseFloat(t.Media.Track[0].Duration, 32)
|
||
|
filesize, err := strconv.Atoi(t.Media.Track[0].FileSize)
|
||
|
width, err := strconv.Atoi(t.Media.Track[1].Width)
|
||
|
height, err := strconv.Atoi(t.Media.Track[1].Height)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
|
||
|
ret := VidInfo{
|
||
|
Length: uint64(duration),
|
||
|
Size: int64(filesize),
|
||
|
Width: uint32(width),
|
||
|
Height: uint32(height),
|
||
|
FrameRate: 0,
|
||
|
}
|
||
|
|
||
|
return &ret, nil
|
||
|
}
|