switch streams
This commit is contained in:
parent
fc3c9d7af3
commit
7aeca4e959
16
handshake.go
16
handshake.go
@ -97,25 +97,25 @@ func createResp(b []byte, key []byte) {
|
|||||||
|
|
||||||
func parseChal(b []byte, peerKey []byte, key []byte) (dig []byte, err int) {
|
func parseChal(b []byte, peerKey []byte, key []byte) (dig []byte, err int) {
|
||||||
if b[0] != 0x3 {
|
if b[0] != 0x3 {
|
||||||
l.Printf("handshake: invalid rtmp version\n")
|
l.Printf("handshake: invalid rtmp version")
|
||||||
err = 1
|
err = 1
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
epoch := b[1:5]
|
epoch := b[1:5]
|
||||||
ver := b[5:9]
|
ver := b[5:9]
|
||||||
l.Printf("handshake: epoch %v ver %v\n", epoch, ver)
|
l.Printf("handshake: epoch %v ver %v", epoch, ver)
|
||||||
|
|
||||||
var offs int
|
var offs int
|
||||||
if offs = findDigest(b[1:], peerKey, 772); offs == -1 {
|
if offs = findDigest(b[1:], peerKey, 772); offs == -1 {
|
||||||
if offs = findDigest(b[1:], peerKey, 8); offs == -1 {
|
if offs = findDigest(b[1:], peerKey, 8); offs == -1 {
|
||||||
l.Printf("handshake: digest not found\n")
|
l.Printf("handshake: digest not found")
|
||||||
err = 1
|
err = 1
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
l.Printf("handshake: offs = %v\n", offs)
|
l.Printf("handshake: offs = %v", offs)
|
||||||
|
|
||||||
dig = makeDigest(key, b[1+offs:1+offs+32], -1)
|
dig = makeDigest(key, b[1+offs:1+offs+32], -1)
|
||||||
return
|
return
|
||||||
@ -124,22 +124,22 @@ func parseChal(b []byte, peerKey []byte, key []byte) (dig []byte, err int) {
|
|||||||
|
|
||||||
func handShake(rw io.ReadWriter) {
|
func handShake(rw io.ReadWriter) {
|
||||||
b := ReadBuf(rw, 1537)
|
b := ReadBuf(rw, 1537)
|
||||||
l.Printf("handshake: got client chal\n")
|
l.Printf("handshake: got client chal")
|
||||||
dig, err := parseChal(b, clientKey2, serverKey)
|
dig, err := parseChal(b, clientKey2, serverKey)
|
||||||
if err != 0 {
|
if err != 0 {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
createChal(b, serverVersion, serverKey2)
|
createChal(b, serverVersion, serverKey2)
|
||||||
l.Printf("handshake: send server chal\n")
|
l.Printf("handshake: send server chal")
|
||||||
rw.Write(b)
|
rw.Write(b)
|
||||||
|
|
||||||
b = make([]byte, 1536)
|
b = make([]byte, 1536)
|
||||||
createResp(b, dig)
|
createResp(b, dig)
|
||||||
l.Printf("handshake: send server resp\n")
|
l.Printf("handshake: send server resp")
|
||||||
rw.Write(b)
|
rw.Write(b)
|
||||||
|
|
||||||
b = ReadBuf(rw, 1536)
|
b = ReadBuf(rw, 1536)
|
||||||
l.Printf("handshake: got client resp\n")
|
l.Printf("handshake: got client resp")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
msg.go
1
msg.go
@ -111,6 +111,7 @@ type MsgStream struct {
|
|||||||
Msg map[int]*Msg
|
Msg map[int]*Msg
|
||||||
vts, ats int
|
vts, ats int
|
||||||
|
|
||||||
|
meta AMFObj
|
||||||
id string
|
id string
|
||||||
role int
|
role int
|
||||||
stat int
|
stat int
|
||||||
|
107
server.go
107
server.go
@ -12,6 +12,7 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"time"
|
"time"
|
||||||
"strings"
|
"strings"
|
||||||
|
_ "runtime/debug"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -36,7 +37,12 @@ func (e eventS) String() string {
|
|||||||
case E_PLAY:
|
case E_PLAY:
|
||||||
return "play"
|
return "play"
|
||||||
case E_DATA:
|
case E_DATA:
|
||||||
return fmt.Sprintf("data %d bytes ts %d", e.m.data.Len(), e.m.curts)
|
switch e.m.typeid {
|
||||||
|
case MSG_VIDEO:
|
||||||
|
return fmt.Sprintf("ts %d video %d bytes key %t", e.m.curts, e.m.data.Len(), e.m.key)
|
||||||
|
case MSG_AUDIO:
|
||||||
|
return fmt.Sprintf("ts %d audio %d bytes", e.m.curts, e.m.data.Len())
|
||||||
|
}
|
||||||
case E_CLOSE:
|
case E_CLOSE:
|
||||||
return "close"
|
return "close"
|
||||||
}
|
}
|
||||||
@ -63,7 +69,6 @@ const (
|
|||||||
E_CLOSE
|
E_CLOSE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
func handleConnect(mr *MsgStream, trans float64, app string) {
|
func handleConnect(mr *MsgStream, trans float64, app string) {
|
||||||
|
|
||||||
l.Printf("stream %v: connect: %s", mr, app)
|
l.Printf("stream %v: connect: %s", mr, app)
|
||||||
@ -96,6 +101,7 @@ func handleConnect(mr *MsgStream, trans float64, app string) {
|
|||||||
|
|
||||||
func handleMeta(mr *MsgStream, obj AMFObj) {
|
func handleMeta(mr *MsgStream, obj AMFObj) {
|
||||||
|
|
||||||
|
mr.meta = obj
|
||||||
mr.W = int(obj.obj["width"].f64)
|
mr.W = int(obj.obj["width"].f64)
|
||||||
mr.H = int(obj.obj["height"].f64)
|
mr.H = int(obj.obj["height"].f64)
|
||||||
|
|
||||||
@ -195,6 +201,8 @@ func handlePlay(mr *MsgStream, strid int) {
|
|||||||
l.Printf("stream %v: test video %dx%d", mr, mr.W, mr.H)
|
l.Printf("stream %v: test video %dx%d", mr, mr.W, mr.H)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
begin := func () {
|
||||||
|
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
WriteInt(&b, 0, 2)
|
WriteInt(&b, 0, 2)
|
||||||
WriteInt(&b, strid, 4)
|
WriteInt(&b, strid, 4)
|
||||||
@ -213,7 +221,7 @@ func handlePlay(mr *MsgStream, strid int) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
l.Printf("stream %v: video size %dx%d", mr, mr.W, mr.H)
|
l.Printf("stream %v: begin: video %dx%d", mr, mr.W, mr.H)
|
||||||
|
|
||||||
mr.WriteAMFMeta(5, strid, []AMFObj {
|
mr.WriteAMFMeta(5, strid, []AMFObj {
|
||||||
AMFObj { atype : AMF_STRING, str : "|RtmpSampleAccess", },
|
AMFObj { atype : AMF_STRING, str : "|RtmpSampleAccess", },
|
||||||
@ -221,8 +229,13 @@ func handlePlay(mr *MsgStream, strid int) {
|
|||||||
AMFObj { atype : AMF_BOOLEAN, i: 1, },
|
AMFObj { atype : AMF_BOOLEAN, i: 1, },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
mr.meta.obj["Server"] = AMFObj { atype : AMF_STRING, str : "Golang Rtmp Server", }
|
||||||
|
mr.meta.atype = AMF_OBJECT
|
||||||
|
l.Printf("stream %v: %v", mr, mr.meta)
|
||||||
mr.WriteAMFMeta(5, strid, []AMFObj {
|
mr.WriteAMFMeta(5, strid, []AMFObj {
|
||||||
AMFObj { atype : AMF_STRING, str : "onMetaData", },
|
AMFObj { atype : AMF_STRING, str : "onMetaData", },
|
||||||
|
mr.meta,
|
||||||
|
/*
|
||||||
AMFObj { atype : AMF_OBJECT,
|
AMFObj { atype : AMF_OBJECT,
|
||||||
obj : map[string] AMFObj {
|
obj : map[string] AMFObj {
|
||||||
"Server" : AMFObj { atype : AMF_STRING, str : "Golang Rtmp Server", },
|
"Server" : AMFObj { atype : AMF_STRING, str : "Golang Rtmp Server", },
|
||||||
@ -238,28 +251,68 @@ func handlePlay(mr *MsgStream, strid int) {
|
|||||||
"audiocodecid" : AMFObj { atype : AMF_NUMBER, f64 : 10, },
|
"audiocodecid" : AMFObj { atype : AMF_NUMBER, f64 : 10, },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
*/
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
end := func () {
|
||||||
|
|
||||||
|
l.Printf("stream %v: end", mr)
|
||||||
|
|
||||||
|
var b bytes.Buffer
|
||||||
|
WriteInt(&b, 1, 2)
|
||||||
|
WriteInt(&b, strid, 4)
|
||||||
|
mr.WriteMsg(0, 2, MSG_USER, 0, 0, b.Bytes()) // stream eof 1
|
||||||
|
|
||||||
|
mr.WriteAMFCmd(5, strid, []AMFObj {
|
||||||
|
AMFObj { atype : AMF_STRING, str : "onStatus", },
|
||||||
|
AMFObj { atype : AMF_NUMBER, f64 : 0, },
|
||||||
|
AMFObj { atype : AMF_NULL, },
|
||||||
|
AMFObj { atype : AMF_OBJECT,
|
||||||
|
obj : map[string] AMFObj {
|
||||||
|
"level" : AMFObj { atype : AMF_STRING, str : "status", },
|
||||||
|
"code" : AMFObj { atype : AMF_STRING, str : "NetStream.Play.Stop", },
|
||||||
|
"description" : AMFObj { atype : AMF_STRING, str : "Stop live.", },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if tsrc == nil {
|
if tsrc == nil {
|
||||||
l.Printf("stream %v: extra size %d %d", mr, len(mr.extraA), len(mr.extraV))
|
|
||||||
|
|
||||||
mr.WriteAAC(strid, 0, mr.extraA[2:])
|
for {
|
||||||
mr.WritePPS(strid, 0, mr.extraV[5:])
|
nr := 0
|
||||||
|
|
||||||
l.Printf("stream %v: player wait data", mr)
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
m := <-mr.que
|
m := <-mr.que
|
||||||
l.Printf("data %v: got %v", mr, m)
|
if m == nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
//if nr == 0 && !m.key {
|
||||||
|
// continue
|
||||||
|
//}
|
||||||
|
if nr == 0 {
|
||||||
|
begin()
|
||||||
|
l.Printf("stream %v: extra size %d %d", mr, len(mr.extraA), len(mr.extraV))
|
||||||
|
mr.WriteAAC(strid, 0, mr.extraA[2:])
|
||||||
|
mr.WritePPS(strid, 0, mr.extraV[5:])
|
||||||
|
}
|
||||||
|
l.Printf("data %v: got %v curts %v", mr, m, m.curts)
|
||||||
switch m.typeid {
|
switch m.typeid {
|
||||||
case MSG_AUDIO:
|
case MSG_AUDIO:
|
||||||
mr.WriteAudio(strid, m.curts, m.data.Bytes()[2:])
|
mr.WriteAudio(strid, m.curts, m.data.Bytes()[2:])
|
||||||
case MSG_VIDEO:
|
case MSG_VIDEO:
|
||||||
mr.WriteVideo(strid, m.curts, m.key, m.data.Bytes()[5:])
|
mr.WriteVideo(strid, m.curts, m.key, m.data.Bytes()[5:])
|
||||||
}
|
}
|
||||||
|
nr++
|
||||||
}
|
}
|
||||||
|
end()
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
begin()
|
||||||
|
|
||||||
lf, _ := os.Create("/tmp/rtmp.log")
|
lf, _ := os.Create("/tmp/rtmp.log")
|
||||||
ll := log.New(lf, "", 0)
|
ll := log.New(lf, "", 0)
|
||||||
|
|
||||||
@ -304,6 +357,9 @@ func serve(mr *MsgStream) {
|
|||||||
event <- eventS{id:E_CLOSE, mr:mr}
|
event <- eventS{id:E_CLOSE, mr:mr}
|
||||||
<-eventDone
|
<-eventDone
|
||||||
l.Printf("stream %v: closed %v", mr, err)
|
l.Printf("stream %v: closed %v", mr, err)
|
||||||
|
//if err != "EOF" {
|
||||||
|
// l.Printf("stream %v: %v", mr, string(debug.Stack()))
|
||||||
|
//}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@ -377,21 +433,27 @@ func listenEvent() {
|
|||||||
pubmap[e.mr.app] = e.mr
|
pubmap[e.mr.app] = e.mr
|
||||||
}
|
}
|
||||||
case e.id == E_PLAY:
|
case e.id == E_PLAY:
|
||||||
src, ok := pubmap[e.mr.app]
|
|
||||||
if !ok || src.stat != WAIT_DATA {
|
|
||||||
l.Printf("event %v: cannot find publisher with app %s", e.mr, e.mr.app)
|
|
||||||
e.mr.Close()
|
|
||||||
} else {
|
|
||||||
e.mr.W = src.W
|
|
||||||
e.mr.H = src.H
|
|
||||||
e.mr.role = PLAYER
|
e.mr.role = PLAYER
|
||||||
e.mr.extraA = src.extraA
|
|
||||||
e.mr.extraV = src.extraV
|
|
||||||
e.mr.que = make(chan *Msg, 16)
|
e.mr.que = make(chan *Msg, 16)
|
||||||
|
for _, mr := range idmap {
|
||||||
|
if mr.role == PUBLISHER && mr.app == e.mr.app && mr.stat == WAIT_DATA {
|
||||||
|
e.mr.W = mr.W
|
||||||
|
e.mr.H = mr.H
|
||||||
|
e.mr.extraA = mr.extraA
|
||||||
|
e.mr.extraV = mr.extraV
|
||||||
|
e.mr.meta = mr.meta
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case e.id == E_CLOSE:
|
case e.id == E_CLOSE:
|
||||||
if e.mr.role == PUBLISHER {
|
if e.mr.role == PUBLISHER {
|
||||||
delete(pubmap, e.mr.app)
|
delete(pubmap, e.mr.app)
|
||||||
|
for _, mr := range idmap {
|
||||||
|
if mr.role == PLAYER && mr.app == e.mr.app {
|
||||||
|
ch := reflect.ValueOf(mr.que)
|
||||||
|
var m *Msg = nil
|
||||||
|
ch.TrySend(reflect.ValueOf(m))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
delete(idmap, e.mr.id)
|
delete(idmap, e.mr.id)
|
||||||
case e.id == E_DATA && e.mr.stat == WAIT_EXTRA:
|
case e.id == E_DATA && e.mr.stat == WAIT_EXTRA:
|
||||||
@ -406,6 +468,15 @@ func listenEvent() {
|
|||||||
if len(e.mr.extraA) > 0 && len(e.mr.extraV) > 0 {
|
if len(e.mr.extraA) > 0 && len(e.mr.extraV) > 0 {
|
||||||
l.Printf("event %v: got all extra", e.mr)
|
l.Printf("event %v: got all extra", e.mr)
|
||||||
e.mr.stat = WAIT_DATA
|
e.mr.stat = WAIT_DATA
|
||||||
|
for _, mr := range idmap {
|
||||||
|
if mr.role == PLAYER && mr.app == e.mr.app {
|
||||||
|
mr.W = e.mr.W
|
||||||
|
mr.H = e.mr.H
|
||||||
|
mr.extraA = e.mr.extraA
|
||||||
|
mr.extraV = e.mr.extraV
|
||||||
|
mr.meta = e.mr.meta
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
case e.id == E_DATA && e.mr.stat == WAIT_DATA:
|
case e.id == E_DATA && e.mr.stat == WAIT_DATA:
|
||||||
for _, mr := range idmap {
|
for _, mr := range idmap {
|
||||||
|
26
util.go
26
util.go
@ -9,29 +9,25 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
type dummyWriter struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *dummyWriter) Write(p []byte) (n int, err error) {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type logger int
|
type logger int
|
||||||
|
|
||||||
func (l logger) Printf(format string, v ...interface{}) {
|
func (l logger) Printf(format string, v ...interface{}) {
|
||||||
str := fmt.Sprintf(format, v...)
|
str := fmt.Sprintf(format, v...)
|
||||||
switch {
|
switch {
|
||||||
case strings.HasPrefix(str, "server") && l >= 0,
|
case strings.HasPrefix(str, "server") && l >= 1,
|
||||||
strings.HasPrefix(str, "stream") && l >= 0,
|
strings.HasPrefix(str, "stream") && l >= 1,
|
||||||
strings.HasPrefix(str, "event") && l >= 0,
|
strings.HasPrefix(str, "event") && l >= 1,
|
||||||
strings.HasPrefix(str, "data") && l >= 0,
|
strings.HasPrefix(str, "data") && l >= 1,
|
||||||
strings.HasPrefix(str, "msg") && l >= 1:
|
strings.HasPrefix(str, "msg") && l >= 2:
|
||||||
l2.Println(str)
|
l2.Println(str)
|
||||||
|
default:
|
||||||
|
if l >= 1 {
|
||||||
|
l2.Println(str)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
dummyW = &dummyWriter{}
|
|
||||||
l = logger(0)
|
l = logger(0)
|
||||||
l2 *log.Logger
|
l2 *log.Logger
|
||||||
)
|
)
|
||||||
@ -41,6 +37,10 @@ func init() {
|
|||||||
l2.SetFlags(log.Lmicroseconds)
|
l2.SetFlags(log.Lmicroseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LogLevel(i int) {
|
||||||
|
l = logger(i)
|
||||||
|
}
|
||||||
|
|
||||||
type stream struct {
|
type stream struct {
|
||||||
r io.ReadWriteCloser
|
r io.ReadWriteCloser
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user