微信小程序\推拉流

This commit is contained in:
2025-12-15 21:57:21 +08:00
parent 2fca6e5c43
commit 73c2073cf1
14 changed files with 2545 additions and 421 deletions

View File

@@ -11,6 +11,7 @@ package api
import (
"fmt"
"log"
"xk-websocket-v2/internal/mediaserver"
"xk-websocket-v2/internal/utils"
@@ -59,10 +60,13 @@ type WebRTCICERequest struct {
type JoinCallRoomResponse struct {
RoomID string `json:"room_id"`
Platform string `json:"platform"`
ICEServers []ICEServerConfig `json:"ice_servers,omitempty"` // H5/App 用
ICEServers []ICEServerConfig `json:"ice_servers,omitempty"` // H5/App 用 (WebRTC 模式)
WSPushURL string `json:"ws_push_url,omitempty"` // H5/App 用 (RTMP 模式 WebSocket 推流地址)
SelfFLVURL string `json:"self_flv_url,omitempty"` // H5/App 用 - 自己的 HTTP-FLV 地址(供小程序拉流)
FlvPullURLs []PullURLInfo `json:"flv_pull_urls,omitempty"` // H5/App 拉取小程序流的 FLV 地址
PushURL string `json:"push_url,omitempty"` // 小程序用
PullURLs []PullURLInfo `json:"pull_urls,omitempty"` // 小程序用
PushURL string `json:"push_url,omitempty"` // 小程序用 RTMP 推流地址
FLVURL string `json:"flv_url,omitempty"` // 小程序用 - 自己的 HTTP-FLV 地址(供其他端拉流)
PullURLs []PullURLInfo `json:"pull_urls,omitempty"` // 小程序用 RTMP 拉流地址
Participants []ParticipantInfo `json:"participants"`
}
@@ -154,17 +158,40 @@ func JoinCallRoomHandler(c *gin.Context) {
switch req.Platform {
case "h5", "app":
// H5/App 使用 WebRTC
// H5/App 使用 WebRTC 或 RTMP 模式
pType := mediaserver.ParticipantTypeWebRTC
_, err := room.AddParticipant(req.UserID, pType)
participant, err := room.AddParticipant(req.UserID, pType)
if err != nil {
utils.BadRequest(c, err.Error())
return
}
// 返回 ICE 服务器配置
// 返回 ICE 服务器配置 (WebRTC 模式)
response.ICEServers = getICEServers(req.UserID)
// 生成 WebSocket 推流地址 (RTMP 模式)
// 格式: ws://host:port/api/call/ws-push?stream_id=xxx&user_id=xxx&room_id=xxx&token=xxx
rtmpServer := ms.GetRTMP()
if rtmpServer != nil {
// 为 H5/App 生成流信息
stream, err := rtmpServer.GenerateStreamURLs(req.RoomID, req.UserID)
if err == nil {
// 更新参与者的流地址
room.SetParticipantRTMPURLs(req.UserID, stream.PushURL, stream.PullURL, stream.FLVURL, stream.ID)
participant.PushURL = stream.PushURL
participant.PullURL = stream.PullURL
participant.FLVURL = stream.FLVURL
participant.StreamID = stream.ID
// 构建 WebSocket 推流 URL
token := mediaserver.GenerateStreamToken(stream.ID)
response.WSPushURL = fmt.Sprintf("wss://g-ws.nailaoyun.cn/api/call/ws-push?stream_id=%s&user_id=%s&room_id=%s&token=%s",
stream.ID, req.UserID, req.RoomID, token)
// 返回 H5/App 自己的 FLV 地址(供小程序拉流)
response.SelfFLVURL = stream.FLVURL
}
}
// 获取房间内小程序用户的 FLV 拉流地址(用于 Web 端播放小程序流)
flvPullURLs := make([]PullURLInfo, 0)
for _, p := range room.GetOtherParticipants(req.UserID) {
@@ -209,6 +236,7 @@ func JoinCallRoomHandler(c *gin.Context) {
participant.StreamID = stream.ID
response.PushURL = stream.PushURL
response.FLVURL = stream.FLVURL // 返回自己的 FLV 地址,供客户端发送给其他端
// 获取房间内其他用户的拉流地址
pullURLs := make([]PullURLInfo, 0)
@@ -432,5 +460,33 @@ func RegisterCallRoutes(router *gin.RouterGroup) {
callGroup.POST("/offer", WebRTCOfferHandler)
callGroup.POST("/ice", WebRTCICEHandler)
callGroup.GET("/ice-servers", GetICEServersHandler)
// 注意ws-push 路由已移到全局main.go避免中间件干扰 WebSocket 升级
}
}
// WSPushHandler WebSocket 推流处理
// GET /api/call/ws-push?stream_id=xxx&user_id=xxx&room_id=xxx&token=xxx
func WSPushHandler(c *gin.Context) {
// 详细日志:确认请求到达
log.Printf("🔌 [WSPush] 收到请求: %s from %s", c.Request.URL.String(), c.ClientIP())
log.Printf("🔌 [WSPush] Headers: Upgrade=%s Connection=%s Origin=%s",
c.GetHeader("Upgrade"), c.GetHeader("Connection"), c.GetHeader("Origin"))
ms := mediaserver.GetServer()
if ms.GetConfig() == nil || !ms.GetConfig().Enabled {
log.Printf("❌ [WSPush] 媒体服务未启用")
utils.Error(c, 503, "媒体服务未启用")
return
}
wsProxy := ms.GetWSProxy()
if wsProxy == nil {
log.Printf("❌ [WSPush] WebSocket代理未启用")
utils.Error(c, 503, "WebSocket代理未启用")
return
}
log.Printf("✅ [WSPush] 转交给 WebSocket 代理处理")
// 交给 WebSocket 代理处理
wsProxy.HandleWebSocket(c.Writer, c.Request)
}