Skip to content

Commit ed5e1c7

Browse files
committed
implement publishing via tcp
1 parent b94cbae commit ed5e1c7

File tree

4 files changed

+153
-86
lines changed

4 files changed

+153
-86
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Features:
1212
* Supports reading and publishing streams
1313
* Supports one publisher at once, while readers can be more than one.
1414
* Supports reading via UDP and TCP
15+
* Supports publishing via UDP and TCP
1516

1617

1718
<br />

main.go

Lines changed: 46 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -36,52 +36,12 @@ func newProgram(rtspPort int, rtpPort int, rtcpPort int) (*program, error) {
3636

3737
var err error
3838

39-
p.rtpl, err = newUdpListener(rtpPort, "RTP", func(l *udpListener, buf []byte) {
40-
p.mutex.RLock()
41-
defer p.mutex.RUnlock()
42-
43-
tcpHeader := [4]byte{0x24, 0x00, 0x00, 0x00}
44-
binary.BigEndian.PutUint16(tcpHeader[2:], uint16(len(buf)))
45-
46-
for c := range p.clients {
47-
if c.state == "PLAY" {
48-
if c.rtpProto == "udp" {
49-
l.nconn.WriteTo(buf, &net.UDPAddr{
50-
IP: c.IP,
51-
Port: c.rtpPort,
52-
})
53-
} else {
54-
c.nconn.Write(tcpHeader[:])
55-
c.nconn.Write(buf)
56-
}
57-
}
58-
}
59-
})
39+
p.rtpl, err = newUdpListener(rtpPort, "RTP", p.handleRtp)
6040
if err != nil {
6141
return nil, err
6242
}
6343

64-
p.rtcpl, err = newUdpListener(rtcpPort, "RTCP", func(l *udpListener, buf []byte) {
65-
p.mutex.RLock()
66-
defer p.mutex.RUnlock()
67-
68-
tcpHeader := [4]byte{0x24, 0x00, 0x00, 0x00}
69-
binary.BigEndian.PutUint16(tcpHeader[2:], uint16(len(buf)))
70-
71-
for c := range p.clients {
72-
if c.state == "PLAY" {
73-
if c.rtpProto == "udp" {
74-
l.nconn.WriteTo(buf, &net.UDPAddr{
75-
IP: c.IP,
76-
Port: c.rtcpPort,
77-
})
78-
} else {
79-
c.nconn.Write(tcpHeader[:])
80-
c.nconn.Write(buf)
81-
}
82-
}
83-
}
84-
})
44+
p.rtcpl, err = newUdpListener(rtcpPort, "RTCP", p.handleRtcp)
8545
if err != nil {
8646
return nil, err
8747
}
@@ -109,6 +69,50 @@ func (p *program) run() {
10969
wg.Wait()
11070
}
11171

72+
func (p *program) handleRtp(buf []byte) {
73+
p.mutex.RLock()
74+
defer p.mutex.RUnlock()
75+
76+
tcpHeader := [4]byte{0x24, 0x00, 0x00, 0x00}
77+
binary.BigEndian.PutUint16(tcpHeader[2:], uint16(len(buf)))
78+
79+
for c := range p.clients {
80+
if c.state == "PLAY" {
81+
if c.rtpProto == "udp" {
82+
p.rtpl.nconn.WriteTo(buf, &net.UDPAddr{
83+
IP: c.IP,
84+
Port: c.rtpPort,
85+
})
86+
} else {
87+
c.nconn.Write(tcpHeader[:])
88+
c.nconn.Write(buf)
89+
}
90+
}
91+
}
92+
}
93+
94+
func (p *program) handleRtcp(buf []byte) {
95+
p.mutex.RLock()
96+
defer p.mutex.RUnlock()
97+
98+
tcpHeader := [4]byte{0x24, 0x00, 0x00, 0x00}
99+
binary.BigEndian.PutUint16(tcpHeader[2:], uint16(len(buf)))
100+
101+
for c := range p.clients {
102+
if c.state == "PLAY" {
103+
if c.rtpProto == "udp" {
104+
p.rtcpl.nconn.WriteTo(buf, &net.UDPAddr{
105+
IP: c.IP,
106+
Port: c.rtcpPort,
107+
})
108+
} else {
109+
c.nconn.Write(tcpHeader[:])
110+
c.nconn.Write(buf)
111+
}
112+
}
113+
}
114+
}
115+
112116
func main() {
113117
kingpin.CommandLine.Help = "rtsp-simple-server " + Version + "\n\n" +
114118
"RTSP server."

rtsp_client.go

Lines changed: 103 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package main
22

33
import (
4+
"bufio"
5+
"encoding/binary"
46
"fmt"
57
"io"
68
"log"
@@ -326,48 +328,75 @@ func (c *rtspClient) run(wg sync.WaitGroup) {
326328

327329
// record
328330
case "ANNOUNCE":
329-
if _, ok := transports["RTP/AVP/UDP"]; !ok {
330-
c.log("ERR: transport header does not contain RTP/AVP/UDP")
331-
return
332-
}
333-
334331
if _, ok := transports["mode=record"]; !ok {
335332
c.log("ERR: transport header does not contain mode=record")
336333
return
337334
}
338335

339-
clientPort1, clientPort2 := getPorts()
340-
if clientPort1 == 0 || clientPort2 == 0 {
341-
c.log("ERR: transport header does not have valid client ports (%s)", transport)
342-
return
343-
}
336+
if _, ok := transports["RTP/AVP/UDP"]; ok {
337+
clientPort1, clientPort2 := getPorts()
338+
if clientPort1 == 0 || clientPort2 == 0 {
339+
c.log("ERR: transport header does not have valid client ports (%s)", transport)
340+
return
341+
}
342+
343+
err = rconn.WriteResponse(&rtsp.Response{
344+
StatusCode: 200,
345+
Status: "OK",
346+
Headers: map[string]string{
347+
"CSeq": cseq,
348+
"Transport": strings.Join([]string{
349+
"RTP/AVP",
350+
"unicast",
351+
fmt.Sprintf("client_port=%d-%d", clientPort1, clientPort2),
352+
fmt.Sprintf("server_port=%d-%d", c.p.rtpPort, c.p.rtcpPort),
353+
"ssrc=1234ABCD",
354+
}, ";"),
355+
"Session": "12345678",
356+
},
357+
})
358+
if err != nil {
359+
c.log("ERR: %s", err)
360+
return
361+
}
362+
363+
c.p.mutex.Lock()
364+
c.rtpProto = "udp"
365+
c.rtpPort = clientPort1
366+
c.rtcpPort = clientPort2
367+
c.state = "PRE_RECORD"
368+
c.p.mutex.Unlock()
369+
370+
} else if _, ok := transports["RTP/AVP/TCP"]; ok {
371+
err = rconn.WriteResponse(&rtsp.Response{
372+
StatusCode: 200,
373+
Status: "OK",
374+
Headers: map[string]string{
375+
"CSeq": cseq,
376+
"Transport": strings.Join([]string{
377+
"RTP/AVP/TCP",
378+
"unicast",
379+
"destionation=127.0.0.1",
380+
"source=127.0.0.1",
381+
}, ";"),
382+
"Session": "12345678",
383+
},
384+
})
385+
if err != nil {
386+
c.log("ERR: %s", err)
387+
return
388+
}
389+
390+
c.p.mutex.Lock()
391+
c.rtpProto = "tcp"
392+
c.state = "PRE_RECORD"
393+
c.p.mutex.Unlock()
344394

345-
err = rconn.WriteResponse(&rtsp.Response{
346-
StatusCode: 200,
347-
Status: "OK",
348-
Headers: map[string]string{
349-
"CSeq": cseq,
350-
"Transport": strings.Join([]string{
351-
"RTP/AVP",
352-
"unicast",
353-
fmt.Sprintf("client_port=%d-%d", clientPort1, clientPort2),
354-
fmt.Sprintf("server_port=%d-%d", c.p.rtpPort, c.p.rtcpPort),
355-
"ssrc=1234ABCD",
356-
}, ";"),
357-
"Session": "12345678",
358-
},
359-
})
360-
if err != nil {
361-
c.log("ERR: %s", err)
395+
} else {
396+
c.log("ERR: transport header does not contain a valid protocol (RTP/AVP or RTP/AVP/TCP) (%s)", transport)
362397
return
363398
}
364399

365-
c.p.mutex.Lock()
366-
c.rtpPort = clientPort1
367-
c.rtcpPort = clientPort2
368-
c.state = "PRE_RECORD"
369-
c.p.mutex.Unlock()
370-
371400
default:
372401
c.log("ERR: client is in state '%s'", c.state)
373402
return
@@ -398,12 +427,10 @@ func (c *rtspClient) run(wg sync.WaitGroup) {
398427
c.state = "PLAY"
399428
c.p.mutex.Unlock()
400429

401-
// when rtp protocol is TCP, the RTSP connection
402-
// becomes a RTP connection.
403-
// receive RTP feedback, do not parse it, wait until
404-
// connection closes.
430+
// when rtp protocol is TCP, the RTSP connection becomes a RTP connection.
431+
// receive RTP feedback, do not parse it, wait until connection closes.
405432
if c.rtpProto == "tcp" {
406-
buf := make([]byte, 10249)
433+
buf := make([]byte, 1024)
407434
for {
408435
_, err := c.nconn.Read(buf)
409436
if err != nil {
@@ -456,12 +483,49 @@ func (c *rtspClient) run(wg sync.WaitGroup) {
456483
return
457484
}
458485

459-
c.log("is publishing (via udp)")
486+
c.log("is publishing (via %s)", c.rtpProto)
460487

461488
c.p.mutex.Lock()
462489
c.state = "RECORD"
463490
c.p.mutex.Unlock()
464491

492+
// when rtp protocol is TCP, the RTSP connection becomes a RTP connection.
493+
// receive RTP feedback, do not parse it, wait until connection closes.
494+
if c.rtpProto == "tcp" {
495+
packet := make([]byte, 2048)
496+
bconn := bufio.NewReader(c.nconn)
497+
for {
498+
byts, err := bconn.Peek(4)
499+
if err != nil {
500+
return
501+
}
502+
bconn.Discard(4)
503+
504+
if byts[0] != 0x24 {
505+
c.log("ERR: wrong magic byte")
506+
return
507+
}
508+
509+
if byts[1] != 0x00 {
510+
c.log("ERR: wrong channel")
511+
return
512+
}
513+
514+
plen := binary.BigEndian.Uint16(byts[2:])
515+
if plen > 2048 {
516+
c.log("ERR: packet len > 2048")
517+
return
518+
}
519+
520+
_, err = io.ReadFull(bconn, packet[:plen])
521+
if err != nil {
522+
return
523+
}
524+
525+
c.p.handleRtp(packet[:plen])
526+
}
527+
}
528+
465529
case "TEARDOWN":
466530
return
467531

udp_listener.go

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,13 @@ import (
66
"sync"
77
)
88

9-
type udpListenerCb func(*udpListener, []byte)
10-
119
type udpListener struct {
1210
nconn *net.UDPConn
1311
logPrefix string
14-
cb udpListenerCb
12+
cb func([]byte)
1513
}
1614

17-
func newUdpListener(port int, logPrefix string, cb udpListenerCb) (*udpListener, error) {
15+
func newUdpListener(port int, logPrefix string, cb func([]byte)) (*udpListener, error) {
1816
nconn, err := net.ListenUDP("udp", &net.UDPAddr{
1917
Port: port,
2018
})
@@ -48,6 +46,6 @@ func (l *udpListener) run(wg sync.WaitGroup) {
4846
break
4947
}
5048

51-
l.cb(l, buf[:n])
49+
l.cb(buf[:n])
5250
}
5351
}

0 commit comments

Comments
 (0)