From 1fd127fe6dccfda487ef422a3d4b0d5c9fa39b21 Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Wed, 30 Aug 2023 12:25:04 -0700 Subject: [PATCH 01/13] WiP --- pkg/pipeline/sink/m3u8/writer.go | 146 ++++++++++++++++++++++++++----- 1 file changed, 125 insertions(+), 21 deletions(-) diff --git a/pkg/pipeline/sink/m3u8/writer.go b/pkg/pipeline/sink/m3u8/writer.go index 93416ecc..718264dc 100644 --- a/pkg/pipeline/sink/m3u8/writer.go +++ b/pkg/pipeline/sink/m3u8/writer.go @@ -15,6 +15,7 @@ package m3u8 import ( + "container/list" "fmt" "io/fs" "os" @@ -23,15 +24,43 @@ import ( "time" ) -type PlaylistWriter struct { +type PlaylistType string + +const ( + PlaylistTypeLive PlaylistType = "" + PlaylistTypeEvent PlaylistType = "EVENT" +) + +type PlaylistWriter interface { + Append(dateTime time.Time, duration float64, filename string) error + Close() error +} + +type basePlaylistWriter struct { filename string targetDuration int } -func NewPlaylistWriter(filename string, targetDuration int) (*PlaylistWriter, error) { - p := &PlaylistWriter{ - filename: filename, - targetDuration: targetDuration, +type eventPlaylistWriter struct { + basePlaylistWriter +} + +type livePlaylistWriter struct { + basePlaylistWriter + + windowSize int + mediaSeq int + + livePlaylistHeader string + livePlaylistSegments *list.List +} + +func NewEventPlaylistWriter(filename string, targetDuration int) (PlaylistWriter, error) { + p := &eventPlaylistWriter{ + basePlaylistWriter: basePlaylistWriter{ + filename: filename, + targetDuration: targetDuration, + }, } f, err := os.Create(p.filename) @@ -40,15 +69,7 @@ func NewPlaylistWriter(filename string, targetDuration int) (*PlaylistWriter, er } defer f.Close() - var sb strings.Builder - sb.WriteString("#EXTM3U\n") - sb.WriteString("#EXT-X-VERSION:4\n") - sb.WriteString("#EXT-X-PLAYLIST-TYPE:EVENT\n") - sb.WriteString("#EXT-X-ALLOW-CACHE:NO\n") - sb.WriteString("#EXT-X-MEDIA-SEQUENCE:0\n") - sb.WriteString(fmt.Sprintf("#EXT-X-TARGETDURATION:%d\n", p.targetDuration)) - - _, err = f.WriteString(sb.String()) + _, err = f.WriteString(p.createHeader(PlaylistTypeEvent)) if err != nil { return nil, err } @@ -56,14 +77,40 @@ func NewPlaylistWriter(filename string, targetDuration int) (*PlaylistWriter, er return p, nil } -func (p *PlaylistWriter) Append(dateTime time.Time, duration float64, filename string) error { - f, err := os.OpenFile(p.filename, os.O_WRONLY|os.O_APPEND, fs.ModeAppend) - if err != nil { - return err +func NewLivePlaylistWriter(filename string, targetDuration int, windowSize int) (PlaylistWriter, error) { + p := &livePlaylistWriter{ + basePlaylistWriter: basePlaylistWriter{ + filename: filename, + targetDuration: targetDuration, + }, + windowSize: windowSize, + livePlaylistSegments: list.New(), + } + + p.livePlaylistHeader = p.createHeader(PlaylistTypeLive) + + return p, nil +} + +func (p *basePlaylistWriter) createHeader(plType PlaylistType) string { + var sb strings.Builder + sb.WriteString("#EXTM3U\n") + sb.WriteString("#EXT-X-VERSION:4\n") + if plType != PlaylistTypeLive { + sb.WriteString(fmt.Sprintf("#EXT-X-PLAYLIST-TYPE:%s\n", plType)) + } + sb.WriteString("#EXT-X-ALLOW-CACHE:NO\n") + sb.WriteString(fmt.Sprintf("#EXT-X-TARGETDURATION:%d\n", p.targetDuration)) + if plType != PlaylistTypeLive { + sb.WriteString("#EXT-X-MEDIA-SEQUENCE:0\n") } - defer f.Close() + return sb.String() +} + +func (p *basePlaylistWriter) createSegmentEntry(dateTime time.Time, duration float64, filename string) string { var sb strings.Builder + sb.WriteString("#EXT-X-PROGRAM-DATE-TIME:") sb.WriteString(dateTime.UTC().Format("2006-01-02T15:04:05.999Z07:00")) sb.WriteString("\n#EXTINF:") @@ -72,12 +119,22 @@ func (p *PlaylistWriter) Append(dateTime time.Time, duration float64, filename s sb.WriteString(filename) sb.WriteString("\n") - _, err = f.WriteString(sb.String()) + return sb.String() +} + +func (p *eventPlaylistWriter) Append(dateTime time.Time, duration float64, filename string) error { + f, err := os.OpenFile(p.filename, os.O_WRONLY|os.O_APPEND, fs.ModeAppend) + if err != nil { + return err + } + defer f.Close() + + _, err = f.WriteString(p.createSegmentEntry(dateTime, duration, filename)) return err } // Close sliding playlist and make them fixed. -func (p *PlaylistWriter) Close() error { +func (p *eventPlaylistWriter) Close() error { f, err := os.OpenFile(p.filename, os.O_WRONLY|os.O_APPEND, fs.ModeAppend) if err != nil { return err @@ -87,3 +144,50 @@ func (p *PlaylistWriter) Close() error { _, err = f.WriteString("#EXT-X-ENDLIST\n") return err } + +func (p *livePlaylistWriter) Append(dateTime time.Time, duration float64, filename string) error { + f, err := os.OpenFile(p.filename, os.O_WRONLY|os.O_CREATE, 0) + if err != nil { + return err + } + defer f.Close() + + segmentStr := p.createSegmentEntry(dateTime, duration, filename) + p.livePlaylistSegments.PushBack(segmentStr) + + for p.livePlaylistSegments.Len() > p.windowSize { + p.livePlaylistSegments.Remove(p.livePlaylistSegments.Front()) + p.mediaSeq++ + } + + _, err = f.WriteString(p.generatePlaylist()) + return err +} + +func (p *livePlaylistWriter) Close() error { + f, err := os.OpenFile(p.filename, os.O_WRONLY|os.O_APPEND, fs.ModeAppend) + if err != nil { + return err + } + defer f.Close() + + _, err = f.WriteString(p.generatePlaylist()) + if err != nil { + return err + } + + _, err = f.WriteString("#EXT-X-ENDLIST\n") + return err +} + +func (p *livePlaylistWriter) generatePlaylist() string { + var sb strings.Builder + sb.WriteString(p.livePlaylistHeader) + sb.WriteString(fmt.Sprintf("#EXT-X-MEDIA-SEQUENCE:%d\n", p.mediaSeq)) + for elem := p.livePlaylistSegments.Front(); elem != nil; elem = elem.Next() { + segmentStr := elem.Value.(string) + sb.WriteString(segmentStr) + } + + return sb.String() +} From 642b311232d7cd9b02e4a164458d83122843a67b Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Wed, 30 Aug 2023 12:26:29 -0700 Subject: [PATCH 02/13] Fix event playlist test --- pkg/pipeline/sink/m3u8/writer_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/pipeline/sink/m3u8/writer_test.go b/pkg/pipeline/sink/m3u8/writer_test.go index 385b5689..03671789 100644 --- a/pkg/pipeline/sink/m3u8/writer_test.go +++ b/pkg/pipeline/sink/m3u8/writer_test.go @@ -26,7 +26,7 @@ import ( func TestPlaylistWriter(t *testing.T) { playlistName := "playlist.m3u8" - w, err := NewPlaylistWriter(playlistName, 6) + w, err := NewEventPlaylistWriter(playlistName, 6) require.NoError(t, err) t.Cleanup(func() { _ = os.Remove(playlistName) }) @@ -44,6 +44,6 @@ func TestPlaylistWriter(t *testing.T) { b, err := os.ReadFile(playlistName) require.NoError(t, err) - expected := "#EXTM3U\n#EXT-X-VERSION:4\n#EXT-X-PLAYLIST-TYPE:EVENT\n#EXT-X-ALLOW-CACHE:NO\n#EXT-X-MEDIA-SEQUENCE:0\n#EXT-X-TARGETDURATION:6\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:04.814Z\n#EXTINF:5.994,\nplaylist_00000.ts\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:10.808Z\n#EXTINF:5.994,\nplaylist_00001.ts\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:16.802Z\n#EXTINF:5.994,\nplaylist_00002.ts\n#EXT-X-ENDLIST\n" + expected := "#EXTM3U\n#EXT-X-VERSION:4\n#EXT-X-PLAYLIST-TYPE:EVENT\n#EXT-X-ALLOW-CACHE:NO\n#EXT-X-TARGETDURATION:6\n#EXT-X-MEDIA-SEQUENCE:0\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:04.814Z\n#EXTINF:5.994,\nplaylist_00000.ts\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:10.808Z\n#EXTINF:5.994,\nplaylist_00001.ts\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:16.802Z\n#EXTINF:5.994,\nplaylist_00002.ts\n#EXT-X-ENDLIST\n" require.Equal(t, expected, string(b)) } From 5cfe6070f4084e91c188fe4991c0590df58be510 Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Wed, 30 Aug 2023 14:33:20 -0700 Subject: [PATCH 03/13] Test --- pkg/pipeline/sink/m3u8/writer.go | 78 +++++++++++++-------------- pkg/pipeline/sink/m3u8/writer_test.go | 38 ++++++++++++- 2 files changed, 76 insertions(+), 40 deletions(-) diff --git a/pkg/pipeline/sink/m3u8/writer.go b/pkg/pipeline/sink/m3u8/writer.go index 718264dc..30ee63b9 100644 --- a/pkg/pipeline/sink/m3u8/writer.go +++ b/pkg/pipeline/sink/m3u8/writer.go @@ -55,43 +55,6 @@ type livePlaylistWriter struct { livePlaylistSegments *list.List } -func NewEventPlaylistWriter(filename string, targetDuration int) (PlaylistWriter, error) { - p := &eventPlaylistWriter{ - basePlaylistWriter: basePlaylistWriter{ - filename: filename, - targetDuration: targetDuration, - }, - } - - f, err := os.Create(p.filename) - if err != nil { - return nil, err - } - defer f.Close() - - _, err = f.WriteString(p.createHeader(PlaylistTypeEvent)) - if err != nil { - return nil, err - } - - return p, nil -} - -func NewLivePlaylistWriter(filename string, targetDuration int, windowSize int) (PlaylistWriter, error) { - p := &livePlaylistWriter{ - basePlaylistWriter: basePlaylistWriter{ - filename: filename, - targetDuration: targetDuration, - }, - windowSize: windowSize, - livePlaylistSegments: list.New(), - } - - p.livePlaylistHeader = p.createHeader(PlaylistTypeLive) - - return p, nil -} - func (p *basePlaylistWriter) createHeader(plType PlaylistType) string { var sb strings.Builder sb.WriteString("#EXTM3U\n") @@ -122,6 +85,28 @@ func (p *basePlaylistWriter) createSegmentEntry(dateTime time.Time, duration flo return sb.String() } +func NewEventPlaylistWriter(filename string, targetDuration int) (PlaylistWriter, error) { + p := &eventPlaylistWriter{ + basePlaylistWriter: basePlaylistWriter{ + filename: filename, + targetDuration: targetDuration, + }, + } + + f, err := os.Create(p.filename) + if err != nil { + return nil, err + } + defer f.Close() + + _, err = f.WriteString(p.createHeader(PlaylistTypeEvent)) + if err != nil { + return nil, err + } + + return p, nil +} + func (p *eventPlaylistWriter) Append(dateTime time.Time, duration float64, filename string) error { f, err := os.OpenFile(p.filename, os.O_WRONLY|os.O_APPEND, fs.ModeAppend) if err != nil { @@ -145,8 +130,23 @@ func (p *eventPlaylistWriter) Close() error { return err } +func NewLivePlaylistWriter(filename string, targetDuration int, windowSize int) (PlaylistWriter, error) { + p := &livePlaylistWriter{ + basePlaylistWriter: basePlaylistWriter{ + filename: filename, + targetDuration: targetDuration, + }, + windowSize: windowSize, + livePlaylistSegments: list.New(), + } + + p.livePlaylistHeader = p.createHeader(PlaylistTypeLive) + + return p, nil +} + func (p *livePlaylistWriter) Append(dateTime time.Time, duration float64, filename string) error { - f, err := os.OpenFile(p.filename, os.O_WRONLY|os.O_CREATE, 0) + f, err := os.Create(p.filename) if err != nil { return err } @@ -165,7 +165,7 @@ func (p *livePlaylistWriter) Append(dateTime time.Time, duration float64, filena } func (p *livePlaylistWriter) Close() error { - f, err := os.OpenFile(p.filename, os.O_WRONLY|os.O_APPEND, fs.ModeAppend) + f, err := os.Create(p.filename) if err != nil { return err } diff --git a/pkg/pipeline/sink/m3u8/writer_test.go b/pkg/pipeline/sink/m3u8/writer_test.go index 03671789..8423fd30 100644 --- a/pkg/pipeline/sink/m3u8/writer_test.go +++ b/pkg/pipeline/sink/m3u8/writer_test.go @@ -23,7 +23,7 @@ import ( "github.com/stretchr/testify/require" ) -func TestPlaylistWriter(t *testing.T) { +func TestEventPlaylistWriter(t *testing.T) { playlistName := "playlist.m3u8" w, err := NewEventPlaylistWriter(playlistName, 6) @@ -47,3 +47,39 @@ func TestPlaylistWriter(t *testing.T) { expected := "#EXTM3U\n#EXT-X-VERSION:4\n#EXT-X-PLAYLIST-TYPE:EVENT\n#EXT-X-ALLOW-CACHE:NO\n#EXT-X-TARGETDURATION:6\n#EXT-X-MEDIA-SEQUENCE:0\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:04.814Z\n#EXTINF:5.994,\nplaylist_00000.ts\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:10.808Z\n#EXTINF:5.994,\nplaylist_00001.ts\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:16.802Z\n#EXTINF:5.994,\nplaylist_00002.ts\n#EXT-X-ENDLIST\n" require.Equal(t, expected, string(b)) } + +func TestLivePlaylistWriter(t *testing.T) { + playlistName := "playlist.m3u8" + + w, err := NewLivePlaylistWriter(playlistName, 6, 3) + require.NoError(t, err) + + t.Cleanup(func() { _ = os.Remove(playlistName) }) + + now := time.Unix(0, 1683154504814142000) + duration := 5.994 + + for i := 0; i < 2; i++ { + require.NoError(t, w.Append(now, duration, fmt.Sprintf("playlist_0000%d.ts", i))) + now = now.Add(time.Millisecond * 5994) + } + + b, err := os.ReadFile(playlistName) + require.NoError(t, err) + + expected := "#EXTM3U\n#EXT-X-VERSION:4\n#EXT-X-ALLOW-CACHE:NO\n#EXT-X-TARGETDURATION:6\n#EXT-X-MEDIA-SEQUENCE:0\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:04.814Z\n#EXTINF:5.994,\nplaylist_00000.ts\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:10.808Z\n#EXTINF:5.994,\nplaylist_00001.ts\n" + require.Equal(t, expected, string(b)) + + for i := 2; i < 4; i++ { + require.NoError(t, w.Append(now, duration, fmt.Sprintf("playlist_0000%d.ts", i))) + now = now.Add(time.Millisecond * 5994) + } + + require.NoError(t, w.Close()) + + b, err = os.ReadFile(playlistName) + require.NoError(t, err) + + expected = "#EXTM3U\n#EXT-X-VERSION:4\n#EXT-X-ALLOW-CACHE:NO\n#EXT-X-TARGETDURATION:6\n#EXT-X-MEDIA-SEQUENCE:1\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:04.814Z\n#EXTINF:5.994,\nplaylist_00001.ts\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:16.802Z\n#EXTINF:5.994,\nplaylist_00002.ts\n#EXT-X-PROGRAM-DATE-TIME:2023-05-03T22:55:22.796Z\n#EXTINF:5.994,\nplaylist_00003.ts\n#EXT-X-ENDLIST\n" + require.Equal(t, expected, string(b)) +} From f23c9ab398adf8b6eeb1bcb48ca6d97aa49df4bb Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Wed, 30 Aug 2023 16:01:47 -0700 Subject: [PATCH 04/13] WiP --- go.mod | 40 +++++++-------- go.sum | 96 +++++++++++++++--------------------- pkg/config/config_test.go | 58 ++++++++++++---------- pkg/config/output_segment.go | 70 ++++++++++++++++++-------- pkg/config/pipeline.go | 2 + 5 files changed, 141 insertions(+), 125 deletions(-) diff --git a/go.mod b/go.mod index 28535e8c..d5b2cf93 100644 --- a/go.mod +++ b/go.mod @@ -19,11 +19,11 @@ require ( github.com/gorilla/websocket v1.5.0 github.com/livekit/livekit-server v1.4.5-0.20230814182001-77c8e824735b github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 - github.com/livekit/protocol v1.6.0 - github.com/livekit/psrpc v0.3.2 + github.com/livekit/protocol v1.6.2-0.20230830214603-ce275be4e6a5 + github.com/livekit/psrpc v0.3.3 github.com/livekit/server-sdk-go v1.0.16-0.20230815025737-c12cd2eb8fe8 github.com/pion/rtp v1.8.1 - github.com/pion/webrtc/v3 v3.2.14 + github.com/pion/webrtc/v3 v3.2.16 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.16.0 github.com/stretchr/testify v1.8.4 @@ -31,7 +31,7 @@ require ( github.com/tinyzimmer/go-gst v0.2.33 github.com/urfave/cli/v2 v2.25.7 go.uber.org/atomic v1.11.0 - go.uber.org/zap v1.24.0 + go.uber.org/zap v1.25.0 google.golang.org/api v0.130.0 google.golang.org/grpc v1.57.0 google.golang.org/protobuf v1.31.0 @@ -39,12 +39,11 @@ require ( ) require ( - cloud.google.com/go v0.110.2 // indirect - cloud.google.com/go/compute v1.19.3 // indirect + cloud.google.com/go v0.110.6 // indirect + cloud.google.com/go/compute v1.23.0 // indirect cloud.google.com/go/compute/metadata v0.2.3 // indirect - cloud.google.com/go/iam v1.1.0 // indirect + cloud.google.com/go/iam v1.1.1 // indirect github.com/Azure/azure-pipeline-go v0.2.3 // indirect - github.com/benbjohnson/clock v1.3.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bep/debounce v1.2.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect @@ -70,7 +69,7 @@ require ( github.com/jmespath/go-jmespath v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/jxskiss/base62 v1.1.0 // indirect - github.com/klauspost/compress v1.16.5 // indirect + github.com/klauspost/compress v1.16.7 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/lithammer/shortuuid/v4 v4.0.0 // indirect github.com/livekit/mediatransportutil v0.0.0-20230814030822-8d5de0008b08 // indirect @@ -80,7 +79,7 @@ require ( github.com/mattn/go-ieproxy v0.0.1 // indirect github.com/mattn/go-pointer v0.0.1 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect - github.com/nats-io/nats.go v1.26.0 // indirect + github.com/nats-io/nats.go v1.28.0 // indirect github.com/nats-io/nkeys v0.4.4 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/pion/datachannel v1.5.5 // indirect @@ -101,26 +100,25 @@ require ( github.com/prometheus/client_model v0.3.0 // indirect github.com/prometheus/common v0.42.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect - github.com/redis/go-redis/v9 v9.0.5 // indirect + github.com/redis/go-redis/v9 v9.1.0 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/thoas/go-funk v0.9.3 // indirect github.com/twitchtv/twirp v8.1.3+incompatible // indirect github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect go.opencensus.io v0.24.0 // indirect - go.uber.org/goleak v1.1.12 // indirect - go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.11.0 // indirect - golang.org/x/exp v0.0.0-20230810033253-352e893a4cad // indirect - golang.org/x/net v0.13.0 // indirect + go.uber.org/multierr v1.10.0 // indirect + golang.org/x/crypto v0.12.0 // indirect + golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 // indirect + golang.org/x/net v0.14.0 // indirect golang.org/x/oauth2 v0.9.0 // indirect golang.org/x/sync v0.3.0 // indirect - golang.org/x/sys v0.10.0 // indirect - golang.org/x/text v0.11.0 // indirect + golang.org/x/sys v0.11.0 // indirect + golang.org/x/text v0.12.0 // indirect golang.org/x/time v0.3.0 // indirect golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 // indirect + google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 // indirect ) diff --git a/go.sum b/go.sum index d0281fc6..49ec9317 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,13 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.110.2 h1:sdFPBr6xG9/wkBbfhmUz/JmZC7X6LavQgcrVINrKiVA= -cloud.google.com/go v0.110.2/go.mod h1:k04UEeEtb6ZBRTv3dZz4CeJC3jKGxyhl0sAiVVquxiw= -cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds= -cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI= +cloud.google.com/go v0.110.6 h1:8uYAkj3YHTP/1iwReuHPxLSbdcyc+dSBbzFMrVwDR6Q= +cloud.google.com/go v0.110.6/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute v1.23.0/go.mod h1:4tCnrn48xsqlwSAiLf1HXMQk8CONslYbdiEZc9FEIbM= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= -cloud.google.com/go/iam v1.1.0 h1:67gSqaPukx7O8WLLHMa0PNs3EBGd2eE4d+psbO/CO94= -cloud.google.com/go/iam v1.1.0/go.mod h1:nxdHjaKfCr7fNYx/HJMM8LgiMugmveWlkatear5gVyk= +cloud.google.com/go/iam v1.1.1 h1:lW7fzj15aVIXYHREOqjRBV9PsH0Z6u8Y46a1YGvQP4Y= +cloud.google.com/go/iam v1.1.1/go.mod h1:A5avdyVL2tCppe4unb0951eI9jreack+RJ0/d+KUZOU= cloud.google.com/go/storage v1.31.0 h1:+S3LjjEN2zZ+L5hOwj4+1OkGCsLVe0NzpXKQ1pSdTCI= cloud.google.com/go/storage v1.31.0/go.mod h1:81ams1PrhW16L4kF7qg+4mTq7SRs5HsbDTM0bWvrwJ0= github.com/Azure/azure-pipeline-go v0.2.3 h1:7U9HBg1JFK3jHl5qmo4CTZKFTVgMwdFHMVtCdfBE21U= @@ -32,12 +32,11 @@ github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kd github.com/aws/aws-sdk-go v1.44.296 h1:ALRZIIKI+6EBWDiWP4RHWmOtHZ7dywRzenL4NWgNI2A= github.com/aws/aws-sdk-go v1.44.296/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/bep/debounce v1.2.1 h1:v67fRdBA9UQu2NhLFXrSg0Brw7CexQekrBwDMM8bzeY= github.com/bep/debounce v1.2.1/go.mod h1:H8yggRPQKLUhUoqrJC1bO2xNya7vanpDl7xR3ISbCJ0= -github.com/bsm/ginkgo/v2 v2.7.0 h1:ItPMPH90RbmZJt5GtkcNvIRuGEdwlBItdNVoyzaNQao= +github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0= github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -153,8 +152,8 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jxskiss/base62 v1.1.0 h1:A5zbF8v8WXx2xixnAKD2w+abC+sIzYJX+nxmhA6HWFw= github.com/jxskiss/base62 v1.1.0/go.mod h1:HhWAlUXvxKThfOlZbcuFzsqwtF5TcqS9ru3y5GfjWAc= -github.com/klauspost/compress v1.16.5 h1:IFV2oUNUzZaz+XyusxpLzpzS8Pt5rh0Z16For/djlyI= -github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I= +github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -177,10 +176,10 @@ github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkD github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= github.com/livekit/mediatransportutil v0.0.0-20230814030822-8d5de0008b08 h1:e0qwVjrtzmADgNZpdgSJgyhlF6BgrHkpdnkONL8pLrw= github.com/livekit/mediatransportutil v0.0.0-20230814030822-8d5de0008b08/go.mod h1:xirUXW8xnLGmfCwUeAv/nj1VGo1OO1BmgxrYP7jK/14= -github.com/livekit/protocol v1.6.0 h1:19S+vFZqnivKIOpyR3DEK/mSaykQ3UEf7H2G/mBOE54= -github.com/livekit/protocol v1.6.0/go.mod h1:SUS9foM1xBzw/AFrgTJuFX/oSuwlnIbHmpdiPdCvwEM= -github.com/livekit/psrpc v0.3.2 h1:eAaJhASme33gtoBhCRLH9jsnWcdm1tHWf0WzaDk56ew= -github.com/livekit/psrpc v0.3.2/go.mod h1:n6JntEg+zT6Ji8InoyTpV7wusPNwGqqtxmHlkNhDN0U= +github.com/livekit/protocol v1.6.2-0.20230830214603-ce275be4e6a5 h1:/S16yVnnoBPDzsbDj2/vRZiC4mPfxP6/RorOJcSQVeU= +github.com/livekit/protocol v1.6.2-0.20230830214603-ce275be4e6a5/go.mod h1:/JuO+G/btZ5gNwX2+901L6za3UvVO6DHRXHsv8kkLsU= +github.com/livekit/psrpc v0.3.3 h1:+lltbuN39IdaynXhLLxRShgYqYsRMWeeXKzv60oqyWo= +github.com/livekit/psrpc v0.3.3/go.mod h1:n6JntEg+zT6Ji8InoyTpV7wusPNwGqqtxmHlkNhDN0U= github.com/livekit/server-sdk-go v1.0.16-0.20230815025737-c12cd2eb8fe8 h1:Vm64l3U2rWAB2IdG/0/CXIZTgVPmldsQrDAOcq+zQQs= github.com/livekit/server-sdk-go v1.0.16-0.20230815025737-c12cd2eb8fe8/go.mod h1:4OIkcDpQJuOQJEThr+Z8+WQfkcs12BXC+9cuhigLnDM= github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs= @@ -198,8 +197,8 @@ github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfr github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g= github.com/nats-io/jwt/v2 v2.3.0 h1:z2mA1a7tIf5ShggOFlR1oBPgd6hGqcDYsISxZByUzdI= github.com/nats-io/nats-server/v2 v2.9.8 h1:jgxZsv+A3Reb3MgwxaINcNq/za8xZInKhDg9Q0cGN1o= -github.com/nats-io/nats.go v1.26.0 h1:fWJTYPnZ8DzxIaqIHOAMfColuznchnd5Ab5dbJpgPIE= -github.com/nats-io/nats.go v1.26.0/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc= +github.com/nats-io/nats.go v1.28.0 h1:Th4G6zdsz2d0OqXdfzKLClo6bOfoI/b1kInhRtFIy5c= +github.com/nats-io/nats.go v1.28.0/go.mod h1:XpbWUlOElGwTYbMR7imivs7jJj9GtK7ypv321Wp6pjc= github.com/nats-io/nkeys v0.4.4 h1:xvBJ8d69TznjcQl9t6//Q5xXuVhyYiSos6RPtvQNTwA= github.com/nats-io/nkeys v0.4.4/go.mod h1:XUkxdLPTufzlihbamfzQ7mw/VGx6ObUs+0bN5sNvt64= github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw= @@ -219,7 +218,6 @@ github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0= github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/ice/v2 v2.3.9/go.mod h1:lT3kv5uUIlHfXHU/ZRD7uKD/ufM202+eTa3C/umgGf4= github.com/pion/ice/v2 v2.3.10 h1:T3bUJKqh7pGEdMyTngUcTeQd6io9X8JjgsVWZDannnY= github.com/pion/ice/v2 v2.3.10/go.mod h1:hHGCibDfmXGqukayQw979xEctASp2Pe5Oe0iDU8pRus= github.com/pion/interceptor v0.1.17 h1:prJtgwFh/gB8zMqGZoOgJPHivOwVAp61i2aG61Du/1w= @@ -252,11 +250,10 @@ github.com/pion/transport/v2 v2.1.0/go.mod h1:AdSw4YBZVDkZm8fpoz+fclXyQwANWmZAlD github.com/pion/transport/v2 v2.2.0/go.mod h1:AdSw4YBZVDkZm8fpoz+fclXyQwANWmZAlDuQdctTThQ= github.com/pion/transport/v2 v2.2.1 h1:7qYnCBlpgSJNYMbLCKuSY9KbQdBFoETvPNETv0y4N7c= github.com/pion/transport/v2 v2.2.1/go.mod h1:cXXWavvCnFF6McHTft3DWS9iic2Mftcz1Aq29pGcU5g= -github.com/pion/turn/v2 v2.1.2/go.mod h1:1kjnPkBcex3dhCU2Am+AAmxDcGhLX3WnMfmkNpvSTQU= github.com/pion/turn/v2 v2.1.3 h1:pYxTVWG2gpC97opdRc5IGsQ1lJ9O/IlNhkzj7MMrGAA= github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= -github.com/pion/webrtc/v3 v3.2.14 h1:GlqnBnnLlcYYA/LOwqLLU1plZYwx0Y/e/57bZ2tzQcU= -github.com/pion/webrtc/v3 v3.2.14/go.mod h1:r1mtixc2MH847mmQTPwlEvGge7D18C2T5qp8jI9Lm44= +github.com/pion/webrtc/v3 v3.2.16 h1:2tfQ8qdyUAjeG5Zn44yE98umMtdxuHembJ3WYhj4Zd4= +github.com/pion/webrtc/v3 v3.2.16/go.mod h1:vm5dipobPQGXn2hNyQ+hh2KbTTTaDxJiDcM+MyAyrsc= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -270,8 +267,8 @@ github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= -github.com/redis/go-redis/v9 v9.0.5 h1:CuQcn5HIEeK7BgElubPP8CGtE0KakrnbBSTLjathl5o= -github.com/redis/go-redis/v9 v9.0.5/go.mod h1:WqMKv5vnQbRuZstUwxQI195wHy+t4PuXDOjzMvcuQHk= +github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= +github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= @@ -280,7 +277,6 @@ github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -301,7 +297,6 @@ github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6S github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= @@ -309,15 +304,13 @@ github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaD go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= -go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= +go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ= +go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c= +go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -327,18 +320,16 @@ golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPh golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220314234659-1baeb1ce4c0b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= -golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20230810033253-352e893a4cad h1:g0bG7Z4uG+OgH2QDODnjp6ggkk1bJDsINcuWmJN1iJU= -golang.org/x/exp v0.0.0-20230810033253-352e893a4cad/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63 h1:m64FZMko/V45gv0bNmrNYoDEq8U5YUhetc9cBWKS1TQ= +golang.org/x/exp v0.0.0-20230817173708-d852ddb80c63/go.mod h1:0v4NqG35kSWCMzLaMeX+IQrlSnVE/bqGSyC2cz/9Le8= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -356,7 +347,6 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= @@ -367,10 +357,9 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= -golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= -golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY= golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.9.0 h1:BPpt2kU7oMRq3kCHAA1tbSEshXRw1LpG2ztgDwrzuAs= @@ -380,7 +369,6 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= @@ -398,9 +386,7 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -412,8 +398,9 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= @@ -422,7 +409,6 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= @@ -435,9 +421,9 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -447,7 +433,6 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3 golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -466,12 +451,12 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc h1:8DyZCyvI8mE1IdLy/60bS+52xfymkE72wv1asokgtao= -google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc h1:kVKPf/IiYSBWEWtkIn6wZXwWGCnLKcC8oWfZvXjsGnM= -google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529 h1:DEH99RbiLZhMxrpEJCZ0A+wdTe0EOgou/poSLx9vWf4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20230629202037-9506855d4529/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5 h1:L6iMMGrtzgHsWofoFcihmDEMYeDR9KN/ThbPWGrh++g= +google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= +google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e h1:z3vDksarJxsAKM5dmEGv0GHwE2hKJ096wZra71Vs4sw= +google.golang.org/genproto/googleapis/api v0.0.0-20230726155614-23370e0ffb3e/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878 h1:lv6/DhyiFFGsmzxbsUUTOkN29II+zeWHxvT8Lpdxsv0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230815205213-6bfd019c3878/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -496,7 +481,6 @@ google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQ google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/pkg/config/config_test.go b/pkg/config/config_test.go index cd9c1825..246820bd 100644 --- a/pkg/config/config_test.go +++ b/pkg/config/config_test.go @@ -134,62 +134,66 @@ func TestSegmentNaming(t *testing.T) { }) for _, test := range []struct { - filenamePrefix string - playlistName string - expectedStorageDir string - expectedPlaylistFilename string - expectedSegmentPrefix string + filenamePrefix string + playlistName string + livePlaylistName string + expectedStorageDir string + expectedPlaylistFilename string + expectedLivePlaylistFilename string + expectedSegmentPrefix string }{ { - filenamePrefix: "", playlistName: "playlist", - expectedStorageDir: "", expectedPlaylistFilename: "playlist.m3u8", expectedSegmentPrefix: "playlist", + filenamePrefix: "", playlistName: "playlist", livePlaylistName: "", + expectedStorageDir: "", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "playlist", }, { - filenamePrefix: "", playlistName: "conf_test/playlist", - expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedSegmentPrefix: "playlist", + filenamePrefix: "", playlistName: "conf_test/playlist", livePlaylistName: "conf_test/live_playlist", + expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "live_playlist.m3u8", expectedSegmentPrefix: "playlist", }, { - filenamePrefix: "filename", playlistName: "", - expectedStorageDir: "", expectedPlaylistFilename: "filename.m3u8", expectedSegmentPrefix: "filename", + filenamePrefix: "filename", playlistName: "", livePlaylistName: "live_playlist2.m3u8", + expectedStorageDir: "", expectedPlaylistFilename: "filename.m3u8", expectedLivePlaylistFilename: "live_playlist2.m3u8", expectedSegmentPrefix: "filename", }, { - filenamePrefix: "filename", playlistName: "playlist", - expectedStorageDir: "", expectedPlaylistFilename: "playlist.m3u8", expectedSegmentPrefix: "filename", + filenamePrefix: "filename", playlistName: "playlist", livePlaylistName: "", + expectedStorageDir: "", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "filename", }, { - filenamePrefix: "filename", playlistName: "conf_test/", - expectedStorageDir: "conf_test/", expectedPlaylistFilename: "filename.m3u8", expectedSegmentPrefix: "filename", + filenamePrefix: "filename", playlistName: "conf_test/", livePlaylistName: "", + expectedStorageDir: "conf_test/", expectedPlaylistFilename: "filename.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "filename", }, { - filenamePrefix: "filename", playlistName: "conf_test/playlist", - expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedSegmentPrefix: "filename", + filenamePrefix: "filename", playlistName: "conf_test/playlist", livePlaylistName: "", + expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "filename", }, { - filenamePrefix: "conf_test/", playlistName: "playlist", - expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedSegmentPrefix: "playlist", + filenamePrefix: "conf_test/", playlistName: "playlist", livePlaylistName: "", + expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "playlist", }, { - filenamePrefix: "conf_test/filename", playlistName: "playlist", - expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedSegmentPrefix: "filename", + filenamePrefix: "conf_test/filename", playlistName: "playlist", livePlaylistName: "", + expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "filename", }, { - filenamePrefix: "conf_test/filename", playlistName: "conf_test/playlist", - expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedSegmentPrefix: "filename", + filenamePrefix: "conf_test/filename", playlistName: "conf_test/playlist", livePlaylistName: "", + expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "filename", }, { - filenamePrefix: "conf_test_2/filename", playlistName: "conf_test/playlist", - expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedSegmentPrefix: "conf_test_2/filename", + filenamePrefix: "conf_test_2/filename", playlistName: "conf_test/playlist", livePlaylistName: "", + expectedStorageDir: "conf_test/", expectedPlaylistFilename: "playlist.m3u8", expectedLivePlaylistFilename: "", expectedSegmentPrefix: "conf_test_2/filename", }, } { p := &PipelineConfig{Info: &livekit.EgressInfo{EgressId: "egress_ID"}} o, err := p.getSegmentConfig(&livekit.SegmentedFileOutput{ - FilenamePrefix: test.filenamePrefix, - PlaylistName: test.playlistName, + FilenamePrefix: test.filenamePrefix, + PlaylistName: test.playlistName, + LivePlaylistName: test.livePlaylistName, }) require.NoError(t, err) require.Equal(t, test.expectedStorageDir, o.StorageDir) require.Equal(t, test.expectedPlaylistFilename, o.PlaylistFilename) + require.Equal(t, test.expectedLivePlaylistFilename, o.LivePlaylistFilename) require.Equal(t, test.expectedSegmentPrefix, o.SegmentPrefix) } } diff --git a/pkg/config/output_segment.go b/pkg/config/output_segment.go index 99dfcf53..fb07b52d 100644 --- a/pkg/config/output_segment.go +++ b/pkg/config/output_segment.go @@ -21,6 +21,7 @@ import ( "strings" "time" + "github.com/livekit/egress/pkg/errors" "github.com/livekit/egress/pkg/types" "github.com/livekit/protocol/livekit" ) @@ -28,13 +29,14 @@ import ( type SegmentConfig struct { outputConfig - SegmentsInfo *livekit.SegmentsInfo - LocalDir string - StorageDir string - PlaylistFilename string - SegmentPrefix string - SegmentSuffix livekit.SegmentedFileSuffix - SegmentDuration int + SegmentsInfo *livekit.SegmentsInfo + LocalDir string + StorageDir string + PlaylistFilename string + LivePlaylistFilename string + SegmentPrefix string + SegmentSuffix livekit.SegmentedFileSuffix + SegmentDuration int DisableManifest bool UploadConfig UploadConfig @@ -51,13 +53,14 @@ func (p *PipelineConfig) GetSegmentConfig() *SegmentConfig { // segments should always be added last, so we can check keyframe interval from file/stream func (p *PipelineConfig) getSegmentConfig(segments *livekit.SegmentedFileOutput) (*SegmentConfig, error) { conf := &SegmentConfig{ - SegmentsInfo: &livekit.SegmentsInfo{}, - SegmentPrefix: clean(segments.FilenamePrefix), - SegmentSuffix: segments.FilenameSuffix, - PlaylistFilename: clean(segments.PlaylistName), - SegmentDuration: int(segments.SegmentDuration), - DisableManifest: segments.DisableManifest, - UploadConfig: p.getUploadConfig(segments), + SegmentsInfo: &livekit.SegmentsInfo{}, + SegmentPrefix: clean(segments.FilenamePrefix), + SegmentSuffix: segments.FilenameSuffix, + PlaylistFilename: clean(segments.PlaylistName), + LivePlaylistFilename: clean(segments.LivePlaylistName), + SegmentDuration: int(segments.SegmentDuration), + DisableManifest: segments.DisableManifest, + UploadConfig: p.getUploadConfig(segments), } if conf.SegmentDuration == 0 { @@ -91,26 +94,40 @@ func (p *PipelineConfig) getSegmentConfig(segments *livekit.SegmentedFileOutput) return conf, nil } +func removeKnownExtension(filename string) string { + if extIdx := strings.LastIndex(filename, "."); extIdx > -1 { + existingExt := types.FileExtension(filename[extIdx:]) + if _, ok := types.FileExtensions[existingExt]; ok { + filename = filename[:extIdx] + } + filename = filename[:extIdx] + } + + return filename +} + func (o *SegmentConfig) updatePrefixAndPlaylist(p *PipelineConfig) error { identifier, replacements := p.getFilenameInfo() o.SegmentPrefix = stringReplace(o.SegmentPrefix, replacements) o.PlaylistFilename = stringReplace(o.PlaylistFilename, replacements) + o.LivePlaylistFilename = stringReplace(o.LivePlaylistFilename, replacements) ext := types.FileExtensionForOutputType[o.OutputType] playlistDir, playlistName := path.Split(o.PlaylistFilename) + livePlaylistDir, livePlaylistName := path.Split(o.LivePlaylistFilename) fileDir, filePrefix := path.Split(o.SegmentPrefix) - // remove extension from playlist name - if extIdx := strings.LastIndex(playlistName, "."); extIdx > -1 { - existingExt := types.FileExtension(playlistName[extIdx:]) - if _, ok := types.FileExtensions[existingExt]; ok { - playlistName = playlistName[:extIdx] - } - playlistName = playlistName[:extIdx] + // force live playlist to be in the same directory as the main playlist + if livePlaylistDir != "" && livePlaylistDir != playlistDir { + return errors.ErrInvalidInput("live_playlist_name must be in same directory as playlist_name") } + // remove extension from playlist name + playlistName = removeKnownExtension(playlistName) + livePlaylistName = removeKnownExtension(livePlaylistName) + // only keep fileDir if it is a subdirectory of playlistDir if fileDir != "" { if playlistDir == fileDir { @@ -130,6 +147,7 @@ func (o *SegmentConfig) updatePrefixAndPlaylist(p *PipelineConfig) error { playlistName = fmt.Sprintf("%s-%s", identifier, time.Now().Format("2006-01-02T150405")) } } + // live playlist disabled by default // ensure filePrefix if filePrefix == "" { @@ -139,8 +157,15 @@ func (o *SegmentConfig) updatePrefixAndPlaylist(p *PipelineConfig) error { // update config o.StorageDir = playlistDir o.PlaylistFilename = fmt.Sprintf("%s%s", playlistName, ext) + if livePlaylistName != "" { + o.LivePlaylistFilename = fmt.Sprintf("%s%s", livePlaylistName, ext) + } o.SegmentPrefix = fmt.Sprintf("%s%s", fileDir, filePrefix) + if o.PlaylistFilename == o.LivePlaylistFilename { + return errors.ErrInvalidInput("live_playlist_name cannot be identical to playlist_name") + } + if o.UploadConfig == nil { o.LocalDir = playlistDir } else { @@ -162,5 +187,8 @@ func (o *SegmentConfig) updatePrefixAndPlaylist(p *PipelineConfig) error { } o.SegmentsInfo.PlaylistName = path.Join(o.StorageDir, o.PlaylistFilename) + if o.LivePlaylistFilename != "" { + o.SegmentsInfo.LivePlaylistName = path.Join(o.StorageDir, o.LivePlaylistFilename) + } return nil } diff --git a/pkg/config/pipeline.go b/pkg/config/pipeline.go index 31585135..51acf5f4 100644 --- a/pkg/config/pipeline.go +++ b/pkg/config/pipeline.go @@ -536,8 +536,10 @@ func (p *PipelineConfig) UpdateInfoFromSDK(identifier string, replacements map[s o.LocalDir = stringReplace(o.LocalDir, replacements) o.StorageDir = stringReplace(o.StorageDir, replacements) o.PlaylistFilename = stringReplace(o.PlaylistFilename, replacements) + o.LivePlaylistFilename = stringReplace(o.LivePlaylistFilename, replacements) o.SegmentPrefix = stringReplace(o.SegmentPrefix, replacements) o.SegmentsInfo.PlaylistName = stringReplace(o.SegmentsInfo.PlaylistName, replacements) + o.SegmentsInfo.LivePlaylistName = stringReplace(o.SegmentsInfo.LivePlaylistName, replacements) } } From 3135c3ace42385c011f4d28e30143ae8bc69a0d9 Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Wed, 30 Aug 2023 16:17:32 -0700 Subject: [PATCH 05/13] WiP --- pkg/pipeline/sink/segments.go | 45 ++++++++++++++++++++++++++++++++--- 1 file changed, 42 insertions(+), 3 deletions(-) diff --git a/pkg/pipeline/sink/segments.go b/pkg/pipeline/sink/segments.go index fd64f01b..a97516bf 100644 --- a/pkg/pipeline/sink/segments.go +++ b/pkg/pipeline/sink/segments.go @@ -33,7 +33,10 @@ import ( "github.com/livekit/protocol/logger" ) -const maxPendingUploads = 100 +const ( + maxPendingUploads = 100 + defaultLivePlaylistWindow = 4 +) type SegmentSink struct { uploader.Uploader @@ -42,7 +45,8 @@ type SegmentSink struct { conf *config.PipelineConfig callbacks *gstreamer.Callbacks - playlist *m3u8.PlaylistWriter + playlist m3u8.PlaylistWriter + livePlaylist m3u8.PlaylistWriter currentItemStartTimestamp int64 currentItemFilename string startDate time.Time @@ -62,17 +66,27 @@ type SegmentUpdate struct { func newSegmentSink(u uploader.Uploader, p *config.PipelineConfig, o *config.SegmentConfig, callbacks *gstreamer.Callbacks) (*SegmentSink, error) { playlistName := path.Join(o.LocalDir, o.PlaylistFilename) - playlist, err := m3u8.NewPlaylistWriter(playlistName, o.SegmentDuration) + playlist, err := m3u8.NewEventPlaylistWriter(playlistName, o.SegmentDuration) if err != nil { return nil, err } + var livePlaylist m3u8.PlaylistWriter + if o.LivePlaylistFilename != "" { + playlistName = path.Join(o.LocalDir, o.PlaylistFilename) + livePlaylist, err = m3u8.NewLivePlaylistWriter(playlistName, o.SegmentDuration, defaultLivePlaylistWindow) + if err != nil { + return nil, err + } + } + return &SegmentSink{ Uploader: u, SegmentConfig: o, conf: p, callbacks: callbacks, playlist: playlist, + livePlaylist: livePlaylist, openSegmentsStartTime: make(map[string]int64), endedSegments: make(chan SegmentUpdate, maxPendingUploads), done: core.NewFuse(), @@ -115,6 +129,15 @@ func (s *SegmentSink) Start() error { if err != nil { return } + + if s.LivePlaylistFilename != "" { + playlistLocalPath = path.Join(s.LocalDir, s.LivePlaylistFilename) + playlistStoragePath = path.Join(s.StorageDir, s.LivePlaylistFilename) + s.SegmentsInfo.LivePlaylistLocation, _, err = s.Upload(playlistLocalPath, playlistStoragePath, s.OutputType, false) + if err != nil { + return + } + } } }() @@ -203,6 +226,11 @@ func (s *SegmentSink) endSegment(filename string, endTime int64) error { if err := s.playlist.Append(segmentStartDate, duration, filename); err != nil { return err } + if s.livePlaylist != nil { + if err := s.livePlaylist.Append(segmentStartDate, duration, filename); err != nil { + return err + } + } return nil } @@ -221,6 +249,17 @@ func (s *SegmentSink) OnStop() error { playlistStoragePath := path.Join(s.StorageDir, s.PlaylistFilename) s.SegmentsInfo.PlaylistLocation, _, _ = s.Upload(playlistLocalPath, playlistStoragePath, s.OutputType, false) + if s.livePlaylist != nil { + if err := s.livePlaylist.Close(); err != nil { + logger.Errorw("failed to send EOS to live playlist writer", err) + } + + // upload the finalized live playlist + playlistLocalPath := path.Join(s.LocalDir, s.LivePlaylistFilename) + playlistStoragePath := path.Join(s.StorageDir, s.LivePlaylistFilename) + s.SegmentsInfo.LivePlaylistLocation, _, _ = s.Upload(playlistLocalPath, playlistStoragePath, s.OutputType, false) + } + if !s.DisableManifest { manifestLocalPath := fmt.Sprintf("%s.json", playlistLocalPath) manifestStoragePath := fmt.Sprintf("%s.json", playlistStoragePath) From b668400545a67a0b5d45b9c8f50f32f1100aba11 Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Thu, 31 Aug 2023 09:34:43 -0700 Subject: [PATCH 06/13] WiP --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index d5b2cf93..4c2318b2 100644 --- a/go.mod +++ b/go.mod @@ -17,7 +17,7 @@ require ( github.com/go-logr/logr v1.2.4 github.com/googleapis/gax-go/v2 v2.11.0 github.com/gorilla/websocket v1.5.0 - github.com/livekit/livekit-server v1.4.5-0.20230814182001-77c8e824735b + github.com/livekit/livekit-server v1.4.6-0.20230831161419-c7683fd3830e github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 github.com/livekit/protocol v1.6.2-0.20230830214603-ce275be4e6a5 github.com/livekit/psrpc v0.3.3 @@ -72,7 +72,7 @@ require ( github.com/klauspost/compress v1.16.7 // indirect github.com/klauspost/cpuid/v2 v2.0.9 // indirect github.com/lithammer/shortuuid/v4 v4.0.0 // indirect - github.com/livekit/mediatransportutil v0.0.0-20230814030822-8d5de0008b08 // indirect + github.com/livekit/mediatransportutil v0.0.0-20230823131232-12f579dc9af0 // indirect github.com/mackerelio/go-osstat v0.2.4 // indirect github.com/magefile/mage v1.15.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect diff --git a/go.sum b/go.sum index 49ec9317..0d5740db 100644 --- a/go.sum +++ b/go.sum @@ -170,12 +170,12 @@ github.com/livekit/go-glib v0.0.0-20230223001336-834490045522 h1:AlU57PAPgzde7q9 github.com/livekit/go-glib v0.0.0-20230223001336-834490045522/go.mod h1:ltV0gO6xNFzZhsIRbFXv8RTq9NGoNT2dmAER4YmZfaM= github.com/livekit/go-gst v0.2.34-0.20230623013326-3ca8cb8f62e5 h1:psIQLAp+yJETA+vZTVphV072nvCgcwC55xcufo1I5v0= github.com/livekit/go-gst v0.2.34-0.20230623013326-3ca8cb8f62e5/go.mod h1:0hI+orMYVT61TEh429LvmoV9UmyqjeTqdJ3DW2TX114= -github.com/livekit/livekit-server v1.4.5-0.20230814182001-77c8e824735b h1:o+fyF5iijKHAMbzXFW6g4pRH/bR/cELRbTZHwRheF24= -github.com/livekit/livekit-server v1.4.5-0.20230814182001-77c8e824735b/go.mod h1:Dbul6eFPxLw/ItxGjovO1a2Gzrs2EHEuXgjADz6squI= +github.com/livekit/livekit-server v1.4.6-0.20230831161419-c7683fd3830e h1:H/faRu6zYKJA93VgBUxajHt1PsrjCHGFyTHmpL9G2PE= +github.com/livekit/livekit-server v1.4.6-0.20230831161419-c7683fd3830e/go.mod h1:BMm0JxGEzBiMw99XjlQ+vaIl+Rq+ZlSgsNnPnqR/ZII= github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1 h1:jm09419p0lqTkDaKb5iXdynYrzB84ErPPO4LbRASk58= github.com/livekit/mageutil v0.0.0-20230125210925-54e8a70427c1/go.mod h1:Rs3MhFwutWhGwmY1VQsygw28z5bWcnEYmS1OG9OxjOQ= -github.com/livekit/mediatransportutil v0.0.0-20230814030822-8d5de0008b08 h1:e0qwVjrtzmADgNZpdgSJgyhlF6BgrHkpdnkONL8pLrw= -github.com/livekit/mediatransportutil v0.0.0-20230814030822-8d5de0008b08/go.mod h1:xirUXW8xnLGmfCwUeAv/nj1VGo1OO1BmgxrYP7jK/14= +github.com/livekit/mediatransportutil v0.0.0-20230823131232-12f579dc9af0 h1:cHNvPzn6VHFcsHx8ZC9LwU/4jj22mW3LILrNg/y5A6I= +github.com/livekit/mediatransportutil v0.0.0-20230823131232-12f579dc9af0/go.mod h1:xirUXW8xnLGmfCwUeAv/nj1VGo1OO1BmgxrYP7jK/14= github.com/livekit/protocol v1.6.2-0.20230830214603-ce275be4e6a5 h1:/S16yVnnoBPDzsbDj2/vRZiC4mPfxP6/RorOJcSQVeU= github.com/livekit/protocol v1.6.2-0.20230830214603-ce275be4e6a5/go.mod h1:/JuO+G/btZ5gNwX2+901L6za3UvVO6DHRXHsv8kkLsU= github.com/livekit/psrpc v0.3.3 h1:+lltbuN39IdaynXhLLxRShgYqYsRMWeeXKzv60oqyWo= From 9ced78a9acdab15a522ca15633ad97c0aa0204c6 Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Thu, 31 Aug 2023 09:54:23 -0700 Subject: [PATCH 07/13] WiP --- test/integration.go | 1 + test/track_composite.go | 18 ++++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/test/integration.go b/test/integration.go index 557b3373..d6d9aacb 100644 --- a/test/integration.go +++ b/test/integration.go @@ -76,6 +76,7 @@ type testCase struct { // used by segmented file tests playlist string + live_playlist string filenameSuffix livekit.SegmentedFileSuffix // used by sdk tests diff --git a/test/track_composite.go b/test/track_composite.go index 49b0fa46..de7b614a 100644 --- a/test/track_composite.go +++ b/test/track_composite.go @@ -165,11 +165,12 @@ func (r *Runner) testTrackCompositeSegments(t *testing.T) { playlist: "tcs_{publisher_identity}_vp8_{time}.m3u8", }, { - name: "H264", - audioCodec: types.MimeTypeOpus, - videoCodec: types.MimeTypeH264, - filename: "tcs_{room_name}_h264_{time}", - playlist: "tcs_{room_name}_h264_{time}.m3u8", + name: "H264", + audioCodec: types.MimeTypeOpus, + videoCodec: types.MimeTypeH264, + filename: "tcs_{room_name}_h264_{time}", + playlist: "tcs_{room_name}_h264_{time}.m3u8", + live_playlist: "tcs_live_{room_name}_h264_{time}.m3u8", }, } { r.runTrackTest(t, test.name, test.audioCodec, test.videoCodec, @@ -183,9 +184,10 @@ func (r *Runner) testTrackCompositeSegments(t *testing.T) { } segmentOutput := &livekit.SegmentedFileOutput{ - FilenamePrefix: r.getFilePath(test.filename), - PlaylistName: test.playlist, - FilenameSuffix: test.filenameSuffix, + FilenamePrefix: r.getFilePath(test.filename), + PlaylistName: test.playlist, + LivePlaylistName: test.live_playlist, + FilenameSuffix: test.filenameSuffix, } if test.filenameSuffix == livekit.SegmentedFileSuffix_INDEX && r.S3Upload != nil { segmentOutput.FilenamePrefix = test.filename From b109d17f3fe76802e448aa97064d858eebd1b63b Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Thu, 31 Aug 2023 10:32:48 -0700 Subject: [PATCH 08/13] Fix --- pkg/pipeline/sink/segments.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/pipeline/sink/segments.go b/pkg/pipeline/sink/segments.go index a97516bf..312eca8b 100644 --- a/pkg/pipeline/sink/segments.go +++ b/pkg/pipeline/sink/segments.go @@ -73,7 +73,7 @@ func newSegmentSink(u uploader.Uploader, p *config.PipelineConfig, o *config.Seg var livePlaylist m3u8.PlaylistWriter if o.LivePlaylistFilename != "" { - playlistName = path.Join(o.LocalDir, o.PlaylistFilename) + playlistName = path.Join(o.LocalDir, o.LivePlaylistFilename) livePlaylist, err = m3u8.NewLivePlaylistWriter(playlistName, o.SegmentDuration, defaultLivePlaylistWindow) if err != nil { return nil, err From 167443aabe31790bf7342d76e1e5c3be20dff3df Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Thu, 31 Aug 2023 12:14:41 -0700 Subject: [PATCH 09/13] Integration test --- pkg/pipeline/sink/segments.go | 2 +- test/ffprobe.go | 11 +++++--- test/file.go | 2 +- test/multi.go | 2 +- test/segments.go | 49 ++++++++++++++++++++++++----------- test/stream.go | 2 +- test/track.go | 2 +- 7 files changed, 47 insertions(+), 23 deletions(-) diff --git a/pkg/pipeline/sink/segments.go b/pkg/pipeline/sink/segments.go index 312eca8b..9f01a027 100644 --- a/pkg/pipeline/sink/segments.go +++ b/pkg/pipeline/sink/segments.go @@ -35,7 +35,7 @@ import ( const ( maxPendingUploads = 100 - defaultLivePlaylistWindow = 4 + defaultLivePlaylistWindow = 5 ) type SegmentSink struct { diff --git a/test/ffprobe.go b/test/ffprobe.go index a79ea1c9..0500a90c 100644 --- a/test/ffprobe.go +++ b/test/ffprobe.go @@ -104,7 +104,7 @@ func ffprobe(input string) (*FFProbeInfo, error) { return info, err } -func verify(t *testing.T, in string, p *config.PipelineConfig, res *livekit.EgressInfo, egressType types.EgressType, withMuting bool, sourceFramerate float64) { +func verify(t *testing.T, in string, p *config.PipelineConfig, res *livekit.EgressInfo, egressType types.EgressType, withMuting bool, sourceFramerate float64, live bool) { info, err := ffprobe(in) require.NoError(t, err, "input %s does not exist", in) @@ -152,8 +152,13 @@ func verify(t *testing.T, in string, p *config.PipelineConfig, res *livekit.Egre require.Len(t, res.GetSegmentResults(), 1) segments := res.GetSegmentResults()[0] - expected := int64(math.Ceil(actual / float64(p.GetSegmentConfig().SegmentDuration))) - require.InDelta(t, expected, segments.SegmentCount, 1) + + if live { + require.InDelta(t, float64(5*p.GetSegmentConfig().SegmentDuration), actual, float64(p.GetSegmentConfig().SegmentDuration)) + } else { + expected := int64(math.Ceil(actual / float64(p.GetSegmentConfig().SegmentDuration))) + require.InDelta(t, expected, segments.SegmentCount, 1) + } case types.EgressTypeWebsocket: size, err := strconv.Atoi(info.Format.Size) diff --git a/test/file.go b/test/file.go index cde38e25..3ab8844b 100644 --- a/test/file.go +++ b/test/file.go @@ -86,5 +86,5 @@ func (r *Runner) verifyFile(t *testing.T, p *config.PipelineConfig, res *livekit } // verify - verify(t, localPath, p, res, types.EgressTypeFile, r.Muting, r.sourceFramerate) + verify(t, localPath, p, res, types.EgressTypeFile, r.Muting, r.sourceFramerate, false) } diff --git a/test/multi.go b/test/multi.go index b6b914e3..ade90e27 100644 --- a/test/multi.go +++ b/test/multi.go @@ -63,6 +63,6 @@ func (r *Runner) runMultipleTest( r.verifyFile(t, p, res) } if segments { - r.verifySegments(t, p, filenameSuffix, res) + r.verifySegments(t, p, filenameSuffix, res, false) } } diff --git a/test/segments.go b/test/segments.go index 2ec0b024..f79540e4 100644 --- a/test/segments.go +++ b/test/segments.go @@ -27,6 +27,7 @@ import ( "github.com/stretchr/testify/require" "github.com/livekit/egress/pkg/config" + "github.com/livekit/egress/pkg/pipeline/sink/m3u8" "github.com/livekit/egress/pkg/types" "github.com/livekit/protocol/livekit" "github.com/livekit/protocol/rpc" @@ -49,10 +50,10 @@ func (r *Runner) runSegmentsTest(t *testing.T, req *rpc.StartEgressRequest, test require.NoError(t, err) require.Equal(t, test.expectVideoTranscoding, p.VideoTranscoding) - r.verifySegments(t, p, test.filenameSuffix, res) + r.verifySegments(t, p, test.filenameSuffix, res, test.live_playlist != "") } -func (r *Runner) verifySegments(t *testing.T, p *config.PipelineConfig, filenameSuffix livekit.SegmentedFileSuffix, res *livekit.EgressInfo) { +func (r *Runner) verifySegments(t *testing.T, p *config.PipelineConfig, filenameSuffix livekit.SegmentedFileSuffix, res *livekit.EgressInfo, enableLivePlaylist bool) { // egress info require.Equal(t, res.Error == "", res.Status != livekit.EgressStatus_EGRESS_FAILED) require.NotZero(t, res.StartedAt) @@ -62,13 +63,21 @@ func (r *Runner) verifySegments(t *testing.T, p *config.PipelineConfig, filename require.Len(t, res.GetSegmentResults(), 1) segments := res.GetSegmentResults()[0] - require.NotEmpty(t, segments.PlaylistName) - require.NotEmpty(t, segments.PlaylistLocation) require.Greater(t, segments.Size, int64(0)) require.Greater(t, segments.Duration, int64(0)) - storedPlaylistPath := segments.PlaylistName - localPlaylistPath := segments.PlaylistName + r.verifySegmentOutput(t, p, filenameSuffix, segments.PlaylistName, segments.PlaylistLocation, int(segments.SegmentCount), res, m3u8.PlaylistTypeEvent) + if enableLivePlaylist { + r.verifySegmentOutput(t, p, filenameSuffix, segments.LivePlaylistName, segments.LivePlaylistLocation, 5, res, m3u8.PlaylistTypeLive) + } +} + +func (r *Runner) verifySegmentOutput(t *testing.T, p *config.PipelineConfig, filenameSuffix livekit.SegmentedFileSuffix, plName string, plLocation string, segmentCount int, res *livekit.EgressInfo, plType m3u8.PlaylistType) { + require.NotEmpty(t, plName) + require.NotEmpty(t, plLocation) + + storedPlaylistPath := plName + localPlaylistPath := plName // download from cloud storage if uploadConfig := p.GetSegmentConfig().UploadConfig; uploadConfig != nil { @@ -76,23 +85,23 @@ func (r *Runner) verifySegments(t *testing.T, p *config.PipelineConfig, filename localPlaylistPath = fmt.Sprintf("%s/%s", r.FilePrefix, storedPlaylistPath) download(t, uploadConfig, localPlaylistPath, storedPlaylistPath) download(t, uploadConfig, localPlaylistPath+".json", storedPlaylistPath+".json") - for i := 0; i < int(segments.SegmentCount); i++ { + for i := 0; i < int(segmentCount); i++ { cloudPath := fmt.Sprintf("%s_%05d.ts", base, i) localPath := fmt.Sprintf("%s/%s", r.FilePrefix, cloudPath) download(t, uploadConfig, localPath, cloudPath) } } - verifyPlaylistProgramDateTime(t, filenameSuffix, localPlaylistPath) + verifyPlaylistProgramDateTime(t, filenameSuffix, localPlaylistPath, plType) // verify - verify(t, localPlaylistPath, p, res, types.EgressTypeSegments, r.Muting, r.sourceFramerate) + verify(t, localPlaylistPath, p, res, types.EgressTypeSegments, r.Muting, r.sourceFramerate, plType == m3u8.PlaylistTypeLive) } -func verifyPlaylistProgramDateTime(t *testing.T, filenameSuffix livekit.SegmentedFileSuffix, localPlaylistPath string) { +func verifyPlaylistProgramDateTime(t *testing.T, filenameSuffix livekit.SegmentedFileSuffix, localPlaylistPath string, plType m3u8.PlaylistType) { p, err := readPlaylist(localPlaylistPath) require.NoError(t, err) - require.Equal(t, "EVENT", p.MediaType) + require.Equal(t, string(plType), p.MediaType) require.True(t, p.Closed) now := time.Now() @@ -147,10 +156,20 @@ func readPlaylist(filename string) (*Playlist, error) { return nil, err } + var segmentLineStart = 5 + var i = 1 + lines := strings.Split(string(b), "\n") - version, _ := strconv.Atoi(strings.Split(lines[1], ":")[1]) - mediaType := strings.Split(lines[2], ":")[1] - targetDuration, _ := strconv.Atoi(strings.Split(lines[5], ":")[1]) + version, _ := strconv.Atoi(strings.Split(lines[i], ":")[1]) + i++ + var mediaType string + if strings.Contains(string(b), "#EXT-X-PLAYLIST-TYPE") { + mediaType = strings.Split(lines[i], ":")[1] + segmentLineStart++ + i++ + } + i++ // #EXT-X-ALLOW-CACHE:NO hardcoded + targetDuration, _ := strconv.Atoi(strings.Split(lines[i], ":")[1]) p := &Playlist{ Version: version, @@ -159,7 +178,7 @@ func readPlaylist(filename string) (*Playlist, error) { Segments: make([]*Segment, 0), } - for i := 6; i < len(lines)-3; i += 3 { + for i := segmentLineStart; i < len(lines)-3; i += 3 { startTime, _ := time.Parse("2006-01-02T15:04:05.999Z07:00", strings.SplitN(lines[i], ":", 2)[1]) durStr := strings.Split(lines[i+1], ":")[1] durStr = durStr[:len(durStr)-1] // remove trailing comma diff --git a/test/stream.go b/test/stream.go index 4b9be2b0..c088802f 100644 --- a/test/stream.go +++ b/test/stream.go @@ -126,6 +126,6 @@ func (r *Runner) runStreamTest(t *testing.T, req *rpc.StartEgressRequest, test * func (r *Runner) verifyStreams(t *testing.T, p *config.PipelineConfig, urls ...string) { for _, url := range urls { - verify(t, url, p, nil, types.EgressTypeStream, false, r.sourceFramerate) + verify(t, url, p, nil, types.EgressTypeStream, false, r.sourceFramerate, false) } } diff --git a/test/track.go b/test/track.go index af66e6f7..3f116cb7 100644 --- a/test/track.go +++ b/test/track.go @@ -165,7 +165,7 @@ func (r *Runner) testTrackStream(t *testing.T) { time.Sleep(time.Second * 30) res := r.stopEgress(t, egressID) - verify(t, filepath, p, res, types.EgressTypeWebsocket, r.Muting, r.sourceFramerate) + verify(t, filepath, p, res, types.EgressTypeWebsocket, r.Muting, r.sourceFramerate, false) }) if r.Short { return From ecee8387338841e1e5affd9c22bc825d9c2c15d7 Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Thu, 31 Aug 2023 12:24:01 -0700 Subject: [PATCH 10/13] Room composite test --- test/room_composite.go | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/test/room_composite.go b/test/room_composite.go index 3e86435a..e3cdd1e3 100644 --- a/test/room_composite.go +++ b/test/room_composite.go @@ -208,14 +208,16 @@ func (r *Runner) testRoomCompositeSegments(t *testing.T) { }, filename: "r_{room_name}_{time}", playlist: "r_{room_name}_{time}.m3u8", + live_playlist: "r_live_{room_name}_{time}.m3u8", filenameSuffix: livekit.SegmentedFileSuffix_TIMESTAMP, expectVideoTranscoding: true, } segmentOutput := &livekit.SegmentedFileOutput{ - FilenamePrefix: r.getFilePath(test.filename), - PlaylistName: test.playlist, - FilenameSuffix: test.filenameSuffix, + FilenamePrefix: r.getFilePath(test.filename), + PlaylistName: test.playlist, + LivePlaylistName: test.live_playlist, + FilenameSuffix: test.filenameSuffix, } if test.filenameSuffix == livekit.SegmentedFileSuffix_INDEX && r.GCPUpload != nil { segmentOutput.FilenamePrefix = test.filename From 2e3cf742e05e6d28348cb46b344559545cb07bac Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Thu, 31 Aug 2023 14:18:52 -0700 Subject: [PATCH 11/13] Fix segment manifest test --- test/segments.go | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/segments.go b/test/segments.go index f79540e4..540e42bf 100644 --- a/test/segments.go +++ b/test/segments.go @@ -67,11 +67,20 @@ func (r *Runner) verifySegments(t *testing.T, p *config.PipelineConfig, filename require.Greater(t, segments.Duration, int64(0)) r.verifySegmentOutput(t, p, filenameSuffix, segments.PlaylistName, segments.PlaylistLocation, int(segments.SegmentCount), res, m3u8.PlaylistTypeEvent) + r.verifyManifest(t, p, segments.PlaylistName) if enableLivePlaylist { r.verifySegmentOutput(t, p, filenameSuffix, segments.LivePlaylistName, segments.LivePlaylistLocation, 5, res, m3u8.PlaylistTypeLive) } } +func (r *Runner) verifyManifest(t *testing.T, p *config.PipelineConfig, plName string) { + localPlaylistPath := fmt.Sprintf("%s/%s", r.FilePrefix, plName) + + if uploadConfig := p.GetSegmentConfig().UploadConfig; uploadConfig != nil { + download(t, uploadConfig, localPlaylistPath+".json", plName+".json") + } +} + func (r *Runner) verifySegmentOutput(t *testing.T, p *config.PipelineConfig, filenameSuffix livekit.SegmentedFileSuffix, plName string, plLocation string, segmentCount int, res *livekit.EgressInfo, plType m3u8.PlaylistType) { require.NotEmpty(t, plName) require.NotEmpty(t, plLocation) @@ -84,7 +93,6 @@ func (r *Runner) verifySegmentOutput(t *testing.T, p *config.PipelineConfig, fil base := storedPlaylistPath[:len(storedPlaylistPath)-5] localPlaylistPath = fmt.Sprintf("%s/%s", r.FilePrefix, storedPlaylistPath) download(t, uploadConfig, localPlaylistPath, storedPlaylistPath) - download(t, uploadConfig, localPlaylistPath+".json", storedPlaylistPath+".json") for i := 0; i < int(segmentCount); i++ { cloudPath := fmt.Sprintf("%s_%05d.ts", base, i) localPath := fmt.Sprintf("%s/%s", r.FilePrefix, cloudPath) From e4ac34d11cc93ce1d3ac5a923e9c46cfd9276b35 Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Thu, 31 Aug 2023 14:54:54 -0700 Subject: [PATCH 12/13] fix test --- test/segments.go | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/test/segments.go b/test/segments.go index 540e42bf..11aba4ea 100644 --- a/test/segments.go +++ b/test/segments.go @@ -90,13 +90,16 @@ func (r *Runner) verifySegmentOutput(t *testing.T, p *config.PipelineConfig, fil // download from cloud storage if uploadConfig := p.GetSegmentConfig().UploadConfig; uploadConfig != nil { - base := storedPlaylistPath[:len(storedPlaylistPath)-5] localPlaylistPath = fmt.Sprintf("%s/%s", r.FilePrefix, storedPlaylistPath) download(t, uploadConfig, localPlaylistPath, storedPlaylistPath) - for i := 0; i < int(segmentCount); i++ { - cloudPath := fmt.Sprintf("%s_%05d.ts", base, i) - localPath := fmt.Sprintf("%s/%s", r.FilePrefix, cloudPath) - download(t, uploadConfig, localPath, cloudPath) + if plType == m3u8.PlaylistTypeEvent { + // Only download segments once + base := storedPlaylistPath[:len(storedPlaylistPath)-5] + for i := 0; i < int(segmentCount); i++ { + cloudPath := fmt.Sprintf("%s_%05d.ts", base, i) + localPath := fmt.Sprintf("%s/%s", r.FilePrefix, cloudPath) + download(t, uploadConfig, localPath, cloudPath) + } } } From 6afbe7887828ea62d303b93670abf3192edafb3b Mon Sep 17 00:00:00 2001 From: Benjamin Pracht Date: Mon, 25 Sep 2023 14:16:44 -0700 Subject: [PATCH 13/13] merge issue --- test/segments.go | 1 - 1 file changed, 1 deletion(-) diff --git a/test/segments.go b/test/segments.go index 7fe49e81..747b8114 100644 --- a/test/segments.go +++ b/test/segments.go @@ -49,7 +49,6 @@ func (r *Runner) runSegmentsTest(t *testing.T, req *rpc.StartEgressRequest, test p, err := config.GetValidatedPipelineConfig(r.ServiceConfig, req) require.NoError(t, err) - require.Equal(t, test.expectVideoTranscoding, p.VideoTranscoding) r.verifySegments(t, p, test.filenameSuffix, res, test.live_playlist != "") if !test.audioOnly { require.Equal(t, test.expectVideoTranscoding, p.VideoTranscoding)