hls

package module
v0.1.1 Latest Latest
Warning

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

Go to latest
Published: Jun 4, 2024 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) Init added in v0.1.1

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

Init returns the initialization segment for fragmented mp4 files as an absolute url relative to base. If there is no initialization segment it returns an empty URL.

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