package httpflv import ( "encoding/json" "net" "net/http" "strings" "github.com/gwuhaolin/livego/av" "github.com/gwuhaolin/livego/protocol/rtmp" log "github.com/sirupsen/logrus" ) type Server struct { handler av.Handler } type stream struct { Key string `json:"key"` Id string `json:"id"` } type streams struct { Publishers []stream `json:"publishers"` Players []stream `json:"players"` } func NewServer(h av.Handler) *Server { return &Server{ handler: h, } } func (server *Server) Serve(l net.Listener) error { mux := http.NewServeMux() mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { server.handleConn(w, r) }) mux.HandleFunc("/streams", func(w http.ResponseWriter, r *http.Request) { server.getStream(w, r) }) if err := http.Serve(l, mux); err != nil { return err } return nil } // 获取发布和播放器的信息 func (server *Server) getStreams(w http.ResponseWriter, r *http.Request) *streams { rtmpStream := server.handler.(*rtmp.RtmpStream) if rtmpStream == nil { return nil } msgs := new(streams) rtmpStream.GetStreams().Range(func(key, val interface{}) bool { if s, ok := val.(*rtmp.Stream); ok { if s.GetReader() != nil { msg := stream{key.(string), s.GetReader().Info().UID} msgs.Publishers = append(msgs.Publishers, msg) } } return true }) rtmpStream.GetStreams().Range(func(key, val interface{}) bool { ws := val.(*rtmp.Stream).GetWs() ws.Range(func(k, v interface{}) bool { if pw, ok := v.(*rtmp.PackWriterCloser); ok { if pw.GetWriter() != nil { msg := stream{key.(string), pw.GetWriter().Info().UID} msgs.Players = append(msgs.Players, msg) } } return true }) return true }) return msgs } func (server *Server) getStream(w http.ResponseWriter, r *http.Request) { msgs := server.getStreams(w, r) if msgs == nil { return } resp, _ := json.Marshal(msgs) w.Header().Set("Content-Type", "application/json") w.Write(resp) } func (server *Server) handleConn(w http.ResponseWriter, r *http.Request) { defer func() { if r := recover(); r != nil { log.Error("http flv handleConn panic: ", r) } }() url := r.URL.String() u := r.URL.Path if pos := strings.LastIndex(u, "."); pos < 0 || u[pos:] != ".flv" { http.Error(w, "invalid path", http.StatusBadRequest) return } path := strings.TrimSuffix(strings.TrimLeft(u, "/"), ".flv") paths := strings.SplitN(path, "/", 2) log.Debug("url:", u, "path:", path, "paths:", paths) if len(paths) != 2 { http.Error(w, "invalid path", http.StatusBadRequest) return } // 判断视屏流是否发布,如果没有发布,直接返回404 msgs := server.getStreams(w, r) if msgs == nil || len(msgs.Publishers) == 0 { http.Error(w, "invalid path", http.StatusNotFound) return } else { include := false for _, item := range msgs.Publishers { log.Debug("playPath=" + path + ",publishPath=" + item.Key) if item.Key == path { log.Info("PlaySuccess:" + "path=" + path) include = true break } } if include == false { http.Error(w, "invalid path", http.StatusNotFound) return } } w.Header().Set("Access-Control-Allow-Origin", "*") writer := NewFLVWriter(paths[0], paths[1], url, w) server.handler.HandleWriter(writer) writer.Wait() }