segmento/internal/playlist/playlist.go

121 lines
2.3 KiB
Go

package playlist
import (
"fmt"
"log"
"time"
segmentpkg "segmento/internal/segment"
)
const DefaultPlaylistDuration = 20 * time.Second
type segment struct {
segmentpkg.Segment
seqId int
}
type PlaylistListener interface {
OnUpdate(p Playlist)
}
type Playlist interface {
Duration() time.Duration
TargetDuration() time.Duration
Render() string
ReadSegments(chan *segmentpkg.Segment) error
AddListener(l PlaylistListener)
}
type MediaPlaylist struct {
Segments []*segment
Listeners []PlaylistListener
seqId int
}
func NewMediaPlaylist() *MediaPlaylist {
return &MediaPlaylist{
Segments: make([]*segment, 0, 10),
Listeners: make([]PlaylistListener, 0),
}
}
func (p *MediaPlaylist) nextSeqId() int {
next := p.seqId
p.seqId++
return next
}
func (p *MediaPlaylist) Duration() time.Duration {
return p.durationOf(p.Segments)
}
func (p *MediaPlaylist) TargetDuration() time.Duration {
return DefaultPlaylistDuration
}
// pass by pointer or value here?
// I think value is better, the struct is tiny and only contains a pointer
// to the data buffer
func (p *MediaPlaylist) AddSegment(s *segmentpkg.Segment) error {
nextId := p.nextSeqId()
ss := segment{*s, nextId}
p.Segments = append(p.Segments, &ss)
if len(p.Segments) > 1 {
for {
if p.durationOf(p.Segments[1:]) > p.TargetDuration() {
p.Segments = p.Segments[1:]
}
break
}
}
p.updateListeners()
return nil
}
func (p *MediaPlaylist) durationOf(ss []*segment) time.Duration {
var t time.Duration
for _, s := range ss {
t += s.Duration()
}
return t
}
func (p *MediaPlaylist) Render() string {
var r string
r += "#EXTM3U\n"
r += "#EXT-X-VERSION:3\n"
r += "#EXT-X-TARGETDURATION:3\n" // TODO
for _, s := range p.Segments {
r += fmt.Sprintf("#EXTINF:%.05f\n", float32(s.Duration())/float32(time.Second))
r += "http://www.example.com/x.mp3\n"
}
r += "#EXT-X-ENDLIST"
return r
}
// Listeners
func (p *MediaPlaylist) AddListener(l PlaylistListener) {
p.Listeners = append(p.Listeners, l)
}
func (p *MediaPlaylist) updateListeners() {
for _, l := range p.Listeners {
l.OnUpdate(p)
}
}
func (p *MediaPlaylist) ReadSegments(segments chan *segmentpkg.Segment) error {
for s := range segments {
log.Println("got segment with duration", s.Duration(), "and len", s.Len(), "bytes")
p.AddSegment(s)
}
log.Println("exiting ReadSegments")
return nil
}