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 }