github.com/cnotch/ipchub@v1.1.0/media/cache/hevccache.go (about)

     1  // Copyright (c) 2019,CAOHONGJU All rights reserved.
     2  // Use of this source code is governed by a MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package cache
     6  
     7  import (
     8  	"sync"
     9  
    10  	"github.com/cnotch/ipchub/av/codec/hevc"
    11  	"github.com/cnotch/ipchub/av/format/rtp"
    12  	"github.com/cnotch/queue"
    13  )
    14  
    15  // HevcCache 画面组缓存(Group of Pictures).
    16  type HevcCache struct {
    17  	cacheGop bool
    18  	l        sync.RWMutex
    19  	gop      queue.Queue
    20  	vps      *rtp.Packet // 视频参数集包
    21  	sps      *rtp.Packet // 序列参数集包
    22  	pps      *rtp.Packet // 图像参数集包
    23  }
    24  
    25  // NewHevcCache 创建 HEVC 缓存
    26  func NewHevcCache(cacheGop bool) *HevcCache {
    27  	return &HevcCache{
    28  		cacheGop: cacheGop,
    29  	}
    30  }
    31  
    32  // CachePack 向HevcCache中缓存包
    33  func (cache *HevcCache) CachePack(pack Pack) bool {
    34  	rtppack := pack.(*rtp.Packet)
    35  
    36  	if rtppack.Channel != rtp.ChannelVideo {
    37  		return false
    38  	}
    39  
    40  	// 判断是否是参数和关键帧包
    41  	vps, sps, pps, islice := cache.getPalyloadType(rtppack.Payload())
    42  
    43  	cache.l.Lock()
    44  	defer cache.l.Unlock()
    45  
    46  	if vps { // 视频参数
    47  		cache.vps = rtppack
    48  		return false
    49  	}
    50  
    51  	if sps { // 序列头参数
    52  		cache.sps = rtppack
    53  		return false
    54  	}
    55  
    56  	if pps { // 图像参数
    57  		cache.pps = rtppack
    58  		return false
    59  	}
    60  
    61  	if cache.cacheGop { // 需要缓存 GOP
    62  		if islice { // 关键帧
    63  			cache.gop.Reset()
    64  			cache.gop.Push(rtppack)
    65  		} else if cache.gop.Len() > 0 {
    66  			cache.gop.Push(rtppack)
    67  		}
    68  	}
    69  	return islice
    70  }
    71  
    72  // Reset 重置HevcCache缓存
    73  func (cache *HevcCache) Reset() {
    74  	cache.l.Lock()
    75  	defer cache.l.Unlock()
    76  
    77  	cache.vps = nil
    78  	cache.sps = nil
    79  	cache.pps = nil
    80  	cache.gop.Reset()
    81  }
    82  
    83  // PushTo 入列到指定的队列
    84  func (cache *HevcCache) PushTo(q *queue.SyncQueue) int {
    85  	bytes := 0
    86  	cache.l.RLock()
    87  	defer cache.l.RUnlock()
    88  
    89  	// 写参数包
    90  	if cache.vps != nil {
    91  		q.Queue().Push(cache.vps)
    92  		bytes += cache.vps.Size()
    93  	}
    94  
    95  	if cache.sps != nil {
    96  		q.Queue().Push(cache.sps)
    97  		bytes += cache.sps.Size()
    98  	}
    99  
   100  	if cache.pps != nil {
   101  		q.Queue().Push(cache.pps)
   102  		bytes += cache.pps.Size()
   103  	}
   104  
   105  	// 如果必要,写 GopCache
   106  	if cache.cacheGop {
   107  		packs := cache.gop.Elems()
   108  		q.Queue().PushN(packs) // 启动阶段调用,无需加锁
   109  		for _, p := range packs {
   110  			bytes += p.(Pack).Size()
   111  		}
   112  	}
   113  
   114  	return bytes
   115  }
   116  
   117  func (cache *HevcCache) getPalyloadType(payload []byte) (vps, sps, pps, islice bool) {
   118  	if len(payload) < 3 {
   119  		return
   120  	}
   121  	naluType := (payload[0] >> 1) & 0x3f
   122  
   123  	switch naluType {
   124  	case hevc.NalStapInRtp: // 在RTP中的聚合(AP)
   125  		off := 2
   126  		// 循环读取被封装的NAL
   127  		for {
   128  			// nal长度
   129  			nalSize := ((uint16(payload[off])) << 8) | uint16(payload[off+1])
   130  			if nalSize < 1 {
   131  				return
   132  			}
   133  
   134  			off += 2
   135  			naluType = (payload[off] >> 1) & 0x3f
   136  			cache.nalType(naluType, &vps, &sps, &pps, &islice)
   137  			off += int(nalSize)
   138  
   139  			if off >= len(payload) { // 扫描完成
   140  				break
   141  			}
   142  		}
   143  		return
   144  	case hevc.NalFuInRtp: // 在RTP中的扩展,分片(FU)
   145  		naluType = payload[2] & 0x3f
   146  		if (payload[2]>>7)&1 == 1 { // 第一个分片
   147  			cache.nalType(naluType, &vps, &sps, &pps, &islice)
   148  		}
   149  		return
   150  	default:
   151  		cache.nalType(naluType, &vps, &sps, &pps, &islice)
   152  		return
   153  	}
   154  }
   155  
   156  func (cache *HevcCache) nalType(nalType byte, vps, sps, pps, islice *bool) {
   157  	if nalType >= hevc.NalBlaWLp && nalType <= hevc.NalCraNut {
   158  		*islice = true
   159  		return
   160  	}
   161  
   162  	switch nalType {
   163  	case hevc.NalVps:
   164  		*vps = true
   165  	case hevc.NalSps:
   166  		*sps = true
   167  	case hevc.NalPps:
   168  		*pps = true
   169  	}
   170  	return
   171  }