Live video on the AT Protocol
at natb/block-javascript-protocol 182 lines 4.8 kB view raw
1package media 2 3import ( 4 "context" 5 "crypto/sha256" 6 "encoding/json" 7 "fmt" 8 "io" 9 "os" 10 "path/filepath" 11 "reflect" 12 "sort" 13 "strings" 14 "testing" 15 16 "github.com/stretchr/testify/require" 17 "stream.place/streamplace/pkg/aqio" 18 "stream.place/streamplace/pkg/config" 19 ct "stream.place/streamplace/pkg/config/configtesting" 20 "stream.place/streamplace/pkg/crypto/spkey" 21 "stream.place/streamplace/test/remote" 22) 23 24var testTimestamp = "2025-01-01T00:00:00.000Z" 25 26func makeServerMediaSigner(t *testing.T) *MediaSignerLocal { 27 priv, _, err := spkey.GenerateStreamKey() 28 require.NoError(t, err) 29 require.NoError(t, err) 30 signer, err := spkey.KeyToSigner(priv) 31 require.NoError(t, err) 32 cli := ct.CLI(t, &config.CLI{ 33 TAURL: "http://timestamp.digicert.com", 34 WideOpen: true, 35 }) 36 msInterface, err := MakeMediaSigner(context.Background(), cli, "test-person", signer, nil) 37 require.NoError(t, err) 38 ms := msInterface.(*MediaSignerLocal) 39 return ms 40} 41 42func TestSegmentRoundtrip(t *testing.T) { 43 testCases := []struct { 44 name string 45 fixture string 46 }{ 47 // { 48 // name: "OneMinute", 49 // fixture: remote.RemoteArchive("4563c7b48c0ca02c3fc87bbe6f1e63a743656e465a82bec0af75ef7eead04a23/1-minute-of-signed-segments.tar.gz"), 50 // }, 51 { 52 name: "ThreeSegs", 53 fixture: remote.RemoteArchive("c21e9352e72ca0729c66af2fcabec1b8997b509601241e8d38d5728f9687386b/threesegs.tar.gz"), 54 }, 55 } 56 for _, testCase := range testCases { 57 t.Run(testCase.name, func(t *testing.T) { 58 withNoGSTLeaks(t, func() { 59 tempDir, err := os.MkdirTemp("", "ingredient_test") 60 t.Logf("tempDir: %s", tempDir) 61 require.NoError(t, err) 62 getTestVids := func() []io.ReadSeeker { 63 testVids := []io.ReadSeeker{} 64 segEntries, err := os.ReadDir(testCase.fixture) 65 require.NoError(t, err) 66 for _, segEntry := range segEntries { 67 if segEntry.Type().IsRegular() { 68 if !strings.HasSuffix(segEntry.Name(), ".mp4") { 69 continue 70 } 71 fd, err := os.Open(filepath.Join(testCase.fixture, segEntry.Name())) 72 require.NoError(t, err) 73 testVids = append(testVids, fd) 74 } 75 } 76 return testVids 77 } 78 79 firstReport, err := makeSegDirReport(t, testCase.fixture) 80 require.NoError(t, err) 81 ms := makeServerMediaSigner(t) 82 rws := aqio.NewReadWriteSeeker([]byte{}) 83 err = CombineSegments(context.Background(), getTestVids(), ms, rws) 84 require.NoError(t, err) 85 86 _, err = rws.Seek(0, io.SeekStart) 87 require.NoError(t, err) 88 89 signedSplitSegDir := makeTestSubdir(t, tempDir, "signed-split-segments") 90 cli := &config.CLI{} 91 fs := cli.NewFlagSet("rtcrec-test") 92 err = cli.Parse(fs, []string{}) 93 require.NoError(t, err) 94 err = SplitSegments(context.Background(), cli, rws, func(fname string) ReadWriteSeekCloser { 95 fd, err := os.Create(filepath.Join(signedSplitSegDir, fname)) 96 require.NoError(t, err) 97 return fd 98 }) 99 require.NoError(t, err) 100 secondReport, err := makeSegDirReport(t, signedSplitSegDir) 101 require.NoError(t, err) 102 require.NoError(t, firstReport.CheckEquals(secondReport), "signed split segments are not equal to original segments") 103 }) 104 }) 105 } 106} 107 108func makeTestSubdir(t *testing.T, tempDir, subdir string) string { 109 subDir := filepath.Join(tempDir, subdir) 110 err := os.MkdirAll(subDir, 0755) 111 require.NoError(t, err) 112 return subDir 113} 114 115type SegDirReport struct { 116 Dir string 117 Segs []string 118 Hashes []string 119} 120 121func makeSegDirReport(t *testing.T, segDir string) (*SegDirReport, error) { 122 segs := []string{} 123 segEntries, err := os.ReadDir(segDir) 124 require.NoError(t, err) 125 for _, segEntry := range segEntries { 126 if segEntry.Type().IsRegular() { 127 segPath := filepath.Join(segDir, segEntry.Name()) 128 segs = append(segs, segPath) 129 } 130 } 131 sort.Strings(segs) 132 hashes := make([]string, len(segs)) 133 for i, segPath := range segs { 134 hash, err := hashFile(segPath) 135 if err != nil { 136 return nil, err 137 } 138 hashes[i] = hash 139 } 140 141 return &SegDirReport{ 142 Dir: segDir, 143 Segs: segs, 144 Hashes: hashes, 145 }, nil 146} 147 148func (s *SegDirReport) Equals(other *SegDirReport) bool { 149 if len(s.Segs) != len(other.Segs) { 150 return false 151 } 152 if len(s.Hashes) != len(other.Hashes) { 153 return false 154 } 155 return reflect.DeepEqual(s.Hashes, other.Hashes) 156} 157 158func hashFile(path string) (string, error) { 159 bs, err := os.ReadFile(path) 160 if err != nil { 161 return "", err 162 } 163 hash := sha256.Sum256(bs) 164 return fmt.Sprintf("%x", hash), nil 165} 166 167func (s *SegDirReport) CheckEquals(other *SegDirReport) error { 168 if !s.Equals(other) { 169 str1 := s.ToString() 170 str2 := other.ToString() 171 return fmt.Errorf("files should be equal: %s\n%s", str1, str2) 172 } 173 return nil 174} 175 176func (s *SegDirReport) ToString() string { 177 bs, err := json.MarshalIndent(s, "", " ") 178 if err != nil { 179 panic(err) 180 } 181 return string(bs) 182}