for session := range group.rtmpSubSessionSet { if session.IsFresh { // TODO chef: 头信息和full gop也可以在SubSession刚加入时发送 if group.rtmpGopCache.MetadataEnsureWithoutSetDataFrame != nil { Log.Debugf("[%s] [%s] write metadata", group.UniqueKey, session.UniqueKey()) _ = session.Write(group.rtmpGopCache.MetadataEnsureWithoutSetDataFrame) } if group.rtmpGopCache.VideoSeqHeader != nil { Log.Debugf("[%s] [%s] write vsh", group.UniqueKey, session.UniqueKey()) _ = session.Write(group.rtmpGopCache.VideoSeqHeader) } if group.rtmpGopCache.AacSeqHeader != nil { Log.Debugf("[%s] [%s] write ash", group.UniqueKey, session.UniqueKey()) _ = session.Write(group.rtmpGopCache.AacSeqHeader) } gopCount := group.rtmpGopCache.GetGopCount() if gopCount > 0 { // GOP缓存中肯定包含了关键帧 session.ShouldWaitVideoKeyFrame = false Log.Debugf("[%s] [%s] write gop cache. gop num=%d", group.UniqueKey, session.UniqueKey(), gopCount) } for i := 0; i < gopCount; i++ { for _, item := range group.rtmpGopCache.GetGopDataAt(i) { _ = session.Write(item) } } // 有新加入的sub session(本次循环的第一个新加入的sub session),把rtmp buf writer中的缓存数据全部广播发送给老的sub session // 从而确保新加入的sub session不会发送这部分脏的数据 // 注意,此处可能被调用多次,但是只有第一次会实际flush缓存数据 if group.rtmpMergeWriter != nil { group.rtmpMergeWriter.Flush() } session.IsFresh = false } if session.ShouldWaitVideoKeyFrame && msg.IsVideoKeyNalu() { // 有sub session在等待关键帧,并且当前是关键帧 // 把rtmp buf writer中的缓存数据全部广播发送给老的sub session // 并且修改这个sub session的标志 // 让rtmp buf writer来发送这个关键帧 if group.rtmpMergeWriter != nil { group.rtmpMergeWriter.Flush() } session.ShouldWaitVideoKeyFrame = false } }