|
|
|
@ -59,8 +59,9 @@ func main() {
|
|
|
|
|
urls := collect(urlTmpl, num)
|
|
|
|
|
|
|
|
|
|
tags, err := httpflv.ReadAllTagsFromFlvFile(filename)
|
|
|
|
|
if err != nil {
|
|
|
|
|
nazalog.Fatalf("read tags from flv file failed. err=%+v", err)
|
|
|
|
|
if err != nil || len(tags) == 0 {
|
|
|
|
|
nazalog.Fatalf("read tags from flv file failed. len=%d, err=%+v", len(tags), err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
nazalog.Infof("read tags from flv file succ. len of tags=%d", len(tags))
|
|
|
|
|
|
|
|
|
@ -75,7 +76,7 @@ func main() {
|
|
|
|
|
wg.Add(len(urls))
|
|
|
|
|
for _, url := range urls {
|
|
|
|
|
go func(u string) {
|
|
|
|
|
push(tags, []string{u}, isRecursive)
|
|
|
|
|
push(tags, u, isRecursive)
|
|
|
|
|
wg.Done()
|
|
|
|
|
atomic.AddInt32(&aliveSessionCount, -1)
|
|
|
|
|
}(url)
|
|
|
|
@ -85,141 +86,33 @@ func main() {
|
|
|
|
|
nazalog.Info("< main.")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func push(tags []httpflv.Tag, urls []string, isRecursive bool) {
|
|
|
|
|
var sessionList []*rtmp.PushSession
|
|
|
|
|
func push(tags []httpflv.Tag, url string, isRecursive bool) {
|
|
|
|
|
ps := rtmp.NewPushSession(func(option *rtmp.PushSessionOption) {
|
|
|
|
|
option.PushTimeoutMs = 5000
|
|
|
|
|
option.WriteAvTimeoutMs = 10000
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
if len(tags) == 0 || len(urls) == 0 {
|
|
|
|
|
if err := ps.Push(url); err != nil {
|
|
|
|
|
nazalog.Errorf("push failed. err=%v", err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
atomic.AddInt32(&aliveSessionCount, 1)
|
|
|
|
|
|
|
|
|
|
var err error
|
|
|
|
|
|
|
|
|
|
for i := range urls {
|
|
|
|
|
ps := rtmp.NewPushSession(func(option *rtmp.PushSessionOption) {
|
|
|
|
|
option.PushTimeoutMs = 5000
|
|
|
|
|
option.WriteAvTimeoutMs = 10000
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
err = ps.Push(urls[i])
|
|
|
|
|
if err != nil {
|
|
|
|
|
nazalog.Errorf("push failed. err=%v", err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
atomic.AddInt32(&aliveSessionCount, 1)
|
|
|
|
|
|
|
|
|
|
nazalog.Infof("push succ. url=%s", urls[i])
|
|
|
|
|
sessionList = append(sessionList, ps)
|
|
|
|
|
}
|
|
|
|
|
check(sessionList)
|
|
|
|
|
|
|
|
|
|
var totalBaseTs uint32 // 每轮最后更新
|
|
|
|
|
var prevTs uint32 // 上一个tag
|
|
|
|
|
var hasReadThisBaseTs bool
|
|
|
|
|
var thisBaseTs uint32 // 每轮第一个tag
|
|
|
|
|
var hasTraceFirstTagTs bool
|
|
|
|
|
var firstTagTs uint32 // 所有轮第一个tag
|
|
|
|
|
var firstTagTick int64 // 所有轮第一个tag的物理发送时间
|
|
|
|
|
|
|
|
|
|
// 1. 保证metadata只在最初发送一次
|
|
|
|
|
// 2. 多轮,时间戳会翻转,需要处理,让它线性增长
|
|
|
|
|
|
|
|
|
|
// 多轮,一个循环代表一次完整文件的发送
|
|
|
|
|
for i := 0; ; i++ {
|
|
|
|
|
nazalog.Infof(" > round. i=%d, totalBaseTs=%d, prevTs=%d, thisBaseTs=%d",
|
|
|
|
|
i, totalBaseTs, prevTs, thisBaseTs)
|
|
|
|
|
|
|
|
|
|
hasReadThisBaseTs = false
|
|
|
|
|
|
|
|
|
|
// 一轮,遍历文件的所有tag数据
|
|
|
|
|
for _, tag := range tags {
|
|
|
|
|
h := remux.FlvTagHeader2RtmpHeader(tag.Header)
|
|
|
|
|
|
|
|
|
|
// metadata只发送一次
|
|
|
|
|
if tag.IsMetadata() {
|
|
|
|
|
if totalBaseTs == 0 {
|
|
|
|
|
h.TimestampAbs = 0
|
|
|
|
|
chunks := rtmp.Message2Chunks(tag.Raw[11:11+h.MsgLen], &h)
|
|
|
|
|
send(sessionList, chunks)
|
|
|
|
|
} else {
|
|
|
|
|
// noop
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if hasReadThisBaseTs {
|
|
|
|
|
// 本轮非第一个tag
|
|
|
|
|
|
|
|
|
|
// 之前已经读到了这轮读文件的base值,ts要减去base
|
|
|
|
|
h.TimestampAbs = tag.Header.Timestamp - thisBaseTs + totalBaseTs
|
|
|
|
|
} else {
|
|
|
|
|
// 本轮第一个tag
|
|
|
|
|
|
|
|
|
|
// 设置base,ts设置为上一轮读文件的值
|
|
|
|
|
thisBaseTs = tag.Header.Timestamp
|
|
|
|
|
h.TimestampAbs = totalBaseTs
|
|
|
|
|
hasReadThisBaseTs = true
|
|
|
|
|
}
|
|
|
|
|
nazalog.Infof("push succ. url=%s", url)
|
|
|
|
|
|
|
|
|
|
if h.TimestampAbs < prevTs {
|
|
|
|
|
// ts比上一个包的还小,直接设置为上一包的值,并且不sleep直接发送
|
|
|
|
|
h.TimestampAbs = prevTs
|
|
|
|
|
nazalog.Errorf("this tag timestamp less than prev timestamp. h.TimestampAbs=%d, prevTs=%d", h.TimestampAbs, prevTs)
|
|
|
|
|
}
|
|
|
|
|
flvFilePump := httpflv.NewFileFilePump(func(option *httpflv.FlvFilePumpOption) {
|
|
|
|
|
option.IsRecursive = isRecursive
|
|
|
|
|
})
|
|
|
|
|
_ = flvFilePump.PumpWithTags(tags, func(tag httpflv.Tag) bool {
|
|
|
|
|
h := remux.FlvTagHeader2RtmpHeader(tag.Header)
|
|
|
|
|
chunks := rtmp.Message2Chunks(tag.Raw[11:11+h.MsgLen], &h)
|
|
|
|
|
|
|
|
|
|
chunks := rtmp.Message2Chunks(tag.Raw[11:11+h.MsgLen], &h)
|
|
|
|
|
|
|
|
|
|
if hasTraceFirstTagTs {
|
|
|
|
|
// 所有轮的非第一个tag
|
|
|
|
|
|
|
|
|
|
// 当前距离第一个tag的物理发送时间,以及距离第一个tag的时间戳
|
|
|
|
|
// 如果物理时间短,就睡眠相应的时间
|
|
|
|
|
n := time.Now().UnixNano() / 1000000
|
|
|
|
|
diffTick := n - firstTagTick
|
|
|
|
|
diffTs := h.TimestampAbs - firstTagTs
|
|
|
|
|
if diffTick < int64(diffTs) {
|
|
|
|
|
time.Sleep(time.Duration(int64(diffTs)-diffTick) * time.Millisecond)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// 所有轮的第一个tag
|
|
|
|
|
|
|
|
|
|
// 记录所有轮的第一个tag的物理发送时间,以及数据的时间戳
|
|
|
|
|
firstTagTick = time.Now().UnixNano() / 1000000
|
|
|
|
|
firstTagTs = h.TimestampAbs
|
|
|
|
|
hasTraceFirstTagTs = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
send(sessionList, chunks)
|
|
|
|
|
|
|
|
|
|
prevTs = h.TimestampAbs
|
|
|
|
|
} // tags for loop
|
|
|
|
|
|
|
|
|
|
totalBaseTs = prevTs + 1
|
|
|
|
|
|
|
|
|
|
if !isRecursive {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func send(sessionList []*rtmp.PushSession, b []byte) {
|
|
|
|
|
var s []*rtmp.PushSession
|
|
|
|
|
for _, ps := range sessionList {
|
|
|
|
|
if err := ps.Write(b); err != nil {
|
|
|
|
|
if err := ps.Write(chunks); err != nil {
|
|
|
|
|
nazalog.Errorf("write data error. err=%v", err)
|
|
|
|
|
continue
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
s = append(s, ps)
|
|
|
|
|
}
|
|
|
|
|
sessionList = s
|
|
|
|
|
|
|
|
|
|
check(sessionList)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func check(sessionList []*rtmp.PushSession) {
|
|
|
|
|
if len(sessionList) == 0 {
|
|
|
|
|
nazalog.Errorf("all push session dead.")
|
|
|
|
|
os.Exit(1)
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func collect(urlTmpl string, num int) (urls []string) {
|
|
|
|
|