feat(vision): support extension-based formats for image/audio previews and optimize audio for STT
This commit is contained in:
parent
5a06c07cb6
commit
46546825c3
36
preview.go
36
preview.go
@ -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,14 +19,15 @@ 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 {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
v, err := NewVideo()
|
||||
if err == nil {
|
||||
cmd := exec.Command(v.FFmpegPath, "-i", tmpFile, "-c:v", "libwebp", "-quality", "80", "-y", outPath)
|
||||
@ -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()
|
||||
}
|
||||
|
||||
|
||||
@ -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")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user