// Copyright 2019, Chef. All rights reserved. // https://github.com/q191201771/lal // // Use of this source code is governed by a MIT-style license // that can be found in the License file. // // Author: Chef (191201771@qq.com) package rtmp_test import ( "bytes" "io" "io/ioutil" "sync" "sync/atomic" "testing" "time" "github.com/q191201771/lal/pkg/httpflv" "github.com/q191201771/lal/pkg/logic" "github.com/q191201771/lal/pkg/rtmp" "github.com/q191201771/naza/pkg/assert" log "github.com/q191201771/naza/pkg/nazalog" ) // 读取 flv 文件,使用 rtmp 协议发送至服务端,再使用 rtmp 协议从服务端拉流,转换 flv 格式存储为文件, // 检查两份 flv 文件是否完全一致。 var ( serverAddr = ":10001" pushURL = "rtmp://127.0.0.1:10001/live/test" pullURL = "rtmp://127.0.0.1:10001/live/test" rFLVFile = "testdata/test.flv" wFLVFile = "testdata/out.flv" wgNum = 4 // FLVFileReader -> [push -> pub -> sub -> pull] -> FLVFileWriter ) var ( pubSessionObs MockPubSessionObserver pullSession *rtmp.PullSession subSession *rtmp.ServerSession wg sync.WaitGroup w httpflv.FLVFileWriter // rc uint32 bc uint32 wc uint32 ) type MockServerObserver struct { } func (so *MockServerObserver) NewRTMPPubSessionCB(session *rtmp.ServerSession) bool { log.Debug("NewRTMPPubSessionCB") session.SetPubSessionObserver(&pubSessionObs) return true } func (so *MockServerObserver) NewRTMPSubSessionCB(session *rtmp.ServerSession) bool { log.Debug("NewRTMPSubSessionCB") subSession = session return true } func (so *MockServerObserver) DelRTMPPubSessionCB(session *rtmp.ServerSession) { log.Debug("DelRTMPPubSessionCB") subSession.Flush() subSession.Dispose() wg.Done() } func (so *MockServerObserver) DelRTMPSubSessionCB(session *rtmp.ServerSession) { log.Debug("DelRTMPSubSessionCB") wg.Done() } type MockPubSessionObserver struct { } func (pso *MockPubSessionObserver) OnReadRTMPAVMsg(msg rtmp.AVMsg) { bc++ // 转发 currHeader := logic.Trans.MakeDefaultRTMPHeader(msg.Header) var absChunks []byte absChunks = rtmp.Message2Chunks(msg.Payload, &currHeader) subSession.AsyncWrite(absChunks) } func TestExample(t *testing.T) { var err error var r httpflv.FLVFileReader err = r.Open(rFLVFile) if err != nil { return } wg.Add(wgNum) var so MockServerObserver s := rtmp.NewServer(&so, serverAddr) go s.RunLoop() // 等待 server 开始监听 time.Sleep(100 * time.Millisecond) go func() { pullSession = rtmp.NewPullSession() err = pullSession.Pull(pullURL, func(msg rtmp.AVMsg) { tag := logic.Trans.RTMPMsg2FLVTag(msg) w.WriteTag(*tag) atomic.AddUint32(&wc, 1) }) log.Error(err) }() pushSession := rtmp.NewPushSession() err = pushSession.Push(pushURL) assert.Equal(t, nil, err) err = w.Open(wFLVFile) assert.Equal(t, nil, err) err = w.WriteRaw(httpflv.FLVHeader) assert.Equal(t, nil, err) for { tag, err := r.ReadTag() if err == io.EOF { break } assert.Equal(t, nil, err) rc++ //log.Debugf("send tag. %d", tag.Header.Timestamp) msg := logic.Trans.FLVTag2RTMPMsg(tag) //log.Debugf("send msg. %d %d", msg.Header.Timestamp, msg.Header.TimestampAbs) chunks := rtmp.Message2Chunks(msg.Payload, &msg.Header) //log.Debugf("%s", hex.Dump(chunks[:16])) err = pushSession.AsyncWrite(chunks) assert.Equal(t, nil, err) } r.Dispose() wg.Done() err = pushSession.Flush() assert.Equal(t, nil, err) pushSession.Dispose() wg.Done() wg.Wait() // 等待 pull 完成 for { if atomic.LoadUint32(&wc) == rc { break } time.Sleep(1 * time.Nanosecond) } //time.Sleep(1 * time.Second) pullSession.Dispose() w.Dispose() s.Dispose() log.Debugf("rc=%d, bc=%d, wc=%d", rc, bc, atomic.LoadUint32(&wc)) compareFile(t) } func compareFile(t *testing.T) { r, err := ioutil.ReadFile(rFLVFile) assert.Equal(t, nil, err) w, err := ioutil.ReadFile(wFLVFile) assert.Equal(t, nil, err) res := bytes.Compare(r, w) assert.Equal(t, 0, res) //err = os.Remove(wFLVFile) assert.Equal(t, nil, err) }