444 lines
9.2 KiB
Go
444 lines
9.2 KiB
Go
package extension
|
|
|
|
import (
|
|
"sync"
|
|
"time"
|
|
)
|
|
|
|
// ==================== 媒体类型定义 ====================
|
|
|
|
type TrackType int
|
|
|
|
const (
|
|
TrackInvalid TrackType = iota - 1
|
|
TrackVideo
|
|
TrackAudio
|
|
TrackTitle
|
|
TrackApplication
|
|
)
|
|
|
|
func (t TrackType) String() string {
|
|
switch t {
|
|
case TrackVideo:
|
|
return "video"
|
|
case TrackAudio:
|
|
return "audio"
|
|
case TrackApplication:
|
|
return "application"
|
|
default:
|
|
return "invalid"
|
|
}
|
|
}
|
|
|
|
func GetTrackType(str string) TrackType {
|
|
switch str {
|
|
case "video":
|
|
return TrackVideo
|
|
case "audio":
|
|
return TrackAudio
|
|
case "application":
|
|
return TrackApplication
|
|
default:
|
|
return TrackInvalid
|
|
}
|
|
}
|
|
|
|
// ==================== 编解码标识 ====================
|
|
|
|
type CodecID int
|
|
|
|
const (
|
|
CodecInvalid CodecID = iota - 1
|
|
CodecH264
|
|
CodecH265
|
|
CodecAAC
|
|
CodecG711A
|
|
CodecG711U
|
|
CodecOpus
|
|
CodecL16
|
|
CodecVP8
|
|
CodecVP9
|
|
CodecAV1
|
|
CodecJPEG
|
|
CodecH266
|
|
CodecTS
|
|
CodecPS
|
|
CodecMP3
|
|
CodecADPCM
|
|
CodecSVACV
|
|
CodecSVACA
|
|
CodecG722
|
|
CodecG723
|
|
CodecG728
|
|
CodecG729
|
|
)
|
|
|
|
var codecNames = map[CodecID]string{
|
|
CodecH264: "H264",
|
|
CodecH265: "H265",
|
|
CodecAAC: "mpeg4-generic",
|
|
CodecG711A: "PCMA",
|
|
CodecG711U: "PCMU",
|
|
CodecOpus: "opus",
|
|
CodecL16: "L16",
|
|
CodecVP8: "VP8",
|
|
CodecVP9: "VP9",
|
|
CodecAV1: "AV1",
|
|
CodecJPEG: "JPEG",
|
|
CodecH266: "H266",
|
|
CodecTS: "TS",
|
|
CodecPS: "PS",
|
|
CodecMP3: "MP3",
|
|
CodecADPCM: "ADPCM",
|
|
CodecSVACV: "SVACV",
|
|
CodecSVACA: "SVACA",
|
|
CodecG722: "G722",
|
|
CodecG723: "G723",
|
|
CodecG728: "G728",
|
|
CodecG729: "G729",
|
|
}
|
|
var codecTypes = map[CodecID]string{
|
|
CodecH264: "video",
|
|
CodecH265: "video",
|
|
CodecAAC: "audio",
|
|
CodecG711A: "audio",
|
|
CodecG711U: "audio",
|
|
CodecOpus: "audio",
|
|
CodecL16: "audio",
|
|
CodecVP8: "video",
|
|
CodecVP9: "video",
|
|
CodecAV1: "video",
|
|
CodecJPEG: "video",
|
|
CodecH266: "video",
|
|
CodecTS: "video",
|
|
CodecPS: "video",
|
|
CodecMP3: "audio",
|
|
CodecADPCM: "audio",
|
|
CodecSVACV: "video",
|
|
CodecSVACA: "audio",
|
|
CodecG722: "audio",
|
|
CodecG723: "audio",
|
|
CodecG728: "audio",
|
|
CodecG729: "audio",
|
|
}
|
|
|
|
func (c CodecID) Type() string {
|
|
if typ, ok := codecTypes[c]; ok {
|
|
return typ
|
|
}
|
|
return "invalid"
|
|
}
|
|
|
|
func (c CodecID) TrackType() TrackType {
|
|
strType := c.Type()
|
|
if strType == "video" {
|
|
return TrackVideo
|
|
} else if strType == "audio" {
|
|
return TrackAudio
|
|
}
|
|
return TrackInvalid
|
|
}
|
|
|
|
func (c CodecID) String() string {
|
|
if name, ok := codecNames[c]; ok {
|
|
return name
|
|
}
|
|
return "invalid"
|
|
}
|
|
|
|
func GetCodecID(name string) CodecID {
|
|
for cid, n := range codecNames {
|
|
if n == name {
|
|
return cid
|
|
}
|
|
}
|
|
return CodecInvalid
|
|
}
|
|
|
|
// ==================== 帧接口定义 ====================
|
|
|
|
type Frame interface {
|
|
DTS() uint64
|
|
PTS() uint64
|
|
PrefixSize() int
|
|
KeyFrame() bool
|
|
ConfigFrame() bool
|
|
CacheAble() bool
|
|
DropAble() bool
|
|
DecodeAble() bool
|
|
Data() []byte
|
|
Size() int
|
|
CodecID() CodecID
|
|
TrackType() TrackType
|
|
}
|
|
|
|
// ==================== 基础帧实现 ====================
|
|
|
|
type BaseFrame struct {
|
|
Dts uint64
|
|
Pts uint64
|
|
VPrefixSize int
|
|
Codec CodecID
|
|
Track TrackType
|
|
}
|
|
|
|
func (f *BaseFrame) DTS() uint64 { return f.Dts }
|
|
func (f *BaseFrame) PTS() uint64 { return f.Pts }
|
|
func (f *BaseFrame) PrefixSize() int { return f.VPrefixSize }
|
|
func (f *BaseFrame) CodecID() CodecID { return f.Codec }
|
|
func (f *BaseFrame) TrackType() TrackType { return f.Track }
|
|
|
|
// ==================== 帧包装器 ====================
|
|
|
|
type FrameFromBytes struct {
|
|
*BaseFrame
|
|
DataPtr []byte
|
|
IsKey bool
|
|
IsConfig bool
|
|
}
|
|
|
|
func (f *FrameFromBytes) KeyFrame() bool { return f.IsKey }
|
|
func (f *FrameFromBytes) ConfigFrame() bool { return f.IsConfig }
|
|
func (f *FrameFromBytes) CacheAble() bool { return false }
|
|
func (f *FrameFromBytes) DropAble() bool { return false }
|
|
func (f *FrameFromBytes) DecodeAble() bool {
|
|
if f.TrackType() != TrackVideo {
|
|
return true
|
|
}
|
|
return !f.ConfigFrame()
|
|
}
|
|
func (f *FrameFromBytes) Data() []byte { return f.DataPtr }
|
|
func (f *FrameFromBytes) Size() int { return len(f.DataPtr) }
|
|
|
|
// ==================== 可缓存帧 ====================
|
|
|
|
type FrameCacheAble struct {
|
|
*FrameFromBytes
|
|
Buffer []byte
|
|
}
|
|
|
|
func NewFrameCacheAble(frame Frame, forceKeyFrame bool) Frame {
|
|
if frame.CacheAble() {
|
|
return frame
|
|
}
|
|
|
|
buffer := make([]byte, frame.Size())
|
|
copy(buffer, frame.Data())
|
|
|
|
return &FrameCacheAble{
|
|
FrameFromBytes: &FrameFromBytes{
|
|
BaseFrame: &BaseFrame{
|
|
Dts: frame.DTS(),
|
|
Pts: frame.PTS(),
|
|
VPrefixSize: frame.PrefixSize(),
|
|
Codec: frame.CodecID(),
|
|
Track: frame.TrackType(),
|
|
},
|
|
DataPtr: buffer,
|
|
IsKey: forceKeyFrame || frame.KeyFrame(),
|
|
IsConfig: frame.ConfigFrame(),
|
|
},
|
|
Buffer: buffer,
|
|
}
|
|
}
|
|
|
|
func (f *FrameCacheAble) CacheAble() bool { return true }
|
|
|
|
// ==================== 时间戳修正 ====================
|
|
|
|
type FrameStamp struct {
|
|
Frame
|
|
Dts int64
|
|
Pts int64
|
|
}
|
|
|
|
func NewFrameStamp(frame Frame, dts, pts int64) Frame {
|
|
return &FrameStamp{Frame: frame, Dts: dts, Pts: pts}
|
|
}
|
|
|
|
func (f *FrameStamp) DTS() uint64 { return uint64(f.Dts) }
|
|
func (f *FrameStamp) PTS() uint64 { return uint64(f.Pts) }
|
|
|
|
// ==================== 帧合并器 ====================
|
|
|
|
type FrameMerger struct {
|
|
frameCache []Frame
|
|
haveDecodeable bool
|
|
mergeType int
|
|
}
|
|
|
|
const (
|
|
MergeNone = iota
|
|
MergeH264Prefix
|
|
MergeMP4NalSize
|
|
)
|
|
|
|
func NewFrameMerger(mergeType int) *FrameMerger {
|
|
return &FrameMerger{mergeType: mergeType}
|
|
}
|
|
|
|
func (m *FrameMerger) WillFlush(frame Frame) bool {
|
|
if len(m.frameCache) == 0 {
|
|
return false
|
|
}
|
|
if frame == nil {
|
|
return true
|
|
}
|
|
|
|
last := m.frameCache[len(m.frameCache)-1]
|
|
switch m.mergeType {
|
|
case MergeNone:
|
|
return last.DTS() != frame.DTS() || len(m.frameCache) > 100
|
|
case MergeH264Prefix, MergeMP4NalSize:
|
|
return last.DTS() != frame.DTS() || frame.DecodeAble() || frame.ConfigFrame() || len(m.frameCache) > 100
|
|
default:
|
|
return true
|
|
}
|
|
}
|
|
|
|
func (m *FrameMerger) DoMerge(buffer *[]byte, frame Frame) {
|
|
switch m.mergeType {
|
|
case MergeNone:
|
|
*buffer = append(*buffer, frame.Data()...)
|
|
case MergeH264Prefix:
|
|
if frame.PrefixSize() == 0 {
|
|
*buffer = append(*buffer, 0, 0, 0, 1)
|
|
}
|
|
// 确保每个NAL单元都有独立的起始码
|
|
*buffer = append(*buffer, frame.Data()...)
|
|
case MergeMP4NalSize:
|
|
size := uint32(frame.Size() - frame.PrefixSize())
|
|
*buffer = append(*buffer, byte(size>>24), byte(size>>16), byte(size>>8), byte(size))
|
|
*buffer = append(*buffer, frame.Data()[frame.PrefixSize():]...)
|
|
}
|
|
}
|
|
|
|
func (m *FrameMerger) InputFrame(frame Frame, cb func(dts, pts uint64, buffer []byte, haveKeyFrame bool)) bool {
|
|
if frame == nil {
|
|
return false
|
|
}
|
|
|
|
if frame.DecodeAble() {
|
|
m.haveDecodeable = true
|
|
}
|
|
m.frameCache = append(m.frameCache, frame)
|
|
|
|
if frame != nil && !m.NeedMerge(frame.CodecID()) {
|
|
cb(frame.DTS(), frame.PTS(), frame.Data(), frame.KeyFrame())
|
|
return true
|
|
}
|
|
|
|
if m.WillFlush(frame) {
|
|
last := m.frameCache[len(m.frameCache)-1]
|
|
var buffer []byte
|
|
var haveKeyFrame = last.KeyFrame()
|
|
|
|
if len(m.frameCache) > 1 || m.mergeType == MergeMP4NalSize {
|
|
buffer = make([]byte, 0, last.Size()+1024)
|
|
for _, f := range m.frameCache {
|
|
m.DoMerge(&buffer, f)
|
|
if f.KeyFrame() {
|
|
haveKeyFrame = true
|
|
}
|
|
}
|
|
cb(last.DTS(), last.PTS(), buffer, haveKeyFrame)
|
|
m.frameCache = m.frameCache[:0]
|
|
m.haveDecodeable = false
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (m *FrameMerger) NeedMerge(codec CodecID) bool {
|
|
return codec == CodecH264 || codec == CodecH265
|
|
}
|
|
|
|
func (m *FrameMerger) Flush(cb func(dts, pts uint64, buffer []byte, haveKeyFrame bool)) {
|
|
if len(m.frameCache) > 0 {
|
|
m.InputFrame(nil, cb)
|
|
}
|
|
m.frameCache = m.frameCache[:0]
|
|
m.haveDecodeable = false
|
|
}
|
|
|
|
// ==================== 帧分发器 ====================
|
|
|
|
type FrameDispatcher struct {
|
|
delegates map[*FrameWriter]bool
|
|
mtx sync.Mutex
|
|
// 统计信息
|
|
videoKeyFrames uint64
|
|
frames uint64
|
|
lastFrames uint64
|
|
gopSize uint64
|
|
gopInterval time.Duration
|
|
lastKeyFrameTS uint64
|
|
}
|
|
|
|
func NewFrameDispatcher() *FrameDispatcher {
|
|
return &FrameDispatcher{delegates: make(map[*FrameWriter]bool)}
|
|
}
|
|
|
|
func (d *FrameDispatcher) AddDelegate(w *FrameWriter) {
|
|
d.mtx.Lock()
|
|
defer d.mtx.Unlock()
|
|
d.delegates[w] = true
|
|
}
|
|
|
|
func (d *FrameDispatcher) DelDelegate(w *FrameWriter) {
|
|
d.mtx.Lock()
|
|
defer d.mtx.Unlock()
|
|
delete(d.delegates, w)
|
|
}
|
|
|
|
func (d *FrameDispatcher) InputFrame(frame Frame) bool {
|
|
if !frame.ConfigFrame() && !frame.DropAble() {
|
|
d.frames++
|
|
if frame.KeyFrame() && frame.TrackType() == TrackVideo {
|
|
d.videoKeyFrames++
|
|
d.gopSize = d.frames - d.lastFrames
|
|
d.gopInterval = time.Duration(frame.DTS()-d.lastKeyFrameTS) * time.Millisecond
|
|
d.lastFrames = d.frames
|
|
d.lastKeyFrameTS = frame.DTS()
|
|
}
|
|
}
|
|
|
|
d.mtx.Lock()
|
|
defer d.mtx.Unlock()
|
|
|
|
var ret bool
|
|
for w := range d.delegates {
|
|
if w.InputFrame(frame) {
|
|
ret = true
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func (d *FrameDispatcher) GetStats() (uint64, uint64, uint64, time.Duration) {
|
|
return d.frames, d.videoKeyFrames, d.gopSize, d.gopInterval
|
|
}
|
|
|
|
// Size 返回当前注册的代理数量
|
|
// Size returns the number of registered delegates
|
|
func (d *FrameDispatcher) Size() int {
|
|
d.mtx.Lock()
|
|
defer d.mtx.Unlock()
|
|
return len(d.delegates)
|
|
}
|
|
|
|
// ==================== 帧写入接口 ====================
|
|
|
|
type FrameWriter struct {
|
|
OnFrame func(Frame) bool
|
|
}
|
|
|
|
func (w *FrameWriter) InputFrame(frame Frame) bool {
|
|
if w.OnFrame != nil {
|
|
return w.OnFrame(frame)
|
|
}
|
|
return false
|
|
}
|