Fixes #2, allow server goroutines to exit on deadlines and context cancel.

This commit is contained in:
Jonas Köritz 2019-10-15 12:59:06 +02:00
parent 03ca0f77e6
commit e82f5a97c9
5 changed files with 108 additions and 58 deletions

View file

@ -61,7 +61,10 @@ type StoredFile struct {
}
// CreateCamera creates a new Camera instance
func CreateCamera(ipAddress net.IP, port int, username, password string) *Camera {
func CreateCamera(ipAddress net.IP, port int, username, password string) (*Camera, error) {
if ipAddress == nil {
return nil, errors.New("Cannot create camera without an IP-Address")
}
camera := &Camera{
ipAddress: ipAddress,
port: port,
@ -70,7 +73,7 @@ func CreateCamera(ipAddress net.IP, port int, username, password string) *Camera
messageHandlers: make(map[uint32][]MessageHandler, 0),
verbose: true,
}
return camera
return camera, nil
}
// Connect to the camera and start responding to keepalive packets

View file

@ -13,6 +13,8 @@ var targetPorts = []int{22600, 21600}
// AutodiscoverCamera will try to find a camera using UDP Broadcasts
func AutodiscoverCamera(verbose bool) (net.IP, error) {
conn, err := net.ListenPacket("udp", ":22601")
conn.SetReadDeadline(time.Now().Add(5 * time.Second))
if err != nil {
return nil, err
}

View file

@ -2,11 +2,13 @@ package libipcamera
import (
"bytes"
"context"
"encoding/binary"
"encoding/hex"
"io"
"log"
"net"
"time"
)
// RTPRelay holds information on the relaying stream listener
@ -15,11 +17,12 @@ type RTPRelay struct {
targetIP net.IP
targetPort int
listener net.PacketConn
context context.Context
}
// CreateRTPRelay creates a UDP listener that handles live data
// from the camera and forwards it as an RTP stream
func CreateRTPRelay(targetAddress net.IP, targetPort int) *RTPRelay {
func CreateRTPRelay(ctx context.Context, targetAddress net.IP, targetPort int) *RTPRelay {
conn, err := net.ListenPacket("udp", ":6669")
if err != nil {
@ -30,6 +33,7 @@ func CreateRTPRelay(targetAddress net.IP, targetPort int) *RTPRelay {
targetIP: targetAddress,
targetPort: targetPort,
listener: conn,
context: ctx,
}
if err != nil {
log.Printf("ERROR: %s\n", err)
@ -64,60 +68,70 @@ func handleCameraStream(relay RTPRelay, conn net.PacketConn) {
packetBuffer := bytes.Buffer{}
for {
if relay.close {
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
select {
case <-relay.context.Done():
log.Println("Context Done")
rtpConn.Close()
relay.listener.Close()
break
}
conn.ReadFrom(buffer)
packetReader.Reset(buffer)
binary.Read(packetReader, binary.BigEndian, &header)
if header.Magic != 0xBCDE {
log.Printf("Received message with invalid magic (%x).", header.Magic)
break
}
if header.Length > 0 {
payload = make([]byte, header.Length)
_, err := io.ReadFull(packetReader, payload)
if err != nil {
log.Printf("Read Error: %s\n", err)
default:
if relay.close {
rtpConn.Close()
relay.listener.Close()
break
}
} else {
payload = []byte{}
}
switch header.MessageType {
case 0x0001: // H.264 Data
frameBuffer.Write(payload)
case 0x0002: // Time
// Append the Framebuffer
packetBuffer.Write(frameBuffer.Bytes())
conn.ReadFrom(buffer)
packetReader.Reset(buffer)
// Send out the packet
rtpConn.Write(packetBuffer.Bytes())
binary.Read(packetReader, binary.BigEndian, &header)
// Prepare the next packet
packetBuffer.Reset()
packetBuffer.Write([]byte{0x80, 0x63})
binary.Write(&packetBuffer, binary.BigEndian, sequenceNumber+1)
binary.Write(&packetBuffer, binary.BigEndian, (uint32)(elapsed)*90)
binary.Write(&packetBuffer, binary.BigEndian, (uint64(0)))
if header.Magic != 0xBCDE {
log.Printf("Received message with invalid magic (%x).", header.Magic)
break
}
// Reset the Framebuffer
frameBuffer.Reset()
sequenceNumber++
if header.Length > 0 {
payload = make([]byte, header.Length)
_, err := io.ReadFull(packetReader, payload)
if err != nil {
log.Printf("Read Error: %s\n", err)
break
}
} else {
payload = []byte{}
}
elapsed = binary.LittleEndian.Uint32(payload[12:])
default:
log.Printf("Received Unknown Message: %+v\n", header)
log.Printf("Payload:\n%s\n", hex.Dump(payload))
switch header.MessageType {
case 0x0001: // H.264 Data
frameBuffer.Write(payload)
case 0x0002: // Time
// Append the Framebuffer
packetBuffer.Write(frameBuffer.Bytes())
// Send out the packet
rtpConn.Write(packetBuffer.Bytes())
// Prepare the next packet
packetBuffer.Reset()
packetBuffer.Write([]byte{0x80, 0x63})
binary.Write(&packetBuffer, binary.BigEndian, sequenceNumber+1)
binary.Write(&packetBuffer, binary.BigEndian, (uint32)(elapsed)*90)
binary.Write(&packetBuffer, binary.BigEndian, (uint64(0)))
// Reset the Framebuffer
frameBuffer.Reset()
sequenceNumber++
elapsed = binary.LittleEndian.Uint32(payload[12:])
default:
log.Printf("Received Unknown Message: %+v\n", header)
log.Printf("Payload:\n%s\n", hex.Dump(payload))
}
}
}
rtpConn.Close()
}
// Stop stops listening for packets