Files
lkm/jitterbuffer/priority_queue.go
2024-05-03 22:50:45 +08:00

190 lines
3.9 KiB
Go

// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT
package jitterbuffer
import (
"errors"
"github.com/pion/rtp"
)
// PriorityQueue provides a linked list sorting of RTP packets by SequenceNumber
type PriorityQueue struct {
next *node
length uint16
}
type node struct {
val *rtp.Packet
next *node
prev *node
priority uint16
}
var (
// ErrInvalidOperation may be returned if a Pop or Find operation is performed on an empty queue
ErrInvalidOperation = errors.New("attempt to find or pop on an empty list")
// ErrNotFound will be returned if the packet cannot be found in the queue
ErrNotFound = errors.New("priority not found")
)
// NewQueue will create a new PriorityQueue whose order relies on monotonically
// increasing Sequence Number, wrapping at MaxUint16, so
// a packet with sequence number MaxUint16 - 1 will be after 0
func NewQueue() *PriorityQueue {
return &PriorityQueue{
next: nil,
length: 0,
}
}
func newNode(val *rtp.Packet, priority uint16) *node {
return &node{
val: val,
prev: nil,
next: nil,
priority: priority,
}
}
// Find a packet in the queue with the provided sequence number,
// regardless of position (the packet is retained in the queue)
func (q *PriorityQueue) Find(sqNum uint16) (*rtp.Packet, error) {
next := q.next
for next != nil {
if next.priority == sqNum {
return next.val, nil
}
next = next.next
}
return nil, ErrNotFound
}
// Push will insert a packet in to the queue in order of sequence number
func (q *PriorityQueue) Push(val *rtp.Packet, priority uint16) {
newPq := newNode(val, priority)
if q.next == nil {
q.next = newPq
q.length++
return
}
if priority < q.next.priority {
newPq.next = q.next
q.next.prev = newPq
q.next = newPq
q.length++
return
}
head := q.next
prev := q.next
for head != nil {
if priority <= head.priority {
break
}
prev = head
head = head.next
}
if head == nil {
if prev != nil {
prev.next = newPq
}
newPq.prev = prev
} else {
newPq.next = head
newPq.prev = prev
if prev != nil {
prev.next = newPq
}
head.prev = newPq
}
q.length++
}
// Length will get the total length of the queue
func (q *PriorityQueue) Length() uint16 {
return q.length
}
// Pop removes the first element from the queue, regardless
// sequence number
func (q *PriorityQueue) Pop() (*rtp.Packet, error) {
if q.next == nil {
return nil, ErrInvalidOperation
}
val := q.next.val
q.length--
q.next = q.next.next
return val, nil
}
// PopAt removes an element at the specified sequence number (priority)
func (q *PriorityQueue) PopAt(sqNum uint16) (*rtp.Packet, error) {
if q.next == nil {
return nil, ErrInvalidOperation
}
if q.next.priority == sqNum {
val := q.next.val
q.next = q.next.next
q.length--
return val, nil
}
pos := q.next
prev := q.next.prev
for pos != nil {
if pos.priority == sqNum {
val := pos.val
prev.next = pos.next
if prev.next != nil {
prev.next.prev = prev
}
q.length--
return val, nil
}
prev = pos
pos = pos.next
}
return nil, ErrNotFound
}
// PopAtTimestamp removes and returns a packet at the given RTP Timestamp, regardless
// sequence number order
func (q *PriorityQueue) PopAtTimestamp(timestamp uint32) (*rtp.Packet, error) {
if q.next == nil {
return nil, ErrInvalidOperation
}
if q.next.val.Timestamp == timestamp {
val := q.next.val
q.next = q.next.next
q.length--
return val, nil
}
pos := q.next
prev := q.next.prev
for pos != nil {
if pos.val.Timestamp == timestamp {
val := pos.val
prev.next = pos.next
if prev.next != nil {
prev.next.prev = prev
}
q.length--
return val, nil
}
prev = pos
pos = pos.next
}
return nil, ErrNotFound
}
// Clear will empty a PriorityQueue
func (q *PriorityQueue) Clear() {
next := q.next
q.length = 0
for next != nil {
next.prev = nil
next = next.next
}
}