first commit
This commit is contained in:
73
av/pktque/buf.go
Normal file
73
av/pktque/buf.go
Normal file
@@ -0,0 +1,73 @@
|
||||
package pktque
|
||||
|
||||
import (
|
||||
"github.com/deepch/vdk/av"
|
||||
)
|
||||
|
||||
type Buf struct {
|
||||
Head, Tail BufPos
|
||||
pkts []av.Packet
|
||||
Size int
|
||||
Count int
|
||||
}
|
||||
|
||||
func NewBuf() *Buf {
|
||||
return &Buf{
|
||||
pkts: make([]av.Packet, 64),
|
||||
}
|
||||
}
|
||||
|
||||
func (self *Buf) Pop() av.Packet {
|
||||
if self.Count == 0 {
|
||||
panic("pktque.Buf: Pop() when count == 0")
|
||||
}
|
||||
|
||||
i := int(self.Head) & (len(self.pkts) - 1)
|
||||
pkt := self.pkts[i]
|
||||
self.pkts[i] = av.Packet{}
|
||||
self.Size -= len(pkt.Data)
|
||||
self.Head++
|
||||
self.Count--
|
||||
|
||||
return pkt
|
||||
}
|
||||
|
||||
func (self *Buf) grow() {
|
||||
newpkts := make([]av.Packet, len(self.pkts)*2)
|
||||
for i := self.Head; i.LT(self.Tail); i++ {
|
||||
newpkts[int(i)&(len(newpkts)-1)] = self.pkts[int(i)&(len(self.pkts)-1)]
|
||||
}
|
||||
self.pkts = newpkts
|
||||
}
|
||||
|
||||
func (self *Buf) Push(pkt av.Packet) {
|
||||
if self.Count == len(self.pkts) {
|
||||
self.grow()
|
||||
}
|
||||
self.pkts[int(self.Tail)&(len(self.pkts)-1)] = pkt
|
||||
self.Tail++
|
||||
self.Count++
|
||||
self.Size += len(pkt.Data)
|
||||
}
|
||||
|
||||
func (self *Buf) Get(pos BufPos) av.Packet {
|
||||
return self.pkts[int(pos)&(len(self.pkts)-1)]
|
||||
}
|
||||
|
||||
func (self *Buf) IsValidPos(pos BufPos) bool {
|
||||
return pos.GE(self.Head) && pos.LT(self.Tail)
|
||||
}
|
||||
|
||||
type BufPos int
|
||||
|
||||
func (self BufPos) LT(pos BufPos) bool {
|
||||
return self-pos < 0
|
||||
}
|
||||
|
||||
func (self BufPos) GE(pos BufPos) bool {
|
||||
return self-pos >= 0
|
||||
}
|
||||
|
||||
func (self BufPos) GT(pos BufPos) bool {
|
||||
return self-pos > 0
|
||||
}
|
||||
190
av/pktque/filters.go
Normal file
190
av/pktque/filters.go
Normal file
@@ -0,0 +1,190 @@
|
||||
// Package pktque provides packet Filter interface and structures used by other components.
|
||||
package pktque
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/deepch/vdk/av"
|
||||
)
|
||||
|
||||
type Filter interface {
|
||||
// Change packet time or drop packet
|
||||
ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error)
|
||||
}
|
||||
|
||||
// Combine multiple Filters into one, ModifyPacket will be called in order.
|
||||
type Filters []Filter
|
||||
|
||||
func (self Filters) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {
|
||||
for _, filter := range self {
|
||||
if drop, err = filter.ModifyPacket(pkt, streams, videoidx, audioidx); err != nil {
|
||||
return
|
||||
}
|
||||
if drop {
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Wrap origin Demuxer and Filter into a new Demuxer, when read this Demuxer filters will be called.
|
||||
type FilterDemuxer struct {
|
||||
av.Demuxer
|
||||
Filter Filter
|
||||
streams []av.CodecData
|
||||
videoidx int
|
||||
audioidx int
|
||||
}
|
||||
|
||||
func (self FilterDemuxer) ReadPacket() (pkt av.Packet, err error) {
|
||||
if self.streams == nil {
|
||||
if self.streams, err = self.Demuxer.Streams(); err != nil {
|
||||
return
|
||||
}
|
||||
for i, stream := range self.streams {
|
||||
if stream.Type().IsVideo() {
|
||||
self.videoidx = i
|
||||
} else if stream.Type().IsAudio() {
|
||||
self.audioidx = i
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
if pkt, err = self.Demuxer.ReadPacket(); err != nil {
|
||||
return
|
||||
}
|
||||
var drop bool
|
||||
if drop, err = self.Filter.ModifyPacket(&pkt, self.streams, self.videoidx, self.audioidx); err != nil {
|
||||
return
|
||||
}
|
||||
if !drop {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Drop packets until first video key frame arrived.
|
||||
type WaitKeyFrame struct {
|
||||
ok bool
|
||||
}
|
||||
|
||||
func (self *WaitKeyFrame) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {
|
||||
if !self.ok && pkt.Idx == int8(videoidx) && pkt.IsKeyFrame {
|
||||
self.ok = true
|
||||
}
|
||||
drop = !self.ok
|
||||
return
|
||||
}
|
||||
|
||||
// Fix incorrect packet timestamps.
|
||||
type FixTime struct {
|
||||
zerobase time.Duration
|
||||
incrbase time.Duration
|
||||
lasttime time.Duration
|
||||
StartFromZero bool // make timestamp start from zero
|
||||
MakeIncrement bool // force timestamp increment
|
||||
}
|
||||
|
||||
func (self *FixTime) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {
|
||||
if self.StartFromZero {
|
||||
if self.zerobase == 0 {
|
||||
self.zerobase = pkt.Time
|
||||
}
|
||||
pkt.Time -= self.zerobase
|
||||
}
|
||||
|
||||
if self.MakeIncrement {
|
||||
pkt.Time -= self.incrbase
|
||||
if self.lasttime == 0 {
|
||||
self.lasttime = pkt.Time
|
||||
}
|
||||
if pkt.Time < self.lasttime || pkt.Time > self.lasttime+time.Millisecond*500 {
|
||||
self.incrbase += pkt.Time - self.lasttime
|
||||
pkt.Time = self.lasttime
|
||||
}
|
||||
self.lasttime = pkt.Time
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Drop incorrect packets to make A/V sync.
|
||||
type AVSync struct {
|
||||
MaxTimeDiff time.Duration
|
||||
time []time.Duration
|
||||
}
|
||||
|
||||
func (self *AVSync) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {
|
||||
if self.time == nil {
|
||||
self.time = make([]time.Duration, len(streams))
|
||||
if self.MaxTimeDiff == 0 {
|
||||
self.MaxTimeDiff = time.Millisecond * 500
|
||||
}
|
||||
}
|
||||
|
||||
start, end, correctable, correcttime := self.check(int(pkt.Idx))
|
||||
if pkt.Time >= start && pkt.Time < end {
|
||||
self.time[pkt.Idx] = pkt.Time
|
||||
} else {
|
||||
if correctable {
|
||||
pkt.Time = correcttime
|
||||
for i := range self.time {
|
||||
self.time[i] = correcttime
|
||||
}
|
||||
} else {
|
||||
drop = true
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *AVSync) check(i int) (start time.Duration, end time.Duration, correctable bool, correcttime time.Duration) {
|
||||
minidx := -1
|
||||
maxidx := -1
|
||||
for j := range self.time {
|
||||
if minidx == -1 || self.time[j] < self.time[minidx] {
|
||||
minidx = j
|
||||
}
|
||||
if maxidx == -1 || self.time[j] > self.time[maxidx] {
|
||||
maxidx = j
|
||||
}
|
||||
}
|
||||
allthesame := self.time[minidx] == self.time[maxidx]
|
||||
|
||||
if i == maxidx {
|
||||
if allthesame {
|
||||
correctable = true
|
||||
} else {
|
||||
correctable = false
|
||||
}
|
||||
} else {
|
||||
correctable = true
|
||||
}
|
||||
|
||||
start = self.time[minidx]
|
||||
end = start + self.MaxTimeDiff
|
||||
correcttime = start + time.Millisecond*40
|
||||
return
|
||||
}
|
||||
|
||||
// Make packets reading speed as same as walltime, effect like ffmpeg -re option.
|
||||
type Walltime struct {
|
||||
firsttime time.Time
|
||||
}
|
||||
|
||||
func (self *Walltime) ModifyPacket(pkt *av.Packet, streams []av.CodecData, videoidx int, audioidx int) (drop bool, err error) {
|
||||
if pkt.Idx == 0 {
|
||||
if self.firsttime.IsZero() {
|
||||
self.firsttime = time.Now()
|
||||
}
|
||||
pkttime := self.firsttime.Add(pkt.Time)
|
||||
delta := pkttime.Sub(time.Now())
|
||||
if delta > 0 {
|
||||
time.Sleep(delta)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
61
av/pktque/timeline.go
Normal file
61
av/pktque/timeline.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package pktque
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
/*
|
||||
pop push
|
||||
|
||||
seg seg seg
|
||||
|--------| |---------| |---|
|
||||
20ms 40ms 5ms
|
||||
----------------- time -------------------->
|
||||
headtm tailtm
|
||||
*/
|
||||
|
||||
type tlSeg struct {
|
||||
tm, dur time.Duration
|
||||
}
|
||||
|
||||
type Timeline struct {
|
||||
segs []tlSeg
|
||||
headtm time.Duration
|
||||
}
|
||||
|
||||
func (self *Timeline) Push(tm time.Duration, dur time.Duration) {
|
||||
if len(self.segs) > 0 {
|
||||
tail := self.segs[len(self.segs)-1]
|
||||
diff := tm-(tail.tm+tail.dur)
|
||||
if diff < 0 {
|
||||
tm -= diff
|
||||
}
|
||||
}
|
||||
self.segs = append(self.segs, tlSeg{tm, dur})
|
||||
}
|
||||
|
||||
func (self *Timeline) Pop(dur time.Duration) (tm time.Duration) {
|
||||
if len(self.segs) == 0 {
|
||||
return self.headtm
|
||||
}
|
||||
|
||||
tm = self.segs[0].tm
|
||||
for dur > 0 && len(self.segs) > 0 {
|
||||
seg := &self.segs[0]
|
||||
sub := dur
|
||||
if seg.dur < sub {
|
||||
sub = seg.dur
|
||||
}
|
||||
seg.dur -= sub
|
||||
dur -= sub
|
||||
seg.tm += sub
|
||||
self.headtm += sub
|
||||
if seg.dur == 0 {
|
||||
copy(self.segs[0:], self.segs[1:])
|
||||
self.segs = self.segs[:len(self.segs)-1]
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user