mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-10-21 15:49:41 +08:00
216 lines
5.2 KiB
Go
216 lines
5.2 KiB
Go
package m7s
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net/url"
|
|
"reflect"
|
|
"strconv"
|
|
"time"
|
|
|
|
. "m7s.live/m7s/v5/pkg"
|
|
"m7s.live/m7s/v5/pkg/config"
|
|
"m7s.live/m7s/v5/pkg/util"
|
|
)
|
|
|
|
type PubSubBase struct {
|
|
Unit
|
|
ID int
|
|
Plugin *Plugin
|
|
StartTime time.Time
|
|
StreamPath string
|
|
Args url.Values
|
|
TimeoutTimer *time.Timer
|
|
io.Closer
|
|
}
|
|
|
|
func (ps *PubSubBase) Stop(reason error) {
|
|
ps.Unit.Stop(reason)
|
|
if ps.Closer != nil {
|
|
ps.Closer.Close()
|
|
}
|
|
}
|
|
|
|
func (ps *PubSubBase) Init(p *Plugin, streamPath string, options ...any) {
|
|
ps.Plugin = p
|
|
ctx := p.Context
|
|
for _, option := range options {
|
|
switch v := option.(type) {
|
|
case context.Context:
|
|
ctx = v
|
|
case io.Closer:
|
|
ps.Closer = v
|
|
}
|
|
}
|
|
ps.Context, ps.CancelCauseFunc = context.WithCancelCause(ctx)
|
|
if u, err := url.Parse(streamPath); err == nil {
|
|
ps.StreamPath, ps.Args = u.Path, u.Query()
|
|
}
|
|
ps.StartTime = time.Now()
|
|
}
|
|
|
|
type UnsubscribeEvent struct {
|
|
*Subscriber
|
|
}
|
|
|
|
type Subscriber struct {
|
|
PubSubBase
|
|
config.Subscribe
|
|
Publisher *Publisher
|
|
}
|
|
|
|
type ISubscriberHandler[T IAVFrame] func(data T)
|
|
|
|
func (s *Subscriber) Handle(audioHandler, videoHandler any) {
|
|
var ar, vr *AVRingReader
|
|
var ah, vh reflect.Value
|
|
var a1, v1 reflect.Type
|
|
var initState = 0
|
|
var subMode = s.SubMode //订阅模式
|
|
if s.Args.Has(s.SubModeArgName) {
|
|
subMode, _ = strconv.Atoi(s.Args.Get(s.SubModeArgName))
|
|
}
|
|
var audioFrame, videoFrame, lastSentAF, lastSentVF *AVFrame
|
|
if audioHandler != nil && s.SubAudio {
|
|
a1 = reflect.TypeOf(audioHandler).In(0)
|
|
}
|
|
if videoHandler != nil && s.SubVideo {
|
|
v1 = reflect.TypeOf(videoHandler).In(0)
|
|
}
|
|
createAudioReader := func() {
|
|
if s.Publisher == nil || a1 == nil {
|
|
return
|
|
}
|
|
if at := s.Publisher.GetAudioTrack(a1); at != nil {
|
|
ar = NewAVRingReader(at)
|
|
ar.Logger = s.Logger.With("reader", a1.String())
|
|
ar.Info("start read")
|
|
ah = reflect.ValueOf(audioHandler)
|
|
}
|
|
}
|
|
createVideoReader := func() {
|
|
if s.Publisher == nil || v1 == nil {
|
|
return
|
|
}
|
|
if vt := s.Publisher.GetVideoTrack(v1); vt != nil {
|
|
vr = NewAVRingReader(vt)
|
|
vr.Logger = s.Logger.With("reader", v1.String())
|
|
vr.Info("start read")
|
|
vh = reflect.ValueOf(videoHandler)
|
|
}
|
|
}
|
|
createAudioReader()
|
|
createVideoReader()
|
|
defer func() {
|
|
if lastSentVF != nil {
|
|
lastSentVF.ReaderLeave()
|
|
}
|
|
if lastSentAF != nil {
|
|
lastSentAF.ReaderLeave()
|
|
}
|
|
}()
|
|
sendAudioFrame := func() {
|
|
lastSentAF = audioFrame
|
|
s.Debug("send audio frame", "seq", audioFrame.Sequence)
|
|
res := ah.Call([]reflect.Value{reflect.ValueOf(audioFrame.Wrap)})
|
|
if len(res) > 0 && !res[0].IsNil() {
|
|
s.Stop(res[0].Interface().(error))
|
|
}
|
|
}
|
|
sendVideoFrame := func() {
|
|
lastSentVF = videoFrame
|
|
s.Debug("send video frame", "seq", videoFrame.Sequence, "data", videoFrame.Wrap.Print())
|
|
res := vh.Call([]reflect.Value{reflect.ValueOf(videoFrame.Wrap)})
|
|
if len(res) > 0 && !res[0].IsNil() {
|
|
s.Stop(res[0].Interface().(error))
|
|
}
|
|
}
|
|
for err := s.Err(); err == nil; err = s.Err() {
|
|
if vr != nil {
|
|
for err == nil {
|
|
err = vr.ReadFrame(subMode)
|
|
if err == nil {
|
|
videoFrame = &vr.Value
|
|
err = s.Err()
|
|
} else {
|
|
s.Stop(err)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
// fmt.Println("video", s.VideoReader.Track.PreFrame().Sequence-frame.Sequence)
|
|
if videoFrame.Wrap.IsIDR() && vr.DecConfChanged() {
|
|
vr.LastCodecCtx = vr.Track.ICodecCtx
|
|
s.Debug("video codec changed", "data", vr.Track.ICodecCtx.GetSequenceFrame().Print())
|
|
vh.Call([]reflect.Value{reflect.ValueOf(vr.Track.ICodecCtx.GetSequenceFrame())})
|
|
}
|
|
if ar != nil {
|
|
if audioFrame != nil {
|
|
if util.Conditoinal(s.SyncMode == 0, videoFrame.Timestamp > audioFrame.Timestamp, videoFrame.WriteTime.After(audioFrame.WriteTime)) {
|
|
// fmt.Println("switch audio", audioFrame.CanRead)
|
|
sendAudioFrame()
|
|
audioFrame = nil
|
|
break
|
|
}
|
|
} else if initState++; initState >= 2 {
|
|
break
|
|
}
|
|
}
|
|
|
|
if !s.IFrameOnly || videoFrame.Wrap.IsIDR() {
|
|
sendVideoFrame()
|
|
}
|
|
}
|
|
} else {
|
|
createVideoReader()
|
|
}
|
|
// 正常模式下或者纯音频模式下,音频开始播放
|
|
if ar != nil {
|
|
for err == nil {
|
|
switch ar.State {
|
|
case READSTATE_INIT:
|
|
if vr != nil {
|
|
ar.FirstTs = vr.FirstTs
|
|
|
|
}
|
|
case READSTATE_NORMAL:
|
|
if vr != nil {
|
|
ar.SkipTs = vr.SkipTs
|
|
}
|
|
}
|
|
err = ar.ReadFrame(subMode)
|
|
if err == nil {
|
|
audioFrame = &ar.Value
|
|
err = s.Err()
|
|
} else {
|
|
s.Stop(err)
|
|
}
|
|
if err != nil {
|
|
return
|
|
}
|
|
// fmt.Println("audio", s.AudioReader.Track.PreFrame().Sequence-frame.Sequence)
|
|
if ar.DecConfChanged() {
|
|
ar.LastCodecCtx = ar.Track.ICodecCtx
|
|
if sf := ar.Track.ICodecCtx.GetSequenceFrame(); sf != nil {
|
|
ah.Call([]reflect.Value{reflect.ValueOf(sf)})
|
|
}
|
|
}
|
|
if vr != nil && videoFrame != nil {
|
|
if util.Conditoinal(s.SyncMode == 0, audioFrame.Timestamp > videoFrame.Timestamp, audioFrame.WriteTime.After(videoFrame.WriteTime)) {
|
|
sendVideoFrame()
|
|
videoFrame = nil
|
|
break
|
|
}
|
|
}
|
|
if audioFrame.Timestamp >= ar.SkipTs {
|
|
sendAudioFrame()
|
|
} else {
|
|
s.Debug("skip audio", "frame.AbsTime", audioFrame.Timestamp, "s.AudioReader.SkipTs", ar.SkipTs)
|
|
}
|
|
}
|
|
} else {
|
|
createAudioReader()
|
|
}
|
|
}
|
|
}
|