diff --git a/muxer_server.go b/muxer_server.go index 1a1a6e4..2f564f3 100644 --- a/muxer_server.go +++ b/muxer_server.go @@ -14,12 +14,12 @@ import ( "github.com/bluenviron/mediacommon/pkg/codecs/av1" "github.com/bluenviron/mediacommon/pkg/codecs/h264" "github.com/bluenviron/mediacommon/pkg/codecs/h265" + "github.com/bluenviron/mediacommon/pkg/formats/fmp4" + "github.com/bluenviron/mediacommon/pkg/formats/fmp4/seekablebuffer" "github.com/bluenviron/gohlslib/pkg/codecparams" "github.com/bluenviron/gohlslib/pkg/codecs" "github.com/bluenviron/gohlslib/pkg/playlist" - "github.com/bluenviron/mediacommon/pkg/formats/fmp4" - "github.com/bluenviron/mediacommon/pkg/formats/fmp4/seekablebuffer" ) func boolPtr(v bool) *bool { @@ -564,7 +564,7 @@ func (s *muxerServer) handleMediaPlaylist(msn string, part string, skip string, // exceeds the last Partial Segment in the current Playlist by the // Advance Part Limit, then the server SHOULD immediately return Bad // Request, such as HTTP 400. - if msnint > (s.nextSegmentID + 1) { + if msnint > (s.nextSegmentID+1) || msnint < (s.nextSegmentID-uint64(len(s.segments)-1)) { w.WriteHeader(http.StatusBadRequest) return nil, nil } diff --git a/muxer_test.go b/muxer_test.go index 912d494..b712f76 100644 --- a/muxer_test.go +++ b/muxer_test.go @@ -3,12 +3,14 @@ package gohlslib import ( "bytes" "fmt" + "io" "net/http" "net/url" "os" "path/filepath" "regexp" "strconv" + "strings" "testing" "time" @@ -1051,3 +1053,77 @@ func TestMuxerInvalidFolder(t *testing.T) { }) } } + +func TestMuxerExpiredSegment(t *testing.T) { + t.Parallel() + + testcases := []struct { + name string + variant MuxerVariant + }{ + { + name: "lowLatency", + variant: MuxerVariantLowLatency, + }, + } + + for _, tc := range testcases { + tc := tc + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + muxer := &Muxer{ + Variant: tc.variant, + SegmentCount: 7, + VideoTrack: testVideoTrack, + Directory: "/nonexisting", + } + err := muxer.Start() + require.NoError(t, err) + for i := 0; i < 9; i++ { + muxer.server.segments = append(muxer.server.segments, &mockSegmentMP4{}) + } + muxer.server.nextSegmentParts = append(muxer.server.nextSegmentParts, &muxerPart{}) + w := &dummyResponseWriter{ + h: make(http.Header), + } + v := url.Values{} + v.Set("_HLS_msn", "1") + v.Set("_HLS_part", "0") + + r := &http.Request{ + URL: &url.URL{ + Path: "stream.m3u8", + RawQuery: v.Encode(), + }, + } + muxer.Handle(w, r) + require.Equal(t, http.StatusBadRequest, w.statusCode) + }) + } +} + +type mockSegmentMP4 struct{} + +func (m mockSegmentMP4) close() { + // do nothing +} + +func (m mockSegmentMP4) getName() string { + return "mock" +} + +func (m mockSegmentMP4) getDuration() time.Duration { + return 100 * time.Millisecond +} + +func (m mockSegmentMP4) getSize() uint64 { + return 12345 +} + +func (m mockSegmentMP4) isForceSwitched() bool { + return false +} + +func (m mockSegmentMP4) reader() (io.ReadCloser, error) { + return io.NopCloser(strings.NewReader("mock")), nil +}