mirror of
https://github.com/cnotch/ipchub.git
synced 2025-09-26 19:41:18 +08:00
171 lines
3.3 KiB
Go
Executable File
171 lines
3.3 KiB
Go
Executable File
// Copyright (c) 2019,CAOHONGJU All rights reserved.
|
||
// Use of this source code is governed by a MIT-style
|
||
// license that can be found in the LICENSE file.
|
||
|
||
package cache
|
||
|
||
import (
|
||
"sync"
|
||
|
||
"github.com/cnotch/ipchub/av/codec/hevc"
|
||
"github.com/cnotch/ipchub/av/format/rtp"
|
||
"github.com/cnotch/queue"
|
||
)
|
||
|
||
// HevcCache 画面组缓存(Group of Pictures).
|
||
type HevcCache struct {
|
||
cacheGop bool
|
||
l sync.RWMutex
|
||
gop queue.Queue
|
||
vps *rtp.Packet // 视频参数集包
|
||
sps *rtp.Packet // 序列参数集包
|
||
pps *rtp.Packet // 图像参数集包
|
||
}
|
||
|
||
// NewHevcCache 创建 HEVC 缓存
|
||
func NewHevcCache(cacheGop bool) *HevcCache {
|
||
return &HevcCache{
|
||
cacheGop: cacheGop,
|
||
}
|
||
}
|
||
|
||
// CachePack 向HevcCache中缓存包
|
||
func (cache *HevcCache) CachePack(pack Pack) {
|
||
rtppack := pack.(*rtp.Packet)
|
||
|
||
if rtppack.Channel != rtp.ChannelVideo {
|
||
return
|
||
}
|
||
|
||
// 判断是否是参数和关键帧包
|
||
vps, sps, pps, islice := cache.getPalyloadType(rtppack.Payload())
|
||
|
||
cache.l.Lock()
|
||
defer cache.l.Unlock()
|
||
|
||
if vps { // 视频参数
|
||
cache.vps = rtppack
|
||
return
|
||
}
|
||
|
||
if sps { // 序列头参数
|
||
cache.sps = rtppack
|
||
return
|
||
}
|
||
|
||
if pps { // 图像参数
|
||
cache.pps = rtppack
|
||
return
|
||
}
|
||
|
||
if cache.cacheGop { // 需要缓存 GOP
|
||
if islice { // 关键帧
|
||
cache.gop.Reset()
|
||
cache.gop.Push(rtppack)
|
||
} else if cache.gop.Len() > 0 {
|
||
cache.gop.Push(rtppack)
|
||
}
|
||
}
|
||
}
|
||
|
||
// Reset 重置HevcCache缓存
|
||
func (cache *HevcCache) Reset() {
|
||
cache.l.Lock()
|
||
defer cache.l.Unlock()
|
||
|
||
cache.vps = nil
|
||
cache.sps = nil
|
||
cache.pps = nil
|
||
cache.gop.Reset()
|
||
}
|
||
|
||
// PushTo 入列到指定的队列
|
||
func (cache *HevcCache) PushTo(q *queue.SyncQueue) int {
|
||
bytes := 0
|
||
cache.l.RLock()
|
||
defer cache.l.RUnlock()
|
||
|
||
// 写参数包
|
||
if cache.vps != nil {
|
||
q.Queue().Push(cache.vps)
|
||
bytes += cache.vps.Size()
|
||
}
|
||
|
||
if cache.sps != nil {
|
||
q.Queue().Push(cache.sps)
|
||
bytes += cache.sps.Size()
|
||
}
|
||
|
||
if cache.pps != nil {
|
||
q.Queue().Push(cache.pps)
|
||
bytes += cache.pps.Size()
|
||
}
|
||
|
||
// 如果必要,写 GopCache
|
||
if cache.cacheGop {
|
||
packs := cache.gop.Elems()
|
||
q.Queue().PushN(packs) // 启动阶段调用,无需加锁
|
||
for _, p := range packs {
|
||
bytes += p.(Pack).Size()
|
||
}
|
||
}
|
||
|
||
return bytes
|
||
}
|
||
|
||
func (cache *HevcCache) getPalyloadType(payload []byte) (vps, sps, pps, islice bool) {
|
||
if len(payload) < 3 {
|
||
return
|
||
}
|
||
naluType := (payload[0] >> 1) & 0x3f
|
||
|
||
switch naluType {
|
||
case hevc.NalStapInRtp: // 在RTP中的聚合(AP)
|
||
off := 2
|
||
// 循环读取被封装的NAL
|
||
for {
|
||
// nal长度
|
||
nalSize := ((uint16(payload[off])) << 8) | uint16(payload[off+1])
|
||
if nalSize < 1 {
|
||
return
|
||
}
|
||
|
||
off += 2
|
||
naluType = (payload[off] >> 1) & 0x3f
|
||
cache.nalType(naluType, &vps, &sps, &pps, &islice)
|
||
off += int(nalSize)
|
||
|
||
if off >= len(payload) { // 扫描完成
|
||
break
|
||
}
|
||
}
|
||
return
|
||
case hevc.NalFuInRtp: // 在RTP中的扩展,分片(FU)
|
||
naluType = payload[2] & 0x3f
|
||
if (payload[2]>>7)&1 == 1 { // 第一个分片
|
||
cache.nalType(naluType, &vps, &sps, &pps, &islice)
|
||
}
|
||
return
|
||
default:
|
||
cache.nalType(naluType, &vps, &sps, &pps, &islice)
|
||
return
|
||
}
|
||
}
|
||
|
||
func (cache *HevcCache) nalType(nalType byte, vps, sps, pps, islice *bool) {
|
||
if nalType >= hevc.NalBlaWLp && nalType <= hevc.NalCraNut {
|
||
*islice = true
|
||
return
|
||
}
|
||
|
||
switch nalType {
|
||
case hevc.NalVps:
|
||
*vps = true
|
||
case hevc.NalSps:
|
||
*sps = true
|
||
case hevc.NalPps:
|
||
*pps = true
|
||
}
|
||
return
|
||
}
|