mirror of
https://github.com/bluenviron/mediamtx
synced 2025-01-11 01:19:35 +00:00
122 lines
2.3 KiB
Go
122 lines
2.3 KiB
Go
package hls
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/url"
|
|
"time"
|
|
|
|
"github.com/aler9/gortsplib"
|
|
|
|
"github.com/aler9/rtsp-simple-server/internal/logger"
|
|
)
|
|
|
|
const (
|
|
clientMPEGTSEntryQueueSize = 100
|
|
clientFMP4MaxPartTracksPerSegment = 200
|
|
clientLiveStartingInvPosition = 3
|
|
clientLiveMaxInvPosition = 5
|
|
clientMaxDTSRTCDiff = 10 * time.Second
|
|
)
|
|
|
|
func clientAbsoluteURL(base *url.URL, relative string) (*url.URL, error) {
|
|
u, err := url.Parse(relative)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return base.ResolveReference(u), nil
|
|
}
|
|
|
|
// ClientLogger allows to receive log lines.
|
|
type ClientLogger interface {
|
|
Log(level logger.Level, format string, args ...interface{})
|
|
}
|
|
|
|
// Client is a HLS client.
|
|
type Client struct {
|
|
fingerprint string
|
|
onTracks func(*gortsplib.TrackH264, *gortsplib.TrackMPEG4Audio) error
|
|
onVideoData func(time.Duration, [][]byte)
|
|
onAudioData func(time.Duration, []byte)
|
|
logger ClientLogger
|
|
|
|
ctx context.Context
|
|
ctxCancel func()
|
|
playlistURL *url.URL
|
|
|
|
// out
|
|
outErr chan error
|
|
}
|
|
|
|
// NewClient allocates a Client.
|
|
func NewClient(
|
|
playlistURLStr string,
|
|
fingerprint string,
|
|
onTracks func(*gortsplib.TrackH264, *gortsplib.TrackMPEG4Audio) error,
|
|
onVideoData func(time.Duration, [][]byte),
|
|
onAudioData func(time.Duration, []byte),
|
|
logger ClientLogger,
|
|
) (*Client, error) {
|
|
playlistURL, err := url.Parse(playlistURLStr)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
|
|
|
c := &Client{
|
|
fingerprint: fingerprint,
|
|
onTracks: onTracks,
|
|
onVideoData: onVideoData,
|
|
onAudioData: onAudioData,
|
|
logger: logger,
|
|
ctx: ctx,
|
|
ctxCancel: ctxCancel,
|
|
playlistURL: playlistURL,
|
|
outErr: make(chan error, 1),
|
|
}
|
|
|
|
go c.run()
|
|
|
|
return c, nil
|
|
}
|
|
|
|
// Close closes all the Client resources.
|
|
func (c *Client) Close() {
|
|
c.ctxCancel()
|
|
}
|
|
|
|
// Wait waits for any error of the Client.
|
|
func (c *Client) Wait() chan error {
|
|
return c.outErr
|
|
}
|
|
|
|
func (c *Client) run() {
|
|
c.outErr <- c.runInner()
|
|
}
|
|
|
|
func (c *Client) runInner() error {
|
|
rp := newClientRoutinePool()
|
|
|
|
dl := newClientDownloaderPrimary(
|
|
c.playlistURL,
|
|
c.fingerprint,
|
|
c.logger,
|
|
rp,
|
|
c.onTracks,
|
|
c.onVideoData,
|
|
c.onAudioData,
|
|
)
|
|
rp.add(dl)
|
|
|
|
select {
|
|
case err := <-rp.errorChan():
|
|
rp.close()
|
|
return err
|
|
|
|
case <-c.ctx.Done():
|
|
rp.close()
|
|
return fmt.Errorf("terminated")
|
|
}
|
|
}
|