微信小程序\推拉流

This commit is contained in:
2025-12-15 13:21:08 +08:00
parent 3994d40f5a
commit 2fca6e5c43
3 changed files with 180 additions and 59 deletions

View File

@@ -25,18 +25,19 @@ const (
// Participant 房间参与者
type Participant struct {
UserID string `json:"user_id"`
Type ParticipantType `json:"type"`
JoinedAt time.Time `json:"joined_at"`
HasAudio bool `json:"has_audio"`
HasVideo bool `json:"has_video"`
UserID string `json:"user_id"`
Type ParticipantType `json:"type"`
JoinedAt time.Time `json:"joined_at"`
HasAudio bool `json:"has_audio"`
HasVideo bool `json:"has_video"`
// WebRTC 相关
PeerConnection interface{} `json:"-"` // *webrtc.PeerConnection
// RTMP 相关
PushURL string `json:"push_url,omitempty"` // 推流地址
PullURL string `json:"pull_url,omitempty"` // 拉流地址
PullURL string `json:"pull_url,omitempty"` // 拉流地址 (RTMP)
FLVURL string `json:"flv_url,omitempty"` // HTTP-FLV 拉流地址 (给 Web 端用)
StreamID string `json:"stream_id,omitempty"` // 流ID
}
@@ -46,24 +47,24 @@ type Room struct {
CreatedAt time.Time `json:"created_at"`
MaxSize int `json:"max_size"`
Participants map[string]*Participant `json:"participants"`
// 房间状态
CallType string `json:"call_type"` // audio/video
IsGroupCall bool `json:"is_group_call"`
mu sync.RWMutex
closeChan chan struct{}
closed bool
CallType string `json:"call_type"` // audio/video
IsGroupCall bool `json:"is_group_call"`
mu sync.RWMutex
closeChan chan struct{}
closed bool
}
// RoomInfo 房间信息(用于API返回)
type RoomInfo struct {
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
ParticipantCount int `json:"participant_count"`
CallType string `json:"call_type"`
IsGroupCall bool `json:"is_group_call"`
Participants []ParticipantInfo `json:"participants"`
ID string `json:"id"`
CreatedAt time.Time `json:"created_at"`
ParticipantCount int `json:"participant_count"`
CallType string `json:"call_type"`
IsGroupCall bool `json:"is_group_call"`
Participants []ParticipantInfo `json:"participants"`
}
// ParticipantInfo 参与者信息(用于API返回)
@@ -99,30 +100,30 @@ func (r *Room) SetCallType(callType string, isGroup bool) {
func (r *Room) AddParticipant(userID string, pType ParticipantType) (*Participant, error) {
r.mu.Lock()
defer r.mu.Unlock()
if r.closed {
return nil, ErrRoomClosed
}
// 检查是否已存在
if p, exists := r.Participants[userID]; exists {
return p, nil
}
// 检查房间容量
if len(r.Participants) >= r.MaxSize {
return nil, ErrRoomFull
}
participant := &Participant{
UserID: userID,
Type: pType,
JoinedAt: time.Now(),
}
r.Participants[userID] = participant
log.Printf("🎥 [Room:%s] 用户 %s 加入 (类型: %d)", r.ID, userID, pType)
return participant, nil
}
@@ -130,7 +131,7 @@ func (r *Room) AddParticipant(userID string, pType ParticipantType) (*Participan
func (r *Room) RemoveParticipant(userID string) {
r.mu.Lock()
defer r.mu.Unlock()
if p, exists := r.Participants[userID]; exists {
// 清理 PeerConnection
if p.PeerConnection != nil {
@@ -139,7 +140,7 @@ func (r *Room) RemoveParticipant(userID string) {
delete(r.Participants, userID)
log.Printf("🎥 [Room:%s] 用户 %s 离开", r.ID, userID)
}
// 如果房间空了,标记为可清理
if len(r.Participants) == 0 {
log.Printf("🎥 [Room:%s] 房间已空,等待清理", r.ID)
@@ -157,7 +158,7 @@ func (r *Room) GetParticipant(userID string) *Participant {
func (r *Room) GetAllParticipants() []*Participant {
r.mu.RLock()
defer r.mu.RUnlock()
participants := make([]*Participant, 0, len(r.Participants))
for _, p := range r.Participants {
participants = append(participants, p)
@@ -169,7 +170,7 @@ func (r *Room) GetAllParticipants() []*Participant {
func (r *Room) GetOtherParticipants(excludeUserID string) []*Participant {
r.mu.RLock()
defer r.mu.RUnlock()
participants := make([]*Participant, 0, len(r.Participants)-1)
for _, p := range r.Participants {
if p.UserID != excludeUserID {
@@ -204,7 +205,7 @@ func (r *Room) IsFull() bool {
func (r *Room) GetInfo() *RoomInfo {
r.mu.RLock()
defer r.mu.RUnlock()
participants := make([]ParticipantInfo, 0, len(r.Participants))
for _, p := range r.Participants {
typeName := "WebRTC"
@@ -220,7 +221,7 @@ func (r *Room) GetInfo() *RoomInfo {
JoinedAt: p.JoinedAt,
})
}
return &RoomInfo{
ID: r.ID,
CreatedAt: r.CreatedAt,
@@ -235,7 +236,7 @@ func (r *Room) GetInfo() *RoomInfo {
func (r *Room) UpdateParticipantMedia(userID string, hasAudio, hasVideo bool) {
r.mu.Lock()
defer r.mu.Unlock()
if p, exists := r.Participants[userID]; exists {
p.HasAudio = hasAudio
p.HasVideo = hasVideo
@@ -243,13 +244,14 @@ func (r *Room) UpdateParticipantMedia(userID string, hasAudio, hasVideo bool) {
}
// SetParticipantRTMPURLs 设置参与者的RTMP推拉流地址
func (r *Room) SetParticipantRTMPURLs(userID, pushURL, pullURL, streamID string) {
func (r *Room) SetParticipantRTMPURLs(userID, pushURL, pullURL, flvURL, streamID string) {
r.mu.Lock()
defer r.mu.Unlock()
if p, exists := r.Participants[userID]; exists {
p.PushURL = pushURL
p.PullURL = pullURL
p.FLVURL = flvURL
p.StreamID = streamID
}
}
@@ -258,14 +260,14 @@ func (r *Room) SetParticipantRTMPURLs(userID, pushURL, pullURL, streamID string)
func (r *Room) Close() {
r.mu.Lock()
defer r.mu.Unlock()
if r.closed {
return
}
r.closed = true
close(r.closeChan)
// 清理所有参与者
for userID, p := range r.Participants {
if p.PeerConnection != nil {
@@ -273,7 +275,7 @@ func (r *Room) Close() {
}
delete(r.Participants, userID)
}
log.Printf("🎥 [Room:%s] 已关闭", r.ID)
}
@@ -288,7 +290,7 @@ func (r *Room) IsClosed() bool {
func (r *Room) Broadcast(excludeUserID string, message interface{}) {
r.mu.RLock()
defer r.mu.RUnlock()
for userID := range r.Participants {
if userID != excludeUserID {
// TODO: 通过 WebSocket 发送消息
@@ -296,4 +298,3 @@ func (r *Room) Broadcast(excludeUserID string, message interface{}) {
}
}
}