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