Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a87dafbbec | |||
| 742cb7699b | |||
| 43449e7b08 | |||
| 33512e73bd | |||
| b367ffee6d | |||
| 69447df6b3 | |||
| a6eac4ff02 | |||
| 1eaf879a76 | |||
| c9ae6dcc03 |
@@ -23,7 +23,7 @@ Ultimate camera streaming application with support RTSP, WebRTC, HomeKit, FFmpeg
|
|||||||
- mixing tracks from different sources to single stream
|
- mixing tracks from different sources to single stream
|
||||||
- auto match client supported codecs
|
- auto match client supported codecs
|
||||||
- [2-way audio](#two-way-audio) for some cameras
|
- [2-way audio](#two-way-audio) for some cameras
|
||||||
- streaming from private networks via [Ngrok](#module-ngrok)
|
- streaming from private networks via [ngrok](#module-ngrok)
|
||||||
- can be [integrated to](#module-api) any smart home platform or be used as [standalone app](#go2rtc-binary)
|
- can be [integrated to](#module-api) any smart home platform or be used as [standalone app](#go2rtc-binary)
|
||||||
|
|
||||||
**Inspired by:**
|
**Inspired by:**
|
||||||
@@ -77,7 +77,7 @@ Ultimate camera streaming application with support RTSP, WebRTC, HomeKit, FFmpeg
|
|||||||
* [Module: WebRTC](#module-webrtc)
|
* [Module: WebRTC](#module-webrtc)
|
||||||
* [Module: HomeKit](#module-homekit)
|
* [Module: HomeKit](#module-homekit)
|
||||||
* [Module: WebTorrent](#module-webtorrent)
|
* [Module: WebTorrent](#module-webtorrent)
|
||||||
* [Module: Ngrok](#module-ngrok)
|
* [Module: ngrok](#module-ngrok)
|
||||||
* [Module: Hass](#module-hass)
|
* [Module: Hass](#module-hass)
|
||||||
* [Module: MP4](#module-mp4)
|
* [Module: MP4](#module-mp4)
|
||||||
* [Module: HLS](#module-hls)
|
* [Module: HLS](#module-hls)
|
||||||
@@ -127,7 +127,7 @@ Don't forget to fix the rights `chmod +x go2rtc_xxx_xxx` on Linux and Mac.
|
|||||||
|
|
||||||
### go2rtc: Docker
|
### go2rtc: Docker
|
||||||
|
|
||||||
Container [alexxit/go2rtc](https://hub.docker.com/r/alexxit/go2rtc) with support `amd64`, `386`, `arm64`, `arm`. This container is the same as [Home Assistant Add-on](#go2rtc-home-assistant-add-on), but can be used separately from Home Assistant. Container has preinstalled [FFmpeg](#source-ffmpeg), [Ngrok](#module-ngrok) and [Python](#source-echo).
|
Container [alexxit/go2rtc](https://hub.docker.com/r/alexxit/go2rtc) with support `amd64`, `386`, `arm64`, `arm`. This container is the same as [Home Assistant Add-on](#go2rtc-home-assistant-add-on), but can be used separately from Home Assistant. Container has preinstalled [FFmpeg](#source-ffmpeg), [ngrok](#module-ngrok) and [Python](#source-echo).
|
||||||
|
|
||||||
### go2rtc: Home Assistant Add-on
|
### go2rtc: Home Assistant Add-on
|
||||||
|
|
||||||
@@ -170,7 +170,7 @@ Available modules:
|
|||||||
- [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
|
||||||
- [ngrok](#module-ngrok) - Ngrok integration (external access for private network)
|
- [ngrok](#module-ngrok) - ngrok integration (external access for private network)
|
||||||
- [hass](#module-hass) - Home Assistant integration
|
- [hass](#module-hass) - Home Assistant integration
|
||||||
- [log](#module-log) - logs config
|
- [log](#module-log) - logs config
|
||||||
|
|
||||||
@@ -861,7 +861,7 @@ webrtc:
|
|||||||
|
|
||||||
**Private IP**
|
**Private IP**
|
||||||
|
|
||||||
- setup integration with [Ngrok service](#module-ngrok)
|
- setup integration with [ngrok service](#module-ngrok)
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
ngrok:
|
ngrok:
|
||||||
@@ -963,29 +963,29 @@ Link example: https://alexxit.github.io/go2rtc/#share=02SNtgjKXY&pwd=wznEQqznxW&
|
|||||||
|
|
||||||
TODO: article how it works...
|
TODO: article how it works...
|
||||||
|
|
||||||
### Module: Ngrok
|
### Module: ngrok
|
||||||
|
|
||||||
With Ngrok integration you can get external access to your streams in situation when you have Internet with private IP-address.
|
With ngrok integration you can get external access to your streams in situations when you have Internet with private IP-address.
|
||||||
|
|
||||||
- Ngrok preistalled for **Docker** and **Hass Add-on** users
|
- ngrok is pre-installed for **Docker** and **Hass Add-on** users
|
||||||
- you may need external access for two different things:
|
- you may need external access for two different things:
|
||||||
- WebRTC stream, so you need tunnel WebRTC TCP port (ex. 8555)
|
- WebRTC stream, so you need tunnel WebRTC TCP port (ex. 8555)
|
||||||
- go2rtc web interface, so you need tunnel API HTTP port (ex. 1984)
|
- go2rtc web interface, so you need tunnel API HTTP port (ex. 1984)
|
||||||
- Ngrok support authorization for your web interface
|
- ngrok support authorization for your web interface
|
||||||
- Ngrok automatically adds HTTPS to your web interface
|
- ngrok automatically adds HTTPS to your web interface
|
||||||
|
|
||||||
Ngrok free subscription limitations:
|
The ngrok free subscription has the following limitations:
|
||||||
|
|
||||||
- you will always get random external address (not a problem for webrtc stream)
|
- You can reserve a free domain for serving the web interface, but the TCP address you get will always be random and change with each restart of the ngrok agent (not a problem for webrtc stream)
|
||||||
- you can forward multiple ports but use only one Ngrok app
|
- You can forward multiple ports from a single agent, but you can only run one ngrok agent on the free plan
|
||||||
|
|
||||||
go2rtc will automatically get your external TCP address (if you enable it in ngrok config) and use it with WebRTC connection (if you enable it in webrtc config).
|
go2rtc will automatically get your external TCP address (if you enable it in ngrok config) and use it with WebRTC connection (if you enable it in webrtc config).
|
||||||
|
|
||||||
You need manually download [Ngrok agent app](https://ngrok.com/download) for your OS and register in [Ngrok service](https://ngrok.com/).
|
You need to manually download the [ngrok agent app](https://ngrok.com/download) for your OS and register with the [ngrok service](https://ngrok.com/signup).
|
||||||
|
|
||||||
**Tunnel for only WebRTC Stream**
|
**Tunnel for only WebRTC Stream**
|
||||||
|
|
||||||
You need to add your [Ngrok token](https://dashboard.ngrok.com/get-started/your-authtoken) and WebRTC TCP port to YAML:
|
You need to add your [ngrok authtoken](https://dashboard.ngrok.com/get-started/your-authtoken) and WebRTC TCP port to YAML:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
ngrok:
|
ngrok:
|
||||||
@@ -1001,7 +1001,7 @@ ngrok:
|
|||||||
command: ngrok start --all --config ngrok.yaml
|
command: ngrok start --all --config ngrok.yaml
|
||||||
```
|
```
|
||||||
|
|
||||||
Ngrok config example:
|
ngrok config example:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: "2"
|
version: "2"
|
||||||
@@ -1017,6 +1017,8 @@ tunnels:
|
|||||||
proto: tcp
|
proto: tcp
|
||||||
```
|
```
|
||||||
|
|
||||||
|
See the [ngrok agent documentation](https://ngrok.com/docs/agent/config/) for more details on the ngrok configuration file.
|
||||||
|
|
||||||
### Module: Hass
|
### Module: Hass
|
||||||
|
|
||||||
The best and easiest way to use go2rtc inside the Home Assistant is to install the custom integration [WebRTC Camera](#go2rtc-home-assistant-integration) and custom lovelace card.
|
The best and easiest way to use go2rtc inside the Home Assistant is to install the custom integration [WebRTC Camera](#go2rtc-home-assistant-integration) and custom lovelace card.
|
||||||
@@ -1156,7 +1158,7 @@ webrtc:
|
|||||||
- external access to WebRTC TCP port is not a problem, because it used only for transmit encrypted media data
|
- 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
|
- 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.
|
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 will try to use the 8555 UDP port for transmit encrypted media. It works without problems on the local network. And sometimes also works for external access, even if you haven't opened this port on your router ([read more](https://en.wikipedia.org/wiki/UDP_hole_punching)). But for stable external WebRTC access, you need to open the 8555 port on your router for both TCP and UDP.
|
PS. Additionally WebRTC will try to use the 8555 UDP port for transmit encrypted media. It works without problems on the local network. And sometimes also works for external access, even if you haven't opened this port on your router ([read more](https://en.wikipedia.org/wiki/UDP_hole_punching)). But for stable external WebRTC access, you need to open the 8555 port on your router for both TCP and UDP.
|
||||||
|
|
||||||
@@ -1298,6 +1300,7 @@ streams:
|
|||||||
**Distributions**
|
**Distributions**
|
||||||
|
|
||||||
- [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=go2rtc)
|
- [Alpine Linux](https://pkgs.alpinelinux.org/packages?name=go2rtc)
|
||||||
|
- [Gentoo](https://github.com/inode64/inode64-overlay/tree/main/media-video/go2rtc)
|
||||||
- [NixOS](https://search.nixos.org/packages?query=go2rtc)
|
- [NixOS](https://search.nixos.org/packages?query=go2rtc)
|
||||||
- [Proxmox Helper Scripts](https://tteck.github.io/Proxmox/)
|
- [Proxmox Helper Scripts](https://tteck.github.io/Proxmox/)
|
||||||
- [QNAP](https://www.myqnap.org/product/go2rtc/)
|
- [QNAP](https://www.myqnap.org/product/go2rtc/)
|
||||||
|
|||||||
+5
-7
@@ -91,6 +91,10 @@ func listen(network, address string) {
|
|||||||
|
|
||||||
log.Info().Str("addr", address).Msg("[api] listen")
|
log.Info().Str("addr", address).Msg("[api] listen")
|
||||||
|
|
||||||
|
if network == "tcp" {
|
||||||
|
Port = ln.Addr().(*net.TCPAddr).Port
|
||||||
|
}
|
||||||
|
|
||||||
server := http.Server{Handler: Handler}
|
server := http.Server{Handler: Handler}
|
||||||
if err = server.Serve(ln); err != nil {
|
if err = server.Serve(ln); err != nil {
|
||||||
log.Fatal().Err(err).Msg("[api] serve")
|
log.Fatal().Err(err).Msg("[api] serve")
|
||||||
@@ -129,12 +133,7 @@ func tlsListen(network, address, certFile, keyFile string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Port() int {
|
var Port int
|
||||||
if ln == nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
return ln.Addr().(*net.TCPAddr).Port
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MimeJSON = "application/json"
|
MimeJSON = "application/json"
|
||||||
@@ -217,7 +216,6 @@ func middlewareCORS(next http.Handler) http.Handler {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
var ln net.Listener
|
|
||||||
var mu sync.Mutex
|
var mu sync.Mutex
|
||||||
|
|
||||||
func apiHandler(w http.ResponseWriter, r *http.Request) {
|
func apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ import (
|
|||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Version = "1.8.3"
|
var Version = "1.8.4"
|
||||||
var UserAgent = "go2rtc/" + Version
|
var UserAgent = "go2rtc/" + Version
|
||||||
|
|
||||||
var ConfigPath string
|
var ConfigPath string
|
||||||
|
|||||||
@@ -98,7 +98,7 @@ func Init() {
|
|||||||
|
|
||||||
srv.mdns = &mdns.ServiceEntry{
|
srv.mdns = &mdns.ServiceEntry{
|
||||||
Name: name,
|
Name: name,
|
||||||
Port: uint16(api.Port()),
|
Port: uint16(api.Port),
|
||||||
Info: map[string]string{
|
Info: map[string]string{
|
||||||
hap.TXTConfigNumber: "1",
|
hap.TXTConfigNumber: "1",
|
||||||
hap.TXTFeatureFlags: "0",
|
hap.TXTFeatureFlags: "0",
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ func main() {
|
|||||||
|
|
||||||
// 6. Helper modules
|
// 6. Helper modules
|
||||||
|
|
||||||
ngrok.Init() // Ngrok module
|
ngrok.Init() // ngrok module
|
||||||
srtp.Init() // SRTP server
|
srtp.Init() // SRTP server
|
||||||
debug.Init() // debug API
|
debug.Init() // debug API
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func IsADTS(b []byte) bool {
|
func IsADTS(b []byte) bool {
|
||||||
_ = b[1]
|
_ = b[1]
|
||||||
return len(b) > 7 && b[0] == 0xFF && b[1]&0xF0 == 0xF0
|
return len(b) > 7 && b[0] == 0xFF && b[1]&0xF6 == 0xF0
|
||||||
}
|
}
|
||||||
|
|
||||||
func ADTSToCodec(b []byte) *core.Codec {
|
func ADTSToCodec(b []byte) *core.Codec {
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
package aac
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"encoding/binary"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||||
|
"github.com/pion/rtp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Producer struct {
|
||||||
|
core.SuperProducer
|
||||||
|
rd *bufio.Reader
|
||||||
|
cl io.Closer
|
||||||
|
}
|
||||||
|
|
||||||
|
func Open(r io.Reader) (*Producer, error) {
|
||||||
|
rd := bufio.NewReader(r)
|
||||||
|
|
||||||
|
b, err := rd.Peek(8)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
codec := ADTSToCodec(b)
|
||||||
|
|
||||||
|
prod := &Producer{rd: rd, cl: r.(io.Closer)}
|
||||||
|
prod.Type = "ADTS producer"
|
||||||
|
prod.Medias = []*core.Media{
|
||||||
|
{
|
||||||
|
Kind: core.KindAudio,
|
||||||
|
Direction: core.DirectionRecvonly,
|
||||||
|
Codecs: []*core.Codec{codec},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
return prod, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Producer) Start() error {
|
||||||
|
for {
|
||||||
|
b, err := c.rd.Peek(6)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
auSize := ReadADTSSize(b)
|
||||||
|
payload := make([]byte, 2+2+auSize)
|
||||||
|
if _, err = io.ReadFull(c.rd, payload[4:]); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Recv += int(auSize)
|
||||||
|
|
||||||
|
if len(c.Receivers) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
payload[1] = 16 // header size in bits
|
||||||
|
binary.BigEndian.PutUint16(payload[2:], auSize<<3)
|
||||||
|
|
||||||
|
pkt := &rtp.Packet{
|
||||||
|
Header: rtp.Header{Timestamp: core.Now90000()},
|
||||||
|
Payload: payload,
|
||||||
|
}
|
||||||
|
c.Receivers[0].WriteRTP(pkt)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Producer) Stop() error {
|
||||||
|
_ = c.SuperProducer.Close()
|
||||||
|
return c.cl.Close()
|
||||||
|
}
|
||||||
+16
-1
@@ -6,6 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
|
|
||||||
|
"github.com/AlexxIT/go2rtc/pkg/aac"
|
||||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||||
"github.com/AlexxIT/go2rtc/pkg/flv"
|
"github.com/AlexxIT/go2rtc/pkg/flv"
|
||||||
"github.com/AlexxIT/go2rtc/pkg/h264/annexb"
|
"github.com/AlexxIT/go2rtc/pkg/h264/annexb"
|
||||||
@@ -33,6 +34,9 @@ func Open(r io.Reader) (core.Producer, error) {
|
|||||||
case bytes.HasPrefix(b, []byte(flv.Signature)):
|
case bytes.HasPrefix(b, []byte(flv.Signature)):
|
||||||
return flv.Open(rd)
|
return flv.Open(rd)
|
||||||
|
|
||||||
|
case bytes.HasPrefix(b, []byte{0xFF, 0xF1}):
|
||||||
|
return aac.Open(rd)
|
||||||
|
|
||||||
case bytes.HasPrefix(b, []byte("--")):
|
case bytes.HasPrefix(b, []byte("--")):
|
||||||
return multipart.Open(rd)
|
return multipart.Open(rd)
|
||||||
|
|
||||||
@@ -40,5 +44,16 @@ func Open(r io.Reader) (core.Producer, error) {
|
|||||||
return mpegts.Open(rd)
|
return mpegts.Open(rd)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New("magic: unsupported header: " + hex.EncodeToString(b))
|
// support MJPEG with trash on start
|
||||||
|
// https://github.com/AlexxIT/go2rtc/issues/747
|
||||||
|
if b, err = rd.Peek(4096); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if i := bytes.Index(b, []byte{0xFF, 0xD8, 0xFF, 0xDB}); i > 0 {
|
||||||
|
_, _ = io.ReadFull(rd, make([]byte, i))
|
||||||
|
return mjpeg.Open(rd)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, errors.New("magic: unsupported header: " + hex.EncodeToString(b[:4]))
|
||||||
}
|
}
|
||||||
|
|||||||
+17
-5
@@ -22,8 +22,6 @@ func Do(req *http.Request) (*http.Response, error) {
|
|||||||
case "https":
|
case "https":
|
||||||
if hostname := req.URL.Hostname(); IsIP(hostname) {
|
if hostname := req.URL.Hostname(); IsIP(hostname) {
|
||||||
secure = &tls.Config{InsecureSkipVerify: true}
|
secure = &tls.Config{InsecureSkipVerify: true}
|
||||||
} else {
|
|
||||||
secure = &tls.Config{ServerName: hostname}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,11 +46,21 @@ func Do(req *http.Request) (*http.Response, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
secure := ctx.Value(connKey).(*tls.Config)
|
|
||||||
tlsConn := tls.Client(conn, secure)
|
var conf *tls.Config
|
||||||
|
if v, ok := ctx.Value(secureKey).(*tls.Config); ok {
|
||||||
|
conf = v
|
||||||
|
} else if host, _, err := net.SplitHostPort(addr); err != nil {
|
||||||
|
conf = &tls.Config{ServerName: addr}
|
||||||
|
} else {
|
||||||
|
conf = &tls.Config{ServerName: host}
|
||||||
|
}
|
||||||
|
|
||||||
|
tlsConn := tls.Client(conn, conf)
|
||||||
if err = tlsConn.Handshake(); err != nil {
|
if err = tlsConn.Handshake(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if pconn, ok := ctx.Value(connKey).(*net.Conn); ok {
|
if pconn, ok := ctx.Value(connKey).(*net.Conn); ok {
|
||||||
*pconn = tlsConn
|
*pconn = tlsConn
|
||||||
}
|
}
|
||||||
@@ -128,7 +136,11 @@ func Do(req *http.Request) (*http.Response, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var client *http.Client
|
var client *http.Client
|
||||||
var connKey, secureKey struct{}
|
|
||||||
|
type key string
|
||||||
|
|
||||||
|
var connKey = key("conn")
|
||||||
|
var secureKey = key("secure")
|
||||||
|
|
||||||
func WithConn() (context.Context, *net.Conn) {
|
func WithConn() (context.Context, *net.Conn) {
|
||||||
pconn := new(net.Conn)
|
pconn := new(net.Conn)
|
||||||
|
|||||||
Reference in New Issue
Block a user