Skip to content

Commit

Permalink
Add Alternatives struct
Browse files Browse the repository at this point in the history
  • Loading branch information
sunfish-shogi committed Dec 18, 2024
1 parent e76eabf commit 6144826
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 34 deletions.
90 changes: 78 additions & 12 deletions master_playlist.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ type MasterPlaylist struct {
// Streams is a list of variant streams.
Streams []*Stream

// Alternatives is a list of alternative renditions.
Alternatives map[string][]*Alternative
// Alternatives contains alternative renditions.
Alternatives Alternatives

// IFrameStreams is a list of I-frame streams.
IFrameStreams []*Stream
Expand All @@ -33,6 +33,13 @@ type Stream struct {
URI string
}

type Alternatives struct {
Video map[string][]*Alternative
Audio map[string][]*Alternative
Subtitles map[string][]*Alternative
ClosedCaptions map[string][]*Alternative
}

type Alternative struct {
// Attributes is a list of attributes in the alternative.
Attributes MediaAttrs
Expand All @@ -44,7 +51,12 @@ func DecodeMasterPlaylist(r io.Reader) (*MasterPlaylist, error) {
var playlist MasterPlaylist
playlist.Tags = make(Tags)
playlist.Streams = make([]*Stream, 0)
playlist.Alternatives = make(map[string][]*Alternative)
playlist.Alternatives = Alternatives{
Video: make(map[string][]*Alternative),
Audio: make(map[string][]*Alternative),
Subtitles: make(map[string][]*Alternative),
ClosedCaptions: make(map[string][]*Alternative),
}
var streamInfAttrs StreamInfAttrs
for scanner.Scan() {
line := scanner.Text()
Expand Down Expand Up @@ -82,17 +94,45 @@ func DecodeMasterPlaylist(r io.Reader) (*MasterPlaylist, error) {
if err != nil {
return nil, err
}
groupID, ok := attrs["GROUP-ID"]
if !ok {
groupID := strings.Trim(attrs["GROUP-ID"], `"`)
if groupID == "" {
return nil, errors.New("missing GROUP-ID")
}
typ := attrs["TYPE"]
delete(attrs, "GROUP-ID")
if _, ok := playlist.Alternatives[groupID]; !ok {
playlist.Alternatives[groupID] = make([]*Alternative, 0)
delete(attrs, "TYPE")
switch MediaType(typ) {
case MediaTypeVideo:
if _, ok := playlist.Alternatives.Video[groupID]; !ok {
playlist.Alternatives.Video[groupID] = make([]*Alternative, 0)
}
playlist.Alternatives.Video[groupID] = append(playlist.Alternatives.Video[groupID], &Alternative{
Attributes: MediaAttrs(attrs),
})
case MediaTypeAudio:
if _, ok := playlist.Alternatives.Audio[groupID]; !ok {
playlist.Alternatives.Audio[groupID] = make([]*Alternative, 0)
}
playlist.Alternatives.Audio[groupID] = append(playlist.Alternatives.Audio[groupID], &Alternative{
Attributes: MediaAttrs(attrs),
})
case MediaTypeSubtitles:
if _, ok := playlist.Alternatives.Subtitles[groupID]; !ok {
playlist.Alternatives.Subtitles[groupID] = make([]*Alternative, 0)
}
playlist.Alternatives.Subtitles[groupID] = append(playlist.Alternatives.Subtitles[groupID], &Alternative{
Attributes: MediaAttrs(attrs),
})
case MediaTypeClosedCaptions:
if _, ok := playlist.Alternatives.ClosedCaptions[groupID]; !ok {
playlist.Alternatives.ClosedCaptions[groupID] = make([]*Alternative, 0)
}
playlist.Alternatives.ClosedCaptions[groupID] = append(playlist.Alternatives.ClosedCaptions[groupID], &Alternative{
Attributes: MediaAttrs(attrs),
})
default:
return nil, errors.New("invalid TYPE")
}
playlist.Alternatives[groupID] = append(playlist.Alternatives[groupID], &Alternative{
Attributes: MediaAttrs(attrs),
})
} else if tagName != "" {
playlist.Tags.Add(&Tag{
Name: tagName,
Expand All @@ -111,9 +151,30 @@ func (playlist *MasterPlaylist) Encode(w io.Writer) error {
return err
}
}
for groupID, alternatives := range playlist.Alternatives {
for groupID, alternatives := range playlist.Alternatives.Video {
for _, alt := range alternatives {
if _, err := fmt.Fprintf(w, "#%s:GROUP-ID=%s,%s\n", TagExtXMedia, groupID, Attributes(alt.Attributes).String()); err != nil {
if err := encodeExtXMedia(w, MediaTypeVideo, groupID, alt.Attributes); err != nil {
return err
}
}
}
for groupID, alternatives := range playlist.Alternatives.Audio {
for _, alt := range alternatives {
if err := encodeExtXMedia(w, MediaTypeAudio, groupID, alt.Attributes); err != nil {
return err
}
}
}
for groupID, alternatives := range playlist.Alternatives.Subtitles {
for _, alt := range alternatives {
if err := encodeExtXMedia(w, MediaTypeSubtitles, groupID, alt.Attributes); err != nil {
return err
}
}
}
for groupID, alternatives := range playlist.Alternatives.ClosedCaptions {
for _, alt := range alternatives {
if err := encodeExtXMedia(w, MediaTypeClosedCaptions, groupID, alt.Attributes); err != nil {
return err
}
}
Expand All @@ -134,6 +195,11 @@ func (playlist *MasterPlaylist) Encode(w io.Writer) error {
return nil
}

func encodeExtXMedia(w io.Writer, typ MediaType, groupID string, attrs MediaAttrs) error {
_, err := fmt.Fprintf(w, "#%s:TYPE=%s,GROUP-ID=\"%s\",%s\n", TagExtXMedia, typ, groupID, Attributes(attrs).String())
return err
}

// Type returns the type of the playlist.
func (playlist *MasterPlaylist) Type() PlaylistType {
return PlaylistTypeMaster
Expand Down
20 changes: 0 additions & 20 deletions master_playlist_tags.go
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,6 @@ func (attrs StreamInfAttrs) SetClosedCaptions(closedCaptions string) {
// MediaAttrs represents the attributes of the EXT-X-MEDIA tag.
type MediaAttrs Attributes

// Type returns the type of the media.
func (attrs MediaAttrs) Type() MediaType {
return MediaType(attrs["TYPE"])
}

// SetType sets the type of the media.
func (attrs MediaAttrs) SetType(mediaType MediaType) {
attrs["TYPE"] = string(mediaType)
}

// URI returns the URI of the media.
func (attrs MediaAttrs) URI() string {
return strings.Trim(attrs["URI"], `"`)
Expand All @@ -144,16 +134,6 @@ func (attrs MediaAttrs) SetURI(uri string) {
attrs["URI"] = `"` + uri + `"`
}

// GroupID returns the group ID of the media.
func (attrs MediaAttrs) GroupID() string {
return strings.Trim(attrs["GROUP-ID"], `"`)
}

// SetGroupID sets the group ID of the media.
func (attrs MediaAttrs) SetGroupID(groupID string) {
attrs["GROUP-ID"] = `"` + groupID + `"`
}

// Language returns the language of the media.
func (attrs MediaAttrs) Language() string {
return strings.Trim(attrs["LANGUAGE"], `"`)
Expand Down
4 changes: 2 additions & 2 deletions master_playlist_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ high-video.m3u8
`

const sampleAlternativeStreamOutput = `#EXTM3U
#EXT-X-MEDIA:GROUP-ID="aac",AUTOSELECT=YES,DEFAULT=YES,LANGUAGE="en",NAME="English",TYPE=AUDIO,URI="main/english-audio.m3u8"
#EXT-X-MEDIA:GROUP-ID="aac",AUTOSELECT=YES,DEFAULT=NO,LANGUAGE="de",NAME="Deutsch",TYPE=AUDIO,URI="main/german-audio.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",AUTOSELECT=YES,DEFAULT=YES,LANGUAGE="en",NAME="English",URI="main/english-audio.m3u8"
#EXT-X-MEDIA:TYPE=AUDIO,GROUP-ID="aac",AUTOSELECT=YES,DEFAULT=NO,LANGUAGE="de",NAME="Deutsch",URI="main/german-audio.m3u8"
#EXT-X-STREAM-INF:AUDIO="aac",BANDWIDTH=1280000,CODECS="avc1.4d401e"
low-video.m3u8
#EXT-X-STREAM-INF:AUDIO="aac",BANDWIDTH=2560000,CODECS="avc1.4d401e"
Expand Down

0 comments on commit 6144826

Please sign in to comment.