feat(vision): support extension-based formats for image/audio previews and optimize audio for STT

This commit is contained in:
AI Engineer 2026-05-17 12:51:25 +08:00
parent 5a06c07cb6
commit 46546825c3
2 changed files with 34 additions and 13 deletions

View File

@ -10,6 +10,7 @@ import (
// GenerateImagePreview 生成图片预览
// 支持缩放并裁剪以填充指定尺寸 (Fill 模式)
// 根据 outPath 后缀自动转换格式 (.webp, .jpg, .png 等)
func GenerateImagePreview(srcPath, outPath string, width, height int) error {
// 使用统一的 Load() 加载,内部已处理好 HEIC/sips/FFmpeg 的复杂格式兼容
c, err := Load(srcPath)
@ -18,8 +19,9 @@ func GenerateImagePreview(srcPath, outPath string, width, height int) error {
}
c.Fill(width, height)
if strings.HasSuffix(strings.ToLower(outPath), ".webp") {
// 借用 FFmpeg 将生成的画布转为高质量 WebP
ext := strings.ToLower(filepath.Ext(outPath))
if ext == ".webp" {
// 借用 FFmpeg 将生成的画布转为高质量 WebP (比标准库或第三方库压缩更好)
tmpFile := filepath.Join(os.TempDir(), fmt.Sprintf("preview_%d.png", os.Getpid()))
defer os.Remove(tmpFile)
if err := Save(c, tmpFile); err != nil {
@ -122,20 +124,30 @@ func GenerateVideoPreview(videoPath, outPath string, width, height int, frameInt
return nil
}
// GenerateAudioPreview 提取 3 分钟内的音频用于预览或语音转写
// 格式: Ogg Opus, 16kHz, 单声道, 12kbps (极致压缩,保留人声特征)
// GenerateAudioPreview 提取音频用于预览或语音转写
// 支持根据 outPath 后缀输出格式:
// - .ogg: 使用 libopus (16kHz, 单声道, 12kbps), 极致压缩且保留人声特征,适合转写
// - .wav: 标准 PCM (16kHz, 单声道), 无损但体积较大,部分转写引擎强制要求
// - 其他: 默认使用 libopus 转为 ogg
func GenerateAudioPreview(mediaPath, outPath string) error {
v, err := NewVideo()
if err != nil {
return err
}
// -vn: 禁用视频
// -c:a libopus: 高效音频压缩
// -ar 16000: 采样率 16k (转写标准)
// -ac 1: 单声道
// -b:a 12k: 极致压缩
// -t 180: 最长 180 秒 (足以获得内容概要)
cmd := exec.Command(v.FFmpegPath, "-i", mediaPath, "-vn", "-c:a", "libopus", "-ar", "16000", "-ac", "1", "-b:a", "12k", "-t", "180", "-y", outPath)
ext := strings.ToLower(filepath.Ext(outPath))
// 通用参数: 禁用视频, 16kHz 采样率 (STT 标准), 单声道
args := []string{"-i", mediaPath, "-vn", "-ar", "16000", "-ac", "1"}
if ext == ".wav" {
// WAV 格式,保留 PCM
args = append(args, "-y", outPath)
} else {
// 默认或 .ogg 使用 libopus 极致压缩,最长 180 秒
args = append(args, "-c:a", "libopus", "-b:a", "12k", "-t", "180", "-y", outPath)
}
cmd := exec.Command(v.FFmpegPath, args...)
return cmd.Run()
}

View File

@ -105,10 +105,19 @@ func TestPreviewer(t *testing.T) {
t.Run("GenerateAudioPreview", func(t *testing.T) {
err := GenerateAudioPreview(videoPath, oggPath)
if err != nil {
t.Errorf("GenerateAudioPreview failed: %v", err)
t.Errorf("GenerateAudioPreview (ogg) failed: %v", err)
}
if _, err := os.Stat(oggPath); os.IsNotExist(err) {
t.Error("Ogg output not created")
}
wavPath := filepath.Join(tmpDir, "preview.wav")
err = GenerateAudioPreview(videoPath, wavPath)
if err != nil {
t.Errorf("GenerateAudioPreview (wav) failed: %v", err)
}
if _, err := os.Stat(wavPath); os.IsNotExist(err) {
t.Error("Wav output not created")
}
})
}