Live video on the AT Protocol
at eli/docker-deployment-docs 174 lines 3.7 kB view raw
1package main 2 3import ( 4 "encoding/json" 5 "errors" 6 "flag" 7 "fmt" 8 "io" 9 "log" 10 "math" 11 "os" 12 "time" 13 14 "stream.place/streamplace/pkg/rtcrec" 15) 16 17func main() { 18 err := Start() 19 if err != nil { 20 log.Fatal(err) 21 } 22} 23 24func Start() error { 25 if len(os.Args) > 1 && os.Args[1] == "decode" { 26 return Decode() 27 } 28 if len(os.Args) > 1 && os.Args[1] == "trim" { 29 return Trim() 30 } 31 return fmt.Errorf("unknown command: %s", os.Args[1]) 32} 33 34func Trim() error { 35 var startDuration time.Duration 36 flag.DurationVar(&startDuration, "start", 0, "timestamp where we should start our clip") 37 var endDuration time.Duration 38 flag.DurationVar(&endDuration, "end", 0, "timestamp where we should end our clip") 39 var inPath string 40 flag.StringVar(&inPath, "in-path", "", "path to the file to decode") 41 var outPath string 42 flag.StringVar(&outPath, "out-path", "", "path to the file to write the trimmed file to") 43 err := flag.CommandLine.Parse(os.Args[2:]) 44 if err != nil { 45 return err 46 } 47 if startDuration == 0 && endDuration == 0 { 48 return fmt.Errorf("start or end duration is required (otherwise, you know, the cp command is right there)") 49 } 50 if inPath == "" { 51 return fmt.Errorf("in-path is required") 52 } 53 if outPath == "" { 54 return fmt.Errorf("out-path is required") 55 } 56 inFile, err := os.Open(inPath) 57 if err != nil { 58 return err 59 } 60 defer inFile.Close() 61 outFile, err := os.Create(outPath) 62 if err != nil { 63 return err 64 } 65 defer outFile.Close() 66 dec, err := rtcrec.MakeWebRTCDecoder(inFile) 67 if err != nil { 68 return err 69 } 70 encoder, err := rtcrec.MakeWebRTCEncoder(outFile) 71 if err != nil { 72 return err 73 } 74 var startCutoff *time.Time 75 var endCutoff *time.Time 76 if startDuration == 0 { 77 startCutoff = &time.Time{} 78 } 79 if endDuration == 0 { 80 t := time.Unix(math.MaxInt64, 0) 81 endCutoff = &t 82 } 83 included := 0 84 dropped := 0 85 for { 86 ev, err := dec.Next() 87 if errors.Is(err, io.EOF) { 88 break 89 } 90 if startCutoff == nil { 91 t := ev.Time.Add(startDuration) 92 startCutoff = &t 93 } 94 if endCutoff == nil { 95 t := ev.Time.Add(endDuration) 96 endCutoff = &t 97 } 98 // we only rewrite trackread events 99 if ev.TrackRead == nil { 100 if ev.Time.Before(*endCutoff) { 101 // included++ 102 encoder.Event(*ev) 103 } 104 continue 105 } 106 if ev.Time.Before(*startCutoff) || ev.Time.After(*endCutoff) { 107 // fmt.Printf("dropped: %s < %s\n", ev.Time.Format(time.RFC3339Nano), cutoff.Format(time.RFC3339Nano)) 108 dropped++ 109 continue 110 } 111 included++ 112 ev.Time = ev.Time.Add(-startDuration) 113 encoder.Event(*ev) 114 } 115 fmt.Printf("included: %d, dropped: %d\n", included, dropped) 116 return nil 117} 118 119func Decode() error { 120 var path string 121 flag.StringVar(&path, "path", "", "path to the file to decode") 122 flag.Parse() 123 if path == "" { 124 return fmt.Errorf("path is required") 125 } 126 return DecodeFile(path) 127} 128 129func DecodeFile(path string) error { 130 f, err := os.Open(path) 131 if err != nil { 132 return err 133 } 134 defer f.Close() 135 dec, err := rtcrec.MakeWebRTCDecoder(f) 136 if err != nil { 137 return err 138 } 139 for { 140 ev, err := dec.Next() 141 if errors.Is(err, io.EOF) { 142 return nil 143 } 144 if err != nil { 145 return err 146 } 147 if ev.TrackRead != nil { 148 // spitting out the data as base64 is pointless, replace with a label 149 n := len(ev.TrackRead.Data) 150 byteString := fmt.Sprintf("%d bytes", n) 151 bs, err := json.Marshal(ev) 152 if err != nil { 153 return err 154 } 155 var m map[string]any 156 err = json.Unmarshal(bs, &m) 157 if err != nil { 158 return err 159 } 160 m["trackRead"].(map[string]any)["data"] = byteString 161 bs, err = json.Marshal(m) 162 if err != nil { 163 return err 164 } 165 fmt.Println(string(bs)) 166 } else { 167 bs, err := json.Marshal(ev) 168 if err != nil { 169 return err 170 } 171 fmt.Println(string(bs)) 172 } 173 } 174}