Scalable video coding (SVC).
This commit is contained in:
@@ -0,0 +1,204 @@
|
||||
// Package packetmap implements remapping of sequence numbers and picture ids.
|
||||
package packetmap
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
const maxEntries = 128
|
||||
|
||||
type Map struct {
|
||||
mu sync.Mutex
|
||||
next uint16
|
||||
nextPid uint16
|
||||
delta uint16
|
||||
pidDelta uint16
|
||||
lastEntry uint16
|
||||
entries []entry
|
||||
}
|
||||
|
||||
type entry struct {
|
||||
first, count uint16
|
||||
delta uint16
|
||||
pidDelta uint16
|
||||
}
|
||||
|
||||
// Map maps a seqno, adding the mapping if required. It returns whether
|
||||
// the seqno could be mapped, the target seqno, and the pid delta to apply.
|
||||
func (m *Map) Map(seqno uint16, pid uint16) (bool, uint16, uint16) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.delta == 0 && m.entries == nil {
|
||||
m.next = seqno + 1
|
||||
m.nextPid = pid
|
||||
return true, seqno, 0
|
||||
}
|
||||
|
||||
if compare(m.next, seqno) <= 0 {
|
||||
if uint16(seqno-m.next) > 8*1024 {
|
||||
m.reset()
|
||||
m.next = seqno + 1
|
||||
m.nextPid = pid
|
||||
return true, seqno, 0
|
||||
}
|
||||
addMapping(m, seqno, pid, m.delta, m.pidDelta)
|
||||
m.next = seqno + 1
|
||||
m.nextPid = pid
|
||||
return true, seqno + m.delta, m.pidDelta
|
||||
}
|
||||
|
||||
if uint16(m.next-seqno) > 8*1024 {
|
||||
m.reset()
|
||||
m.next = seqno + 1
|
||||
m.nextPid = pid
|
||||
return true, seqno, 0
|
||||
}
|
||||
|
||||
return m.direct(seqno)
|
||||
}
|
||||
|
||||
func (m *Map) reset() {
|
||||
m.next = 0
|
||||
m.nextPid = 0
|
||||
m.delta = 0
|
||||
m.pidDelta = 0
|
||||
m.lastEntry = 0
|
||||
m.entries = nil
|
||||
}
|
||||
|
||||
func addMapping(m *Map, seqno, pid uint16, delta, pidDelta uint16) {
|
||||
if len(m.entries) == 0 {
|
||||
m.entries = []entry{
|
||||
entry{
|
||||
first: seqno,
|
||||
count: 1,
|
||||
delta: delta,
|
||||
pidDelta: pidDelta,
|
||||
},
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
i := m.lastEntry
|
||||
if delta == m.entries[i].delta && pidDelta == m.entries[i].pidDelta {
|
||||
m.entries[m.lastEntry].count = seqno - m.entries[i].first + 1
|
||||
return
|
||||
}
|
||||
|
||||
e := entry{
|
||||
first: seqno,
|
||||
count: 1,
|
||||
delta: delta,
|
||||
pidDelta: pidDelta,
|
||||
}
|
||||
|
||||
if len(m.entries) < maxEntries {
|
||||
m.entries = append(m.entries, e)
|
||||
m.lastEntry = uint16(len(m.entries) - 1)
|
||||
return
|
||||
}
|
||||
|
||||
j := (m.lastEntry + 1) % maxEntries
|
||||
m.entries[j] = e
|
||||
m.lastEntry = j
|
||||
}
|
||||
|
||||
// direct maps a seqno to a target seqno. It returns true if the seqno
|
||||
// could be mapped, the target seqno, and the pid delta to apply.
|
||||
// Called with the m.mu taken.
|
||||
func (m *Map) direct(seqno uint16) (bool, uint16, uint16) {
|
||||
if len(m.entries) == 0 {
|
||||
return false, 0, 0
|
||||
}
|
||||
i := m.lastEntry
|
||||
for {
|
||||
f := m.entries[i].first
|
||||
if seqno >= f {
|
||||
if seqno < f+m.entries[i].count {
|
||||
return true,
|
||||
seqno + m.entries[i].delta,
|
||||
m.entries[i].pidDelta
|
||||
}
|
||||
return false, 0, 0
|
||||
}
|
||||
if i > 0 {
|
||||
i--
|
||||
} else {
|
||||
i = uint16(len(m.entries) - 1)
|
||||
}
|
||||
if i == m.lastEntry {
|
||||
break
|
||||
}
|
||||
}
|
||||
return false, 0, 0
|
||||
}
|
||||
|
||||
// Reverse maps a target seqno to the original seqno. It returns true if
|
||||
// the seqno could be mapped, the original seqno, and the pid delta to
|
||||
// apply in reverse.
|
||||
func (m *Map) Reverse(seqno uint16) (bool, uint16, uint16) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.delta == 0 && m.entries == nil {
|
||||
return true, seqno, 0
|
||||
}
|
||||
if m.entries == nil {
|
||||
if m.delta == 0 {
|
||||
return true, seqno, 0
|
||||
}
|
||||
return false, 0, 0
|
||||
}
|
||||
|
||||
i := m.lastEntry
|
||||
for {
|
||||
f := m.entries[i].first + m.entries[i].delta
|
||||
if seqno >= f {
|
||||
if seqno < f+m.entries[i].count {
|
||||
return true,
|
||||
seqno - m.entries[i].delta,
|
||||
m.entries[i].pidDelta
|
||||
}
|
||||
return false, 0, 0
|
||||
}
|
||||
if i > 0 {
|
||||
i--
|
||||
} else {
|
||||
i = uint16(len(m.entries) - 1)
|
||||
}
|
||||
if i == m.lastEntry {
|
||||
break
|
||||
}
|
||||
}
|
||||
return false, 0, 0
|
||||
}
|
||||
|
||||
// Drop attempts to record a dropped packet. It returns true if the
|
||||
// packet is safe to drop.
|
||||
func (m *Map) Drop(seqno uint16, pid uint16) bool {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if seqno != m.next {
|
||||
return false
|
||||
}
|
||||
|
||||
m.pidDelta += pid - m.nextPid
|
||||
m.nextPid = pid
|
||||
|
||||
m.delta--
|
||||
m.next = seqno + 1
|
||||
return true
|
||||
}
|
||||
|
||||
// compare performs comparison modulo 2^16.
|
||||
func compare(s1, s2 uint16) int {
|
||||
if s1 == s2 {
|
||||
return 0
|
||||
}
|
||||
if ((s2 - s1) & 0x8000) != 0 {
|
||||
return 1
|
||||
}
|
||||
return -1
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package packetmap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestNoDrops(t *testing.T) {
|
||||
m := Map{}
|
||||
|
||||
ok, s, p := m.Map(42, 1001)
|
||||
if !ok || s != 42 || p != 0 {
|
||||
t.Errorf("Expected 42, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(43, 1001)
|
||||
if !ok || s != 43 || p != 0 {
|
||||
t.Errorf("Expected 43, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(44, 1002)
|
||||
if !ok || s != 44 || p != 0 {
|
||||
t.Errorf("Expected 43, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(40, 1000)
|
||||
if !ok || s != 40 || p != 0 {
|
||||
t.Errorf("Expected 40, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
if len(m.entries) > 0 || m.delta != 0 || m.pidDelta != 0 {
|
||||
t.Errorf("Expected 0, got %v %v %v",
|
||||
len(m.entries), m.delta, m.pidDelta)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDrop(t *testing.T) {
|
||||
m := Map{}
|
||||
|
||||
ok, s, p := m.Map(42, 1001)
|
||||
if !ok || s != 42 || p != 0 {
|
||||
t.Errorf("Expected 42, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok = m.Drop(43, 1001)
|
||||
if !ok || m.pidDelta != 0 {
|
||||
t.Errorf("Expected 0, got %v, %v", ok, m.pidDelta)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(44, 1001)
|
||||
if !ok || s != 43 || p != 0 {
|
||||
t.Errorf("Expected 43, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(45, 1002)
|
||||
if !ok || s != 44 || p != 0 {
|
||||
t.Errorf("Expected 44, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok = m.Drop(46, 1003)
|
||||
if !ok || m.pidDelta != 1 {
|
||||
t.Errorf("Expected 1, got %v, %v", ok, m.pidDelta)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(47, 1003)
|
||||
if !ok || s != 45 || p != 1 {
|
||||
t.Errorf("Expected 45, 1, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok = m.Drop(48, 1003)
|
||||
if !ok || m.pidDelta != 1 {
|
||||
t.Errorf("Expected 1, got %v, %v", ok, m.pidDelta)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(49, 1003)
|
||||
if !ok || s != 46 || p != 1 {
|
||||
t.Errorf("Expected 45, 1, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(60, 1007)
|
||||
if !ok || s != 57 || p != 1 {
|
||||
t.Errorf("Expected 57, 1, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(13, 1000)
|
||||
if ok {
|
||||
t.Errorf("Expected not ok")
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(44, 1001)
|
||||
if !ok || s != 43 || p != 0 {
|
||||
t.Errorf("Expected 43, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(45, 1002)
|
||||
if !ok || s != 44 || p != 0 {
|
||||
t.Errorf("Expected 44, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(48, 3)
|
||||
if ok {
|
||||
t.Errorf("Expected not ok")
|
||||
}
|
||||
|
||||
ok, s, p = m.direct(1000)
|
||||
if ok {
|
||||
t.Errorf("Expected not ok")
|
||||
}
|
||||
|
||||
ok, s, p = m.direct(13)
|
||||
if ok {
|
||||
t.Errorf("Expected not ok")
|
||||
}
|
||||
|
||||
ok, s, p = m.Reverse(44)
|
||||
if !ok || s != 45 || p != 0 {
|
||||
t.Errorf("Expected 45, 0, got %v %v %v", ok, s, p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWraparound(t *testing.T) {
|
||||
m := Map{}
|
||||
|
||||
ok, s, p := m.Map(0, 0)
|
||||
if !ok || s != 0 || p != 0 {
|
||||
t.Errorf("Expected 0, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(1, 0)
|
||||
if !ok || s != 1 || p != 0 {
|
||||
t.Errorf("Expected 1, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok = m.Drop(2, 1)
|
||||
if !ok || m.pidDelta != 1 {
|
||||
t.Errorf("Expected 1, got %v, %v", ok, m.pidDelta)
|
||||
}
|
||||
|
||||
ok = m.Drop(3, 1)
|
||||
if !ok || m.pidDelta != 1 {
|
||||
t.Errorf("Expected 1, got %v, %v", ok, m.pidDelta)
|
||||
}
|
||||
|
||||
for i := 4; i < 256000; i++ {
|
||||
ok, s, p = m.Map(uint16(i), uint16((i/2) & 0x7FFF))
|
||||
if !ok || s != uint16(i-2) || p != 1 {
|
||||
t.Errorf("Expected %v, %v, got %v, %v, %v",
|
||||
uint16(i-2), 1, ok, s, p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestReset(t *testing.T) {
|
||||
m := Map{}
|
||||
|
||||
ok, s, p := m.Map(42, 1001)
|
||||
if !ok || s != 42 || p != 0 {
|
||||
t.Errorf("Expected 42, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok = m.Drop(43, 1001)
|
||||
if !ok || m.pidDelta != 0 {
|
||||
t.Errorf("Expected 0, got %v, %v", ok, m.pidDelta)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(44, 1001)
|
||||
if !ok || s != 43 || p != 0 {
|
||||
t.Errorf("Expected 43, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(40000, 2001)
|
||||
if !ok || s != 40000 || p != 0 {
|
||||
t.Errorf("Expected 32000, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
|
||||
if m.delta != 0 || m.entries != nil {
|
||||
t.Errorf("Expected reset")
|
||||
}
|
||||
|
||||
ok, s, p = m.Map(40001, 2001)
|
||||
if !ok || s != 40001 || p != 0 {
|
||||
t.Errorf("Expected 32001, 0, got %v, %v, %v", ok, s, p)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user