├── Dockerfile ├── .github └── workflows │ └── docker.yml ├── go.mod ├── README.md ├── examples ├── subscriber │ └── main.go └── publisher │ └── main.go ├── cmd └── main.go └── go.sum /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:alpine AS builder 2 | 3 | COPY . /go/src/github.com/kevmo314/tinywhip 4 | 5 | WORKDIR /go/src/github.com/kevmo314/tinywhip 6 | 7 | RUN go build -o /go/bin/tinywhip cmd/main.go 8 | 9 | FROM alpine:latest 10 | 11 | COPY --from=builder /go/bin/tinywhip /tinywhip 12 | 13 | ENTRYPOINT ["/tinywhip"] 14 | -------------------------------------------------------------------------------- /.github/workflows/docker.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: 6 | - 'main' 7 | 8 | jobs: 9 | docker: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - 13 | name: Set up QEMU 14 | uses: docker/setup-qemu-action@v2 15 | - 16 | name: Set up Docker Buildx 17 | uses: docker/setup-buildx-action@v2 18 | - 19 | name: Login to Docker Hub 20 | uses: docker/login-action@v2 21 | with: 22 | username: kevmo314 23 | password: ${{ secrets.DOCKERHUB_TOKEN }} 24 | - 25 | name: Build and push 26 | uses: docker/build-push-action@v3 27 | with: 28 | push: true 29 | tags: kevmo314/tinywhip:latest -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/kevmo314/tinywhip 2 | 3 | go 1.19 4 | 5 | require ( 6 | github.com/google/uuid v1.3.0 7 | github.com/pion/mediadevices v0.3.12 8 | github.com/pion/webrtc/v3 v3.1.50 9 | ) 10 | 11 | require ( 12 | github.com/pion/datachannel v1.5.5 // indirect 13 | github.com/pion/dtls/v2 v2.1.5 // indirect 14 | github.com/pion/ice/v2 v2.2.12 // indirect 15 | github.com/pion/interceptor v0.1.12 // indirect 16 | github.com/pion/logging v0.2.2 // indirect 17 | github.com/pion/mdns v0.0.5 // indirect 18 | github.com/pion/randutil v0.1.0 // indirect 19 | github.com/pion/rtcp v1.2.10 // indirect 20 | github.com/pion/rtp v1.7.13 // indirect 21 | github.com/pion/sctp v1.8.5 // indirect 22 | github.com/pion/sdp/v3 v3.0.6 // indirect 23 | github.com/pion/srtp/v2 v2.0.10 // indirect 24 | github.com/pion/stun v0.3.5 // indirect 25 | github.com/pion/transport v0.14.1 // indirect 26 | github.com/pion/turn/v2 v2.0.8 // indirect 27 | github.com/pion/udp v0.1.1 // indirect 28 | golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2 // indirect 29 | golang.org/x/image v0.1.0 // indirect 30 | golang.org/x/net v0.3.0 // indirect 31 | golang.org/x/sys v0.3.0 // indirect 32 | ) 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # kevmo314/tinywhip 2 | 3 | A simple, lightweight [WHIP](https://datatracker.ietf.org/doc/html/draft-ietf-wish-whip)/[WHEP](https://www.ietf.org/id/draft-murillo-whep-01.html) server. It accepts video as WHIP and publishes video as WHEP. 4 | 5 | This server is intended to make testing and development of WHIP/WHEP clients easier as well as to provide a minimal, easy to understand and hack implementation of the protocol. It is not intended for production use. 6 | 7 | Looking for a production-ready WHIP/WHEP server? Check out [LiveKit](https://livekit.io/)! It's a full-featured, scalable, and production-ready video conferencing platform that supports WHIP/WHEP. 8 | 9 | ## Usage 10 | 11 | ### Docker 12 | 13 | ```bash 14 | docker run -p 8080:8080 kevmo314/tinywhip 15 | ``` 16 | 17 | ### Binary 18 | 19 | ```bash 20 | go run cmd/main.go 21 | ``` 22 | 23 | Optionally, 24 | 25 | ```bash 26 | PORT=8080 go run cmd/main.go 27 | ``` 28 | 29 | ## Example 30 | 31 | Run the server 32 | 33 | ```bash 34 | go run cmd/main.go 35 | ``` 36 | 37 | In a separate terminal, run the client 38 | 39 | ```bash 40 | go run examples/publisher/main.go http://localhost:8080 testvideo 41 | ``` 42 | 43 | This will print out a track id in the server: 44 | 45 | ``` 46 | 2023/01/13 12:09:47 Adding track: 3e3c9c71-3a2d-4a72-a21f-8f9cae60a4ed 47 | ``` 48 | 49 | Then in one more terminal, run the subscriber with this track id 50 | 51 | ```bash 52 | go run examples/subscriber/main.go http://localhost:8080/3e3c9c71-3a2d-4a72-a21f-8f9cae60a4ed 53 | ``` 54 | 55 | For a more fun (and realistic) experience, check out the [OBS WHIP](https://github.com/obsproject/obs-studio/pull/7926) support or [ffmpeg-whip](https://github.com/kevmo314/ffmpeg-whip) to publish real video instead of a test pattern. 56 | 57 | ## API 58 | 59 | The WHIP endpoint is `/`. An SDP posted to that endpoint will be ingested according to the WHIP specification. 60 | 61 | The WHEP endpoints are `/`, where `` is the stream ID. The WHEP stream will be published according to the WHEP specification. 62 | -------------------------------------------------------------------------------- /examples/subscriber/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "log" 7 | "net/http" 8 | "net/url" 9 | "os" 10 | 11 | "github.com/pion/webrtc/v3" 12 | 13 | _ "github.com/pion/mediadevices/pkg/driver/audiotest" 14 | _ "github.com/pion/mediadevices/pkg/driver/videotest" 15 | ) 16 | 17 | func main() { 18 | pc, err := webrtc.NewPeerConnection(webrtc.Configuration{ 19 | ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}, 20 | }) 21 | if err != nil { 22 | panic(err) 23 | } 24 | 25 | pc.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { 26 | log.Printf("Connection State has changed %s", connectionState.String()) 27 | }) 28 | 29 | pc.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) { 30 | log.Printf("Track (ID: %s, StreamID: %s) added", track.ID(), track.StreamID()) 31 | for { 32 | p, _, err := track.ReadRTP() 33 | if err != nil { 34 | if err == io.EOF { 35 | return 36 | } 37 | panic(err) 38 | } 39 | log.Printf("Got RTP packet for track %s of size %d", track.ID(), len(p.Payload)) 40 | } 41 | }) 42 | 43 | resp, err := http.Post(os.Args[1], "application/sdp", bytes.NewReader([]byte{})) 44 | if err != nil { 45 | panic(err) 46 | } 47 | 48 | body, err := io.ReadAll(resp.Body) 49 | if err != nil { 50 | panic(err) 51 | } 52 | 53 | if err := pc.SetRemoteDescription(webrtc.SessionDescription{ 54 | Type: webrtc.SDPTypeOffer, 55 | SDP: string(body), 56 | }); err != nil { 57 | panic(err) 58 | } 59 | 60 | answer, err := pc.CreateAnswer(nil) 61 | if err != nil { 62 | panic(err) 63 | } 64 | 65 | gatherComplete := webrtc.GatheringCompletePromise(pc) 66 | 67 | if err := pc.SetLocalDescription(answer); err != nil { 68 | panic(err) 69 | } 70 | 71 | <-gatherComplete 72 | 73 | location, err := url.Parse(resp.Header.Get("Location")) 74 | if err != nil { 75 | panic(err) 76 | } 77 | 78 | if _, err := http.DefaultClient.Do(&http.Request{ 79 | Method: http.MethodPatch, 80 | URL: location, 81 | Header: http.Header{ 82 | "Content-Type": []string{"application/sdp"}, 83 | }, 84 | Body: io.NopCloser(bytes.NewReader([]byte(pc.LocalDescription().SDP))), 85 | }); err != nil { 86 | panic(err) 87 | } 88 | 89 | select {} 90 | } 91 | -------------------------------------------------------------------------------- /examples/publisher/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "io" 5 | "log" 6 | "net/http" 7 | "os" 8 | "strings" 9 | 10 | "github.com/pion/mediadevices" 11 | "github.com/pion/mediadevices/pkg/codec/x264" 12 | "github.com/pion/mediadevices/pkg/frame" 13 | "github.com/pion/mediadevices/pkg/prop" 14 | "github.com/pion/webrtc/v3" 15 | 16 | _ "github.com/pion/mediadevices/pkg/driver/videotest" 17 | ) 18 | 19 | func main() { 20 | x264Params, err := x264.NewParams() 21 | if err != nil { 22 | panic(err) 23 | } 24 | x264Params.BitRate = 500_000 25 | 26 | codecSelector := mediadevices.NewCodecSelector( 27 | mediadevices.WithVideoEncoders(&x264Params), 28 | ) 29 | 30 | mediaEngine := webrtc.MediaEngine{} 31 | codecSelector.Populate(&mediaEngine) 32 | api := webrtc.NewAPI(webrtc.WithMediaEngine(&mediaEngine)) 33 | pc, err := api.NewPeerConnection(webrtc.Configuration{ 34 | ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}, 35 | }) 36 | if err != nil { 37 | panic(err) 38 | } 39 | 40 | pc.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { 41 | log.Printf("Connection State has changed %s", connectionState.String()) 42 | }) 43 | 44 | s, err := mediadevices.GetUserMedia(mediadevices.MediaStreamConstraints{ 45 | Video: func(c *mediadevices.MediaTrackConstraints) { 46 | c.FrameFormat = prop.FrameFormat(frame.FormatI420) 47 | c.Width = prop.Int(640) 48 | c.Height = prop.Int(480) 49 | }, 50 | Codec: codecSelector, 51 | }) 52 | if err != nil { 53 | panic(err) 54 | } 55 | 56 | for _, track := range s.GetTracks() { 57 | log.Printf("Track (ID: %s, StreamID: %s) added", track.ID(), track.StreamID()) 58 | track.OnEnded(func(err error) { 59 | log.Printf("Track (ID: %s) ended with error: %v\n", 60 | track.ID(), err) 61 | }) 62 | 63 | if _, err := pc.AddTransceiverFromTrack(track, webrtc.RtpTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly}); err != nil { 64 | panic(err) 65 | } 66 | } 67 | 68 | offer, err := pc.CreateOffer(nil) 69 | if err != nil { 70 | panic(err) 71 | } 72 | 73 | if err := pc.SetLocalDescription(offer); err != nil { 74 | panic(err) 75 | } 76 | 77 | resp, err := http.Post(os.Args[1], "application/sdp", strings.NewReader(offer.SDP)) 78 | if err != nil { 79 | panic(err) 80 | } 81 | 82 | body, err := io.ReadAll(resp.Body) 83 | if err != nil { 84 | panic(err) 85 | } 86 | 87 | if err := pc.SetRemoteDescription(webrtc.SessionDescription{ 88 | Type: webrtc.SDPTypeAnswer, 89 | SDP: string(body), 90 | }); err != nil { 91 | panic(err) 92 | } 93 | 94 | select {} 95 | } 96 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "net/http" 8 | 9 | "github.com/google/uuid" 10 | "github.com/pion/webrtc/v3" 11 | ) 12 | 13 | func main() { 14 | pcs := make(map[string]*webrtc.PeerConnection) 15 | tracks := []*webrtc.TrackLocalStaticRTP{} 16 | 17 | http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 18 | id := r.URL.Path[len("/"):] 19 | switch r.Method { 20 | case http.MethodGet: 21 | w.WriteHeader(http.StatusMethodNotAllowed) 22 | 23 | case http.MethodPost: 24 | // WHIP create 25 | body, err := io.ReadAll(r.Body) 26 | if err != nil { 27 | w.WriteHeader(http.StatusBadRequest) 28 | return 29 | } 30 | 31 | pc, err := webrtc.NewPeerConnection(webrtc.Configuration{ 32 | ICEServers: []webrtc.ICEServer{{URLs: []string{"stun:stun.l.google.com:19302"}}}, 33 | }) 34 | if err != nil { 35 | log.Printf("Failed to create peer connection: %s", err) 36 | w.WriteHeader(http.StatusInternalServerError) 37 | return 38 | } 39 | 40 | pc.OnICEConnectionStateChange(func(connectionState webrtc.ICEConnectionState) { 41 | log.Printf("Connection State has changed %s", connectionState.String()) 42 | }) 43 | 44 | if id != "" { 45 | // WHEP create 46 | for _, t := range tracks { 47 | if t.StreamID() != id { 48 | continue 49 | } 50 | log.Printf("Adding track: %s", t.StreamID()) 51 | 52 | if _, err := pc.AddTransceiverFromTrack(t, webrtc.RtpTransceiverInit{Direction: webrtc.RTPTransceiverDirectionSendonly}); err != nil { 53 | log.Printf("Failed to add track: %s", err) 54 | return 55 | } 56 | } 57 | } else { 58 | pc.OnTrack(func(tr *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) { 59 | log.Printf("Got track: %s", tr.StreamID()) 60 | 61 | tl, err := webrtc.NewTrackLocalStaticRTP(tr.Codec().RTPCodecCapability, tr.ID(), tr.StreamID()) 62 | if err != nil { 63 | log.Printf("Failed to create track: %s", err) 64 | return 65 | } 66 | 67 | tracks = append(tracks, tl) 68 | defer func() { 69 | for i, t := range tracks { 70 | if t == tl { 71 | tracks = append(tracks[:i], tracks[i+1:]...) 72 | break 73 | } 74 | } 75 | }() 76 | 77 | for { 78 | p, _, err := tr.ReadRTP() 79 | if err != nil { 80 | log.Printf("Failed to read RTP: %s", err) 81 | return 82 | } 83 | if err := tl.WriteRTP(p); err != nil { 84 | log.Printf("Failed to write RTP: %s", err) 85 | return 86 | } 87 | } 88 | }) 89 | } 90 | 91 | gatherComplete := webrtc.GatheringCompletePromise(pc) 92 | 93 | if len(body) > 0 { 94 | if err := pc.SetRemoteDescription(webrtc.SessionDescription{ 95 | Type: webrtc.SDPTypeOffer, 96 | SDP: string(body), 97 | }); err != nil { 98 | log.Printf("Failed to set remote description: %s", err) 99 | w.WriteHeader(http.StatusInternalServerError) 100 | return 101 | } 102 | 103 | answer, err := pc.CreateAnswer(nil) 104 | if err != nil { 105 | log.Printf("Failed to create answer: %s", err) 106 | w.WriteHeader(http.StatusInternalServerError) 107 | return 108 | } 109 | 110 | if err := pc.SetLocalDescription(answer); err != nil { 111 | log.Printf("Failed to set local description: %s", err) 112 | w.WriteHeader(http.StatusInternalServerError) 113 | return 114 | } 115 | } else { 116 | offer, err := pc.CreateOffer(nil) 117 | if err != nil { 118 | log.Printf("Failed to create offer: %s", err) 119 | w.WriteHeader(http.StatusInternalServerError) 120 | return 121 | } 122 | 123 | if err := pc.SetLocalDescription(offer); err != nil { 124 | log.Printf("Failed to set local description: %s", err) 125 | w.WriteHeader(http.StatusInternalServerError) 126 | return 127 | } 128 | } 129 | <-gatherComplete 130 | 131 | pcid := uuid.NewString() 132 | 133 | w.Header().Set("Content-Type", "application/sdp") 134 | w.Header().Set("Location", fmt.Sprintf("http://%s/%s", r.Host, pcid)) 135 | if id == "" { 136 | w.WriteHeader(http.StatusCreated) 137 | } 138 | 139 | 140 | if _, err := w.Write([]byte(pc.LocalDescription().SDP)); err != nil { 141 | log.Printf("Failed to write response: %s", err) 142 | w.WriteHeader(http.StatusInternalServerError) 143 | return 144 | } 145 | 146 | pcs[pcid] = pc 147 | case http.MethodPatch: 148 | if r.Header.Get("Content-Type") != "application/sdp" { 149 | // Trickle ICE/ICE restart not implemented 150 | panic("Not implemented") 151 | } 152 | 153 | pc, ok := pcs[id] 154 | if !ok { 155 | w.WriteHeader(http.StatusNotFound) 156 | return 157 | } 158 | 159 | body, err := io.ReadAll(r.Body) 160 | if err != nil { 161 | w.WriteHeader(http.StatusBadRequest) 162 | return 163 | } 164 | if err := pc.SetRemoteDescription(webrtc.SessionDescription{ 165 | Type: webrtc.SDPTypeAnswer, 166 | SDP: string(body), 167 | }); err != nil { 168 | log.Printf("Failed to set remote description: %s", err) 169 | w.WriteHeader(http.StatusInternalServerError) 170 | return 171 | } 172 | case http.MethodDelete: 173 | pc, ok := pcs[id] 174 | if !ok { 175 | w.WriteHeader(http.StatusNotFound) 176 | return 177 | } 178 | 179 | if err := pc.Close(); err != nil { 180 | log.Printf("Failed to close peer connection: %s", err) 181 | w.WriteHeader(http.StatusInternalServerError) 182 | return 183 | } 184 | 185 | delete(pcs, id) 186 | } 187 | }) 188 | 189 | panic(http.ListenAndServe(":8080", nil)) 190 | } 191 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/blackjack/webcam v0.0.0-20220329180758-ba064708e165/go.mod h1:G0X+rEqYPWSq0dG8OMf8M446MtKytzpPjgS3HbdOJZ4= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 4 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 5 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 6 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 7 | github.com/gen2brain/malgo v0.11.10/go.mod h1:f9TtuN7DVrXMiV/yIceMeWpvanyVzJQMlBecJFVMxww= 8 | github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo= 9 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 10 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 11 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 12 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 13 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 14 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 15 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 16 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 17 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 18 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 19 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 20 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 21 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 22 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 23 | github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= 24 | github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 25 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 26 | github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240/go.mod h1:3P4UH/k22rXyHIJD2w4h2XMqPX4Of/eySEZq9L6wqc4= 27 | github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329/go.mod h1:2VPVQDR4wO7KXHwP+DAypEy67rXf+okUx2zjgpCxZw4= 28 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 29 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 30 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 31 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 32 | github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= 33 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 34 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 35 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 36 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 37 | github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 38 | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 39 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 40 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 41 | github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= 42 | github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ= 43 | github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew8= 44 | github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0= 45 | github.com/pion/dtls/v2 v2.1.5 h1:jlh2vtIyUBShchoTDqpCCqiYCyRFJ/lvf/gQ8TALs+c= 46 | github.com/pion/dtls/v2 v2.1.5/go.mod h1:BqCE7xPZbPSubGasRoDFJeTsyJtdD1FanJYL0JGheqY= 47 | github.com/pion/ice/v2 v2.2.11/go.mod h1:NqUDUao6SjSs1+4jrqpexDmFlptlVhGxQjcymXLaVvE= 48 | github.com/pion/ice/v2 v2.2.12 h1:n3M3lUMKQM5IoofhJo73D3qVla+mJN2nVvbSPq32Nig= 49 | github.com/pion/ice/v2 v2.2.12/go.mod h1:z2KXVFyRkmjetRlaVRgjO9U3ShKwzhlUylvxKfHfd5A= 50 | github.com/pion/interceptor v0.1.11/go.mod h1:tbtKjZY14awXd7Bq0mmWvgtHB5MDaRN7HV3OZ/uy7s8= 51 | github.com/pion/interceptor v0.1.12 h1:CslaNriCFUItiXS5o+hh5lpL0t0ytQkFnUcbbCs2Zq8= 52 | github.com/pion/interceptor v0.1.12/go.mod h1:bDtgAD9dRkBZpWHGKaoKb42FhDHTG2rX8Ii9LRALLVA= 53 | github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= 54 | github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= 55 | github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw= 56 | github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g= 57 | github.com/pion/mediadevices v0.3.12 h1:B3Vn6o5jrqH8Sp5Yop7JLAwijgcOnbLJmZzCJqnUfeE= 58 | github.com/pion/mediadevices v0.3.12/go.mod h1:hWckH+b9Q+M0U1F8toPJCWLeirKhQm0C9z1Y3HHG9lc= 59 | github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= 60 | github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= 61 | github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo= 62 | github.com/pion/rtcp v1.2.10 h1:nkr3uj+8Sp97zyItdN60tE/S6vk4al5CPRR6Gejsdjc= 63 | github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I= 64 | github.com/pion/rtp v1.7.13 h1:qcHwlmtiI50t1XivvoawdCGTP4Uiypzfrsap+bijcoA= 65 | github.com/pion/rtp v1.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= 66 | github.com/pion/sctp v1.8.0/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= 67 | github.com/pion/sctp v1.8.3/go.mod h1:OHbDjdk7kg+L+7TJim9q/qGVefdEJohuA2SZyihccgI= 68 | github.com/pion/sctp v1.8.5 h1:JCc25nghnXWOlSn3OVtEnA9PjQ2JsxQbG+CXZ1UkJKQ= 69 | github.com/pion/sctp v1.8.5/go.mod h1:SUFFfDpViyKejTAdwD1d/HQsCu+V/40cCs2nZIvC3s0= 70 | github.com/pion/sdp/v3 v3.0.6 h1:WuDLhtuFUUVpTfus9ILC4HRyHsW6TdugjEX/QY9OiUw= 71 | github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw= 72 | github.com/pion/srtp/v2 v2.0.10 h1:b8ZvEuI+mrL8hbr/f1YiJFB34UMrOac3R3N1yq2UN0w= 73 | github.com/pion/srtp/v2 v2.0.10/go.mod h1:XEeSWaK9PfuMs7zxXyiN252AHPbH12NX5q/CFDWtUuA= 74 | github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg= 75 | github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA= 76 | github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= 77 | github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A= 78 | github.com/pion/transport v0.13.0/go.mod h1:yxm9uXpK9bpBBWkITk13cLo1y5/ur5VQpG22ny6EP7g= 79 | github.com/pion/transport v0.13.1/go.mod h1:EBxbqzyv+ZrmDb82XswEE0BjfQFtuw1Nu6sjnjWCsGg= 80 | github.com/pion/transport v0.14.1 h1:XSM6olwW+o8J4SCmOBb/BpwZypkHeyM0PGFCxNQBr40= 81 | github.com/pion/transport v0.14.1/go.mod h1:4tGmbk00NeYA3rUa9+n+dzCCoKkcy3YlYb99Jn2fNnI= 82 | github.com/pion/turn/v2 v2.0.8 h1:KEstL92OUN3k5k8qxsXHpr7WWfrdp7iJZHx99ud8muw= 83 | github.com/pion/turn/v2 v2.0.8/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw= 84 | github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= 85 | github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= 86 | github.com/pion/webrtc/v3 v3.1.48/go.mod h1:JOk9h4pOtogTAWM1SCoEG2opDQmEOR0QZcbEo9vy0Xc= 87 | github.com/pion/webrtc/v3 v3.1.50 h1:wLMo1+re4WMZ9Kun9qcGcY+XoHkE3i0CXrrc0sjhVCk= 88 | github.com/pion/webrtc/v3 v3.1.50/go.mod h1:y9n09weIXB+sjb9mi0GBBewNxo4TKUQm5qdtT5v3/X4= 89 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 90 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 91 | github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= 92 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 93 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 94 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 95 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 96 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 97 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 98 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 99 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 100 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= 101 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 102 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 103 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 104 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 105 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 106 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 107 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= 108 | golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 109 | golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2 h1:x8vtB3zMecnlqZIwJNUUpwYKYSqCz5jXbiyv0ZJJZeI= 110 | golang.org/x/crypto v0.0.0-20221010152910-d6f0a8c073c2/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 111 | golang.org/x/image v0.1.0 h1:r8Oj8ZA2Xy12/b5KZYj3tuv7NG/fBz3TwQVvpJ9l8Rk= 112 | golang.org/x/image v0.1.0/go.mod h1:iyPr49SD/G/TBxYVB/9RRtGUT5eNbo2u4NamWeQcD5c= 113 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 114 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= 115 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 116 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 117 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 118 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 119 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 120 | golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 121 | golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 122 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 123 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 124 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 125 | golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 126 | golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 127 | golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 128 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 129 | golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= 130 | golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= 131 | golang.org/x/net v0.3.0 h1:VWL6FNY2bEEmsGVKabSlHu5Irp34xmMRoqb/9lF9lxk= 132 | golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= 133 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 134 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 135 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 136 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 137 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 138 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 139 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 140 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 141 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 142 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 143 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 144 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 145 | golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 146 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 147 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 148 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 149 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 150 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 151 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 152 | golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 153 | golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 154 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 155 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 156 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 157 | golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 158 | golang.org/x/sys v0.3.0 h1:w8ZOecv6NaNa/zC8944JTU3vz4u6Lagfk4RPQxv92NQ= 159 | golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 160 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 161 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 162 | golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 163 | golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= 164 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 165 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 166 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 167 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 168 | golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 169 | golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= 170 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 171 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 172 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 173 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= 174 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 175 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 176 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 177 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 178 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 179 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 180 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 181 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 182 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 183 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 184 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 185 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 186 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 187 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 188 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 189 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 190 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 191 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 192 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 193 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 194 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 195 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 196 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 197 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 198 | --------------------------------------------------------------------------------