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

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"
)
// 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]
}