微信小程序\推拉流
This commit is contained in:
@@ -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{}) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user