Files
media/service/video/transcode_service.go
T
2026-06-22 09:18:45 +08:00

94 lines
2.5 KiB
Go

package video
import (
"context"
"fmt"
"os"
"os/exec"
"path/filepath"
"time"
dto "media/model/dto/video"
"github.com/gogf/gf/v2/frame/g"
)
type transcodeService struct{}
var Transcode = new(transcodeService)
// TranscodeToMP4 将视频转码为 H.264 + AAC + MP4 + faststart
// inputPath: 输入文件路径
// outputDir: 输出目录(可选),为空则使用 resource/temp
// 返回输出文件完整路径、文件信息
func (s *transcodeService) TranscodeToMP4(ctx context.Context, inputPath string, outputDir string) (*dto.TranscodeRes, error) {
ffmpegPath, err := lookupFFmpegPath()
if err != nil {
return nil, fmt.Errorf("ffmpeg 未找到: %v", err)
}
// 输出目录
if outputDir == "" {
outputDir = g.Cfg().MustGet(ctx, "ffmpeg.temp_dir", "resource/temp").String()
if outputDir == "" {
outputDir = "resource/temp"
}
if !filepath.IsAbs(outputDir) {
absDir, _ := filepath.Abs(outputDir)
outputDir = absDir
}
}
os.MkdirAll(outputDir, 0755)
// 输出文件名:原文件名_转码_时间戳.mp4
base := filepath.Base(inputPath)
ext := filepath.Ext(base)
name := base[:len(base)-len(ext)]
outputName := fmt.Sprintf("%s_transcoded_%d.mp4", name, time.Now().UnixMilli())
outputPath := filepath.Join(outputDir, outputName)
g.Log().Infof(ctx, "[转码] 开始: %s → %s", inputPath, outputPath)
// FFmpeg 转码命令
// -c:v libx264 H.264 视频编码
// -preset fast 编码速度/质量平衡
// -crf 22 画质(0-51,越小越好,18-28常用)
// -c:a aac AAC 音频编码
// -b:a 128k 音频码率 128k
// -movflags +faststart MOOV 前置(关键!seed-lite 必需)
args := []string{
"-i", inputPath,
"-c:v", "libx264",
"-preset", "fast",
"-crf", "22",
"-c:a", "aac",
"-b:a", "128k",
"-movflags", "+faststart",
"-y", outputPath,
}
cmd := exec.CommandContext(ctx, ffmpegPath, args...)
output, err := cmd.CombinedOutput()
if err != nil {
return nil, fmt.Errorf("FFmpeg 转码失败: %v\n%s", err, string(output))
}
// 检查输出文件
stat, statErr := os.Stat(outputPath)
if statErr != nil {
return nil, fmt.Errorf("转码输出文件不存在: %v", statErr)
}
// 获取时长
durationStr := getVideoDurationStr(ctx, outputPath)
g.Log().Infof(ctx, "[转码] 完成: %s, 大小=%d, 时长=%s", outputPath, stat.Size(), durationStr)
return &dto.TranscodeRes{
OutputPath: outputPath,
FileSize: stat.Size(),
FileName: outputName,
DurationStr: durationStr,
}, nil
}