Files
engine/subscriber.go
2020-02-27 20:48:04 +08:00

136 lines
3.2 KiB
Go

package engine
import (
"context"
"fmt"
"github.com/Monibuca/engine/avformat"
"time"
)
type Subscriber interface {
Send(*avformat.SendPacket) error
}
type SubscriberInfo struct {
ID string
TotalDrop int //总丢帧
TotalPacket int
Type string
BufferLength int
SubscribeTime time.Time
}
type OutputStream struct {
context.Context
*Room
SubscriberInfo
SendHandler func(*avformat.SendPacket) error
Cancel context.CancelFunc
Sign string
VTSent bool
ATSent bool
VSentTime uint32
ASentTime uint32
packetQueue chan *avformat.SendPacket
dropCount int
OffsetTime uint32
firstScreenIndex int
}
func (s *OutputStream) IsClosed() bool {
return s.Context != nil && s.Err() != nil
}
func (s *OutputStream) Close() {
if s.Cancel != nil {
s.Cancel()
}
}
func (s *OutputStream) Play(streamPath string) (err error) {
AllRoom.Get(streamPath).Subscribe(s)
defer s.UnSubscribe(s)
for {
select {
case <-s.Done():
return s.Err()
case p := <-s.packetQueue:
if err = s.SendHandler(p); err != nil {
s.Cancel() //此处为了使得IsClosed 返回true
return
}
p.Recycle()
}
}
}
func (s *OutputStream) sendPacket(packet *avformat.AVPacket, timestamp uint32) {
if !packet.IsAVCSequence && timestamp == 0 {
timestamp = 1 //防止为0
}
s.TotalPacket++
s.BufferLength = len(s.packetQueue)
if s.dropCount > 0 {
if packet.IsKeyFrame() {
fmt.Printf("%s drop packet:%d\n", s.ID, s.dropCount)
s.dropCount = 0 //退出丢包
} else {
s.dropCount++
s.TotalDrop++
return
}
}
if s.BufferLength == cap(s.packetQueue) {
s.dropCount++
s.TotalDrop++
packet.Recycle()
} else if !s.IsClosed() {
s.packetQueue <- avformat.NewSendPacket(packet, timestamp)
}
}
func (s *OutputStream) sendVideo(video *avformat.AVPacket) error {
isKF := video.IsKeyFrame()
if s.VTSent {
if s.FirstScreen == nil || s.firstScreenIndex == -1 {
s.sendPacket(video, video.Timestamp-s.VSentTime+s.OffsetTime)
} else if !isKF && s.firstScreenIndex < len(s.FirstScreen) {
firstScreen := s.FirstScreen[s.firstScreenIndex]
firstScreen.RefCount++
s.VSentTime = firstScreen.Timestamp - s.FirstScreen[0].Timestamp
s.sendPacket(firstScreen, s.VSentTime)
video.Recycle() //回收当前数据
s.firstScreenIndex++
} else {
s.firstScreenIndex = -1 //收到关键帧或者首屏缓冲已播完后退出首屏渲染模式
s.OffsetTime += s.VSentTime
s.VSentTime = video.Timestamp
s.sendPacket(video, s.OffsetTime)
}
return nil
}
//非首屏渲染模式跳过开头的非关键帧
if !isKF {
if s.FirstScreen == nil {
return nil
}
} else if s.FirstScreen != nil {
s.firstScreenIndex = -1 //跳过首屏渲染
}
s.VTSent = true
s.sendPacket(s.VideoTag, 0)
s.VSentTime = video.Timestamp
return s.sendVideo(video)
}
func (s *OutputStream) sendAudio(audio *avformat.AVPacket) error {
if s.ATSent {
if s.FirstScreen != nil && s.firstScreenIndex == -1 {
audio.Recycle()
return nil
}
s.sendPacket(audio, audio.Timestamp-s.ASentTime)
return nil
}
s.ATSent = true
s.sendPacket(s.AudioTag, 0)
s.ASentTime = audio.Timestamp
return s.sendAudio(audio)
}