diff --git a/README.md b/README.md index 089cd05b..01bf0c8c 100644 --- a/README.md +++ b/README.md @@ -326,10 +326,10 @@ http://localhost:8888/mystream where `mystream` is the name of a stream that is being published. -The direct HLS URL, that can be used to read the stream with Javascript libraries (hls.js) can be obtained by appending `/stream.m3u8`: +The direct HLS URL, that can be used to read the stream with players (VLC) or Javascript libraries (hls.js) can be obtained by appending `/index.m3u8`: ``` -http://localhost:8888/mystream/stream.m3u8 +http://localhost:8888/mystream/index.m3u8 ``` Please note that most browsers don't support HLS directly (except Safari); a Javascript library, like [hls.js](https://github.com/video-dev/hls.js), must be used to load the stream. diff --git a/internal/core/hls_remuxer.go b/internal/core/hls_remuxer.go index f956180a..dd64176b 100644 --- a/internal/core/hls_remuxer.go +++ b/internal/core/hls_remuxer.go @@ -57,7 +57,7 @@ const create = () => { const video = document.getElementById('video'); if (video.canPlayType('application/vnd.apple.mpegurl')) { - video.src = 'stream.m3u8'; + video.src = 'index.m3u8'; video.play(); } else { const hls = new Hls({ @@ -74,7 +74,7 @@ const create = () => { } }); - hls.loadSource('stream.m3u8'); + hls.loadSource('index.m3u8'); hls.attachMedia(video); video.play(); @@ -434,9 +434,13 @@ func (r *hlsRemuxer) handleRequest(req hlsRemuxerRequest) { } switch { + case req.File == "index.m3u8": + req.W.Header().Set("Content-Type", `application/x-mpegURL`) + req.Res <- r.muxer.PrimaryPlaylist() + case req.File == "stream.m3u8": req.W.Header().Set("Content-Type", `application/x-mpegURL`) - req.Res <- r.muxer.Playlist() + req.Res <- r.muxer.StreamPlaylist() case strings.HasSuffix(req.File, ".ts"): r := r.muxer.TSFile(req.File) diff --git a/internal/core/hls_server_test.go b/internal/core/hls_server_test.go index 27d9c326..9821bcbd 100644 --- a/internal/core/hls_server_test.go +++ b/internal/core/hls_server_test.go @@ -41,7 +41,7 @@ func TestHLSServerRead(t *testing.T) { time.Sleep(1 * time.Second) cnt2, err := newContainer("ffmpeg", "dest", []string{ - "-i", "http://localhost:8888/test/stream/stream.m3u8", + "-i", "http://localhost:8888/test/stream/index.m3u8", "-vframes", "1", "-f", "image2", "-y", "/dev/null", @@ -75,7 +75,7 @@ func TestHLSServerReadAuth(t *testing.T) { time.Sleep(1 * time.Second) cnt2, err := newContainer("ffmpeg", "dest", []string{ - "-i", "http://testuser:testpass@127.0.0.1:8888/teststream/stream.m3u8", + "-i", "http://testuser:testpass@127.0.0.1:8888/teststream/index.m3u8", "-vframes", "1", "-f", "image2", "-y", "/dev/null", diff --git a/internal/hls/muxer.go b/internal/hls/muxer.go index eefd8e42..27b81d7b 100644 --- a/internal/hls/muxer.go +++ b/internal/hls/muxer.go @@ -2,6 +2,7 @@ package hls import ( "bytes" + "encoding/hex" "io" "math" "strconv" @@ -208,15 +209,34 @@ func (m *Muxer) WriteAAC(pts time.Duration, aus [][]byte) error { return nil } -// Playlist returns a reader to read the playlist. -func (m *Muxer) Playlist() io.Reader { - m.mutex.RLock() - defer m.mutex.RUnlock() +// PrimaryPlaylist returns a reader to read the primary playlist +func (m *Muxer) PrimaryPlaylist() io.Reader { + var codecs []string + if m.videoTrack != nil { + codecs = append(codecs, "avc1."+hex.EncodeToString(m.h264SPS[1:4])) + } + + if m.audioTrack != nil { + codecs = append(codecs, "mp4a.40.2") + } + + cnt := "#EXTM3U\n" + cnt += "#EXT-X-STREAM-INF:BANDWIDTH=200000,CODECS=\"" + strings.Join(codecs, ",") + "\"\n" + cnt += "stream.m3u8\n" + + return bytes.NewReader([]byte(cnt)) +} + +// StreamPlaylist returns a reader to read the stream playlist. +func (m *Muxer) StreamPlaylist() io.Reader { cnt := "#EXTM3U\n" cnt += "#EXT-X-VERSION:3\n" cnt += "#EXT-X-ALLOW-CACHE:NO\n" + m.mutex.RLock() + defer m.mutex.RUnlock() + targetDuration := func() uint { ret := uint(math.Ceil(m.hlsSegmentDuration.Seconds())) diff --git a/internal/hls/muxer_test.go b/internal/hls/muxer_test.go index edcc98a7..2241e36e 100644 --- a/internal/hls/muxer_test.go +++ b/internal/hls/muxer_test.go @@ -64,7 +64,14 @@ func TestMuxer(t *testing.T) { }) require.NoError(t, err) - byts, err := ioutil.ReadAll(m.Playlist()) + byts, err := ioutil.ReadAll(m.PrimaryPlaylist()) + require.NoError(t, err) + + require.Equal(t, "#EXTM3U\n"+ + "#EXT-X-STREAM-INF:BANDWIDTH=200000,CODECS=\"avc1.010203,mp4a.40.2\"\n"+ + "stream.m3u8\n", string(byts)) + + byts, err = ioutil.ReadAll(m.StreamPlaylist()) require.NoError(t, err) re := regexp.MustCompile(`^#EXTM3U\n` +