streaming

Media streaming and broadcast systems in Go
Log | Files | Refs | README | LICENSE

commit 2be3531b0dcadd5c29adc1fff94844876de00afe
parent b3f3a7ccc95f08e87b4df8d758c035e4ef5e0fc5
Author: Luiz Henrique Rapatao <rapatao@rapatao.com>
Date:   Sun, 20 Apr 2025 21:56:47 +0100

m3u8: add support for parsing media sequence from playlist

Diffstat:
Mm3u8/parse.go | 18++++++++++++++++++
Mm3u8/parse_test.go | 15+++++++++++++++
Am3u8/testdata/sequence.m3u8 | 11+++++++++++
3 files changed, 44 insertions(+), 0 deletions(-)

diff --git a/m3u8/parse.go b/m3u8/parse.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "io" + "regexp" "strconv" "strings" "time" @@ -85,6 +86,12 @@ func Decode(rd io.Reader) (*Playlist, error) { p.Segments = append(p.Segments, *segment) case tagEndList: p.End = true + case tagMediaSequence: + val, err := parseSequence(lex) + if err != nil { + return p, fmt.Errorf("parse media sequence: %w", err) + } + p.Sequence = val } } } @@ -381,3 +388,14 @@ func parseByteRange(s string) (ByteRange, error) { } return ByteRange{n, nn}, nil } + +func parseSequence(lex *lexer) (int, error) { + re := regexp.MustCompile(`#EXT-X-MEDIA-SEQUENCE:(\d+)`) + + match := re.FindStringSubmatch(lex.input) + if len(match) > 1 { + return strconv.Atoi(match[1]) + } + + return 0, fmt.Errorf("invalid sequence number") +} diff --git a/m3u8/parse_test.go b/m3u8/parse_test.go @@ -135,3 +135,18 @@ func TestFrameRate(t *testing.T) { } } } + +func TestParseSequence(t *testing.T) { + f, err := os.Open("testdata/sequence.m3u8") + if err != nil { + t.Fatal(err) + } + defer f.Close() + plist, err := Decode(f) + if err != nil { + t.Fatal(err) + } + if plist.Sequence != 91240 { + t.Errorf("want %d, got %d", 91240, plist.Sequence) + } +} diff --git a/m3u8/testdata/sequence.m3u8 b/m3u8/testdata/sequence.m3u8 @@ -0,0 +1,11 @@ +#EXTM3U +#EXT-X-VERSION:3 +#EXT-X-TARGETDURATION:3 +#EXT-X-MEDIA-SEQUENCE:91240 +#EXT-X-DISCONTINUITY-SEQUENCE:0 +#EXTINF:2.002, +media-seg_91240.ts +#EXTINF:2.002, +media-seg_91241.ts +#EXTINF:2.002, +media-seg_91242.ts