/** * package mediaserver * * 媒体服务器主入口 * 功能: * 1. 集成 WebRTC SFU(支持 H5/App 端音视频通话) * 2. 集成 RTMP 服务(支持微信小程序 live-pusher/live-player) * 3. 实现 WebRTC ↔ RTMP 媒体桥接(跨平台互通) */ package mediaserver import ( "log" "sync" "github.com/spf13/viper" ) // MediaServer 媒体服务器主结构 type MediaServer struct { sfu *SFUServer // WebRTC SFU 服务 rtmp *RTMPServer // RTMP 服务 wsProxy *WebMToRTMPProxy // WebSocket to RTMP 代理 rooms map[string]*Room // 通话房间管理 mu sync.RWMutex // 房间锁 config *MediaServerConfig // 配置 } // MediaServerConfig 媒体服务器配置 type MediaServerConfig struct { Enabled bool `mapstructure:"enabled"` PublicIP string `mapstructure:"public_ip"` WebRTCPort int `mapstructure:"webrtc_port"` RTMPPort int `mapstructure:"rtmp_port"` HTTPFLVPort int `mapstructure:"http_flv_port"` // SFU 配置 MaxRoomSize int `mapstructure:"max_room_size"` // 单房间最大人数 // 编解码配置 VideoCodec string `mapstructure:"video_codec"` // 视频编解码:h264 AudioCodec string `mapstructure:"audio_codec"` // 音频编解码:opus/aac } // 全局媒体服务器实例 var ( server *MediaServer once sync.Once ) // GetServer 获取媒体服务器单例 func GetServer() *MediaServer { once.Do(func() { server = newMediaServer() }) return server } // newMediaServer 创建媒体服务器实例 func newMediaServer() *MediaServer { config := loadConfig() ms := &MediaServer{ rooms: make(map[string]*Room), config: config, } if config.Enabled { ms.sfu = NewSFUServer(config) ms.rtmp = NewRTMPServer(config) // 创建 WebSocket to RTMP 代理 ms.wsProxy = NewWebMToRTMPProxy(ms.rtmp) } return ms } // loadConfig 从 viper 加载配置 func loadConfig() *MediaServerConfig { config := &MediaServerConfig{ Enabled: viper.GetBool("media.enabled"), PublicIP: viper.GetString("media.public_ip"), WebRTCPort: viper.GetInt("media.webrtc_port"), RTMPPort: viper.GetInt("media.rtmp_port"), HTTPFLVPort: viper.GetInt("media.http_flv_port"), MaxRoomSize: viper.GetInt("media.max_room_size"), VideoCodec: viper.GetString("media.video_codec"), AudioCodec: viper.GetString("media.audio_codec"), } // 设置默认值 if config.PublicIP == "" { config.PublicIP = viper.GetString("turn.public_ip") // 复用 TURN 的公网IP } if config.WebRTCPort == 0 { config.WebRTCPort = 8080 } if config.RTMPPort == 0 { config.RTMPPort = 1935 } if config.HTTPFLVPort == 0 { config.HTTPFLVPort = 7001 } if config.MaxRoomSize == 0 { config.MaxRoomSize = 9 // 默认最多9人 } if config.VideoCodec == "" { config.VideoCodec = "h264" } if config.AudioCodec == "" { config.AudioCodec = "opus" } return config } // Start 启动媒体服务器 func Start() { ms := GetServer() if !ms.config.Enabled { log.Println("ℹ️ [MediaServer] 服务已禁用") return } log.Println("🎥 [MediaServer] 正在启动...") // 启动 WebRTC SFU if ms.sfu != nil { go ms.sfu.Start() } // 启动 RTMP 服务 if ms.rtmp != nil { go ms.rtmp.Start() } log.Printf("🎥 [MediaServer] 已启动 | WebRTC: %d | RTMP: %d | HTTP-FLV: %d", ms.config.WebRTCPort, ms.config.RTMPPort, ms.config.HTTPFLVPort) } // Stop 停止媒体服务器 func (ms *MediaServer) Stop() { log.Println("🛑 [MediaServer] 正在停止...") // 关闭所有房间 ms.mu.Lock() for _, room := range ms.rooms { room.Close() } ms.rooms = make(map[string]*Room) ms.mu.Unlock() // 停止 SFU if ms.sfu != nil { ms.sfu.Stop() } // 停止 RTMP if ms.rtmp != nil { ms.rtmp.Stop() } log.Println("🛑 [MediaServer] 已停止") } // GetOrCreateRoom 获取或创建房间 func (ms *MediaServer) GetOrCreateRoom(roomID string) *Room { ms.mu.Lock() defer ms.mu.Unlock() if room, exists := ms.rooms[roomID]; exists { return room } room := NewRoom(roomID, ms.config.MaxRoomSize) ms.rooms[roomID] = room log.Printf("🎥 [MediaServer] 创建房间: %s", roomID) return room } // GetRoom 获取房间 func (ms *MediaServer) GetRoom(roomID string) *Room { ms.mu.RLock() defer ms.mu.RUnlock() return ms.rooms[roomID] } // RemoveRoom 移除房间 func (ms *MediaServer) RemoveRoom(roomID string) { ms.mu.Lock() defer ms.mu.Unlock() if room, exists := ms.rooms[roomID]; exists { room.Close() delete(ms.rooms, roomID) log.Printf("🎥 [MediaServer] 移除房间: %s", roomID) } } // GetConfig 获取配置 func (ms *MediaServer) GetConfig() *MediaServerConfig { return ms.config } // GetSFU 获取 SFU 服务 func (ms *MediaServer) GetSFU() *SFUServer { return ms.sfu } // GetRTMP 获取 RTMP 服务 func (ms *MediaServer) GetRTMP() *RTMPServer { return ms.rtmp } // GetWSProxy 获取 WebSocket to RTMP 代理 func (ms *MediaServer) GetWSProxy() *WebMToRTMPProxy { return ms.wsProxy } // GetRoomCount 获取房间数量 func (ms *MediaServer) GetRoomCount() int { ms.mu.RLock() defer ms.mu.RUnlock() return len(ms.rooms) } // GetAllRooms 获取所有房间信息(用于调试/监控) func (ms *MediaServer) GetAllRooms() []*RoomInfo { ms.mu.RLock() defer ms.mu.RUnlock() infos := make([]*RoomInfo, 0, len(ms.rooms)) for _, room := range ms.rooms { infos = append(infos, room.GetInfo()) } return infos }