Improve HLS reader

This commit is contained in:
Alexey Khit
2023-08-20 16:35:09 +03:00
parent 32bf64028d
commit fb51dc781d
+35 -25
View File
@@ -2,7 +2,6 @@ package hls
import ( import (
"bytes" "bytes"
"errors"
"io" "io"
"net/http" "net/http"
"net/url" "net/url"
@@ -16,6 +15,7 @@ type reader struct {
client *http.Client client *http.Client
request *http.Request request *http.Request
playlist []byte
lastSegment []byte lastSegment []byte
lastTime time.Time lastTime time.Time
@@ -28,18 +28,22 @@ func NewReader(u *url.URL, body io.ReadCloser) (io.Reader, error) {
return nil, err return nil, err
} }
var rawURL string
re := regexp.MustCompile(`#EXT-X-STREAM-INF.+?\n(\S+)`) re := regexp.MustCompile(`#EXT-X-STREAM-INF.+?\n(\S+)`)
m := re.FindSubmatch(b) m := re.FindSubmatch(b)
if m == nil { if m != nil {
return nil, errors.New("hls: wrong playlist: " + string(b))
}
ref, err := url.Parse(string(m[1])) ref, err := url.Parse(string(m[1]))
if err != nil { if err != nil {
return nil, err return nil, err
} }
req, err := http.NewRequest("GET", u.ResolveReference(ref).String(), nil) rawURL = u.ResolveReference(ref).String()
} else {
rawURL = u.String()
}
req, err := http.NewRequest("GET", rawURL, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -75,7 +79,8 @@ func (r *reader) Read(dst []byte) (n int, err error) {
} }
func (r *reader) getSegment() ([]byte, error) { func (r *reader) getSegment() ([]byte, error) {
for { for i := 0; i < 5; i++ {
if r.playlist == nil {
if wait := time.Second - time.Since(r.lastTime); wait > 0 { if wait := time.Second - time.Since(r.lastTime); wait > 0 {
time.Sleep(wait) time.Sleep(wait)
} }
@@ -86,37 +91,40 @@ func (r *reader) getSegment() ([]byte, error) {
return nil, err return nil, err
} }
playlist, err := io.ReadAll(res.Body) r.playlist, err = io.ReadAll(res.Body)
if err != nil { if err != nil {
return nil, err return nil, err
} }
r.lastTime = time.Now() r.lastTime = time.Now()
//log.Printf("[hls] load playlist\n%s", playlist) //log.Printf("[hls] load playlist\n%s", r.playlist)
}
// 2. Remove all previous segments from playlist
if i := bytes.Index(playlist, r.lastSegment); i > 0 { for r.playlist != nil {
playlist = playlist[i:] // 2. Remove all previous segments from playlist
if i := bytes.Index(r.playlist, r.lastSegment); i > 0 {
r.playlist = r.playlist[i:]
} }
for playlist != nil {
// 3. Get link to new segment // 3. Get link to new segment
var segment []byte segment := getSegment(r.playlist)
if segment, playlist = getSegment(playlist); segment == nil { if segment == nil {
r.playlist = nil
break break
} }
//log.Printf("[hls] load segment: %s", segment) //log.Printf("[hls] load segment: %s", segment)
ref, err2 := url.Parse(string(segment)) ref, err := url.Parse(string(segment))
if err2 != nil { if err != nil {
return nil, err2 return nil, err
} }
ref = r.request.URL.ResolveReference(ref) ref = r.request.URL.ResolveReference(ref)
if res, err2 = r.client.Get(ref.String()); err2 != nil { res, err := r.client.Get(ref.String())
return nil, err2 if err != nil {
return nil, err
} }
r.lastSegment = segment r.lastSegment = segment
@@ -124,23 +132,25 @@ func (r *reader) getSegment() ([]byte, error) {
return io.ReadAll(res.Body) return io.ReadAll(res.Body)
} }
} }
return nil, io.EOF
} }
func getSegment(src []byte) (segment, left []byte) { func getSegment(src []byte) []byte {
for ok := false; !ok; { for ok := false; !ok; {
ok = bytes.HasPrefix(src, []byte("#EXTINF")) ok = bytes.HasPrefix(src, []byte("#EXTINF"))
i := bytes.IndexByte(src, '\n') + 1 i := bytes.IndexByte(src, '\n') + 1
if i == 0 { if i == 0 {
return nil, nil return nil
} }
src = src[i:] src = src[i:]
} }
if i := bytes.IndexByte(src, '\n'); i > 0 { if i := bytes.IndexByte(src, '\n'); i > 0 {
return src[:i], src[i+1:] return src[:i]
} }
return src, nil return src
} }