-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmedia_playlist.go
140 lines (126 loc) · 3.57 KB
/
media_playlist.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
package m3u8
import (
"bufio"
"errors"
"fmt"
"io"
)
// ErrUnexpectedSegmentTags is returned when segment tags are found without a segment URI.
var ErrUnexpectedSegmentTags = errors.New("unexpected segment tags")
// MediaPlaylist represents a media playlist.
type MediaPlaylist struct {
// Tags is a list of tags in the media playlist.
// This list does not include segment tags.
Tags MediaPlaylistTags
// Segments is a list of segments in the media playlist.
Segments []*Segment
// EndList indicates that no more media segments will be added to the
// media playlist file in the future.
EndList bool
}
// Segment represents a media segment with its tags.
type Segment struct {
// Tags is a list of tags in the segment.
Tags SegmentTags
// URI is the URI of the segment.
URI string
// Sequence is the media sequence number of the segment.
// This field is set by DecodeMediaPlaylist.
// When encoding a media playlist, this field is ignored.
Sequence int64
// DiscontinuitySequence is the discontinuity sequence number of the segment.
// This field is set by DecodeMediaPlaylist.
// When encoding a media playlist, this field is ignored.
DiscontinuitySequence int64
}
// DecodeMediaPlaylist decodes a media playlist from io.Reader.
func DecodeMediaPlaylist(r io.Reader) (*MediaPlaylist, error) {
scanner := bufio.NewScanner(r)
var playlist MediaPlaylist
playlist.Tags = make(MediaPlaylistTags)
playlist.Segments = make([]*Segment, 0, 8)
segmentTags := make(SegmentTags)
for scanner.Scan() {
line := scanner.Text()
if line == "" {
continue
}
tagName := TagName(line)
if tagName == "" {
playlist.Segments = append(playlist.Segments, &Segment{
Tags: segmentTags,
URI: line,
})
segmentTags = make(SegmentTags)
} else if IsSegmentTagName(tagName) {
segmentTags.Raw().Add(&Tag{
Name: tagName,
Attributes: AttributeString(line),
})
} else if tagName == TagExtXEndlist {
playlist.EndList = true
} else {
playlist.Tags.Raw().Add(&Tag{
Name: tagName,
Attributes: AttributeString(line),
})
}
}
if err := scanner.Err(); err != nil {
return nil, err
}
sequence := playlist.Tags.MediaSequence()
discSequence := playlist.Tags.DiscontinuitySequence()
for _, segment := range playlist.Segments {
if _, exists := segment.Tags[TagExtXDiscontinuity]; exists {
discSequence++
}
segment.Sequence = sequence
segment.DiscontinuitySequence = discSequence
sequence++
}
if len(segmentTags) != 0 {
return &playlist, ErrUnexpectedSegmentTags
}
return &playlist, nil
}
// Encode encodes a media playlist to io.Writer.
func (playlist *MediaPlaylist) Encode(w io.Writer) error {
for _, tag := range playlist.Tags.Raw().List() {
err := tag.Encode(w)
if err != nil {
return err
}
}
for _, segment := range playlist.Segments {
for _, tag := range segment.Tags.Raw().List() {
err := tag.Encode(w)
if err != nil {
return err
}
}
if _, err := fmt.Fprintf(w, "%s\n", segment.URI); err != nil {
return err
}
}
if playlist.EndList {
if _, err := w.Write([]byte("#" + TagExtXEndlist + "\n")); err != nil {
return err
}
}
return nil
}
// Type returns the type of the playlist.
func (playlist *MediaPlaylist) Type() PlaylistType {
return PlaylistTypeMedia
}
// Master returns the master playlist.
// If the playlist is not a master playlist, it returns nil.
func (playlist *MediaPlaylist) Master() *MasterPlaylist {
return nil
}
// Media returns the media playlist.
// If the playlist is not a media playlist, it returns nil.
func (playlist *MediaPlaylist) Media() *MediaPlaylist {
return playlist
}