mirror of
https://github.com/lkmio/lkm.git
synced 2025-10-22 22:59:26 +08:00
fix: 错误判断合并写是否包含关键帧的问题
This commit is contained in:
@@ -33,6 +33,7 @@ func (t *TransStream) Input(packet utils.AVPacket) ([][]byte, int64, bool, error
|
|||||||
var videoKey bool
|
var videoKey bool
|
||||||
var dts int64
|
var dts int64
|
||||||
var pts int64
|
var pts int64
|
||||||
|
var keyBuffer bool
|
||||||
|
|
||||||
dts = packet.ConvertDts(1000)
|
dts = packet.ConvertDts(1000)
|
||||||
pts = packet.ConvertPts(1000)
|
pts = packet.ConvertPts(1000)
|
||||||
@@ -48,8 +49,9 @@ func (t *TransStream) Input(packet utils.AVPacket) ([][]byte, int64, bool, error
|
|||||||
|
|
||||||
// 关键帧都放在切片头部,所以遇到关键帧创建新切片, 发送当前切片剩余流
|
// 关键帧都放在切片头部,所以遇到关键帧创建新切片, 发送当前切片剩余流
|
||||||
if videoKey && !t.MWBuffer.IsNewSegment() {
|
if videoKey && !t.MWBuffer.IsNewSegment() {
|
||||||
segment := t.forceFlushSegment()
|
segment, key := t.forceFlushSegment()
|
||||||
t.AppendOutStreamBuffer(segment)
|
t.AppendOutStreamBuffer(segment)
|
||||||
|
keyBuffer = key
|
||||||
}
|
}
|
||||||
|
|
||||||
var n int
|
var n int
|
||||||
@@ -73,12 +75,13 @@ func (t *TransStream) Input(packet utils.AVPacket) ([][]byte, int64, bool, error
|
|||||||
copy(bytes[n:], data)
|
copy(bytes[n:], data)
|
||||||
|
|
||||||
// 合并写满再发
|
// 合并写满再发
|
||||||
if segment := t.MWBuffer.PeekCompletedSegment(); len(segment) > 0 {
|
if segment, key := t.MWBuffer.PeekCompletedSegment(); len(segment) > 0 {
|
||||||
// 已经分配末尾换行符内存
|
// 已经分配末尾换行符内存
|
||||||
|
keyBuffer = key
|
||||||
t.AppendOutStreamBuffer(t.FormatSegment(segment))
|
t.AppendOutStreamBuffer(t.FormatSegment(segment))
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.OutBuffer[:t.OutBufferSize], 0, true, nil
|
return t.OutBuffer[:t.OutBufferSize], 0, keyBuffer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStream) AddTrack(track *stream.Track) error {
|
func (t *TransStream) AddTrack(track *stream.Track) error {
|
||||||
@@ -91,8 +94,8 @@ func (t *TransStream) AddTrack(track *stream.Track) error {
|
|||||||
} else if utils.AVMediaTypeVideo == track.Stream.Type() {
|
} else if utils.AVMediaTypeVideo == track.Stream.Type() {
|
||||||
t.muxer.AddVideoTrack(track.Stream.CodecId())
|
t.muxer.AddVideoTrack(track.Stream.CodecId())
|
||||||
|
|
||||||
t.muxer.AddProperty("width", track.Stream.CodecParameters().Width())
|
t.muxer.MetaData().AddNumberProperty("width", float64(track.Stream.CodecParameters().Width()))
|
||||||
t.muxer.AddProperty("height", track.Stream.CodecParameters().Height())
|
t.muxer.MetaData().AddNumberProperty("height", float64(track.Stream.CodecParameters().Height()))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -157,7 +160,7 @@ func (t *TransStream) Close() ([][]byte, int64, error) {
|
|||||||
|
|
||||||
// 发送剩余的流
|
// 发送剩余的流
|
||||||
if !t.MWBuffer.IsNewSegment() {
|
if !t.MWBuffer.IsNewSegment() {
|
||||||
if segment := t.forceFlushSegment(); len(segment) > 0 {
|
if segment, _ := t.forceFlushSegment(); len(segment) > 0 {
|
||||||
t.AppendOutStreamBuffer(segment)
|
t.AppendOutStreamBuffer(segment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -166,16 +169,16 @@ func (t *TransStream) Close() ([][]byte, int64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 保存为完整的http-flv切片
|
// 保存为完整的http-flv切片
|
||||||
func (t *TransStream) forceFlushSegment() []byte {
|
func (t *TransStream) forceFlushSegment() ([]byte, bool) {
|
||||||
// 预览末尾换行符
|
// 预览末尾换行符
|
||||||
t.MWBuffer.Reserve(2)
|
t.MWBuffer.Reserve(2)
|
||||||
segment := t.MWBuffer.FlushSegment()
|
segment, key := t.MWBuffer.FlushSegment()
|
||||||
return t.FormatSegment(segment)
|
return t.FormatSegment(segment), key
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetHttpFLVBlock 跳过头部的无效数据,返回http-flv块
|
// GetHttpFLVBlock 跳过头部的无效数据,返回http-flv块
|
||||||
func (t *TransStream) GetHttpFLVBlock(data []byte) []byte {
|
func (t *TransStream) GetHttpFLVBlock(data []byte) []byte {
|
||||||
return data[t.computeSkipCount(data):]
|
return data[t.computeSkipBytesSize(data):]
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatSegment 为切片添加包长和换行符
|
// FormatSegment 为切片添加包长和换行符
|
||||||
@@ -184,7 +187,7 @@ func (t *TransStream) FormatSegment(segment []byte) []byte {
|
|||||||
return t.GetHttpFLVBlock(segment)
|
return t.GetHttpFLVBlock(segment)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *TransStream) computeSkipCount(data []byte) int {
|
func (t *TransStream) computeSkipBytesSize(data []byte) int {
|
||||||
return int(6 + binary.BigEndian.Uint16(data[4:]))
|
return int(6 + binary.BigEndian.Uint16(data[4:]))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -220,7 +223,7 @@ func (t *TransStream) writeSeparator(dst []byte) {
|
|||||||
|
|
||||||
func NewHttpTransStream() stream.TransStream {
|
func NewHttpTransStream() stream.TransStream {
|
||||||
return &TransStream{
|
return &TransStream{
|
||||||
muxer: libflv.NewMuxer(),
|
muxer: libflv.NewMuxer(nil),
|
||||||
header: make([]byte, 1024),
|
header: make([]byte, 1024),
|
||||||
headerSize: HttpFlvBlockHeaderSize,
|
headerSize: HttpFlvBlockHeaderSize,
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@ func (s *Sink) StartStreaming(transStream stream.TransStream) error {
|
|||||||
|
|
||||||
// 创建PeerConnection
|
// 创建PeerConnection
|
||||||
var remoteTrack *webrtc.TrackLocalStaticSample
|
var remoteTrack *webrtc.TrackLocalStaticSample
|
||||||
s.tracks = make([]*webrtc.TrackLocalStaticSample, transStream.TrackCount())
|
s.tracks = make([]*webrtc.TrackLocalStaticSample, transStream.TrackSize())
|
||||||
|
|
||||||
connection, err := webrtcApi.NewPeerConnection(webrtc.Configuration{})
|
connection, err := webrtcApi.NewPeerConnection(webrtc.Configuration{})
|
||||||
connection.OnICECandidate(func(candidate *webrtc.ICECandidate) {
|
connection.OnICECandidate(func(candidate *webrtc.ICECandidate) {
|
||||||
|
@@ -34,6 +34,7 @@ func (t *transStream) Input(packet utils.AVPacket) ([][]byte, int64, bool, error
|
|||||||
var chunkPayloadOffset int
|
var chunkPayloadOffset int
|
||||||
var dts int64
|
var dts int64
|
||||||
var pts int64
|
var pts int64
|
||||||
|
var keyBuffer bool
|
||||||
|
|
||||||
dts = packet.ConvertDts(1000)
|
dts = packet.ConvertDts(1000)
|
||||||
pts = packet.ConvertPts(1000)
|
pts = packet.ConvertPts(1000)
|
||||||
@@ -55,15 +56,15 @@ func (t *transStream) Input(packet utils.AVPacket) ([][]byte, int64, bool, error
|
|||||||
|
|
||||||
// 遇到视频关键帧, 发送剩余的流, 创建新切片
|
// 遇到视频关键帧, 发送剩余的流, 创建新切片
|
||||||
if videoKey {
|
if videoKey {
|
||||||
if segment := t.MWBuffer.FlushSegment(); len(segment) > 0 {
|
if segment, key := t.MWBuffer.FlushSegment(); len(segment) > 0 {
|
||||||
|
keyBuffer = key
|
||||||
t.AppendOutStreamBuffer(segment)
|
t.AppendOutStreamBuffer(segment)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 分配内存
|
// type为0的header大小
|
||||||
// 固定type0
|
|
||||||
chunkHeaderSize := 12
|
chunkHeaderSize := 12
|
||||||
// type3chunk数量
|
// type为3的chunk数量
|
||||||
numChunks := (payloadSize - 1) / t.chunkSize
|
numChunks := (payloadSize - 1) / t.chunkSize
|
||||||
rtmpMsgSize := chunkHeaderSize + payloadSize + numChunks
|
rtmpMsgSize := chunkHeaderSize + payloadSize + numChunks
|
||||||
// 如果时间戳超过3字节, 每个chunk都需要多4字节的扩展时间戳
|
// 如果时间戳超过3字节, 每个chunk都需要多4字节的扩展时间戳
|
||||||
@@ -71,29 +72,32 @@ func (t *transStream) Input(packet utils.AVPacket) ([][]byte, int64, bool, error
|
|||||||
rtmpMsgSize += (1 + numChunks) * 4
|
rtmpMsgSize += (1 + numChunks) * 4
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 分配指定大小的内存
|
||||||
allocate := t.MWBuffer.Allocate(rtmpMsgSize, dts, videoKey)
|
allocate := t.MWBuffer.Allocate(rtmpMsgSize, dts, videoKey)
|
||||||
|
|
||||||
// 写chunk header
|
// 写第一个type为0的chunk header
|
||||||
chunk.Length = payloadSize
|
chunk.Length = payloadSize
|
||||||
chunk.Timestamp = uint32(dts)
|
chunk.Timestamp = uint32(dts)
|
||||||
n := chunk.MarshalHeader(allocate)
|
n := chunk.MarshalHeader(allocate)
|
||||||
|
|
||||||
// 写flv
|
// 封装成flv
|
||||||
if videoPkt {
|
if videoPkt {
|
||||||
n += t.muxer.WriteVideoData(allocate[n:], uint32(ct), packet.KeyFrame(), false)
|
n += t.muxer.WriteVideoData(allocate[n:], uint32(ct), packet.KeyFrame(), false)
|
||||||
} else {
|
} else {
|
||||||
n += t.muxer.WriteAudioData(allocate[n:], false)
|
n += t.muxer.WriteAudioData(allocate[n:], false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 将flv data写入chunk body
|
||||||
n += chunk.WriteBody(allocate[n:], data, t.chunkSize, chunkPayloadOffset)
|
n += chunk.WriteBody(allocate[n:], data, t.chunkSize, chunkPayloadOffset)
|
||||||
utils.Assert(len(allocate) == n)
|
utils.Assert(len(allocate) == n)
|
||||||
|
|
||||||
// 合并写满了再发
|
// 合并写满了再发
|
||||||
if segment := t.MWBuffer.PeekCompletedSegment(); len(segment) > 0 {
|
if segment, key := t.MWBuffer.PeekCompletedSegment(); len(segment) > 0 {
|
||||||
|
keyBuffer = key
|
||||||
t.AppendOutStreamBuffer(segment)
|
t.AppendOutStreamBuffer(segment)
|
||||||
}
|
}
|
||||||
|
|
||||||
return t.OutBuffer[:t.OutBufferSize], 0, true, nil
|
return t.OutBuffer[:t.OutBufferSize], 0, keyBuffer, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *transStream) ReadExtraData(_ int64) ([][]byte, int64, error) {
|
func (t *transStream) ReadExtraData(_ int64) ([][]byte, int64, error) {
|
||||||
|
@@ -26,12 +26,12 @@ type Sink struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sink) StartStreaming(transStream stream.TransStream) error {
|
func (s *Sink) StartStreaming(transStream stream.TransStream) error {
|
||||||
utils.Assert(transStream.TrackCount() > 0)
|
utils.Assert(transStream.TrackSize() > 0)
|
||||||
if s.senders != nil {
|
if s.senders != nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
s.senders = make([]*librtp.RtpSender, transStream.TrackCount())
|
s.senders = make([]*librtp.RtpSender, transStream.TrackSize())
|
||||||
// sdp回调给sink, sink应答给describe请求
|
// sdp回调给sink, sink应答给describe请求
|
||||||
if s.cb != nil {
|
if s.cb != nil {
|
||||||
s.cb(transStream.(*TransStream).sdp)
|
s.cb(transStream.(*TransStream).sdp)
|
||||||
|
@@ -11,21 +11,22 @@ import (
|
|||||||
type MergeWritingBuffer interface {
|
type MergeWritingBuffer interface {
|
||||||
Allocate(size int, ts int64, videoKey bool) []byte
|
Allocate(size int, ts int64, videoKey bool) []byte
|
||||||
|
|
||||||
// PeekCompletedSegment 返回当前完整切片, 如果不满, 返回nil.
|
// PeekCompletedSegment 返回当前完整切片, 以及是否是关键帧切片, 未满返回nil.
|
||||||
PeekCompletedSegment() []byte
|
PeekCompletedSegment() ([]byte, bool)
|
||||||
|
|
||||||
// FlushSegment 保存当前切片, 创建新的切片
|
// FlushSegment 生成并返回当前切片, 以及是否是关键帧切片.
|
||||||
FlushSegment() []byte
|
FlushSegment() ([]byte, bool)
|
||||||
|
|
||||||
|
// IsFull 当前切片已满
|
||||||
IsFull(ts int64) bool
|
IsFull(ts int64) bool
|
||||||
|
|
||||||
// IsNewSegment 新切片, 还未写数据
|
// IsNewSegment 当前切片是否还未写数据
|
||||||
IsNewSegment() bool
|
IsNewSegment() bool
|
||||||
|
|
||||||
// Reserve 从当前切片中预留指定长度数据
|
// Reserve 从当前切片中预留指定长度数据
|
||||||
Reserve(number int)
|
Reserve(length int)
|
||||||
|
|
||||||
// ReadSegmentsFromKeyFrameIndex 从最近的关键帧读取切片
|
// ReadSegmentsFromKeyFrameIndex 返回最近的关键帧切片
|
||||||
ReadSegmentsFromKeyFrameIndex(cb func([]byte))
|
ReadSegmentsFromKeyFrameIndex(cb func([]byte))
|
||||||
|
|
||||||
Capacity() int
|
Capacity() int
|
||||||
@@ -36,6 +37,7 @@ type mwBlock struct {
|
|||||||
keyVideo bool
|
keyVideo bool
|
||||||
buffer collections.MemoryPool
|
buffer collections.MemoryPool
|
||||||
completed bool
|
completed bool
|
||||||
|
Time int64
|
||||||
}
|
}
|
||||||
|
|
||||||
type mergeWritingBuffer struct {
|
type mergeWritingBuffer struct {
|
||||||
@@ -45,7 +47,7 @@ type mergeWritingBuffer struct {
|
|||||||
startTS int64 // 当前切片的开始时间
|
startTS int64 // 当前切片的开始时间
|
||||||
duration int // 当前切片时长
|
duration int // 当前切片时长
|
||||||
|
|
||||||
lastKeyFrameIndex int // 最新关键帧所在切片的索引
|
lastKeyFrameIndex int // 最近的关键帧所在切片的索引
|
||||||
keyFrameCount int // 关键帧计数
|
keyFrameCount int // 关键帧计数
|
||||||
existVideo bool // 是否存在视频
|
existVideo bool // 是否存在视频
|
||||||
|
|
||||||
@@ -55,9 +57,9 @@ type mergeWritingBuffer struct {
|
|||||||
|
|
||||||
func (m *mergeWritingBuffer) createMWBlock(videoKey bool) mwBlock {
|
func (m *mergeWritingBuffer) createMWBlock(videoKey bool) mwBlock {
|
||||||
if videoKey {
|
if videoKey {
|
||||||
return mwBlock{true, videoKey, collections.NewDirectMemoryPool(m.keyFrameBufferMaxLength), false}
|
return mwBlock{true, videoKey, collections.NewDirectMemoryPool(m.keyFrameBufferMaxLength), false, 0}
|
||||||
} else {
|
} else {
|
||||||
return mwBlock{true, false, collections.NewDirectMemoryPool(m.nonKeyFrameBufferMaxLength), false}
|
return mwBlock{true, false, collections.NewDirectMemoryPool(m.nonKeyFrameBufferMaxLength), false, 0}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -88,6 +90,7 @@ func (m *mergeWritingBuffer) Allocate(size int, ts int64, videoKey bool) []byte
|
|||||||
// 循环使用
|
// 循环使用
|
||||||
m.mwBlocks[m.index].buffer.Clear()
|
m.mwBlocks[m.index].buffer.Clear()
|
||||||
|
|
||||||
|
// 关键帧被覆盖, 减少计数
|
||||||
if m.mwBlocks[m.index].keyVideo {
|
if m.mwBlocks[m.index].keyVideo {
|
||||||
m.keyFrameCount--
|
m.keyFrameCount--
|
||||||
}
|
}
|
||||||
@@ -96,14 +99,15 @@ func (m *mergeWritingBuffer) Allocate(size int, ts int64, videoKey bool) []byte
|
|||||||
m.mwBlocks[m.index].free = false
|
m.mwBlocks[m.index].free = false
|
||||||
m.mwBlocks[m.index].completed = false
|
m.mwBlocks[m.index].completed = false
|
||||||
m.mwBlocks[m.index].keyVideo = videoKey
|
m.mwBlocks[m.index].keyVideo = videoKey
|
||||||
|
m.mwBlocks[m.index].Time = ts
|
||||||
}
|
}
|
||||||
|
|
||||||
if videoKey {
|
if videoKey {
|
||||||
// 请务必确保关键帧帧从新的切片开始
|
// 请务必确保关键帧帧从新的切片开始
|
||||||
// 外部遇到关键帧请先调用FlushSegment
|
// 外部遇到关键帧请先调用FlushSegment
|
||||||
utils.Assert(m.mwBlocks[m.index].buffer.IsEmpty())
|
utils.Assert(m.mwBlocks[m.index].buffer.IsEmpty())
|
||||||
m.lastKeyFrameIndex = m.index
|
//m.lastKeyFrameIndex = m.index
|
||||||
m.keyFrameCount++
|
//m.keyFrameCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
if ts < m.startTS {
|
if ts < m.startTS {
|
||||||
@@ -114,49 +118,61 @@ func (m *mergeWritingBuffer) Allocate(size int, ts int64, videoKey bool) []byte
|
|||||||
return m.mwBlocks[m.index].buffer.Allocate(size)
|
return m.mwBlocks[m.index].buffer.Allocate(size)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mergeWritingBuffer) FlushSegment() []byte {
|
func (m *mergeWritingBuffer) FlushSegment() ([]byte, bool) {
|
||||||
if !AppConfig.GOPCache || !m.existVideo {
|
if !AppConfig.GOPCache || !m.existVideo {
|
||||||
return nil
|
return nil, false
|
||||||
} else if m.mwBlocks[m.index].buffer == nil || m.mwBlocks[m.index].free {
|
} else if m.mwBlocks[m.index].buffer == nil || m.mwBlocks[m.index].free {
|
||||||
return nil
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
data, _ := m.mwBlocks[m.index].buffer.Data()
|
data, _ := m.mwBlocks[m.index].buffer.Data()
|
||||||
if len(data) == 0 {
|
if len(data) == 0 {
|
||||||
return nil
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
//更新缓冲长度
|
key := m.mwBlocks[m.index].keyVideo
|
||||||
|
if key {
|
||||||
|
m.lastKeyFrameIndex = m.index
|
||||||
|
m.keyFrameCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算最大切片数据长度,后续创建新切片按照最大长度分配内存空间
|
||||||
if m.lastKeyFrameIndex == m.index && m.keyFrameBufferMaxLength < len(data) {
|
if m.lastKeyFrameIndex == m.index && m.keyFrameBufferMaxLength < len(data) {
|
||||||
m.keyFrameBufferMaxLength = len(data) * 3 / 2
|
m.keyFrameBufferMaxLength = len(data) * 3 / 2
|
||||||
} else if m.lastKeyFrameIndex != m.index && m.nonKeyFrameBufferMaxLength < len(data) {
|
} else if m.lastKeyFrameIndex != m.index && m.nonKeyFrameBufferMaxLength < len(data) {
|
||||||
m.nonKeyFrameBufferMaxLength = len(data) * 3 / 2
|
m.nonKeyFrameBufferMaxLength = len(data) * 3 / 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置当前切片的完整性
|
||||||
|
m.mwBlocks[m.index].completed = true
|
||||||
|
|
||||||
|
// 分配下一个切片
|
||||||
capacity := cap(m.mwBlocks)
|
capacity := cap(m.mwBlocks)
|
||||||
if m.index+1 == capacity && m.keyFrameCount == 1 {
|
if m.index+1 == capacity && m.keyFrameCount == 1 {
|
||||||
m.grow()
|
m.grow()
|
||||||
|
capacity = cap(m.mwBlocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 计算下一个切片索引
|
||||||
m.index = (m.index + 1) % capacity
|
m.index = (m.index + 1) % capacity
|
||||||
m.mwBlocks[m.index].completed = true
|
|
||||||
|
|
||||||
|
// 清空下一个切片的标记
|
||||||
m.startTS = -1
|
m.startTS = -1
|
||||||
m.duration = 0
|
m.duration = 0
|
||||||
m.mwBlocks[m.index].free = true
|
m.mwBlocks[m.index].free = true
|
||||||
m.mwBlocks[m.index].completed = false
|
m.mwBlocks[m.index].completed = false
|
||||||
return data
|
return data, key
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mergeWritingBuffer) PeekCompletedSegment() []byte {
|
func (m *mergeWritingBuffer) PeekCompletedSegment() ([]byte, bool) {
|
||||||
if !AppConfig.GOPCache || !m.existVideo {
|
if !AppConfig.GOPCache || !m.existVideo {
|
||||||
data, _ := m.mwBlocks[0].buffer.Data()
|
data, _ := m.mwBlocks[0].buffer.Data()
|
||||||
m.mwBlocks[0].buffer.Clear()
|
m.mwBlocks[0].buffer.Clear()
|
||||||
return data
|
return data, false
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.duration < AppConfig.MergeWriteLatency {
|
if m.duration < AppConfig.MergeWriteLatency {
|
||||||
return nil
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return m.FlushSegment()
|
return m.FlushSegment()
|
||||||
@@ -174,10 +190,10 @@ func (m *mergeWritingBuffer) IsNewSegment() bool {
|
|||||||
return m.mwBlocks[m.index].buffer == nil || m.mwBlocks[m.index].free
|
return m.mwBlocks[m.index].buffer == nil || m.mwBlocks[m.index].free
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mergeWritingBuffer) Reserve(number int) {
|
func (m *mergeWritingBuffer) Reserve(length int) {
|
||||||
utils.Assert(m.mwBlocks[m.index].buffer != nil)
|
utils.Assert(m.mwBlocks[m.index].buffer != nil)
|
||||||
|
|
||||||
_ = m.mwBlocks[m.index].buffer.Allocate(number)
|
_ = m.mwBlocks[m.index].buffer.Allocate(length)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mergeWritingBuffer) ReadSegmentsFromKeyFrameIndex(cb func([]byte)) {
|
func (m *mergeWritingBuffer) ReadSegmentsFromKeyFrameIndex(cb func([]byte)) {
|
||||||
@@ -185,21 +201,28 @@ func (m *mergeWritingBuffer) ReadSegmentsFromKeyFrameIndex(cb func([]byte)) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := m.lastKeyFrameIndex; i < cap(m.mwBlocks); i++ {
|
ranges := [2][2]int{{-1, -1}, {-1, -1}}
|
||||||
|
if m.lastKeyFrameIndex <= m.index {
|
||||||
|
ranges[0][0] = m.lastKeyFrameIndex
|
||||||
|
ranges[0][1] = m.index + 1
|
||||||
|
} else {
|
||||||
|
// 回环, 先遍历后面和前面的数据
|
||||||
|
ranges[0][0] = m.lastKeyFrameIndex
|
||||||
|
ranges[0][1] = cap(m.mwBlocks)
|
||||||
|
|
||||||
|
ranges[1][0] = 0
|
||||||
|
ranges[1][1] = m.index + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, index := range ranges {
|
||||||
|
for i := index[0]; i > -1 && i < index[1]; i++ {
|
||||||
if m.mwBlocks[i].buffer == nil || !m.mwBlocks[i].completed {
|
if m.mwBlocks[i].buffer == nil || !m.mwBlocks[i].completed {
|
||||||
continue
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
data, _ := m.mwBlocks[i].buffer.Data()
|
data, _ := m.mwBlocks[i].buffer.Data()
|
||||||
cb(data)
|
cb(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
//回调循环使用的头部数据
|
|
||||||
if m.index < m.lastKeyFrameIndex {
|
|
||||||
for i := 0; i < m.index; i++ {
|
|
||||||
data, _ := m.mwBlocks[i].buffer.Data()
|
|
||||||
cb(data)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,7 +240,7 @@ func NewMergeWritingBuffer(existVideo bool) MergeWritingBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !existVideo || !AppConfig.GOPCache {
|
if !existVideo || !AppConfig.GOPCache {
|
||||||
blocks[0] = mwBlock{true, false, collections.NewDirectMemoryPool(1024 * 100), false}
|
blocks[0] = mwBlock{true, false, collections.NewDirectMemoryPool(1024 * 100), false, 0}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &mergeWritingBuffer{
|
return &mergeWritingBuffer{
|
||||||
|
@@ -300,7 +300,9 @@ func IsSupportMux(protocol TransStreamProtocol, audioCodecId, videoCodecId utils
|
|||||||
func (s *PublishSource) CreateTransStream(id TransStreamID, protocol TransStreamProtocol, tracks []*Track) (TransStream, error) {
|
func (s *PublishSource) CreateTransStream(id TransStreamID, protocol TransStreamProtocol, tracks []*Track) (TransStream, error) {
|
||||||
log.Sugar.Infof("创建%s-stream source: %s", protocol.String(), s.ID)
|
log.Sugar.Infof("创建%s-stream source: %s", protocol.String(), s.ID)
|
||||||
|
|
||||||
transStream, err := CreateTransStream(s, protocol, tracks)
|
source := SourceManager.Find(s.ID)
|
||||||
|
utils.Assert(source != nil)
|
||||||
|
transStream, err := CreateTransStream(source, protocol, tracks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Sugar.Errorf("创建传输流失败 err: %s source: %s", err.Error(), s.ID)
|
log.Sugar.Errorf("创建传输流失败 err: %s source: %s", err.Error(), s.ID)
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -313,7 +315,7 @@ func (s *PublishSource) CreateTransStream(id TransStreamID, protocol TransStream
|
|||||||
}
|
}
|
||||||
|
|
||||||
transStream.SetID(id)
|
transStream.SetID(id)
|
||||||
transStream.SetTransStreamProtocol(protocol)
|
transStream.SetProtocol(protocol)
|
||||||
|
|
||||||
// 创建输出流对应的拉流队列
|
// 创建输出流对应的拉流队列
|
||||||
s.TransStreamSinks[id] = make(map[SinkID]Sink, 128)
|
s.TransStreamSinks[id] = make(map[SinkID]Sink, 128)
|
||||||
@@ -461,6 +463,15 @@ func (s *PublishSource) doAddSink(sink Sink) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 累加拉流计数
|
||||||
|
if s.recordSink != sink {
|
||||||
|
s.sinkCount++
|
||||||
|
log.Sugar.Infof("sink count: %d source: %s", s.sinkCount, s.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
s.sinks[sink.GetID()] = sink
|
||||||
|
s.TransStreamSinks[transStreamId][sink.GetID()] = sink
|
||||||
|
|
||||||
// TCP拉流开启异步发包, 一旦出现网络不好的链路, 其余正常链路不受影响.
|
// TCP拉流开启异步发包, 一旦出现网络不好的链路, 其余正常链路不受影响.
|
||||||
conn, ok := sink.GetConn().(*transport.Conn)
|
conn, ok := sink.GetConn().(*transport.Conn)
|
||||||
if ok && sink.IsTCPStreaming() && transStream.OutStreamBufferCapacity() > 2 {
|
if ok && sink.IsTCPStreaming() && transStream.OutStreamBufferCapacity() > 2 {
|
||||||
@@ -478,15 +489,6 @@ func (s *PublishSource) doAddSink(sink Sink) bool {
|
|||||||
s.write(sink, 0, data, timestamp)
|
s.write(sink, 0, data, timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 累加拉流计数
|
|
||||||
if s.recordSink != sink {
|
|
||||||
s.sinkCount++
|
|
||||||
log.Sugar.Infof("sink count: %d source: %s", s.sinkCount, s.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
s.sinks[sink.GetID()] = sink
|
|
||||||
s.TransStreamSinks[transStreamId][sink.GetID()] = sink
|
|
||||||
|
|
||||||
// 新建传输流,发送已经缓存的音视频帧
|
// 新建传输流,发送已经缓存的音视频帧
|
||||||
if !exist && AppConfig.GOPCache && s.existVideo {
|
if !exist && AppConfig.GOPCache && s.existVideo {
|
||||||
s.DispatchGOPBuffer(transStream)
|
s.DispatchGOPBuffer(transStream)
|
||||||
|
@@ -10,37 +10,42 @@ type TransStream interface {
|
|||||||
|
|
||||||
SetID(id TransStreamID)
|
SetID(id TransStreamID)
|
||||||
|
|
||||||
|
// Input 封装传输流, 返回合并写块、时间戳、合并写块是否包含视频关键帧
|
||||||
Input(packet utils.AVPacket) ([][]byte, int64, bool, error)
|
Input(packet utils.AVPacket) ([][]byte, int64, bool, error)
|
||||||
|
|
||||||
AddTrack(track *Track) error
|
AddTrack(track *Track) error
|
||||||
|
|
||||||
TrackCount() int
|
TrackSize() int
|
||||||
|
|
||||||
GetTracks() []*Track
|
GetTracks() []*Track
|
||||||
|
|
||||||
|
// WriteHeader track添加完毕, 通过调用此函数告知
|
||||||
WriteHeader() error
|
WriteHeader() error
|
||||||
|
|
||||||
// GetProtocol 返回输出流协议
|
// GetProtocol 返回输出流协议
|
||||||
GetProtocol() TransStreamProtocol
|
GetProtocol() TransStreamProtocol
|
||||||
|
|
||||||
// ReadExtraData 获取封装后的编码器扩展数据
|
SetProtocol(protocol TransStreamProtocol)
|
||||||
|
|
||||||
|
// ReadExtraData 读取传输流的编码器扩展数据
|
||||||
ReadExtraData(timestamp int64) ([][]byte, int64, error)
|
ReadExtraData(timestamp int64) ([][]byte, int64, error)
|
||||||
|
|
||||||
// ReadKeyFrameBuffer 读取已经缓存的包含关键视频帧的输出流
|
// ReadKeyFrameBuffer 读取最近的包含视频关键帧的合并写队列
|
||||||
ReadKeyFrameBuffer() ([][]byte, int64, error)
|
ReadKeyFrameBuffer() ([][]byte, int64, error)
|
||||||
|
|
||||||
|
// Close 关闭传输流, 返回还未flush的合并写块
|
||||||
Close() ([][]byte, int64, error)
|
Close() ([][]byte, int64, error)
|
||||||
|
|
||||||
|
// ClearOutStreamBuffer 清空传输流的合并写块队列
|
||||||
ClearOutStreamBuffer()
|
ClearOutStreamBuffer()
|
||||||
|
|
||||||
|
// AppendOutStreamBuffer 添加合并写块到队列
|
||||||
AppendOutStreamBuffer(buffer []byte)
|
AppendOutStreamBuffer(buffer []byte)
|
||||||
|
|
||||||
// OutStreamBufferCapacity 返回输出流缓冲区的容量大小, 输出流缓冲区同时作为向sink推流的发送缓冲区, 容量大小决定向sink异步推流的队列大小;
|
// OutStreamBufferCapacity 返回合并写块队列容量大小, 作为sink异步推流的队列大小;
|
||||||
OutStreamBufferCapacity() int
|
OutStreamBufferCapacity() int
|
||||||
|
|
||||||
IsExistVideo() bool
|
IsExistVideo() bool
|
||||||
|
|
||||||
SetTransStreamProtocol(protocol TransStreamProtocol)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseTransStream struct {
|
type BaseTransStream struct {
|
||||||
@@ -50,8 +55,8 @@ type BaseTransStream struct {
|
|||||||
ExistVideo bool
|
ExistVideo bool
|
||||||
Protocol TransStreamProtocol
|
Protocol TransStreamProtocol
|
||||||
|
|
||||||
OutBuffer [][]byte // 输出流的返回队列
|
OutBuffer [][]byte // 传输流的合并写块队列
|
||||||
OutBufferSize int
|
OutBufferSize int // 传输流返合并写块队列大小
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *BaseTransStream) GetID() TransStreamID {
|
func (t *BaseTransStream) GetID() TransStreamID {
|
||||||
@@ -82,6 +87,10 @@ func (t *BaseTransStream) GetProtocol() TransStreamProtocol {
|
|||||||
return t.Protocol
|
return t.Protocol
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (t *BaseTransStream) SetProtocol(protocol TransStreamProtocol) {
|
||||||
|
t.Protocol = protocol
|
||||||
|
}
|
||||||
|
|
||||||
func (t *BaseTransStream) ClearOutStreamBuffer() {
|
func (t *BaseTransStream) ClearOutStreamBuffer() {
|
||||||
t.OutBufferSize = 0
|
t.OutBufferSize = 0
|
||||||
}
|
}
|
||||||
@@ -106,7 +115,7 @@ func (t *BaseTransStream) OutStreamBufferCapacity() int {
|
|||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *BaseTransStream) TrackCount() int {
|
func (t *BaseTransStream) TrackSize() int {
|
||||||
return len(t.Tracks)
|
return len(t.Tracks)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,10 +135,6 @@ func (t *BaseTransStream) ReadKeyFrameBuffer() ([][]byte, int64, error) {
|
|||||||
return nil, 0, nil
|
return nil, 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *BaseTransStream) SetTransStreamProtocol(protocol TransStreamProtocol) {
|
|
||||||
t.Protocol = protocol
|
|
||||||
}
|
|
||||||
|
|
||||||
type TCPTransStream struct {
|
type TCPTransStream struct {
|
||||||
BaseTransStream
|
BaseTransStream
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user