You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lal/httpflv/gopcache.go

265 lines
5.7 KiB
Go

package httpflv
import (
"bytes"
"github.com/q191201771/lal/log"
"sync"
)
type Gop struct {
tags []*Tag
//raw []byte
firstTimestamp uint32
}
type GopCache struct {
gopNum int
metadata *Tag
avcSeqHeader *Tag
aacSeqHeader *Tag
gops []*Gop // TODO chef: maybe use other container to mock a queue
mutex sync.Mutex
}
// gopNum: 0 means only cache metadata, avc seq header, aac seq header
func NewGopCache(gopNum int) *GopCache {
return &GopCache{
gopNum: gopNum,
}
}
func (c *GopCache) Push(tag *Tag) {
c.mutex.Lock()
defer c.mutex.Unlock()
if tag.isMetaData() {
// TODO chef: will this happen?
if c.metadata != nil {
log.Debugf("updating metadata.")
log.Debug(tag.Header, tag.Raw[tagHeaderSize:])
log.Debug(c.metadata.Header, c.metadata.Raw[tagHeaderSize:])
c.clearGop()
}
c.metadata = tag
}
if tag.isAvcKeySeqHeader() {
//log.Debug(parseAvcSeqHeader(tag.Raw[tagHeaderSize:]))
if c.avcSeqHeader == nil {
c.avcSeqHeader = tag
} else {
// TODO chef: compare nessary? if other way to update seq header and handle cache stuff?
if bytes.Compare(tag.Raw[tagHeaderSize:], c.avcSeqHeader.Raw[tagHeaderSize:]) == 0 {
// noop
} else {
log.Debugf("updating avc seq header.")
log.Debug(tag.Header, tag.Raw[tagHeaderSize:])
log.Debug(c.avcSeqHeader.Header, c.avcSeqHeader.Raw[tagHeaderSize:])
c.clearGop()
c.avcSeqHeader = tag
}
}
}
if tag.isAacSeqHeader() {
if c.aacSeqHeader == nil {
c.aacSeqHeader = tag
} else {
if bytes.Compare(tag.Raw[tagHeaderSize:], c.aacSeqHeader.Raw[tagHeaderSize:]) == 0 {
// noop
} else {
log.Debugf("updating aac seq header.")
c.clearGop()
c.aacSeqHeader = tag
}
}
c.aacSeqHeader = tag
}
if c.gopNum == 0 {
return
}
if len(c.gops) == 0 {
if tag.isAvcKeyNalu() {
gop := &Gop{}
gop.firstTimestamp = tag.Header.Timestamp
gop.tags = append(gop.tags, tag)
//gop.raw = append(gop.raw, tag.Raw...)
c.gops = append(c.gops, gop)
c.syncOldestKeyNaluTimestampToSeqHeader()
}
} else {
if tag.isAvcKeyNalu() {
gop := &Gop{}
gop.firstTimestamp = tag.Header.Timestamp
gop.tags = append(gop.tags, tag)
//gop.raw = append(gop.raw, tag.Raw...)
c.gops = append(c.gops, gop)
if len(c.gops) > c.gopNum+1 {
c.gops = c.gops[1:]
c.syncOldestKeyNaluTimestampToSeqHeader()
}
} else {
//c.gops[len(c.gops)-1].raw = append(c.gops[len(c.gops)-1].raw, tag.Raw...)
c.gops[len(c.gops)-1].tags = append(c.gops[len(c.gops)-1].tags, tag)
}
}
}
func (c *GopCache) WriteWholeThings(writer Writer) (hasKeyFrame bool) {
if tag := c.getMetadata(); tag != nil {
writer.Write(tag)
}
avc := c.getAvcSeqHeader()
aac := c.getAacSeqHeader()
// TODO chef: if nessary to sort them by timestamp
if avc != nil && aac != nil {
if avc.Header.Timestamp <= aac.Header.Timestamp {
writer.Write(avc)
writer.Write(aac)
} else {
writer.Write(aac)
writer.Write(avc)
}
} else if avc != nil && aac == nil {
writer.Write(avc)
} else if avc == nil && aac != nil {
writer.Write(aac)
}
c.writeGops(writer, false)
//if gops := c.getGops(false); gops != nil {
// res = append(res, gops...)
// log.Debug("cache match.")
// hasKeyFrame = true
//}
return
}
//func (c *GopCache) GetWholeThings() (hasKeyFrame bool, res []byte) {
// if tag := c.getMetadata(); tag != nil {
// res = append(res, tag.Raw...)
// }
//
// avc := c.getAvcSeqHeader()
// aac := c.getAacSeqHeader()
// // TODO chef: if nessary to sort them by timestamp
// if avc != nil && aac != nil {
// if avc.Header.Timestamp <= aac.Header.Timestamp {
// res = append(res, avc.Raw...)
// res = append(res, aac.Raw...)
// } else {
// res = append(res, aac.Raw...)
// res = append(res, avc.Raw...)
// }
// } else if avc != nil && aac == nil {
// res = append(res, avc.Raw...)
// } else if avc == nil && aac != nil {
// res = append(res, aac.Raw...)
// }
//
// if gops := c.getGops(false); gops != nil {
// res = append(res, gops...)
// log.Debug("cache match.")
// hasKeyFrame = true
// }
// return
//}
func (c *GopCache) ClearAll() {
c.mutex.Lock()
defer c.mutex.Unlock()
c.metadata = nil
c.avcSeqHeader = nil
c.aacSeqHeader = nil
c.gops = nil
}
func (c *GopCache) writeGops(write Writer, mustCompleted bool) bool {
c.mutex.Lock()
defer c.mutex.Unlock()
neededLen := len(c.gops)
if mustCompleted {
neededLen--
}
if neededLen <= 0 {
return false
}
for i := 0; i != neededLen; i++ {
for j := 0; j != len(c.gops[i].tags); j++ {
write.Write(c.gops[i].tags[j])
}
}
return true
}
//func (c *GopCache) getGops(mustCompleted bool) []byte {
// c.mutex.Lock()
// defer c.mutex.Unlock()
//
// neededLen := len(c.gops)
// if mustCompleted {
// neededLen--
// }
// if neededLen <= 0 {
// return nil
// }
//
// var res []byte
// for i := 0; i != neededLen; i++ {
// for j := 0; j != len(c.gops[i].tags); j++ {
// res = append(res, c.gops[i].tags[j].Raw...)
// }
// //res = append(res, c.gops[i].raw...)
// }
// return res
//}
func (c *GopCache) getMetadata() (res *Tag) {
c.mutex.Lock()
defer c.mutex.Unlock()
if c.metadata != nil {
res = c.metadata.cloneTag()
}
return
}
func (c *GopCache) getAvcSeqHeader() (res *Tag) {
c.mutex.Lock()
defer c.mutex.Unlock()
if c.avcSeqHeader != nil {
res = c.avcSeqHeader.cloneTag()
}
return
}
func (c *GopCache) getAacSeqHeader() (res *Tag) {
c.mutex.Lock()
defer c.mutex.Unlock()
if c.aacSeqHeader != nil {
res = c.aacSeqHeader.cloneTag()
}
return
}
func (c *GopCache) clearGop() {
log.Debug("clearGop")
c.gops = nil
}
// TODO chef: if nessary
func (c *GopCache) syncOldestKeyNaluTimestampToSeqHeader() {
ts := c.gops[0].firstTimestamp
if c.avcSeqHeader != nil {
c.avcSeqHeader.Header.Timestamp = ts
}
if c.aacSeqHeader != nil {
c.aacSeqHeader.Header.Timestamp = ts
}
}