streaming

Media streaming and broadcast systems in Go
Log | Files | Refs | README | LICENSE

parse_test.go (4433B)


      1 package m3u8
      2 
      3 import (
      4 	"fmt"
      5 	"os"
      6 	"path"
      7 	"path/filepath"
      8 	"reflect"
      9 	"strings"
     10 	"testing"
     11 	"time"
     12 )
     13 
     14 func ExampleEncode() {
     15 	p := &Playlist{
     16 		Version: 7,
     17 		Segments: []Segment{
     18 			{URI: "001.ts", Duration: 4 * time.Second},
     19 		},
     20 
     21 		TargetDuration: 4 * time.Second,
     22 		Sequence:       0,
     23 		Type:           PlaylistEvent,
     24 	}
     25 
     26 	sb := &strings.Builder{}
     27 	_ = Encode(sb, p)
     28 
     29 	fmt.Println(sb)
     30 
     31 	// Output: #EXTM3U
     32 	// #EXT-X-VERSION:7
     33 	// #EXT-X-PLAYLIST-TYPE:EVENT
     34 	// #EXT-X-TARGETDURATION:4
     35 	// #EXT-X-MEDIA-SEQUENCE:0
     36 	// #EXTINF:4.000
     37 	// 001.ts
     38 }
     39 
     40 func ExampleDecode() {
     41 	s := `
     42 #EXTM3U
     43 #EXT-X-STREAM-INF:BANDWIDTH=1280000,RESOLUTION=640x360,HDCP-LEVEL=NONE
     44 url_0/low.m3u8
     45 #EXT-X-STREAM-INF:BANDWIDTH=2560000,RESOLUTION=1280x720,HDCP-LEVEL=NONE
     46 url_0/mid.m3u8
     47 #EXT-X-STREAM-INF:BANDWIDTH=7680000,RESOLUTION=1920x1080,HDCP-LEVEL=NONE
     48 url_0/high.m3u8`
     49 
     50 	p, err := Decode(strings.NewReader(s))
     51 	if err != nil {
     52 		// handle error
     53 	}
     54 	for _, v := range p.Variants {
     55 		fmt.Printf("%s %dp@%dkbps\n", v.URI, v.Resolution[1], v.Bandwidth/1e3)
     56 	}
     57 	// Output:
     58 	// url_0/low.m3u8 360p@1280kbps
     59 	// url_0/mid.m3u8 720p@2560kbps
     60 	// url_0/high.m3u8 1080p@7680kbps
     61 }
     62 
     63 func TestDecode(t *testing.T) {
     64 	names, err := filepath.Glob("testdata/*.m3u8")
     65 	if err != nil {
     66 		t.Fatal(err)
     67 	}
     68 	for _, name := range names {
     69 		t.Run(path.Base(name), func(t *testing.T) {
     70 			f, err := os.Open(name)
     71 			if err != nil {
     72 				t.Fatal(err)
     73 			}
     74 			defer f.Close()
     75 			_, err = Decode(f)
     76 			if err != nil {
     77 				t.Fatal(err)
     78 			}
     79 		})
     80 	}
     81 }
     82 
     83 func TestVariant(t *testing.T) {
     84 	f, err := os.Open("testdata/master.m3u8")
     85 	if err != nil {
     86 		t.Fatal(err)
     87 	}
     88 	defer f.Close()
     89 	plist, err := Decode(f)
     90 	if err != nil {
     91 		t.Fatalf("read playlist: %v", err)
     92 	}
     93 	want := []Variant{
     94 		{
     95 			Bandwidth: 1280000,
     96 			Video:     "low",
     97 			HDCP:      HDCPType0,
     98 			URI:       "low/main/audio-video.m3u8",
     99 		},
    100 		{
    101 			Bandwidth: 2560000,
    102 			Video:     "mid",
    103 			HDCP:      HDCPType1,
    104 			URI:       "mid/main/audio-video.m3u8",
    105 		},
    106 		{
    107 			Bandwidth: 7680000,
    108 			Video:     "hi",
    109 			URI:       "hi/main/audio-video.m3u8",
    110 		},
    111 		{
    112 			Bandwidth: 65000,
    113 			Codecs:    []string{"mp4a.40.5"},
    114 			HDCP:      HDCPNone,
    115 			URI:       "main/audio-only.m3u8",
    116 		},
    117 	}
    118 	for i, got := range plist.Variants {
    119 		if !reflect.DeepEqual(got, want[i]) {
    120 			t.Errorf("variant %d: got %v, want %v", i, got, want)
    121 		}
    122 	}
    123 }
    124 
    125 func TestParseDuration(t *testing.T) {
    126 	want := 9967000 * time.Microsecond
    127 	it := item{typ: itemNumber, val: "9.967"}
    128 	dur, err := parseSegmentDuration(it.val)
    129 	if err != nil {
    130 		t.Fatal(err)
    131 	}
    132 	if dur != want {
    133 		t.Errorf("parseSegmentDuration(%s) = %s, want %s", it, dur, want)
    134 	}
    135 }
    136 
    137 func TestParseByteRange(t *testing.T) {
    138 	var tests = []struct {
    139 		in    string
    140 		want  ByteRange
    141 		valid bool
    142 	}{
    143 		{"27@46", ByteRange{27, 46}, true},
    144 		{"69", ByteRange{69}, true},
    145 		{"732@", ByteRange{0, 0}, false},
    146 		{"@", ByteRange{0, 0}, false},
    147 	}
    148 	for _, tt := range tests {
    149 		t.Run(tt.in, func(t *testing.T) {
    150 			r, err := parseByteRange(tt.in)
    151 			if err != nil && tt.valid {
    152 				t.Fatalf("parseByteRange(%s): %v", tt.in, err)
    153 			} else if err == nil && !tt.valid {
    154 				t.Fatalf("parseByteRange(%s): nil error on invalid byte range", tt.in)
    155 			}
    156 			if r != tt.want {
    157 				t.Errorf("parseByteRange(%s) = %v, want %v", tt.in, r, tt.want)
    158 			}
    159 		})
    160 	}
    161 }
    162 
    163 // Tests that we parse floats and integers of different precisions ok.
    164 func TestFrameRate(t *testing.T) {
    165 	f, err := os.Open("testdata/frame_rate.m3u8")
    166 	if err != nil {
    167 		t.Fatal(err)
    168 	}
    169 	defer f.Close()
    170 	plist, err := Decode(f)
    171 	if err != nil {
    172 		t.Fatal(err)
    173 	}
    174 	rates := []float32{24, 25, 29.97, 30, 23.976, 60}
    175 	for i, v := range plist.Variants {
    176 		if v.FrameRate != rates[i] {
    177 			t.Errorf("want %f, got %f", rates[i], v.FrameRate)
    178 		}
    179 	}
    180 }
    181 
    182 func TestParseSequence(t *testing.T) {
    183 	f, err := os.Open("testdata/sequence.m3u8")
    184 	if err != nil {
    185 		t.Fatal(err)
    186 	}
    187 	defer f.Close()
    188 	plist, err := Decode(f)
    189 	if err != nil {
    190 		t.Fatal(err)
    191 	}
    192 	if plist.Sequence != 91240 {
    193 		t.Errorf("want %d, got %d", 91240, plist.Sequence)
    194 	}
    195 }
    196 
    197 func TestResolution(t *testing.T) {
    198 	var tests = []struct {
    199 		name string
    200 		s    string
    201 	}{
    202 		{"double x", "1280xx720"},
    203 		{"missing x", "1280720"},
    204 		{"missing height", "1280x"},
    205 		{"negative", "-1x-1"},
    206 		{"decimal", "10.69x20"},
    207 	}
    208 
    209 	for _, tt := range tests {
    210 		_, err := parseResolution(tt.s)
    211 		if err == nil {
    212 			t.Errorf("parse resolution %q (%s): no error", tt.s, tt.name)
    213 			continue
    214 		}
    215 	}
    216 }