You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
269 lines
6.5 KiB
Go
269 lines
6.5 KiB
Go
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"sort"
|
|
"time"
|
|
|
|
"github.com/deepch/vdk/av"
|
|
|
|
webrtc "github.com/deepch/vdk/format/webrtcv3"
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
type JCodec struct {
|
|
Type string
|
|
}
|
|
|
|
func serveHTTP() {
|
|
gin.SetMode(gin.ReleaseMode)
|
|
|
|
router := gin.Default()
|
|
router.Use(CORSMiddleware())
|
|
|
|
if _, err := os.Stat("./web"); !os.IsNotExist(err) {
|
|
router.LoadHTMLGlob("web/templates/*")
|
|
router.GET("/", HTTPAPIServerIndex)
|
|
router.GET("/stream/player/:uuid", HTTPAPIServerStreamPlayer)
|
|
}
|
|
router.POST("/stream/receiver/:uuid", HTTPAPIServerStreamWebRTC)
|
|
router.GET("/stream/codec/:uuid", HTTPAPIServerStreamCodec)
|
|
router.POST("/stream", HTTPAPIServerStreamWebRTC2)
|
|
|
|
router.StaticFS("/static", http.Dir("web/static"))
|
|
err := router.Run(Config.Server.HTTPPort)
|
|
if err != nil {
|
|
log.Fatalln("Start HTTP Server error", err)
|
|
}
|
|
}
|
|
|
|
//HTTPAPIServerIndex index
|
|
func HTTPAPIServerIndex(c *gin.Context) {
|
|
_, all := Config.list()
|
|
if len(all) > 0 {
|
|
c.Header("Cache-Control", "no-cache, max-age=0, must-revalidate, no-store")
|
|
c.Header("Access-Control-Allow-Origin", "*")
|
|
c.Redirect(http.StatusMovedPermanently, "stream/player/"+all[0])
|
|
} else {
|
|
c.HTML(http.StatusOK, "index.tmpl", gin.H{
|
|
"port": Config.Server.HTTPPort,
|
|
"version": time.Now().String(),
|
|
})
|
|
}
|
|
}
|
|
|
|
//HTTPAPIServerStreamPlayer stream player
|
|
func HTTPAPIServerStreamPlayer(c *gin.Context) {
|
|
_, all := Config.list()
|
|
sort.Strings(all)
|
|
c.HTML(http.StatusOK, "player.tmpl", gin.H{
|
|
"port": Config.Server.HTTPPort,
|
|
"suuid": c.Param("uuid"),
|
|
"suuidMap": all,
|
|
"version": time.Now().String(),
|
|
})
|
|
}
|
|
|
|
//HTTPAPIServerStreamCodec stream codec
|
|
func HTTPAPIServerStreamCodec(c *gin.Context) {
|
|
if Config.ext(c.Param("uuid")) {
|
|
Config.RunIFNotRun(c.Param("uuid"))
|
|
codecs := Config.coGe(c.Param("uuid"))
|
|
if codecs == nil {
|
|
return
|
|
}
|
|
var tmpCodec []JCodec
|
|
for _, codec := range codecs {
|
|
if codec.Type() != av.H264 && codec.Type() != av.PCM_ALAW && codec.Type() != av.PCM_MULAW && codec.Type() != av.OPUS {
|
|
log.Println("Codec Not Supported WebRTC ignore this track", codec.Type())
|
|
continue
|
|
}
|
|
if codec.Type().IsVideo() {
|
|
tmpCodec = append(tmpCodec, JCodec{Type: "video"})
|
|
} else {
|
|
tmpCodec = append(tmpCodec, JCodec{Type: "audio"})
|
|
}
|
|
}
|
|
b, err := json.Marshal(tmpCodec)
|
|
if err == nil {
|
|
_, err = c.Writer.Write(b)
|
|
if err != nil {
|
|
log.Println("Write Codec Info error", err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//HTTPAPIServerStreamWebRTC stream video over WebRTC
|
|
func HTTPAPIServerStreamWebRTC(c *gin.Context) {
|
|
if !Config.ext(c.PostForm("suuid")) {
|
|
log.Println("Stream Not Found")
|
|
return
|
|
}
|
|
Config.RunIFNotRun(c.PostForm("suuid"))
|
|
codecs := Config.coGe(c.PostForm("suuid"))
|
|
if codecs == nil {
|
|
log.Println("Stream Codec Not Found")
|
|
return
|
|
}
|
|
var AudioOnly bool
|
|
if len(codecs) == 1 && codecs[0].Type().IsAudio() {
|
|
AudioOnly = true
|
|
}
|
|
muxerWebRTC := webrtc.NewMuxer(webrtc.Options{ICEServers: Config.GetICEServers(), ICEUsername: Config.GetICEUsername(), ICECredential: Config.GetICECredential(), PortMin: Config.GetWebRTCPortMin(), PortMax: Config.GetWebRTCPortMax()})
|
|
answer, err := muxerWebRTC.WriteHeader(codecs, c.PostForm("data"))
|
|
if err != nil {
|
|
log.Println("WriteHeader", err)
|
|
return
|
|
}
|
|
_, err = c.Writer.Write([]byte(answer))
|
|
if err != nil {
|
|
log.Println("Write", err)
|
|
return
|
|
}
|
|
go func() {
|
|
cid, ch := Config.clAd(c.PostForm("suuid"))
|
|
defer Config.clDe(c.PostForm("suuid"), cid)
|
|
defer muxerWebRTC.Close()
|
|
var videoStart bool
|
|
noVideo := time.NewTimer(10 * time.Second)
|
|
for {
|
|
select {
|
|
case <-noVideo.C:
|
|
log.Println("noVideo")
|
|
return
|
|
case pck := <-ch:
|
|
if pck.IsKeyFrame || AudioOnly {
|
|
noVideo.Reset(10 * time.Second)
|
|
videoStart = true
|
|
}
|
|
if !videoStart && !AudioOnly {
|
|
continue
|
|
}
|
|
err = muxerWebRTC.WritePacket(pck)
|
|
if err != nil {
|
|
log.Println("WritePacket", err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|
|
|
|
func CORSMiddleware() gin.HandlerFunc {
|
|
return func(c *gin.Context) {
|
|
c.Header("Access-Control-Allow-Origin", "*")
|
|
c.Header("Access-Control-Allow-Credentials", "true")
|
|
c.Header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization, x-access-token")
|
|
c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers, Cache-Control, Content-Language, Content-Type")
|
|
c.Header("Access-Control-Allow-Methods", "POST, OPTIONS, GET, PUT")
|
|
|
|
if c.Request.Method == "OPTIONS" {
|
|
c.AbortWithStatus(http.StatusNoContent)
|
|
return
|
|
}
|
|
|
|
c.Next()
|
|
}
|
|
}
|
|
|
|
type Response struct {
|
|
Tracks []string `json:"tracks"`
|
|
Sdp64 string `json:"sdp64"`
|
|
}
|
|
|
|
type ResponseError struct {
|
|
Error string `json:"error"`
|
|
}
|
|
|
|
func HTTPAPIServerStreamWebRTC2(c *gin.Context) {
|
|
url := c.PostForm("url")
|
|
if _, ok := Config.Streams[url]; !ok {
|
|
Config.Streams[url] = StreamST{
|
|
URL: url,
|
|
OnDemand: true,
|
|
Cl: make(map[string]viewer),
|
|
}
|
|
}
|
|
|
|
Config.RunIFNotRun(url)
|
|
|
|
codecs := Config.coGe(url)
|
|
if codecs == nil {
|
|
log.Println("Stream Codec Not Found")
|
|
c.JSON(500, ResponseError{Error: Config.LastError.Error()})
|
|
return
|
|
}
|
|
|
|
muxerWebRTC := webrtc.NewMuxer(
|
|
webrtc.Options{
|
|
ICEServers: Config.GetICEServers(),
|
|
PortMin: Config.GetWebRTCPortMin(),
|
|
PortMax: Config.GetWebRTCPortMax(),
|
|
},
|
|
)
|
|
|
|
sdp64 := c.PostForm("sdp64")
|
|
answer, err := muxerWebRTC.WriteHeader(codecs, sdp64)
|
|
if err != nil {
|
|
log.Println("Muxer WriteHeader", err)
|
|
c.JSON(500, ResponseError{Error: err.Error()})
|
|
return
|
|
}
|
|
|
|
response := Response{
|
|
Sdp64: answer,
|
|
}
|
|
|
|
for _, codec := range codecs {
|
|
if codec.Type() != av.H264 &&
|
|
codec.Type() != av.PCM_ALAW &&
|
|
codec.Type() != av.PCM_MULAW &&
|
|
codec.Type() != av.OPUS {
|
|
log.Println("Codec Not Supported WebRTC ignore this track", codec.Type())
|
|
continue
|
|
}
|
|
if codec.Type().IsVideo() {
|
|
response.Tracks = append(response.Tracks, "video")
|
|
} else {
|
|
response.Tracks = append(response.Tracks, "audio")
|
|
}
|
|
}
|
|
|
|
c.JSON(200, response)
|
|
|
|
AudioOnly := len(codecs) == 1 && codecs[0].Type().IsAudio()
|
|
|
|
go func() {
|
|
cid, ch := Config.clAd(url)
|
|
defer Config.clDe(url, cid)
|
|
defer muxerWebRTC.Close()
|
|
var videoStart bool
|
|
noVideo := time.NewTimer(10 * time.Second)
|
|
for {
|
|
select {
|
|
case <-noVideo.C:
|
|
log.Println("noVideo")
|
|
return
|
|
case pck := <-ch:
|
|
if pck.IsKeyFrame || AudioOnly {
|
|
noVideo.Reset(10 * time.Second)
|
|
videoStart = true
|
|
}
|
|
if !videoStart && !AudioOnly {
|
|
continue
|
|
}
|
|
err = muxerWebRTC.WritePacket(pck)
|
|
if err != nil {
|
|
log.Println("WritePacket", err)
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
}
|