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/pkg/logic/gop_cache.go

165 lines
3.7 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Copyright 2019, 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 logic
import (
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/remux"
"github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/naza/pkg/nazalog"
)
// 考虑以下两种场景:
// - 只有上行没有下行没有必要做rtmp chunk切片的操作
// - 有多个下行只需要做一次rtmp chunk切片
// 所以这一步做了懒处理
type LazyChunkDivider struct {
message []byte
header *base.RTMPHeader
chunks []byte
}
func (lcd *LazyChunkDivider) Init(message []byte, header *base.RTMPHeader) {
lcd.message = message
lcd.header = header
}
func (lcd *LazyChunkDivider) Get() []byte {
if lcd.chunks == nil {
lcd.chunks = rtmp.Message2Chunks(lcd.message, lcd.header)
}
return lcd.chunks
}
// 懒转换
type LazyRTMPMsg2FLVTag struct {
msg base.RTMPMsg
tag []byte
}
func (l *LazyRTMPMsg2FLVTag) Init(msg base.RTMPMsg) {
l.msg = msg
}
func (l *LazyRTMPMsg2FLVTag) Get() []byte {
if l.tag == nil {
l.tag = remux.RTMPMsg2FLVTag(l.msg).Raw
}
return l.tag
}
type GOPCache struct {
t string
uniqueKey string
Metadata []byte
VideoSeqHeader []byte
AACSeqHeader []byte
gopRing []GOP
gopRingFirst int
gopRingLast int
gopSize int
}
func NewGOPCache(t string, uniqueKey string, gopNum int) *GOPCache {
return &GOPCache{
t: t,
uniqueKey: uniqueKey,
gopSize: gopNum + 1,
gopRing: make([]GOP, gopNum+1, gopNum+1),
gopRingFirst: 0,
gopRingLast: 0,
}
}
type LazyGet func() []byte
func (gc *GOPCache) Feed(msg base.RTMPMsg, lg LazyGet) {
switch msg.Header.MsgTypeID {
case base.RTMPTypeIDMetadata:
gc.Metadata = lg()
nazalog.Debugf("[%s] cache %s metadata. size:%d", gc.uniqueKey, gc.t, len(gc.Metadata))
return
case base.RTMPTypeIDAudio:
if msg.IsAACSeqHeader() {
gc.AACSeqHeader = lg()
nazalog.Debugf("[%s] cache %s aac seq header. size:%d", gc.uniqueKey, gc.t, len(gc.AACSeqHeader))
return
}
case base.RTMPTypeIDVideo:
if msg.IsVideoKeySeqHeader() {
gc.VideoSeqHeader = lg()
nazalog.Debugf("[%s] cache %s video seq header. size:%d", gc.uniqueKey, gc.t, len(gc.VideoSeqHeader))
return
}
}
// 这个size的判断去掉也行
if gc.gopSize > 1 {
if msg.IsVideoKeyNALU() {
gc.feedNewGOP(msg, lg())
} else {
gc.feedLastGOP(msg, lg())
}
}
}
func (gc *GOPCache) GetGOPCount() int {
return (gc.gopRingLast + gc.gopSize - gc.gopRingFirst) % gc.gopSize
}
func (gc *GOPCache) GetGOPDataAt(pos int) [][]byte {
if pos >= gc.GetGOPCount() || pos < 0 {
return nil
}
return gc.gopRing[(pos+gc.gopRingFirst)%gc.gopSize].data
}
func (gc *GOPCache) Clear() {
gc.Metadata = nil
gc.VideoSeqHeader = nil
gc.AACSeqHeader = nil
gc.gopRingLast = 0
gc.gopRingFirst = 0
}
func (gc *GOPCache) feedLastGOP(msg base.RTMPMsg, b []byte) {
if !gc.isGOPRingEmpty() {
gc.gopRing[(gc.gopRingLast-1+gc.gopSize)%gc.gopSize].Feed(msg, b)
}
}
func (gc *GOPCache) feedNewGOP(msg base.RTMPMsg, b []byte) {
if gc.isGOPRingFull() {
gc.gopRingFirst = (gc.gopRingFirst + 1) % gc.gopSize
}
gc.gopRing[gc.gopRingLast].Clear()
gc.gopRing[gc.gopRingLast].Feed(msg, b)
gc.gopRingLast = (gc.gopRingLast + 1) % gc.gopSize
}
func (gc *GOPCache) isGOPRingFull() bool {
return (gc.gopRingLast+1)%gc.gopSize == gc.gopRingFirst
}
func (gc *GOPCache) isGOPRingEmpty() bool {
return gc.gopRingFirst == gc.gopRingLast
}
type GOP struct {
data [][]byte
}
func (g *GOP) Feed(msg base.RTMPMsg, b []byte) {
g.data = append(g.data, b)
}
func (g *GOP) Clear() {
g.data = g.data[:0]
}