m3u8

package module
v0.5.2 Latest Latest
Warning

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

Go to latest
Published: Oct 4, 2015 License: GPL-3.0 Imports: 9 Imported by: 204

README

M3U8

This is a most complete opensource library for parsing and generating of M3U8 playlists used in HTTP Live Streaming (Apple HLS) for internet video translations.

M3U8 is simple text format and parsing library for it must be simple too. It did not offer ways to play HLS or handle playlists over HTTP. So library features are:

  • Support HLS specs up to version 5 of the protocol.
  • Parsing and generation of master-playlists and media-playlists.
  • Autodetect input streams as master or media playlists.
  • Offer structures for keeping playlists metadata.
  • Encryption keys support for use with DRM systems like Verimatrix etc.
  • Support for non standard Google Widevine tags.

Library licensed under GPLv3. Copyleft by library authors (see AUTHORS).

Install

go get github.com/grafov/m3u8

or get releases from https://github.com/grafov/m3u8/releases

Documentation Go Walker

Package online documentation (examples included) available at:

Supported by the HLS protocol tags and their library support explained in M3U8 cheatsheet.

Examples

Parse playlist:

	f, err := os.Open("playlist.m3u8")
	if err != nil {
		panic(err)
	}
	p, listType, err := DecodeFrom(bufio.NewReader(f), true)
	if err != nil {
		panic(err)
	}
	switch listType {
	case MEDIA:
	    mediapl := p.(*MediaPlaylist)
		fmt.Printf("%+v\n", mediapl)
	case MASTER:
	    masterpl := p.(*MasterPlaylist)
		fmt.Printf("%+v\n", masterpl)
	}

Then you get filled with parsed data structures. For master playlists you get Master struct with slice consists of pointers to Variant structures (which represent playlists to each bitrate). For media playlist parser returns MediaPlaylist structure with slice of Segments. Each segment is of MediaSegment type. See structure.go or full documentation (link below).

You may use API methods to fill structures or create them manually to generate playlists. Example of media playlist generation:

	p, e := NewMediaPlaylist(3, 10) // with window of size 3 and capacity 10
	if e != nil {
		panic(fmt.Sprintf("Creating of media playlist failed: %s", e))
	}
	for i := 0; i < 5; i++ {
		e = p.Add(fmt.Sprintf("test%d.ts", i), 6.0, "")
		if e != nil {
			panic(fmt.Sprintf("Add segment #%d to a media playlist failed: %s", i, e))
		}
	}
	fmt.Println(p.Encode().String())

Library structure

Library has compact code and bundled in three files:

  • structure.go — declares all structures related to playlists and their properties
  • reader.go — playlist parser methods
  • writer.go — playlist generator methods

Each file has own test suite placed in *_test.go accordingly.

Library usage

This library successfully used in streaming software developed for my employer and tested with generating of VOD and Live streams and parsing of Widevine Live streams. Also library usage noted in opensource software:

M3U8 parsing/generation in other languages

Project status

In development.

Build Status for last commit from master or draft branches.

Build Status for master branch.

Development rules:

  • Feature changes first applied to draft branch then after minimal testing it will merge with master branch.
  • Bugfixes or minor doc changes applied to master branch and then merged to draft.
  • Code in draft branch may be in an inconsistent state.
  • After complete testing and one week usage with my prober for HLS Stream Surfer it may be released as new library version.
  • Each new API call or M3U8 tag accompanied by at least with one unit test till new release (this rule will be apply after v1.0).
  • Versioning scheme follows http://semver.org rules (but versions till v1.0 not support bacward compatibility, see release notes carefully).

Project dashboard: https://waffle.io/grafov/m3u8 Stories in Ready

Roadmap

To version 1.0:

  • Support all M3U8 tags up to latest version of specs.
  • Code coverage by unit tests more than 90%

Documentation

Overview

This is a most complete opensource library for parsing and generating of M3U8 playlists used in HTTP Live Streaming (Apple HLS) for internet video translations.

M3U8 is simple text format and parsing library for it must be simple too. It did not offer ways to play HLS or handle playlists over HTTP. Library features are:

  • Support HLS specs up to version 5 of the protocol.
  • Parsing and generation of master-playlists and media-playlists.
  • Autodetect input streams as master or media playlists.
  • Offer structures for keeping playlists metadata.
  • Encryption keys support for usage with DRM systems like Verimatrix (http://verimatrix.com) etc.
  • Support for non standard Google Widevine (http://www.widevine.com) tags.

Library coded acordingly with IETF draft http://tools.ietf.org/html/draft-pantos-http-live-streaming

Examples of usage may be found in *_test.go files of a package. Also see below some simple examples.

Create simple media playlist with sliding window of 3 segments and maximum of 50 segments.

p, e := NewMediaPlaylist(3, 50)
if e != nil {
  panic(fmt.Sprintf("Create media playlist failed: %s", e))
}
for i := 0; i < 5; i++ {
  e = p.Add(fmt.Sprintf("test%d.ts", i), 5.0)
  if e != nil {
    panic(fmt.Sprintf("Add segment #%d to a media playlist failed: %s", i, e))
  }
}
fmt.Println(p.Encode(true).String())

We add 5 testX.ts segments to playlist then encode it to M3U8 format and convert to string.

Next example shows parsing of master playlist:

f, err := os.Open("sample-playlists/master.m3u8")
if err != nil {
  fmt.Println(err)
}
p := NewMasterPlaylist()
err = p.Decode(bufio.NewReader(f), false)
if err != nil {
  fmt.Println(err)
}

fmt.Printf("Playlist object: %+v\n", p)

We are open playlist from the file and parse it as master playlist.

Index

Examples

Constants

View Source
const (
	DATETIME = time.RFC3339Nano // Format for EXT-X-PROGRAM-DATE-TIME defined in section 3.4.5
)

Variables

This section is empty.

Functions

func Decode

func Decode(data bytes.Buffer, strict bool) (Playlist, ListType, error)

Detect playlist type and decode it from the buffer.

func DecodeFrom

func DecodeFrom(reader io.Reader, strict bool) (Playlist, ListType, error)

Detect playlist type and decode it from input stream.

Types

type Alternative

type Alternative struct {
	GroupId         string
	URI             string
	Type            string
	Language        string
	Name            string
	Default         bool
	Autoselect      string
	Forced          string
	Characteristics string
	Subtitles       string
}

This structure represents EXT-X-MEDIA tag in variants.

type Key

type Key struct {
	Method            string
	URI               string
	IV                string
	Keyformat         string
	Keyformatversions string
}

This structure represents information about stream encryption.

Realizes EXT-X-KEY tag.

type ListType

type ListType uint
const (
	// use 0 for not defined type
	MASTER ListType = iota + 1
	MEDIA
)

type Map added in v0.3.1

type Map struct {
	URI    string
	Limit  int64 // <n> is length in bytes for the file under URI
	Offset int64 // [@o] is offset from the start of the file under URI
}

It applies to every Media Segment that appears after it in the Playlist until the next EXT-X-MAP tag or until the end of the playlist.

Realizes EXT-MAP tag.

type MasterPlaylist

type MasterPlaylist struct {
	Variants      []*Variant
	Args          string // optional arguments placed after URI (URI?Args)
	CypherVersion string // non-standard tag for Widevine (see also WV struct)
	// contains filtered or unexported fields
}

This structure represents a master playlist which combines media playlists for multiple bitrates. URI lines in the playlist identify media playlists. Sample of Master Playlist file:

#EXTM3U
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=1280000
http://example.com/low.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=2560000
http://example.com/mid.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=7680000
http://example.com/hi.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=65000,CODECS="mp4a.40.5"
http://example.com/audio-only.m3u8

func NewMasterPlaylist

func NewMasterPlaylist() *MasterPlaylist

Create new empty master playlist. Master playlist consists of variants.

func (*MasterPlaylist) Append

func (p *MasterPlaylist) Append(uri string, chunklist *MediaPlaylist, params VariantParams)

Append variant to master playlist. This operation does reset playlist cache.

func (*MasterPlaylist) Decode

func (p *MasterPlaylist) Decode(data bytes.Buffer, strict bool) error

Parse master playlist from the buffer. If `strict` parameter is true then return first syntax error.

func (*MasterPlaylist) DecodeFrom

func (p *MasterPlaylist) DecodeFrom(reader io.Reader, strict bool) error

Parse master playlist from the io.Reader stream. If `strict` parameter is true then return first syntax error.

func (*MasterPlaylist) Encode

func (p *MasterPlaylist) Encode() *bytes.Buffer

Generate output in M3U8 format.

func (*MasterPlaylist) ResetCache

func (p *MasterPlaylist) ResetCache()

func (*MasterPlaylist) String added in v0.3.1

func (p *MasterPlaylist) String() string

For compatibility with Stringer interface For example fmt.Printf("%s", sampleMediaList) will encode playist and print its string representation.

Example

Create new master playlist Add media playlist Encode structures to HLS

m := NewMasterPlaylist()
p, _ := NewMediaPlaylist(3, 5)
for i := 0; i < 5; i++ {
	p.Append(fmt.Sprintf("test%d.ts", i), 5.0, "")
}
m.Append("chunklist1.m3u8", p, VariantParams{ProgramId: 123, Bandwidth: 1500000, Resolution: "576x480"})
m.Append("chunklist2.m3u8", p, VariantParams{ProgramId: 123, Bandwidth: 1500000, Resolution: "576x480"})
fmt.Printf("%s", m)
Output:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-STREAM-INF:PROGRAM-ID=123,BANDWIDTH=1500000,RESOLUTION=576x480
chunklist1.m3u8
#EXT-X-STREAM-INF:PROGRAM-ID=123,BANDWIDTH=1500000,RESOLUTION=576x480
chunklist2.m3u8

type MediaPlaylist

type MediaPlaylist struct {
	TargetDuration float64
	SeqNo          uint64 // EXT-X-MEDIA-SEQUENCE
	Segments       []*MediaSegment
	Args           string // optional arguments placed after URIs (URI?Args)
	Iframe         bool   // EXT-X-I-FRAMES-ONLY
	Closed         bool   // is this VOD (closed) or Live (sliding) playlist?
	MediaType      MediaType

	Key *Key // EXT-X-KEY is optional encryption key displayed before any segments (default key for the playlist)
	Map *Map // EXT-X-MAP is optional tag specifies how to obtain the Media Initialization Section (default map for the playlist)
	WV  *WV  // Widevine related tags outside of M3U8 specs
	// contains filtered or unexported fields
}

This structure represents a single bitrate playlist aka media playlist. It related to both a simple media playlists and a sliding window media playlists. URI lines in the Playlist point to media segments.

Simple Media Playlist file sample:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:5220
#EXTINF:5219.2,
http://media.example.com/entire.ts
#EXT-X-ENDLIST

Sample of Sliding Window Media Playlist, using HTTPS:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-TARGETDURATION:8
#EXT-X-MEDIA-SEQUENCE:2680

#EXTINF:7.975,
https://priv.example.com/fileSequence2680.ts
#EXTINF:7.941,
https://priv.example.com/fileSequence2681.ts
#EXTINF:7.975,
https://priv.example.com/fileSequence2682.ts

func NewMediaPlaylist

func NewMediaPlaylist(winsize uint, capacity uint) (*MediaPlaylist, error)

Creates new media playlist structure. Winsize defines how much items will displayed on playlist generation. Capacity is total size of a playlist.

func (*MediaPlaylist) Append

func (p *MediaPlaylist) Append(uri string, duration float64, title string) error

Append general chunk to the tail of chunk slice for a media playlist. This operation does reset playlist cache.

func (*MediaPlaylist) Close

func (p *MediaPlaylist) Close()

Close sliding playlist and make them fixed.

func (*MediaPlaylist) Count added in v0.3.2

func (p *MediaPlaylist) Count() uint

Count tells us the number of items that are currently in the media playlist

func (*MediaPlaylist) Decode

func (p *MediaPlaylist) Decode(data bytes.Buffer, strict bool) error

Parse media playlist from the buffer. If `strict` parameter is true then return first syntax error.

func (*MediaPlaylist) DecodeFrom

func (p *MediaPlaylist) DecodeFrom(reader io.Reader, strict bool) error

Parse media playlist from the io.Reader stream. If `strict` parameter is true then return first syntax error.

func (*MediaPlaylist) DurationAsInt

func (p *MediaPlaylist) DurationAsInt(yes bool)

TargetDuration will be int on Encode

Example

Example of parsing a playlist with EXT-X-DISCONTINIUTY tag and output it with integer segment durations.

f, _ := os.Open("sample-playlists/media-playlist-with-discontinuity.m3u8")
p, _, _ := DecodeFrom(bufio.NewReader(f), true)
pp := p.(*MediaPlaylist)
pp.DurationAsInt(true)
fmt.Printf("%s", pp)
Output:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-TARGETDURATION:10
#EXTINF:10,
ad0.ts
#EXTINF:8,
ad1.ts
#EXT-X-DISCONTINUITY
#EXTINF:10,
movieA.ts
#EXTINF:10,
movieB.ts

func (*MediaPlaylist) Encode

func (p *MediaPlaylist) Encode() *bytes.Buffer

Generate output in M3U8 format. Marshal `winsize` elements from bottom of the `segments` queue.

func (*MediaPlaylist) Remove

func (p *MediaPlaylist) Remove() (err error)

Remove current segment from the head of chunk slice form a media playlist. Useful for sliding playlists. This operation does reset playlist cache.

func (*MediaPlaylist) ResetCache

func (p *MediaPlaylist) ResetCache()

Reset playlist cache. Next called Encode() will regenerate playlist from the chunk slice.

func (*MediaPlaylist) SetDefaultKey

func (p *MediaPlaylist) SetDefaultKey(method, uri, iv, keyformat, keyformatversions string) error

Set encryption key appeared once in header of the playlist (pointer to MediaPlaylist.Key). It useful when keys not changed during playback. Set tag for the whole list.

func (*MediaPlaylist) SetDefaultMap added in v0.3.1

func (p *MediaPlaylist) SetDefaultMap(uri string, limit, offset int64)

Set map appeared once in header of the playlist (pointer to MediaPlaylist.Key). It useful when map not changed during playback. Set tag for the whole list.

func (*MediaPlaylist) SetDiscontinuity

func (p *MediaPlaylist) SetDiscontinuity() error

Set discontinuity flag for the current media segment. EXT-X-DISCONTINUITY indicates an encoding discontinuity between the media segment that follows it and the one that preceded it (i.e. file format, number and type of tracks, encoding parameters, encoding sequence, timestamp sequence).

func (*MediaPlaylist) SetIframeOnly added in v0.3.1

func (p *MediaPlaylist) SetIframeOnly()

Mark medialist as consists of only I-frames (Intra frames). Set tag for the whole list.

func (*MediaPlaylist) SetKey

func (p *MediaPlaylist) SetKey(method, uri, iv, keyformat, keyformatversions string) error

Set encryption key for the current segment of media playlist (pointer to Segment.Key)

func (*MediaPlaylist) SetMap added in v0.3.1

func (p *MediaPlaylist) SetMap(uri string, limit, offset int64) error

Set encryption key for the current segment of media playlist (pointer to Segment.Key)

func (*MediaPlaylist) SetProgramDateTime

func (p *MediaPlaylist) SetProgramDateTime(value time.Time) error

Set program date and time for the current media segment. EXT-X-PROGRAM-DATE-TIME tag associates the first sample of a media segment with an absolute date and/or time. It applies only to the current media segment. Date/time format is YYYY-MM-DDThh:mm:ssZ (ISO8601) and includes time zone.

func (*MediaPlaylist) SetRange

func (p *MediaPlaylist) SetRange(limit, offset int64) error

Set limit and offset for the current media segment (EXT-X-BYTERANGE support for protocol version 4).

func (*MediaPlaylist) Slide

func (p *MediaPlaylist) Slide(uri string, duration float64, title string)

Combines two operations: firstly it removes one chunk from the head of chunk slice and move pointer to next chunk. Secondly it appends one chunk to the tail of chunk slice. Useful for sliding playlists. This operation does reset cache.

func (*MediaPlaylist) String added in v0.3.1

func (p *MediaPlaylist) String() string

For compatibility with Stringer interface For example fmt.Printf("%s", sampleMediaList) will encode playist and print its string representation.

Example

Create new media playlist Add two segments to media playlist Print it

p, _ := NewMediaPlaylist(1, 2)
p.Append("test01.ts", 5.0, "")
p.Append("test02.ts", 6.0, "")
fmt.Printf("%s\n", p)
Output:

#EXTM3U
#EXT-X-VERSION:3
#EXT-X-MEDIA-SEQUENCE:1
#EXT-X-TARGETDURATION:6
#EXTINF:5.000,
test01.ts

type MediaSegment

type MediaSegment struct {
	SeqId           uint64
	Title           string // optional second parameter for EXTINF tag
	URI             string
	Duration        float64   // first parameter for EXTINF tag; duration must be integers if protocol version is less than 3 but we are always keep them float
	Limit           int64     // EXT-X-BYTERANGE <n> is length in bytes for the file under URI
	Offset          int64     // EXT-X-BYTERANGE [@o] is offset from the start of the file under URI
	Key             *Key      // EXT-X-KEY displayed before the segment and means changing of encryption key (in theory each segment may have own key)
	Map             *Map      // EXT-X-MAP displayed before the segment
	Discontinuity   bool      // EXT-X-DISCONTINUITY indicates an encoding discontinuity between the media segment that follows it and the one that preceded it (i.e. file format, number and type of tracks, encoding parameters, encoding sequence, timestamp sequence)
	ProgramDateTime time.Time // EXT-X-PROGRAM-DATE-TIME tag associates the first sample of a media segment with an absolute date and/or time
}

This structure represents a media segment included in a media playlist. Media segment may be encrypted. Widevine supports own tags for encryption metadata.

type MediaType

type MediaType uint

for EXT-X-PLAYLIST-TYPE tag

const (
	// use 0 for not defined type
	EVENT MediaType = iota + 1
	VOD
)

type Playlist

type Playlist interface {
	Encode() *bytes.Buffer
	Decode(bytes.Buffer, bool) error
	DecodeFrom(reader io.Reader, strict bool) error
	String() string
}

Interface applied to various playlist types.

type Variant

type Variant struct {
	URI       string
	Chunklist *MediaPlaylist
	VariantParams
}

This structure represents variants for master playlist. Variants included in a master playlist and point to media playlists.

type VariantParams

type VariantParams struct {
	ProgramId    uint32
	Bandwidth    uint32
	Codecs       string
	Resolution   string
	Audio        string // EXT-X-STREAM-INF only
	Video        string
	Subtitles    string // EXT-X-STREAM-INF only
	Captions     string // EXT-X-STREAM-INF only
	Iframe       bool   // EXT-X-I-FRAME-STREAM-INF
	Alternatives []*Alternative
}

This stucture represents additional parameters for a variant used in EXT-X-STREAM-INF and EXT-X-I-FRAME-STREAM-INF

type WV

type WV struct {
	AudioChannels          uint
	AudioFormat            uint
	AudioProfileIDC        uint
	AudioSampleSize        uint
	AudioSamplingFrequency uint
	CypherVersion          string
	ECM                    string
	VideoFormat            uint
	VideoFrameRate         uint
	VideoLevelIDC          uint
	VideoProfileIDC        uint
	VideoResolution        string
	VideoSAR               string
}

This structure represents metadata for Google Widevine playlists. This format not described in IETF draft but provied by Widevine Live Packager as additional tags with #WV-prefix.

Jump to

Keyboard shortcuts

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