Skip to content
This repository has been archived by the owner on Dec 8, 2024. It is now read-only.

Parse more tags from stream inf #112

Merged
merged 2 commits into from
Jan 18, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,6 +299,17 @@ func decodeLineOfMasterPlaylist(p *MasterPlaylist, state *decodingState, line st
state.variant.Captions = v
case "NAME":
state.variant.Name = v
case "AVERAGE-BANDWIDTH":
var val int
val, err = strconv.Atoi(v)
if strict && err != nil {
return err
}
state.variant.AverageBandwidth = uint32(val)
case "FRAME-RATE":
if state.variant.FrameRate, err = strconv.ParseFloat(v, 64); strict && err != nil {
return err
}
}
}
case state.tagStreamInf && !strings.HasPrefix(line, "#"):
Expand Down Expand Up @@ -472,6 +483,11 @@ func decodeLineOfMediaPlaylist(p *MediaPlaylist, wv *WV, state *decodingState, l
p.MediaType = VOD
}
}
case strings.HasPrefix(line, "#EXT-X-DISCONTINUITY-SEQUENCE:"):
state.listType = MEDIA
if _, err = fmt.Sscanf(line, "#EXT-X-DISCONTINUITY-SEQUENCE:%d", &p.DiscontinuitySeq); strict && err != nil {
return err
}
case strings.HasPrefix(line, "#EXT-X-KEY:"):
state.listType = MEDIA
state.xkey = new(Key)
Expand Down
53 changes: 53 additions & 0 deletions reader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,40 @@ func TestDecodeMasterPlaylistWithIFrameStreamInf(t *testing.T) {
}
}

func TestDecodeMasterPlaylistWithStreamInfAverageBandwidth(t *testing.T) {
f, err := os.Open("sample-playlists/master-with-stream-inf-1.m3u8")
if err != nil {
t.Fatal(err)
}
p := NewMasterPlaylist()
err = p.DecodeFrom(bufio.NewReader(f), false)
if err != nil {
t.Fatal(err)
}
for _, variant := range p.Variants {
if variant.AverageBandwidth == 0 {
t.Errorf("Empty average bandwidth tag on variant URI: %s", variant.URI)
}
}
}

func TestDecodeMasterPlaylistWithStreamInfFrameRate(t *testing.T) {
f, err := os.Open("sample-playlists/master-with-stream-inf-1.m3u8")
if err != nil {
t.Fatal(err)
}
p := NewMasterPlaylist()
err = p.DecodeFrom(bufio.NewReader(f), false)
if err != nil {
t.Fatal(err)
}
for _, variant := range p.Variants {
if variant.FrameRate == 0 {
t.Errorf("Empty frame rate tag on variant URI: %s", variant.URI)
}
}
}

func TestDecodeMediaPlaylist(t *testing.T) {
f, err := os.Open("sample-playlists/wowza-vod-chunklist.m3u8")
if err != nil {
Expand Down Expand Up @@ -446,6 +480,25 @@ func TestMediaPlaylistWithOATCLSSCTE35Tag(t *testing.T) {
}
}

func TestDecodeMediaPlaylistWithDiscontinuitySeq(t *testing.T) {
f, err := os.Open("sample-playlists/media-playlist-with-discontinuity-seq.m3u8")
if err != nil {
t.Fatal(err)
}
p, listType, err := DecodeFrom(bufio.NewReader(f), true)
if err != nil {
t.Fatal(err)
}
pp := p.(*MediaPlaylist)
CheckType(t, pp)
if listType != MEDIA {
t.Error("Sample not recognized as media playlist.")
}
if pp.DiscontinuitySeq == 0 {
t.Error("Empty discontinuity sequenece tag")
}
}

/***************************
* Code parsing examples *
***************************/
Expand Down
12 changes: 12 additions & 0 deletions sample-playlists/master-with-stream-inf-1.m3u8
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=300000,AVERAGE-BANDWIDTH=300000,CODECS="avc1.42c015,mp4a.40.2",FRAME-RATE=25.000
chunklist-b300000.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=600000,AVERAGE-BANDWIDTH=600000,CODECS="avc1.42c015,mp4a.40.2",FRAME-RATE=25.000
chunklist-b600000.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=850000,AVERAGE-BANDWIDTH=850000,CODECS="avc1.42c015,mp4a.40.2",FRAME-RATE=25.000
chunklist-b850000.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1000000,AVERAGE-BANDWIDTH=1000000,CODECS="avc1.42c015,mp4a.40.2",FRAME-RATE=25.000
chunklist-b1000000.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1500000,AVERAGE-BANDWIDTH=1500000,CODECS="avc1.42c015,mp4a.40.2",FRAME-RATE=25.000
chunklist-b1500000.m3u8
14 changes: 14 additions & 0 deletions sample-playlists/media-playlist-with-discontinuity-seq.m3u8
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#EXTM3U
#EXT-X-TARGETDURATION:10
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:0
#EXT-X-DISCONTINUITY-SEQUENCE:2
#EXTINF:10.0,
ad0.ts
#EXTINF:8.0,
ad1.ts
#EXT-X-DISCONTINUITY
#EXTINF:10.0,
movieA.ts
#EXTINF:10.0,
movieB.ts
63 changes: 33 additions & 30 deletions structure.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,25 +102,26 @@ const (
https://priv.example.com/fileSequence2682.ts
*/
type MediaPlaylist struct {
TargetDuration float64
SeqNo uint64 // EXT-X-MEDIA-SEQUENCE
Segments []*MediaSegment
Args string // optional arguments placed after URIs (URI?Args)
Iframe bool // EXT-X-I-FRAMES-ONLY
Closed bool // is this VOD (closed) or Live (sliding) playlist?
MediaType MediaType
durationAsInt bool // output durations as integers of floats?
keyformat int
winsize uint // max number of segments displayed in an encoded playlist; need set to zero for VOD playlists
capacity uint // total capacity of slice used for the playlist
head uint // head of FIFO, we add segments to head
tail uint // tail of FIFO, we remove segments from tail
count uint // number of segments added to the playlist
buf bytes.Buffer
ver uint8
Key *Key // EXT-X-KEY is optional encryption key displayed before any segments (default key for the playlist)
Map *Map // EXT-X-MAP is optional tag specifies how to obtain the Media Initialization Section (default map for the playlist)
WV *WV // Widevine related tags outside of M3U8 specs
TargetDuration float64
SeqNo uint64 // EXT-X-MEDIA-SEQUENCE
Segments []*MediaSegment
Args string // optional arguments placed after URIs (URI?Args)
Iframe bool // EXT-X-I-FRAMES-ONLY
Closed bool // is this VOD (closed) or Live (sliding) playlist?
MediaType MediaType
DiscontinuitySeq uint64 // EXT-X-DISCONTINUITY-SEQUENCE
durationAsInt bool // output durations as integers of floats?
keyformat int
winsize uint // max number of segments displayed in an encoded playlist; need set to zero for VOD playlists
capacity uint // total capacity of slice used for the playlist
head uint // head of FIFO, we add segments to head
tail uint // tail of FIFO, we remove segments from tail
count uint // number of segments added to the playlist
buf bytes.Buffer
ver uint8
Key *Key // EXT-X-KEY is optional encryption key displayed before any segments (default key for the playlist)
Map *Map // EXT-X-MAP is optional tag specifies how to obtain the Media Initialization Section (default map for the playlist)
WV *WV // Widevine related tags outside of M3U8 specs
}

/*
Expand Down Expand Up @@ -157,17 +158,19 @@ type Variant struct {
// This structure represents additional parameters for a variant
// used in EXT-X-STREAM-INF and EXT-X-I-FRAME-STREAM-INF
type VariantParams struct {
ProgramId uint32
Bandwidth uint32
Codecs string
Resolution string
Audio string // EXT-X-STREAM-INF only
Video string
Subtitles string // EXT-X-STREAM-INF only
Captions string // EXT-X-STREAM-INF only
Name string // EXT-X-STREAM-INF only (non standard Wowza/JWPlayer extension to name the variant/quality in UA)
Iframe bool // EXT-X-I-FRAME-STREAM-INF
Alternatives []*Alternative // EXT-X-MEDIA
ProgramId uint32
Bandwidth uint32
AverageBandwidth uint32 // EXT-X-STREAM-INF only
Codecs string
Resolution string
Audio string // EXT-X-STREAM-INF only
Video string
Subtitles string // EXT-X-STREAM-INF only
Captions string // EXT-X-STREAM-INF only
Name string // EXT-X-STREAM-INF only (non standard Wowza/JWPlayer extension to name the variant/quality in UA)
FrameRate float64 // EXT-X-STREAM-INF
Iframe bool // EXT-X-I-FRAME-STREAM-INF
Alternatives []*Alternative // EXT-X-MEDIA
}

// This structure represents EXT-X-MEDIA tag in variants.
Expand Down
12 changes: 12 additions & 0 deletions writer.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,10 @@ func (p *MasterPlaylist) Encode() *bytes.Buffer {
p.buf.WriteString(strconv.FormatUint(uint64(pl.ProgramId), 10))
p.buf.WriteString(",BANDWIDTH=")
p.buf.WriteString(strconv.FormatUint(uint64(pl.Bandwidth), 10))
if pl.AverageBandwidth != 0 {
p.buf.WriteString(",AVERAGE-BANDWIDTH=")
p.buf.WriteString(strconv.FormatUint(uint64(pl.Bandwidth), 10))
}
if pl.Codecs != "" {
p.buf.WriteString(",CODECS=\"")
p.buf.WriteString(pl.Codecs)
Expand Down Expand Up @@ -213,6 +217,10 @@ func (p *MasterPlaylist) Encode() *bytes.Buffer {
p.buf.WriteString(pl.Name)
p.buf.WriteRune('"')
}
if pl.FrameRate != 0 {
p.buf.WriteString(",FRAME-RATE=")
p.buf.WriteString(strconv.FormatFloat(pl.FrameRate, 'f', 3, 64))
}
p.buf.WriteRune('\n')
p.buf.WriteString(pl.URI)
if p.Args != "" {
Expand Down Expand Up @@ -392,6 +400,10 @@ func (p *MediaPlaylist) Encode() *bytes.Buffer {
p.buf.WriteString("#EXT-X-TARGETDURATION:")
p.buf.WriteString(strconv.FormatInt(int64(math.Ceil(p.TargetDuration)), 10)) // due section 3.4.2 of M3U8 specs EXT-X-TARGETDURATION must be integer
p.buf.WriteRune('\n')
if p.DiscontinuitySeq != 0 {
p.buf.WriteString("#EXT-X-DISCONTINUITY-SEQUENCE:")
p.buf.WriteString(strconv.FormatUint(uint64(p.DiscontinuitySeq), 10))
}
if p.Iframe {
p.buf.WriteString("#EXT-X-I-FRAMES-ONLY\n")
}
Expand Down
8 changes: 4 additions & 4 deletions writer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -844,15 +844,15 @@ func ExampleMasterPlaylist_String() {
for i := 0; i < 5; i++ {
p.Append(fmt.Sprintf("test%d.ts", i), 5.0, "")
}
m.Append("chunklist1.m3u8", p, VariantParams{ProgramId: 123, Bandwidth: 1500000, Resolution: "576x480"})
m.Append("chunklist2.m3u8", p, VariantParams{ProgramId: 123, Bandwidth: 1500000, Resolution: "576x480"})
m.Append("chunklist1.m3u8", p, VariantParams{ProgramId: 123, Bandwidth: 1500000, AverageBandwidth: 1500000, Resolution: "576x480", FrameRate: 25.000})
m.Append("chunklist2.m3u8", p, VariantParams{ProgramId: 123, Bandwidth: 1500000, AverageBandwidth: 1500000, Resolution: "576x480", FrameRate: 25.000})
fmt.Printf("%s", m)
// Output:
// #EXTM3U
// #EXT-X-VERSION:3
// #EXT-X-STREAM-INF:PROGRAM-ID=123,BANDWIDTH=1500000,RESOLUTION=576x480
// #EXT-X-STREAM-INF:PROGRAM-ID=123,BANDWIDTH=1500000,AVERAGE-BANDWIDTH=1500000,RESOLUTION=576x480,FRAME-RATE=25.000
// chunklist1.m3u8
// #EXT-X-STREAM-INF:PROGRAM-ID=123,BANDWIDTH=1500000,RESOLUTION=576x480
// #EXT-X-STREAM-INF:PROGRAM-ID=123,BANDWIDTH=1500000,AVERAGE-BANDWIDTH=1500000,RESOLUTION=576x480,FRAME-RATE=25.000
// chunklist2.m3u8
}

Expand Down