hls

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 23, 2021 License: BSD-3-Clause Imports: 10 Imported by: 0

README

hls

go get github.com/as/hls/...

func TestDecodeMaster(t *testing.T) {
	sample := `
	#EXTM3U
	#EXT-X-VERSION:3
	#EXT-X-INDEPENDENT-SEGMENTS
	#EXT-X-STREAM-INF:BANDWIDTH=1111,AVERAGE-BANDWIDTH=1000,RESOLUTION=1x1,FRAME-RATE=29.970,CODECS="avc1.4D401F,mp4a.40.2"
	m1.m3u8
	#EXT-X-STREAM-INF:BANDWIDTH=2222,AVERAGE-BANDWIDTH=2000,RESOLUTION=2x2,FRAME-RATE=29.970,CODECS="avc1.4D401F,mp4a.40.2"
	m2.m3u8
	#EXT-X-STREAM-INF:BANDWIDTH=3333,AVERAGE-BANDWIDTH=3000,RESOLUTION=3x3,FRAME-RATE=29.970,CODECS="avc1.4D401F,mp4a.40.2"
	m3.m3u8
	#EXT-X-STREAM-INF:BANDWIDTH=4444,AVERAGE-BANDWIDTH=4000,RESOLUTION=4x4,FRAME-RATE=29.970,CODECS="avc1.4D401E,mp4a.40.2"
	m4.m3u8
	#EXT-X-STREAM-INF:BANDWIDTH=5555,AVERAGE-BANDWIDTH=5000,RESOLUTION=5x5,FRAME-RATE=29.970,CODECS="avc1.4D401E,mp4a.40.2"
	m5.m3u8
	#EXT-X-STREAM-INF:BANDWIDTH=6666,AVERAGE-BANDWIDTH=6000,RESOLUTION=6x6,FRAME-RATE=29.970,CODECS="avc1.4D400D,mp4a.40.2"
	m6.m3u8
	`
	want := Master{
		Version:     3,
		Independent: true,
		Stream: []StreamInfo{
			{URL: "m1.m3u8", Bandwidth: 1111, BandwidthAvg: 1000, Resolution: image.Pt(1, 1), Codecs: []string{"avc1.4D401F", "mp4a.40.2"}, Framerate: 29.97},
			{URL: "m2.m3u8", Bandwidth: 2222, BandwidthAvg: 2000, Resolution: image.Pt(2, 2), Codecs: []string{"avc1.4D401F", "mp4a.40.2"}, Framerate: 29.97},
			{URL: "m3.m3u8", Bandwidth: 3333, BandwidthAvg: 3000, Resolution: image.Pt(3, 3), Codecs: []string{"avc1.4D401F", "mp4a.40.2"}, Framerate: 29.97},
			{URL: "m4.m3u8", Bandwidth: 4444, BandwidthAvg: 4000, Resolution: image.Pt(4, 4), Codecs: []string{"avc1.4D401E", "mp4a.40.2"}, Framerate: 29.97},
			{URL: "m5.m3u8", Bandwidth: 5555, BandwidthAvg: 5000, Resolution: image.Pt(5, 5), Codecs: []string{"avc1.4D401E", "mp4a.40.2"}, Framerate: 29.97},
			{URL: "m6.m3u8", Bandwidth: 6666, BandwidthAvg: 6000, Resolution: image.Pt(6, 6), Codecs: []string{"avc1.4D400D", "mp4a.40.2"}, Framerate: 29.97},
		},
	}

	m := Master{}
	m.DecodeHLS(strings.NewReader(sample))
	if !reflect.DeepEqual(m, want) {
		t.Fatalf("mismatch:\n\t\thave: %+v\n\t\twant: %+v", m, want)
	}
}
func TestDecodeMedia(t *testing.T) {
	sample := `
	#EXTM3U
	#EXT-X-VERSION:3
	#EXT-X-INDEPENDENT-SEGMENTS
	#EXT-X-PLAYLIST-TYPE:EVENT
	#EXT-X-START:TIME-OFFSET=25,PRECISE=YES
	#EXT-X-TARGETDURATION:10
	#EXT-X-MEDIA-SEQUENCE:1
	#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
	#EXT-X-ENDLIST
	`
	want := Media{
		MediaHeader: MediaHeader{
			Version:       3,
			Independent:   true,
			Type:          "EVENT",
			Duration:      10 * time.Second,
			Sequence:      1,
			Discontinuity: 2,
			Start:         Start{Offset: 25 * time.Second, Precise: true},
			End:           true,
		},
		File: []File{
			{Inf: Inf{10 * time.Second, "ad0.ts"}},
			{Inf: Inf{8 * time.Second, "ad1.ts"}},
			{Inf: Inf{10 * time.Second, "movieA.ts"}, Discontinuous: true},
			{Inf: Inf{10 * time.Second, "movieB.ts"}},
		},
	}

	m := Media{}
	m.DecodeHLS(strings.NewReader(sample))
	if !reflect.DeepEqual(m, want) {
		t.Fatalf("mismatch:\n\t\thave: %+v\n\t\twant: %+v", m, want)
	}
}

Documentation

Overview

Package hls implement an HLS codec for Master and Media files in m3u format At this time, the codec only supports decoding

Index

Constants

View Source
const (
	Vod   = "VOD"   // immutable
	Event = "EVENT" // append-only
	Live  = ""      // sliding-window
)

Media playlist types

Variables

View Source
var (
	ErrHeader = errors.New("hls: no m3u8 tag")
	ErrEmpty  = errors.New("hls: empty playlist")
	ErrType   = errors.New("hls: playlist type mismatch")
)

Functions

func Decode added in v0.1.0

func Decode(r io.Reader) (t []m3u.Tag, master bool, err error)

Decode reads an HLS playlist from the reader and tokenizes it into a list of tags. Master is true if and only if the input looks like a master playlist.

func Runtime added in v0.0.2

func Runtime(f ...File) (cumulative time.Duration)

Runtime measures the cumulative duration of the given window of segments (files)

Types

type File

type File struct {
	Discontinuous bool      `hls:"EXT-X-DISCONTINUITY,omitempty"`
	Time          time.Time `hls:"EXT-X-PROGRAM-DATE-TIME,omitempty"`
	Range         Range     `hls:"EXT-X-BYTERANGE,omitempty"`
	Map           Map       `hls:"EXT-X-MAP,omitempty"`
	Key           Key       `hls:"EXT-X-KEY,omitempty"`
	Inf           Inf       `hls:"EXTINF"`
}

func (File) Duration added in v0.0.4

func (f File) Duration(target time.Duration) time.Duration

Duration returns the segment duration. An optional target can be provided as a fallback in case the duration was not set.

func (File) Location

func (f File) Location(base *url.URL) *url.URL

Location returns the media URL relative to base. It conditionally applies the base URL in cases where the media URL is a relative path. Base may be nil. This function never returns nil, but may return an empty URL. For error handling, process f.Inf.URL manually

type Inf

type Inf struct {
	Duration    time.Duration `hls:"$1"`
	Description string        `hls:"$2"`

	URL string `hls:"$file"`
}

type Key

type Key struct {
}

type Map

type Map struct {
	URI string `hls:"URI"`
}

type Master

type Master struct {
	M3U         bool         `hls:"EXTM3U"`
	Version     int          `hls:"EXT-X-VERSION"`
	Independent bool         `hls:"EXT-X-INDEPENDENT-SEGMENTS"`
	Media       []MediaInfo  `hls:"EXT-X-MEDIA"`
	Stream      []StreamInfo `hls:"EXT-X-STREAM-INF"`
}

Master is a master playlist. It contains a list of streams (variants) and media information associated by group id. By convention, the master playlist is immutable.

func (*Master) Decode added in v0.1.0

func (m *Master) Decode(r io.Reader) error

Decode decodes the master playlist into m.

func (*Master) DecodeTag added in v0.1.0

func (m *Master) DecodeTag(t ...m3u.Tag) error

func (*Master) Len added in v0.0.4

func (m *Master) Len() int

Len returns the number of variant streams

type Media

type Media struct {
	MediaHeader
	File []File `hls:""`
}

Media is a media playlist. It consists of a header and one or more files. A file is EXTINF and the content of any additional tags that apply to that EXTINF tag.

func (*Media) Current added in v0.0.3

func (m *Media) Current() (f File)

Current returns the most-recent segment in the stream

func (*Media) Decode added in v0.1.0

func (m *Media) Decode(r io.Reader) error

Decode decodes the playlist in r and stores the result in m. It returns ErrEmpty if the playlist is well-formed, but contains no variant streams.

func (*Media) DecodeTag added in v0.1.0

func (m *Media) DecodeTag(t ...m3u.Tag) error

DecodeTag decodes the list of tags as a media playlist

func (Media) EncodeTag added in v0.1.0

func (m Media) EncodeTag() (t []m3u.Tag, err error)

func (*Media) Len added in v0.0.4

func (m *Media) Len() int

Len returns the number of segments visibile to the playlist

type MediaHeader

type MediaHeader struct {
	M3U           bool          `hls:"EXTM3U"`
	Version       int           `hls:"EXT-X-VERSION"`
	Independent   bool          `hls:"EXT-X-INDEPENDENT-SEGMENTS"`
	Type          string        `hls:"EXT-X-PLAYLIST-TYPE"`
	Target        time.Duration `hls:"EXT-X-TARGETDURATION"`
	Start         Start         `hls:"EXT-X-START"`
	Sequence      int           `hls:"EXT-X-MEDIA-SEQUENCE"`
	Discontinuity int           `hls:"EXT-X-DISCONTINUITY-SEQUENCE"`
	End           bool          `hls:"EXT-X-ENDLIST"`
}

type MediaInfo

type MediaInfo struct {
	Type       string `hls:"TYPE"`
	Group      string `hls:"GROUP-ID"`
	Name       string `hls:"NAME"`
	Default    bool   `hls:"DEFAULT"`
	Autoselect bool   `hls:"AUTOSELECT"`
	Forced     bool   `hls:"FORCED"`
	Lang       string `hls:"LANGUAGE"`
	URI        string `hls:"URI"`
}

type Range

type Range struct {
	V string `hls:""`
}

func (Range) Value

func (r Range) Value(n int) (at, size int, err error)

type Start

type Start struct {
	Offset  time.Duration `hls:"TIME-OFFSET"`
	Precise bool          `hls:"PRECISE"`
}

type StreamInfo

type StreamInfo struct {
	URL string `hls:"$file"`

	Index        int         `hls:"PROGRAM-ID"`
	Framerate    float64     `hls:"FRAME-RATE"`
	Bandwidth    int         `hls:"BANDWIDTH"`
	BandwidthAvg int         `hls:"AVERAGE-BANDWIDTH"`
	Codecs       []string    `hls:"CODECS"`
	Resolution   image.Point `hls:"RESOLUTION"`
	VideoRange   string      `hls:"VIDEO-RANGE"`
	HDCP         string      `hls:"HDCP-LEVEL"`

	Audio    string `hls:"AUDIO"`
	Video    string `hls:"VIDEO"`
	Subtitle string `hls:"SUBTITLES"`
	Caption  string `hls:"CLOSED-CAPTIONS"`
}

func (StreamInfo) Location

func (s StreamInfo) Location(base *url.URL) *url.URL

Location returns the stream URL relative to base. It conditionally applies the base URL in cases where the stream URL is a relative path. Base may be nil. This function never returns nil, but may return an empty URL. For error handling, process s.URLmanually.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL