Merge branch 'master' into rtsp-backchannel
This commit is contained in:
@@ -170,7 +170,7 @@ Available modules:
|
|||||||
- [api](#module-api) - HTTP API (important for WebRTC support)
|
- [api](#module-api) - HTTP API (important for WebRTC support)
|
||||||
- [rtsp](#module-rtsp) - RTSP Server (important for FFmpeg support)
|
- [rtsp](#module-rtsp) - RTSP Server (important for FFmpeg support)
|
||||||
- [webrtc](#module-webrtc) - WebRTC Server
|
- [webrtc](#module-webrtc) - WebRTC Server
|
||||||
- [mp4](#module-mp4) - MSE, MP4 stream and MP4 shapshot Server
|
- [mp4](#module-mp4) - MSE, MP4 stream and MP4 snapshot Server
|
||||||
- [hls](#module-hls) - HLS TS or fMP4 stream Server
|
- [hls](#module-hls) - HLS TS or fMP4 stream Server
|
||||||
- [mjpeg](#module-mjpeg) - MJPEG Server
|
- [mjpeg](#module-mjpeg) - MJPEG Server
|
||||||
- [ffmpeg](#source-ffmpeg) - FFmpeg integration
|
- [ffmpeg](#source-ffmpeg) - FFmpeg integration
|
||||||
@@ -648,10 +648,11 @@ This source type support Roborock vacuums with cameras. Known working models:
|
|||||||
|
|
||||||
- Roborock S6 MaxV - only video (the vacuum has no microphone)
|
- Roborock S6 MaxV - only video (the vacuum has no microphone)
|
||||||
- Roborock S7 MaxV - video and two way audio
|
- Roborock S7 MaxV - video and two way audio
|
||||||
|
- Roborock Qrevo MaxV - video and two way audio
|
||||||
|
|
||||||
Source support load Roborock credentials from Home Assistant [custom integration](https://github.com/humbertogontijo/homeassistant-roborock). Otherwise, you need to log in to your Roborock account (MiHome account is not supported). Go to: go2rtc WebUI > Add webpage. Copy `roborock://...` source for your vacuum and paste it to `go2rtc.yaml` config.
|
Source support load Roborock credentials from Home Assistant [custom integration](https://github.com/humbertogontijo/homeassistant-roborock) or the [core integration](https://www.home-assistant.io/integrations/roborock). Otherwise, you need to log in to your Roborock account (MiHome account is not supported). Go to: go2rtc WebUI > Add webpage. Copy `roborock://...` source for your vacuum and paste it to `go2rtc.yaml` config.
|
||||||
|
|
||||||
If you have graphic pin for your vacuum - add it as numeric pin (lines: 123, 456, 678) to the end of the roborock-link.
|
If you have graphic pin for your vacuum - add it as numeric pin (lines: 123, 456, 789) to the end of the roborock-link.
|
||||||
|
|
||||||
#### Source: WebRTC
|
#### Source: WebRTC
|
||||||
|
|
||||||
|
|||||||
@@ -19,15 +19,15 @@ go2rtc -c log.format=text -c /config/go2rtc.yaml -c rtsp.listen='' -c /usr/local
|
|||||||
|
|
||||||
## Environment variables
|
## Environment variables
|
||||||
|
|
||||||
Also go2rtc support templates for using environment variables in any part of config:
|
There is support for loading external variables into the config. First, they will be attempted to be loaded from [credential files](https://systemd.io/CREDENTIALS). If `CREDENTIALS_DIRECTORY` is not set, then the key will be loaded from an environment variable. If no environment variable is set, then the string will be left as-is.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
streams:
|
streams:
|
||||||
camera1: rtsp://rtsp:${CAMERA_PASSWORD}@192.168.1.123/av_stream/ch0
|
camera1: rtsp://rtsp:${CAMERA_PASSWORD}@192.168.1.123/av_stream/ch0
|
||||||
|
|
||||||
rtsp:
|
rtsp:
|
||||||
username: ${RTSP_USER:admin} # "admin" if env "RTSP_USER" not set
|
username: ${RTSP_USER:admin} # "admin" if "RTSP_USER" not set
|
||||||
password: ${RTSP_PASS:secret} # "secret" if env "RTSP_PASS" not set
|
password: ${RTSP_PASS:secret} # "secret" if "RTSP_PASS" not set
|
||||||
```
|
```
|
||||||
|
|
||||||
## JSON Schema
|
## JSON Schema
|
||||||
|
|||||||
@@ -179,6 +179,7 @@ func parseArgs(s string) *ffmpeg.Args {
|
|||||||
Version: verAV,
|
Version: verAV,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var source = s
|
||||||
var query url.Values
|
var query url.Values
|
||||||
if i := strings.IndexByte(s, '#'); i >= 0 {
|
if i := strings.IndexByte(s, '#'); i >= 0 {
|
||||||
query = streams.ParseQuery(s[i+1:])
|
query = streams.ParseQuery(s[i+1:])
|
||||||
@@ -221,6 +222,10 @@ func parseArgs(s string) *ffmpeg.Args {
|
|||||||
default:
|
default:
|
||||||
s += "?video&audio"
|
s += "?video&audio"
|
||||||
}
|
}
|
||||||
|
s += "&source=ffmpeg:" + url.QueryEscape(source)
|
||||||
|
for _, v := range query["query"] {
|
||||||
|
s += "&" + v
|
||||||
|
}
|
||||||
args.Input = inputTemplate("rtsp", s, query)
|
args.Input = inputTemplate("rtsp", s, query)
|
||||||
} else if i = strings.Index(s, "?"); i > 0 {
|
} else if i = strings.Index(s, "?"); i > 0 {
|
||||||
switch s[:i] {
|
switch s[:i] {
|
||||||
|
|||||||
+13
-2
@@ -148,6 +148,7 @@ func tcpHandler(conn *rtsp.Conn) {
|
|||||||
var closer func()
|
var closer func()
|
||||||
|
|
||||||
trace := log.Trace().Enabled()
|
trace := log.Trace().Enabled()
|
||||||
|
level := zerolog.WarnLevel
|
||||||
|
|
||||||
conn.Listen(func(msg any) {
|
conn.Listen(func(msg any) {
|
||||||
if trace {
|
if trace {
|
||||||
@@ -206,8 +207,18 @@ func tcpHandler(conn *rtsp.Conn) {
|
|||||||
conn.PacketSize = uint16(core.Atoi(s))
|
conn.PacketSize = uint16(core.Atoi(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// param name like ffmpeg style https://ffmpeg.org/ffmpeg-protocols.html
|
||||||
|
if s := query.Get("log_level"); s != "" {
|
||||||
|
if lvl, err := zerolog.ParseLevel(s); err == nil {
|
||||||
|
level = lvl
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// will help to protect looping requests to same source
|
||||||
|
conn.Connection.Source = query.Get("source")
|
||||||
|
|
||||||
if err := stream.AddConsumer(conn); err != nil {
|
if err := stream.AddConsumer(conn); err != nil {
|
||||||
log.Warn().Err(err).Str("stream", name).Msg("[rtsp]")
|
log.WithLevel(level).Err(err).Str("stream", name).Msg("[rtsp]")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +256,7 @@ func tcpHandler(conn *rtsp.Conn) {
|
|||||||
|
|
||||||
if err := conn.Accept(); err != nil {
|
if err := conn.Accept(); err != nil {
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
log.Warn().Err(err).Caller().Send()
|
log.WithLevel(level).Err(err).Caller().Send()
|
||||||
}
|
}
|
||||||
if closer != nil {
|
if closer != nil {
|
||||||
closer()
|
closer()
|
||||||
|
|||||||
@@ -22,6 +22,12 @@ func (s *Stream) AddConsumer(cons core.Consumer) (err error) {
|
|||||||
|
|
||||||
producers:
|
producers:
|
||||||
for prodN, prod := range s.producers {
|
for prodN, prod := range s.producers {
|
||||||
|
// check for loop request, ex. `camera1: ffmpeg:camera1`
|
||||||
|
if info, ok := cons.(core.Info); ok && prod.url == info.GetSource() {
|
||||||
|
log.Trace().Msgf("[streams] skip cons=%d prod=%d", consN, prodN)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
if prodErrors[prodN] != nil {
|
if prodErrors[prodN] != nil {
|
||||||
log.Trace().Msgf("[streams] skip cons=%d prod=%d", consN, prodN)
|
log.Trace().Msgf("[streams] skip cons=%d prod=%d", consN, prodN)
|
||||||
continue
|
continue
|
||||||
@@ -129,7 +135,7 @@ func formatError(consMedias, prodMedias []*core.Media, prodErrors []error) error
|
|||||||
for _, media := range prodMedias {
|
for _, media := range prodMedias {
|
||||||
if media.Direction == core.DirectionRecvonly {
|
if media.Direction == core.DirectionRecvonly {
|
||||||
for _, codec := range media.Codecs {
|
for _, codec := range media.Codecs {
|
||||||
prod = appendString(prod, codec.PrintName())
|
prod = appendString(prod, media.Kind+":"+codec.PrintName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +143,7 @@ func formatError(consMedias, prodMedias []*core.Media, prodErrors []error) error
|
|||||||
for _, media := range consMedias {
|
for _, media := range consMedias {
|
||||||
if media.Direction == core.DirectionSendonly {
|
if media.Direction == core.DirectionSendonly {
|
||||||
for _, codec := range media.Codecs {
|
for _, codec := range media.Codecs {
|
||||||
cons = appendString(cons, codec.PrintName())
|
cons = appendString(cons, media.Kind+":"+codec.PrintName())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
app.Version = "1.9.6"
|
app.Version = "1.9.7"
|
||||||
|
|
||||||
// 1. Core modules: app, api/ws, streams
|
// 1. Core modules: app, api/ws, streams
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ type Info interface {
|
|||||||
SetSource(string)
|
SetSource(string)
|
||||||
SetURL(string)
|
SetURL(string)
|
||||||
WithRequest(*http.Request)
|
WithRequest(*http.Request)
|
||||||
|
GetSource() string
|
||||||
}
|
}
|
||||||
|
|
||||||
// Connection just like webrtc.PeerConnection
|
// Connection just like webrtc.PeerConnection
|
||||||
@@ -123,6 +124,10 @@ func (c *Connection) WithRequest(r *http.Request) {
|
|||||||
c.UserAgent = r.UserAgent()
|
c.UserAgent = r.UserAgent()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Connection) GetSource() string {
|
||||||
|
return c.Source
|
||||||
|
}
|
||||||
|
|
||||||
// Create like os.Create, init Consumer with existing Transport
|
// Create like os.Create, init Consumer with existing Transport
|
||||||
func Create(w io.Writer) (*Connection, error) {
|
func Create(w io.Writer) (*Connection, error) {
|
||||||
return &Connection{Transport: w}, nil
|
return &Connection{Transport: w}, nil
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package shell
|
|||||||
import (
|
import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
@@ -51,6 +52,13 @@ func ReplaceEnvVars(text string) string {
|
|||||||
dok = true
|
dok = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if dir, vok := os.LookupEnv("CREDENTIALS_DIRECTORY"); vok {
|
||||||
|
value, err := os.ReadFile(filepath.Join(dir, key))
|
||||||
|
if err == nil {
|
||||||
|
return strings.TrimSpace(string(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if value, vok := os.LookupEnv(key); vok {
|
if value, vok := os.LookupEnv(key); vok {
|
||||||
return value
|
return value
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user