视频会议保存

This commit is contained in:
duoaohui
2026-05-18 19:42:55 +08:00
parent 315884685f
commit ac8ec4c903
41 changed files with 5069 additions and 39 deletions

View File

@@ -67,6 +67,10 @@ type CreateProducerReq struct {
TransportID string `json:"transportId"`
Kind string `json:"kind"` // "audio" | "video"
RtpParameters json.RawMessage `json:"rtpParameters"` // 直接转发给 Node由其做结构校验
// AppData 客户端自定义元信息,例如 {"screen": true} 表示该 Producer 是屏幕共享流
// Phase 3 屏幕共享引入:与摄像头/麦克风 video Producer 同走 sendTransport
// 通过 appData.screen 区分,便于服务端识别并触发 meeting.screen.* 广播
AppData json.RawMessage `json:"appData,omitempty"`
}
// CreateConsumerReq 创建 Consumer 请求
@@ -78,6 +82,38 @@ type CreateConsumerReq struct {
RtpCapabilities json.RawMessage `json:"rtpCapabilities"`
}
// ====== 会议录制 RPCPhase B 引入)======
// StartRecordingReq 启动录制请求
//
// ProducerIDs 指定要录的若干 Producer由业务层决定Phase B1 通常是 host 的
// audio + video 两个Phase B3 扩展为屏幕共享 + 多人音频后会更多)
type StartRecordingReq struct {
RouterID string `json:"routerId"`
RoomCode string `json:"roomCode"`
ProducerIDs []string `json:"producerIds"`
}
// StartRecordingResp media-server 启动成功后的响应
type StartRecordingResp struct {
RecordingID string `json:"recordingId"`
OutputPath string `json:"outputPath"` // media-server 本地 mp4 路径,用于停止后回拉上传
Tracks []struct {
ProducerID string `json:"producerId"`
Kind string `json:"kind"`
} `json:"tracks"`
}
// StopRecordingResp media-server 停止录制后返回的元数据
type StopRecordingResp struct {
RecordingID string `json:"recordingId"`
OutputPath string `json:"outputPath"`
SizeBytes int64 `json:"sizeBytes"`
DurationMS int64 `json:"durationMs"`
ExitCode *int `json:"exitCode,omitempty"` // ffmpeg 退出码nil = 进程未启动到退出阶段
FailureReason string `json:"failureReason,omitempty"` // 非空表示录制最终标记为 failed
}
// MediaOrchestrator 媒体服务器编排接口(设计 §6.6 NodeClient
// Task 7 (2026-04-21) 起默认实现为 HTTPMediaOrchestrator通过 X-Internal-Token
// 调用 Node media-server 的 /internal/v1/* REST API
@@ -122,6 +158,16 @@ type MediaOrchestrator interface {
ResumeConsumer(ctx context.Context, consumerID string) error
// CloseConsumer 关闭指定 Consumer幂等
CloseConsumer(ctx context.Context, consumerID string) error
// ====== 录制Phase B 引入)======
// StartRecording 在 media-server 上启动一份会议录制
// 失败语义404 RouterNotFound / 409 producerId 不可消费等返回 ErrMediaServerError
// 成功返回 recordingIdhex32+ media-server 本地 mp4 输出路径
StartRecording(ctx context.Context, req *StartRecordingReq) (*StartRecordingResp, error)
// StopRecording 停止录制
// 幂等媒体侧已结束404返回 ErrMediaResourceNotFound调用方一般视为"已停止"
StopRecording(ctx context.Context, recordingID string) (*StopRecordingResp, error)
}
// NoopMediaOrchestrator 本地调试占位实现Task 7 后默认不再使用)
@@ -205,3 +251,18 @@ func (n *NoopMediaOrchestrator) ResumeConsumer(_ context.Context, _ string) erro
func (n *NoopMediaOrchestrator) CloseConsumer(_ context.Context, _ string) error {
return nil
}
// StartRecording 占位:返回伪造 recordingId 与空 outputPath
func (n *NoopMediaOrchestrator) StartRecording(_ context.Context, req *StartRecordingReq) (*StartRecordingResp, error) {
return &StartRecordingResp{
RecordingID: "noop-recording-" + req.RoomCode,
OutputPath: "",
}, nil
}
// StopRecording 占位:返回零大小元数据
func (n *NoopMediaOrchestrator) StopRecording(_ context.Context, recordingID string) (*StopRecordingResp, error) {
return &StopRecordingResp{
RecordingID: recordingID,
}, nil
}