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

215 lines
5.7 KiB
Go

// 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"
)
3 years ago
// TODO(chef): refactor 本文件移出package logic
// 考虑以下两种场景:
// - 只有上行没有下行没有必要做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
}
// ---------------------------------------------------------------------------------------------------------------------
// 提供两个功能:
// 1. 缓存Metadata, VideoSeqHeader, AacSeqHeader
// 2. 缓存音视频GOP数据
//
// 以下只讨论GopCache的第2点功能
//
// 音频和视频都会缓存
//
// GopCache也可能不缓存GOP数据见NewGopCache函数的gopNum参数说明
//
// 以下我们只讨论gopNum > 0(也即gopSize > 1)的情况
//
// GopCache为空时只有输入了关键帧才能开启GOP缓存非关键帧以及音频数据不会被缓存
// 因此单音频的流是ok的相当于不缓存任何数据
//
// GopCache不为空时输入关键帧触发生成新的GOP元素其他情况则往最后一个GOP元素一直追加
//
// first用于读取第一个GOP可能不完整last的前一个用于写入当前GOP
//
// 最近不完整的GOP也会被缓存见NewGopCache函数的gopNum参数说明
//
// -----
// gopNum = 1
// gopSize = 2
//
// first | first | first | 在后面两个状态间转换,就不画了
// | | | | | |
// 0 1 | 0 1 | 0 1 |
// * * | * * | * * |
// | | | | | |
// last | last | last |
// | | |
// (empty) | (full) | (full) |
// GetGopCount: 0 | 1 | 1 |
// -----
//
//
type GopCache struct {
t string
uniqueKey string
Metadata []byte
VideoSeqHeader []byte
AacSeqHeader []byte
gopRing []Gop
gopRingFirst int
gopRingLast int
gopSize int
}
// @param gopNum: gop缓存大小
// 如果为0则不缓存音频数据也即GOP缓存功能不生效
// 如果>0则缓存<gopNum>个完整GOP另外还可能有半个最近不完整的GOP
//
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
}
}
if gc.gopSize > 1 {
if msg.IsVideoKeyNalu() {
gc.feedNewGop(msg, lg())
} else {
gc.feedLastGop(msg, lg())
}
}
}
// 获取GOP数量注意最后一个可能是不完整的
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
}
// 往最后一个GOP元素追加一个msg
// 注意如果GopCache为空则不缓存msg
func (gc *GopCache) feedLastGop(msg base.RtmpMsg, b []byte) {
if !gc.isGopRingEmpty() {
gc.gopRing[(gc.gopRingLast-1+gc.gopSize)%gc.gopSize].Feed(msg, b)
}
}
// 生成一个最新的GOP元素并往里追加一个msg
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]
}