Compare commits

..

16 Commits

Author SHA1 Message Date
Alexey Khit 18d7b9075b Autoadd cameras from Hass config 2022-08-25 06:41:39 +03:00
Alexey Khit 7c4497f856 Fix incoming RTSP without OPTIONS 2022-08-25 06:38:08 +03:00
Alexey Khit befa4ca1e6 Remove wrong RTSP channel panic 2022-08-25 06:37:47 +03:00
Alexey Khit dd3b326f7a Update readme 2022-08-24 14:41:00 +03:00
Alexey Khit e36123bb19 Update build docker 2022-08-24 14:30:02 +03:00
Alexey Khit 9310343ad3 Update docker cwd to /config 2022-08-24 12:49:59 +03:00
Alexey Khit e2d4fa3393 Advanced debug on app start 2022-08-24 12:49:48 +03:00
Alexey Khit 5fea2932c1 Error on wrong config 2022-08-24 12:49:40 +03:00
Alexey Khit 1fd110b70d Update readme 2022-08-24 09:55:34 +03:00
Alexey Khit 8377cf2655 Change url param to src in Web API 2022-08-24 09:55:16 +03:00
Alexey Khit 8f01b08d42 Code refactoring 2022-08-24 09:54:28 +03:00
Alexey Khit 97ce4c3114 Adds Security section to readme 2022-08-23 09:34:06 +03:00
Alexey Khit 4813a64d9d Adds build script for mips 2022-08-23 05:43:15 +03:00
Alexey Khit 7923ec74a8 Adds network filter for webrtc 2022-08-23 05:43:01 +03:00
Alexey Khit 1f0a5fb880 Stop WebRTC conn on AddConsumer error 2022-08-22 22:46:08 +03:00
Alexey Khit c6a3ee65b8 Remove UPX from Windows builds because antiviruses 2022-08-22 22:32:23 +03:00
16 changed files with 151 additions and 85 deletions
+44 -21
View File
@@ -1,6 +1,6 @@
# go2rtc
**go2rtc** - ultimate camera streaming application with support RTSP, WebRTC, FFmpeg, RTMP, etc.
Ultimate camera streaming application with support RTSP, WebRTC, FFmpeg, RTMP, etc.
- zero-dependency and zero-config small [app for all OS](#go2rtc-binary) (Windows, macOS, Linux, ARM)
- zero-delay for all supported protocols (lowest possible streaming latency)
@@ -95,6 +95,16 @@ Don't forget to fix the rights `chmod +x go2rtc_xxx_xxx` on Linux and Mac.
Container [alexxit/go2rtc](https://hub.docker.com/r/alexxit/go2rtc) with support `amd64`, `386`, `arm64`, `arm`. This container same as [Home Assistant Add-on](#go2rtc-home-assistant-add-on), but can be used separately from the Home Assistant. Container has preinstalled [FFmpeg](#source-ffmpeg) and [Ngrok](#module-ngrok) applications.
```yaml
services:
go2rtc:
image: alexxit/go2rtc
network_mode: host
restart: always
volumes:
- "~/go2rtc.yaml:/config/go2rtc.yaml"
```
## Configuration
Create file `go2rtc.yaml` next to the app.
@@ -118,7 +128,7 @@ Available modules:
### Module: Streams
**go2rtc** support different stream source types. You can config one or multiple link as stream source.
**go2rtc** support different stream source types. You can config one or multiple links of any type as stream source.
Available source types:
@@ -135,7 +145,7 @@ Available source types:
- Support **RTSP and RTSPS** links with multiple video and audio tracks
- Support **2-way audio** ONLY for [ONVIF Profile T](https://www.onvif.org/specs/stream/ONVIF-Streaming-Spec.pdf) cameras (back channel connection)
**Attention:** proprietary 2-way audio standards are not supported!
**Attention:** other 2-way audio standards are not supported! ONVIF without Profile T is not supported!
```yaml
streams:
@@ -189,28 +199,15 @@ streams:
rtsp: ffmpeg:rtsp://rtsp:12345678@192.168.1.123/av_stream/ch0#video=copy#audio=copy
```
All trascoding formats has built-in templates. But you can override them via YAML config. You can also add your own formats to config and use them with source params.
All trascoding formats has [built-in templates](https://github.com/AlexxIT/go2rtc/blob/master/cmd/ffmpeg/ffmpeg.go): `h264`, `h264/ultra`, `h264/high`, `h265`, `opus`, `pcmu`, `pcmu/16000`, `pcmu/48000`, `pcma`, `pcma/16000`, `pcma/48000`, `aac/16000`.
But you can override them via YAML config. You can also add your own formats to config and use them with source params.
```yaml
ffmpeg:
bin: ffmpeg # path to ffmpeg binary
link: -hide_banner -i {input} # if input is link
file: -hide_banner -re -stream_loop -1 -i {input} # if input not link
rtsp: -hide_banner -fflags nobuffer -flags low_delay -rtsp_transport tcp -i {input} # if input is RTSP link
output: -rtsp_transport tcp -f rtsp {output} # output
h264: "-codec:v libx264 -g 30 -preset superfast -tune zerolatency -profile main -level 4.1"
h264/ultra: "-codec:v libx264 -g 30 -preset ultrafast -tune zerolatency"
h264/high: "-codec:v libx264 -g 30 -preset superfast -tune zerolatency"
h265: "-codec:v libx265 -g 30 -preset ultrafast -tune zerolatency"
opus: "-codec:a libopus -ar 48000 -ac 2"
pcmu: "-codec:a pcm_mulaw -ar 8000 -ac 1"
pcmu/16000: "-codec:a pcm_mulaw -ar 16000 -ac 1"
pcmu/48000: "-codec:a pcm_mulaw -ar 48000 -ac 1"
pcma: "-codec:a pcm_alaw -ar 8000 -ac 1"
pcma/16000: "-codec:a pcm_alaw -ar 16000 -ac 1"
pcma/48000: "-codec:a pcm_alaw -ar 48000 -ac 1"
aac/16000: "-codec:a aac -ar 16000 -ac 1"
h264: "-codec:v libx264 -g 30 -preset superfast -tune zerolatency -profile main -level 4.1"
mycodec: "-any args that support ffmpeg..."
```
#### Source: Exec
@@ -426,3 +423,29 @@ log:
streams: error
webrtc: fatal
```
## Security
By default `go2rtc` start Web interface on port `1984` and RTSP on port `8554`. Both ports are accessible from your local network. So anyone on your local network can watch video from your cameras without authorization. The same rule applies to the Home Assistant Add-on.
This is not a problem if you trust your local network as much as I do. But you can change this behaviour with a `go2rtc.yaml` config:
```yaml
api:
listen: "127.0.0.1:1984" # localhost
rtsp:
listen: "127.0.0.1:8554" # localhost
webrtc:
listen: ":8555" # external TCP port
```
- local access to RTSP is not a problem for [FFmpeg](#source-ffmpeg) integration, because it runs locally on your server
- local access to API is not a problem for [Home Assistant Add-on](#go2rtc-home-assistant-add-on), because Hass runs locally on same server and Add-on Web UI protected with Hass authorization ([Ingress feature](https://www.home-assistant.io/blog/2019/04/15/hassio-ingress/))
- external access to WebRTC TCP port is not a problem, because it used only for transmit encrypted media data
- anyway you need to open this port to your local network and to the Internet in order for WebRTC to work
If you need Web interface protection without Home Assistant Add-on - you need to use reverse proxy, like [Nginx](https://nginx.org/), [Caddy](https://caddyserver.com/), [Ngrok](https://ngrok.com/), etc.
PS. Additionally WebRTC opens a lot of random UDP ports for transmit encrypted media. They work without problems on the local network. And sometimes work for external access, even if you haven't opened ports on your router. But for stable external WebRTC access, you need to configure the TCP port.
+7 -5
View File
@@ -5,16 +5,18 @@ RUN apk add --no-cache git go ffmpeg
ARG BUILD_ARCH
WORKDIR app
RUN git clone https://github.com/AlexxIT/go2rtc .
RUN CGO_ENABLED=0 go build -ldflags "-s -w" -trimpath
RUN git clone https://github.com/AlexxIT/go2rtc \
&& cd go2rtc \
&& CGO_ENABLED=0 go build -ldflags "-s -w" -trimpath -o /usr/local/bin
# https://github.com/home-assistant/docker-base/blob/master/alpine/Dockerfile
RUN if [ "${BUILD_ARCH}" = "aarch64" ]; then BUILD_ARCH="arm64"; \
elif [ "${BUILD_ARCH}" = "armv7" ]; then BUILD_ARCH="arm"; fi \
&& cd go2rtc \
&& curl $(curl -s "https://raw.githubusercontent.com/ngrok/docker-ngrok/main/releases.json" | jq -r ".${BUILD_ARCH}.url") -o ngrok.zip \
&& unzip ngrok
&& unzip ngrok -d /usr/local/bin
RUN rm -r /go2rtc
COPY run.sh /
RUN chmod a+x /run.sh
+7 -6
View File
@@ -2,12 +2,13 @@
set +e
while true; do
if [ -x /config/go2rtc ]; then
/config/go2rtc -config /config/go2rtc.yaml
else
/app/go2rtc -config /config/go2rtc.yaml
fi
# set cwd for go2rtc (for config file, Hass itegration, etc)
cd /config
# add the feature to override go2rtc binary from Hass config folder
export PATH="/config:$PATH"
while true; do
go2rtc
sleep 5
done
+2 -1
View File
@@ -44,6 +44,7 @@ func Init() {
listener, err := net.Listen("tcp", cfg.Mod.Listen)
if err != nil {
log.Fatal().Err(err).Msg("[api] listen")
return
}
log.Info().Str("addr", cfg.Mod.Listen).Msg("[api] listen")
@@ -51,7 +52,7 @@ func Init() {
go func() {
s := http.Server{}
if err = s.Serve(listener); err != nil {
log.Fatal().Err(err).Msg("[api] Serve")
log.Fatal().Err(err).Msg("[api] serve")
}
}()
}
+2 -2
View File
@@ -8,8 +8,8 @@ import (
)
func frameHandler(w http.ResponseWriter, r *http.Request) {
url := r.URL.Query().Get("url")
stream := streams.Get(url)
src := r.URL.Query().Get("src")
stream := streams.Get(src)
if stream == nil {
return
}
+8 -2
View File
@@ -24,7 +24,11 @@ func Init() {
Mod map[string]string `yaml:"log"`
}
LoadConfig(&cfg)
if data != nil {
if err := yaml.Unmarshal(data, &cfg); err != nil {
println("ERROR: " + err.Error())
}
}
var writer io.Writer = os.Stdout
@@ -48,7 +52,9 @@ func Init() {
modules = cfg.Mod
log.Info().Msgf("go2rtc %s/%s", runtime.GOOS, runtime.GOARCH)
path, _ := os.Getwd()
log.Debug().Str("os", runtime.GOOS).Str("arch", runtime.GOARCH).
Str("cwd", path).Int("conf_size", len(data)).Msgf("[app]")
}
func LoadConfig(v interface{}) {
+28 -11
View File
@@ -41,28 +41,45 @@ func Init() {
return
}
ent := new(entries)
if err = json.Unmarshal(data, ent); err != nil {
storage := new(entries)
if err = json.Unmarshal(data, storage); err != nil {
return
}
urls := map[string]string{}
for _, entrie := range ent.Data.Entries {
switch entrie.Domain {
case "generic":
if entrie.Options.StreamSource != "" {
urls[entrie.Title] = entrie.Options.StreamSource
}
}
}
streams.HandleFunc("hass", func(url string) (streamer.Producer, error) {
if hurl := urls[url[5:]]; hurl != "" {
return streams.GetProducer(hurl)
}
return nil, fmt.Errorf("can't get url: %s", url)
})
for _, entrie := range storage.Data.Entries {
switch entrie.Domain {
case "generic":
if entrie.Options.StreamSource == "" {
continue
}
urls[entrie.Title] = entrie.Options.StreamSource
//case "homekit_controller":
// if entrie.Data.ClientID == "" {
// continue
// }
// urls[entrie.Title] = fmt.Sprintf(
// "homekit://%s:%d?client_id=%s&client_private=%s%s&device_id=%s&device_public=%s",
// entrie.Data.DeviceHost, entrie.Data.DevicePort,
// entrie.Data.ClientID, entrie.Data.ClientPrivate, entrie.Data.ClientPublic,
// entrie.Data.DeviceID, entrie.Data.DevicePublic,
// )
default:
continue
}
streams.Get("hass:" + entrie.Title)
}
}
var log zerolog.Logger
+2 -2
View File
@@ -13,8 +13,8 @@ func Init() {
}
func handler(ctx *api.Context, msg *streamer.Message) {
url := ctx.Request.URL.Query().Get("url")
stream := streams.Get(url)
src := ctx.Request.URL.Query().Get("src")
stream := streams.Get(src)
if stream == nil {
return
}
+4 -3
View File
@@ -63,13 +63,13 @@ var log zerolog.Logger
var NewPConn func() (*pion.PeerConnection, error)
func offerHandler(ctx *api.Context, msg *streamer.Message) {
name := ctx.Request.URL.Query().Get("url")
stream := streams.Get(name)
src := ctx.Request.URL.Query().Get("src")
stream := streams.Get(src)
if stream == nil {
return
}
log.Debug().Str("stream", name).Msg("[webrtc] new consumer")
log.Debug().Str("src", src).Msg("[webrtc] new consumer")
var err error
@@ -169,6 +169,7 @@ func ExchangeSDP(
// 2. AddConsumer, so we get new tracks
if err = stream.AddConsumer(conn); err != nil {
log.Warn().Err(err).Msg("[api.webrtc] add consumer")
_ = conn.Conn.Close()
return
}
+7 -4
View File
@@ -451,15 +451,17 @@ func (c *Conn) Accept() error {
return err
}
if c.URL == nil {
c.URL = req.URL
c.UserAgent = req.Header.Get("User-Agent")
}
c.Fire(req)
// Receiver: OPTIONS > DESCRIBE > SETUP... > PLAY > TEARDOWN
// Sender: OPTIONS > ANNOUNCE > SETUP... > RECORD > TEARDOWN
switch req.Method {
case MethodOptions:
c.URL = req.URL
c.UserAgent = req.Header.Get("User-Agent")
res := &tcp.Response{
Header: map[string][]string{
"Public": {"OPTIONS, SETUP, TEARDOWN, DESCRIBE, PLAY, PAUSE, ANNOUNCE, RECORD"},
@@ -638,7 +640,8 @@ func (c *Conn) Handle() (err error) {
_ = track.WriteRTP(packet)
//return fmt.Errorf("wrong channelID: %d", channelID)
} else {
panic("wrong channelID")
continue // TODO: maybe fix this
//panic("wrong channelID")
}
} else {
msg := &RTCP{Channel: channelID}
+20 -20
View File
@@ -1,6 +1,7 @@
package webrtc
import (
"github.com/pion/ice/v2"
"github.com/pion/interceptor"
"github.com/pion/webrtc/v3"
"net"
@@ -21,31 +22,30 @@ func NewAPI(address string) (*webrtc.API, error) {
return nil, err
}
if address == "" {
return webrtc.NewAPI(
webrtc.WithMediaEngine(m),
webrtc.WithInterceptorRegistry(i),
), nil
}
ln, err := net.Listen("tcp", address)
if err != nil {
return webrtc.NewAPI(
webrtc.WithMediaEngine(m),
webrtc.WithInterceptorRegistry(i),
), err
}
s := webrtc.SettingEngine{
//LoggerFactory: customLoggerFactory{},
}
s.SetNetworkTypes([]webrtc.NetworkType{
webrtc.NetworkTypeUDP4, webrtc.NetworkTypeUDP6,
webrtc.NetworkTypeTCP4, webrtc.NetworkTypeTCP6,
// disable listen on Hassio docker interfaces
s.SetInterfaceFilter(func(name string) bool {
return name != "hassio" && name != "docker0"
})
tcpMux := webrtc.NewICETCPMux(nil, ln, 8)
s.SetICETCPMux(tcpMux)
// disable mDNS listener
s.SetICEMulticastDNSMode(ice.MulticastDNSModeDisabled)
if address != "" {
ln, err := net.Listen("tcp", address)
if err == nil {
s.SetNetworkTypes([]webrtc.NetworkType{
webrtc.NetworkTypeUDP4, webrtc.NetworkTypeUDP6,
webrtc.NetworkTypeTCP4, webrtc.NetworkTypeTCP6,
})
tcpMux := webrtc.NewICETCPMux(nil, ln, 8)
s.SetICETCPMux(tcpMux)
}
}
return webrtc.NewAPI(
webrtc.WithMediaEngine(m),
+7
View File
@@ -2,11 +2,18 @@
- UPX-3.96 pack broken bin for `linux_mipsel`
- UPX-3.95 pack broken bin for `mac_amd64`
- UPX windows pack is recognised by anti-viruses as malicious
- `aarch64` = `arm64`
- `armv7` = `arm`
## Virus
- https://go.dev/doc/faq#virus
- https://groups.google.com/g/golang-nuts/c/lPwiWYaApSU
## Useful links
- https://github.com/golang/go/wiki/GoArm
- https://gist.github.com/asukakenji/f15ba7e588ac42795f421b48b8aede63
- https://en.wikipedia.org/wiki/AArch64
- https://stackoverflow.com/questions/22267189/what-does-the-w-flag-mean-when-passed-in-via-the-ldflags-option-to-the-go-comman
+4 -4
View File
@@ -2,13 +2,13 @@
@SET GOOS=windows
@SET GOARCH=amd64
@SET FILENAME=go2rtc_win64.exe
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx-3.96 %FILENAME%
@SET FILENAME=go2rtc_win64.zip
go build -ldflags "-s -w" -trimpath && 7z a -sdel %FILENAME% go2rtc.exe
@SET GOOS=windows
@SET GOARCH=386
@SET FILENAME=go2rtc_win32.exe
go build -ldflags "-s -w" -trimpath -o %FILENAME% && upx-3.96 %FILENAME%
@SET FILENAME=go2rtc_win32.zip
go build -ldflags "-s -w" -trimpath && 7z a -sdel %FILENAME% go2rtc.exe
@SET GOOS=linux
@SET GOARCH=amd64
+5
View File
@@ -0,0 +1,5 @@
@ECHO OFF
@SET GOOS=linux
@SET GOARCH=mipsle
cd ..
go build -ldflags "-s -w" -trimpath && upx-3.95 go2rtc
+1 -1
View File
@@ -1,4 +1,4 @@
@SET GOOS=windows
@SET GOARCH=amd64
cd ..
go build -ldflags "-s -w" -trimpath && upx-3.96 go2rtc.exe
go build -ldflags "-w -s" -trimpath
+3 -3
View File
@@ -63,9 +63,9 @@
);
const links = [
'<a href="webrtc.html?url={name}">webrtc</a>',
'<a href="mse.html?url={name}">mse</a>',
'<a href="api/frame.mp4?url={name}">frame.mp4</a>',
'<a href="webrtc.html?src={name}">webrtc</a>',
'<a href="mse.html?src={name}">mse</a>',
'<a href="api/frame.mp4?src={name}">frame.mp4</a>',
'<a href="api/streams?src={name}">info</a>',
];