mirror of
https://github.com/oscar-davids/lpmsdemo.git
synced 2025-12-24 12:37:59 +08:00
655 lines
19 KiB
Go
655 lines
19 KiB
Go
/*
|
|
Playlist parsing tests.
|
|
|
|
Copyright 2013-2017 The Project Developers.
|
|
See the AUTHORS and LICENSE files at the top-level directory of this distribution
|
|
and at https://github.com/grafov/m3u8/
|
|
|
|
ॐ तारे तुत्तारे तुरे स्व
|
|
*/
|
|
package m3u8
|
|
|
|
import (
|
|
"bufio"
|
|
"bytes"
|
|
"fmt"
|
|
"os"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
const badMediaPlaylist = `#EXTM3U
|
|
#EXT-X-VERSION:3
|
|
#EXT-X-MEDIA-SEQUENCE:0
|
|
#EXT-X-TARGETDURATION:2
|
|
#EXTINF:1.968,
|
|
/stream/official_test_source_2s_keys_24pfsmp4_3b7900e6a5d0c1c3a3a2_0/source/0.ts
|
|
#EXTM3U
|
|
#EXT-X-VERSION:3
|
|
#EXT-X-MEDIA-SEQUENCE:0
|
|
#EXT-X-TARGETDURATION:2
|
|
#EXTINF:1.968,
|
|
/stream/official_test_source_2s_keys_24pfsmp4_3b7900e6a5d0c1c3a3a2_0/source/0.ts`
|
|
|
|
func TestDecodeMasterPlaylist(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/master.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p := NewMasterPlaylist()
|
|
err = p.DecodeFrom(bufio.NewReader(f), false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// check parsed values
|
|
if p.ver != 3 {
|
|
t.Errorf("Version of parsed playlist = %d (must = 3)", p.ver)
|
|
}
|
|
if len(p.Variants) != 5 {
|
|
t.Error("Not all variants in master playlist parsed.")
|
|
}
|
|
// TODO check other values
|
|
// fmt.Println(p.Encode().String())
|
|
}
|
|
|
|
func TestDecodeMasterPlaylistWithMultipleCodecs(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/master-with-multiple-codecs.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p := NewMasterPlaylist()
|
|
err = p.DecodeFrom(bufio.NewReader(f), false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// check parsed values
|
|
if p.ver != 3 {
|
|
t.Errorf("Version of parsed playlist = %d (must = 3)", p.ver)
|
|
}
|
|
if len(p.Variants) != 5 {
|
|
t.Error("Not all variants in master playlist parsed.")
|
|
}
|
|
for _, v := range p.Variants {
|
|
if v.Codecs != "avc1.42c015,mp4a.40.2" {
|
|
t.Error("Codec string is wrong")
|
|
}
|
|
}
|
|
// TODO check other values
|
|
// fmt.Println(p.Encode().String())
|
|
}
|
|
|
|
func TestDecodeMasterPlaylistWithAlternatives(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/master-with-alternatives.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p := NewMasterPlaylist()
|
|
err = p.DecodeFrom(bufio.NewReader(f), false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// check parsed values
|
|
if p.ver != 3 {
|
|
t.Errorf("Version of parsed playlist = %d (must = 3)", p.ver)
|
|
}
|
|
if len(p.Variants) != 4 {
|
|
t.Fatal("not all variants in master playlist parsed")
|
|
}
|
|
// TODO check other values
|
|
for i, v := range p.Variants {
|
|
if i == 0 && len(v.Alternatives) != 3 {
|
|
t.Fatalf("not all alternatives from #EXT-X-MEDIA parsed (has %d but should be 3", len(v.Alternatives))
|
|
}
|
|
if i == 1 && len(v.Alternatives) != 3 {
|
|
t.Fatalf("not all alternatives from #EXT-X-MEDIA parsed (has %d but should be 3", len(v.Alternatives))
|
|
}
|
|
if i == 2 && len(v.Alternatives) != 3 {
|
|
t.Fatalf("not all alternatives from #EXT-X-MEDIA parsed (has %d but should be 3", len(v.Alternatives))
|
|
}
|
|
if i == 3 && len(v.Alternatives) > 0 {
|
|
t.Fatal("should not be alternatives for this variant")
|
|
}
|
|
}
|
|
// fmt.Println(p.Encode().String())
|
|
}
|
|
|
|
func TestDecodeMasterPlaylistWithClosedCaptionEqNone(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/master-with-closed-captions-eq-none.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p := NewMasterPlaylist()
|
|
err = p.DecodeFrom(bufio.NewReader(f), false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if len(p.Variants) != 3 {
|
|
t.Fatal("not all variants in master playlist parsed")
|
|
}
|
|
for _, v := range p.Variants {
|
|
if v.Captions != "NONE" {
|
|
t.Fatal("variant field for CLOSED-CAPTIONS should be equal to NONE but it equals", v.Captions)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Decode a master playlist with Name tag in EXT-X-STREAM-INF
|
|
func TestDecodeMasterPlaylistWithStreamInfName(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/master-with-stream-inf-name.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p := NewMasterPlaylist()
|
|
err = p.DecodeFrom(bufio.NewReader(f), false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, variant := range p.Variants {
|
|
if variant.Name == "" {
|
|
t.Errorf("Empty name tag on variant URI: %s", variant.URI)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecodeMediaPlaylistByteRange(t *testing.T) {
|
|
f, _ := os.Open("sample-playlists/media-playlist-with-byterange.m3u8")
|
|
p, _ := NewMediaPlaylist(3, 3)
|
|
_ = p.DecodeFrom(bufio.NewReader(f), true)
|
|
expected := []*MediaSegment{
|
|
{URI: "video.ts", Duration: 10, Limit: 75232},
|
|
{URI: "video.ts", Duration: 10, Limit: 82112, Offset: 752321},
|
|
{URI: "video.ts", Duration: 10, Limit: 69864},
|
|
}
|
|
for i, seg := range p.Segments {
|
|
if *seg != *expected[i] {
|
|
t.Errorf("exp: %+v\ngot: %+v", expected[i], seg)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Decode a master playlist with i-frame-stream-inf
|
|
func TestDecodeMasterPlaylistWithIFrameStreamInf(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/master-with-i-frame-stream-inf.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p := NewMasterPlaylist()
|
|
err = p.DecodeFrom(bufio.NewReader(f), false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
expected := map[int]*Variant{
|
|
86000: {URI: "low/iframe.m3u8", VariantParams: VariantParams{Bandwidth: 86000, ProgramId: 1, Codecs: "c1", Resolution: "1x1", Video: "1", Iframe: true}},
|
|
150000: {URI: "mid/iframe.m3u8", VariantParams: VariantParams{Bandwidth: 150000, ProgramId: 1, Codecs: "c2", Resolution: "2x2", Video: "2", Iframe: true}},
|
|
550000: {URI: "hi/iframe.m3u8", VariantParams: VariantParams{Bandwidth: 550000, ProgramId: 1, Codecs: "c2", Resolution: "2x2", Video: "2", Iframe: true}},
|
|
}
|
|
for _, variant := range p.Variants {
|
|
for k, expect := range expected {
|
|
if reflect.DeepEqual(variant, expect) {
|
|
delete(expected, k)
|
|
}
|
|
}
|
|
}
|
|
for _, expect := range expected {
|
|
t.Errorf("not found:%+v", expect)
|
|
}
|
|
}
|
|
|
|
func TestDecodeMediaPlaylist(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/wowza-vod-chunklist.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p, err := NewMediaPlaylist(5, 798)
|
|
if err != nil {
|
|
t.Fatalf("Create media playlist failed: %s", err)
|
|
}
|
|
err = p.DecodeFrom(bufio.NewReader(f), true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
//fmt.Printf("Playlist object: %+v\n", p)
|
|
// check parsed values
|
|
if p.ver != 3 {
|
|
t.Errorf("Version of parsed playlist = %d (must = 3)", p.ver)
|
|
}
|
|
if p.TargetDuration != 12 {
|
|
t.Errorf("TargetDuration of parsed playlist = %f (must = 12.0)", p.TargetDuration)
|
|
}
|
|
if p.Live {
|
|
t.Error("This is a VOD playlist but Live field = true")
|
|
}
|
|
titles := []string{"Title 1", "Title 2", ""}
|
|
for i, s := range p.Segments {
|
|
if i > len(titles)-1 {
|
|
break
|
|
}
|
|
if s.Title != titles[i] {
|
|
t.Errorf("Segment %v's title = %v (must = %q)", i, s.Title, titles[i])
|
|
}
|
|
}
|
|
// TODO check other values…
|
|
//fmt.Println(p.Encode().String()), stream.Name}
|
|
}
|
|
|
|
func TestDecodeMediaPlaylistDoubleHeader(t *testing.T) {
|
|
assert := assert.New(t)
|
|
p, err := NewMediaPlaylist(5, 798)
|
|
if err != nil {
|
|
t.Fatalf("Create media playlist failed: %s", err)
|
|
}
|
|
err = p.Decode(*bytes.NewBuffer([]byte(badMediaPlaylist)), true)
|
|
assert.EqualError(err, errStartTag.Error(), "Multiple EXTM3U headers should return `Start tag already seen` error")
|
|
}
|
|
|
|
func TestDecodeMediaPlaylistExtInfNonStrict2(t *testing.T) {
|
|
header := `#EXTM3U
|
|
#EXT-X-TARGETDURATION:10
|
|
#EXT-X-VERSION:3
|
|
#EXT-X-MEDIA-SEQUENCE:0
|
|
%s
|
|
`
|
|
|
|
tests := []struct {
|
|
strict bool
|
|
extInf string
|
|
wantError bool
|
|
wantSegment *MediaSegment
|
|
}{
|
|
// strict mode on
|
|
{true, "#EXTINF:10.000,", false, &MediaSegment{Duration: 10.0, Title: ""}},
|
|
{true, "#EXTINF:10.000,Title", false, &MediaSegment{Duration: 10.0, Title: "Title"}},
|
|
{true, "#EXTINF:10.000,Title,Track", false, &MediaSegment{Duration: 10.0, Title: "Title,Track"}},
|
|
{true, "#EXTINF:invalid,", true, nil},
|
|
{true, "#EXTINF:10.000", true, nil},
|
|
|
|
// strict mode off
|
|
{false, "#EXTINF:10.000,", false, &MediaSegment{Duration: 10.0, Title: ""}},
|
|
{false, "#EXTINF:10.000,Title", false, &MediaSegment{Duration: 10.0, Title: "Title"}},
|
|
{false, "#EXTINF:10.000,Title,Track", false, &MediaSegment{Duration: 10.0, Title: "Title,Track"}},
|
|
{false, "#EXTINF:invalid,", false, &MediaSegment{Duration: 0.0, Title: ""}},
|
|
{false, "#EXTINF:10.000", false, &MediaSegment{Duration: 10.0, Title: ""}},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
p, err := NewMediaPlaylist(1, 1)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
reader := bytes.NewBufferString(fmt.Sprintf(header, test.extInf))
|
|
err = p.DecodeFrom(reader, test.strict)
|
|
if test.wantError {
|
|
if err == nil {
|
|
t.Errorf("expected error but have: %v", err)
|
|
}
|
|
continue
|
|
}
|
|
if err != nil {
|
|
t.Errorf("unexpected error: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(p.Segments[0], test.wantSegment) {
|
|
t.Errorf("\nhave: %+v\nwant: %+v", p.Segments[0], test.wantSegment)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecodeMediaPlaylistWithWidevine(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/widevine-bitrate.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p, err := NewMediaPlaylist(5, 798)
|
|
if err != nil {
|
|
t.Fatalf("Create media playlist failed: %s", err)
|
|
}
|
|
err = p.DecodeFrom(bufio.NewReader(f), true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
//fmt.Printf("Playlist object: %+v\n", p)
|
|
// check parsed values
|
|
if p.ver != 2 {
|
|
t.Errorf("Version of parsed playlist = %d (must = 2)", p.ver)
|
|
}
|
|
if p.TargetDuration != 9 {
|
|
t.Errorf("TargetDuration of parsed playlist = %f (must = 9.0)", p.TargetDuration)
|
|
}
|
|
// TODO check other values…
|
|
//fmt.Printf("%+v\n", p.Key)
|
|
//fmt.Println(p.Encode().String())
|
|
}
|
|
|
|
func TestDecodeMasterPlaylistWithAutodetection(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/master.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
m, listType, err := DecodeFrom(bufio.NewReader(f), false)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if listType != MASTER {
|
|
t.Error("Sample not recognized as master playlist.")
|
|
}
|
|
mp := m.(*MasterPlaylist)
|
|
// fmt.Printf(">%+v\n", mp)
|
|
// for _, v := range mp.Variants {
|
|
// fmt.Printf(">>%+v +v\n", v)
|
|
// }
|
|
//fmt.Println("Type below must be MasterPlaylist:")
|
|
CheckType(t, mp)
|
|
}
|
|
|
|
func TestDecodeMediaPlaylistWithAutodetection(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/wowza-vod-chunklist.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p, listType, err := DecodeFrom(bufio.NewReader(f), true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pp := p.(*MediaPlaylist)
|
|
CheckType(t, pp)
|
|
if listType != MEDIA {
|
|
t.Error("Sample not recognized as media playlist.")
|
|
}
|
|
// check parsed values
|
|
if pp.TargetDuration != 12 {
|
|
t.Errorf("TargetDuration of parsed playlist = %f (must = 12.0)", pp.TargetDuration)
|
|
}
|
|
|
|
if pp.Live {
|
|
t.Error("This is a VOD playlist but Live field = true")
|
|
}
|
|
if pp.winsize != 0 {
|
|
t.Errorf("Media window size %v != 0", pp.winsize)
|
|
}
|
|
// TODO check other values…
|
|
// fmt.Println(pp.Encode().String())
|
|
}
|
|
|
|
// TestDecodeMediaPlaylistAutoDetectExtend tests a very large playlist auto
|
|
// extends to the appropriate size.
|
|
func TestDecodeMediaPlaylistAutoDetectExtend(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/media-playlist-large.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p, listType, err := DecodeFrom(bufio.NewReader(f), true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pp := p.(*MediaPlaylist)
|
|
CheckType(t, pp)
|
|
if listType != MEDIA {
|
|
t.Error("Sample not recognized as media playlist.")
|
|
}
|
|
var exp uint = 40001
|
|
if pp.Count() != exp {
|
|
t.Errorf("Media segment count %v != %v", pp.Count(), exp)
|
|
}
|
|
}
|
|
|
|
// Test for FullTimeParse of EXT-X-PROGRAM-DATE-TIME
|
|
// We testing ISO/IEC 8601:2004 where we can get time in UTC, UTC with Nanoseconds
|
|
// timeZone in formats '±00:00', '±0000', '±00'
|
|
// m3u8.FullTimeParse()
|
|
func TestFullTimeParse(t *testing.T) {
|
|
var timestamps = []struct {
|
|
name string
|
|
value string
|
|
}{
|
|
{"time_in_utc", "2006-01-02T15:04:05Z"},
|
|
{"time_in_utc_nano", "2006-01-02T15:04:05.123456789Z"},
|
|
{"time_with_positive_zone_and_colon", "2006-01-02T15:04:05+01:00"},
|
|
{"time_with_positive_zone_no_colon", "2006-01-02T15:04:05+0100"},
|
|
{"time_with_positive_zone_2digits", "2006-01-02T15:04:05+01"},
|
|
{"time_with_negative_zone_and_colon", "2006-01-02T15:04:05-01:00"},
|
|
{"time_with_negative_zone_no_colon", "2006-01-02T15:04:05-0100"},
|
|
{"time_with_negative_zone_2digits", "2006-01-02T15:04:05-01"},
|
|
}
|
|
|
|
var err error
|
|
for _, tstamp := range timestamps {
|
|
_, err = FullTimeParse(tstamp.value)
|
|
if err != nil {
|
|
t.Errorf("FullTimeParse Error at %s [%s]: %s", tstamp.name, tstamp.value, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Test for StrictTimeParse of EXT-X-PROGRAM-DATE-TIME
|
|
// We testing Strict format of RFC3339 where we can get time in UTC, UTC with Nanoseconds
|
|
// timeZone in formats '±00:00', '±0000', '±00'
|
|
// m3u8.StrictTimeParse()
|
|
func TestStrictTimeParse(t *testing.T) {
|
|
var timestamps = []struct {
|
|
name string
|
|
value string
|
|
}{
|
|
{"time_in_utc", "2006-01-02T15:04:05Z"},
|
|
{"time_in_utc_nano", "2006-01-02T15:04:05.123456789Z"},
|
|
{"time_with_positive_zone_and_colon", "2006-01-02T15:04:05+01:00"},
|
|
{"time_with_negative_zone_and_colon", "2006-01-02T15:04:05-01:00"},
|
|
}
|
|
|
|
var err error
|
|
for _, tstamp := range timestamps {
|
|
_, err = StrictTimeParse(tstamp.value)
|
|
if err != nil {
|
|
t.Errorf("StrictTimeParse Error at %s [%s]: %s", tstamp.name, tstamp.value, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestMediaPlaylistWithOATCLSSCTE35Tag(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/media-playlist-with-oatcls-scte35.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p, _, err := DecodeFrom(bufio.NewReader(f), true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pp := p.(*MediaPlaylist)
|
|
|
|
expect := map[int]*SCTE{
|
|
0: {Syntax: SCTE35_OATCLS, CueType: SCTE35Cue_Start, Cue: "/DAlAAAAAAAAAP/wFAUAAAABf+/+ANgNkv4AFJlwAAEBAQAA5xULLA==", Time: 15},
|
|
1: {Syntax: SCTE35_OATCLS, CueType: SCTE35Cue_Mid, Cue: "/DAlAAAAAAAAAP/wFAUAAAABf+/+ANgNkv4AFJlwAAEBAQAA5xULLA==", Time: 15, Elapsed: 8.844},
|
|
2: {Syntax: SCTE35_OATCLS, CueType: SCTE35Cue_End},
|
|
}
|
|
for i := 0; i < int(pp.Count()); i++ {
|
|
if !reflect.DeepEqual(pp.Segments[i].SCTE, expect[i]) {
|
|
t.Errorf("OATCLS SCTE35 segment %v (uri: %v)\ngot: %#v\nexp: %#v",
|
|
i, pp.Segments[i].URI, pp.Segments[i].SCTE, expect[i],
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************
|
|
* Code parsing examples *
|
|
***************************/
|
|
|
|
// Example of parsing a playlist with EXT-X-DISCONTINIUTY tag
|
|
// and output it with integer segment durations.
|
|
func ExampleMediaPlaylist_DurationAsInt() {
|
|
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:0
|
|
// #EXT-X-TARGETDURATION:10
|
|
// #EXTINF:10,
|
|
// ad0.ts
|
|
// #EXTINF:8,
|
|
// ad1.ts
|
|
// #EXT-X-DISCONTINUITY
|
|
// #EXTINF:10,
|
|
// movieA.ts
|
|
// #EXTINF:10,
|
|
// movieB.ts
|
|
}
|
|
|
|
func TestMediaPlaylistWithSCTE35Tag(t *testing.T) {
|
|
test_cases := []struct {
|
|
playlistLocation string
|
|
expectedSCTEIndex int
|
|
expectedSCTECue string
|
|
expectedSCTEID string
|
|
expectedSCTETime float64
|
|
}{
|
|
{
|
|
"sample-playlists/media-playlist-with-scte35.m3u8",
|
|
2,
|
|
"/DAIAAAAAAAAAAAQAAZ/I0VniQAQAgBDVUVJQAAAAH+cAAAAAA==",
|
|
"123",
|
|
123.12,
|
|
},
|
|
{
|
|
"sample-playlists/media-playlist-with-scte35-1.m3u8",
|
|
1,
|
|
"/DAIAAAAAAAAAAAQAAZ/I0VniQAQAgBDVUVJQAA",
|
|
"",
|
|
0,
|
|
},
|
|
}
|
|
for _, c := range test_cases {
|
|
f, _ := os.Open(c.playlistLocation)
|
|
playlist, _, _ := DecodeFrom(bufio.NewReader(f), true)
|
|
mediaPlaylist := playlist.(*MediaPlaylist)
|
|
for index, item := range mediaPlaylist.Segments {
|
|
if item == nil {
|
|
break
|
|
}
|
|
if index != c.expectedSCTEIndex && item.SCTE != nil {
|
|
t.Error("Not expecting SCTE information on this segment")
|
|
} else if index == c.expectedSCTEIndex && item.SCTE == nil {
|
|
t.Error("Expecting SCTE information on this segment")
|
|
} else if index == c.expectedSCTEIndex && item.SCTE != nil {
|
|
if (*item.SCTE).Cue != c.expectedSCTECue {
|
|
t.Error("Expected ", c.expectedSCTECue, " got ", (*item.SCTE).Cue)
|
|
} else if (*item.SCTE).ID != c.expectedSCTEID {
|
|
t.Error("Expected ", c.expectedSCTEID, " got ", (*item.SCTE).ID)
|
|
} else if (*item.SCTE).Time != c.expectedSCTETime {
|
|
t.Error("Expected ", c.expectedSCTETime, " got ", (*item.SCTE).Time)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestDecodeMediaPlaylistWithProgramDateTime(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/media-playlist-with-program-date-time.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p, listType, err := DecodeFrom(bufio.NewReader(f), true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pp := p.(*MediaPlaylist)
|
|
CheckType(t, pp)
|
|
if listType != MEDIA {
|
|
t.Error("Sample not recognized as media playlist.")
|
|
}
|
|
// check parsed values
|
|
if pp.TargetDuration != 15 {
|
|
t.Errorf("TargetDuration of parsed playlist = %f (must = 15.0)", pp.TargetDuration)
|
|
}
|
|
|
|
if pp.Live {
|
|
t.Error("VOD sample media playlist, closed should be true.")
|
|
}
|
|
|
|
if pp.SeqNo != 0 {
|
|
t.Error("Media sequence defined in sample playlist is 0")
|
|
}
|
|
|
|
segNames := []string{"20181231/0555e0c371ea801726b92512c331399d_00000000.ts",
|
|
"20181231/0555e0c371ea801726b92512c331399d_00000001.ts",
|
|
"20181231/0555e0c371ea801726b92512c331399d_00000002.ts",
|
|
"20181231/0555e0c371ea801726b92512c331399d_00000003.ts"}
|
|
if pp.Count() != uint(len(segNames)) {
|
|
t.Errorf("Segments in playlist %d != %d", pp.Count(), len(segNames))
|
|
}
|
|
|
|
for idx, name := range segNames {
|
|
if pp.Segments[idx].URI != name {
|
|
t.Errorf("Segment name mismatch (%d/%d): %s != %s", idx, pp.Count(), pp.Segments[idx].Title, name)
|
|
}
|
|
}
|
|
|
|
// The ProgramDateTime of the 1st segment should be: 2018-12-31T09:47:22+08:00
|
|
st, _ := time.Parse(time.RFC3339, "2018-12-31T09:47:22+08:00")
|
|
if !pp.Segments[0].ProgramDateTime.Equal(st) {
|
|
t.Errorf("The program date time of the 1st segment should be: %v, actual value: %v",
|
|
st, pp.Segments[0].ProgramDateTime)
|
|
}
|
|
}
|
|
func TestDecodeMediaPlaylistWithDateRange(t *testing.T) {
|
|
f, err := os.Open("sample-playlists/media-playlist-with-date-range.m3u8")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
p, listType, err := DecodeFrom(bufio.NewReader(f), true)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
pp := p.(*MediaPlaylist)
|
|
CheckType(t, pp)
|
|
if listType != MEDIA {
|
|
t.Error("Sample not recognized as media playlist.")
|
|
}
|
|
|
|
for _, s := range pp.Segments[0:pp.Count()] {
|
|
if s.DateRange.ID == "" {
|
|
t.Errorf("Media segment date range ID cannot be empty")
|
|
}
|
|
|
|
if s.DateRange.StartDate.IsZero() {
|
|
t.Errorf("Media segment date range Start Date cannot be empty")
|
|
}
|
|
}
|
|
}
|
|
/****************
|
|
* Benchmarks *
|
|
****************/
|
|
|
|
func BenchmarkDecodeMasterPlaylist(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
f, err := os.Open("sample-playlists/master.m3u8")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
p := NewMasterPlaylist()
|
|
if err := p.DecodeFrom(bufio.NewReader(f), false); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func BenchmarkDecodeMediaPlaylist(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
f, err := os.Open("sample-playlists/media-playlist-large.m3u8")
|
|
if err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
p, err := NewMediaPlaylist(50000, 50000)
|
|
if err != nil {
|
|
b.Fatalf("Create media playlist failed: %s", err)
|
|
}
|
|
if err = p.DecodeFrom(bufio.NewReader(f), true); err != nil {
|
|
b.Fatal(err)
|
|
}
|
|
}
|
|
}
|