4.0初步改造

This commit is contained in:
dexter
2022-02-02 10:39:09 +08:00
parent 6ace71fac6
commit b2489b2305
59 changed files with 6192 additions and 2061 deletions

View File

@@ -1,188 +0,0 @@
package engine
import (
"time"
"github.com/Monibuca/utils/v3/codec"
)
type AudioPack struct {
AVPack
Raw []byte
}
type AudioTrack struct {
AVTrack
SoundRate int //2bit
SoundSize byte //1bit
Channels byte //1bit
ExtraData []byte `json:"-"` //rtmp协议需要先发这个帧
PushByteStream func(ts uint32, payload []byte) `json:"-"`
PushRaw func(ts uint32, payload []byte) `json:"-"`
writeByteStream func() //使用函数写入,避免申请内存
*AudioPack `json:"-"` // 当前正在写入的音频对象
}
func (at *AudioTrack) pushByteStream(ts uint32, payload []byte) {
if len(payload) == 0 {
return
}
switch at.CodecID = payload[0] >> 4; at.CodecID {
case codec.CodecID_AAC:
if len(payload) < 4 || payload[1] != 0 {
return
} else {
config1, config2 := payload[2], payload[3]
//audioObjectType = (config1 & 0xF8) >> 3
// 1 AAC MAIN ISO/IEC 14496-3 subpart 4
// 2 AAC LC ISO/IEC 14496-3 subpart 4
// 3 AAC SSR ISO/IEC 14496-3 subpart 4
// 4 AAC LTP ISO/IEC 14496-3 subpart 4
at.SoundRate = codec.SamplingFrequencies[((config1&0x7)<<1)|(config2>>7)]
at.Channels = ((config2 >> 3) & 0x0F) //声道
//frameLengthFlag = (config2 >> 2) & 0x01
//dependsOnCoreCoder = (config2 >> 1) & 0x01
//extensionFlag = config2 & 0x01
at.ExtraData = payload
at.timebase = time.Duration(at.SoundRate)
at.PushByteStream = func(ts uint32, payload []byte) {
if len(payload) < 3 {
return
}
at.setTS(ts)
at.Raw = payload[2:]
at.Payload = payload
at.push()
}
at.Stream.AudioTracks.AddTrack("aac", at)
}
default:
at.SoundRate = codec.SoundRate[(payload[0]&0x0c)>>2] // 采样率 0 = 5.5 kHz or 1 = 11 kHz or 2 = 22 kHz or 3 = 44 kHz
at.SoundSize = (payload[0] & 0x02) >> 1 // 采样精度 0 = 8-bit samples or 1 = 16-bit samples
at.Channels = payload[0]&0x01 + 1
at.ExtraData = payload[:1]
at.timebase = time.Duration(at.SoundRate)
at.PushByteStream = func(ts uint32, payload []byte) {
if len(payload) < 2 {
return
}
at.setTS(ts)
at.Raw = payload[1:]
at.Payload = payload
at.push()
}
switch at.CodecID {
case codec.CodecID_PCMA:
at.Stream.AudioTracks.AddTrack("pcma", at)
case codec.CodecID_PCMU:
at.Stream.AudioTracks.AddTrack("pcmu", at)
}
at.PushByteStream(ts, payload)
}
}
func (at *AudioTrack) setCurrent() {
at.AVTrack.setCurrent()
at.AudioPack = at.Value.(*AudioPack)
}
func (at *AudioTrack) pushRaw(ts uint32, payload []byte) {
switch at.CodecID {
case 10:
at.writeByteStream = func() {
at.Reset()
at.Buffer.Write([]byte{at.ExtraData[0], 1})
at.Buffer.Write(at.Raw)
at.Bytes2Payload()
}
default:
at.writeByteStream = func() {
at.Reset()
at.WriteByte(at.ExtraData[0])
at.Buffer.Write(at.Raw)
at.Bytes2Payload()
}
}
at.PushRaw = func(ts uint32, payload []byte) {
at.setTS(ts)
at.Raw = payload
at.push()
}
at.PushRaw(ts, payload)
}
// Push 来自发布者推送的音频
func (at *AudioTrack) push() {
if at.Stream != nil {
at.Stream.Update()
}
if at.writeByteStream != nil {
at.writeByteStream()
}
at.addBytes(len(at.Raw))
at.GetBPS()
if at.Timestamp.Sub(at.ts) > time.Second {
at.resetBPS()
}
at.Step()
at.setCurrent()
}
func (s *Stream) NewAudioTrack(codec byte) (at *AudioTrack) {
at = &AudioTrack{}
at.timebase = 8000
at.CodecID = codec
at.PushByteStream = at.pushByteStream
at.PushRaw = at.pushRaw
at.Stream = s
at.Init(s.Context, 256)
at.poll = time.Millisecond * 10
at.Do(func(v interface{}) {
v.(*AVItem).Value = new(AudioPack)
})
at.setCurrent()
switch codec {
case 10:
s.AudioTracks.AddTrack("aac", at)
case 7:
s.AudioTracks.AddTrack("pcma", at)
case 8:
s.AudioTracks.AddTrack("pcmu", at)
}
return
}
func (at *AudioTrack) SetASC(asc []byte) {
at.ExtraData = append([]byte{0xAF, 0}, asc...)
config1 := asc[0]
config2 := asc[1]
at.CodecID = 10
//audioObjectType = (config1 & 0xF8) >> 3
// 1 AAC MAIN ISO/IEC 14496-3 subpart 4
// 2 AAC LC ISO/IEC 14496-3 subpart 4
// 3 AAC SSR ISO/IEC 14496-3 subpart 4
// 4 AAC LTP ISO/IEC 14496-3 subpart 4
at.SoundRate = codec.SamplingFrequencies[((config1&0x7)<<1)|(config2>>7)]
at.Channels = (config2 >> 3) & 0x0F //声道
//frameLengthFlag = (config2 >> 2) & 0x01
//dependsOnCoreCoder = (config2 >> 1) & 0x01
//extensionFlag = config2 & 0x01
at.timebase = time.Duration(at.SoundRate)
at.Stream.AudioTracks.AddTrack("aac", at)
}
func (at *AudioTrack) Play(onAudio func(uint32, *AudioPack), exit1, exit2 <-chan struct{}) {
ar := at.Clone()
item, ap := ar.Read()
for startTimestamp := item.Timestamp; ; item, ap = ar.Read() {
select {
case <-exit1:
return
case <-exit2:
return
default:
onAudio(uint32(item.Timestamp.Sub(startTimestamp).Milliseconds()), ap.(*AudioPack))
ar.MoveNext()
}
}
}

34
b.go
View File

@@ -1,34 +0,0 @@
package engine
type TSSlice []uint32
func (s TSSlice) Len() int { return len(s) }
func (s TSSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s TSSlice) Less(i, j int) bool { return s[i] < s[j] }
type B struct {
TSSlice
data []*RTPNalu
MaxTS uint32
}
func (b *B) Push(x interface{}) {
p := x.(*RTPNalu)
if p.PTS > b.MaxTS {
b.MaxTS = p.PTS
}
b.TSSlice = append(b.TSSlice, p.PTS)
b.data = append(b.data, p)
}
func (b *B) Pop() interface{} {
l := b.Len()-1
defer func() {
b.TSSlice = b.TSSlice[:l]
b.data = b.data[:l]
}()
return struct {
DTS uint32
*RTPNalu
}{DTS: b.TSSlice[l], RTPNalu: b.data[l]}
}

View File

@@ -1,196 +0,0 @@
package engine
import (
"bytes"
"container/ring"
"context"
"encoding/json"
"sync"
"time"
"github.com/Monibuca/utils/v3"
)
type Track interface {
GetBPS()
}
type BaseTrack struct {
Stream *Stream `json:"-"`
PacketCount int
BPS int
bytes int
ts time.Time
}
func (t *BaseTrack) addBytes(size int) {
t.bytes += size
}
type AVPack struct {
bytes.Buffer
Payload []byte // 字节流格式的媒体数据如果是需要拼接而成的则等同于Buffer里面的值
}
func (pack *AVPack) Bytes2Payload() {
pack.Payload = pack.Bytes()
}
type AVTrack struct {
AVRing `json:"-"`
CodecID byte
BaseTrack
*AVItem `json:"-"` //当前正在写入的数据对象
lastTs uint32
lastTime time.Time
timebase time.Duration
}
func (t *DataTrack) resetBPS() {
t.bytes = 0
t.ts = t.Current().Timestamp
}
func (t *DataTrack) GetBPS() {
t.PacketCount++
t.Sequence = t.PacketCount
if delta := time.Since(t.ts); delta != 0 {
t.BPS = t.bytes * 1000 / int(delta)
}
}
func (t *AVTrack) setCurrent() {
t.AVItem = t.Current()
}
func (t *AVTrack) resetBPS() {
t.bytes = 0
t.ts = t.Current().Timestamp
}
func (t *AVTrack) GetBPS() {
t.PacketCount++
t.Sequence = t.PacketCount
if delta := int(t.Timestamp.Sub(t.ts).Milliseconds()); delta != 0 {
t.BPS = t.bytes * 1000 / delta
}
}
func (t *AVTrack) setTS(ts uint32) {
if t.lastTs == 0 {
t.Timestamp = time.Now()
} else {
if t.lastTs > ts || ts-t.lastTs > 10000 {
utils.Printf("timestamp wrong %s lastTs:%d currentTs:%d", t.Stream.StreamPath, t.lastTs, ts)
//按照频率估算时间戳增量
t.Timestamp = t.lastTime.Add(time.Second / t.timebase)
} else {
t.Timestamp = t.lastTime.Add(time.Duration(ts-t.lastTs) * time.Millisecond)
}
}
t.lastTs = ts
t.lastTime = t.Timestamp
}
// func (t *Track_Base) Dispose() {
// t.RingDisposable.Dispose()
// }
type Tracks struct {
RingBuffer
m map[string]Track
context.Context
sync.RWMutex
head *ring.Ring
}
func (ts *Tracks) MarshalJSON() ([]byte, error) {
ts.RLock()
defer ts.RUnlock()
return json.Marshal(ts.m)
}
func (ts *Tracks) Init(ctx context.Context) {
ts.RingBuffer.Init(ctx, 8)
ts.head = ts.Ring
ts.m = make(map[string]Track)
ts.Context, _ = context.WithTimeout(context.Background(), time.Second*5)
}
func (ts *Tracks) AddTrack(name string, t Track) {
ts.Lock()
defer ts.Unlock()
if _, ok := ts.m[name]; !ok {
ts.m[name] = t
ts.Write(name)
}
}
func (ts *Tracks) GetTrack(name string) Track {
ts.RLock()
defer ts.RUnlock()
return ts.m[name]
}
func (ts *Tracks) OnTrack(callback func(string, Track)) {
ts.SubRing(ts.head).ReadLoop(func(name string) {
callback(name, ts.GetTrack(name))
}, false)
}
func (ts *Tracks) WaitTrack(names ...string) Track {
ring := ts.SubRing(ts.head)
if ts.Context.Err() == nil { //在等待时间范围内
if wait := make(chan string); len(names) == 0 { //任意编码需求,只取第一个
go func() {
if rt, ok := ring.Read().(string); ok {
wait <- rt
}
}()
select {
case t := <-wait:
return ts.GetTrack(t)
case <-ts.Context.Done():
return nil
}
} else {
go ring.ReadLoop(wait, false)
// go func() {
// for {
// if rt, ok := ring.Read().(string); ok {
// wait <- rt
// ring.MoveNext()
// } else {
// break
// }
// }
// }()
for {
select {
case t := <-wait:
for _, name := range names {
if t == name {
return ts.GetTrack(t)
}
}
case <-ts.Context.Done():
return nil
}
}
}
} else { //进入不等待状态
ts.RLock()
defer ts.RUnlock()
if len(names) == 0 {
if len(ts.m) == 0 {
return nil
}
return ts.m[ring.Read().(string)]
} else {
for _, name := range names {
if t, ok := ts.m[name]; ok {
return t
}
}
return nil
}
}
}

220
codec/codec.go Normal file
View File

@@ -0,0 +1,220 @@
package codec
import (
"errors"
)
const (
ADTS_HEADER_SIZE = 7
CodecID_AAC = 0xA
CodecID_PCMA = 7
CodecID_PCMU = 8
CodecID_H264 = 7
CodecID_H265 = 0xC
)
// ISO/IEC 14496-3 38(52)/page
//
// Audio
//
type AudioSpecificConfig struct {
AudioObjectType byte // 5 bits
SamplingFrequencyIndex byte // 4 bits
ChannelConfiguration byte // 4 bits
GASpecificConfig
}
type GASpecificConfig struct {
FrameLengthFlag byte // 1 bit
DependsOnCoreCoder byte // 1 bit
ExtensionFlag byte // 1 bit
}
//
// AudioObjectTypes -> ISO/IEC 14496-3 43(57)/page
//
// 1 AAC MAIN ISO/IEC 14496-3 subpart 4
// 2 AAC LC ISO/IEC 14496-3 subpart 4
// 3 AAC SSR ISO/IEC 14496-3 subpart 4
// 4 AAC LTP ISO/IEC 14496-3 subpart 4
//
//
// ISO/IEC 13838-7 20(25)/page
//
// Advanced Audio Coding
//
// AudioDataTransportStream
type ADTS struct {
ADTSFixedHeader
ADTSVariableHeader
}
// 28 bits
type ADTSFixedHeader struct {
SyncWord uint16 // 12 bits The bit string 1111 1111 1111. See ISO/IEC 11172-3,subclause 2.4.2.3 (Table 8)
ID byte // 1 bit MPEG identifier, set to 1. See ISO/IEC 11172-3,subclause 2.4.2.3 (Table 8)
Layer byte // 2 bits Indicates which layer is used. Set to 00. See ISO/IEC 11172-3,subclause 2.4.2.3 (Table 8)
ProtectionAbsent byte // 1 bit Indicates whether error_check() data is present or not. Same assyntax element protection_bit in ISO/IEC 11172-3,subclause 2.4.1 and 2.4.2 (Table 8)
Profile byte // 2 bits profile used. See clause 2 (Table 8)
SamplingFrequencyIndex byte // 4 bits indicates the sampling frequency used according to the followingtable (Table 8)
PrivateBit byte // 1 bit see ISO/IEC 11172-3, subclause 2.4.2.3 (Table 8)
ChannelConfiguration byte // 3 bits indicates the channel configuration used. Ifchannel_configuration is greater than 0, the channelconfiguration is given in Table 42, see subclause 8.5.3.1. Ifchannel_configuration equals 0, the channel configuration is notspecified in the header and must be given by aprogram_config_element() following as first syntactic element inthe first raw_data_block() after the header (seesubclause 8.5.3.2), or by the implicit configuration (seesubclause 8.5.3.3) or must be known in the application (Table 8)
OriginalCopy byte // 1 bit see ISO/IEC 11172-3, definition of data element copyright
Home byte // 1 bit see ISO/IEC 11172-3, definition of data element original/copy
}
// SyncWord, 同步头 总是0xFFF, all bits must be 1代表着一个ADTS帧的开始
// ID, MPEG Version: 0 for MPEG-4, 1 for MPEG-2
// Layer, always: '00'
// ProtectionAbsent, 表示是否误码校验
// Profile, 表示使用哪个级别的AAC有些芯片只支持AAC LC 。在MPEG-2 AAC中定义了3种.
// SamplingFrequencyIndex, 表示使用的采样率下标,通过这个下标在 Sampling Frequencies[ ]数组中查找得知采样率的值
// PrivateBit,
// ChannelConfiguration, 表示声道数
// OriginalCopy,
// Home,
// Profile:
//
// 0: Main profile
// 1: Low Complexity profile(LC)
// 2: Scalable Sampling Rate profile(SSR)
// 3: Reserved
//
var SamplingFrequencies = [...]int{96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0}
// Sampling Frequencies[]:
//
// 0: 96000 Hz
// 1: 88200 Hz
// 2: 64000 Hz
// 3: 48000 Hz
// 4: 44100 Hz
// 5: 32000 Hz
// 6: 24000 Hz
// 7: 22050 Hz
// 8: 16000 Hz
// 9: 12000 Hz
// 10: 11025 Hz
// 11: 8000 Hz
// 12: 7350 Hz
// 13: Reserved
// 14: Reserved
// 15: frequency is written explictly
//
// ChannelConfiguration:
//
// 0: Defined in AOT Specifc Config
// 1: 1 channel: front-center
// 2: 2 channels: front-left, front-right
// 3: 3 channels: front-center, front-left, front-right
// 4: 4 channels: front-center, front-left, front-right, back-center
// 5: 5 channels: front-center, front-left, front-right, back-left, back-right
// 6: 6 channels: front-center, front-left, front-right, back-left, back-right, LFE-channel
// 7: 8 channels: front-center, front-left, front-right, side-left, side-right, back-left, back-right, LFE-channel
// 8-15: Reserved
//
// 28 bits
type ADTSVariableHeader struct {
CopyrightIdentificationBit byte // 1 bit One bit of the 72-bit copyright identification field (seecopyright_id above). The bits of this field are transmitted frame by frame; the first bit is indicated by the copyright_identification_start bit set to 1. The field consists of an 8-bit copyright_identifier, followed by a 64-bit copyright_number.The copyright identifier is given by a Registration Authority as designated by SC29. The copyright_number is a value which identifies uniquely the copyrighted material. See ISO/IEC 13818-3, subclause 2.5.2.13 (Table 9)
CopyrightIdentificationStart byte // 1 bit One bit to indicate that the copyright_identification_bit in this audio frame is the first bit of the 72-bit copyright identification. If no copyright identification is transmitted, this bit should be kept '0'.'0' no start of copyright identification in this audio frame '1' start of copyright identification in this audio frame See ISO/IEC 13818-3, subclause 2.5.2.13 (Table 9)
AACFrameLength uint16 // 13 bits Length of the frame including headers and error_check in bytes(Table 9)
ADTSBufferFullness uint16 // 11 bits state of the bit reservoir in the course of encoding the ADTS frame, up to and including the first raw_data_block() and the optionally following adts_raw_data_block_error_check(). It is transmitted as the number of available bits in the bit reservoir divided by NCC divided by 32 and truncated to an integer value (Table 9). A value of hexadecimal 7FF signals that the bitstream is a variable rate bitstream. In this case, buffer fullness is not applicable
NumberOfRawDataBlockInFrame byte // 2 bits Number of raw_data_block()s that are multiplexed in the adts_frame() is equal to number_of_raw_data_blocks_in_frame + 1. The minimum value is 0 indicating 1 raw_data_block()(Table 9)
}
// CopyrightIdentificationBit,
// CopyrightIdentificationStart,
// AACFrameLength, 一个ADTS帧的长度包括ADTS头和raw data block.
// ADTSBufferFullness, 0x7FF 说明是码率可变的码流.
// NumberOfRawDataBlockInFrame, 表示ADTS帧中有number_of_raw_data_blocks_in_frame + 1个AAC原始帧
// 所以说number_of_raw_data_blocks_in_frame == 0 表示说ADTS帧中有一个AAC数据块并不是说没有。(一个AAC原始帧包含一段时间内1024个采样及相关数据)
func ADTSToAudioSpecificConfig(data []byte) []byte {
profile := ((data[2] & 0xc0) >> 6) + 1
sampleRate := (data[2] & 0x3c) >> 2
channel := ((data[2] & 0x1) << 2) | ((data[3] & 0xc0) >> 6)
config1 := (profile << 3) | ((sampleRate & 0xe) >> 1)
config2 := ((sampleRate & 0x1) << 7) | (channel << 3)
return []byte{0xAF, 0x00, config1, config2}
}
func AudioSpecificConfigToADTS(asc AudioSpecificConfig, rawDataLength int) (adts ADTS, adtsByte []byte, err error) {
if asc.ChannelConfiguration > 8 || asc.FrameLengthFlag > 13 {
err = errors.New("Reserved field.")
return
}
// ADTSFixedHeader
adts.SyncWord = 0xfff
adts.ID = 0
adts.Layer = 0
adts.ProtectionAbsent = 1
// SyncWord(12) + ID(1) + Layer(2) + ProtectionAbsent(1)
adtsByte = append(adtsByte, 0xff)
adtsByte = append(adtsByte, 0xf1)
if asc.AudioObjectType >= 3 || asc.AudioObjectType == 0 {
adts.Profile = 1
} else {
adts.Profile = asc.AudioObjectType - 1
}
adts.SamplingFrequencyIndex = asc.SamplingFrequencyIndex
adts.PrivateBit = 0
adts.ChannelConfiguration = asc.ChannelConfiguration
adts.OriginalCopy = 0
adts.Home = 0
// Profile(2) + SamplingFrequencyIndex(4) + PrivateBit(1) + ChannelConfiguration(3)(取高1位)
byte3 := uint8(adts.Profile<<6) + uint8(adts.SamplingFrequencyIndex<<2) + uint8(adts.PrivateBit<<1) + uint8((adts.ChannelConfiguration&0x7)>>2)
adtsByte = append(adtsByte, byte3)
// ADTSVariableHeader
adts.CopyrightIdentificationBit = 0
adts.CopyrightIdentificationStart = 0
adts.AACFrameLength = 7 + uint16(rawDataLength)
adts.ADTSBufferFullness = 0x7ff
adts.NumberOfRawDataBlockInFrame = 0
// ChannelConfiguration(3)(取低2位) + OriginalCopy(1) + Home(1) + CopyrightIdentificationBit(1) + CopyrightIdentificationStart(1) + AACFrameLength(13)(取高2位)
byte4 := uint8((adts.ChannelConfiguration&0x3)<<6) + uint8((adts.AACFrameLength&0x1fff)>>11)
adtsByte = append(adtsByte, byte4)
// AACFrameLength(13)
// xx xxxxxxxx xxx
// 取中间的部分
byte5 := uint8(((adts.AACFrameLength & 0x1fff) >> 3) & 0x0ff)
adtsByte = append(adtsByte, byte5)
// AACFrameLength(13)(取低3位) + ADTSBufferFullness(11)(取高5位)
byte6 := uint8((adts.AACFrameLength&0x0007)<<5) + 0x1f
adtsByte = append(adtsByte, byte6)
// ADTSBufferFullness(11)(取低6位) + NumberOfRawDataBlockInFrame(2)
adtsByte = append(adtsByte, 0xfc)
return
}
func ParseRTPAAC(payload []byte) (result [][]byte) {
auHeaderLen := (int16(payload[0]) << 8) + int16(payload[1])
auHeaderLen = auHeaderLen >> 3
auHeaderCount := int(auHeaderLen / 2)
var auLenArray []int
for iIndex := 0; iIndex < int(auHeaderCount); iIndex++ {
auHeaderInfo := (int16(payload[2+2*iIndex]) << 8) + int16(payload[2+2*iIndex+1])
auLen := auHeaderInfo >> 3
auLenArray = append(auLenArray, int(auLen))
}
startOffset := 2 + 2*auHeaderCount
for _, auLen := range auLenArray {
endOffset := startOffset + auLen
result = append(result, payload[startOffset:endOffset])
startOffset = startOffset + auLen
}
return
}

110
codec/flv.go Normal file
View File

@@ -0,0 +1,110 @@
package codec
import (
"io"
"net"
"github.com/Monibuca/engine/v4/util"
)
const (
// FLV Tag Type
FLV_TAG_TYPE_AUDIO = 0x08
FLV_TAG_TYPE_VIDEO = 0x09
FLV_TAG_TYPE_SCRIPT = 0x12
)
var (
Codec2SoundFormat = map[string]byte{
"aac": 10,
"pcma": 7,
"pcmu": 8,
}
// 音频格式. 4 bit
SoundFormat = map[byte]string{
0: "Linear PCM, platform endian",
1: "ADPCM",
2: "MP3",
3: "Linear PCM, little endian",
4: "Nellymoser 16kHz mono",
5: "Nellymoser 8kHz mono",
6: "Nellymoser",
7: "PCMA",
8: "PCMU",
9: "reserved",
10: "AAC",
11: "Speex",
14: "MP3 8Khz",
15: "Device-specific sound"}
// 采样频率. 2 bit
SoundRate = map[byte]int{
0: 5500,
1: 11000,
2: 22000,
3: 44000}
// 量化精度. 1 bit
SoundSize = map[byte]string{
0: "8Bit",
1: "16Bit"}
// 音频类型. 1bit
SoundType = map[byte]string{
0: "Mono",
1: "Stereo"}
// 视频帧类型. 4bit
FrameType = map[byte]string{
1: "keyframe (for AVC, a seekable frame)",
2: "inter frame (for AVC, a non-seekable frame)",
3: "disposable inter frame (H.263 only)",
4: "generated keyframe (reserved for server use only)",
5: "video info/command frame"}
// 视频编码类型. 4bit
CodecID = map[byte]string{
1: "JPEG (currently unused)",
2: "Sorenson H.263",
3: "Screen video",
4: "On2 VP6",
5: "On2 VP6 with alpha channel",
6: "Screen video version 2",
7: "H264",
12: "H265"}
)
var FLVHeader = []byte{0x46, 0x4c, 0x56, 0x01, 0x05, 0, 0, 0, 9, 0, 0, 0, 0}
func WriteFLVTag(w io.Writer, t byte, timestamp uint32, payload net.Buffers) (err error) {
head := make([]byte, 11)
tail := make([]byte, 4)
head[0] = t
dataSize := uint32(len(payload))
util.PutBE(tail, dataSize+11)
util.PutBE(head[1:4], dataSize)
head[4] = byte(timestamp >> 16)
head[5] = byte(timestamp >> 8)
head[6] = byte(timestamp)
head[7] = byte(timestamp >> 24)
var tag = net.Buffers{head}
tag = append(tag, payload...)
tag = append(tag, tail)
// Tag Data
_, err = tag.WriteTo(w)
return
}
func ReadFLVTag(r io.Reader) (t byte, timestamp uint32, payload []byte, err error) {
head := make([]byte, 11)
if _, err = io.ReadFull(r, head); err != nil {
return
}
t = head[0]
dataSize := util.ReadBE[int](head[1:4])
timestamp = (uint32(head[7]) << 24) | (uint32(head[4]) << 16) | (uint32(head[5]) << 8) | uint32(head[6])
payload = make([]byte, dataSize)
if _, err = io.ReadFull(r, payload); err == nil {
_, err = io.ReadFull(r, head[:4])
}
return
}

293
codec/h264.go Normal file
View File

@@ -0,0 +1,293 @@
package codec
import (
"bytes"
"errors"
"io"
"github.com/Monibuca/engine/v4/util"
"github.com/Monibuca/engine/v4/util/bits/pio"
)
// Start Code + NAL Unit -> NALU Header + NALU Body
// RTP Packet -> NALU Header + NALU Body
// NALU Body -> Slice Header + Slice data
// Slice data -> flags + Macroblock layer1 + Macroblock layer2 + ...
// Macroblock layer1 -> mb_type + PCM Data
// Macroblock layer2 -> mb_type + Sub_mb_pred or mb_pred + Residual Data
// Residual Data ->
const (
// NALU Type
NALU_Unspecified byte = iota
NALU_Non_IDR_Picture // 1
NALU_Data_Partition_A // 2
NALU_Data_Partition_B // 3
NALU_Data_Partition_C // 4
NALU_IDR_Picture // 5
NALU_SEI // 6
NALU_SPS // 7
NALU_PPS // 8
NALU_Access_Unit_Delimiter // 9
NALU_Sequence_End // 10
NALU_Stream_End // 11
NALU_Filler_Data // 12
NALU_SPS_Extension // 13
NALU_Prefix // 14
NALU_SPS_Subset // 15
NALU_DPS // 16
NALU_Reserved1 // 17
NALU_Reserved2 // 18
NALU_Not_Auxiliary_Coded // 19
NALU_Coded_Slice_Extension // 20
NALU_Reserved3 // 21
NALU_Reserved4 // 22
NALU_Reserved5 // 23
NALU_STAPA // 24
NALU_STAPB
NALU_MTAP16
NALU_MTAP24
NALU_FUA // 28
NALU_FUB
// 24 - 31 NALU_NotReserved
)
var (
NALU_AUD_BYTE = []byte{0x00, 0x00, 0x00, 0x01, 0x09, 0xF0}
NALU_Delimiter1 = []byte{0x00, 0x00, 0x01}
NALU_Delimiter2 = []byte{0x00, 0x00, 0x00, 0x01}
// 0x17 keyframe 7:AVC
// 0x00 AVC sequence header
// 0x00 0x00 0x00
// 0x01 configurationVersion
// 0x42 AVCProfileIndication
// 0x00 profile_compatibility
// 0x1E AVCLevelIndication
// 0xFF lengthSizeMinusOne
RTMP_AVC_HEAD = []byte{0x17, 0x00, 0x00, 0x00, 0x00, 0x01, 0x42, 0x00, 0x1E, 0xFF}
RTMP_KEYFRAME_HEAD = []byte{0x17, 0x01, 0x00, 0x00, 0x00}
RTMP_NORMALFRAME_HEAD = []byte{0x27, 0x01, 0x00, 0x00, 0x00}
)
var NALU_SEI_BYTE []byte
// H.264/AVC视频编码标准中,整个系统框架被分为了两个层面:视频编码层面(VCL)和网络抽象层面(NAL)
// NAL - Network Abstract Layer
// raw byte sequence payload (RBSP) 原始字节序列载荷
// SplitH264 以0x00000001分割H264裸数据
func SplitH264(payload []byte) (nalus [][]byte) {
for _, v := range bytes.SplitN(payload, NALU_Delimiter2, -1) {
if len(v) == 0 {
continue
}
nalus = append(nalus, bytes.SplitN(v, NALU_Delimiter1, -1)...)
}
return
}
func BuildH264SeqHeaderFromSpsPps(sps, pps []byte) (seqHeader []byte) {
lenSPS, lenPPS := len(sps), len(pps)
seqHeader = append([]byte{}, RTMP_AVC_HEAD...)
if lenSPS > 3 {
copy(seqHeader[6:], sps[1:4])
}
seqHeader = append(seqHeader, 0xE1, byte(lenSPS>>8), byte(lenSPS))
seqHeader = append(seqHeader, sps...)
seqHeader = append(append(seqHeader, 0x01, byte(lenPPS>>8), byte(lenPPS)), pps...)
return
}
// ISO/IEC 14496-15 11(16)/page
//
// Advanced Video Coding
//
// AVCC
type AVCDecoderConfigurationRecord struct {
ConfigurationVersion byte // 8 bits Version
AVCProfileIndication byte // 8 bits
ProfileCompatibility byte // 8 bits
AVCLevelIndication byte // 8 bits
Reserved1 byte // 6 bits
LengthSizeMinusOne byte // 2 bits 非常重要,每个NALU包前面都(lengthSizeMinusOne & 3)+1个字节的NAL包长度描述
Reserved2 byte // 3 bits
NumOfSequenceParameterSets byte // 5 bits SPS 的个数,计算方法是 numOfSequenceParameterSets & 0x1F
NumOfPictureParameterSets byte // 8 bits PPS 的个数
SequenceParameterSetLength uint16 // 16 byte SPS Length
SequenceParameterSetNALUnit []byte // n byte SPS
PictureParameterSetLength uint16 // 16 byte PPS Length
PictureParameterSetNALUnit []byte // n byte PPS
}
func (p *AVCDecoderConfigurationRecord) Marshal(b []byte) (n int) {
b[0] = 1
b[1] = p.AVCProfileIndication
b[2] = p.ProfileCompatibility
b[3] = p.AVCLevelIndication
b[4] = p.LengthSizeMinusOne | 0xfc
b[5] = uint8(1) | 0xe0
n += 6
pio.PutU16BE(b[n:], p.SequenceParameterSetLength)
n += 2
copy(b[n:], p.SequenceParameterSetNALUnit)
n += len(p.SequenceParameterSetNALUnit)
b[n] = uint8(1)
n++
pio.PutU16BE(b[n:], p.PictureParameterSetLength)
n += 2
copy(b[n:], p.PictureParameterSetNALUnit)
n += len(p.PictureParameterSetNALUnit)
return
}
var ErrDecconfInvalid = errors.New("decode error")
func (p *AVCDecoderConfigurationRecord) Unmarshal(b []byte) (n int, err error) {
if len(b) < 7 {
err = errors.New("not enough len")
return
}
p.AVCProfileIndication = b[1]
p.ProfileCompatibility = b[2]
p.AVCLevelIndication = b[3]
p.LengthSizeMinusOne = b[4] & 0x03
spscount := int(b[5] & 0x1f)
n += 6
var sps, pps [][]byte
for i := 0; i < spscount; i++ {
if len(b) < n+2 {
err = ErrDecconfInvalid
return
}
spslen := util.ReadBE[int](b[n : n+2])
n += 2
if len(b) < n+spslen {
err = ErrDecconfInvalid
return
}
sps = append(sps, b[n:n+spslen])
n += spslen
}
p.SequenceParameterSetLength = uint16(len(sps[0]))
p.SequenceParameterSetNALUnit = sps[0]
if len(b) < n+1 {
err = ErrDecconfInvalid
return
}
ppscount := int(b[n])
n++
for i := 0; i < ppscount; i++ {
if len(b) < n+2 {
err = ErrDecconfInvalid
return
}
ppslen := util.ReadBE[int](b[n : n+2])
n += 2
if len(b) < n+ppslen {
err = ErrDecconfInvalid
return
}
pps = append(pps, b[n:n+ppslen])
n += ppslen
}
if ppscount >= 1 {
p.PictureParameterSetLength = uint16(len(pps[0]))
p.PictureParameterSetNALUnit = pps[0]
} else {
err = ErrDecconfInvalid
}
return
}
type NALUnit struct {
NALUHeader
RBSP
}
type NALUHeader struct {
forbidden_zero_bit byte // 1 bit 0
nal_ref_idc byte // 2 bits nal_unit_type等于6,9,10,11或12的NAL单元其nal_ref_idc都应等于 0
nal_uint_type byte // 5 bits 包含在 NAL 单元中的 RBSP 数据结构的类型
}
type RBSP interface {
}
/*
0 Unspecified non-VCL
1 Coded slice of a non-IDR picture VCL
2 Coded slice data partition A VCL
3 Coded slice data partition B VCL
4 Coded slice data partition C VCL
5 Coded slice of an IDR picture VCL
6 Supplemental enhancement information (SEI) non-VCL
7 Sequence parameter set non-VCL
8 Picture parameter set non-VCL
9 Access unit delimiter non-VCL
10 End of sequence non-VCL
11 End of stream non-VCL
12 Filler data non-VCL
13 Sequence parameter set extension non-VCL
14 Prefix NAL unit non-VCL
15 Subset sequence parameter set non-VCL
16 Depth parameter set non-VCL
17..18 Reserved non-VCL
19 Coded slice of an auxiliary coded picture without partitioning non-VCL
20 Coded slice extension non-VCL
21 Coded slice extension for depth view components non-VCL
22..23 Reserved non-VCL
24..31 Unspecified non-VCL
0:未规定
1:非IDR图像中不采用数据划分的片段
2:非IDR图像中A类数据划分片段
3:非IDR图像中B类数据划分片段
4:非IDR图像中C类数据划分片段
5:IDR图像的片段
6:补充增强信息SEI
7:序列参数集SPS
8:图像参数集PPS
9:分割符
10:序列结束符
11:流结束符
12:填充数据
13:序列参数集扩展
14:带前缀的NAL单元
15:子序列参数集
16 18:保留
19:不采用数据划分的辅助编码图像片段
20:编码片段扩展
21 23:保留
24 31:未规定
nal_unit_type NAL类型 nal_reference_bit
0 未使用 0
1 非IDR的片 此片属于参考帧,则不等于0,不属于参考帧则等与0
2 片数据A分区 同上
3 片数据B分区 同上
4 片数据C分区 同上
5 IDR图像的片 5
6 补充增强信息单元SEI 0
7 序列参数集 非0
8 图像参数集 非0
9 分界符 0
10 序列结束 0
11 码流结束 0
12 填充 0
13..23 保留 0
24..31 不保留 0
*/
func ReadPPS(w io.Writer) {
}

531
codec/h265.go Normal file
View File

@@ -0,0 +1,531 @@
package codec
import (
"bytes"
"errors"
"github.com/Monibuca/engine/v4/util"
"github.com/q191201771/naza/pkg/nazabits"
)
const (
// HEVC_VPS = 0x40
// HEVC_SPS = 0x42
// HEVC_PPS = 0x44
// HEVC_SEI = 0x4E
// HEVC_IDR = 0x26
// HEVC_PSLICE = 0x02
NAL_UNIT_CODED_SLICE_TRAIL_N byte = iota // 0
NAL_UNIT_CODED_SLICE_TRAIL_R // 1
NAL_UNIT_CODED_SLICE_TSA_N // 2
NAL_UNIT_CODED_SLICE_TLA // 3 // Current name in the spec: TSA_R
NAL_UNIT_CODED_SLICE_STSA_N // 4
NAL_UNIT_CODED_SLICE_STSA_R // 5
NAL_UNIT_CODED_SLICE_RADL_N // 6
NAL_UNIT_CODED_SLICE_DLP // 7 // Current name in the spec: RADL_R
NAL_UNIT_CODED_SLICE_RASL_N // 8
NAL_UNIT_CODED_SLICE_TFD // 9 // Current name in the spec: RASL_R
NAL_UNIT_RESERVED_10
NAL_UNIT_RESERVED_11
NAL_UNIT_RESERVED_12
NAL_UNIT_RESERVED_13
NAL_UNIT_RESERVED_14
NAL_UNIT_RESERVED_15
NAL_UNIT_CODED_SLICE_BLA // 16 // Current name in the spec: BLA_W_LP
NAL_UNIT_CODED_SLICE_BLANT // 17 // Current name in the spec: BLA_W_DLP
NAL_UNIT_CODED_SLICE_BLA_N_LP // 18
NAL_UNIT_CODED_SLICE_IDR // 19// Current name in the spec: IDR_W_DLP
NAL_UNIT_CODED_SLICE_IDR_N_LP // 20
NAL_UNIT_CODED_SLICE_CRA // 21
NAL_UNIT_RESERVED_22
NAL_UNIT_RESERVED_23
NAL_UNIT_RESERVED_24
NAL_UNIT_RESERVED_25
NAL_UNIT_RESERVED_26
NAL_UNIT_RESERVED_27
NAL_UNIT_RESERVED_28
NAL_UNIT_RESERVED_29
NAL_UNIT_RESERVED_30
NAL_UNIT_RESERVED_31
NAL_UNIT_VPS // 32
NAL_UNIT_SPS // 33
NAL_UNIT_PPS // 34
NAL_UNIT_ACCESS_UNIT_DELIMITER // 35
NAL_UNIT_EOS // 36
NAL_UNIT_EOB // 37
NAL_UNIT_FILLER_DATA // 38
NAL_UNIT_SEI // 39 Prefix SEI
NAL_UNIT_SEI_SUFFIX // 40 Suffix SEI
NAL_UNIT_RESERVED_41
NAL_UNIT_RESERVED_42
NAL_UNIT_RESERVED_43
NAL_UNIT_RESERVED_44
NAL_UNIT_RESERVED_45
NAL_UNIT_RESERVED_46
NAL_UNIT_RESERVED_47
NAL_UNIT_UNSPECIFIED_48
NAL_UNIT_UNSPECIFIED_49
NAL_UNIT_UNSPECIFIED_50
NAL_UNIT_UNSPECIFIED_51
NAL_UNIT_UNSPECIFIED_52
NAL_UNIT_UNSPECIFIED_53
NAL_UNIT_UNSPECIFIED_54
NAL_UNIT_UNSPECIFIED_55
NAL_UNIT_UNSPECIFIED_56
NAL_UNIT_UNSPECIFIED_57
NAL_UNIT_UNSPECIFIED_58
NAL_UNIT_UNSPECIFIED_59
NAL_UNIT_UNSPECIFIED_60
NAL_UNIT_UNSPECIFIED_61
NAL_UNIT_UNSPECIFIED_62
NAL_UNIT_UNSPECIFIED_63
NAL_UNIT_INVALID
)
var AudNalu = []byte{0x00, 0x00, 0x00, 0x01, 0x46, 0x01, 0x10}
var ErrHevc = errors.New("m7s.hevc: fxxk")
//HVCC
type HVCDecoderConfigurationRecord struct {
PicWidthInLumaSamples uint32 // sps
PicHeightInLumaSamples uint32 // sps
configurationVersion uint8
generalProfileSpace uint8
generalTierFlag uint8
generalProfileIdc uint8
generalProfileCompatibilityFlags uint32
generalConstraintIndicatorFlags uint64
generalLevelIdc uint8
lengthSizeMinusOne uint8
numTemporalLayers uint8
temporalIdNested uint8
chromaFormat uint8
bitDepthLumaMinus8 uint8
bitDepthChromaMinus8 uint8
}
func ParseVpsSpsPpsFromSeqHeaderWithoutMalloc(payload []byte) (vps, sps, pps []byte, err error) {
if len(payload) < 5 {
return nil, nil, nil, ErrHevc
}
if payload[0] != 0x1c || payload[1] != 0x00 || payload[2] != 0 || payload[3] != 0 || payload[4] != 0 {
return nil, nil, nil, ErrHevc
}
if len(payload) < 33 {
return nil, nil, nil, ErrHevc
}
index := 27
if numOfArrays := payload[index]; numOfArrays != 3 && numOfArrays != 4 {
return nil, nil, nil, ErrHevc
}
index++
if payload[index] != byte(NAL_UNIT_VPS)&0x3f {
return nil, nil, nil, ErrHevc
}
if numNalus := util.ReadBE[int](payload[index+1 : index+3]); numNalus != 1 {
return nil, nil, nil, ErrHevc
}
vpsLen := util.ReadBE[int](payload[index+3 : index+5])
if len(payload) < 33+vpsLen {
return nil, nil, nil, ErrHevc
}
vps = payload[index+5 : index+5+vpsLen]
index += 5 + vpsLen
if len(payload) < 38+vpsLen {
return nil, nil, nil, ErrHevc
}
if payload[index] != byte(NAL_UNIT_SPS)&0x3f {
return nil, nil, nil, ErrHevc
}
if numNalus := util.ReadBE[int](payload[index+1 : index+3]); numNalus != 1 {
return nil, nil, nil, ErrHevc
}
spsLen := util.ReadBE[int](payload[index+3 : index+5])
if len(payload) < 38+vpsLen+spsLen {
return nil, nil, nil, ErrHevc
}
sps = payload[index+5 : index+5+spsLen]
index += 5 + spsLen
if len(payload) < 43+vpsLen+spsLen {
return nil, nil, nil, ErrHevc
}
if payload[index] != byte(NAL_UNIT_PPS)&0x3f {
return nil, nil, nil, ErrHevc
}
if numNalus := util.ReadBE[int](payload[index+1 : index+3]); numNalus != 1 {
return nil, nil, nil, ErrHevc
}
ppsLen := util.ReadBE[int](payload[index+3 : index+5])
if len(payload) < 43+vpsLen+spsLen+ppsLen {
return nil, nil, nil, ErrHevc
}
pps = payload[index+5 : index+5+ppsLen]
return
}
func BuildH265SeqHeaderFromVpsSpsPps(vps, sps, pps []byte) ([]byte, error) {
sh := make([]byte, 43+len(vps)+len(sps)+len(pps))
sh[0] = 0x1c
sh[1] = 0x0
sh[2] = 0x0
sh[3] = 0x0
sh[4] = 0x0
// unsigned int(8) configurationVersion = 1;
sh[5] = 0x1
ctx := HVCDecoderConfigurationRecord{
configurationVersion: 1,
lengthSizeMinusOne: 3, // 4 bytes
generalProfileCompatibilityFlags: 0xffffffff,
generalConstraintIndicatorFlags: 0xffffffffffff,
}
if err := ctx.ParseVps(vps); err != nil {
return nil, err
}
if err := ctx.ParseSps(sps); err != nil {
return nil, err
}
// unsigned int(2) general_profile_space;
// unsigned int(1) general_tier_flag;
// unsigned int(5) general_profile_idc;
sh[6] = ctx.generalProfileSpace<<6 | ctx.generalTierFlag<<5 | ctx.generalProfileIdc
// unsigned int(32) general_profile_compatibility_flags
util.PutBE(sh[7:7+4], ctx.generalProfileCompatibilityFlags)
// unsigned int(48) general_constraint_indicator_flags
util.PutBE(sh[11:11+4], uint32(ctx.generalConstraintIndicatorFlags>>16))
util.PutBE(sh[15:15+2], uint16(ctx.generalConstraintIndicatorFlags))
// unsigned int(8) general_level_idc;
sh[17] = ctx.generalLevelIdc
// bit(4) reserved = 1111b;
// unsigned int(12) min_spatial_segmentation_idc;
// bit(6) reserved = 111111b;
// unsigned int(2) parallelismType;
// TODO chef: 这两个字段没有解析
util.PutBE(sh[18:20], 0xf000)
sh[20] = 0xfc
// bit(6) reserved = 111111b;
// unsigned int(2) chromaFormat;
sh[21] = ctx.chromaFormat | 0xfc
// bit(5) reserved = 11111b;
// unsigned int(3) bitDepthLumaMinus8;
sh[22] = ctx.bitDepthLumaMinus8 | 0xf8
// bit(5) reserved = 11111b;
// unsigned int(3) bitDepthChromaMinus8;
sh[23] = ctx.bitDepthChromaMinus8 | 0xf8
// bit(16) avgFrameRate;
util.PutBE(sh[24:26], 0)
// bit(2) constantFrameRate;
// bit(3) numTemporalLayers;
// bit(1) temporalIdNested;
// unsigned int(2) lengthSizeMinusOne;
sh[26] = 0<<6 | ctx.numTemporalLayers<<3 | ctx.temporalIdNested<<2 | ctx.lengthSizeMinusOne
// num of vps sps pps
sh[27] = 0x03
i := 28
sh[i] = byte(NAL_UNIT_VPS)
// num of vps
util.PutBE(sh[i+1:i+3], 1)
// length
util.PutBE(sh[i+3:i+5], len(vps))
copy(sh[i+5:], vps)
i = i + 5 + len(vps)
sh[i] = byte(NAL_UNIT_SPS)
util.PutBE(sh[i+1:i+3], 1)
util.PutBE(sh[i+3:i+5], len(sps))
copy(sh[i+5:], sps)
i = i + 5 + len(sps)
sh[i] = byte(NAL_UNIT_PPS)
util.PutBE(sh[i+1:i+3], 1)
util.PutBE(sh[i+3:i+5], len(pps))
copy(sh[i+5:], pps)
return sh, nil
}
func (ctx *HVCDecoderConfigurationRecord) ParseVps(vps []byte) error {
if len(vps) < 2 {
return ErrHevc
}
rbsp := nal2rbsp(vps[2:])
br := nazabits.NewBitReader(rbsp)
// skip
// vps_video_parameter_set_id u(4)
// vps_reserved_three_2bits u(2)
// vps_max_layers_minus1 u(6)
if _, err := br.ReadBits16(12); err != nil {
return ErrHevc
}
vpsMaxSubLayersMinus1, err := br.ReadBits8(3)
if err != nil {
return ErrHevc
}
if vpsMaxSubLayersMinus1+1 > ctx.numTemporalLayers {
ctx.numTemporalLayers = vpsMaxSubLayersMinus1 + 1
}
// skip
// vps_temporal_id_nesting_flag u(1)
// vps_reserved_0xffff_16bits u(16)
if _, err := br.ReadBits32(17); err != nil {
return ErrHevc
}
return ctx.parsePtl(&br, vpsMaxSubLayersMinus1)
}
func (ctx *HVCDecoderConfigurationRecord) ParseSps(sps []byte) error {
var err error
if len(sps) < 2 {
return ErrHevc
}
rbsp := nal2rbsp(sps[2:])
br := nazabits.NewBitReader(rbsp)
// sps_video_parameter_set_id
if _, err = br.ReadBits8(4); err != nil {
return err
}
spsMaxSubLayersMinus1, err := br.ReadBits8(3)
if err != nil {
return err
}
if spsMaxSubLayersMinus1+1 > ctx.numTemporalLayers {
ctx.numTemporalLayers = spsMaxSubLayersMinus1 + 1
}
// sps_temporal_id_nesting_flag
if ctx.temporalIdNested, err = br.ReadBit(); err != nil {
return err
}
if err = ctx.parsePtl(&br, spsMaxSubLayersMinus1); err != nil {
return err
}
// sps_seq_parameter_set_id
if _, err = br.ReadGolomb(); err != nil {
return err
}
var cf uint32
if cf, err = br.ReadGolomb(); err != nil {
return err
}
ctx.chromaFormat = uint8(cf)
if ctx.chromaFormat == 3 {
if _, err = br.ReadBit(); err != nil {
return err
}
}
if ctx.PicWidthInLumaSamples, err = br.ReadGolomb(); err != nil {
return err
}
if ctx.PicHeightInLumaSamples, err = br.ReadGolomb(); err != nil {
return err
}
conformanceWindowFlag, err := br.ReadBit()
if err != nil {
return err
}
if conformanceWindowFlag != 0 {
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
}
var bdlm8 uint32
if bdlm8, err = br.ReadGolomb(); err != nil {
return err
}
ctx.bitDepthChromaMinus8 = uint8(bdlm8)
var bdcm8 uint32
if bdcm8, err = br.ReadGolomb(); err != nil {
return err
}
ctx.bitDepthChromaMinus8 = uint8(bdcm8)
_, err = br.ReadGolomb()
if err != nil {
return err
}
spsSubLayerOrderingInfoPresentFlag, err := br.ReadBit()
if err != nil {
return err
}
var i uint8
if spsSubLayerOrderingInfoPresentFlag != 0 {
i = 0
} else {
i = spsMaxSubLayersMinus1
}
for ; i <= spsMaxSubLayersMinus1; i++ {
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
return nil
}
func (ctx *HVCDecoderConfigurationRecord) parsePtl(br *nazabits.BitReader, maxSubLayersMinus1 uint8) error {
var err error
var ptl HVCDecoderConfigurationRecord
if ptl.generalProfileSpace, err = br.ReadBits8(2); err != nil {
return err
}
if ptl.generalTierFlag, err = br.ReadBit(); err != nil {
return err
}
if ptl.generalProfileIdc, err = br.ReadBits8(5); err != nil {
return err
}
if ptl.generalProfileCompatibilityFlags, err = br.ReadBits32(32); err != nil {
return err
}
if ptl.generalConstraintIndicatorFlags, err = br.ReadBits64(48); err != nil {
return err
}
if ptl.generalLevelIdc, err = br.ReadBits8(8); err != nil {
return err
}
ctx.updatePtl(&ptl)
if maxSubLayersMinus1 == 0 {
return nil
}
subLayerProfilePresentFlag := make([]uint8, maxSubLayersMinus1)
subLayerLevelPresentFlag := make([]uint8, maxSubLayersMinus1)
for i := uint8(0); i < maxSubLayersMinus1; i++ {
if subLayerProfilePresentFlag[i], err = br.ReadBit(); err != nil {
return err
}
if subLayerLevelPresentFlag[i], err = br.ReadBit(); err != nil {
return err
}
}
if maxSubLayersMinus1 > 0 {
for i := maxSubLayersMinus1; i < 8; i++ {
if _, err = br.ReadBits8(2); err != nil {
return err
}
}
}
for i := uint8(0); i < maxSubLayersMinus1; i++ {
if subLayerProfilePresentFlag[i] != 0 {
if _, err = br.ReadBits32(32); err != nil {
return err
}
if _, err = br.ReadBits32(32); err != nil {
return err
}
if _, err = br.ReadBits32(24); err != nil {
return err
}
}
if subLayerLevelPresentFlag[i] != 0 {
if _, err = br.ReadBits8(8); err != nil {
return err
}
}
}
return nil
}
func (ctx *HVCDecoderConfigurationRecord) updatePtl(ptl *HVCDecoderConfigurationRecord) {
ctx.generalProfileSpace = ptl.generalProfileSpace
if ptl.generalTierFlag > ctx.generalTierFlag {
ctx.generalLevelIdc = ptl.generalLevelIdc
ctx.generalTierFlag = ptl.generalTierFlag
} else {
if ptl.generalLevelIdc > ctx.generalLevelIdc {
ctx.generalLevelIdc = ptl.generalLevelIdc
}
}
if ptl.generalProfileIdc > ctx.generalProfileIdc {
ctx.generalProfileIdc = ptl.generalProfileIdc
}
ctx.generalProfileCompatibilityFlags &= ptl.generalProfileCompatibilityFlags
ctx.generalConstraintIndicatorFlags &= ptl.generalConstraintIndicatorFlags
}
func nal2rbsp(nal []byte) []byte {
// TODO chef:
// 1. 输出应该可由外部申请
// 2. 替换性能
// 3. 该函数应该放入avc中
return bytes.Replace(nal, []byte{0x0, 0x0, 0x3}, []byte{0x0, 0x0}, -1)
}

2419
codec/mp4.go Normal file

File diff suppressed because it is too large Load Diff

227
codec/sps.go Normal file
View File

@@ -0,0 +1,227 @@
package codec
import (
"bytes"
"github.com/cnotch/ipchub/av/codec/hevc"
"github.com/Monibuca/engine/v4/util/bits"
)
type SPSInfo struct {
ProfileIdc uint
LevelIdc uint
MbWidth uint
MbHeight uint
CropLeft uint
CropRight uint
CropTop uint
CropBottom uint
Width uint
Height uint
}
func ParseSPS(data []byte) (self SPSInfo, err error) {
r := &bits.GolombBitReader{R: bytes.NewReader(data)}
if _, err = r.ReadBits(8); err != nil {
return
}
if self.ProfileIdc, err = r.ReadBits(8); err != nil {
return
}
// constraint_set0_flag-constraint_set6_flag,reserved_zero_2bits
if _, err = r.ReadBits(8); err != nil {
return
}
// level_idc
if self.LevelIdc, err = r.ReadBits(8); err != nil {
return
}
// seq_parameter_set_id
if _, err = r.ReadExponentialGolombCode(); err != nil {
return
}
if self.ProfileIdc == 100 || self.ProfileIdc == 110 ||
self.ProfileIdc == 122 || self.ProfileIdc == 244 ||
self.ProfileIdc == 44 || self.ProfileIdc == 83 ||
self.ProfileIdc == 86 || self.ProfileIdc == 118 {
var chroma_format_idc uint
if chroma_format_idc, err = r.ReadExponentialGolombCode(); err != nil {
return
}
if chroma_format_idc == 3 {
// residual_colour_transform_flag
if _, err = r.ReadBit(); err != nil {
return
}
}
// bit_depth_luma_minus8
if _, err = r.ReadExponentialGolombCode(); err != nil {
return
}
// bit_depth_chroma_minus8
if _, err = r.ReadExponentialGolombCode(); err != nil {
return
}
// qpprime_y_zero_transform_bypass_flag
if _, err = r.ReadBit(); err != nil {
return
}
var seq_scaling_matrix_present_flag uint
if seq_scaling_matrix_present_flag, err = r.ReadBit(); err != nil {
return
}
if seq_scaling_matrix_present_flag != 0 {
for i := 0; i < 8; i++ {
var seq_scaling_list_present_flag uint
if seq_scaling_list_present_flag, err = r.ReadBit(); err != nil {
return
}
if seq_scaling_list_present_flag != 0 {
var sizeOfScalingList uint
if i < 6 {
sizeOfScalingList = 16
} else {
sizeOfScalingList = 64
}
lastScale := uint(8)
nextScale := uint(8)
for j := uint(0); j < sizeOfScalingList; j++ {
if nextScale != 0 {
var delta_scale uint
if delta_scale, err = r.ReadSE(); err != nil {
return
}
nextScale = (lastScale + delta_scale + 256) % 256
}
if nextScale != 0 {
lastScale = nextScale
}
}
}
}
}
}
// log2_max_frame_num_minus4
if _, err = r.ReadExponentialGolombCode(); err != nil {
return
}
var pic_order_cnt_type uint
if pic_order_cnt_type, err = r.ReadExponentialGolombCode(); err != nil {
return
}
if pic_order_cnt_type == 0 {
// log2_max_pic_order_cnt_lsb_minus4
if _, err = r.ReadExponentialGolombCode(); err != nil {
return
}
} else if pic_order_cnt_type == 1 {
// delta_pic_order_always_zero_flag
if _, err = r.ReadBit(); err != nil {
return
}
// offset_for_non_ref_pic
if _, err = r.ReadSE(); err != nil {
return
}
// offset_for_top_to_bottom_field
if _, err = r.ReadSE(); err != nil {
return
}
var num_ref_frames_in_pic_order_cnt_cycle uint
if num_ref_frames_in_pic_order_cnt_cycle, err = r.ReadExponentialGolombCode(); err != nil {
return
}
for i := uint(0); i < num_ref_frames_in_pic_order_cnt_cycle; i++ {
if _, err = r.ReadSE(); err != nil {
return
}
}
}
// max_num_ref_frames
if _, err = r.ReadExponentialGolombCode(); err != nil {
return
}
// gaps_in_frame_num_value_allowed_flag
if _, err = r.ReadBit(); err != nil {
return
}
if self.MbWidth, err = r.ReadExponentialGolombCode(); err != nil {
return
}
self.MbWidth++
if self.MbHeight, err = r.ReadExponentialGolombCode(); err != nil {
return
}
self.MbHeight++
var frame_mbs_only_flag uint
if frame_mbs_only_flag, err = r.ReadBit(); err != nil {
return
}
if frame_mbs_only_flag == 0 {
// mb_adaptive_frame_field_flag
if _, err = r.ReadBit(); err != nil {
return
}
}
// direct_8x8_inference_flag
if _, err = r.ReadBit(); err != nil {
return
}
var frame_cropping_flag uint
if frame_cropping_flag, err = r.ReadBit(); err != nil {
return
}
if frame_cropping_flag != 0 {
if self.CropLeft, err = r.ReadExponentialGolombCode(); err != nil {
return
}
if self.CropRight, err = r.ReadExponentialGolombCode(); err != nil {
return
}
if self.CropTop, err = r.ReadExponentialGolombCode(); err != nil {
return
}
if self.CropBottom, err = r.ReadExponentialGolombCode(); err != nil {
return
}
}
self.Width = (self.MbWidth * 16) - self.CropLeft*2 - self.CropRight*2
self.Height = ((2 - frame_mbs_only_flag) * self.MbHeight * 16) - self.CropTop*2 - self.CropBottom*2
return
}
func ParseHevcSPS(data []byte) (self SPSInfo, err error) {
var rawsps hevc.H265RawSPS
if err = rawsps.Decode(data); err == nil {
self.CropLeft, self.CropRight, self.CropTop, self.CropBottom = uint(rawsps.Conf_win_left_offset), uint(rawsps.Conf_win_right_offset), uint(rawsps.Conf_win_top_offset), uint(rawsps.Conf_win_bottom_offset)
self.Width = uint(rawsps.Pic_width_in_luma_samples)
self.Height = uint(rawsps.Pic_height_in_luma_samples)
}
return
}

126
common/frame.go Normal file
View File

@@ -0,0 +1,126 @@
package common
import (
"net"
"time"
"github.com/Monibuca/engine/v4/codec"
"github.com/pion/rtp"
)
type NALUSlice net.Buffers
type H264Slice NALUSlice
type H265Slice NALUSlice
type BuffersType interface {
NALUSlice | net.Buffers
}
func SizeOfBuffers[T BuffersType](buf T) (size int) {
for _, b := range buf {
size += len(b)
}
return
}
type H264NALU []NALUSlice
type H265NALU []NALUSlice
type AudioSlice []byte
type AACSlice AudioSlice
type G711Slice AudioSlice
// 裸数据片段
type RawSlice interface {
NALUSlice | AudioSlice
}
func (nalu H264NALU) IFrame() bool {
return H264Slice(nalu[0]).Type() == codec.NALU_IDR_Picture
}
func (nalu *H264NALU) Append(slice ...NALUSlice) {
*nalu = append(*nalu, slice...)
}
func (nalu H264Slice) Type() byte {
return nalu[0][0] & 0b0001_1111
}
func (nalu H265Slice) Type() byte {
return nalu[0][0] & 0x7E >> 1
}
func (nalu *H265NALU) Append(slice ...NALUSlice) {
*nalu = append(*nalu, slice...)
}
func (nalu H265NALU) IFrame() bool {
switch H265Slice(nalu[0]).Type() {
case codec.NAL_UNIT_CODED_SLICE_BLA,
codec.NAL_UNIT_CODED_SLICE_BLANT,
codec.NAL_UNIT_CODED_SLICE_BLA_N_LP,
codec.NAL_UNIT_CODED_SLICE_IDR,
codec.NAL_UNIT_CODED_SLICE_IDR_N_LP,
codec.NAL_UNIT_CODED_SLICE_CRA:
return true
}
return false
}
type AVCCFrame []byte // 一帧AVCC格式的数据
type AnnexBFrame []byte // 一帧AnnexB格式数据
type BaseFrame struct {
DeltaTime uint32 // 相对上一帧时间戳,毫秒
SeqInStream uint32 //在一个流中的总序号
SeqInTrack uint32 //在一个Track中的序号
BytesIn int // 输入字节数用于计算BPS
}
type DataFrame[T any] struct {
Timestamp time.Time // 写入时间
BaseFrame
Value T
}
type AVFrame[T RawSlice] struct {
BaseFrame
IFrame bool
PTS uint32
DTS uint32
AVCC net.Buffers // 打包好的AVCC格式
RTP net.Buffers // 打包好的RTP格式
RTPPackets []rtp.Packet
Raw []T //裸数据
canRead bool
}
func (av *AVFrame[T]) AppendRaw(raw ...T) {
av.Raw = append(av.Raw, raw...)
}
func (av *AVFrame[T]) AppendAVCC(avcc ...[]byte) {
av.AVCC = append(av.AVCC, avcc...)
}
func (av *AVFrame[T]) AppendRTP(rtp []byte) {
av.RTP = append(av.RTP, rtp)
}
func (av *AVFrame[T]) AppendRTPPackets(rtp rtp.Packet) {
av.RTPPackets = append(av.RTPPackets, rtp)
}
func (av *AVFrame[T]) Reset() {
av.AVCC = nil
av.RTP = nil
av.RTPPackets = nil
av.Raw = nil
}
func (avcc AVCCFrame) IsIDR() bool {
return avcc[0]>>4 == 1
}
func (avcc AVCCFrame) IsSequence() bool {
return avcc[1] == 0
}
func (avcc AVCCFrame) CTS() uint32 {
return uint32(avcc[2])<<24 | uint32(avcc[3])<<8 | uint32(avcc[4])
}
func (avcc AVCCFrame) VideoCodecID() byte {
return avcc[0] & 0x0F
}
func (avcc AVCCFrame) AudioCodecID() byte {
return avcc[0] >> 4
}

38
common/index.go Normal file
View File

@@ -0,0 +1,38 @@
package common
import "time"
type Track interface {
Get(size int) (result []byte)
Put(b []byte)
}
type AVTrack interface {
Track
WriteAVCC(ts uint32, frame AVCCFrame) //写入AVCC格式的数据
Flush()
}
type BPS struct {
ts time.Time
bytes int
BPS int
}
func (bps *BPS) ComputeBPS(bytes int) {
bps.bytes += bytes
if elapse := time.Since(bps.ts).Seconds(); elapse > 1 {
bps.BPS = bps.bytes / int(elapse)
bps.ts = time.Now()
}
}
type HZ uint32
func (hz HZ) ToMini(nts uint32) uint32 {
return nts / (uint32(hz) / 1000)
}
func (hz HZ) ToNTS(mini uint32) uint32 {
return mini * (uint32(hz) / 1000)
}

36
common/ring.go Normal file
View File

@@ -0,0 +1,36 @@
package common
import (
"github.com/Monibuca/engine/v4/util"
)
type RingBuffer[T any] struct {
*util.Ring[T]
Size int
MoveCount uint32
}
func (rb *RingBuffer[T]) Init(n int) *RingBuffer[T] {
if rb == nil {
rb = new(RingBuffer[T])
}
rb.Ring = util.NewRing[T](n)
rb.Size = n
return rb
}
func (rb RingBuffer[T]) SubRing(rr *util.Ring[T]) *RingBuffer[T] {
rb.Ring = rr
rb.MoveCount = 0
return &rb
}
func (rb *RingBuffer[T]) MoveNext() *T {
rb.Ring = rb.Next()
rb.MoveCount++
return &rb.Value
}
func (rb *RingBuffer[T]) PreValue() *T {
return &rb.Prev().Value
}

59
common/ring_av.go Normal file
View File

@@ -0,0 +1,59 @@
package common
import (
"context"
"runtime"
"time"
"github.com/Monibuca/engine/v4/util"
)
type AVRing[T RawSlice] struct {
RingBuffer[AVFrame[T]]
ctx context.Context
Poll time.Duration
}
func (r *AVRing[T]) Init(ctx context.Context, n int) {
r.ctx = ctx
r.RingBuffer.Init(n)
}
func (r AVRing[T]) SubRing(rr *util.Ring[AVFrame[T]]) *AVRing[T] {
r.Ring = rr
return &r
}
func (r *AVRing[T]) Step() *AVFrame[T] {
last := &r.Value
current := r.MoveNext()
current.SeqInTrack = r.MoveCount
current.canRead = false
current.Reset()
last.canRead = true
return current
}
func (r *AVRing[T]) wait() {
if r.Poll == 0 {
runtime.Gosched()
} else {
time.Sleep(r.Poll)
}
}
func (r *AVRing[T]) Read() *AVFrame[T] {
item := &r.Value
for r.ctx.Err() == nil && !item.canRead {
r.wait()
}
return item
}
func (r *AVRing[T]) TryRead() *AVFrame[T] {
item := &r.Value
if r.ctx.Err() == nil && !item.canRead {
return nil
}
return item
}

View File

@@ -1,92 +1,46 @@
package engine package common
import ( import (
"container/ring"
"context" "context"
"reflect" "reflect"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time"
) )
type DataItem struct { type LockFrame[T any] struct {
Timestamp time.Time DataFrame[T]
Sequence int
Value interface{}
}
// TODO: 池化,泛型
type LockItem struct {
DataItem
sync.RWMutex sync.RWMutex
} }
type RingBuffer struct { type LockRing[T any] struct {
*ring.Ring RingBuffer[LockFrame[T]]
Size int ctx context.Context
Flag *int32 Flag *int32
context.Context
} }
func (rb *RingBuffer) Init(ctx context.Context, n int) *RingBuffer { func (lr *LockRing[T]) Init(ctx context.Context, n int) *LockRing[T] {
var flag int32 var flag int32
if rb == nil { if lr == nil {
rb = &RingBuffer{Context: ctx, Ring: ring.New(n), Flag: &flag} lr = &LockRing[T]{}
} else {
rb.Ring = ring.New(n)
rb.Size = n
rb.Context = ctx
rb.Flag = &flag
} }
for x := rb.Ring; x.Value == nil; x = x.Next() { lr.ctx = ctx
x.Value = new(LockItem) lr.RingBuffer.Init(n)
} lr.Flag = &flag
rb.Current().Lock() lr.Value.Lock()
return rb return lr
} }
func (rb RingBuffer) Clone() *RingBuffer { func (rb *LockRing[T]) Read() *DataFrame[T] {
return &rb current := rb.Value
}
func (rb RingBuffer) SubRing(rr *ring.Ring) *RingBuffer {
rb.Ring = rr
return &rb
}
func (rb *RingBuffer) CurrentValue() interface{} {
return rb.Current().Value
}
func (rb *RingBuffer) NextValue() interface{} {
return rb.Next().Value.(*LockItem).Value
}
func (rb *RingBuffer) Current() *LockItem {
return rb.Ring.Value.(*LockItem)
}
func (rb *RingBuffer) MoveNext() {
rb.Ring = rb.Next()
}
func (rb *RingBuffer) GetNext() *LockItem {
rb.MoveNext()
return rb.Current()
}
func (rb *RingBuffer) Read() interface{} {
current := rb.Current()
current.RLock() current.RLock()
defer current.RUnlock() defer current.RUnlock()
return current.Value return &current.DataFrame
} }
func (rb *RingBuffer) Step() { func (rb *LockRing[T]) Step() {
last := rb.Current() last := &rb.Value
if atomic.CompareAndSwapInt32(rb.Flag, 0, 1) { if atomic.CompareAndSwapInt32(rb.Flag, 0, 1) {
current := rb.GetNext() current := rb.MoveNext()
current.Lock() current.Lock()
last.Unlock() last.Unlock()
//Flag不为1代表被Dispose了但尚未处理Done //Flag不为1代表被Dispose了但尚未处理Done
@@ -96,11 +50,11 @@ func (rb *RingBuffer) Step() {
} }
} }
func (rb *RingBuffer) Write(value interface{}) { func (rb *LockRing[T]) Write(value T) {
last := rb.Current() last := &rb.Value
last.Value = value last.Value = value
if atomic.CompareAndSwapInt32(rb.Flag, 0, 1) { if atomic.CompareAndSwapInt32(rb.Flag, 0, 1) {
current := rb.GetNext() current := rb.MoveNext()
current.Lock() current.Lock()
last.Unlock() last.Unlock()
//Flag不为1代表被Dispose了但尚未处理Done //Flag不为1代表被Dispose了但尚未处理Done
@@ -110,8 +64,8 @@ func (rb *RingBuffer) Write(value interface{}) {
} }
} }
func (rb *RingBuffer) Dispose() { func (rb *LockRing[T]) Dispose() {
current := rb.Current() current := &rb.Value
if atomic.CompareAndSwapInt32(rb.Flag, 0, 2) { if atomic.CompareAndSwapInt32(rb.Flag, 0, 2) {
current.Unlock() current.Unlock()
} else if atomic.CompareAndSwapInt32(rb.Flag, 1, 2) { } else if atomic.CompareAndSwapInt32(rb.Flag, 1, 2) {
@@ -121,22 +75,22 @@ func (rb *RingBuffer) Dispose() {
} }
} }
func (rb *RingBuffer) read() reflect.Value { func (rb *LockRing[T]) read() reflect.Value {
return reflect.ValueOf(rb.Read()) return reflect.ValueOf(rb.Read())
} }
func (rb *RingBuffer) nextRead() reflect.Value { func (rb *LockRing[T]) nextRead() reflect.Value {
rb.MoveNext() rb.MoveNext()
return rb.read() return rb.read()
} }
func (rb *RingBuffer) condition() bool { func (rb *LockRing[T]) condition() bool {
return rb.Err() == nil && *rb.Flag != 2 return rb.ctx.Err() == nil && *rb.Flag != 2
} }
// ReadLoop 循环读取,采用了反射机制,不适用高性能场景 // ReadLoop 循环读取,采用了反射机制,不适用高性能场景
// handler入参可以传入回调函数或者channel // handler入参可以传入回调函数或者channel
func (rb *RingBuffer) ReadLoop(handler interface{}, async bool) { func (rb *LockRing[T]) ReadLoop(handler interface{}, async bool) {
if async { if async {
rb.ReadLoopConditionalGo(handler, rb.condition) rb.ReadLoopConditionalGo(handler, rb.condition)
} else { } else {
@@ -145,7 +99,7 @@ func (rb *RingBuffer) ReadLoop(handler interface{}, async bool) {
} }
// goon判断函数用来判断是否继续读取,返回false将终止循环 // goon判断函数用来判断是否继续读取,返回false将终止循环
func (rb *RingBuffer) ReadLoopConditional(handler interface{}, goon func() bool) { func (rb *LockRing[T]) ReadLoopConditional(handler interface{}, goon func() bool) {
switch t := reflect.ValueOf(handler); t.Kind() { switch t := reflect.ValueOf(handler); t.Kind() {
case reflect.Chan: case reflect.Chan:
for v := rb.read(); goon(); v = rb.nextRead() { for v := rb.read(); goon(); v = rb.nextRead() {
@@ -159,7 +113,7 @@ func (rb *RingBuffer) ReadLoopConditional(handler interface{}, goon func() bool)
} }
// goon判断函数用来判断是否继续读取,返回false将终止循环 // goon判断函数用来判断是否继续读取,返回false将终止循环
func (r *RingBuffer) ReadLoopConditionalGo(handler interface{}, goon func() bool) { func (r *LockRing[T]) ReadLoopConditionalGo(handler interface{}, goon func() bool) {
switch t := reflect.ValueOf(handler); t.Kind() { switch t := reflect.ValueOf(handler); t.Kind() {
case reflect.Chan: case reflect.Chan:
for v := r.read(); goon(); v = r.nextRead() { for v := r.read(); goon(); v = r.nextRead() {

9
common/stream.go Normal file
View File

@@ -0,0 +1,9 @@
package common
import "context"
type IStream interface {
context.Context
Update() uint32
AddTrack(string, Track)
}

View File

@@ -1,58 +1,57 @@
package engine package engine
import ( // import (
"sync" // "sync"
"time" // "time"
"unsafe" // "unsafe"
)
type DataTrack struct { // "github.com/Monibuca/engine/v4/util"
RingBuffer // )
BaseTrack
*LockItem
sync.Locker // 写入锁,可选,单一写入可以不加锁
}
func (s *Stream) NewDataTrack(l sync.Locker) (dt *DataTrack) { // type DataTrack struct {
dt = &DataTrack{ // LockRing[any]
Locker: l, // BaseTrack
} // *LockFrame[any]
dt.Stream = s // sync.Locker // 写入锁,可选,单一写入可以不加锁
dt.Init(s.Context, 256) // }
dt.setCurrent()
return
}
func (dt *DataTrack) Push(data interface{}) { // func (s *Stream) NewDataTrack(l sync.Locker) (dt *DataTrack) {
if dt.Locker != nil { // dt = &DataTrack{
dt.Lock() // Locker: l,
defer dt.Unlock() // }
} // dt.Stream = s
dt.Timestamp = time.Now() // dt.Init(s.Context, 256)
dt.addBytes(int(unsafe.Sizeof(data))) // dt.setCurrent()
dt.GetBPS() // return
if time.Since(dt.ts) > 1000 { // }
dt.resetBPS()
}
dt.Write(data)
dt.setCurrent()
}
func (at *DataTrack) setCurrent() { // func (dt *DataTrack) Push(data any) {
at.LockItem = at.Current() // if dt.Locker != nil {
} // dt.Lock()
// defer dt.Unlock()
// }
// dt.Timestamp = time.Now()
// dt.bytesIn = (int(unsafe.Sizeof(data)))
// dt.GetBPS()
// dt.Write(data)
// dt.setCurrent()
// }
func (dt *DataTrack) Play(onData func(*DataItem), exit1, exit2 <-chan struct{}) { // func (at *DataTrack) setCurrent() {
dr := dt.Clone() // at.LockFrame = at.Current()
for dp := dr.Read(); ; dp = dr.Read() { // }
select {
case <-exit1: // func (dt *DataTrack) Play(onData func(DataFrame[any]), exit1, exit2 <-chan struct{}) {
return // dr := util.Clone(dt.LockRing)
case <-exit2: // for dp := dr.Read(); ; dp = dr.Read() {
return // select {
default: // case <-exit1:
onData(dp.(*DataItem)) // return
dr.MoveNext() // case <-exit2:
} // return
} // default:
} // onData(dp)
// dr.MoveNext()
// }
// }
// }

20
events.go Normal file
View File

@@ -0,0 +1,20 @@
package engine
import (
"github.com/asaskevich/EventBus"
)
type TransCodeReq struct {
*Subscriber
RequestCodec string
}
const (
Event_SUBSCRIBE = "Subscribe"
Event_UNSUBSCRIBE = "UnSubscibe"
Event_STREAMCLOSE = "StreamClose"
Event_PUBLISH = "Publish"
Event_REQUEST_TRANSAUDIO = "RequestTransAudio"
)
var Bus = EventBus.New()

18
go.mod
View File

@@ -1,12 +1,24 @@
module github.com/Monibuca/engine/v3 module github.com/Monibuca/engine/v4
go 1.13 go 1.18
require ( require (
github.com/BurntSushi/toml v0.4.1 github.com/BurntSushi/toml v0.4.1
github.com/Monibuca/utils/v3 v3.0.5 github.com/cnotch/ipchub v1.1.0
github.com/google/uuid v1.3.0 github.com/google/uuid v1.3.0
github.com/logrusorgru/aurora v2.0.3+incompatible github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/mattn/go-colorable v0.1.8
github.com/pion/rtp v1.7.4 github.com/pion/rtp v1.7.4
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/q191201771/naza v0.19.1
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a
)
require (
github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/mattn/go-isatty v0.0.12 // indirect
github.com/pion/randutil v0.1.0 // indirect
github.com/stretchr/testify v1.7.0 // indirect
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 // indirect
) )

20
go.sum
View File

@@ -1,10 +1,8 @@
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw= github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/Monibuca/utils/v3 v3.0.4 h1:PssGhww+qePzw4qpB3g2DCG5Buru0Cu64UiqtAPuHjc= github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef h1:2JGTg6JapxP9/R33ZaagQtAM4EkkSYnIAlOG5EI8gkM=
github.com/Monibuca/utils/v3 v3.0.4/go.mod h1:RpNS95gapWs6gimwh8Xn2x72FN5tO7Powabj7dTFyvE= github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII=
github.com/Monibuca/utils/v3 v3.0.5 h1:w14x0HkWTbF4MmHbINLlOwe4VJNoSOeaQChMk5E/4es=
github.com/Monibuca/utils/v3 v3.0.5/go.mod h1:RpNS95gapWs6gimwh8Xn2x72FN5tO7Powabj7dTFyvE=
github.com/cnotch/apirouter v0.0.0-20200731232942-89e243a791f3/go.mod h1:5deJPLON/x/s2dLOQfuKS0lenhOIT4xX0pvtN/OEIuY= github.com/cnotch/apirouter v0.0.0-20200731232942-89e243a791f3/go.mod h1:5deJPLON/x/s2dLOQfuKS0lenhOIT4xX0pvtN/OEIuY=
github.com/cnotch/ipchub v1.1.0 h1:hH0lh2mU3AZXPiqMwA0pdtqrwo7PFIMRGush9OobMUs= github.com/cnotch/ipchub v1.1.0 h1:hH0lh2mU3AZXPiqMwA0pdtqrwo7PFIMRGush9OobMUs=
github.com/cnotch/ipchub v1.1.0/go.mod h1:2PbeBs2q2VxxTVCn1eYCDwpAWuVXbq1+N0FU7GimOH4= github.com/cnotch/ipchub v1.1.0/go.mod h1:2PbeBs2q2VxxTVCn1eYCDwpAWuVXbq1+N0FU7GimOH4=
@@ -13,14 +11,11 @@ github.com/cnotch/queue v0.0.0-20200326024423-6e88bdbf2ad4/go.mod h1:zOssjAlNusO
github.com/cnotch/queue v0.0.0-20201224060551-4191569ce8f6/go.mod h1:zOssjAlNusOxvtaqT+EMA+Iyi8rrtKr4/XfzN1Fgoeg= github.com/cnotch/queue v0.0.0-20201224060551-4191569ce8f6/go.mod h1:zOssjAlNusOxvtaqT+EMA+Iyi8rrtKr4/XfzN1Fgoeg=
github.com/cnotch/scheduler v0.0.0-20200522024700-1d2da93eefc5/go.mod h1:F4GE3SZkJZ8an1Y0ZCqvSM3jeozNuKzoC67erG1PhIo= github.com/cnotch/scheduler v0.0.0-20200522024700-1d2da93eefc5/go.mod h1:F4GE3SZkJZ8an1Y0ZCqvSM3jeozNuKzoC67erG1PhIo=
github.com/cnotch/xlog v0.0.0-20201208005456-cfda439cd3a0/go.mod h1:RW9oHsR79ffl3sR3yMGgxYupMn2btzdtJUwoxFPUE5E= github.com/cnotch/xlog v0.0.0-20201208005456-cfda439cd3a0/go.mod h1:RW9oHsR79ffl3sR3yMGgxYupMn2btzdtJUwoxFPUE5E=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/emitter-io/address v1.0.0/go.mod h1:GfZb5+S/o8694B1GMGK2imUYQyn2skszMvGNA5D84Ug= github.com/emitter-io/address v1.0.0/go.mod h1:GfZb5+S/o8694B1GMGK2imUYQyn2skszMvGNA5D84Ug=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478 h1:Db9StoJ6RZN3YttC0Pm0I4Y5izITRYch3RMbT59BYN0=
github.com/funny/slab v0.0.0-20180511031532-b1fad5e5d478/go.mod h1:0j1+svBH8ABEIPdUP0AIg4qedsybnXGJBakCEw8cfoo=
github.com/funny/utest v0.0.0-20161029064919-43870a374500 h1:Z0r1CZnoIWFB/Uiwh1BU5FYmuFe6L5NPi6XWQEmsTRg=
github.com/funny/utest v0.0.0-20161029064919-43870a374500/go.mod h1:mUn39tBov9jKnTWV1RlOYoNzxdBFHiSzXWdY1FoNGGg=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -53,8 +48,9 @@ github.com/q191201771/naza v0.19.1/go.mod h1:5LeGupZZFtYP1g/S203n9vXoUNVdlRnPIfM
github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A= github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -66,11 +62,11 @@ golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae h1:/WDfKMnPU+m5M4xB+6x4kaepxRw6jWvR5iDRdvjHgy8=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik=
golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=

98
hook.go
View File

@@ -1,98 +0,0 @@
package engine
import (
"context"
"reflect"
"runtime"
"sync"
)
type TransCodeReq struct {
*Subscriber
RequestCodec string
}
const (
HOOK_SUBSCRIBE = "Subscribe"
HOOK_UNSUBSCRIBE = "UnSubscibe"
HOOK_STREAMCLOSE = "StreamClose"
HOOK_PUBLISH = "Publish"
HOOK_REQUEST_TRANSAUDIO = "RequestTransAudio"
)
var Hooks = make(map[string]*RingBuffer)
var hookLocker sync.Mutex
func addHookRing(name string) (r *RingBuffer) {
r = r.Init(context.TODO(), 10)
Hooks[name] = r
return
}
func AddHooks(hooks map[string]interface{}) {
hookLocker.Lock()
for name, hook := range hooks {
rl, ok := Hooks[name]
if !ok {
rl = addHookRing(name)
}
vf := reflect.ValueOf(hook)
if vf.Kind() != reflect.Func {
panic("callback is not a function")
}
go rl.Clone().ReadLoop(vf.Call, false)
}
hookLocker.Unlock()
}
func addHook(name string, callback interface{}, async bool) {
hookLocker.Lock()
rl, ok := Hooks[name]
if !ok {
rl = addHookRing(name)
}
hookLocker.Unlock()
vf := reflect.ValueOf(callback)
if vf.Kind() != reflect.Func {
panic("callback is not a function")
}
rl.Clone().ReadLoop(vf.Call, async)
}
func AddHook(name string, callback interface{}) {
addHook(name, callback, false)
}
func AddHookGo(name string, callback interface{}) {
addHook(name, callback, true)
}
func AddHookConditional(name string, callback interface{}, goon func() bool) {
hookLocker.Lock()
rl, ok := Hooks[name]
if !ok {
rl = addHookRing(name)
}
hookLocker.Unlock()
vf := reflect.ValueOf(callback)
if vf.Kind() != reflect.Func {
panic("callback is not a function")
}
rl.Clone().ReadLoopConditional(vf.Call, goon)
}
func TriggerHook(name string, payload ...interface{}) {
args := make([]reflect.Value, len(payload))
for i, arg := range payload {
args[i] = reflect.ValueOf(arg)
}
defer runtime.Gosched() //防止连续写入
hookLocker.Lock()
defer hookLocker.Unlock()
if rl, ok := Hooks[name]; ok {
rl.Write(args)
} else {
rl = addHookRing(name)
rl.Write(args)
}
}

View File

@@ -1,28 +0,0 @@
package engine
import (
"fmt"
"sync"
"testing"
"time"
)
func TestAddHook(t *testing.T) {
t.Run(t.Name(), func(t *testing.T) {
var wg sync.WaitGroup
wg.Add(1)
go AddHook("test", func(a, b int) {
fmt.Printf("on test,%d,%d", a, b)
})
go AddHook("done", wg.Done)
TriggerHook("test", 2, 10)
go AddHook("test", func(a, b int) {
fmt.Printf("on test,%d,%d", a, b)
})
<-time.After(time.Millisecond * 100)
TriggerHook("test", 1, 12)
<-time.After(time.Millisecond * 100)
TriggerHook("done")
wg.Wait()
})
}

49
main.go
View File

@@ -13,31 +13,43 @@ import (
"strings" "strings"
"time" // colorable "time" // colorable
"github.com/Monibuca/utils/v3"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/Monibuca/engine/v3/util" "github.com/Monibuca/engine/v4/util"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
. "github.com/logrusorgru/aurora" . "github.com/logrusorgru/aurora"
) )
var Version = "3.2.2" var Version = "4.0.0"
type Second int
func (s Second) Duration() time.Duration {
return time.Duration(s) * time.Second
}
// StreamConfig 流的三级覆盖配置(全局,插件,流)
type StreamConfig struct {
EnableAudio bool
EnableVideo bool
AutoReconnect bool // 自动重连
PullOnStart bool // 启动时拉流
PullOnSubscribe bool // 订阅时自动拉流
PublishTimeout Second // 发布无数据超时
WaitTimeout Second // 等待流超时
WaitCloseTimeout Second // 延迟自动关闭(无订阅时)
}
var ( var (
config = &struct { config = &struct {
EnableAudio bool StreamConfig
EnableVideo bool
PublishTimeout time.Duration
MaxRingSize int
AutoCloseAfter int
RTPReorder bool RTPReorder bool
}{true, true, 60, 256, -1, false} }{StreamConfig{true, true, true, true, true, 10, 10, 0}, false}
// ConfigRaw 配置信息的原始数据 // ConfigRaw 配置信息的原始数据
ConfigRaw []byte ConfigRaw []byte
StartTime time.Time //启动时间 StartTime time.Time //启动时间
Plugins = make(map[string]*PluginConfig) // Plugins 所有的插件配置 Plugins = make(map[string]*PluginConfig) // Plugins 所有的插件配置
HasTranscoder bool
Ctx context.Context Ctx context.Context
settingDir string settingDir string
) )
@@ -61,11 +73,11 @@ func (opt *PluginConfig) Install(run func()) {
opt.Version = parts[len(parts)-1] opt.Version = parts[len(parts)-1]
} }
Plugins[opt.Name] = opt Plugins[opt.Name] = opt
utils.Print(Green("install plugin"), BrightCyan(opt.Name), BrightBlue(opt.Version)) util.Print(Green("install plugin"), BrightCyan(opt.Name), BrightBlue(opt.Version))
} }
func init() { func init() {
if parts := strings.Split(utils.CurrentDir(), "@"); len(parts) > 1 { if parts := strings.Split(util.CurrentDir(), "@"); len(parts) > 1 {
Version = parts[len(parts)-1] Version = parts[len(parts)-1]
} }
} }
@@ -74,19 +86,19 @@ func init() {
func Run(ctx context.Context, configFile string) (err error) { func Run(ctx context.Context, configFile string) (err error) {
Ctx = ctx Ctx = ctx
if err := util.CreateShutdownScript(); err != nil { if err := util.CreateShutdownScript(); err != nil {
utils.Print(Red("create shutdown script error:"), err) util.Print(Red("create shutdown script error:"), err)
} }
StartTime = time.Now() StartTime = time.Now()
if ConfigRaw, err = ioutil.ReadFile(configFile); err != nil { if ConfigRaw, err = ioutil.ReadFile(configFile); err != nil {
utils.Print(Red("read config file error:"), err) util.Print(Red("read config file error:"), err)
return return
} }
settingDir = filepath.Join(filepath.Dir(configFile), ".m7s") settingDir = filepath.Join(filepath.Dir(configFile), ".m7s")
if err = os.MkdirAll(settingDir, 0755); err != nil { if err = os.MkdirAll(settingDir, 0755); err != nil {
utils.Print(Red("create dir .m7s error:"), err) util.Print(Red("create dir .m7s error:"), err)
return return
} }
utils.Print(BgGreen(Black("Ⓜ starting m7s ")), BrightBlue(Version)) util.Print(BgGreen(Black("Ⓜ starting m7s ")), BrightBlue(Version))
var cg map[string]interface{} var cg map[string]interface{}
if _, err = toml.Decode(string(ConfigRaw), &cg); err == nil { if _, err = toml.Decode(string(ConfigRaw), &cg); err == nil {
if cfg, ok := cg["Engine"]; ok { if cfg, ok := cg["Engine"]; ok {
@@ -94,7 +106,6 @@ func Run(ctx context.Context, configFile string) (err error) {
if err = json.Unmarshal(b, config); err != nil { if err = json.Unmarshal(b, config); err != nil {
log.Println(err) log.Println(err)
} }
config.PublishTimeout *= time.Second
} }
for name, config := range Plugins { for name, config := range Plugins {
if cfg, ok := cg[name]; ok { if cfg, ok := cg[name]; ok {
@@ -112,7 +123,7 @@ func Run(ctx context.Context, configFile string) (err error) {
} }
} }
} else { } else {
utils.Print(Red("decode config file error:"), err) util.Print(Red("decode config file error:"), err)
} }
UUID := uuid.NewString() UUID := uuid.NewString()
reportTimer := time.NewTimer(time.Minute) reportTimer := time.NewTimer(time.Minute)
@@ -122,7 +133,7 @@ func Run(ctx context.Context, configFile string) (err error) {
req.Header.Set("uuid", UUID) req.Header.Set("uuid", UUID)
var c http.Client var c http.Client
for { for {
req.Header.Set("streams", fmt.Sprintf("%d", len(Streams.m))) req.Header.Set("streams", fmt.Sprintf("%d", Streams.Len()))
c.Do(req) c.Do(req)
select { select {
case <-ctx.Done(): case <-ctx.Done():

5
publisher.go Normal file
View File

@@ -0,0 +1,5 @@
package engine
type Publisher interface {
OnStateChange(oldState StreamState, newState StreamState) bool
}

View File

@@ -1,91 +0,0 @@
package engine
import (
"container/ring"
"context"
"runtime"
"time"
)
type AVItem struct {
DataItem
canRead bool
}
type AVRing struct {
RingBuffer
poll time.Duration
}
func (r *AVRing) Init(ctx context.Context, n int) *AVRing {
r.Ring = ring.New(n)
r.Context = ctx
r.Size = n
for x := r.Ring; x.Value == nil; x = x.Next() {
x.Value = new(AVItem)
}
return r
}
func (r AVRing) Clone() *AVRing {
return &r
}
func (r AVRing) SubRing(rr *ring.Ring) *AVRing {
r.Ring = rr
return &r
}
func (r *AVRing) Write(value interface{}) {
last := r.Current()
last.Value = value
r.GetNext().canRead = false
last.canRead = true
}
func (r *AVRing) Step() {
last := r.Current()
r.GetNext().canRead = false
last.canRead = true
}
func (r *AVRing) wait() {
if r.poll == 0 {
runtime.Gosched()
} else {
time.Sleep(r.poll)
}
}
func (r *AVRing) CurrentValue() interface{} {
return r.Current().Value
}
func (r *AVRing) Current() *AVItem {
return r.Ring.Value.(*AVItem)
}
func (r *AVRing) NextValue() interface{} {
return r.Next().Value.(*AVItem).Value
}
func (r *AVRing) PreItem() *AVItem {
return r.Prev().Value.(*AVItem)
}
func (r *AVRing) GetNext() *AVItem {
r.MoveNext()
return r.Current()
}
func (r *AVRing) Read() (item *AVItem, value interface{}) {
current := r.Current()
for r.Err() == nil && !current.canRead {
r.wait()
}
return current, current.Value
}
func (r *AVRing) TryRead() (item *AVItem, value interface{}) {
current := r.Current()
if r.Err() == nil && !current.canRead {
return nil, nil
}
return current, current.Value
}

106
rtp.go
View File

@@ -1,106 +0,0 @@
package engine
import (
"time"
"github.com/Monibuca/utils/v3"
"github.com/pion/rtp"
)
// 对rtp包进行解封装并修复时间戳包括时间戳跳跃
type RTPDemuxer struct {
rtp.Packet
PTS uint32 // 修复后的时间戳(毫秒)
lastTs uint32 // 记录上一个收到的时间戳
lastSeq uint16 // 记录上一个收到的序列号
lastSeq2 uint16 // 记录上上一个收到的序列号
timeBase *time.Duration // 采样率
timestamp time.Time // 客观时间用于计算耗时
orderMap map[uint16]RTPNalu // 缓存,用于乱序重排
OnDemux func(uint32, []byte)
}
func (r *RTPDemuxer) tryPop(ts uint32, payload []byte) {
for {
r.lastSeq++
r.push(ts, payload)
if next, ok := r.orderMap[r.lastSeq+1]; ok {
delete(r.orderMap, r.lastSeq+1)
ts = next.PTS
payload = next.Payload
} else {
break
}
}
}
func (r *RTPDemuxer) push(ts uint32, payload []byte) {
if ts > r.lastTs {
delta := uint32(uint64(ts-r.lastTs) * 1000 / uint64(*r.timeBase))
if delta > 1000 { // 时间戳跳跃
r.PTS += uint32(time.Since(r.timestamp) / time.Millisecond)
} else {
r.PTS += delta
}
} else if r.lastTs > ts {
delta := uint32(uint64(r.lastTs-ts) * 1000 / uint64(*r.timeBase))
if delta > 1000 { // 时间戳跳跃
r.PTS += uint32(time.Since(r.timestamp) / time.Millisecond)
} else {
r.PTS -= delta
}
}
r.timestamp = time.Now()
r.OnDemux(r.PTS, r.Payload)
r.lastTs = ts
}
func (r *RTPDemuxer) Push(rtpRaw []byte) {
if err := r.Unmarshal(rtpRaw); err != nil {
utils.Println("RTP Unmarshal error", err)
return
}
if config.RTPReorder {
if r.SequenceNumber < r.lastSeq {
return
} else if r.lastSeq == 0 {
r.timestamp = time.Now()
r.tryPop(r.Timestamp, r.Payload)
r.lastSeq = r.SequenceNumber
} else if r.lastSeq+1 == r.SequenceNumber {
r.tryPop(r.Timestamp, r.Payload)
} else if _, ok := r.orderMap[r.SequenceNumber]; !ok {
r.orderMap[r.SequenceNumber] = RTPNalu{
Payload: r.Payload,
PTS: r.Timestamp,
}
// 20个包都没有出现丢弃
if len(r.orderMap) > 20 {
utils.Println("RTP SequenceNumber lost", r.lastSeq+1)
r.lastSeq++
next, ok := r.orderMap[r.lastSeq]
for !ok {
r.lastSeq++
next, ok = r.orderMap[r.lastSeq]
}
delete(r.orderMap, r.lastSeq)
r.tryPop(next.PTS, next.Payload)
}
}
return
} else {
if r.lastSeq == 0 {
r.timestamp = time.Now()
r.lastSeq = r.SequenceNumber
} else if r.SequenceNumber == r.lastSeq2+1 { // 本次序号是上上次的序号+1 说明中间隔了一个错误序号某些rtsp流中的rtcp包写成了rtp包导致的
r.lastSeq = r.SequenceNumber
} else {
r.lastSeq2 = r.lastSeq
r.lastSeq = r.SequenceNumber
if r.lastSeq != r.lastSeq2+1 { //序号不连续
utils.Println("RTP SequenceNumber error", r.lastSeq2, r.lastSeq)
return
}
}
}
r.push(r.Timestamp, r.Payload)
}

View File

@@ -1,46 +0,0 @@
package engine
import (
"time"
"github.com/Monibuca/utils/v3"
"github.com/Monibuca/utils/v3/codec"
)
type RTPAudio struct {
RTPDemuxer `json:"-"`
*AudioTrack
}
func (s *Stream) NewRTPAudio(codec byte) (r *RTPAudio) {
r = &RTPAudio{
AudioTrack: s.NewAudioTrack(codec),
}
if config.RTPReorder {
r.orderMap = make(map[uint16]RTPNalu)
}
r.timeBase = &r.timebase
r.OnDemux = r.push
return
}
// 该函数只执行一次
func (v *RTPAudio) push(ts uint32, payload []byte) {
switch v.CodecID {
case codec.CodecID_AAC:
v.OnDemux = func(ts uint32, payload []byte) {
for _, payload := range codec.ParseRTPAAC(payload) {
v.PushRaw(ts, payload)
}
}
case codec.CodecID_PCMA, codec.CodecID_PCMU:
v.OnDemux = func(ts uint32, payload []byte) {
v.PushRaw(ts, payload)
}
default:
utils.Println("RTP Publisher: Unsupported codec", v.CodecID)
return // TODO
}
v.timestamp = time.Now()
v.OnDemux(ts, payload)
}

View File

@@ -1,343 +0,0 @@
package engine
import (
"bytes"
"encoding/binary"
"github.com/Monibuca/utils/v3"
"github.com/Monibuca/utils/v3/codec"
// "github.com/pion/rtp/codecs"
)
const (
fuaStartBitmask = 0b1000_0000
fuaEndBitmask = 0b0100_0000
stapaNALULengthSize = 2
naluRefIdcBitmask = 0x60
)
var sizeMap = map[uint8]int{
codec.NALU_STAPA: 1,
codec.NALU_STAPB: 3,
codec.NALU_MTAP16: 4,
codec.NALU_MTAP24: 5,
codec.NALU_FUA: 2,
codec.NALU_FUB: 4,
}
type RTPNalu struct {
Payload []byte
PTS uint32
Next *RTPNalu
}
type RTPVideo struct {
RTPDemuxer `json:"-"`
*VideoTrack
fuaBuffer *bytes.Buffer
demuxNalu func([]byte) *RTPNalu
}
func (s *Stream) NewRTPVideo(codecID byte) (r *RTPVideo) {
r = &RTPVideo{
VideoTrack: s.NewVideoTrack(codecID),
}
if config.RTPReorder {
r.orderMap = make(map[uint16]RTPNalu)
}
r.timeBase = &r.timebase
switch codecID {
case codec.CodecID_H264:
r.demuxNalu = r.demuxH264
case codec.CodecID_H265:
r.demuxNalu = r.demuxH265
}
r.OnDemux = r._demux
return
}
func (v *RTPVideo) demuxH264(payload []byte) (result *RTPNalu) {
naluLen := len(payload)
if naluLen == 0 {
return
}
naluType := payload[0] & naluTypeBitmask
lenSize := sizeMap[naluType]
switch naluType {
case codec.NALU_STAPA, codec.NALU_STAPB:
current := &result
for currOffset, naluSize := lenSize, 0; currOffset < naluLen; currOffset += naluSize {
naluSize = int(binary.BigEndian.Uint16(payload[currOffset:]))
if currOffset += stapaNALULengthSize; naluLen < currOffset+naluSize {
utils.Printf("STAP-A declared size(%d) is larger then buffer(%d)", naluSize, naluLen-currOffset)
return
}
*current = &RTPNalu{Payload: payload[currOffset : currOffset+naluSize], PTS: v.PTS}
current = &(*current).Next
}
case codec.NALU_MTAP16, codec.NALU_MTAP24:
current := &result
for currOffset, naluSize := 3, 0; currOffset < naluLen; currOffset += naluSize {
naluSize = int(binary.BigEndian.Uint16(payload[currOffset:]))
currOffset += lenSize
if naluLen < currOffset+naluSize {
utils.Printf("MTAP16 declared size(%d) is larger then buffer(%d)", naluSize, naluLen-currOffset)
return
}
ts := binary.BigEndian.Uint16(payload[currOffset+3:])
if lenSize == 5 {
ts = (ts << 8) | uint16(payload[currOffset+5])
}
*current = &RTPNalu{Payload: payload[currOffset : currOffset+naluSize], PTS: v.PTS + uint32(ts)}
current = &(*current).Next
}
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PayloadHdr (Type=29) | FU header | DONL (cond) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
| DONL (cond) | |
|-+-+-+-+-+-+-+-+ |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PayloadHdr (Type=28) | NALU 1 Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 1 HDR | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ NALU 1 Data |
| . . . |
| |
+ +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| . . . | NALU 2 Size | NALU 2 HDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 2 HDR | |
+-+-+-+-+-+-+-+-+ NALU 2 Data |
| . . . |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| :...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
case codec.NALU_FUA, codec.NALU_FUB:
if naluLen < lenSize {
utils.Printf("Payload is not large enough to be FU-A")
return
}
if payload[1]&fuaStartBitmask != 0 {
v.fuaBuffer = bytes.NewBuffer([]byte{})
v.fuaBuffer.WriteByte((payload[0] & naluRefIdcBitmask) | (payload[1] & naluTypeBitmask))
}
if v.fuaBuffer != nil {
if v.fuaBuffer.Write(payload[lenSize:]); payload[1]&fuaEndBitmask != 0 {
result = &RTPNalu{Payload: v.fuaBuffer.Bytes(), PTS: v.PTS}
v.fuaBuffer = nil
}
}
default:
return &RTPNalu{Payload: payload, PTS: v.PTS}
}
return
}
func (v *RTPVideo) demuxH265(payload []byte) (result *RTPNalu) {
naluLen := len(payload)
if naluLen == 0 {
return
}
naluType := payload[0] & naluTypeBitmask_hevc >> 1
switch naluType {
// 4.4.2. Aggregation Packets (APs) (p25)
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PayloadHdr (Type=48) | NALU 1 DONL |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 1 Size | NALU 1 HDR |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| |
| NALU 1 Data . . . |
| |
+ . . . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| | NALU 2 DOND | NALU 2 Size |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| NALU 2 HDR | |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ NALU 2 Data |
| |
| . . . +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| : ...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
case codec.NAL_UNIT_UNSPECIFIED_48:
currOffset := 2
if v.UsingDonlField {
currOffset = 4
}
current := &result
for naluSize := 0; currOffset < naluLen; currOffset += naluSize {
naluSize = int(binary.BigEndian.Uint16(payload[currOffset:]))
currOffset += 2
if naluLen < currOffset+naluSize {
utils.Printf("STAP-A declared size(%d) is larger then buffer(%d)", naluSize, naluLen-currOffset)
return
}
*current = &RTPNalu{Payload: payload[currOffset : currOffset+naluSize], PTS: v.PTS}
current = &(*current).Next
if v.UsingDonlField {
currOffset += 1
}
}
// 4.4.3. Fragmentation Units (p29)
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| PayloadHdr (Type=49) | FU header | DONL (cond) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-|
| DONL (cond) | |
|-+-+-+-+-+-+-+-+ |
| FU payload |
| |
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| : ...OPTIONAL RTP padding |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+---------------+
|0|1|2|3|4|5|6|7|
+-+-+-+-+-+-+-+-+
|S|E| FuType |
+---------------+
*/
case codec.NAL_UNIT_UNSPECIFIED_49:
offset := 3
if v.UsingDonlField {
offset = 5
}
if naluLen < offset {
return
}
fuheader := payload[2]
if naluType = fuheader & 0b00111111; fuheader&fuaStartBitmask != 0 {
v.fuaBuffer = bytes.NewBuffer([]byte{})
payload[0] = payload[0]&0b10000001 | (naluType << 1)
v.fuaBuffer.Write(payload[:2])
}
if v.fuaBuffer != nil {
if v.fuaBuffer.Write(payload[offset:]); fuheader&fuaEndBitmask != 0 {
result = &RTPNalu{Payload: v.fuaBuffer.Bytes(), PTS: v.PTS}
v.fuaBuffer = nil
}
}
default:
return &RTPNalu{Payload: payload, PTS: v.PTS}
}
return
}
// func (p *RTPVideo) demuxH265(payload []byte) (result *RTPNalu) {
// var h265 codecs.H265Packet
// if _, err := h265.Unmarshal(payload); err == nil {
// switch v := h265.Packet().(type) {
// case (*codecs.H265FragmentationUnitPacket):
// if v.FuHeader().S() {
// p.fuaBuffer = bytes.NewBuffer([]byte{})
// payload[0] = payload[0]&0b10000001 | ((byte(v.FuHeader()) & 0b00111111) << 1)
// p.fuaBuffer.Write(payload[:2])
// }
// p.fuaBuffer.Write(v.Payload())
// if v.FuHeader().E() {
// result = &RTPNalu{Payload: p.fuaBuffer.Bytes(), PTS: p.Timestamp}
// p.fuaBuffer = nil
// }
// case (*codecs.H265AggregationPacket):
// head := &RTPNalu{Payload: v.FirstUnit().NalUnit(), PTS: p.Timestamp}
// for _, nalu := range v.OtherUnits() {
// head.Next = &RTPNalu{Payload: nalu.NalUnit(), PTS: p.Timestamp}
// head = head.Next
// }
// return head
// case (*codecs.H265PACIPacket):
// return &RTPNalu{Payload: v.Payload(), PTS: p.Timestamp}
// case (*codecs.H265SingleNALUnitPacket):
// return &RTPNalu{Payload: v.Payload(), PTS: p.Timestamp}
// }
// }
// return
// }
// func (p *RTPVideo) _demux(ts uint32, payload []byte) {
// p.timestamp = time.Now()
// if last := p.demuxNalu(payload); last != nil {
// p.OnDemux = func(ts uint32, payload []byte) {
// if current := p.demuxNalu(payload); current != nil {
// if last.PTS > current.PTS { //有B帧
// var b B
// utils.Println("rtp has B-frame!!")
// for heap.Push(&b, last); last.Next != nil; last = last.Next {
// heap.Push(&b, last.Next)
// }
// for heap.Push(&b, current); current.Next != nil; current = current.Next {
// heap.Push(&b, current.Next)
// }
// p.OnDemux = func(ts uint32, payload []byte) {
// if current := p.demuxNalu(payload); current != nil {
// if current.PTS > b.MaxTS {
// for b.Len() > 0 {
// el := heap.Pop(&b).(struct {
// DTS uint32
// *RTPNalu
// })
// p.PushNalu(el.DTS, (el.PTS - el.DTS), el.Payload)
// }
// b.MaxTS = 0
// }
// for heap.Push(&b, current); current.Next != nil; current = current.Next {
// heap.Push(&b, current.Next)
// }
// }
// }
// return
// }
// p.PushNalu(p.PTS, 0, last.Payload)
// for last = current; last.Next != nil; last = last.Next {
// p.PushNalu(p.PTS, 0, last.Payload)
// }
// }
// }
// }
// }
func (p *RTPVideo) _demux(ts uint32, payload []byte) {
if nalus := p.demuxNalu(payload); nalus != nil {
startPTS := nalus.PTS
dtsEst := NewDTSEstimator()
dts := dtsEst.Feed(0)
p.PushNalu(dts, 0, nalus.Payload)
var cache [][]byte
pts := startPTS
for nalus = nalus.Next; nalus != nil; nalus = nalus.Next {
pts = nalus.PTS - startPTS
dts = dtsEst.Feed(pts)
p.PushNalu(dts, pts-dts, nalus.Payload)
}
p.OnDemux = func(ts uint32, payload []byte) {
for nalus := p.demuxNalu(p.Payload); nalus != nil; nalus = nalus.Next {
if len(cache) == 0 {
pts = nalus.PTS - startPTS
dts = dtsEst.Feed(pts)
}
cache = append(cache, nalus.Payload)
if p.Marker {
p.PushNalu(dts, pts-dts, cache...)
cache = cache[:0]
}
}
}
}
}

413
stream.go
View File

@@ -2,259 +2,228 @@ package engine
import ( import (
"context" "context"
"sync" "sync/atomic"
"time" "time"
utils "github.com/Monibuca/utils/v3" "github.com/Monibuca/engine/v4/track"
"github.com/Monibuca/engine/v4/util"
. "github.com/logrusorgru/aurora" . "github.com/logrusorgru/aurora"
"github.com/pkg/errors"
) )
type StreamCollection struct { type StreamState byte
sync.RWMutex type StreamAction byte
m map[string]*Stream
}
func (sc *StreamCollection) GetStream(streamPath string) *Stream { const (
sc.RLock() STATE_WAITPUBLISH StreamState = iota // 等待发布者状态
defer sc.RUnlock() STATE_WAITTRACK // 等待Track
if s, ok := sc.m[streamPath]; ok { STATE_PUBLISHING // 正在发布流状态
return s STATE_WAITCLOSE // 等待关闭状态(自动关闭延时开启)
} STATE_CLOSED
return nil )
}
func (sc *StreamCollection) Delete(streamPath string) {
sc.Lock()
delete(sc.m, streamPath)
sc.Unlock()
}
func (sc *StreamCollection) ToList() (r []*Stream) { const (
sc.RLock() ACTION_PUBLISH StreamAction = iota
defer sc.RUnlock() ACTION_TIMEOUT // 发布流长时间没有数据/长时间没有发布者发布流/等待关闭时间到
for _, s := range sc.m { ACTION_PUBLISHLOST // 发布者意外断开
r = append(r, s) ACTION_CLOSE // 主动关闭流
} ACTION_LASTLEAVE // 最后一个订阅者离开
return ACTION_FIRSTENTER // 第一个订阅者进入
} )
func (sc *StreamCollection) Range(f func(*Stream)) { var StreamFSM = [STATE_CLOSED + 1]map[StreamAction]StreamState{
sc.RLock() {
defer sc.RUnlock() ACTION_PUBLISH: STATE_WAITTRACK,
for _, s := range sc.m { ACTION_LASTLEAVE: STATE_CLOSED,
f(s) ACTION_CLOSE: STATE_CLOSED,
} },
} {
ACTION_PUBLISHLOST: STATE_WAITPUBLISH,
func init() { ACTION_TIMEOUT: STATE_PUBLISHING,
Streams.m = make(map[string]*Stream) ACTION_CLOSE: STATE_CLOSED,
},
{
ACTION_PUBLISHLOST: STATE_WAITPUBLISH,
ACTION_TIMEOUT: STATE_WAITPUBLISH,
ACTION_LASTLEAVE: STATE_WAITCLOSE,
ACTION_CLOSE: STATE_CLOSED,
},
{
ACTION_PUBLISHLOST: STATE_CLOSED,
ACTION_TIMEOUT: STATE_CLOSED,
ACTION_FIRSTENTER: STATE_PUBLISHING,
ACTION_CLOSE: STATE_CLOSED,
},
{},
} }
// Streams 所有的流集合 // Streams 所有的流集合
var Streams StreamCollection var Streams = util.Map[string, *Stream]{Map: make(map[string]*Stream)}
var StreamTimeoutError = errors.New("timeout")
//FindStream 根据流路径查找流 type SubscribeAction *Subscriber
func FindStream(streamPath string) *Stream { type UnSubscibeAction *Subscriber
return Streams.GetStream(streamPath)
}
// Publish 直接发布
func Publish(streamPath, t string) *Stream {
var stream = &Stream{
StreamPath: streamPath,
Type: t,
}
if stream.Publish() {
return stream
}
return nil
}
type StreamContext struct {
context.Context
cancel context.CancelFunc
timeout *time.Timer //更新时间用来做超时处理
IsTimeout bool
}
func (r *StreamContext) Err() error {
if r.IsTimeout {
return StreamTimeoutError
}
return r.Context.Err()
}
func (r *StreamContext) Update() {
if r.timeout != nil {
r.timeout.Reset(config.PublishTimeout)
}
}
// Stream 流定义 // Stream 流定义
type Stream struct { type Stream struct {
context.Context
cancel context.CancelFunc
Publisher
State StreamState
timeout *time.Timer //当前状态的超时定时器
actionChan chan any
Config StreamConfig
URL string //远程地址,仅远程拉流有值 URL string //远程地址,仅远程拉流有值
StreamContext `json:"-"`
StreamPath string StreamPath string
Type string //流类型,来自发布者
StartTime time.Time //流的创建时间 StartTime time.Time //流的创建时间
Subscribers []*Subscriber // 订阅者 Subscribers util.Slice[*Subscriber] // 订阅者
VideoTracks Tracks Tracks
AudioTracks Tracks FrameCount uint32 //帧总数
DataTracks Tracks }
AutoCloseAfter *int //当无人订阅时延迟N秒后自动停止发布
Transcoding map[string]string //转码配置key目标编码value发布者提供的编码 func (r *Stream) Register(streamPath string) (result bool) {
subscribeMutex sync.Mutex if r == nil {
OnClose func() `json:"-"` r = &Stream{
ExtraProp interface{} //额外的属性用于实现子类化减少map的使用 Config: config.StreamConfig,
closeDelay *time.Timer }
}
r.StreamPath = streamPath
if result = Streams.Add(streamPath, r); result {
r.actionChan = make(chan any, 1)
r.StartTime = time.Now()
r.timeout = time.NewTimer(r.Config.WaitTimeout.Duration())
r.Context, r.cancel = context.WithCancel(Ctx)
r.Init(r)
go r.run()
}
return
}
// ForceRegister 强制注册流,会将已有的流踢掉
func (r *Stream) ForceRegister(streamPath string) {
if ok := r.Register(streamPath); !ok {
if s := Streams.Get(streamPath); s != nil {
s.Close()
<-s.Done()
}
r.ForceRegister(streamPath)
} else {
return
}
}
func (r *Stream) action(action StreamAction) {
if next, ok := StreamFSM[r.State][action]; ok {
if r.Publisher == nil || r.OnStateChange(r.State, next) {
util.Print(Yellow("Stream "), BrightCyan(r.StreamPath), " state changed :", r.State, "->", next)
r.State = next
switch next {
case STATE_WAITPUBLISH:
r.timeout.Reset(r.Config.WaitTimeout.Duration())
case STATE_WAITTRACK:
r.timeout.Reset(time.Second * 5)
case STATE_PUBLISHING:
r.WaitDone()
r.timeout.Reset(r.Config.PublishTimeout.Duration())
case STATE_WAITCLOSE:
r.timeout.Reset(r.Config.WaitCloseTimeout.Duration())
case STATE_CLOSED:
r.cancel()
r.WaitDone()
close(r.actionChan)
Streams.Delete(r.StreamPath)
fallthrough
default:
r.timeout.Stop()
}
}
}
} }
func (r *Stream) Close() { func (r *Stream) Close() {
Streams.Lock() r.actionChan <- ACTION_CLOSE
//如果没有发布过,就不需要进行处理 }
if r.cancel == nil { func (r *Stream) UnSubscribe(sub *Subscriber) {
Streams.Unlock() r.actionChan <- UnSubscibeAction(sub)
}
func (r *Stream) Subscribe(sub *Subscriber) {
r.actionChan <- SubscribeAction(sub)
}
func (r *Stream) run() {
for {
select {
case <-r.timeout.C:
util.Print(Yellow("Stream "), BrightCyan(r.StreamPath), "timeout:", r.State)
r.action(ACTION_TIMEOUT)
case <-r.Done():
r.action(ACTION_CLOSE)
case action, ok := <-r.actionChan:
if ok {
switch v := action.(type) {
case StreamAction:
r.action(v)
case SubscribeAction:
v.Stream = r
v.Context, v.cancel = context.WithCancel(r)
r.Subscribers.Add(v)
util.Print(Sprintf(Yellow("%s subscriber %s added remains:%d"), BrightCyan(r.StreamPath), Cyan(v.ID), Blue(len(r.Subscribers))))
if r.Subscribers.Len() == 1 {
r.action(ACTION_FIRSTENTER)
}
case UnSubscibeAction:
if r.Subscribers.Delete(v) {
util.Print(Sprintf(Yellow("%s subscriber %s removed remains:%d"), BrightCyan(r.StreamPath), Cyan(v.ID), Blue(len(r.Subscribers))))
if r.Subscribers.Len() == 0 && r.Config.WaitCloseTimeout > 0 {
r.action(ACTION_LASTLEAVE)
}
}
}
} else {
return return
} }
if r.closeDelay != nil {
r.closeDelay.Stop()
} }
r.cancel()
r.cancel = nil
delete(Streams.m, r.StreamPath)
Streams.Unlock()
r.VideoTracks.Dispose()
r.AudioTracks.Dispose()
r.DataTracks.Dispose()
if r.OnClose != nil {
r.OnClose()
}
TriggerHook(HOOK_STREAMCLOSE, r)
utils.Print(Yellow("Stream destoryed :"), BrightCyan(r.StreamPath))
}
// Publish 发布者进行发布操作
func (r *Stream) Publish() bool {
Streams.Lock()
defer Streams.Unlock()
if _, ok := Streams.m[r.StreamPath]; ok {
return false
}
if r.AutoCloseAfter == nil {
r.AutoCloseAfter = &config.AutoCloseAfter
}
var closeChann <-chan time.Time
if *r.AutoCloseAfter > 0 {
r.closeDelay = time.NewTimer(time.Duration(*r.AutoCloseAfter) * time.Second)
r.closeDelay.Stop()
closeChann = r.closeDelay.C
}
r.Context, r.cancel = context.WithCancel(Ctx)
r.VideoTracks.Init(r)
r.AudioTracks.Init(r)
r.DataTracks.Init(r)
r.StartTime = time.Now()
Streams.m[r.StreamPath] = r
utils.Print(Green("Stream publish:"), BrightCyan(r.StreamPath))
go r.waitClose(closeChann)
//触发钩子
TriggerHook(HOOK_PUBLISH, r)
return true
}
// 等待流关闭
func (r *Stream) waitClose(closeChann <-chan time.Time) {
r.timeout = time.NewTimer(config.PublishTimeout)
defer r.timeout.Stop()
if r.closeDelay != nil {
defer r.closeDelay.Stop()
}
select {
case <-r.Done():
case <-closeChann:
utils.Print(Yellow("Stream closeDelay:"), BrightCyan(r.StreamPath))
r.Close()
case <-r.timeout.C:
utils.Print(Yellow("Stream timeout:"), BrightCyan(r.StreamPath))
r.IsTimeout = true
r.Close()
} }
} }
func (r *Stream) WaitDataTrack(names ...string) *DataTrack { // Update 更新数据重置超时定时器
if !config.EnableVideo { func (r *Stream) Update() uint32 {
if r.State == STATE_PUBLISHING {
r.timeout.Reset(r.Config.PublishTimeout.Duration())
}
return atomic.AddUint32(&r.FrameCount, 1)
}
// 如果暂时不知道编码格式可以用这个
func (r *Stream) NewVideoTrack() (vt *track.UnknowVideo) {
vt = &track.UnknowVideo{
Stream: r,
}
return
}
func (r *Stream) NewH264Track() (vt *track.H264) {
return track.NewH264(r)
}
func (r *Stream) NewH265Track() (vt *track.H265) {
return track.NewH265(r)
}
// func (r *Stream) WaitDataTrack(names ...string) DataTrack {
// t := <-r.WaitTrack(names...)
// return t.(DataTrack)
// }
func (r *Stream) WaitVideoTrack(names ...string) track.Video {
if !r.Config.EnableVideo {
return nil return nil
} }
if track := r.DataTracks.WaitTrack(names...); track != nil { t := <-r.WaitTrack(names...)
return track.(*DataTrack) return t.(track.Video)
}
return nil
} }
func (r *Stream) WaitVideoTrack(names ...string) *VideoTrack { func (r *Stream) WaitAudioTrack(names ...string) track.Audio {
if !config.EnableVideo { if !r.Config.EnableAudio {
return nil return nil
} }
if track := r.VideoTracks.WaitTrack(names...); track != nil { t := <-r.WaitTrack(names...)
return track.(*VideoTrack) return t.(track.Audio)
}
return nil
}
// TODO: 触发转码逻辑
func (r *Stream) WaitAudioTrack(names ...string) *AudioTrack {
if !config.EnableAudio {
return nil
}
if track := r.AudioTracks.WaitTrack(names...); track != nil {
return track.(*AudioTrack)
}
return nil
}
//Subscribe 订阅流
func (r *Stream) Subscribe(s *Subscriber) {
if s.Stream = r; r.Err() == nil {
s.SubscribeTime = time.Now()
utils.Print(Sprintf(Yellow("subscribe :%s %s,to Stream %s"), Blue(s.Type), Cyan(s.ID), BrightCyan(r.StreamPath)))
s.Context, s.cancel = context.WithCancel(r)
r.subscribeMutex.Lock()
if *r.AutoCloseAfter > 0 {
r.closeDelay.Stop()
}
r.Subscribers = append(r.Subscribers, s)
TriggerHook(HOOK_SUBSCRIBE, s, len(r.Subscribers))
r.subscribeMutex.Unlock()
utils.Print(Sprintf(Yellow("%s subscriber %s added remains:%d"), BrightCyan(r.StreamPath), Cyan(s.ID), Blue(len(r.Subscribers))))
}
}
//UnSubscribe 取消订阅流
func (r *Stream) UnSubscribe(s *Subscriber) {
if r.Err() == nil {
var deleted bool
r.subscribeMutex.Lock()
defer r.subscribeMutex.Unlock()
r.Subscribers, deleted = DeleteSliceItem_Subscriber(r.Subscribers, s)
if deleted {
utils.Print(Sprintf(Yellow("%s subscriber %s removed remains:%d"), BrightCyan(r.StreamPath), Cyan(s.ID), Blue(len(r.Subscribers))))
l := len(r.Subscribers)
TriggerHook(HOOK_UNSUBSCRIBE, s, l)
if l == 0 && *r.AutoCloseAfter >= 0 {
if *r.AutoCloseAfter == 0 {
r.Close()
} else {
r.closeDelay.Reset(time.Duration(*r.AutoCloseAfter) * time.Second)
}
}
}
}
}
func DeleteSliceItem_Subscriber(slice []*Subscriber, item *Subscriber) ([]*Subscriber, bool) {
for i, val := range slice {
if val == item {
return append(slice[:i], slice[i+1:]...), true
}
}
return slice, false
} }

View File

@@ -6,14 +6,18 @@ import (
"sync" "sync"
"time" "time"
. "github.com/Monibuca/engine/v4/common"
"github.com/Monibuca/engine/v4/track"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
type AudioFrame AVFrame[AudioSlice]
type VideoFrame AVFrame[NALUSlice]
// Subscriber 订阅者实体定义 // Subscriber 订阅者实体定义
type Subscriber struct { type Subscriber struct {
context.Context `json:"-"` context.Context `json:"-"`
cancel context.CancelFunc cancel context.CancelFunc
Ctx2 context.Context `json:"-"`
*Stream `json:"-"` *Stream `json:"-"`
ID string ID string
TotalDrop int //总丢帧 TotalDrop int //总丢帧
@@ -23,8 +27,8 @@ type Subscriber struct {
Delay uint32 Delay uint32
SubscribeTime time.Time SubscribeTime time.Time
SubscribeArgs url.Values SubscribeArgs url.Values
OnAudio func(uint32, *AudioPack) `json:"-"` OnAudio func(*AudioFrame) bool `json:"-"`
OnVideo func(uint32, *VideoPack) `json:"-"` OnVideo func(*VideoFrame) bool `json:"-"`
closeOnce sync.Once closeOnce sync.Once
} }
@@ -51,7 +55,7 @@ func (s *Subscriber) Subscribe(streamPath string) error {
} else { } else {
streamPath = u.Path streamPath = u.Path
} }
if stream := FindStream(streamPath); stream == nil { if stream := Streams.Get(streamPath); stream == nil {
return errors.Errorf("subscribe %s faild :stream not found", streamPath) return errors.Errorf("subscribe %s faild :stream not found", streamPath)
} else { } else {
if stream.Subscribe(s); s.Context == nil { if stream.Subscribe(s); s.Context == nil {
@@ -62,7 +66,7 @@ func (s *Subscriber) Subscribe(streamPath string) error {
} }
//Play 开始播放 //Play 开始播放
func (s *Subscriber) Play(at *AudioTrack, vt *VideoTrack) { func (s *Subscriber) Play(at track.Audio, vt track.Video) {
defer s.Close() defer s.Close()
if vt == nil && at == nil { if vt == nil && at == nil {
return return
@@ -74,71 +78,42 @@ func (s *Subscriber) Play(at *AudioTrack, vt *VideoTrack) {
s.PlayVideo(vt) s.PlayVideo(vt)
return return
} }
var extraExit <-chan struct{} vr := vt.ReadRing() //从关键帧开始读取,首屏秒开
if s.Ctx2 != nil { ar := at.ReadRing()
extraExit = s.Ctx2.Done() vp := vr.Read()
} ap := ar.TryRead()
streamExit := s.Context.Done() // chase := true
select {
case <-vt.WaitIDR: //等待获取到第一个关键帧
case <-streamExit: //可能等不到关键帧就退出了
return
case <-extraExit: //可能等不到关键帧就退出了
return
}
vr := vt.SubRing(vt.IDRing) //从关键帧开始读取,首屏秒开
realSt := vt.PreItem().Timestamp // 当前时间戳
ar := at.Clone()
iv, vp := vr.Read()
ia, ap := ar.TryRead()
vst := iv.Timestamp
chase := true
for { for {
select { if ap == nil && vp == nil {
case <-extraExit:
return
case <-streamExit:
return
default:
if ia == nil && iv == nil {
time.Sleep(time.Millisecond * 10) time.Sleep(time.Millisecond * 10)
} else if ia != nil && (iv == nil || iv.Timestamp.After(ia.Timestamp)) { } else if ap != nil && (vp == nil || vp.SeqInStream > ap.SeqInStream) {
s.OnAudio(uint32(ia.Timestamp.Sub(vst).Milliseconds()), ap.(*AudioPack)) s.onAudio(ap)
ar.MoveNext() ar.MoveNext()
} else if iv != nil && (ia == nil || ia.Timestamp.After(iv.Timestamp)) { } else if vp != nil && (ap == nil || ap.SeqInStream > vp.SeqInStream) {
s.OnVideo(uint32(iv.Timestamp.Sub(vst).Milliseconds()), vp.(*VideoPack)) s.onVideo(vp)
if chase { // if chase {
if add10 := vst.Add(time.Millisecond * 10); realSt.After(add10) { // if add10 := vst.Add(time.Millisecond * 10); realSt.After(add10) {
vst = add10 // vst = add10
} else { // } else {
vst = realSt // vst = realSt
chase = false // chase = false
} // }
} // }
vr.MoveNext() vr.MoveNext()
} }
ia, ap = ar.TryRead() ap = ar.TryRead()
iv, vp = vr.TryRead() vp = vr.TryRead()
}
} }
} }
func (s *Subscriber) onAudio(ts uint32, ap *AudioPack) { func (s *Subscriber) onAudio(af *AVFrame[AudioSlice]) bool {
s.OnAudio(ts, ap) return s.OnAudio((*AudioFrame)(af))
} }
func (s *Subscriber) onVideo(ts uint32, vp *VideoPack) { func (s *Subscriber) onVideo(vf *AVFrame[NALUSlice]) bool {
s.OnVideo(ts, vp) return s.OnVideo((*VideoFrame)(vf))
} }
func (s *Subscriber) PlayAudio(at *AudioTrack) { func (s *Subscriber) PlayAudio(vt track.Audio) {
if s.Ctx2 != nil { vt.Play(s.onAudio)
at.Play(s.onAudio, s.Done(), s.Ctx2.Done())
} else {
at.Play(s.onAudio, s.Done(), nil)
}
} }
func (s *Subscriber) PlayVideo(vt *VideoTrack) { func (s *Subscriber) PlayVideo(vt track.Video) {
if s.Ctx2 != nil { vt.Play(s.onVideo)
vt.Play(s.onVideo, s.Done(), s.Ctx2.Done())
} else {
vt.Play(s.onVideo, s.Done(), nil)
}
} }

38
track/aac.go Normal file
View File

@@ -0,0 +1,38 @@
package track
import (
"github.com/Monibuca/engine/v4/codec"
. "github.com/Monibuca/engine/v4/common"
"time"
)
func NewAAC(stream IStream) (aac *AAC) {
aac = &AAC{}
aac.Stream = stream
aac.CodecID = codec.CodecID_AAC
aac.Init(stream, 32)
aac.Poll = time.Millisecond * 20
return
}
type AAC struct {
BaseAudio
}
func (aac *AAC) WriteAVCC(ts uint32, frame AVCCFrame) {
if frame.IsSequence() {
aac.DecoderConfiguration.Reset()
aac.DecoderConfiguration.AppendAVCC(frame)
config1, config2 := frame[2], frame[3]
//audioObjectType = (config1 & 0xF8) >> 3
// 1 AAC MAIN ISO/IEC 14496-3 subpart 4
// 2 AAC LC ISO/IEC 14496-3 subpart 4
// 3 AAC SSR ISO/IEC 14496-3 subpart 4
// 4 AAC LTP ISO/IEC 14496-3 subpart 4
aac.Channels = ((config2 >> 3) & 0x0F) //声道
aac.SampleRate = HZ(codec.SamplingFrequencies[((config1&0x7)<<1)|(config2>>7)])
aac.DecoderConfiguration.AppendRaw(AudioSlice(frame[2:]))
} else {
aac.BaseAudio.WriteAVCC(ts, frame)
}
}

89
track/audio.go Normal file
View File

@@ -0,0 +1,89 @@
package track
import (
"strings"
"github.com/Monibuca/engine/v4/codec"
. "github.com/Monibuca/engine/v4/common"
"github.com/Monibuca/engine/v4/util"
)
type Audio interface {
AVTrack
ReadRing() *AVRing[AudioSlice]
Play(onAudio func(*AVFrame[AudioSlice]) bool)
}
type BaseAudio struct {
Media[AudioSlice]
Channels byte
avccHead []byte
}
func (at *BaseAudio) ReadRing() *AVRing[AudioSlice] {
return util.Clone(at.AVRing)
}
func (at *BaseAudio) Play(onAudio func(*AVFrame[AudioSlice]) bool) {
ar := at.ReadRing()
for ap := ar.Read(); at.Stream.Err() == nil; ap = ar.Read() {
if !onAudio(ap) {
break
}
ar.MoveNext()
}
}
func (at *BaseAudio) WriteAVCC(ts uint32, frame AVCCFrame) {
at.Media.WriteAVCC(ts, frame)
at.Flush()
}
func (at *BaseAudio) Flush() {
if at.Value.AVCC == nil {
at.Value.AppendAVCC(at.avccHead)
for _, raw := range at.Value.Raw {
at.Value.AppendAVCC(raw)
}
}
at.Media.Flush()
}
type UnknowAudio struct {
Name string
Stream IStream
Know Audio
}
func (at *UnknowAudio) WriteAVCC(ts uint32, frame AVCCFrame) {
if at.Know == nil {
codecID := frame.AudioCodecID()
if at.Name == "" {
at.Name = strings.ToLower(codec.SoundFormat[codecID])
}
switch codecID {
case codec.CodecID_AAC:
if !frame.IsSequence() {
return
}
a := NewAAC(at.Stream)
at.Know = a
a.avccHead = []byte{frame[0], 1}
a.WriteAVCC(0, frame)
a.Stream.AddTrack(a.Name, a)
case codec.CodecID_PCMA,
codec.CodecID_PCMU:
alaw := true
if codecID == codec.CodecID_PCMU {
alaw = false
}
a := NewG711(at.Stream, alaw)
at.Know = a
a.SampleRate = HZ(codec.SoundRate[(frame[0]&0x0c)>>2])
a.Channels = frame[0]&0x01 + 1
a.avccHead = frame[:1]
a.Stream.AddTrack(a.Name, a)
}
} else {
at.Know.WriteAVCC(ts, frame)
}
}

56
track/base.go Normal file
View File

@@ -0,0 +1,56 @@
package track
import (
. "github.com/Monibuca/engine/v4/common"
"github.com/Monibuca/engine/v4/util"
"github.com/pion/rtp"
)
// Base 基础Track类
type Base struct {
Name string
Stream IStream `json:"-"`
BPS
}
func (bt *Base) Flush(bf *BaseFrame) {
bt.ComputeBPS(bf.BytesIn)
bf.SeqInStream = bt.Stream.Update()
}
// Media 基础媒体Track类
type Media[T RawSlice] struct {
Base
AVRing[T] `json:"-"`
CodecID byte
SampleRate HZ
DecoderConfiguration AVFrame[T] `json:"-"` //H264(SPS、PPS) H265(VPS、SPS、PPS) AAC(config)
util.BytesPool //无锁内存池,用于发布者(在同一个协程中)复用小块的内存,通常是解包时需要临时使用
}
func (av *Media[T]) WriteRTP(raw []byte) {
av.Value.AppendRTP(raw)
var packet rtp.Packet
if err := packet.Unmarshal(raw); err != nil {
return
}
av.Value.AppendRTPPackets(packet)
if packet.Marker {
av.Flush()
}
}
func (av *Media[T]) WriteSlice(slice T) {
av.Value.AppendRaw(slice)
}
func (av *Media[T]) WriteAVCC(ts uint32, frame AVCCFrame) {
av.Value.BytesIn = len(frame)
av.Value.AppendAVCC(frame)
av.Value.DTS = av.SampleRate.ToNTS(ts)
av.Value.PTS = av.SampleRate.ToNTS(ts + frame.CTS())
}
func (av *Media[T]) Flush() {
av.Base.Flush(&av.Value.BaseFrame)
av.Step()
}

30
track/g711.go Normal file
View File

@@ -0,0 +1,30 @@
package track
import (
"time"
"github.com/Monibuca/engine/v4/codec"
. "github.com/Monibuca/engine/v4/common"
)
func NewG711(stream IStream, alaw bool) (g711 *G711) {
g711 = &G711{}
g711.Stream = stream
if alaw {
g711.CodecID = codec.CodecID_PCMA
} else {
g711.CodecID = codec.CodecID_PCMU
}
g711.Init(stream, 32)
g711.Poll = time.Millisecond * 20
return
}
type G711 struct {
BaseAudio
}
func (g711 *G711) WriteAVCC(ts uint32, frame AVCCFrame) {
g711.Value.AppendRaw(AudioSlice(frame[1:]))
g711.BaseAudio.WriteAVCC(ts, frame)
}

78
track/h264.go Normal file
View File

@@ -0,0 +1,78 @@
package track
import (
"time"
"github.com/Monibuca/engine/v4/codec"
. "github.com/Monibuca/engine/v4/common"
"github.com/Monibuca/engine/v4/util"
)
type H264 struct {
H264H265
}
func NewH264(stream IStream) (vt *H264) {
vt = &H264{}
vt.CodecID = codec.CodecID_H264
vt.SampleRate = 90000
vt.Stream = stream
vt.Init(stream, 256)
vt.Poll = time.Millisecond * 20
return
}
func (vt *H264) WriteSlice(slice NALUSlice) {
switch H264Slice(slice).Type() {
case codec.NALU_SPS:
vt.DecoderConfiguration.Reset()
vt.DecoderConfiguration.AppendRaw(slice)
case codec.NALU_PPS:
vt.DecoderConfiguration.AppendRaw(slice)
vt.SPSInfo, _ = codec.ParseSPS(slice[0])
lenSPS := SizeOfBuffers(vt.DecoderConfiguration.Raw[0])
lenPPS := SizeOfBuffers(vt.DecoderConfiguration.Raw[1])
if lenSPS > 3 {
vt.DecoderConfiguration.AppendAVCC(codec.RTMP_AVC_HEAD[:6], vt.DecoderConfiguration.Raw[0][0][1:4])
} else {
vt.DecoderConfiguration.AppendAVCC(codec.RTMP_AVC_HEAD)
}
tmp := []byte{0xE1, 0, 0, 0x01, 0, 0}
vt.DecoderConfiguration.AppendAVCC(tmp[:1], util.PutBE(tmp[1:3], lenSPS), vt.DecoderConfiguration.Raw[0][0], tmp[3:4], util.PutBE(tmp[3:6], lenPPS), vt.DecoderConfiguration.Raw[1][0])
case codec.NALU_IDR_Picture:
case codec.NALU_Non_IDR_Picture:
case codec.NALU_SEI:
vt.Media.WriteSlice(slice)
}
}
func (vt *H264) WriteAVCC(ts uint32, frame AVCCFrame) {
if frame.IsSequence() {
vt.DecoderConfiguration.Reset()
vt.DecoderConfiguration.SeqInTrack = vt.Value.SeqInTrack
vt.DecoderConfiguration.AppendAVCC(frame)
var info codec.AVCDecoderConfigurationRecord
if _, err := info.Unmarshal(frame[5:]); err == nil {
vt.SPSInfo, _ = codec.ParseSPS(info.SequenceParameterSetNALUnit)
vt.nalulenSize = int(info.LengthSizeMinusOne&3 + 1)
vt.DecoderConfiguration.AppendRaw(NALUSlice{info.SequenceParameterSetNALUnit}, NALUSlice{info.PictureParameterSetNALUnit})
}
} else {
vt.H264H265.WriteAVCC(ts, frame)
}
}
func (vt *H264) Flush() {
if H264NALU(vt.Value.Raw).IFrame() {
vt.Value.IFrame = true
if vt.IDRing == nil {
defer vt.Stream.AddTrack(vt.Name, vt)
}
vt.ComputeGOP()
}
// RTP格式补完
if vt.Value.RTP == nil {
}
vt.H264H265.Flush()
}

75
track/h265.go Normal file
View File

@@ -0,0 +1,75 @@
package track
import (
"time"
"github.com/Monibuca/engine/v4/codec"
. "github.com/Monibuca/engine/v4/common"
)
type H265 struct {
H264H265
}
func NewH265(stream IStream) (vt *H265) {
vt = &H265{}
vt.CodecID = codec.CodecID_H265
vt.SampleRate = 90000
vt.Stream = stream
vt.Init(stream, 256)
vt.Poll = time.Millisecond * 20
return
}
func (vt *H265) WriteSlice(slice NALUSlice) {
switch H265Slice(slice).Type() {
case codec.NAL_UNIT_VPS:
vt.DecoderConfiguration.Reset()
vt.DecoderConfiguration.AppendRaw(slice)
case codec.NAL_UNIT_SPS:
vt.DecoderConfiguration.AppendRaw(slice)
vt.SPSInfo, _ = codec.ParseHevcSPS(slice[0])
case codec.NAL_UNIT_PPS:
vt.DecoderConfiguration.AppendRaw(slice)
extraData, err := codec.BuildH265SeqHeaderFromVpsSpsPps(vt.DecoderConfiguration.Raw[0][0], vt.DecoderConfiguration.Raw[1][0], vt.DecoderConfiguration.Raw[2][0])
if err == nil {
vt.DecoderConfiguration.AppendAVCC(extraData)
}
case 0, 1, 2, 3, 4, 5, 6, 7, 9,
codec.NAL_UNIT_CODED_SLICE_BLA,
codec.NAL_UNIT_CODED_SLICE_BLANT,
codec.NAL_UNIT_CODED_SLICE_BLA_N_LP,
codec.NAL_UNIT_CODED_SLICE_IDR,
codec.NAL_UNIT_CODED_SLICE_IDR_N_LP,
codec.NAL_UNIT_CODED_SLICE_CRA:
vt.Media.WriteSlice(slice)
}
}
func (vt *H265) WriteAVCC(ts uint32, frame AVCCFrame) {
if frame.IsSequence() {
vt.DecoderConfiguration.Reset()
vt.DecoderConfiguration.SeqInTrack = vt.Value.SeqInTrack
vt.DecoderConfiguration.AppendAVCC(frame)
if vps, sps, pps, err := codec.ParseVpsSpsPpsFromSeqHeaderWithoutMalloc(frame); err == nil {
vt.SPSInfo, _ = codec.ParseHevcSPS(frame)
vt.nalulenSize = int(frame[26]) & 0x03
vt.DecoderConfiguration.AppendRaw(NALUSlice{vps}, NALUSlice{sps}, NALUSlice{pps})
}
} else {
vt.H264H265.WriteAVCC(ts, frame)
}
}
func (vt *H265) Flush() {
if H265NALU(vt.Value.Raw).IFrame() {
vt.Value.IFrame = true
if vt.IDRing == nil {
defer vt.Stream.AddTrack(vt.Name, vt)
}
vt.ComputeGOP()
}
// RTP格式补完
if vt.Value.RTP == nil {
}
vt.H264H265.Flush()
}

137
track/video.go Normal file
View File

@@ -0,0 +1,137 @@
package track
import (
"strings"
"github.com/Monibuca/engine/v4/codec"
. "github.com/Monibuca/engine/v4/common"
"github.com/Monibuca/engine/v4/util"
)
type Video interface {
AVTrack
ReadRing() *AVRing[NALUSlice]
Play(onVideo func(*AVFrame[NALUSlice]) bool)
}
type H264H265 struct {
Media[NALUSlice]
IDRing *util.Ring[AVFrame[NALUSlice]] `json:"-"` //最近的关键帧位置,首屏渲染
SPSInfo codec.SPSInfo
GOP int //关键帧间隔
nalulenSize int //avcc格式中表示nalu长度的字节数通常为4
idrCount int //缓存中包含的idr数量
}
func (t *H264H265) ComputeGOP() {
t.idrCount++
if t.IDRing != nil {
t.GOP = int(t.Value.SeqInTrack - t.IDRing.Value.SeqInTrack)
if l := t.Size - t.GOP - 5; l > 5 {
t.Size -= l
//缩小缓冲环节省内存
t.Unlink(l).Do(func(v AVFrame[NALUSlice]) {
if v.IFrame {
t.idrCount--
}
v.Reset()
})
}
}
t.IDRing = t.Ring
}
func (vt *H264H265) WriteAVCC(ts uint32, frame AVCCFrame) {
vt.Media.WriteAVCC(ts, frame)
for nalus := frame[5:]; len(nalus) > vt.nalulenSize; {
nalulen := util.ReadBE[int](nalus[:vt.nalulenSize])
if end := nalulen + vt.nalulenSize; len(nalus) >= end {
vt.Value.AppendRaw(NALUSlice{nalus[vt.nalulenSize:end]})
nalus = nalus[end:]
} else {
util.Printf("WriteAVCC error,len %d,nalulenSize:%d,end:%d", len(nalus), vt.nalulenSize, end)
break
}
}
vt.Flush()
}
func (vt *H264H265) Flush() {
// AVCC格式补完
if vt.Value.AVCC == nil {
b := []byte{vt.CodecID, 1, 0, 0, 0}
if vt.Value.IFrame {
b[0] |= 0x10
} else {
b[0] |= 0x20
}
// 写入CTS
util.PutBE(b[2:5], vt.SampleRate.ToMini(vt.Value.PTS-vt.Value.DTS))
vt.Value.AppendAVCC(b)
for _, nalu := range vt.Value.Raw {
vt.Value.AppendAVCC(util.PutBE(make([]byte, 4), SizeOfBuffers(nalu)))
vt.Value.AppendAVCC(nalu...)
}
}
// 下一帧为I帧即将覆盖
if vt.Next().Value.IFrame {
// 仅存一枚I帧需要扩环
if vt.idrCount == 1 {
if vt.Size < 256 {
vt.Link(util.NewRing[AVFrame[NALUSlice]](5)) // 扩大缓冲环
}
} else {
vt.idrCount--
}
}
vt.Media.Flush()
}
func (vt *H264H265) ReadRing() *AVRing[NALUSlice] {
vr := util.Clone(vt.AVRing)
vr.Ring = vt.IDRing
return vr
}
func (vt *H264H265) Play(onVideo func(*AVFrame[NALUSlice]) bool) {
vr := vt.ReadRing()
for vp := vr.Read(); vt.Stream.Err() == nil; vp = vr.Read() {
if !onVideo(vp) {
break
}
vr.MoveNext()
}
}
type UnknowVideo struct {
Name string
Stream IStream
Know Video
}
func (vt *UnknowVideo) WriteAnnexB(pts uint32, dts uint32, frame AnnexBFrame) {
}
func (vt *UnknowVideo) WriteAVCC(ts uint32, frame AVCCFrame) {
if vt.Know == nil {
if frame.IsSequence() {
codecID := frame.VideoCodecID()
if vt.Name == "" {
vt.Name = strings.ToLower(codec.CodecID[codecID])
}
switch codecID {
case codec.CodecID_H264:
v := NewH264(vt.Stream)
vt.Know = v
v.WriteAVCC(0, frame)
v.Stream.AddTrack(v.Name, v)
case codec.CodecID_H265:
v := NewH265(vt.Stream)
vt.Know = v
v.WriteAVCC(0, frame)
v.Stream.AddTrack(v.Name, v)
}
}
} else {
vt.Know.WriteAVCC(ts, frame)
}
}

83
tracks.go Normal file
View File

@@ -0,0 +1,83 @@
package engine
import (
"context"
"encoding/json"
"sync"
. "github.com/Monibuca/engine/v4/common"
)
type Tracks struct {
context.Context
sync.RWMutex
m map[string]Track
waiters map[string][]*chan Track
}
func (ts *Tracks) MarshalJSON() ([]byte, error) {
ts.RLock()
defer ts.RUnlock()
return json.Marshal(ts.m)
}
func (ts *Tracks) Init(ctx context.Context) {
ts.m = make(map[string]Track)
ts.waiters = make(map[string][]*chan Track)
ts.Context = ctx
}
func (ts *Tracks) AddTrack(name string, t Track) {
ts.Lock()
defer ts.Unlock()
if _, ok := ts.m[name]; !ok {
if ts.m[name] = t; ts.Err() == nil {
for i, ch := range ts.waiters[name] {
if ch != nil {
*ch <- t
close(*ch)
ts.waiters[name][i] = nil //通过设置为nil防止重复通知
}
}
}
}
}
func (ts *Tracks) GetTrack(name string) Track {
ts.RLock()
defer ts.RUnlock()
return ts.m[name]
}
// WaitDone 当等待结束时需要调用该函数防止订阅者无限等待Track
func (ts *Tracks) WaitDone() {
ts.Lock()
defer ts.Unlock()
for _, chs := range ts.waiters {
for i, ch := range chs {
if ch != nil {
close(*ch)
chs[i] = nil //通过设置为nil防止重复关闭
}
}
}
}
func (ts *Tracks) WaitTrack(names ...string) (ch chan Track) {
ch = make(chan Track, 1)
ts.Lock()
defer ts.Unlock()
for _, name := range names {
if t, ok := ts.m[name]; ok {
ch <- t
return
}
}
if ts.Err() == nil { //在等待时间范围内
for _, name := range names {
ts.waiters[name] = append(ts.waiters[name], &ch)
}
} else {
close(ch)
}
return
}

17
util/big_endian.go Normal file
View File

@@ -0,0 +1,17 @@
package util
import "constraints"
func PutBE[T constraints.Integer](b []byte, num T) []byte {
for i, n := 0, len(b); i < n; i++ {
b[i] = byte(num >> ((n - i - 1) << 3))
}
return b
}
func ReadBE[T constraints.Integer](b []byte) (num T) {
for i, n := 0, len(b); i < n; i++ {
num += T(b[i]) << ((n - i - 1) << 3)
}
return
}

118
util/bits/bits.go Normal file
View File

@@ -0,0 +1,118 @@
package bits
import (
"io"
)
type Reader struct {
R io.Reader
n int
bits uint64
}
func (self *Reader) ReadBits64(n int) (bits uint64, err error) {
if self.n < n {
var b [8]byte
var got int
want := (n - self.n + 7) / 8
if got, err = self.R.Read(b[:want]); err != nil {
return
}
if got < want {
err = io.EOF
return
}
for i := 0; i < got; i++ {
self.bits <<= 8
self.bits |= uint64(b[i])
}
self.n += got * 8
}
bits = self.bits >> uint(self.n-n)
self.bits ^= bits << uint(self.n-n)
self.n -= n
return
}
func (self *Reader) ReadBits(n int) (bits uint, err error) {
var bits64 uint64
if bits64, err = self.ReadBits64(n); err != nil {
return
}
bits = uint(bits64)
return
}
func (self *Reader) Read(p []byte) (n int, err error) {
for n < len(p) {
want := 8
if len(p)-n < want {
want = len(p) - n
}
var bits uint64
if bits, err = self.ReadBits64(want * 8); err != nil {
break
}
for i := 0; i < want; i++ {
p[n+i] = byte(bits >> uint((want-i-1)*8))
}
n += want
}
return
}
type Writer struct {
W io.Writer
n int
bits uint64
}
func (self *Writer) WriteBits64(bits uint64, n int) (err error) {
if self.n+n > 64 {
move := uint(64 - self.n)
mask := bits >> move
self.bits = (self.bits << move) | mask
self.n = 64
if err = self.FlushBits(); err != nil {
return
}
n -= int(move)
bits ^= (mask << move)
}
self.bits = (self.bits << uint(n)) | bits
self.n += n
return
}
func (self *Writer) WriteBits(bits uint, n int) (err error) {
return self.WriteBits64(uint64(bits), n)
}
func (self *Writer) Write(p []byte) (n int, err error) {
for n < len(p) {
if err = self.WriteBits64(uint64(p[n]), 8); err != nil {
return
}
n++
}
return
}
func (self *Writer) FlushBits() (err error) {
if self.n > 0 {
var b [8]byte
bits := self.bits
if self.n%8 != 0 {
bits <<= uint(8 - (self.n % 8))
}
want := (self.n + 7) / 8
for i := 0; i < want; i++ {
b[i] = byte(bits >> uint((want-i-1)*8))
}
if _, err = self.W.Write(b[:want]); err != nil {
return
}
self.n = 0
}
return
}

61
util/bits/bits_test.go Normal file
View File

@@ -0,0 +1,61 @@
package bits
import (
"bytes"
"testing"
)
func TestBits(t *testing.T) {
rdata := []byte{0xf3, 0xb3, 0x45, 0x60}
rbuf := bytes.NewReader(rdata[:])
r := &Reader{R: rbuf}
var u32 uint
if u32, _ = r.ReadBits(4); u32 != 0xf {
t.FailNow()
}
if u32, _ = r.ReadBits(4); u32 != 0x3 {
t.FailNow()
}
if u32, _ = r.ReadBits(2); u32 != 0x2 {
t.FailNow()
}
if u32, _ = r.ReadBits(2); u32 != 0x3 {
t.FailNow()
}
b := make([]byte, 2)
if r.Read(b); b[0] != 0x34 || b[1] != 0x56 {
t.FailNow()
}
wbuf := &bytes.Buffer{}
w := &Writer{W: wbuf}
w.WriteBits(0xf, 4)
w.WriteBits(0x3, 4)
w.WriteBits(0x2, 2)
w.WriteBits(0x3, 2)
n, _ := w.Write([]byte{0x34, 0x56})
if n != 2 {
t.FailNow()
}
w.FlushBits()
wdata := wbuf.Bytes()
if wdata[0] != 0xf3 || wdata[1] != 0xb3 || wdata[2] != 0x45 || wdata[3] != 0x60 {
t.FailNow()
}
b = make([]byte, 8)
PutUInt64BE(b, 0x11223344)
if b[0] != 0x11 || b[1] != 0x22 || b[2] != 0x33 || b[3] != 0x44 {
t.FailNow()
}
}
func PutUInt64BE(b []byte, v uint64) {
b[0] = byte(v >> 56)
b[1] = byte(v >> 48)
b[2] = byte(v >> 40)
b[3] = byte(v >> 32)
b[4] = byte(v >> 24)
b[5] = byte(v >> 16)
b[6] = byte(v >> 8)
b[7] = byte(v)
}

22
util/bits/bufio/bufio.go Normal file
View File

@@ -0,0 +1,22 @@
package bufio
import (
"io"
)
type Reader struct {
buf [][]byte
R io.ReadSeeker
}
func NewReaderSize(r io.ReadSeeker, size int) *Reader {
buf := make([]byte, size*2)
return &Reader{
R: r,
buf: [][]byte{buf[0:size], buf[size:]},
}
}
func (self *Reader) ReadAt(b []byte, off int64) (n int, err error) {
return
}

View File

@@ -0,0 +1,65 @@
package bits
import (
"io"
)
type GolombBitReader struct {
R io.Reader
buf [1]byte
left byte
}
func (self *GolombBitReader) ReadBit() (res uint, err error) {
if self.left == 0 {
if _, err = self.R.Read(self.buf[:]); err != nil {
return
}
self.left = 8
}
self.left--
res = uint(self.buf[0]>>self.left) & 1
return
}
func (self *GolombBitReader) ReadBits(n int) (res uint, err error) {
for i := 0; i < n; i++ {
var bit uint
if bit, err = self.ReadBit(); err != nil {
return
}
res |= bit << uint(n-i-1)
}
return
}
func (self *GolombBitReader) ReadExponentialGolombCode() (res uint, err error) {
i := 0
for {
var bit uint
if bit, err = self.ReadBit(); err != nil {
return
}
if !(bit == 0 && i < 32) {
break
}
i++
}
if res, err = self.ReadBits(i); err != nil {
return
}
res += (1 << uint(i)) - 1
return
}
func (self *GolombBitReader) ReadSE() (res uint, err error) {
if res, err = self.ReadExponentialGolombCode(); err != nil {
return
}
if res&0x01 != 0 {
res = (res + 1) / 2
} else {
res = -res / 2
}
return
}

3
util/bits/pio/pio.go Normal file
View File

@@ -0,0 +1,3 @@
package pio
var RecommendBufioSize = 1024 * 64

121
util/bits/pio/reader.go Normal file
View File

@@ -0,0 +1,121 @@
package pio
func U8(b []byte) (i uint8) {
return b[0]
}
func U16BE(b []byte) (i uint16) {
i = uint16(b[0])
i <<= 8
i |= uint16(b[1])
return
}
func I16BE(b []byte) (i int16) {
i = int16(b[0])
i <<= 8
i |= int16(b[1])
return
}
func I24BE(b []byte) (i int32) {
i = int32(int8(b[0]))
i <<= 8
i |= int32(b[1])
i <<= 8
i |= int32(b[2])
return
}
func U24BE(b []byte) (i uint32) {
i = uint32(b[0])
i <<= 8
i |= uint32(b[1])
i <<= 8
i |= uint32(b[2])
return
}
func I32BE(b []byte) (i int32) {
i = int32(int8(b[0]))
i <<= 8
i |= int32(b[1])
i <<= 8
i |= int32(b[2])
i <<= 8
i |= int32(b[3])
return
}
func U32LE(b []byte) (i uint32) {
i = uint32(b[3])
i <<= 8
i |= uint32(b[2])
i <<= 8
i |= uint32(b[1])
i <<= 8
i |= uint32(b[0])
return
}
func U32BE(b []byte) (i uint32) {
i = uint32(b[0])
i <<= 8
i |= uint32(b[1])
i <<= 8
i |= uint32(b[2])
i <<= 8
i |= uint32(b[3])
return
}
func U40BE(b []byte) (i uint64) {
i = uint64(b[0])
i <<= 8
i |= uint64(b[1])
i <<= 8
i |= uint64(b[2])
i <<= 8
i |= uint64(b[3])
i <<= 8
i |= uint64(b[4])
return
}
func U64BE(b []byte) (i uint64) {
i = uint64(b[0])
i <<= 8
i |= uint64(b[1])
i <<= 8
i |= uint64(b[2])
i <<= 8
i |= uint64(b[3])
i <<= 8
i |= uint64(b[4])
i <<= 8
i |= uint64(b[5])
i <<= 8
i |= uint64(b[6])
i <<= 8
i |= uint64(b[7])
return
}
func I64BE(b []byte) (i int64) {
i = int64(int8(b[0]))
i <<= 8
i |= int64(b[1])
i <<= 8
i |= int64(b[2])
i <<= 8
i |= int64(b[3])
i <<= 8
i |= int64(b[4])
i <<= 8
i |= int64(b[5])
i <<= 8
i |= int64(b[6])
i <<= 8
i |= int64(b[7])
return
}

68
util/bits/pio/vec.go Normal file
View File

@@ -0,0 +1,68 @@
package pio
func VecLen(vec [][]byte) (n int) {
for _, b := range vec {
n += len(b)
}
return
}
func VecSliceTo(in [][]byte, out [][]byte, s int, e int) (n int) {
if s < 0 {
s = 0
}
if e >= 0 && e < s {
panic("pio: VecSlice start > end")
}
i := 0
off := 0
for s > 0 && i < len(in) {
left := len(in[i])
read := s
if left < read {
read = left
}
left -= read
off += read
s -= read
e -= read
if left == 0 {
i++
off = 0
}
}
if s > 0 {
panic("pio: VecSlice start out of range")
}
for e != 0 && i < len(in) {
left := len(in[i]) - off
read := left
if e > 0 && e < read {
read = e
}
out[n] = in[i][off : off+read]
n++
left -= read
e -= read
off += read
if left == 0 {
i++
off = 0
}
}
if e > 0 {
panic("pio: VecSlice end out of range")
}
return
}
func VecSlice(in [][]byte, s int, e int) (out [][]byte) {
out = make([][]byte, len(in))
n := VecSliceTo(in, out, s, e)
out = out[:n]
return
}

22
util/bits/pio/vec_test.go Normal file
View File

@@ -0,0 +1,22 @@
package pio
import (
"fmt"
"testing"
)
func TestExampleVec(t *testing.T) {
vec := [][]byte{[]byte{1, 2, 3}, []byte{4, 5, 6, 7, 8, 9}, []byte{10, 11, 12, 13}}
println(VecLen(vec))
vec = VecSlice(vec, 1, -1)
fmt.Println(vec)
vec = VecSlice(vec, 2, -1)
fmt.Println(vec)
vec = VecSlice(vec, 8, 8)
fmt.Println(vec)
// Output:
}

87
util/bits/pio/writer.go Normal file
View File

@@ -0,0 +1,87 @@
package pio
func PutU8(b []byte, v uint8) {
b[0] = v
}
func PutI16BE(b []byte, v int16) {
b[0] = byte(v >> 8)
b[1] = byte(v)
}
func PutU16BE(b []byte, v uint16) {
b[0] = byte(v >> 8)
b[1] = byte(v)
}
func PutI24BE(b []byte, v int32) {
b[0] = byte(v >> 16)
b[1] = byte(v >> 8)
b[2] = byte(v)
}
func PutU24BE(b []byte, v uint32) {
b[0] = byte(v >> 16)
b[1] = byte(v >> 8)
b[2] = byte(v)
}
func PutI32BE(b []byte, v int32) {
b[0] = byte(v >> 24)
b[1] = byte(v >> 16)
b[2] = byte(v >> 8)
b[3] = byte(v)
}
func PutU32BE(b []byte, v uint32) {
b[0] = byte(v >> 24)
b[1] = byte(v >> 16)
b[2] = byte(v >> 8)
b[3] = byte(v)
}
func PutU32LE(b []byte, v uint32) {
b[3] = byte(v >> 24)
b[2] = byte(v >> 16)
b[1] = byte(v >> 8)
b[0] = byte(v)
}
func PutU40BE(b []byte, v uint64) {
b[0] = byte(v >> 32)
b[1] = byte(v >> 24)
b[2] = byte(v >> 16)
b[3] = byte(v >> 8)
b[4] = byte(v)
}
func PutU48BE(b []byte, v uint64) {
b[0] = byte(v >> 40)
b[1] = byte(v >> 32)
b[2] = byte(v >> 24)
b[3] = byte(v >> 16)
b[4] = byte(v >> 8)
b[5] = byte(v)
}
func PutU64BE(b []byte, v uint64) {
b[0] = byte(v >> 56)
b[1] = byte(v >> 48)
b[2] = byte(v >> 40)
b[3] = byte(v >> 32)
b[4] = byte(v >> 24)
b[5] = byte(v >> 16)
b[6] = byte(v >> 8)
b[7] = byte(v)
}
func PutI64BE(b []byte, v int64) {
b[0] = byte(v >> 56)
b[1] = byte(v >> 48)
b[2] = byte(v >> 40)
b[3] = byte(v >> 32)
b[4] = byte(v >> 24)
b[5] = byte(v >> 16)
b[6] = byte(v >> 8)
b[7] = byte(v)
}

30
util/buffer.go Normal file
View File

@@ -0,0 +1,30 @@
package util
type Buffer []byte
func (b *Buffer) Write(a []byte) (n int, err error) {
*b = append(*b, a...)
return len(a), nil
}
func (b Buffer) Len() int {
return len(b)
}
func (b Buffer) Cap() int {
return cap(b)
}
func (b Buffer) SubBuf(start int, length int) Buffer {
return b[start : start+length]
}
func (b *Buffer) Malloc(count int) Buffer {
l := b.Len()
if l+count > b.Cap() {
n := make(Buffer, l+count)
copy(n, *b)
*b = n
}
return b.SubBuf(l, count)
}
func (b *Buffer) Reset() {
*b = b.SubBuf(0, 0)
}

18
util/buffer_test.go Normal file
View File

@@ -0,0 +1,18 @@
package util
import (
"testing"
)
func TestBuffer(t *testing.T) {
t.Run(t.Name(), func(t *testing.T) {
var b Buffer
t.Log(b == nil)
b.Write([]byte{1, 2, 3})
if b == nil {
t.Fail()
} else {
t.Logf("b:% x", b)
}
})
}

17
util/bytes_pool.go Normal file
View File

@@ -0,0 +1,17 @@
package util
type BytesPool [][]byte
func (pool *BytesPool) Get(size int) (result []byte) {
if l := len(*pool); l > 0 {
result = (*pool)[l-1]
*pool = (*pool)[:l-1]
} else {
result = make([]byte, size, 10)
}
return
}
func (pool *BytesPool) Put(b []byte) {
*pool = append(*pool, b)
}

5
util/index.go Normal file
View File

@@ -0,0 +1,5 @@
package util
func Clone[T any](x T) *T {
return &x
}

87
util/logger.go Normal file
View File

@@ -0,0 +1,87 @@
package util
import (
"context"
"fmt"
"io"
"log"
"os"
"time"
colorable "github.com/mattn/go-colorable"
"github.com/logrusorgru/aurora"
)
// MultiLogWriter 多端写日志类
type MultiLogWriter struct {
writers []io.Writer
io.Writer
}
var logWriter MultiLogWriter
var multiLogger = log.New(&logWriter, "", log.LstdFlags)
var colorLogger = log.New(colorable.NewColorableStdout(), "", log.LstdFlags)
func init() {
log.SetOutput(io.MultiWriter(os.Stdout, &logWriter))
logWriter.Writer = io.MultiWriter()
}
// AddWriter 添加日志输出端
func AddWriter(wn io.Writer) {
logWriter.writers = append(logWriter.writers, wn)
logWriter.Writer = io.MultiWriter(logWriter.writers...)
}
// MayBeError 优雅错误判断加日志辅助函数
func MayBeError(info error) (hasError bool) {
if hasError = info != nil; hasError {
Print(aurora.Red(info))
}
return
}
func getNoColor(v ...interface{}) (noColor []interface{}) {
noColor = append(noColor, v...)
for i, value := range v {
if vv, ok := value.(aurora.Value); ok {
noColor[i] = vv.Value()
}
}
return
}
// Print 带颜色识别
func Print(v ...interface{}) {
noColor := getNoColor(v...)
colorLogger.Output(2, fmt.Sprint(v...))
multiLogger.Output(2, fmt.Sprint(noColor...))
}
// Printf calls Output to print to the standard logger.
// Arguments are handled in the manner of fmt.Printf.
func Printf(format string, v ...interface{}) {
noColor := getNoColor(v...)
colorLogger.Output(2, fmt.Sprintf(format, v...))
multiLogger.Output(2, fmt.Sprintf(format, noColor...))
}
// Println calls Output to print to the standard logger.
// Arguments are handled in the manner of fmt.Println.
func Println(v ...interface{}) {
noColor := getNoColor(v...)
colorLogger.Output(2, fmt.Sprintln(v...))
multiLogger.Output(2, fmt.Sprintln(noColor...))
}
type Event struct {
Timestamp time.Time
Level int
Label string
Tag string
}
type EventContext struct {
Name string
context.Context
EventChan chan *Event
}

70
util/map.go Normal file
View File

@@ -0,0 +1,70 @@
package util
import "sync"
type Map[K comparable, V any] struct {
sync.RWMutex
Map map[K]V
}
func (m *Map[K, V]) Init() {
m.Map = make(map[K]V)
}
func (m *Map[K, V]) Add(k K, v V) bool {
m.Lock()
defer m.Unlock()
if _, ok := m.Map[k]; ok {
return false
}
m.Map[k] = v
return true
}
func (m *Map[K, V]) Set(k K, v V) {
m.Lock()
m.Map[k] = v
m.Unlock()
}
func (m *Map[K, V]) Has(k K) (ok bool) {
m.RLock()
defer m.RUnlock()
_, ok = m.Map[k]
return
}
func (m *Map[K, V]) Len() int {
m.RLock()
defer m.RUnlock()
return len(m.Map)
}
func (m *Map[K, V]) Get(k K) V {
m.RLock()
defer m.RUnlock()
return m.Map[k]
}
func (m *Map[K, V]) Delete(k K) {
m.Lock()
delete(m.Map, k)
m.Unlock()
}
func (m *Map[K, V]) ToList() (r []V) {
m.RLock()
defer m.RUnlock()
for _, s := range m.Map {
r = append(r, s)
}
return
}
func (m *Map[K, V]) Range(f func(V)) {
m.RLock()
defer m.RUnlock()
for _, s := range m.Map {
f(s)
}
}

136
util/ring.go Normal file
View File

@@ -0,0 +1,136 @@
package util
// A Ring is an element of a circular list, or ring.
// Rings do not have a beginning or end; a pointer to any ring element
// serves as reference to the entire ring. Empty rings are represented
// as nil Ring pointers. The zero value for a Ring is a one-element
// ring with a nil Value.
//
type Ring[T any] struct {
next, prev *Ring[T]
Value T // for use by client; untouched by this library
}
func (r *Ring[T]) init() *Ring[T] {
r.next = r
r.prev = r
return r
}
// Next returns the next ring element. r must not be empty.
func (r *Ring[T]) Next() *Ring[T] {
if r.next == nil {
return r.init()
}
return r.next
}
// Prev returns the previous ring element. r must not be empty.
func (r *Ring[T]) Prev() *Ring[T] {
if r.next == nil {
return r.init()
}
return r.prev
}
// Move moves n % r.Len() elements backward (n < 0) or forward (n >= 0)
// in the ring and returns that ring element. r must not be empty.
//
func (r *Ring[T]) Move(n int) *Ring[T] {
if r.next == nil {
return r.init()
}
switch {
case n < 0:
for ; n < 0; n++ {
r = r.prev
}
case n > 0:
for ; n > 0; n-- {
r = r.next
}
}
return r
}
// New creates a ring of n elements.
func NewRing[T any](n int) *Ring[T] {
if n <= 0 {
return nil
}
r := new(Ring[T])
p := r
for i := 1; i < n; i++ {
p.next = &Ring[T]{prev: p}
p = p.next
}
p.next = r
r.prev = p
return r
}
// Link connects ring r with ring s such that r.Next()
// becomes s and returns the original value for r.Next().
// r must not be empty.
//
// If r and s point to the same ring, linking
// them removes the elements between r and s from the ring.
// The removed elements form a subring and the result is a
// reference to that subring (if no elements were removed,
// the result is still the original value for r.Next(),
// and not nil).
//
// If r and s point to different rings, linking
// them creates a single ring with the elements of s inserted
// after r. The result points to the element following the
// last element of s after insertion.
//
func (r *Ring[T]) Link(s *Ring[T]) *Ring[T] {
n := r.Next()
if s != nil {
p := s.Prev()
// Note: Cannot use multiple assignment because
// evaluation order of LHS is not specified.
r.next = s
s.prev = r
n.prev = p
p.next = n
}
return n
}
// Unlink removes n % r.Len() elements from the ring r, starting
// at r.Next(). If n % r.Len() == 0, r remains unchanged.
// The result is the removed subring. r must not be empty.
//
func (r *Ring[T]) Unlink(n int) *Ring[T] {
if n <= 0 {
return nil
}
return r.Link(r.Move(n + 1))
}
// Len computes the number of elements in ring r.
// It executes in time proportional to the number of elements.
//
func (r *Ring[T]) Len() int {
n := 0
if r != nil {
n = 1
for p := r.Next(); p != r; p = p.next {
n++
}
}
return n
}
// Do calls function f on each element of the ring, in forward order.
// The behavior of Do is undefined if f changes *r.
func (r *Ring[T]) Do(f func(T)) {
if r != nil {
f(r.Value)
for p := r.Next(); p != r; p = p.next {
f(p.Value)
}
}
}

21
util/slice.go Normal file
View File

@@ -0,0 +1,21 @@
package util
type Slice[T comparable] []T
func (s Slice[T]) Len() int {
return len(s)
}
func (s *Slice[T]) Add(v T) {
*s = append(*s, v)
}
func (s *Slice[T]) Delete(v T) bool {
for i, val := range *s {
if val == v {
*s = append((*s)[:i], (*s)[i+1:]...)
return true
}
}
return false
}

92
util/socket.go Normal file
View File

@@ -0,0 +1,92 @@
package util
import (
"log"
"net"
"net/http"
"os"
"time"
"golang.org/x/sync/errgroup"
)
// ListenAddrs Listen http and https
func ListenAddrs(addr, addTLS, cert, key string, handler http.Handler) {
var g errgroup.Group
if addTLS != "" {
g.Go(func() error {
return http.ListenAndServeTLS(addTLS, cert, key, handler)
})
}
if addr != "" {
g.Go(func() error { return http.ListenAndServe(addr, handler) })
}
if err := g.Wait(); err != nil {
log.Fatal(err)
}
}
func ListenTCP(addr string, process func(net.Conn)) error {
listener, err := net.Listen("tcp", addr)
if err != nil {
return err
}
var tempDelay time.Duration
for {
conn, err := listener.Accept()
conn.(*net.TCPConn).SetNoDelay(false)
if err != nil {
if ne, ok := err.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
Printf("%s: Accept error: %v; retrying in %v", addr, err, tempDelay)
time.Sleep(tempDelay)
continue
}
return err
}
tempDelay = 0
go process(conn)
}
}
func ListenUDP(address string, networkBuffer int) (*net.UDPConn, error) {
addr, err := net.ResolveUDPAddr("udp", address)
if err != nil {
log.Fatalf("udp server ResolveUDPAddr :%s error, %v", address, err)
}
conn, err := net.ListenUDP("udp", addr)
if err != nil {
log.Fatalf("udp server ListenUDP :%s error, %v", address, err)
}
if err = conn.SetReadBuffer(networkBuffer); err != nil {
Printf("udp server video conn set read buffer error, %v", err)
}
if err = conn.SetWriteBuffer(networkBuffer); err != nil {
Printf("udp server video conn set write buffer error, %v", err)
}
return conn, err
}
func CORS(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Credentials", "true")
origin := r.Header["Origin"]
if len(origin) == 0 {
w.Header().Set("Access-Control-Allow-Origin", "*")
} else {
w.Header().Set("Access-Control-Allow-Origin", origin[0])
}
}
// 检查文件或目录是否存在
// 如果由 filename 指定的文件或目录存在则返回 true否则返回 false
func Exist(filename string) bool {
_, err := os.Stat(filename)
return err == nil || os.IsExist(err)
}

70
util/sse.go Normal file
View File

@@ -0,0 +1,70 @@
package util
import (
"context"
"encoding/json"
"net/http"
"os/exec"
)
var (
sseEent = []byte("event: ")
sseBegin = []byte("data: ")
sseEnd = []byte("\n\n")
)
type SSE struct {
http.ResponseWriter
context.Context
}
func (sse *SSE) Write(data []byte) (n int, err error) {
if err = sse.Err(); err != nil {
return
}
_, err = sse.ResponseWriter.Write(sseBegin)
n, err = sse.ResponseWriter.Write(data)
_, err = sse.ResponseWriter.Write(sseEnd)
if err != nil {
return
}
sse.ResponseWriter.(http.Flusher).Flush()
return
}
func (sse *SSE) WriteEvent(event string, data []byte) (err error) {
if err = sse.Err(); err != nil {
return
}
_, err = sse.ResponseWriter.Write(sseEent)
_, err = sse.ResponseWriter.Write([]byte(event))
_, err = sse.ResponseWriter.Write([]byte("\n"))
_, err = sse.Write(data)
return
}
func NewSSE(w http.ResponseWriter, ctx context.Context) *SSE {
header := w.Header()
header.Set("Content-Type", "text/event-stream")
header.Set("Cache-Control", "no-cache")
header.Set("Connection", "keep-alive")
header.Set("X-Accel-Buffering", "no")
header.Set("Access-Control-Allow-Origin", "*")
return &SSE{
w,
ctx,
}
}
func (sse *SSE) WriteJSON(data interface{}) error {
jsonData, err := json.Marshal(data)
if err == nil {
_, err = sse.Write(jsonData)
}
return err
}
func (sse *SSE) WriteExec(cmd *exec.Cmd) error {
cmd.Stderr = sse
cmd.Stdout = sse
return cmd.Run()
}

View File

@@ -1,444 +0,0 @@
package engine
import (
"container/list"
"container/ring"
"encoding/binary"
"time"
"github.com/Monibuca/utils/v3"
"github.com/Monibuca/utils/v3/codec"
)
const (
naluTypeBitmask = 0b0001_1111
naluTypeBitmask_hevc = 0x7E
)
type VideoPack struct {
AVPack
CompositionTime uint32
NALUs [][]byte
IDR bool // 是否关键帧
}
func (v *VideoPack) ResetNALUs() {
if cap(v.NALUs) > 0 {
v.NALUs = v.NALUs[:0]
}
}
func (v *VideoPack) SetNalu0(nalu []byte) {
if cap(v.NALUs) > 0 {
v.NALUs = v.NALUs[:1]
v.NALUs[0] = nalu
} else {
v.NALUs = [][]byte{nalu}
}
}
type VideoTrack struct {
IDRing *ring.Ring `json:"-"` //最近的关键帧位置,首屏渲染
AVTrack
SPSInfo codec.SPSInfo
GOP int //关键帧间隔
ExtraData *VideoPack `json:"-"` //H264(SPS、PPS) H265(VPS、SPS、PPS)
WaitIDR chan struct{} `json:"-"`
revIDR func()
PushNalu func(ts uint32, cts uint32, nalus ...[]byte) `json:"-"`
UsingDonlField bool
writeByteStream func()
idrCount int //处于缓冲中的关键帧数量
nalulenSize int
*VideoPack `json:"-"` //当前写入的视频数据
keyFrameBuffers *list.List //用于作为关键帧缓存的对象池缓冲中每个节点都有buffer但是关键帧的长度较长会导致每个节点都可能增长空间
}
func (s *Stream) NewVideoTrack(codec byte) (vt *VideoTrack) {
vt = &VideoTrack{
WaitIDR: make(chan struct{}),
revIDR: func() {
vt.IDRing = vt.Ring
close(vt.WaitIDR)
idrSequence := vt.Sequence
vt.ts = vt.Timestamp
vt.idrCount++
vt.revIDR = func() {
vt.idrCount++
vt.GOP = vt.Sequence - idrSequence
if l := vt.Size - vt.GOP - 5; l > 5 {
vt.Size -= l
//缩小缓冲环节省内存
vt.Unlink(l).Do(func(v interface{}) {
if v.(*AVItem).Value.(*VideoPack).IDR {
// 将关键帧的缓存放入对象池
vt.keyFrameBuffers.PushBack(v.(*AVItem).Value)
vt.idrCount--
}
})
}
vt.IDRing = vt.Ring
idrSequence = vt.Sequence
vt.resetBPS()
}
},
keyFrameBuffers: list.New(),
}
vt.timebase = 90000
vt.PushNalu = vt.pushNalu
vt.Stream = s
vt.CodecID = codec
vt.Init(s.Context, 256)
vt.poll = time.Millisecond * 20
vt.Do(func(v interface{}) {
v.(*AVItem).Value = new(VideoPack)
})
vt.setCurrent()
return
}
func (vt *VideoTrack) PushAnnexB(ts uint32, cts uint32, payload []byte) {
vt.PushNalu(ts, cts, codec.SplitH264(payload)...)
}
func (vt *VideoTrack) pushNalu(ts uint32, cts uint32, nalus ...[]byte) {
idrBit := 0x10 | vt.CodecID
nIdrBit := 0x20 | vt.CodecID
tmp := make([]byte, 4)
// 缓冲中只包含Nalu数据所以写入rtmp格式时需要按照ByteStream格式写入
vt.writeByteStream = func() {
vt.Reset()
if vt.IDR {
tmp[0] = idrBit
} else {
tmp[0] = nIdrBit
}
tmp[1] = 1
vt.Buffer.Write(tmp[:2])
utils.BigEndian.PutUint24(tmp, vt.CompositionTime)
vt.Buffer.Write(tmp[:3])
for _, nalu := range vt.NALUs {
binary.Write(&vt.Buffer, binary.BigEndian, uint32(len(nalu)))
vt.Buffer.Write(nalu)
}
vt.Bytes2Payload()
}
switch vt.CodecID {
case codec.CodecID_H264:
{
var info codec.AVCDecoderConfigurationRecord
vt.PushNalu = func(ts uint32, cts uint32, nalus ...[]byte) {
// 等待接收SPS和PPS数据
for _, nalu := range nalus {
if len(nalu) == 0 {
continue
}
switch nalu[0] & naluTypeBitmask {
case codec.NALU_SPS:
info.SequenceParameterSetNALUnit = nalu
info.SequenceParameterSetLength = uint16(len(nalu))
vt.SPSInfo, _ = codec.ParseSPS(nalu)
case codec.NALU_PPS:
info.PictureParameterSetNALUnit = nalu
info.PictureParameterSetLength = uint16(len(nalu))
}
}
if info.SequenceParameterSetNALUnit != nil && info.PictureParameterSetNALUnit != nil {
vt.ExtraData = &VideoPack{
NALUs: [][]byte{info.SequenceParameterSetNALUnit, info.PictureParameterSetNALUnit},
}
vt.ExtraData.Payload = codec.BuildH264SeqHeaderFromSpsPps(info.SequenceParameterSetNALUnit, info.PictureParameterSetNALUnit)
}
if vt.ExtraData == nil {
return
}
vt.Stream.VideoTracks.AddTrack("h264", vt)
//已完成SPS和PPS 组装重置push函数接收视频数据
vt.PushNalu = func(ts uint32, cts uint32, nalus ...[]byte) {
var nonIDRs int
var IDRs int
for _, nalu := range nalus {
naluLen := len(nalu)
if naluLen == 0 {
continue
}
naluType := nalu[0] & naluTypeBitmask
switch naluType {
case codec.NALU_SPS:
vt.ExtraData.NALUs[0] = nalu
vt.SPSInfo, _ = codec.ParseSPS(nalu)
case codec.NALU_PPS:
vt.ExtraData.NALUs[1] = nalu
vt.ExtraData.Payload = codec.BuildH264SeqHeaderFromSpsPps(vt.ExtraData.NALUs[0], vt.ExtraData.NALUs[1])
case codec.NALU_Access_Unit_Delimiter:
case codec.NALU_IDR_Picture:
vt.addBytes(naluLen)
if IDRs == 0 {
vt.setIDR(true)
vt.SetNalu0(nalu)
} else {
vt.NALUs = append(vt.NALUs, nalu)
}
IDRs++
case codec.NALU_Non_IDR_Picture:
vt.addBytes(naluLen)
if nonIDRs == 0 {
vt.setIDR(false)
vt.SetNalu0(nalu)
} else {
vt.NALUs = append(vt.NALUs, nalu)
}
nonIDRs++
case codec.NALU_SEI:
case codec.NALU_Filler_Data:
default:
utils.Printf("%s,nalType not support yet:%d,[0]=0x%X", vt.Stream.StreamPath, naluType, nalu[0])
}
}
if nonIDRs + IDRs > 0 {
vt.setTS(ts)
vt.CompositionTime = cts
vt.push()
}
}
}
}
case codec.CodecID_H265:
var vps, sps, pps []byte
vt.PushNalu = func(ts uint32, cts uint32, nalus ...[]byte) {
// 等待接收SPS和PPS数据
for _, nalu := range nalus {
if len(nalu) == 0 {
continue
}
switch nalu[0] & naluTypeBitmask_hevc >> 1 {
case codec.NAL_UNIT_VPS:
vps = nalu
case codec.NAL_UNIT_SPS:
sps = nalu
vt.SPSInfo, _ = codec.ParseHevcSPS(nalu)
case codec.NAL_UNIT_PPS:
pps = nalu
}
}
if vps != nil && sps != nil && pps != nil {
extraData, err := codec.BuildH265SeqHeaderFromVpsSpsPps(vps, sps, pps)
if err != nil {
return
}
vt.ExtraData = &VideoPack{
NALUs: [][]byte{vps, sps, pps},
}
vt.ExtraData.Payload = extraData
}
if vt.ExtraData != nil {
vt.Stream.VideoTracks.AddTrack("h265", vt)
vt.PushNalu = func(ts uint32, cts uint32, nalus ...[]byte) {
var nonIDRs [][]byte
for _, nalu := range nalus {
naluLen := len(nalu)
if naluLen == 0 {
continue
}
/*
0 1
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|F| Type | LayerId | TID |
+-------------+-----------------+
Forbidden zero(F) : 1 bit
NAL unit type(Type) : 6 bits
NUH layer ID(LayerId) : 6 bits
NUH temporal ID plus 1 (TID) : 3 bits
*/
naluType := nalu[0] & naluTypeBitmask_hevc >> 1
switch naluType {
case codec.NAL_UNIT_VPS:
vps = nalu
vt.ExtraData.NALUs[0] = vps
case codec.NAL_UNIT_SPS:
sps = nalu
vt.ExtraData.NALUs[1] = sps
vt.SPSInfo, _ = codec.ParseHevcSPS(nalu)
case codec.NAL_UNIT_PPS:
pps = nalu
vt.ExtraData.NALUs[2] = pps
extraData, err := codec.BuildH265SeqHeaderFromVpsSpsPps(vps, sps, pps)
if err != nil {
return
}
vt.ExtraData.Payload = extraData
case codec.NAL_UNIT_CODED_SLICE_BLA,
codec.NAL_UNIT_CODED_SLICE_BLANT,
codec.NAL_UNIT_CODED_SLICE_BLA_N_LP,
codec.NAL_UNIT_CODED_SLICE_IDR,
codec.NAL_UNIT_CODED_SLICE_IDR_N_LP,
codec.NAL_UNIT_CODED_SLICE_CRA:
vt.setIDR(true)
vt.setTS(ts)
vt.CompositionTime = cts
vt.SetNalu0(nalu)
vt.addBytes(naluLen)
vt.push()
case 0, 1, 2, 3, 4, 5, 6, 7, 9:
nonIDRs = append(nonIDRs, nalu)
vt.addBytes(naluLen)
}
}
if len(nonIDRs) > 0 {
vt.setIDR(false)
vt.setTS(ts)
vt.CompositionTime = cts
vt.NALUs = nonIDRs
vt.push()
}
}
}
}
}
vt.PushNalu(ts, cts, nalus...)
}
func (vt *VideoTrack) setCurrent() {
vt.AVTrack.setCurrent()
vt.VideoPack = vt.Value.(*VideoPack)
}
func (vt *VideoTrack) PushByteStream(ts uint32, payload []byte) {
if payload[1] == 0 {
vt.CodecID = payload[0] & 0x0F
switch vt.CodecID {
case 7:
var info codec.AVCDecoderConfigurationRecord
if _, err := info.Unmarshal(payload[5:]); err == nil {
vt.SPSInfo, _ = codec.ParseSPS(info.SequenceParameterSetNALUnit)
vt.nalulenSize = int(info.LengthSizeMinusOne&3 + 1)
vt.ExtraData = &VideoPack{
NALUs: [][]byte{info.SequenceParameterSetNALUnit, info.PictureParameterSetNALUnit},
}
vt.ExtraData.Payload = payload
vt.Stream.VideoTracks.AddTrack("h264", vt)
}
case 12:
if vps, sps, pps, err := codec.ParseVpsSpsPpsFromSeqHeaderWithoutMalloc(payload); err == nil {
vt.SPSInfo, _ = codec.ParseSPS(sps)
vt.nalulenSize = int(payload[26]) & 0x03
vt.ExtraData = &VideoPack{
NALUs: [][]byte{vps, sps, pps},
}
vt.ExtraData.Payload = payload
vt.Stream.VideoTracks.AddTrack("h265", vt)
}
}
} else {
if len(payload) < 4 {
return
}
vt.addBytes(len(payload))
vt.IDR = payload[0]>>4 == 1
vt.setTS(ts)
vt.Payload = payload
vt.CompositionTime = utils.BigEndian.Uint24(payload[2:])
vt.ResetNALUs()
for nalus := payload[5:]; len(nalus) > vt.nalulenSize; {
nalulen := 0
for i := 0; i < vt.nalulenSize; i++ {
nalulen += int(nalus[i]) << ((vt.nalulenSize - i - 1) << 3)
}
if end := nalulen + vt.nalulenSize; len(nalus) >= end {
vt.NALUs = append(vt.NALUs, nalus[vt.nalulenSize:end])
nalus = nalus[end:]
} else {
utils.Printf("PushByteStream error,len %d,nalulenSize:%d,end:%d", len(nalus), vt.nalulenSize, end)
break
}
}
if len(vt.NALUs) > 0 {
vt.push()
}
}
}
// 设置关键帧信息,主要是为了判断缓存之前是否是关键帧,用来调度缓存
func (vt *VideoTrack) setIDR(idr bool) {
// 如果当前帧的类型和需要设置的类型相同,则不需要操作
if idr == vt.IDR {
return
}
// 原来是非关键帧,现在是关键帧,需要从关键帧池里面拿出一个缓存
if idr {
if cache := vt.keyFrameBuffers.Back(); cache != nil {
vt.AVItem.Value = vt.keyFrameBuffers.Remove(cache)
vt.VideoPack = vt.AVItem.Value.(*VideoPack) //设置当前操作的指针
}
} else { //原来是关键帧,现在是非关键帧,把原来的关键帧缓存放回去
vt.keyFrameBuffers.PushBack(vt.AVItem.Value)
vt.VideoPack = new(VideoPack) //设置当前操作的指针
vt.AVItem.Value = vt.VideoPack
}
vt.IDR = idr
}
func (vt *VideoTrack) push() {
if len(vt.NALUs) == 0 {
panic("push error,nalus is empty")
}
if vt.Stream != nil {
vt.Stream.Update()
}
if vt.writeByteStream != nil {
vt.writeByteStream()
}
if vt.GetBPS(); vt.IDR {
vt.revIDR()
}
if nextPack := vt.NextValue().(*VideoPack); nextPack.IDR {
if vt.idrCount == 1 {
if vt.Size < config.MaxRingSize {
exRing := ring.New(5)
for x := exRing; x.Value == nil; x = x.Next() {
x.Value = &AVItem{DataItem: DataItem{Value: new(VideoPack)}}
}
vt.Link(exRing) // 扩大缓冲环
}
} else {
vt.idrCount--
}
}
vt.Step()
vt.setCurrent()
}
func (vt *VideoTrack) Play(onVideo func(uint32, *VideoPack), exit1, exit2 <-chan struct{}) {
select {
case <-vt.WaitIDR:
case <-exit1:
return
case <-exit2: //可能等不到关键帧就退出了
return
}
vr := vt.SubRing(vt.IDRing) //从关键帧开始读取,首屏秒开
realSt := vt.PreItem().Timestamp // 当前时间戳
item, vp := vr.Read()
startTimestamp := item.Timestamp
for chase := true; ; item, vp = vr.Read() {
select {
case <-exit1:
return
case <-exit2:
return
default:
onVideo(uint32(item.Timestamp.Sub(startTimestamp).Milliseconds()), vp.(*VideoPack))
if chase {
add10 := startTimestamp.Add(time.Millisecond * 10)
if realSt.After(add10) {
startTimestamp = add10
} else {
startTimestamp = realSt
chase = false
}
}
vr.MoveNext()
}
}
}

View File

@@ -1,20 +0,0 @@
package engine
import (
"encoding/json"
"testing"
)
func TestJSON(t *testing.T) {
t.Run(t.Name(), func(t *testing.T) {
var s Stream
s.StreamPath = "test"
s.Publish()
s.NewVideoTrack(7)
bytes, err := json.Marshal(&s)
if err == nil {
str := string(bytes)
t.Logf("%s", str)
}
})
}