视频保存
This commit is contained in:
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/disintegration/imaging"
|
||||
"github.com/echochat/backend/config"
|
||||
"github.com/echochat/backend/pkg/logs"
|
||||
"github.com/echochat/backend/pkg/storage"
|
||||
"github.com/google/uuid"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"go.uber.org/zap"
|
||||
@@ -259,12 +260,5 @@ func (s *FileService) UploadVoice(ctx context.Context, fileHeader *multipart.Fil
|
||||
|
||||
// buildURL 根据 MinIO 配置构建文件访问 URL
|
||||
func (s *FileService) buildURL(objectName string) string {
|
||||
if base := strings.TrimRight(strings.TrimSpace(s.minioCfg.PublicBaseURL), "/"); base != "" {
|
||||
return fmt.Sprintf("%s/%s", base, objectName)
|
||||
}
|
||||
scheme := "http"
|
||||
if s.minioCfg.UseSSL {
|
||||
scheme = "https"
|
||||
}
|
||||
return fmt.Sprintf("%s://%s/%s/%s", scheme, s.minioCfg.Endpoint, s.minioCfg.Bucket, objectName)
|
||||
return storage.BuildObjectURL(s.minioCfg, objectName)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/echochat/backend/app/meeting/model"
|
||||
"github.com/echochat/backend/config"
|
||||
"github.com/echochat/backend/pkg/logs"
|
||||
"github.com/echochat/backend/pkg/storage"
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.uber.org/zap"
|
||||
@@ -503,14 +504,7 @@ func (s *MeetingRecordingService) uploadRecording(ctx context.Context, localPath
|
||||
}
|
||||
|
||||
func (s *MeetingRecordingService) buildRecordingURL(objectKey string) string {
|
||||
if base := strings.TrimRight(strings.TrimSpace(s.minioCfg.PublicBaseURL), "/"); base != "" {
|
||||
return fmt.Sprintf("%s/%s", base, objectKey)
|
||||
}
|
||||
scheme := "http"
|
||||
if s.minioCfg.UseSSL {
|
||||
scheme = "https"
|
||||
}
|
||||
return fmt.Sprintf("%s://%s/%s/%s", scheme, s.minioCfg.Endpoint, s.minioCfg.Bucket, objectKey)
|
||||
return storage.BuildObjectURL(s.minioCfg, objectKey)
|
||||
}
|
||||
|
||||
// prepareUploadArtifact 决定最终上传到 MinIO 的产物
|
||||
@@ -542,8 +536,30 @@ func (s *MeetingRecordingService) prepareUploadArtifact(ctx context.Context, web
|
||||
zap.Int64("recording_id", recordingID), zap.String("webm_path", webmPath), zap.Error(err))
|
||||
return webmPath, ".webm", "video/webm", ""
|
||||
}
|
||||
webmStat, webmStatErr := os.Stat(webmPath)
|
||||
mp4Stat, mp4StatErr := os.Stat(mp4Path)
|
||||
if webmStatErr == nil && mp4StatErr == nil {
|
||||
if mp4Stat.Size() >= webmStat.Size() {
|
||||
logs.Warn(ctx, funcName, "MP4 转码产物未变小,回退上传原始 webm",
|
||||
zap.Int64("recording_id", recordingID),
|
||||
zap.String("webm_path", webmPath),
|
||||
zap.Int64("webm_size_bytes", webmStat.Size()),
|
||||
zap.String("mp4_path", mp4Path),
|
||||
zap.Int64("mp4_size_bytes", mp4Stat.Size()))
|
||||
return webmPath, ".webm", "video/webm", mp4Path
|
||||
}
|
||||
logs.Info(ctx, funcName, "WebM→MP4 转码完成",
|
||||
zap.Int64("recording_id", recordingID),
|
||||
zap.String("mp4_path", mp4Path),
|
||||
zap.Int64("webm_size_bytes", webmStat.Size()),
|
||||
zap.Int64("mp4_size_bytes", mp4Stat.Size()))
|
||||
return mp4Path, ".mp4", "video/mp4", mp4Path
|
||||
}
|
||||
logs.Info(ctx, funcName, "WebM→MP4 转码完成",
|
||||
zap.Int64("recording_id", recordingID), zap.String("mp4_path", mp4Path))
|
||||
zap.Int64("recording_id", recordingID),
|
||||
zap.String("mp4_path", mp4Path),
|
||||
zap.NamedError("webm_stat_error", webmStatErr),
|
||||
zap.NamedError("mp4_stat_error", mp4StatErr))
|
||||
return mp4Path, ".mp4", "video/mp4", mp4Path
|
||||
}
|
||||
|
||||
@@ -555,8 +571,8 @@ func (s *MeetingRecordingService) prepareUploadArtifact(ctx context.Context, web
|
||||
// - 转码产物不存在或大小为 0
|
||||
//
|
||||
// 参数选型:
|
||||
// - -c:v libx264 -preset veryfast -crf 23:常见 1080p 短会议清晰度/CPU 折中
|
||||
// - -c:a aac -b:a 128k:覆盖绝大多数浏览器原生播放
|
||||
// - -c:v libx264 -preset medium -crf 30:压缩优先,避免 WebM 转 MP4 后体积变大
|
||||
// - -c:a aac -b:a 64k:会议语音场景的体积/清晰度折中
|
||||
// - -movflags +faststart:moov 前置,HTTP Range 即点即播
|
||||
// - -y:覆盖目标文件,避免重复 stop 时 EEXIST
|
||||
func (s *MeetingRecordingService) transcodeWebmToMp4(ctx context.Context, webmPath string) (string, error) {
|
||||
@@ -587,8 +603,8 @@ func (s *MeetingRecordingService) transcodeWebmToMp4(ctx context.Context, webmPa
|
||||
cmd := exec.CommandContext(tctx, ffmpegPath,
|
||||
"-y",
|
||||
"-i", webmPath,
|
||||
"-c:v", "libx264", "-preset", "veryfast", "-crf", "23",
|
||||
"-c:a", "aac", "-b:a", "128k",
|
||||
"-c:v", "libx264", "-preset", "medium", "-crf", "30",
|
||||
"-c:a", "aac", "-b:a", "64k",
|
||||
"-movflags", "+faststart",
|
||||
mp4Path,
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user