From b65bcd4afaa1ec2903a359f7c6b135a7e1d0319d Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 10 Aug 2026 17:22:54 -0700 Subject: [PATCH] format: fix the hls-ts media-sequence decode bound Ingest admits mediaSequence up to MaxInt64-(count-1), but the payload decoder rejected anything above MaxInt64-count, so a boundary playlist ingested successfully and then failed every view. Mirror the ingest bound, covered by a round-trip test at the boundary. --- weed/format/hlsts/hlsts.go | 3 ++- weed/format/hlsts/hlsts_test.go | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) 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