视频保存

This commit is contained in:
duoaohui
2026-05-19 14:34:54 +08:00
parent 6d61bf2428
commit 624b38140d
3 changed files with 14 additions and 6 deletions

View File

@@ -255,7 +255,9 @@ func (s *MeetingRecordingService) StopRecording(ctx context.Context, userID int6
}
// 上传到 MinIO
objectKey := fmt.Sprintf("recordings/%s/%d-%s.mp4", strings.ToLower(code), rec.ID, rec.RemoteID)
// 后缀 .webm 与 media-server 实际产物保持一致
// VP8/Opus 不能装进 mp4media-server 直接出 webm后续如需 mp4 可异步转码)
objectKey := fmt.Sprintf("recordings/%s/%d-%s.webm", strings.ToLower(code), rec.ID, rec.RemoteID)
fileURL, sizeBytes, uploadErr := s.uploadRecording(ctx, outputPath, objectKey)
if uploadErr != nil {
logs.Error(ctx, funcName, "录制文件上传 MinIO 失败",

View File

@@ -154,7 +154,7 @@ func (c *OpenAICompatibleClient) Transcribe(ctx context.Context, cfg *dao.STTCon
// downloadAudio 从给定 URL 拉取音频内容,返回字节流 + 推断的文件名
//
// 文件名仅作为 multipart 的 filename 参数,主要决定 Content-Type 推断;
// 取 URL path 末段,无后缀则默认 recording.mp4
// 取 URL path 末段,无后缀则默认 recording.webm与 media-server 实际产物一致)
func (c *OpenAICompatibleClient) downloadAudio(ctx context.Context, fileURL string) ([]byte, string, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fileURL, nil)
if err != nil {

View File

@@ -3,7 +3,7 @@
*
* 录制管线:
* ┌─────────────┐ RTP/RTCP ┌──────────────┐ SDP+stdin ┌────────┐ fs ┌─────────┐
* │ Producer X │ ───────────> │ PlainTransport│ ───────────────> │ ffmpeg │ ──────> │ *.mp4
* │ Producer X │ ───────────> │ PlainTransport│ ───────────────> │ ffmpeg │ ──────> │ *.webm
* │ (audio/video)│ │ + Consumer │ (UDP loopback) │ (子进程)│ └─────────┘
* └─────────────┘ └──────────────┘ └────────┘
*
@@ -95,7 +95,7 @@ interface RecordingEntry {
ffmpeg: ChildProcess;
/** SDP 临时文件 */
sdpFilePath: string;
/** 输出 mp4 文件 */
/** 输出 webm 文件VP8 + Opus 原生容器,-c copy 零重编码) */
outputPath: string;
/** ffmpeg 退出 promisestop 时 await */
ffmpegExited: Promise<void>;
@@ -339,7 +339,10 @@ function buildSdp(tracks: RecordingTrack[], localIp: string): string {
* -use_wallclock_as_timestamps 1避免 RTP 时间戳跳变导致 mp4 时长异常
* -c copy不重编码直接复用 RTP 载荷
* - 优点CPU 占用极低
* - 缺点:浏览器侧 VP8 进 mp4 容器在某些播放器不支持;后续可改 libx264
* - 限制:输出容器必须能装 VP8/Opus。mp4 muxer 不接受 VP8ffmpeg 报
* "Could not find tag for codec vp8" 后 exit code=1。改用 webm 恶不必重编码
* VP8+Opus 是 webm 原生组合)。后续如需 mp4 可在 go-service 上传后异步 ffmpeg 转一次
* -f webm显式指定 webm muxer依赖后缀推断也可但显式更稳
* -y覆盖已存在文件防止 recordingId 碰撞)
*/
function buildFfmpegArgs(sdpFilePath: string, outputPath: string): string[] {
@@ -360,6 +363,8 @@ function buildFfmpegArgs(sdpFilePath: string, outputPath: string): string[] {
'0:v?',
'-c',
'copy',
'-f',
'webm',
'-y',
outputPath,
];
@@ -404,7 +409,8 @@ export async function startRecording(input: StartRecordingInput): Promise<StartR
const id = recordingId();
const startedAt = Date.now();
const localIp = '127.0.0.1';
const outputPath = join(OUTPUT_DIR, `${id}.mp4`);
// 输出后缀 .webmVP8 不能装进 mp4webm 是 VP8+Opus 的原生容器,-c copy 零重编码
const outputPath = join(OUTPUT_DIR, `${id}.webm`);
const sdpFilePath = join(OUTPUT_DIR, `${id}.sdp`);
// 已分配资源,用于失败时回滚