94 lines
2.5 KiB
Go
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
|
|
}
|