diff --git a/drivers/audio/wavfifo.go b/drivers/audio/wavfifo.go new file mode 100644 index 0000000..ee85834 --- /dev/null +++ b/drivers/audio/wavfifo.go @@ -0,0 +1,92 @@ +package audio + +import ( + "context" + "encoding/binary" + "io" + + "github.com/pion/mediadevices/pkg/io/audio" + "github.com/pion/mediadevices/pkg/prop" + "github.com/pion/mediadevices/pkg/wave" + "github.com/sirupsen/logrus" +) + +const ( + DataChunkIDSize = 4 + DataChunkSizeSize = 4 +) + +// BufferSize for pcm bytes +const BitsPerByte = 8 + +type PCMStreamDriver struct { + PCM <-chan []byte + BufferSizeByBytes uint16 + WaveHeader *WavHeader + closed <-chan struct{} + cancel func() +} + +func (w *PCMStreamDriver) Open() error { + ctx, cancel := context.WithCancel(context.Background()) + w.closed = ctx.Done() + w.cancel = cancel + + return nil +} + +func (w *PCMStreamDriver) Close() error { + w.cancel() + return nil +} + +func (w *PCMStreamDriver) Properties() []prop.Media { + logrus.Debugf("wave header: %v", w.WaveHeader) + return []prop.Media{ + { + Audio: prop.Audio{ + SampleRate: int(w.WaveHeader.SampleRate), + ChannelCount: int(w.WaveHeader.NumChannels), + SampleSize: int(w.WaveHeader.BitsPerSample), + Latency: w.WaveHeader.GetLatnecy(w.BufferSizeByBytes), + IsFloat: false, // just 8bit or 16bit with qemu + IsBigEndian: false, // qemu should be little endian + IsInterleaved: true, + }, + }, + } +} + +func (w *PCMStreamDriver) AudioRecord(p prop.Media) (audio.Reader, error) { + logrus.Debug(p) + chunkInfo := wave.ChunkInfo{ + Len: int(w.BufferSizeByBytes) / int(p.SampleSize/BitsPerByte), + Channels: p.ChannelCount, + SamplingRate: p.SampleRate, + } + + reader := func() (wave.Audio, func(), error) { + a := wave.NewInt16Interleaved(chunkInfo) + select { + case <-w.closed: + return nil, func() {}, io.EOF + case pcmData, ok := <-w.PCM: + logrus.Debug("got %d bytes pcm data", len(pcmData)) + if !ok { + return nil, func() {}, io.ErrClosedPipe + } + copy(a.Data, bytesTo16BitSamples(pcmData[:])) + } + return a, func() {}, nil + } + return audio.ReaderFunc(reader), nil +} + +func bytesTo16BitSamples(b []byte) []int16 { + samples := make([]int16, 0) + for i := 0; i < len(b); i += 2 { + sample := binary.LittleEndian.Uint16(b[i : i+1]) + samples = append(samples, int16(sample)) + } + return samples +} diff --git a/drivers/audio/wavheader.go b/drivers/audio/wavheader.go new file mode 100644 index 0000000..6e6f62d --- /dev/null +++ b/drivers/audio/wavheader.go @@ -0,0 +1,124 @@ +package audio + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "errors" + "io" + "time" +) + +// Skip riff header and `fmt ` just 16 bytes +const ( + FmtHeaderOffset = 0x0c + FmtHeaderIDSize = 4 + FmtHeaderChunkSizeSize = 4 + FmtHeaderSizeDefault = 16 +) + +type WavHeader struct { + ID [4]byte + Size uint32 + AudioFormat uint16 + NumChannels uint16 + SampleRate uint32 + ByteRate uint32 + BlockAlign uint16 + BitsPerSample uint16 +} + +func NewHeader(f io.Reader) (*WavHeader, error) { + w := &WavHeader{} + if err := w.Parse(f); err != nil { + return nil, err + } + return w, nil +} + +func DefaultHeader() *WavHeader { + return &WavHeader{ + Size: uint32(FmtHeaderSizeDefault), + AudioFormat: 1, + NumChannels: 2, + SampleRate: 48000, // opus only support 48kHz + BlockAlign: 4, + BitsPerSample: 16, + } +} + +func (w *WavHeader) Parse(f io.Reader) error { + // skip headers + var headers [FmtHeaderOffset]byte + if _, err := f.Read(headers[:]); err != nil { + return err + } + + var id [4]byte + if _, err := f.Read(id[:]); err != nil { + return err + } + if bytes.Equal(id[:], []byte{'f', 'm', 't', '0'}[:]) { + return errors.New("bad header") + } + w.ID = id + + var size [4]byte + if _, err := f.Read(size[:]); err != nil { + return err + } + w.Size = binary.LittleEndian.Uint32(size[:]) + + var af [2]byte + if _, err := f.Read(af[:]); err != nil { + return err + } + w.AudioFormat = binary.LittleEndian.Uint16(af[:]) + + var nc [2]byte + if _, err := f.Read(nc[:]); err != nil { + return err + } + w.NumChannels = binary.LittleEndian.Uint16(nc[:]) + + var sr [4]byte + if _, err := f.Read(sr[:]); err != nil { + return err + } + w.SampleRate = binary.LittleEndian.Uint32(sr[:]) + + var br [4]byte + if _, err := f.Read(br[:]); err != nil { + return err + } + w.ByteRate = binary.LittleEndian.Uint32(br[:]) + + var ba [2]byte + if _, err := f.Read(ba[:]); err != nil { + return err + } + w.BlockAlign = binary.LittleEndian.Uint16(ba[:]) + + var bps [2]byte + if _, err := f.Read(bps[:]); err != nil { + return err + } + w.BitsPerSample = binary.LittleEndian.Uint16(bps[:]) + + return nil +} + +func (w *WavHeader) String() string { + b, _ := json.Marshal(w) + return string(b) +} + +func (w *WavHeader) GetLatnecy(bufferSizeByBytes uint16) time.Duration { + bytesPerSample := w.BitsPerSample / BitsPerByte + bufferLength := bufferSizeByBytes / bytesPerSample + + return time.Second * + time.Duration(bufferLength) / + time.Duration(w.NumChannels) / + time.Duration(w.SampleRate) +} diff --git a/drivers/video/ppm.go b/drivers/video/ppm.go new file mode 100644 index 0000000..3be6027 --- /dev/null +++ b/drivers/video/ppm.go @@ -0,0 +1,92 @@ +package video + +import ( + "context" + "image" + "image/color" + "io" + + "github.com/pion/mediadevices/pkg/frame" + "github.com/pion/mediadevices/pkg/io/video" + "github.com/pion/mediadevices/pkg/prop" + "github.com/sirupsen/logrus" + + _ "github.com/jbuchbinder/gopnm" +) + +const DefaultFPS float32 = 60.0 + +type PPMStreamDriver struct { + Height, Width int + FPS float32 + PPMImage <-chan io.ReadCloser + closed <-chan struct{} + cancel func() +} + +func (v *PPMStreamDriver) Open() error { + defer logrus.Debug("device opened") + ctx, cancel := context.WithCancel(context.Background()) + v.closed = ctx.Done() + v.cancel = cancel + return nil +} + +func (v *PPMStreamDriver) Close() error { + v.cancel() + return nil +} + +func (v *PPMStreamDriver) Properties() []prop.Media { + return []prop.Media{ + { + Video: prop.Video{ + Width: v.Width, + Height: v.Height, + FrameRate: v.FPS, + FrameFormat: frame.FormatYUYV, + }, + }, + } +} + +func (v *PPMStreamDriver) VideoRecord(p prop.Media) (video.Reader, error) { + r := video.ReaderFunc(func() (img image.Image, release func(), err error) { + canvas := image.NewYCbCr( + image.Rect(0, 0, p.Width, p.Height), + image.YCbCrSubsampleRatio420, + ) + + select { + case <-v.closed: + return nil, func() {}, io.EOF + case ppmF, ok := <-v.PPMImage: + if !ok { + return nil, func() {}, io.ErrClosedPipe + } + defer ppmF.Close() + + img, _, err := image.Decode(ppmF) + if err != nil { + return nil, func() {}, err + } + + // draw input image in middle of canvas + offsetX := (canvas.Bounds().Dx() - img.Bounds().Dx()) / 2 + offsetY := (canvas.Bounds().Dy() - img.Bounds().Dy()) / 2 + for x := 0; x < img.Bounds().Dx(); x++ { + for y := 0; y < img.Bounds().Dy(); y++ { + r, g, b, _ := img.At(x, y).RGBA() + Y, Cb, Cr := color.RGBToYCbCr(uint8(r), uint8(g), uint8(b)) + + canvas.Y[canvas.YOffset(offsetX+x, offsetY+y)] = Y + canvas.Cb[canvas.COffset(offsetX+x, offsetY+y)] = Cb + canvas.Cr[canvas.COffset(offsetX+x, offsetY+y)] = Cr + } + } + } + + return canvas, func() {}, nil + }) + return r, nil +} diff --git a/go.mod b/go.mod index 3ee5a9f..79ecbd3 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module git.sense-t.eu.org/ACE/ace go 1.19 require ( + github.com/jbuchbinder/gopnm v0.0.0-20220507095634-e31f54490ce0 github.com/pion/mediadevices v0.3.11 github.com/urfave/cli/v2 v2.11.2 ) @@ -50,7 +51,6 @@ require ( require ( github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect github.com/digitalocean/go-qemu v0.0.0-20220804221245-2002801203aa - github.com/gin-contrib/static v0.0.1 github.com/gin-gonic/gin v1.8.1 github.com/pion/webrtc/v3 v3.1.43 github.com/russross/blackfriday/v2 v2.1.0 // indirect diff --git a/go.sum b/go.sum index 183f3f4..24fdaf1 100644 --- a/go.sum +++ b/go.sum @@ -16,27 +16,20 @@ github.com/gen2brain/malgo v0.10.35/go.mod h1:zHSUNZAXfCeNsZou0RtQ6Zk7gDYLIcKOrU github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= -github.com/gin-contrib/static v0.0.1 h1:JVxuvHPuUfkoul12N7dtQw7KRn/pSMq7Ue1Va9Swm1U= -github.com/gin-contrib/static v0.0.1/go.mod h1:CSxeF+wep05e0kCOsqWdAWbSszmc31zTIbD8TvWl7Hs= -github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.8.1 h1:4+fr/el88TOO3ewCmQr8cx/CtZ/umlIRIs5M4NTNjf8= github.com/gin-gonic/gin v1.8.1/go.mod h1:ji8BvRH1azfM+SYow9zQ6SZMvR8qOMZHmsCuWR9tTTk= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= -github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= -github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= -github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.10.0 h1:I7mrTYv78z8k8VXa/qJlOlEXn/nBh+BF8dHX5nt/dr0= github.com/go-playground/validator/v10 v10.10.0/go.mod h1:74x4gJWsvQexRdW8Pn3dXSGrTK4nAUsbPlLADvpJkos= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/goccy/go-json v0.9.7 h1:IcB+Aqpx/iMHu5Yooh7jEzJk1JZ7Pjtmys2ukPr7EeM= github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= @@ -54,8 +47,9 @@ github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jbuchbinder/gopnm v0.0.0-20220507095634-e31f54490ce0 h1:9GwwkVzUn1vRWAQ8GRu7UOaoM+FZGnvw88DsjyiqfXc= +github.com/jbuchbinder/gopnm v0.0.0-20220507095634-e31f54490ce0/go.mod h1:6U0E76+sB1jTuSSXJjePtLd44vExeoYThOWgOoXo3x8= github.com/jezek/xgb v0.0.0-20210312150743-0e0f116e1240/go.mod h1:3P4UH/k22rXyHIJD2w4h2XMqPX4Of/eySEZq9L6wqc4= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kbinani/screenshot v0.0.0-20210720154843-7d3a670d8329/go.mod h1:2VPVQDR4wO7KXHwP+DAypEy67rXf+okUx2zjgpCxZw4= @@ -67,17 +61,14 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= github.com/lxn/win v0.0.0-20210218163916-a377121e959e/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -147,15 +138,12 @@ github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0 github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 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= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M= -github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= github.com/urfave/cli/v2 v2.11.2 h1:FVfNg4m3vbjbBpLYxW//WjxUoHvJ9TlppXcqY9Q9ZfA= @@ -204,7 +192,6 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201018230417-eeed37f84f13/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -225,7 +212,6 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc 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/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= @@ -259,7 +245,6 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=