Create publisher for remote srt stream

This commit is contained in:
Ingo Oppermann
2022-08-12 18:42:53 +03:00
parent c04ab1e82f
commit b51a38c99e
331 changed files with 18224 additions and 12383 deletions

View File

@@ -5,7 +5,7 @@ all: build
## test: Run all tests
test:
go test -race -coverprofile=/dev/null -timeout 15s -v ./...
go test -race -coverprofile=/dev/null -timeout 60s -v ./...
## vet: Analyze code for potential errors
vet:
@@ -43,7 +43,7 @@ server:
## coverage: Generate code coverage analysis
coverage:
go test -race -coverprofile=cover.out -timeout 15s -v ./...
go test -race -coverprofile=cover.out -timeout 60s -v ./...
go tool cover -html=cover.out -o cover.html
## commit: Prepare code for commit (vet, fmt, test)

View File

@@ -218,17 +218,17 @@ func DefaultConfig() Config {
// UnmarshalURL takes a SRT URL and parses out the configuration. A SRT URL is
// srt://[host]:[port]?[key1]=[value1]&[key2]=[value2]...
func (c *Config) UnmarshalURL(addr string) error {
func (c *Config) UnmarshalURL(addr string) (string, error) {
u, err := url.Parse(addr)
if err != nil {
return err
return "", err
}
if u.Scheme != "srt" {
return fmt.Errorf("the URL doesn't seem to be an srt:// URL")
return "", fmt.Errorf("the URL doesn't seem to be an srt:// URL")
}
return c.UnmarshalQuery(u.RawQuery)
return u.Host, c.UnmarshalQuery(u.RawQuery)
}
// UnmarshalQuery parses a query string and interprets it as a configuration

View File

@@ -93,6 +93,7 @@ type srtConn struct {
config Config
cryptoLock sync.Mutex
crypto crypto.Crypto
keyBaseEncryption packet.PacketEncryption
kmPreAnnounceCountdown uint64
@@ -453,6 +454,7 @@ func (c *srtConn) pop(p packet.Packet) {
p.Header().DestinationSocketId = c.peerSocketId
if !p.Header().IsControlPacket {
c.cryptoLock.Lock()
if c.crypto != nil {
p.Header().KeyBaseEncryptionFlag = c.keyBaseEncryption
c.crypto.EncryptOrDecryptPayload(p.Data(), p.Header().KeyBaseEncryptionFlag, p.Header().PacketSequenceNumber.Val())
@@ -483,6 +485,7 @@ func (c *srtConn) pop(p packet.Packet) {
c.crypto.GenerateSEK(c.keyBaseEncryption.Opposite())
}
}
c.cryptoLock.Unlock()
c.log("data:send:dump", func() string { return p.Dump() })
}
@@ -614,6 +617,7 @@ func (c *srtConn) handlePacket(p packet.Packet) {
c.log("data:recv:dump", func() string { return p.Dump() })
c.cryptoLock.Lock()
if c.crypto != nil {
if header.KeyBaseEncryptionFlag != 0 {
if err := c.crypto.EncryptOrDecryptPayload(p.Data(), header.KeyBaseEncryptionFlag, header.PacketSequenceNumber.Val()); err != nil {
@@ -625,6 +629,7 @@ func (c *srtConn) handlePacket(p packet.Packet) {
c.statistics.byteRecvUndecrypt += p.Len()
}
}
c.cryptoLock.Unlock()
// Put the packet into receive congestion control
c.recv.Push(p)
@@ -759,8 +764,11 @@ func (c *srtConn) handleKMRequest(p packet.Packet) {
c.statistics.pktRecvKM++
c.cryptoLock.Lock()
if c.crypto == nil {
c.log("control:recv:KM:error", func() string { return "connection is not encrypted" })
c.cryptoLock.Unlock()
return
}
@@ -769,6 +777,7 @@ func (c *srtConn) handleKMRequest(p packet.Packet) {
if err := p.UnmarshalCIF(cif); err != nil {
c.statistics.pktRecvInvalid++
c.log("control:recv:KM:error", func() string { return fmt.Sprintf("invalid KM: %s", err) })
c.cryptoLock.Unlock()
return
}
@@ -779,15 +788,23 @@ func (c *srtConn) handleKMRequest(p packet.Packet) {
c.log("control:recv:KM:error", func() string {
return "invalid KM. wants to reset the key that is already in use"
})
c.cryptoLock.Unlock()
return
}
if err := c.crypto.UnmarshalKM(cif, c.config.Passphrase); err != nil {
c.statistics.pktRecvInvalid++
c.log("control:recv:KM:error", func() string { return fmt.Sprintf("invalid KM: %s", err) })
c.cryptoLock.Unlock()
return
}
// Switch the keys
c.keyBaseEncryption = c.keyBaseEncryption.Opposite()
c.cryptoLock.Unlock()
// Send KM Response
p.Header().SubType = packet.EXTTYPE_KMRSP
c.statistics.pktSentKM++
@@ -801,6 +818,9 @@ func (c *srtConn) handleKMResponse(p packet.Packet) {
c.statistics.pktRecvKM++
c.cryptoLock.Lock()
defer c.cryptoLock.Unlock()
if c.crypto == nil {
c.log("control:recv:KM:error", func() string { return "connection is not encrypted" })
return

View File

@@ -66,7 +66,8 @@ type connResponse struct {
// The address is of the form "host:port".
//
// Example:
// Dial("srt", "127.0.0.1:3000", DefaultConfig())
//
// Dial("srt", "127.0.0.1:3000", DefaultConfig())
//
// In case of an error the returned Conn is nil and the error is non-nil.
func Dial(network, address string, config Config) (Conn, error) {
@@ -663,6 +664,17 @@ func (dl *dialer) Read(p []byte) (n int, err error) {
return dl.conn.Read(p)
}
func (dl *dialer) readPacket() (packet.Packet, error) {
if err := dl.checkConnection(); err != nil {
return nil, err
}
dl.connLock.RLock()
defer dl.connLock.RUnlock()
return dl.conn.readPacket()
}
func (dl *dialer) Write(p []byte) (n int, err error) {
if err := dl.checkConnection(); err != nil {
return 0, err
@@ -674,6 +686,17 @@ func (dl *dialer) Write(p []byte) (n int, err error) {
return dl.conn.Write(p)
}
func (dl *dialer) writePacket(p packet.Packet) error {
if err := dl.checkConnection(); err != nil {
return err
}
dl.connLock.RLock()
defer dl.connLock.RUnlock()
return dl.conn.writePacket(p)
}
func (dl *dialer) SetDeadline(t time.Time) error { return dl.conn.SetDeadline(t) }
func (dl *dialer) SetReadDeadline(t time.Time) error { return dl.conn.SetReadDeadline(t) }
func (dl *dialer) SetWriteDeadline(t time.Time) error { return dl.conn.SetWriteDeadline(t) }

View File

@@ -12,7 +12,7 @@ import (
// PubSub is a publish/subscriber service for SRT connections.
type PubSub interface {
// Publish accepts a SRT connection where it reads from. It blocks
// until the connection closes. The returned error indicated why it
// until the connection closes. The returned error indicates why it
// stopped. There can be only one publisher.
Publish(c Conn) error
@@ -23,6 +23,11 @@ type PubSub interface {
Subscribe(c Conn) error
}
type packetReadWriter interface {
readPacket() (packet.Packet, error)
writePacket(p packet.Packet) error
}
// pubSub is an implementation of the PubSub interface
type pubSub struct {
incoming chan packet.Packet
@@ -102,28 +107,30 @@ func (pb *pubSub) Publish(c Conn) error {
var p packet.Packet
var err error
conn, ok := c.(*srtConn)
conn, ok := c.(packetReadWriter)
if !ok {
err := fmt.Errorf("the provided connection is not a SRT connection")
pb.logger.Print("pubsub:error", 0, 1, func() string { return err.Error() })
return err
}
pb.logger.Print("pubsub:publish", conn.SocketId(), 1, func() string { return "new publisher" })
socketId := c.SocketId()
pb.logger.Print("pubsub:publish", socketId, 1, func() string { return "new publisher" })
pb.publish = true
for {
p, err = conn.readPacket()
if err != nil {
pb.logger.Print("pubsub:error", conn.SocketId(), 1, func() string { return err.Error() })
pb.logger.Print("pubsub:error", socketId, 1, func() string { return err.Error() })
break
}
select {
case pb.incoming <- p:
default:
pb.logger.Print("pubsub:error", conn.SocketId(), 1, func() string { return "incoming queue is full" })
pb.logger.Print("pubsub:error", socketId, 1, func() string { return "incoming queue is full" })
}
}
@@ -135,7 +142,7 @@ func (pb *pubSub) Publish(c Conn) error {
func (pb *pubSub) Subscribe(c Conn) error {
l := make(chan packet.Packet, 1024)
socketId := c.SocketId()
conn, ok := c.(*srtConn)
conn, ok := c.(packetReadWriter)
if !ok {
err := fmt.Errorf("the provided connection is not a SRT connection")
pb.logger.Print("pubsub:error", 0, 1, func() string { return err.Error() })