Add web HTTP-FLV playback path
This commit is contained in:
@@ -18,9 +18,11 @@ func main() {
|
||||
// Initialize Chat WebSocket Hub
|
||||
chat.InitChat()
|
||||
|
||||
srv := stream.NewRTMPServer()
|
||||
|
||||
// Start the API server in a goroutine so it doesn't block the RTMP server
|
||||
go func() {
|
||||
r := api.SetupRouter()
|
||||
r := api.SetupRouter(srv)
|
||||
log.Println("[INFO] API Server is listening on :8080...")
|
||||
if err := r.Run(":8080"); err != nil {
|
||||
log.Fatalf("Failed to start API server: %v", err)
|
||||
@@ -29,7 +31,6 @@ func main() {
|
||||
|
||||
// Setup and start the RTMP server
|
||||
log.Println("[INFO] Ready to receive RTMP streams from OBS.")
|
||||
srv := stream.NewRTMPServer()
|
||||
if err := srv.Start(":1935"); err != nil {
|
||||
log.Fatalf("Failed to start RTMP server: %v", err)
|
||||
}
|
||||
|
||||
@@ -2,10 +2,12 @@ package api
|
||||
|
||||
import (
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"hightube/internal/stream"
|
||||
)
|
||||
|
||||
// SetupRouter configures the Gin router and defines API endpoints
|
||||
func SetupRouter() *gin.Engine {
|
||||
func SetupRouter(streamServer *stream.RTMPServer) *gin.Engine {
|
||||
// 设置为发布模式,消除 "[WARNING] Running in debug mode" 警告
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
|
||||
@@ -21,6 +23,7 @@ func SetupRouter() *gin.Engine {
|
||||
r.POST("/api/register", Register)
|
||||
r.POST("/api/login", Login)
|
||||
r.GET("/api/rooms/active", GetActiveRooms)
|
||||
r.GET("/live/:room_id", streamServer.HandleHTTPFLV)
|
||||
|
||||
// WebSocket endpoint for live chat
|
||||
r.GET("/api/ws/room/:room_id", WSHandler)
|
||||
|
||||
@@ -3,12 +3,15 @@ package stream
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/nareix/joy4/av/avutil"
|
||||
"github.com/nareix/joy4/av/pubsub"
|
||||
"github.com/nareix/joy4/format"
|
||||
"github.com/nareix/joy4/format/flv"
|
||||
"github.com/nareix/joy4/format/rtmp"
|
||||
|
||||
"hightube/internal/chat"
|
||||
@@ -28,6 +31,16 @@ type RTMPServer struct {
|
||||
mutex sync.RWMutex
|
||||
}
|
||||
|
||||
type writeFlusher struct {
|
||||
httpFlusher http.Flusher
|
||||
io.Writer
|
||||
}
|
||||
|
||||
func (w writeFlusher) Flush() error {
|
||||
w.httpFlusher.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewRTMPServer creates and initializes a new media server
|
||||
func NewRTMPServer() *RTMPServer {
|
||||
s := &RTMPServer{
|
||||
@@ -140,3 +153,46 @@ func (s *RTMPServer) Start(addr string) error {
|
||||
fmt.Printf("[INFO] RTMP Server is listening on %s...\n", addr)
|
||||
return s.server.ListenAndServe()
|
||||
}
|
||||
|
||||
// HandleHTTPFLV serves browser-compatible HTTP-FLV playback for web clients.
|
||||
func (s *RTMPServer) HandleHTTPFLV(c *gin.Context) {
|
||||
streamPath := fmt.Sprintf("/live/%s", c.Param("room_id"))
|
||||
|
||||
s.mutex.RLock()
|
||||
q, ok := s.channels[streamPath]
|
||||
s.mutex.RUnlock()
|
||||
|
||||
if !ok {
|
||||
c.JSON(http.StatusNotFound, gin.H{"error": "Stream not found or inactive"})
|
||||
return
|
||||
}
|
||||
|
||||
flusher, ok := c.Writer.(http.Flusher)
|
||||
if !ok {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Streaming is not supported by the current server"})
|
||||
return
|
||||
}
|
||||
|
||||
c.Header("Content-Type", "video/x-flv")
|
||||
c.Header("Transfer-Encoding", "chunked")
|
||||
c.Header("Cache-Control", "no-cache")
|
||||
c.Header("Connection", "keep-alive")
|
||||
c.Header("Access-Control-Allow-Origin", "*")
|
||||
c.Status(http.StatusOK)
|
||||
flusher.Flush()
|
||||
|
||||
muxer := flv.NewMuxerWriteFlusher(writeFlusher{
|
||||
httpFlusher: flusher,
|
||||
Writer: c.Writer,
|
||||
})
|
||||
cursor := q.Latest()
|
||||
|
||||
if err := avutil.CopyFile(muxer, cursor); err != nil && err != io.EOF {
|
||||
errStr := err.Error()
|
||||
if strings.Contains(errStr, "broken pipe") || strings.Contains(errStr, "connection reset by peer") {
|
||||
fmt.Printf("[INFO] HTTP-FLV viewer disconnected normally: %s\n", streamPath)
|
||||
return
|
||||
}
|
||||
fmt.Printf("[ERROR] HTTP-FLV playback error on %s: %v\n", streamPath, err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user