mirror of
https://github.com/aler9/rtsp-simple-server
synced 2025-09-27 12:02:27 +08:00

Discarded frames, decode errors and lost packets were logged individually, then there was a mechanism that prevented more than 1 log entry per second from being printed, resulting in inaccurate reports. Now discarded frames, decode errors and lost packets are accurately counted, and their count is printed once every second.
132 lines
2.9 KiB
Go
132 lines
2.9 KiB
Go
// Package hls contains the HLS static source.
|
|
package hls
|
|
|
|
import (
|
|
"net/http"
|
|
"time"
|
|
|
|
"github.com/bluenviron/gohlslib/v2"
|
|
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
|
|
|
"github.com/bluenviron/mediamtx/internal/conf"
|
|
"github.com/bluenviron/mediamtx/internal/counterdumper"
|
|
"github.com/bluenviron/mediamtx/internal/defs"
|
|
"github.com/bluenviron/mediamtx/internal/logger"
|
|
"github.com/bluenviron/mediamtx/internal/protocols/hls"
|
|
"github.com/bluenviron/mediamtx/internal/protocols/tls"
|
|
"github.com/bluenviron/mediamtx/internal/stream"
|
|
)
|
|
|
|
// Source is a HLS static source.
|
|
type Source struct {
|
|
ReadTimeout conf.Duration
|
|
Parent defs.StaticSourceParent
|
|
}
|
|
|
|
// Log implements logger.Writer.
|
|
func (s *Source) Log(level logger.Level, format string, args ...interface{}) {
|
|
s.Parent.Log(level, "[HLS source] "+format, args...)
|
|
}
|
|
|
|
// Run implements StaticSource.
|
|
func (s *Source) Run(params defs.StaticSourceRunParams) error {
|
|
var stream *stream.Stream
|
|
|
|
defer func() {
|
|
if stream != nil {
|
|
s.Parent.SetNotReady(defs.PathSourceStaticSetNotReadyReq{})
|
|
}
|
|
}()
|
|
|
|
decodeErrors := &counterdumper.CounterDumper{
|
|
OnReport: func(val uint64) {
|
|
s.Log(logger.Warn, "%s decode %s",
|
|
val,
|
|
func() string {
|
|
if val == 1 {
|
|
return "error"
|
|
}
|
|
return "errors"
|
|
}())
|
|
},
|
|
}
|
|
|
|
decodeErrors.Start()
|
|
defer decodeErrors.Stop()
|
|
|
|
tr := &http.Transport{
|
|
TLSClientConfig: tls.ConfigForFingerprint(params.Conf.SourceFingerprint),
|
|
}
|
|
defer tr.CloseIdleConnections()
|
|
|
|
var c *gohlslib.Client
|
|
c = &gohlslib.Client{
|
|
URI: params.ResolvedSource,
|
|
HTTPClient: &http.Client{
|
|
Timeout: time.Duration(s.ReadTimeout),
|
|
Transport: tr,
|
|
},
|
|
OnDownloadPrimaryPlaylist: func(u string) {
|
|
s.Log(logger.Debug, "downloading primary playlist %v", u)
|
|
},
|
|
OnDownloadStreamPlaylist: func(u string) {
|
|
s.Log(logger.Debug, "downloading stream playlist %v", u)
|
|
},
|
|
OnDownloadSegment: func(u string) {
|
|
s.Log(logger.Debug, "downloading segment %v", u)
|
|
},
|
|
OnDownloadPart: func(u string) {
|
|
s.Log(logger.Debug, "downloading part %v", u)
|
|
},
|
|
OnDecodeError: func(_ error) {
|
|
decodeErrors.Increase()
|
|
},
|
|
OnTracks: func(tracks []*gohlslib.Track) error {
|
|
medias, err := hls.ToStream(c, tracks, &stream)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
res := s.Parent.SetReady(defs.PathSourceStaticSetReadyReq{
|
|
Desc: &description.Session{Medias: medias},
|
|
GenerateRTPPackets: true,
|
|
})
|
|
if res.Err != nil {
|
|
return res.Err
|
|
}
|
|
|
|
stream = res.Stream
|
|
|
|
return nil
|
|
},
|
|
}
|
|
|
|
err := c.Start()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for {
|
|
select {
|
|
case err := <-c.Wait():
|
|
c.Close()
|
|
return err
|
|
|
|
case <-params.ReloadConf:
|
|
|
|
case <-params.Context.Done():
|
|
c.Close()
|
|
<-c.Wait()
|
|
return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
// APISourceDescribe implements StaticSource.
|
|
func (*Source) APISourceDescribe() defs.APIPathSourceOrReader {
|
|
return defs.APIPathSourceOrReader{
|
|
Type: "hlsSource",
|
|
ID: "",
|
|
}
|
|
}
|