diff --git a/weed/format/hlsts/hlsts.go b/weed/format/hlsts/hlsts.go index 55b64fbdb..f6d3b60f9 100644 --- a/weed/format/hlsts/hlsts.go +++ b/weed/format/hlsts/hlsts.go @@ -67,8 +67,9 @@ func decodePlaylistInfo(payload []byte, extentCount int) (*playlistInfo, error) if err != nil || target == 0 || target > math.MaxInt32 { return nil, fmt.Errorf("invalid hls-ts target duration") } + // mirror the ingest bound: the last segment number is sequence+count-1 sequence, err := binary.ReadUvarint(reader) - if err != nil || sequence > math.MaxInt64-uint64(extentCount) { + if err != nil || sequence > math.MaxInt64-uint64(extentCount-1) { return nil, fmt.Errorf("invalid hls-ts media sequence") } info := &playlistInfo{TargetDuration: int64(target), MediaSequence: int64(sequence), DurationsMs: make([]int64, extentCount)} diff --git a/weed/format/hlsts/hlsts_test.go b/weed/format/hlsts/hlsts_test.go index dfa2cf794..7302863a2 100644 --- a/weed/format/hlsts/hlsts_test.go +++ b/weed/format/hlsts/hlsts_test.go @@ -168,6 +168,28 @@ func TestViewSegmentHonorsMediaSequence(t *testing.T) { } } +// The ingest bound admits mediaSequence = MaxInt64-(count-1); the payload +// decoder must accept the same boundary or every view of such an asset fails. +func TestMediaSequenceBoundaryRoundTrip(t *testing.T) { + playlist := "#EXTM3U\n#EXT-X-MEDIA-SEQUENCE:9223372036854775806\n" + + "#EXTINF:6,\n#EXT-X-BYTERANGE:188@0\nv.ts\n#EXTINF:6,\n#EXT-X-BYTERANGE:188\nv.ts\n#EXT-X-ENDLIST\n" + layout, err := Adapter{}.IndexSidecar([]byte(playlist)) + if err != nil { + t.Fatalf("IndexSidecar() error = %v", err) + } + obj := format.Object{Name: "v.ts", Size: layout.TotalSize(), Layout: layout} + if _, err := (Adapter{}).View(format.ViewRequest{Query: url.Values{}}, obj); err != nil { + t.Fatalf("playlist view error = %v", err) + } + plan, err := Adapter{}.View(format.ViewRequest{Query: url.Values{"seq": {"9223372036854775807"}}}, obj) + if err != nil { + t.Fatalf("last segment view error = %v", err) + } + if plan.Extent != 1 { + t.Fatalf("Extent = %d, want 1", plan.Extent) + } +} + func TestSniff(t *testing.T) { head := make([]byte, 400) head[0], head[TSPacketSize] = tsSyncByte, tsSyncByte