golanggohlsrtmpwebrtcmedia-serverobs-studiortcprtmp-proxyrtmp-serverrtprtsprtsp-proxyrtsp-relayrtsp-serversrtstreamingwebrtc-proxy
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
127 lines
2.5 KiB
127 lines
2.5 KiB
package hls |
|
|
|
import ( |
|
"context" |
|
"fmt" |
|
"net/url" |
|
"time" |
|
|
|
"github.com/aler9/gortsplib/v2/pkg/format" |
|
|
|
"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 |
|
logger ClientLogger |
|
|
|
ctx context.Context |
|
ctxCancel func() |
|
onTracks func([]format.Format) error |
|
onData map[format.Format]func(time.Duration, interface{}) |
|
playlistURL *url.URL |
|
|
|
// out |
|
outErr chan error |
|
} |
|
|
|
// NewClient allocates a Client. |
|
func NewClient( |
|
playlistURLStr string, |
|
fingerprint string, |
|
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, |
|
logger: logger, |
|
ctx: ctx, |
|
ctxCancel: ctxCancel, |
|
playlistURL: playlistURL, |
|
onData: make(map[format.Format]func(time.Duration, interface{})), |
|
outErr: make(chan error, 1), |
|
} |
|
|
|
return c, nil |
|
} |
|
|
|
// Start starts the client. |
|
func (c *Client) Start() { |
|
go c.run() |
|
} |
|
|
|
// 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 |
|
} |
|
|
|
// OnTracks sets a callback that is called when tracks are read. |
|
func (c *Client) OnTracks(cb func([]format.Format) error) { |
|
c.onTracks = cb |
|
} |
|
|
|
// OnData sets a callback that is called when data arrives. |
|
func (c *Client) OnData(forma format.Format, cb func(time.Duration, interface{})) { |
|
c.onData[forma] = cb |
|
} |
|
|
|
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.onData, |
|
) |
|
rp.add(dl) |
|
|
|
select { |
|
case err := <-rp.errorChan(): |
|
rp.close() |
|
return err |
|
|
|
case <-c.ctx.Done(): |
|
rp.close() |
|
return fmt.Errorf("terminated") |
|
} |
|
}
|
|
|