Add 'rtsp/' from commit 'df0f52cf38a45d842291bb72e1adc24de397d1dd'
git-subtree-dir: rtsp git-subtree-mainline: 6b682ae3918d3f153df86e11eb07d386bfcd26b8 git-subtree-split: df0f52cf38a45d842291bb72e1adc24de397d1dd
This commit is contained in:
commit
452846833c
1214
rtsp/client.go
Normal file
1214
rtsp/client.go
Normal file
File diff suppressed because it is too large
Load Diff
25
rtsp/conn.go
Normal file
25
rtsp/conn.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
package rtsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type connWithTimeout struct {
|
||||||
|
Timeout time.Duration
|
||||||
|
net.Conn
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self connWithTimeout) Read(p []byte) (n int, err error) {
|
||||||
|
if self.Timeout > 0 {
|
||||||
|
self.Conn.SetReadDeadline(time.Now().Add(self.Timeout))
|
||||||
|
}
|
||||||
|
return self.Conn.Read(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self connWithTimeout) Write(p []byte) (n int, err error) {
|
||||||
|
if self.Timeout > 0 {
|
||||||
|
self.Conn.SetWriteDeadline(time.Now().Add(self.Timeout))
|
||||||
|
}
|
||||||
|
return self.Conn.Write(p)
|
||||||
|
}
|
116
rtsp/sdp/parser.go
Normal file
116
rtsp/sdp/parser.go
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
package sdp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"github.com/nareix/av"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Session struct {
|
||||||
|
Uri string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Media struct {
|
||||||
|
AVType string
|
||||||
|
Type av.CodecType
|
||||||
|
TimeScale int
|
||||||
|
Control string
|
||||||
|
Rtpmap int
|
||||||
|
Config []byte
|
||||||
|
SpropParameterSets [][]byte
|
||||||
|
PayloadType int
|
||||||
|
SizeLength int
|
||||||
|
IndexLength int
|
||||||
|
}
|
||||||
|
|
||||||
|
func Parse(content string) (sess Session, medias []Media) {
|
||||||
|
var media *Media
|
||||||
|
|
||||||
|
for _, line := range strings.Split(content, "\n") {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
typeval := strings.SplitN(line, "=", 2)
|
||||||
|
if len(typeval) == 2 {
|
||||||
|
fields := strings.SplitN(typeval[1], " ", 2)
|
||||||
|
|
||||||
|
switch typeval[0] {
|
||||||
|
case "m":
|
||||||
|
if len(fields) > 0 {
|
||||||
|
switch fields[0] {
|
||||||
|
case "audio", "video":
|
||||||
|
medias = append(medias, Media{AVType: fields[0]})
|
||||||
|
media = &medias[len(medias)-1]
|
||||||
|
mfields := strings.Split(fields[1], " ")
|
||||||
|
if len(mfields) >= 3 {
|
||||||
|
media.PayloadType, _ = strconv.Atoi(mfields[2])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
case "u":
|
||||||
|
sess.Uri = typeval[1]
|
||||||
|
|
||||||
|
case "a":
|
||||||
|
if media != nil {
|
||||||
|
for _, field := range fields {
|
||||||
|
keyval := strings.SplitN(field, ":", 2)
|
||||||
|
if len(keyval) >= 2 {
|
||||||
|
key := keyval[0]
|
||||||
|
val := keyval[1]
|
||||||
|
switch key {
|
||||||
|
case "control":
|
||||||
|
media.Control = val
|
||||||
|
case "rtpmap":
|
||||||
|
media.Rtpmap, _ = strconv.Atoi(val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keyval = strings.Split(field, "/")
|
||||||
|
if len(keyval) >= 2 {
|
||||||
|
key := keyval[0]
|
||||||
|
switch strings.ToUpper(key) {
|
||||||
|
case "MPEG4-GENERIC":
|
||||||
|
media.Type = av.AAC
|
||||||
|
case "H264":
|
||||||
|
media.Type = av.H264
|
||||||
|
}
|
||||||
|
if i, err := strconv.Atoi(keyval[1]); err == nil {
|
||||||
|
media.TimeScale = i
|
||||||
|
}
|
||||||
|
if false {
|
||||||
|
fmt.Println("sdp:", keyval[1], media.TimeScale)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
keyval = strings.Split(field, ";")
|
||||||
|
if len(keyval) > 1 {
|
||||||
|
for _, field := range keyval {
|
||||||
|
keyval := strings.SplitN(field, "=", 2)
|
||||||
|
if len(keyval) == 2 {
|
||||||
|
key := strings.TrimSpace(keyval[0])
|
||||||
|
val := keyval[1]
|
||||||
|
switch key {
|
||||||
|
case "config":
|
||||||
|
media.Config, _ = hex.DecodeString(val)
|
||||||
|
case "sizelength":
|
||||||
|
media.SizeLength, _ = strconv.Atoi(val)
|
||||||
|
case "indexlength":
|
||||||
|
media.IndexLength, _ = strconv.Atoi(val)
|
||||||
|
case "sprop-parameter-sets":
|
||||||
|
fields := strings.Split(val, ",")
|
||||||
|
for _, field := range fields {
|
||||||
|
val, _ := base64.StdEncoding.DecodeString(field)
|
||||||
|
media.SpropParameterSets = append(media.SpropParameterSets, val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
44
rtsp/sdp/parser_test.go
Normal file
44
rtsp/sdp/parser_test.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package sdp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParse(t *testing.T) {
|
||||||
|
infos := Decode(`
|
||||||
|
v=0
|
||||||
|
o=- 1459325504777324 1 IN IP4 192.168.0.123
|
||||||
|
s=RTSP/RTP stream from Network Video Server
|
||||||
|
i=mpeg4cif
|
||||||
|
t=0 0
|
||||||
|
a=tool:LIVE555 Streaming Media v2009.09.28
|
||||||
|
a=type:broadcast
|
||||||
|
a=control:*
|
||||||
|
a=range:npt=0-
|
||||||
|
a=x-qt-text-nam:RTSP/RTP stream from Network Video Server
|
||||||
|
a=x-qt-text-inf:mpeg4cif
|
||||||
|
m=video 0 RTP/AVP 96
|
||||||
|
c=IN IP4 0.0.0.0
|
||||||
|
b=AS:300
|
||||||
|
a=rtpmap:96 H264/90000
|
||||||
|
a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z00AHpWoKA9k,aO48gA==
|
||||||
|
a=x-dimensions: 720, 480
|
||||||
|
a=x-framerate: 15
|
||||||
|
a=control:track1
|
||||||
|
m=audio 0 RTP/AVP 96
|
||||||
|
c=IN IP4 0.0.0.0
|
||||||
|
b=AS:256
|
||||||
|
a=rtpmap:96 MPEG4-GENERIC/16000/2
|
||||||
|
a=fmtp:96 streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1408
|
||||||
|
a=control:track2
|
||||||
|
m=audio 0 RTP/AVP 0
|
||||||
|
c=IN IP4 0.0.0.0
|
||||||
|
b=AS:50
|
||||||
|
a=recvonly
|
||||||
|
a=control:rtsp://109.195.127.207:554/mpeg4cif/trackID=2
|
||||||
|
a=rtpmap:0 PCMU/8000
|
||||||
|
a=Media_header:MEDIAINFO=494D4B48010100000400010010710110401F000000FA000000000000000000000000000000000000;
|
||||||
|
a=appversion:1.0
|
||||||
|
`)
|
||||||
|
t.Logf("%v", infos)
|
||||||
|
}
|
29
rtsp/stream.go
Normal file
29
rtsp/stream.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package rtsp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/nareix/av"
|
||||||
|
"github.com/nareix/rtsp/sdp"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Stream struct {
|
||||||
|
av.CodecData
|
||||||
|
Sdp sdp.Media
|
||||||
|
client *Client
|
||||||
|
|
||||||
|
// h264
|
||||||
|
fuStarted bool
|
||||||
|
fuBuffer []byte
|
||||||
|
sps []byte
|
||||||
|
pps []byte
|
||||||
|
spsChanged bool
|
||||||
|
ppsChanged bool
|
||||||
|
|
||||||
|
gotpkt bool
|
||||||
|
pkt av.Packet
|
||||||
|
timestamp uint32
|
||||||
|
firsttimestamp uint32
|
||||||
|
|
||||||
|
lasttime time.Duration
|
||||||
|
}
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user