// Copyright 2022, 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 rtprtcp import ( "fmt" "github.com/q191201771/naza/pkg/nazabytes" ) type RtpPacketListItem struct { Packet RtpPacket Next *RtpPacketListItem } // RtpPacketList rtp packet的有序链表,前面的seq小于后面的seq // // 为什么不用红黑树等查找性能更高的有序kv结构? // 第一,容器有最大值,这个数量级用啥容器都差不多, // 第二,插入时,99.99%的seq号是当前最大号附近的,遍历找就可以了, // 注意,这个链表并不是一个定长容器,当数据有序时,容器内缓存的数据是一个帧的数据。 type RtpPacketList struct { // TODO(chef): [refactor] 隐藏这两个变量的访问权限 202207 Head RtpPacketListItem // 哨兵,自身不存放rtp包,第一个rtp包存在在head.next中 Size int // 实际元素个数 doneSeqFlag bool // 如果为false,则说明我们是初始化阶段,还不知道需要的packet的seq是多少 doneSeq uint16 // 已处理的seq号,之后我们需要seq+1的packet maxSize int } // IsStale 是否过期 func (l *RtpPacketList) IsStale(seq uint16) bool { if !l.doneSeqFlag { return false } // 序号太小 return CompareSeq(seq, l.doneSeq) <= 0 } // Insert 插入有序链表,并去重 func (l *RtpPacketList) Insert(pkt RtpPacket) { // 遍历查找插入位置 p := &l.Head // TODO(chef): [perf] 考虑优化成从后往前查找,提高查找效率 202207 for ; p.Next != nil; p = p.Next { res := CompareSeq(pkt.Header.Seq, p.Next.Packet.Header.Seq) switch res { case 0: // 包已经存在,不需要插入了 return case 1: // noop case -1: item := &RtpPacketListItem{ Packet: pkt, Next: p.Next, } p.Next = item l.Size++ return } } item := &RtpPacketListItem{ Packet: pkt, Next: p.Next, } p.Next = item l.Size++ return } // PopFirst 弹出第一个包。注意,调用方保证容器不为空时调用 func (l *RtpPacketList) PopFirst() RtpPacket { pkt := l.Head.Next.Packet l.Head.Next = l.Head.Next.Next l.Size-- return pkt } // PeekFirst 查看第一个包。注意,调用方保证容器不为空时调用 func (l *RtpPacketList) PeekFirst() RtpPacket { return l.Head.Next.Packet } // InitMaxSize 设置容器最大容量 func (l *RtpPacketList) InitMaxSize(maxSize int) { l.maxSize = maxSize } // Full 是否已经满了 func (l *RtpPacketList) Full() bool { return l.Size >= l.maxSize } // IsFirstSequential 第一个包是否是需要的(与之前已处理的是连续的) func (l *RtpPacketList) IsFirstSequential() bool { first := l.Head.Next if first == nil { return false } if !l.doneSeqFlag { return true } return SubSeq(first.Packet.Header.Seq, l.doneSeq) == 1 } // SetDoneSeq 设置已处理的包序号,比如已经成功合成了,或者主动丢弃到该位置结束丢弃了 func (l *RtpPacketList) SetDoneSeq(seq uint16) { l.doneSeqFlag = true l.doneSeq = seq } func (l *RtpPacketList) Reset() { l.doneSeqFlag = false l.doneSeq = 0 l.Head.Next = nil } func (l *RtpPacketList) DebugString() string { p := l.Head.Next buf := nazabytes.NewBuffer(65535) buf.WriteString(fmt.Sprintf("size=%d, doneSeq=%d", l.Size, l.doneSeq)) buf.WriteString(" [") for p != nil { buf.WriteString(fmt.Sprintf("%d ", p.Packet.Header.Seq)) p = p.Next } buf.WriteString("]") return buf.String() }