|
@@ -9,32 +9,42 @@ import (
|
|
|
"net/http"
|
|
|
"net/url"
|
|
|
"strings"
|
|
|
+ "sync/atomic"
|
|
|
"time"
|
|
|
)
|
|
|
|
|
|
+type HLSOptions struct {
|
|
|
+ Timeout time.Duration
|
|
|
+ MaxAttempts int
|
|
|
+}
|
|
|
+
|
|
|
type HLS struct {
|
|
|
- ctx context.Context
|
|
|
- ctxClose context.CancelFunc
|
|
|
- runtimeClose chan any
|
|
|
+ ctx context.Context
|
|
|
+ ctxClose context.CancelFunc
|
|
|
+ exit chan any
|
|
|
|
|
|
+ options HLSOptions
|
|
|
client *http.Client
|
|
|
request *http.Request
|
|
|
+
|
|
|
+ retryAttempts atomic.Uint32
|
|
|
}
|
|
|
|
|
|
-func NewHLS(r func() (*http.Request, error), t time.Duration) (*HLS, error) {
|
|
|
+func NewHLS(r func() (*http.Request, error), opts HLSOptions) (*HLS, error) {
|
|
|
if r == nil {
|
|
|
return nil, errors.New("illegal state: empty requester")
|
|
|
}
|
|
|
|
|
|
var (
|
|
|
d = HLS{
|
|
|
+ options: opts,
|
|
|
client: &http.Client{
|
|
|
Transport: func() *http.Transport {
|
|
|
transport := http.DefaultTransport.(*http.Transport)
|
|
|
transport.Proxy = http.ProxyFromEnvironment
|
|
|
return transport
|
|
|
}(),
|
|
|
- Timeout: t,
|
|
|
+ Timeout: opts.Timeout,
|
|
|
},
|
|
|
}
|
|
|
err error
|
|
@@ -55,15 +65,12 @@ func (d *HLS) Close() error {
|
|
|
d.ctxClose()
|
|
|
}
|
|
|
|
|
|
- if d.runtimeClose != nil {
|
|
|
- d.runtimeClose <- 0
|
|
|
- }
|
|
|
-
|
|
|
+ d.exit <- 0
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func (d *HLS) Start() chan []byte {
|
|
|
- d.runtimeClose = make(chan any)
|
|
|
+ d.exit = make(chan any)
|
|
|
|
|
|
var (
|
|
|
c = make(chan []byte)
|
|
@@ -77,12 +84,15 @@ func (d *HLS) Start() chan []byte {
|
|
|
|
|
|
for {
|
|
|
select {
|
|
|
- case <-d.runtimeClose:
|
|
|
+ case <-d.exit:
|
|
|
c <- nil
|
|
|
return
|
|
|
default:
|
|
|
if medialist, err = d.getMediaList(); err != nil {
|
|
|
- log.Debug().Err(err).Msg("could not retrieve m3u8 playlist")
|
|
|
+ log.Debug().
|
|
|
+ Err(err).
|
|
|
+ Msg("could not retrieve m3u8 playlist")
|
|
|
+
|
|
|
c <- nil
|
|
|
return
|
|
|
}
|
|
@@ -160,6 +170,37 @@ func (d *HLS) Start() chan []byte {
|
|
|
return c
|
|
|
}
|
|
|
|
|
|
+func (d *HLS) requestMediaList(request *http.Request) (*http.Response, error) {
|
|
|
+ var (
|
|
|
+ response *http.Response
|
|
|
+ err error
|
|
|
+ )
|
|
|
+
|
|
|
+ response, err = d.client.Do(request)
|
|
|
+ if err != nil && errors.Is(err, http.ErrHandlerTimeout) {
|
|
|
+ if d.retryAttempts.Load() == uint32(d.options.MaxAttempts) {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ select {
|
|
|
+ case <-time.After(time.Second):
|
|
|
+ }
|
|
|
+
|
|
|
+ if d.options.MaxAttempts != -1 {
|
|
|
+ d.retryAttempts.Add(1)
|
|
|
+ }
|
|
|
+
|
|
|
+ return d.requestMediaList(request)
|
|
|
+ }
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+
|
|
|
+ d.retryAttempts.Store(0)
|
|
|
+ return response, nil
|
|
|
+}
|
|
|
+
|
|
|
func (d *HLS) getMediaList() (*m3u8.MediaPlaylist, error) {
|
|
|
var (
|
|
|
request *http.Request
|
|
@@ -171,7 +212,7 @@ func (d *HLS) getMediaList() (*m3u8.MediaPlaylist, error) {
|
|
|
)
|
|
|
|
|
|
request = d.request.Clone(d.ctx)
|
|
|
- if response, err = d.client.Do(request); err != nil {
|
|
|
+ if response, err = d.requestMediaList(request); err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|