package http import ( "errors" "net" "net/http" "net/url" "strings" "github.com/AlexxIT/go2rtc/internal/api" "github.com/AlexxIT/go2rtc/internal/streams" "github.com/AlexxIT/go2rtc/pkg/core" "github.com/AlexxIT/go2rtc/pkg/hls" "github.com/AlexxIT/go2rtc/pkg/magic" "github.com/AlexxIT/go2rtc/pkg/mjpeg" "github.com/AlexxIT/go2rtc/pkg/multipart" "github.com/AlexxIT/go2rtc/pkg/tcp" ) func Init() { streams.HandleFunc("http", handleHTTP) streams.HandleFunc("https", handleHTTP) streams.HandleFunc("httpx", handleHTTP) streams.HandleFunc("tcp", handleTCP) api.HandleFunc("api/stream", apiStream) } func handleHTTP(rawURL string) (core.Producer, error) { rawURL, rawQuery, _ := strings.Cut(rawURL, "#") // first we get the Content-Type to define supported producer req, err := http.NewRequest("GET", rawURL, nil) if err != nil { return nil, err } if rawQuery != "" { query := streams.ParseQuery(rawQuery) for _, header := range query["header"] { key, value, _ := strings.Cut(header, ":") req.Header.Add(key, strings.TrimSpace(value)) } } res, err := tcp.Do(req) if err != nil { return nil, err } if res.StatusCode != http.StatusOK { return nil, errors.New(res.Status) } // 1. Guess format from content type ct := res.Header.Get("Content-Type") if i := strings.IndexByte(ct, ';'); i > 0 { ct = ct[:i] } var ext string if i := strings.LastIndexByte(req.URL.Path, '.'); i > 0 { ext = req.URL.Path[i+1:] } switch { case ct == "image/jpeg": return mjpeg.NewClient(res), nil case ct == "multipart/x-mixed-replace": return multipart.Open(res.Body) case ct == "application/vnd.apple.mpegurl" || ext == "m3u8": return hls.OpenURL(req.URL, res.Body) } return magic.Open(res.Body) } func handleTCP(rawURL string) (core.Producer, error) { u, err := url.Parse(rawURL) if err != nil { return nil, err } conn, err := net.DialTimeout("tcp", u.Host, core.ConnDialTimeout) if err != nil { return nil, err } return magic.Open(conn) } func apiStream(w http.ResponseWriter, r *http.Request) { dst := r.URL.Query().Get("dst") stream := streams.Get(dst) if stream == nil { http.Error(w, api.StreamNotFound, http.StatusNotFound) return } client, err := magic.Open(r.Body) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } stream.AddProducer(client) defer stream.RemoveProducer(client) if err = client.Start(); err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } }