mirror of
				https://github.com/AlexxIT/go2rtc.git
				synced 2025-10-26 09:40:58 +08:00 
			
		
		
		
	Adds support RTSPtoWeb API (entity_id for zero config from Hass)
This commit is contained in:
		
							
								
								
									
										94
									
								
								cmd/hass/api.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								cmd/hass/api.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,94 @@ | ||||
| package hass | ||||
|  | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"github.com/AlexxIT/go2rtc/cmd/api" | ||||
| 	"github.com/AlexxIT/go2rtc/cmd/streams" | ||||
| 	"github.com/AlexxIT/go2rtc/cmd/webrtc" | ||||
| 	"net/http" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| func initAPI() { | ||||
| 	ok := func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.Header().Set("Content-Type", "application/json") | ||||
| 		_, _ = w.Write([]byte(`{"status":1,"payload":{}}`)) | ||||
| 	} | ||||
|  | ||||
| 	// support https://www.home-assistant.io/integrations/rtsp_to_webrtc/ | ||||
| 	api.HandleFunc("/static", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.WriteHeader(http.StatusOK) | ||||
| 	}) | ||||
|  | ||||
| 	api.HandleFunc("/streams", ok) | ||||
|  | ||||
| 	api.HandleFunc("/stream/", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		switch { | ||||
| 		// /stream/{id}/add | ||||
| 		case strings.HasSuffix(r.RequestURI, "/add"): | ||||
| 			var v addJSON | ||||
| 			if err := json.NewDecoder(r.Body).Decode(&v); err != nil { | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			if streams.Has(v.Name) { | ||||
| 				stream := streams.Get(v.Name) | ||||
| 				stream.SetSource(v.Channels.First.Url) | ||||
| 			} else { | ||||
| 				streams.New(v.Name, v.Channels.First.Url) | ||||
| 			} | ||||
|  | ||||
| 			ok(w, r) | ||||
|  | ||||
| 		// /stream/{id}/channel/0/webrtc | ||||
| 		default: | ||||
| 			i := strings.IndexByte(r.RequestURI[8:], '/') | ||||
| 			src := r.RequestURI[8 : 8+i] | ||||
| 			if !streams.Has(src) { | ||||
| 				w.WriteHeader(http.StatusNotFound) | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			if err := r.ParseForm(); err != nil { | ||||
| 				log.Error().Err(err).Msg("[api.hass] parse form") | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			s := r.FormValue("data") | ||||
| 			offer, err := base64.StdEncoding.DecodeString(s) | ||||
| 			if err != nil { | ||||
| 				log.Error().Err(err).Msg("[api.hass] sdp64 decode") | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			// check if stream links to our rtsp server | ||||
| 			//if strings.HasPrefix(src, "rtsp://") { | ||||
| 			//	i := strings.IndexByte(src[7:], '/') | ||||
| 			//	if i > 0 && streams.Has(src[8+i:]) { | ||||
| 			//		src = src[8+i:] | ||||
| 			//	} | ||||
| 			//} | ||||
|  | ||||
| 			stream := streams.Get(src) | ||||
| 			s, err = webrtc.ExchangeSDP(stream, string(offer), r.UserAgent()) | ||||
| 			if err != nil { | ||||
| 				log.Error().Err(err).Msg("[api.hass] exchange SDP") | ||||
| 				return | ||||
| 			} | ||||
|  | ||||
| 			s = base64.StdEncoding.EncodeToString([]byte(s)) | ||||
| 			_, _ = w.Write([]byte(s)) | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| type addJSON struct { | ||||
| 	Name     string `json:"name"` | ||||
| 	Channels struct { | ||||
| 		First struct { | ||||
| 			//Name string `json:"name"` | ||||
| 			Url string `json:"url"` | ||||
| 		} `json:"0"` | ||||
| 	} `json:"channels"` | ||||
| } | ||||
| @@ -1,20 +1,14 @@ | ||||
| package hass | ||||
|  | ||||
| import ( | ||||
| 	"encoding/base64" | ||||
| 	"encoding/json" | ||||
| 	"fmt" | ||||
| 	"github.com/AlexxIT/go2rtc/cmd/api" | ||||
| 	"github.com/AlexxIT/go2rtc/cmd/app" | ||||
| 	"github.com/AlexxIT/go2rtc/cmd/streams" | ||||
| 	"github.com/AlexxIT/go2rtc/cmd/webrtc" | ||||
| 	"github.com/AlexxIT/go2rtc/pkg/streamer" | ||||
| 	"github.com/rs/zerolog" | ||||
| 	"net/http" | ||||
| 	"net/url" | ||||
| 	"os" | ||||
| 	"path" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| func Init() { | ||||
| @@ -28,11 +22,7 @@ func Init() { | ||||
|  | ||||
| 	log = app.GetLogger("hass") | ||||
|  | ||||
| 	// support https://www.home-assistant.io/integrations/rtsp_to_webrtc/ | ||||
| 	api.HandleFunc("/static", func(w http.ResponseWriter, r *http.Request) { | ||||
| 		w.WriteHeader(http.StatusOK) | ||||
| 	}) | ||||
| 	api.HandleFunc("/stream", handler) | ||||
| 	initAPI() | ||||
|  | ||||
| 	// support load cameras from Hass config file | ||||
| 	filename := path.Join(conf.Mod.Config, ".storage/core.config_entries") | ||||
| @@ -78,73 +68,13 @@ func Init() { | ||||
| 			continue | ||||
| 		} | ||||
|  | ||||
| 		log.Info().Str("url", "hass:" + entrie.Title).Msg("[hass] load stream") | ||||
| 		log.Info().Str("url", "hass:"+entrie.Title).Msg("[hass] load stream") | ||||
| 		//streams.Get("hass:" + entrie.Title) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var log zerolog.Logger | ||||
|  | ||||
| func handler(w http.ResponseWriter, r *http.Request) { | ||||
| 	if err := r.ParseForm(); err != nil { | ||||
| 		log.Error().Err(err).Msg("[api.hass] parse form") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	src := r.FormValue("url") | ||||
| 	src, err := url.QueryUnescape(src) | ||||
| 	if err != nil { | ||||
| 		log.Error().Err(err).Msg("[api.hass] query unescape") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	str := r.FormValue("sdp64") | ||||
|  | ||||
| 	offer, err := base64.StdEncoding.DecodeString(str) | ||||
| 	if err != nil { | ||||
| 		log.Error().Err(err).Msg("[api.hass] sdp64 decode") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	// check if stream links to our rtsp server | ||||
| 	if strings.HasPrefix(src, "rtsp://") { | ||||
| 		i := strings.IndexByte(src[7:], '/') | ||||
| 		if i > 0 && streams.Has(src[8+i:]) { | ||||
| 			src = src[8+i:] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	stream := streams.Get(src) | ||||
| 	if stream == nil { | ||||
| 		log.Error().Str("url", src).Msg("[api.hass] unsupported source") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	str, err = webrtc.ExchangeSDP(stream, string(offer), r.UserAgent()) | ||||
| 	if err != nil { | ||||
| 		log.Error().Err(err).Msg("[api.hass] exchange SDP") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	resp := struct { | ||||
| 		Answer string `json:"sdp64"` | ||||
| 	}{ | ||||
| 		Answer: base64.StdEncoding.EncodeToString([]byte(str)), | ||||
| 	} | ||||
|  | ||||
| 	data, err := json.Marshal(resp) | ||||
| 	if err != nil { | ||||
| 		log.Error().Err(err).Msg("[api.hass] marshal JSON") | ||||
| 		return | ||||
| 	} | ||||
|  | ||||
| 	w.Header().Set("Content-Type", "application/json") | ||||
| 	if _, err = w.Write(data); err != nil { | ||||
| 		log.Error().Err(err).Msg("[api.hass] write") | ||||
| 		return | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type entries struct { | ||||
| 	Data struct { | ||||
| 		Entries []struct { | ||||
|   | ||||
| @@ -38,6 +38,12 @@ func NewStream(source interface{}) *Stream { | ||||
| 	return s | ||||
| } | ||||
|  | ||||
| func (s *Stream) SetSource(source string) { | ||||
| 	if len(s.producers) > 0 { | ||||
| 		s.producers[0].url = source | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (s *Stream) AddConsumer(cons streamer.Consumer) (err error) { | ||||
| 	ic := len(s.consumers) | ||||
|  | ||||
|   | ||||
| @@ -4,6 +4,7 @@ import ( | ||||
| 	"github.com/AlexxIT/go2rtc/cmd/app" | ||||
| 	"github.com/AlexxIT/go2rtc/cmd/app/store" | ||||
| 	"github.com/rs/zerolog" | ||||
| 	"strings" | ||||
| ) | ||||
|  | ||||
| func Init() { | ||||
| @@ -27,7 +28,6 @@ func Init() { | ||||
| func Get(src string) *Stream { | ||||
| 	if stream, ok := streams[src]; ok { | ||||
| 		return stream | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	if !HasProducer(src) { | ||||
| @@ -45,6 +45,19 @@ func Has(src string) bool { | ||||
| } | ||||
|  | ||||
| func New(name string, source interface{}) { | ||||
| 	switch source := source.(type) { | ||||
| 	case string: | ||||
| 		// check if new stream already link on our other stream | ||||
| 		if strings.HasPrefix(source, "rtsp://") { | ||||
| 			if i := strings.IndexByte(source[7:], '/'); i > 0 { | ||||
| 				if stream, ok := streams[source[8+i:]]; ok { | ||||
| 					streams[name] = stream | ||||
| 					return | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	streams[name] = NewStream(source) | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Alexey Khit
					Alexey Khit