vision/video_ffmpeg.go

92 lines
2.6 KiB
Go
Raw Permalink Normal View History

package vision
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"runtime"
"apigo.cc/go/file"
)
// Video 代表一个视频操作封装
type Video struct {
FFmpegPath string
}
// NewVideo 创建一个视频处理器,自动查找或下载 ffmpeg
func NewVideo() (*Video, error) {
p, err := EnsureFFmpeg()
if err != nil {
return nil, err
}
return &Video{FFmpegPath: p}, nil
}
// ExtractFrame 从视频中提取指定时间的帧
func (v *Video) ExtractFrame(videoPath string, offsetSeconds float64) (*Canvas, error) {
tmpFile := filepath.Join(os.TempDir(), fmt.Sprintf("frame_%d.png", os.Getpid()))
defer os.Remove(tmpFile)
cmd := exec.Command(v.FFmpegPath, "-ss", fmt.Sprintf("%f", offsetSeconds), "-i", videoPath, "-frames:v", "1", tmpFile)
if err := cmd.Run(); err != nil {
return nil, fmt.Errorf("ffmpeg extract failed: %w", err)
}
return Load(tmpFile)
}
// CreateVideoFromImages 从一系列图片创建视频
func (v *Video) CreateVideoFromImages(imagePattern string, frameRate int, outPath string) error {
cmd := exec.Command(v.FFmpegPath, "-framerate", fmt.Sprintf("%d", frameRate), "-i", imagePattern, "-c:v", "libx264", "-pix_fmt", "yuv420p", outPath)
return cmd.Run()
}
// EnsureFFmpeg 确保 ffmpeg 命令可用
func EnsureFFmpeg() (string, error) {
// 1. 检查 PATH
if p, err := exec.LookPath("ffmpeg"); err == nil {
return p, nil
}
// 2. 检查本地目录
localDir := filepath.Join(os.Getenv("HOME"), ".vision", "bin")
localFF := filepath.Join(localDir, "ffmpeg")
if runtime.GOOS == "windows" {
localFF += ".exe"
}
if file.Exists(localFF) {
return localFF, nil
}
// 3. 自动下载
return DownloadFFmpeg(localDir)
}
// DownloadFFmpeg 下载对应系统的 FFmpeg 二进制文件
func DownloadFFmpeg(targetDir string) (string, error) {
if err := os.MkdirAll(targetDir, 0755); err != nil {
return "", err
}
var url string
switch runtime.GOOS {
case "darwin":
// 使用针对 macOS 的精简版二进制 (示例 URL实际应指向可靠镜像)
url = "https://evermeet.cx/ffmpeg/get/zip"
case "linux":
url = "https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz"
case "windows":
url = "https://www.gyan.dev/ffmpeg/builds/ffmpeg-release-essentials.zip"
default:
return "", fmt.Errorf("unsupported OS: %s", runtime.GOOS)
}
// 注意:实际下载逻辑需要处理解压、权限等。
// 为了精简,这里我们只提供语义。在真实场景中可以使用 go/http 下载并解压。
fmt.Printf("FFmpeg not found. Please install it or download from: %s\n", url)
return "", fmt.Errorf("ffmpeg not found, please install manually or check %s", url)
}