mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-11-02 14:04:02 +08:00
feat: rtsp client
This commit is contained in:
4
api.go
4
api.go
@@ -182,7 +182,7 @@ func (s *Server) GetSubscribers(ctx context.Context, req *pb.SubscribersRequest)
|
|||||||
}
|
}
|
||||||
func (s *Server) AudioTrackSnap(ctx context.Context, req *pb.StreamSnapRequest) (res *pb.TrackSnapShotResponse, err error) {
|
func (s *Server) AudioTrackSnap(ctx context.Context, req *pb.StreamSnapRequest) (res *pb.TrackSnapShotResponse, err error) {
|
||||||
s.Call(func() {
|
s.Call(func() {
|
||||||
if pub, ok := s.Streams.Get(req.StreamPath); ok && !pub.AudioTrack.IsEmpty() {
|
if pub, ok := s.Streams.Get(req.StreamPath); ok && pub.HasAudioTrack() {
|
||||||
res = &pb.TrackSnapShotResponse{}
|
res = &pb.TrackSnapShotResponse{}
|
||||||
for _, memlist := range pub.AudioTrack.Allocator.GetChildren() {
|
for _, memlist := range pub.AudioTrack.Allocator.GetChildren() {
|
||||||
var list []*pb.MemoryBlock
|
var list []*pb.MemoryBlock
|
||||||
@@ -260,7 +260,7 @@ func (s *Server) api_VideoTrack_SSE(rw http.ResponseWriter, r *http.Request) {
|
|||||||
|
|
||||||
func (s *Server) VideoTrackSnap(ctx context.Context, req *pb.StreamSnapRequest) (res *pb.TrackSnapShotResponse, err error) {
|
func (s *Server) VideoTrackSnap(ctx context.Context, req *pb.StreamSnapRequest) (res *pb.TrackSnapShotResponse, err error) {
|
||||||
s.Call(func() {
|
s.Call(func() {
|
||||||
if pub, ok := s.Streams.Get(req.StreamPath); ok && !pub.VideoTrack.IsEmpty() {
|
if pub, ok := s.Streams.Get(req.StreamPath); ok && pub.HasVideoTrack() {
|
||||||
res = &pb.TrackSnapShotResponse{}
|
res = &pb.TrackSnapShotResponse{}
|
||||||
for _, memlist := range pub.VideoTrack.Allocator.GetChildren() {
|
for _, memlist := range pub.VideoTrack.Allocator.GetChildren() {
|
||||||
var list []*pb.MemoryBlock
|
var list []*pb.MemoryBlock
|
||||||
|
|||||||
10
example/rtsp-pull/config1.yaml
Normal file
10
example/rtsp-pull/config1.yaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
global:
|
||||||
|
loglevel: info
|
||||||
|
tcp:
|
||||||
|
listenaddr: :50050
|
||||||
|
hdl:
|
||||||
|
publish:
|
||||||
|
pubaudio: false
|
||||||
|
pull:
|
||||||
|
pullonstart:
|
||||||
|
live/test: /Users/dexter/Movies/jb-demo.flv
|
||||||
12
example/rtsp-pull/config2.yaml
Normal file
12
example/rtsp-pull/config2.yaml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
global:
|
||||||
|
tcp:
|
||||||
|
listenaddr: :50051
|
||||||
|
http:
|
||||||
|
listenaddr: :8081
|
||||||
|
listenaddrtls: :8555
|
||||||
|
rtsp:
|
||||||
|
tcp:
|
||||||
|
listenaddr: :8554
|
||||||
|
pull:
|
||||||
|
pullonstart:
|
||||||
|
live/test: rtsp://localhost/live/test
|
||||||
24
example/rtsp-pull/main.go
Normal file
24
example/rtsp-pull/main.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"flag"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"m7s.live/m7s/v5"
|
||||||
|
_ "m7s.live/m7s/v5/plugin/debug"
|
||||||
|
_ "m7s.live/m7s/v5/plugin/hdl"
|
||||||
|
_ "m7s.live/m7s/v5/plugin/rtsp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx := context.Background()
|
||||||
|
var multi bool
|
||||||
|
flag.BoolVar(&multi, "multi", false, "debug")
|
||||||
|
flag.Parse()
|
||||||
|
if multi {
|
||||||
|
go m7s.Run(ctx, "config1.yaml")
|
||||||
|
}
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
m7s.NewServer().Run(ctx, "config2.yaml")
|
||||||
|
}
|
||||||
@@ -2,4 +2,3 @@ global:
|
|||||||
loglevel: info
|
loglevel: info
|
||||||
tcp:
|
tcp:
|
||||||
listenaddr: :50050
|
listenaddr: :50050
|
||||||
|
|
||||||
|
|||||||
10
pkg/log.go
10
pkg/log.go
@@ -8,6 +8,16 @@ import (
|
|||||||
|
|
||||||
var _ slog.Handler = (*MultiLogHandler)(nil)
|
var _ slog.Handler = (*MultiLogHandler)(nil)
|
||||||
|
|
||||||
|
func ParseLevel(level string) slog.Level {
|
||||||
|
var lv slog.LevelVar
|
||||||
|
if level == "trace" {
|
||||||
|
lv.Set(TraceLevel)
|
||||||
|
} else {
|
||||||
|
lv.UnmarshalText([]byte(level))
|
||||||
|
}
|
||||||
|
return lv.Level()
|
||||||
|
}
|
||||||
|
|
||||||
type MultiLogHandler struct {
|
type MultiLogHandler struct {
|
||||||
handlers []slog.Handler
|
handlers []slog.Handler
|
||||||
parentLevel *slog.Level
|
parentLevel *slog.Level
|
||||||
|
|||||||
@@ -123,11 +123,8 @@ func (r *BufReader) ReadRange(n int, yield func([]byte)) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
n -= r.buf.Length
|
n -= r.buf.Length
|
||||||
if yield != nil {
|
|
||||||
r.buf.Range(yield)
|
r.buf.Range(yield)
|
||||||
}
|
}
|
||||||
r.buf.MoveToEnd()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -241,6 +241,17 @@ func (r *MemoryReader) ReadBE(n int) (num int, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *MemoryReader) Range(yield func([]byte)) {
|
||||||
|
if yield != nil {
|
||||||
|
for r.Length > 0 {
|
||||||
|
yield(r.GetCurrent())
|
||||||
|
r.skipBuf()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r.MoveToEnd()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *MemoryReader) RangeN(n int, yield func([]byte)) {
|
func (r *MemoryReader) RangeN(n int, yield func([]byte)) {
|
||||||
for good := yield != nil; r.Length > 0 && n > 0; r.skipBuf() {
|
for good := yield != nil; r.Length > 0 && n > 0; r.skipBuf() {
|
||||||
curBuf := r.GetCurrent()
|
curBuf := r.GetCurrent()
|
||||||
|
|||||||
@@ -218,9 +218,9 @@ func (sma *ScalableMemoryAllocator) Read(reader io.Reader, n int) (mem []byte, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sma *ScalableMemoryAllocator) FreeRest(mem *[]byte, keep int) {
|
func (sma *ScalableMemoryAllocator) FreeRest(mem *[]byte, keep int) {
|
||||||
if keep < len(*mem) {
|
if m := *mem; keep < len(m) {
|
||||||
sma.Free((*mem)[keep:])
|
sma.Free(m[keep:])
|
||||||
*mem = (*mem)[:keep]
|
*mem = m[:keep]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package m7s
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"log/slog"
|
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -64,12 +63,7 @@ func (plugin *PluginMeta) Init(s *Server, userConfig map[string]any) {
|
|||||||
}
|
}
|
||||||
p.Config.ParseUserFile(userConfig)
|
p.Config.ParseUserFile(userConfig)
|
||||||
finalConfig, _ := yaml.Marshal(p.Config.GetMap())
|
finalConfig, _ := yaml.Marshal(p.Config.GetMap())
|
||||||
var lv slog.LevelVar
|
p.Logger.Handler().(*MultiLogHandler).SetLevel(ParseLevel(p.config.LogLevel))
|
||||||
_ = lv.UnmarshalText([]byte(p.config.LogLevel))
|
|
||||||
if p.config.LogLevel == "trace" {
|
|
||||||
lv.Set(TraceLevel)
|
|
||||||
}
|
|
||||||
p.Logger.Handler().(*MultiLogHandler).SetLevel(lv.Level())
|
|
||||||
p.Debug("config", "detail", string(finalConfig))
|
p.Debug("config", "detail", string(finalConfig))
|
||||||
if s.DisableAll {
|
if s.DisableAll {
|
||||||
p.Disabled = true
|
p.Disabled = true
|
||||||
|
|||||||
@@ -27,13 +27,8 @@ type LogRotatePlugin struct {
|
|||||||
var _ = m7s.InstallPlugin[LogRotatePlugin](&pb.Logrotate_ServiceDesc, pb.RegisterLogrotateHandler)
|
var _ = m7s.InstallPlugin[LogRotatePlugin](&pb.Logrotate_ServiceDesc, pb.RegisterLogrotateHandler)
|
||||||
|
|
||||||
func (config *LogRotatePlugin) OnInit() (err error) {
|
func (config *LogRotatePlugin) OnInit() (err error) {
|
||||||
var lv slog.LevelVar
|
|
||||||
lv.UnmarshalText([]byte(config.Level))
|
|
||||||
if config.Level == "trace" {
|
|
||||||
lv.Set(pkg.TraceLevel)
|
|
||||||
}
|
|
||||||
builder := func(w io.Writer, opts *slog.HandlerOptions) slog.Handler {
|
builder := func(w io.Writer, opts *slog.HandlerOptions) slog.Handler {
|
||||||
return console.NewHandler(w, &console.HandlerOptions{NoColor: true, Level: lv.Level(),TimeFormat: "2006-01-02 15:04:05.000"})
|
return console.NewHandler(w, &console.HandlerOptions{NoColor: true, Level: pkg.ParseLevel(config.Level), TimeFormat: "2006-01-02 15:04:05.000"})
|
||||||
}
|
}
|
||||||
config.handler, err = rotoslog.NewHandler(rotoslog.LogHandlerBuilder(builder), rotoslog.LogDir(config.Path), rotoslog.MaxFileSize(config.Size), rotoslog.DateTimeLayout(config.Formatter), rotoslog.MaxRotatedFiles(config.MaxFiles))
|
config.handler, err = rotoslog.NewHandler(rotoslog.LogHandlerBuilder(builder), rotoslog.LogDir(config.Path), rotoslog.MaxFileSize(config.Size), rotoslog.DateTimeLayout(config.Formatter), rotoslog.MaxRotatedFiles(config.MaxFiles))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|||||||
@@ -319,6 +319,7 @@ func (c *NetConnection) Receive(sendMode bool) (channelID byte, buf []byte, err
|
|||||||
}
|
}
|
||||||
buf = c.MemoryAllocator.Malloc(size)
|
buf = c.MemoryAllocator.Malloc(size)
|
||||||
if err = c.ReadNto(size, buf); err != nil {
|
if err = c.ReadNto(size, buf); err != nil {
|
||||||
|
c.MemoryAllocator.Free(buf)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,8 +13,8 @@ import (
|
|||||||
|
|
||||||
type Stream struct {
|
type Stream struct {
|
||||||
*NetConnection
|
*NetConnection
|
||||||
AudioChannelID byte
|
AudioChannelID int
|
||||||
VideoChannelID byte
|
VideoChannelID int
|
||||||
}
|
}
|
||||||
type Sender struct {
|
type Sender struct {
|
||||||
*m7s.Subscriber
|
*m7s.Subscriber
|
||||||
@@ -36,7 +36,7 @@ func (ns *Stream) Close() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sender) GetMedia() (medias []*core.Media, err error) {
|
func (s *Sender) GetMedia() (medias []*core.Media, err error) {
|
||||||
if s.SubAudio && s.Publisher.PubAudio {
|
if s.SubAudio && s.Publisher.PubAudio && s.Publisher.HasAudioTrack() {
|
||||||
audioTrack := s.Publisher.GetAudioTrack(reflect.TypeOf((*mrtp.RTPAudio)(nil)))
|
audioTrack := s.Publisher.GetAudioTrack(reflect.TypeOf((*mrtp.RTPAudio)(nil)))
|
||||||
if err = audioTrack.WaitReady(); err != nil {
|
if err = audioTrack.WaitReady(); err != nil {
|
||||||
return
|
return
|
||||||
@@ -54,11 +54,11 @@ func (s *Sender) GetMedia() (medias []*core.Media, err error) {
|
|||||||
}},
|
}},
|
||||||
ID: fmt.Sprintf("trackID=%d", len(medias)),
|
ID: fmt.Sprintf("trackID=%d", len(medias)),
|
||||||
}
|
}
|
||||||
|
s.AudioChannelID = len(medias) << 1
|
||||||
medias = append(medias, media)
|
medias = append(medias, media)
|
||||||
s.AudioChannelID = 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.SubVideo && s.Publisher.PubVideo {
|
if s.SubVideo && s.Publisher.PubVideo && s.Publisher.HasVideoTrack() {
|
||||||
videoTrack := s.Publisher.GetVideoTrack(reflect.TypeOf((*mrtp.RTPVideo)(nil)))
|
videoTrack := s.Publisher.GetVideoTrack(reflect.TypeOf((*mrtp.RTPVideo)(nil)))
|
||||||
if err = videoTrack.WaitReady(); err != nil {
|
if err = videoTrack.WaitReady(); err != nil {
|
||||||
return
|
return
|
||||||
@@ -77,20 +77,19 @@ func (s *Sender) GetMedia() (medias []*core.Media, err error) {
|
|||||||
Codecs: []*core.Codec{&c},
|
Codecs: []*core.Codec{&c},
|
||||||
ID: fmt.Sprintf("trackID=%d", len(medias)),
|
ID: fmt.Sprintf("trackID=%d", len(medias)),
|
||||||
}
|
}
|
||||||
s.VideoChannelID = byte(len(medias)) << 1
|
s.VideoChannelID = len(medias) << 1
|
||||||
medias = append(medias, media)
|
medias = append(medias, media)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Sender) Send() error {
|
func (s *Sender) sendRTP(pack *mrtp.RTPData, channel int) (err error) {
|
||||||
sendRTP := func(pack *mrtp.RTPData, channel byte) (err error) {
|
|
||||||
s.StartWrite()
|
s.StartWrite()
|
||||||
defer s.StopWrite()
|
defer s.StopWrite()
|
||||||
for _, packet := range pack.Packets {
|
for _, packet := range pack.Packets {
|
||||||
size := packet.MarshalSize()
|
size := packet.MarshalSize()
|
||||||
chunk := s.MemoryAllocator.Borrow(size + 4)
|
chunk := s.MemoryAllocator.Borrow(size + 4)
|
||||||
chunk[0], chunk[1], chunk[2], chunk[3] = '$', channel, byte(size>>8), byte(size)
|
chunk[0], chunk[1], chunk[2], chunk[3] = '$', byte(channel), byte(size>>8), byte(size)
|
||||||
if _, err = packet.MarshalTo(chunk[4:]); err != nil {
|
if _, err = packet.MarshalTo(chunk[4:]); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -99,19 +98,31 @@ func (s *Sender) Send() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go func(err error) {
|
|
||||||
|
func (s *Sender) send() error {
|
||||||
|
return m7s.PlayBlock(s.Subscriber, func(audio *mrtp.RTPAudio) error {
|
||||||
|
return s.sendRTP(&audio.RTPData, s.AudioChannelID)
|
||||||
|
}, func(video *mrtp.RTPVideo) error {
|
||||||
|
return s.sendRTP(&video.RTPData, s.VideoChannelID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Sender) receive() {
|
||||||
|
var err error
|
||||||
for err == nil {
|
for err == nil {
|
||||||
_, _, err = s.NetConnection.Receive(true)
|
_, _, err = s.NetConnection.Receive(true)
|
||||||
}
|
}
|
||||||
}(nil)
|
|
||||||
return m7s.PlayBlock(s.Subscriber, func(audio *mrtp.RTPAudio) error {
|
|
||||||
return sendRTP(&audio.RTPData, s.AudioChannelID)
|
|
||||||
}, func(video *mrtp.RTPVideo) error {
|
|
||||||
return sendRTP(&video.RTPData, s.VideoChannelID)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Sender) Send() (err error) {
|
||||||
|
go s.receive()
|
||||||
|
return s.send()
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Receiver) SetMedia(medias []*core.Media) (err error) {
|
func (r *Receiver) SetMedia(medias []*core.Media) (err error) {
|
||||||
|
r.AudioChannelID = -1
|
||||||
|
r.VideoChannelID = -1
|
||||||
for i, media := range medias {
|
for i, media := range medias {
|
||||||
if codec := media.Codecs[0]; codec.IsAudio() {
|
if codec := media.Codecs[0]; codec.IsAudio() {
|
||||||
r.AudioCodecParameters = &webrtc.RTPCodecParameters{
|
r.AudioCodecParameters = &webrtc.RTPCodecParameters{
|
||||||
@@ -124,9 +135,9 @@ func (r *Receiver) SetMedia(medias []*core.Media) (err error) {
|
|||||||
},
|
},
|
||||||
PayloadType: webrtc.PayloadType(codec.PayloadType),
|
PayloadType: webrtc.PayloadType(codec.PayloadType),
|
||||||
}
|
}
|
||||||
r.AudioChannelID = byte(i) << 1
|
r.AudioChannelID = i << 1
|
||||||
} else if codec.IsVideo() {
|
} else if codec.IsVideo() {
|
||||||
r.VideoChannelID = byte(i) << 1
|
r.VideoChannelID = i << 1
|
||||||
r.VideoCodecParameters = &webrtc.RTPCodecParameters{
|
r.VideoCodecParameters = &webrtc.RTPCodecParameters{
|
||||||
RTPCodecCapability: webrtc.RTPCodecCapability{
|
RTPCodecCapability: webrtc.RTPCodecCapability{
|
||||||
MimeType: "video/" + codec.Name,
|
MimeType: "video/" + codec.Name,
|
||||||
@@ -160,7 +171,7 @@ func (r *Receiver) Receive() (err error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if channelID&1 == 0 {
|
if channelID&1 == 0 {
|
||||||
switch channelID {
|
switch int(channelID) {
|
||||||
case r.AudioChannelID:
|
case r.AudioChannelID:
|
||||||
if !r.PubAudio {
|
if !r.PubAudio {
|
||||||
continue
|
continue
|
||||||
|
|||||||
32
publisher.go
32
publisher.go
@@ -57,10 +57,6 @@ type AVTracks struct {
|
|||||||
util.Collection[reflect.Type, *AVTrack]
|
util.Collection[reflect.Type, *AVTrack]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *AVTracks) IsEmpty() bool {
|
|
||||||
return t.Length == 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *AVTracks) CreateSubTrack(dataType reflect.Type) (track *AVTrack) {
|
func (t *AVTracks) CreateSubTrack(dataType reflect.Type) (track *AVTrack) {
|
||||||
track = NewAVTrack(dataType, t.AVTrack)
|
track = NewAVTrack(dataType, t.AVTrack)
|
||||||
track.WrapIndex = t.Length
|
track.WrapIndex = t.Length
|
||||||
@@ -116,11 +112,11 @@ func (p *Publisher) checkTimeout() (err error) {
|
|||||||
err = p.timeout()
|
err = p.timeout()
|
||||||
default:
|
default:
|
||||||
if p.PublishTimeout > 0 {
|
if p.PublishTimeout > 0 {
|
||||||
if !p.VideoTrack.IsEmpty() && !p.VideoTrack.LastValue.WriteTime.IsZero() && time.Since(p.VideoTrack.LastValue.WriteTime) > p.PublishTimeout {
|
if p.HasVideoTrack() && !p.VideoTrack.LastValue.WriteTime.IsZero() && time.Since(p.VideoTrack.LastValue.WriteTime) > p.PublishTimeout {
|
||||||
p.Error("video timeout", "writeTime", p.VideoTrack.LastValue.WriteTime)
|
p.Error("video timeout", "writeTime", p.VideoTrack.LastValue.WriteTime)
|
||||||
err = ErrPublishTimeout
|
err = ErrPublishTimeout
|
||||||
}
|
}
|
||||||
if !p.AudioTrack.IsEmpty() && !p.AudioTrack.LastValue.WriteTime.IsZero() && time.Since(p.AudioTrack.LastValue.WriteTime) > p.PublishTimeout {
|
if p.HasAudioTrack() && !p.AudioTrack.LastValue.WriteTime.IsZero() && time.Since(p.AudioTrack.LastValue.WriteTime) > p.PublishTimeout {
|
||||||
p.Error("audio timeout", "writeTime", p.AudioTrack.LastValue.WriteTime)
|
p.Error("audio timeout", "writeTime", p.AudioTrack.LastValue.WriteTime)
|
||||||
err = ErrPublishTimeout
|
err = ErrPublishTimeout
|
||||||
}
|
}
|
||||||
@@ -144,10 +140,10 @@ func (p *Publisher) RemoveSubscriber(subscriber *Subscriber) {
|
|||||||
} else {
|
} else {
|
||||||
p.BufferTime = p.Plugin.GetCommonConf().Publish.BufferTime
|
p.BufferTime = p.Plugin.GetCommonConf().Publish.BufferTime
|
||||||
}
|
}
|
||||||
if !p.AudioTrack.IsEmpty() {
|
if p.HasAudioTrack() {
|
||||||
p.AudioTrack.AVTrack.BufferRange[0] = p.BufferTime
|
p.AudioTrack.AVTrack.BufferRange[0] = p.BufferTime
|
||||||
}
|
}
|
||||||
if !p.VideoTrack.IsEmpty() {
|
if p.HasVideoTrack() {
|
||||||
p.VideoTrack.AVTrack.BufferRange[0] = p.BufferTime
|
p.VideoTrack.AVTrack.BufferRange[0] = p.BufferTime
|
||||||
}
|
}
|
||||||
if p.State == PublisherStateSubscribed && p.Subscribers.Length == 0 {
|
if p.State == PublisherStateSubscribed && p.Subscribers.Length == 0 {
|
||||||
@@ -166,10 +162,10 @@ func (p *Publisher) AddSubscriber(subscriber *Subscriber) {
|
|||||||
p.Info("subscriber +1", "count", p.Subscribers.Length)
|
p.Info("subscriber +1", "count", p.Subscribers.Length)
|
||||||
if subscriber.BufferTime > p.BufferTime {
|
if subscriber.BufferTime > p.BufferTime {
|
||||||
p.BufferTime = subscriber.BufferTime
|
p.BufferTime = subscriber.BufferTime
|
||||||
if !p.AudioTrack.IsEmpty() {
|
if p.HasAudioTrack() {
|
||||||
p.AudioTrack.AVTrack.BufferRange[0] = p.BufferTime
|
p.AudioTrack.AVTrack.BufferRange[0] = p.BufferTime
|
||||||
}
|
}
|
||||||
if !p.VideoTrack.IsEmpty() {
|
if p.HasVideoTrack() {
|
||||||
p.VideoTrack.AVTrack.BufferRange[0] = p.BufferTime
|
p.VideoTrack.AVTrack.BufferRange[0] = p.BufferTime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -420,7 +416,7 @@ func (p *Publisher) GetAudioTrack(dataType reflect.Type) (t *AVTrack) {
|
|||||||
if t, ok := p.AudioTrack.Get(dataType); ok {
|
if t, ok := p.AudioTrack.Get(dataType); ok {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
if !p.AudioTrack.IsEmpty() {
|
if p.HasAudioTrack() {
|
||||||
return p.AudioTrack.CreateSubTrack(dataType)
|
return p.AudioTrack.CreateSubTrack(dataType)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@@ -432,12 +428,20 @@ func (p *Publisher) GetVideoTrack(dataType reflect.Type) (t *AVTrack) {
|
|||||||
if t, ok := p.VideoTrack.Get(dataType); ok {
|
if t, ok := p.VideoTrack.Get(dataType); ok {
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
if !p.VideoTrack.IsEmpty() {
|
if p.HasVideoTrack() {
|
||||||
return p.VideoTrack.CreateSubTrack(dataType)
|
return p.VideoTrack.CreateSubTrack(dataType)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *Publisher) HasAudioTrack() bool {
|
||||||
|
return p.AudioTrack.Length > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Publisher) HasVideoTrack() bool {
|
||||||
|
return p.VideoTrack.Length > 0
|
||||||
|
}
|
||||||
|
|
||||||
func (p *Publisher) Dispose(err error) {
|
func (p *Publisher) Dispose(err error) {
|
||||||
p.Lock()
|
p.Lock()
|
||||||
defer p.Unlock()
|
defer p.Unlock()
|
||||||
@@ -448,10 +452,10 @@ func (p *Publisher) Dispose(err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if p.IsStopped() {
|
if p.IsStopped() {
|
||||||
if !p.AudioTrack.IsEmpty() {
|
if p.HasAudioTrack() {
|
||||||
p.AudioTrack.Dispose()
|
p.AudioTrack.Dispose()
|
||||||
}
|
}
|
||||||
if !p.VideoTrack.IsEmpty() {
|
if p.HasVideoTrack() {
|
||||||
p.VideoTrack.Dispose()
|
p.VideoTrack.Dispose()
|
||||||
}
|
}
|
||||||
p.State = PublisherStateDisposed
|
p.State = PublisherStateDisposed
|
||||||
|
|||||||
@@ -150,12 +150,7 @@ func (s *Server) run(ctx context.Context, conf any) (err error) {
|
|||||||
if cg != nil {
|
if cg != nil {
|
||||||
s.Config.ParseUserFile(cg["global"])
|
s.Config.ParseUserFile(cg["global"])
|
||||||
}
|
}
|
||||||
var lv slog.LevelVar
|
s.LogHandler.SetLevel(ParseLevel(s.config.LogLevel))
|
||||||
lv.UnmarshalText([]byte(s.config.LogLevel))
|
|
||||||
if s.config.LogLevel == "trace" {
|
|
||||||
lv.Set(TraceLevel)
|
|
||||||
}
|
|
||||||
s.LogHandler.SetLevel(lv.Level())
|
|
||||||
s.registerHandler(map[string]http.HandlerFunc{
|
s.registerHandler(map[string]http.HandlerFunc{
|
||||||
"/api/config/json/{name}": s.api_Config_JSON_,
|
"/api/config/json/{name}": s.api_Config_JSON_,
|
||||||
"/api/stream/annexb/{streamPath...}": s.api_Stream_AnnexB_,
|
"/api/stream/annexb/{streamPath...}": s.api_Stream_AnnexB_,
|
||||||
|
|||||||
Reference in New Issue
Block a user