streaming

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

segment_test.go (3664B)


      1 package m3u8
      2 
      3 import (
      4 	"encoding/binary"
      5 	"os"
      6 	"reflect"
      7 	"testing"
      8 	"time"
      9 )
     10 
     11 func TestMarshalSegments(t *testing.T) {
     12 	var cases = []struct {
     13 		name string
     14 		seg  Segment
     15 		out  string
     16 	}{
     17 		{
     18 			"duration",
     19 			Segment{Duration: 10 * time.Second, URI: "bunny.ts"},
     20 			"#EXTINF:10.000\nbunny.ts",
     21 		},
     22 		{
     23 			"duration milliseconds",
     24 			Segment{URI: "something.ts", Duration: 9967 * time.Millisecond},
     25 			"#EXTINF:9.967\nsomething.ts",
     26 		},
     27 		{
     28 			"discontinuity with URI",
     29 			Segment{
     30 				Duration:      30 * time.Second,
     31 				Discontinuity: true,
     32 				URI:           "adbreak.ts",
     33 			},
     34 			"#EXT-X-DISCONTINUITY\n#EXTINF:30.000\nadbreak.ts",
     35 		},
     36 		{
     37 			"byte range",
     38 			Segment{
     39 				Duration: 2 * time.Second,
     40 				URI:      "vid.ts",
     41 				Range:    ByteRange{69, 420},
     42 			},
     43 			"#EXT-X-BYTERANGE:69@420\n#EXTINF:2.000\nvid.ts",
     44 		},
     45 		{
     46 			"title",
     47 			Segment{
     48 				Duration: 2 * time.Second,
     49 				URI:      "1.fmp4",
     50 				Title:    "first",
     51 			},
     52 			"#EXTINF:2.000,first\n1.fmp4",
     53 		},
     54 	}
     55 	for _, tt := range cases {
     56 		t.Run(tt.name, func(t *testing.T) {
     57 			b, err := tt.seg.MarshalText()
     58 			if err != nil {
     59 				t.Fatal(err)
     60 			}
     61 			got := string(b)
     62 			if got != tt.out {
     63 				t.Errorf("segment text does not match expected")
     64 				t.Log("got:", got)
     65 				t.Log("want:", tt.out)
     66 			}
     67 		})
     68 	}
     69 }
     70 
     71 func TestMarshalBadSegments(t *testing.T) {
     72 	var cases = []struct {
     73 		name string
     74 		seg  Segment
     75 	}{
     76 		{"empty", Segment{}},
     77 		{"no duration", Segment{URI: "video.ts"}},
     78 		{"impossible range", Segment{URI: "bbb.ts", Duration: 6 * time.Second, Range: ByteRange{999, 10}}},
     79 	}
     80 	for _, tt := range cases {
     81 		t.Run(tt.name, func(t *testing.T) {
     82 			if _, err := tt.seg.MarshalText(); err == nil {
     83 				t.Fatalf("nil error encoding invalid segment")
     84 			}
     85 		})
     86 	}
     87 }
     88 
     89 func TestWriteKey(t *testing.T) {
     90 	var iv [16]byte
     91 	binary.LittleEndian.PutUint64(iv[:8], 10000)
     92 	binary.LittleEndian.PutUint64(iv[8:], 98765432)
     93 	k := Key{
     94 		Method:         EncryptMethodAES128,
     95 		URI:            "magic.key",
     96 		IV:             iv,
     97 		Format:         defaultKeyFormat,
     98 		FormatVersions: []uint32{1, 2, 5},
     99 	}
    100 	want := `#EXT-X-KEY:METHOD=AES-128,URI="magic.key",IV=0x1027000000000000780ae30500000000,KEYFORMAT="identity",KEYFORMATVERSIONS="1/2/5"`
    101 	if k.String() != want {
    102 		t.Errorf("unexpected segment key text")
    103 		t.Log("got:", k.String())
    104 		t.Log("want:", want)
    105 	}
    106 }
    107 
    108 func TestParseSegment(t *testing.T) {
    109 	f, err := os.Open("testdata/discontinuities.m3u8")
    110 	if err != nil {
    111 		t.Fatal(err)
    112 	}
    113 	defer f.Close()
    114 
    115 	plist, err := Decode(f)
    116 	if err != nil {
    117 		t.Fatalf("decode playlist: %v", err)
    118 	}
    119 
    120 	encrypted := Segment{
    121 		Duration: 10 * time.Second,
    122 		Key: &Key{
    123 			Method: EncryptMethodAES128,
    124 			URI:    "key1.json?f=1041&s=0&p=1822767&m=1506045858",
    125 			IV:     [...]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x1B, 0xD0, 0x2F},
    126 		},
    127 		DateTime: time.Date(2020, 12, 2, 18, 33, 3, 447e6, time.UTC),
    128 		URI:      "1041_6_1822767.ts?m=1506045858",
    129 	}
    130 
    131 	if !reflect.DeepEqual(plist.Segments[0], encrypted) {
    132 		t.Errorf("decode encrypted segment: got %v, want %v", plist.Segments[0], encrypted)
    133 	}
    134 }
    135 
    136 func TestSegmentTitles(t *testing.T) {
    137 	f, err := os.Open("testdata/segment_titles.m3u8")
    138 	if err != nil {
    139 		t.Fatal(err)
    140 	}
    141 	defer f.Close()
    142 
    143 	plist, err := Decode(f)
    144 	if err != nil {
    145 		t.Fatalf("decode %s: %v", f.Name(), err)
    146 	}
    147 
    148 	want := Segment{
    149 		Duration: 6 * time.Second,
    150 		Title:    "second",
    151 		URI:      "002.fmp4",
    152 	}
    153 
    154 	if want.Title != plist.Segments[1].Title {
    155 		t.Errorf("second segment title = %s, want %s", plist.Segments[1].Title, want.Title)
    156 	}
    157 	if plist.Segments[0].Title != "" || plist.Segments[2].Title != "" {
    158 		t.Errorf("unexpected non-empty titles in parsed segments")
    159 	}
    160 }