Use modified x264 lib (#275)

* Use modified x264 lib

* Add x264 system lib

* Set x264 version 155 for Debian (Buster)

* Add h264 config params

* Set vp8 codec
This commit is contained in:
sergystepanov 2021-02-21 13:51:44 +03:00 committed by Sergey Stepanov
parent b56069c96e
commit bd701f10fc
No known key found for this signature in database
GPG key ID: A56B4929BAA8556B
26 changed files with 418 additions and 209 deletions

View file

@ -32,12 +32,12 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y make pkg-config libvpx-dev libopus-dev libopusfile-dev libsdl2-dev libgl1-mesa-glx
sudo apt-get install -y make pkg-config libvpx-dev libx264-dev libopus-dev libopusfile-dev libsdl2-dev libgl1-mesa-glx
- name: Get MacOS dev libraries and tools
if: matrix.os == 'macos-latest'
run: |
brew install libvpx pkg-config opus opusfile sdl2
brew install pkg-config libvpx x264 opus opusfile sdl2
- name: Get Windows dev libraries and tools
if: matrix.os == 'windows-latest'
@ -51,6 +51,7 @@ jobs:
mingw-w64-x86_64-pkg-config
mingw-w64-x86_64-dlfcn
mingw-w64-x86_64-libvpx
mingw-w64-x86_64-x264-git
mingw-w64-x86_64-opusfile
mingw-w64-x86_64-SDL2

View file

@ -49,12 +49,12 @@ jobs:
if: matrix.os == 'ubuntu-latest'
run: |
sudo apt-get update
sudo apt-get install -y make pkg-config libvpx-dev libopus-dev libopusfile-dev libsdl2-dev
sudo apt-get install -y make pkg-config libvpx-dev libx264-dev libopus-dev libopusfile-dev libsdl2-dev
- name: Get MacOS dev libraries and tools
if: matrix.os == 'macos-latest'
run: |
brew install libvpx pkg-config opus opusfile sdl2
brew install pkg-config libvpx x264 opus opusfile sdl2
- name: Get Windows dev libraries and tools
if: matrix.os == 'windows-latest'
@ -81,6 +81,7 @@ jobs:
mingw-w64-x86_64-pkg-config
mingw-w64-x86_64-dlfcn
mingw-w64-x86_64-libvpx
mingw-w64-x86_64-x264-git
mingw-w64-x86_64-opusfile
mingw-w64-x86_64-SDL2

2
Dockerfile vendored
View file

@ -11,6 +11,7 @@ RUN apt-get update && apt-get install -y \
make \
pkg-config \
libvpx-dev \
libx264-dev \
libopus-dev \
libopusfile-dev \
libsdl2-dev \
@ -32,6 +33,7 @@ WORKDIR /usr/local/share/cloud-game
RUN apt-get update && apt-get install --no-install-recommends -y \
ca-certificates \
libvpx5 \
libx264-155 \
libopus0 \
libopusfile0 \
libsdl2-2.0-0 \

8
README.md vendored
View file

@ -42,16 +42,16 @@ Install Golang https://golang.org/doc/install . Because the project uses GoModul
### Install Dependencies
* Install [libvpx](https://www.webmproject.org/code/), [libopus](http://opus-codec.org/), [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/), [sdl2](https://wiki.libsdl.org/Installation)
* Install [libvpx](https://www.webmproject.org/code/), [libx264](https://www.videolan.org/developers/x264.html), [libopus](http://opus-codec.org/), [pkg-config](https://www.freedesktop.org/wiki/Software/pkg-config/), [sdl2](https://wiki.libsdl.org/Installation)
```
# Ubuntu / Windows (WSL2)
apt-get install -y make gcc pkg-config libvpx-dev libopus-dev libopusfile-dev libsdl2-dev
apt-get install -y make gcc pkg-config libvpx-dev libx264-dev libopus-dev libopusfile-dev libsdl2-dev
# MacOS
brew install libvpx pkg-config opus opusfile sdl2
brew install pkg-config libvpx x264 opus opusfile sdl2
# Windows (MSYS2)
pacman -Sy --noconfirm --needed git make mingw-w64-x86_64-{gcc,pkg-config,dlfcn,libvpx,opusfile,SDL2}
pacman -Sy --noconfirm --needed git make mingw-w64-x86_64-{gcc,pkg-config,dlfcn,libvpx,x264-git,opusfile,SDL2}
```
Because the coordinator and workers need to run simultaneously. Workers connect to the coordinator.

15
configs/config.yaml vendored
View file

@ -150,6 +150,21 @@ encoder:
# audio frame duration needed for WebRTC (Opus)
frame: 20
frequency: 48000
video:
# h264, vpx
codec: vpx
# see: https://trac.ffmpeg.org/wiki/Encode/H.264
h264:
# Constant Rate Factor (CRF) 0-51 (default: 23)
crf: 17
# ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo
preset: veryfast
# baseline, main, high, high10, high422, high444
profile: main
# film, animation, grain, stillimage, psnr, ssim, fastdecode, zerolatency
tune: zerolatency
# 0-3
logLevel: 0
# run without a game
# (experimental)
withoutGame: false

24
go.mod vendored
View file

@ -3,12 +3,11 @@ module github.com/giongto35/cloud-game/v2
go 1.13
require (
cloud.google.com/go v0.75.0 // indirect
cloud.google.com/go/storage v1.12.0
cloud.google.com/go v0.77.0 // indirect
cloud.google.com/go/storage v1.13.0
github.com/cavaliercoder/grab v1.0.1-0.20201108051000-98a5bfe305ec
github.com/faiface/mainthread v0.0.0-20171120011319-8b78f0a41ae3
github.com/fsnotify/fsnotify v1.4.9
github.com/gen2brain/x264-go v0.0.0-20200605131102-0523307cbe23
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7
github.com/gofrs/flock v0.8.0
github.com/gofrs/uuid v4.0.0+incompatible
@ -19,26 +18,19 @@ require (
github.com/kkyr/fig v0.2.0
github.com/mitchellh/mapstructure v1.4.1 // indirect
github.com/pelletier/go-toml v1.8.1 // indirect
github.com/pion/ice/v2 v2.0.15 // indirect
github.com/pion/interceptor v0.0.9
github.com/pion/rtp v1.6.2
github.com/pion/webrtc/v3 v3.0.4
github.com/pion/webrtc/v3 v3.0.8
github.com/prometheus/client_golang v1.9.0
github.com/prometheus/procfs v0.3.0 // indirect
github.com/prometheus/procfs v0.6.0 // indirect
github.com/sergystepanov/x264-go/v2 v2.0.0-20210211174636-21169c39dab5
github.com/spf13/pflag v1.0.5
github.com/veandco/go-sdl2 v0.4.5
go.opencensus.io v0.22.6 // indirect
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
golang.org/x/image v0.0.0-20201208152932-35266b937fa6
golang.org/x/mod v0.4.1 // indirect
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect
golang.org/x/oauth2 v0.0.0-20210126194326-f9ce19ea3013 // indirect
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c // indirect
golang.org/x/text v0.3.5 // indirect
golang.org/x/tools v0.1.0 // indirect
google.golang.org/api v0.38.0 // indirect
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506 // indirect
google.golang.org/grpc v1.35.0 // indirect
golang.org/x/image v0.0.0-20210216034530-4410531fe030
golang.org/x/oauth2 v0.0.0-20210210192628-66670185b0cd // indirect
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65 // indirect
gopkg.in/hraban/opus.v2 v2.0.0-20201025103112-d779bb1cc5a2
gopkg.in/yaml.v2 v2.4.0 // indirect
)

66
go.sum vendored
View file

@ -13,12 +13,12 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.66.0 h1:DZeAkuQGQqnm9Xv36SbMJEU8aFBz4wL04UpMWPWwjzg=
cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0Ko=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0 h1:XgtDnVJRCPEUG21gjFiRPz4zI1Mjg16R+NYQjfmU4XY=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go v0.77.0 h1:qA5V5+uQf6Mgr+tmFI8UT3D/ELyhIYkPwNGao/3Y+sQ=
cloud.google.com/go v0.77.0/go.mod h1:R8fYSLIilC247Iu8WS2OGHw1E/Ufn7Pd7HiDjTqiURs=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
@ -37,12 +37,13 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0 h1:STgFzyU5/8miMl0//zKh2aQeTyeaUH3WN9bSUiJ09bA=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.12.0 h1:4y3gHptW1EHVtcPAVE0eBBlFuGqEejTTG3KdIE0lUX4=
cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho=
cloud.google.com/go/storage v1.13.0 h1:amPvhCOI+Hltp6rPu+62YdwhIrjf+34PKVAL4HwgYwk=
cloud.google.com/go/storage v1.13.0/go.mod h1:pqFyBUK3zZqMIIU5+8NaZq6/Ma3ClgUg9Hv5jfuJnvo=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/BurntSushi/xgb v0.0.0-20210121224620-deaf085860bc/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
@ -114,8 +115,7 @@ github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/gen2brain/x264-go v0.0.0-20200605131102-0523307cbe23 h1:/bEJAMYC2kdJwLeemSoFiEir0ONJdBdK7sMdLMKBQHU=
github.com/gen2brain/x264-go v0.0.0-20200605131102-0523307cbe23/go.mod h1:17kvfYQKi9/QHiKPeqmJW0YuDPZEgy72tSBVmweSyiE=
github.com/gen2brain/shm v0.0.0-20200228170931-49f9650110c5/go.mod h1:uF6rMu/1nvu+5DpiRLwusA6xB8zlkNoGzKn8lmYONUo=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
@ -201,10 +201,10 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
@ -270,6 +270,7 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kbinani/screenshot v0.0.0-20191211154542-3a185f1ce18f/go.mod h1:f8GY5V3lRzakvEyr49P7hHRYoHtPr8zvj/7JodCoRzw=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkyr/fig v0.2.0 h1:t/5yENaBw8ATXbQSWpPqwXLCn6wdhEi6jWXRfUgytZI=
@ -284,6 +285,7 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
github.com/lxn/win v0.0.0-20201111105847-2a20daff6a55/go.mod h1:KxxjdtRkfNoYDCUP5ryK7XJJNTnpC8atvtmTheChOtk=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@ -354,8 +356,8 @@ github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXm
github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg=
github.com/pion/dtls/v2 v2.0.4 h1:WuUcqi6oYMu/noNTz92QrF1DaFj4eXbhQ6dzaaAwOiI=
github.com/pion/dtls/v2 v2.0.4/go.mod h1:qAkFscX0ZHoI1E07RfYPoRw3manThveu+mlTDdOxoGI=
github.com/pion/ice/v2 v2.0.14 h1:FxXxauyykf89SWAtkQCfnHkno6G8+bhRkNguSh9zU+4=
github.com/pion/ice/v2 v2.0.14/go.mod h1:wqaUbOq5ObDNU5ox1hRsEst0rWfsKuH1zXjQFEWiZwM=
github.com/pion/dtls/v2 v2.0.5 h1:jgQJRK2IJ9eWQAcUEZN4M0tnCi5X/cERnxH9J8qOjR0=
github.com/pion/dtls/v2 v2.0.5/go.mod h1:QuDII+8FVvk9Dp5t5vYIMTo7hh7uBkra+8QIm7QGm10=
github.com/pion/ice/v2 v2.0.15 h1:KZrwa2ciL9od8+TUVJiYTNsCW9J5lktBjGwW1MacEnQ=
github.com/pion/ice/v2 v2.0.15/go.mod h1:ZIiVGevpgAxF/cXiIVmuIUtCb3Xs4gCzCbXB6+nFkSI=
github.com/pion/interceptor v0.0.9 h1:fk5hTdyLO3KURQsf/+RjMpEm4NE3yeTY9Kh97b5BvwA=
@ -385,7 +387,6 @@ github.com/pion/transport v0.8.10/go.mod h1:tBmha/UCjpum5hqTWhfAEs3CO4/tHSg0MYRh
github.com/pion/transport v0.10.0/go.mod h1:BnHnUipd0rZQyTVB2SBGojFHT9CBt5C5TcsJSQGkvSE=
github.com/pion/transport v0.10.1 h1:2W+yJT+0mOQ160ThZYUx5Zp2skzshiNgxrNE9GUfhJM=
github.com/pion/transport v0.10.1/go.mod h1:PBis1stIILMiis0PewDw91WJeLJkyIMcEk+DwKOzf4A=
github.com/pion/transport v0.12.0/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
github.com/pion/transport v0.12.1/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
github.com/pion/transport v0.12.2 h1:WYEjhloRHt1R86LhUKjC5y+P52Y11/QqEUalvtzVoys=
github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q=
@ -393,8 +394,8 @@ github.com/pion/turn/v2 v2.0.5 h1:iwMHqDfPEDEOFzwWKT56eFmh6DYC6o/+xnLAEzgISbA=
github.com/pion/turn/v2 v2.0.5/go.mod h1:APg43CFyt/14Uy7heYUOGWdkem/Wu4PhCO/bjyrTqMw=
github.com/pion/udp v0.1.0 h1:uGxQsNyrqG3GLINv36Ff60covYmfrLoxzwnCsIYspXI=
github.com/pion/udp v0.1.0/go.mod h1:BPELIjbwE9PRbd/zxI/KYBnbo7B6+oA6YuEaNE8lths=
github.com/pion/webrtc/v3 v3.0.4 h1:Tiw3H9fpfcwkvaxonB+Gv1DG9tmgYBQaM1vBagDHP40=
github.com/pion/webrtc/v3 v3.0.4/go.mod h1:1TmFSLpPYFTFXFHPtoq9eGP1ASTa9LC6FBh7sUY8cd4=
github.com/pion/webrtc/v3 v3.0.8 h1:Dgu/NZ6QAIvoNZU3qk/B35iPPx6TVHP506FfCE4SXCA=
github.com/pion/webrtc/v3 v3.0.8/go.mod h1:C5uzSMa9sGCtfVPLA+pB0eWoW/exZ0OV0KW7JJbkvp0=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
@ -434,8 +435,8 @@ github.com/prometheus/procfs v0.1.3 h1:F0+tqvhOksq22sc6iCHF5WGlWjdwj92p0udFh1VFB
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.3.0 h1:Uehi/mxLK0eiUc0H0++5tpMGTexB8wZ598MIgU8VpDM=
github.com/prometheus/procfs v0.3.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
@ -444,6 +445,8 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/sergystepanov/x264-go/v2 v2.0.0-20210211174636-21169c39dab5 h1:M7Ck/uhuFxa7rTMcZcC7hCcdTVPJgAXbGwuK1D6mYro=
github.com/sergystepanov/x264-go/v2 v2.0.0-20210211174636-21169c39dab5/go.mod h1:WfHP0dmR01K6NQeOyZf5oZdWvnlmSt/th1puDtIeJbI=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@ -526,8 +529,8 @@ golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EH
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20201208152932-35266b937fa6 h1:nfeHNc1nAqecKCy2FCy4HY+soOOe5sDLJ/gZLbx6GYI=
golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20210216034530-4410531fe030 h1:lP9pYkih3DUSC641giIXa2XqfTIbbbRr0w2EOTA7wHA=
golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@ -592,8 +595,6 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102 h1:42cLlJJdEh+ySyeUUbEQ5bsTiq8voBeTuweGVkY6Puw=
@ -617,8 +618,9 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5 h1:Lm4OryKCca1vehdsWogr9N4t7NfZxLbJoc/H0w4K4S4=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210126194326-f9ce19ea3013 h1:55H5j7lotzuFCEOKDsMch+fRNUQ9DgtyHOUP31FNqKc=
golang.org/x/oauth2 v0.0.0-20210126194326-f9ce19ea3013/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210113205817-d3ed898aa8a3/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210210192628-66670185b0cd h1:2arJsLyTCJGek+eeptQ3z49Rqndm0f+zvvpwNIXWNIA=
golang.org/x/oauth2 v0.0.0-20210210192628-66670185b0cd/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -672,10 +674,10 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f h1:+Nyd8tzPX9R7BWHguqsrbFdRx3WQ/1ib8I44HXV5yTA=
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=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -683,6 +685,8 @@ golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c h1:VwygUrnw9jn88c4u8GD3rZQbqrP/tgas88tPUbBxQrk=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65 h1:pTMjDVnP5eVRRlWO76rEWJ8JoC6Lf1CmyjPZXRiy2Sw=
golang.org/x/sys v0.0.0-20210216163648-f7da38b97c65/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -746,12 +750,7 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200828161849-5deb26317202/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c h1:AQsh/7arPVFDBraQa8x7GoVnwnGg1kM7J2ySI0kF5WU=
golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20200918232735-d647fc253266 h1:k7tVuG0g1JwmD3Jh8oAl1vQ1C3jb4Hi/dUl1wWDBJpQ=
golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@ -783,14 +782,13 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.31.0/go.mod h1:CL+9IBCa2WWU6gRuBWaKqGWLFFwbEUXkfeMkHLQWYWo=
google.golang.org/api v0.32.0 h1:Le77IccnTqEa8ryp9wIpX5W3zYm7Gf9LhOp9PHcwFts=
google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0 h1:l2Nfbl2GPXdWorv+dT2XfinX2jOOw4zv1VhLstx+6rE=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.38.0 h1:vDyWk6eup8eQAidaZ31sNWIn8tZEL8qpbtGkBD4ytQo=
google.golang.org/api v0.38.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/api v0.40.0 h1:uWrpz12dpVPn7cojP82mk02XDgTJLDPc2KbVTxrWb4A=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@ -832,17 +830,15 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200831141814-d751682dd103/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200921151605-7abf4a1a14d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506 h1:uLBY0yHDCj2PMQ98KWDSIDFwn9zK2zh+tgWtbvPPBjI=
google.golang.org/genproto v0.0.0-20210126160654-44e461bb6506/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210203152818-3206188e46ba/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210212180131-e7f2df4ecc2d h1:Edhcm0CKDPLQIecHCp5Iz57Lo7MfT6zUFBAlocmOjcY=
google.golang.org/genproto v0.0.0-20210212180131-e7f2df4ecc2d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
@ -862,8 +858,6 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0=
google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=

View file

@ -2,6 +2,7 @@ package encoder
type Encoder struct {
Audio Audio
Video Video
WithoutGame bool
}
@ -11,6 +12,17 @@ type Audio struct {
Frequency int
}
type Video struct {
Codec string
H264 struct {
Crf uint8
Preset string
Profile string
Tune string
LogLevel int
}
}
func (a *Audio) GetFrameDuration() int {
return a.Frequency * a.Frame / 1000 * a.Channels
}

View file

@ -6,3 +6,11 @@ const (
H264 VideoCodec = iota
VPX
)
func (v VideoCodec) String() string {
if v == H264 {
return "h264"
} else {
return "vpx"
}
}

View file

@ -0,0 +1,95 @@
package h264
import (
"bytes"
"log"
"runtime/debug"
"github.com/giongto35/cloud-game/v2/pkg/encoder"
"github.com/giongto35/cloud-game/v2/pkg/util"
)
// Encoder converts a yuvI420 image to h264 frame.
type Encoder struct {
Output chan encoder.OutFrame
Input chan encoder.InFrame
done chan struct{}
buf *bytes.Buffer
enc *H264
// C
width int
height int
fps int
}
// NewEncoder creates h264 encoder
func NewEncoder(width, height int, options ...Option) (encoder.Encoder, error) {
enc := &Encoder{
Output: make(chan encoder.OutFrame, 10),
Input: make(chan encoder.InFrame, 2),
done: make(chan struct{}),
buf: bytes.NewBuffer(make([]byte, 0)),
width: width,
height: height,
}
if err := enc.init(options...); err != nil {
return nil, err
}
return enc, nil
}
func (e *Encoder) init(options ...Option) error {
enc, err := NewH264Encoder(e.buf, e.width, e.height, options...)
if err != nil {
panic(err)
}
e.enc = enc
go e.startLooping()
return nil
}
func (e *Encoder) startLooping() {
defer func() {
if r := recover(); r != nil {
log.Println("Warn: Recovered panic in encoding ", r)
log.Println(debug.Stack())
}
}()
size := int(float32(e.width*e.height) * 1.5)
yuv := make([]byte, size, size)
for img := range e.Input {
util.RgbaToYuvInplace(img.Image, yuv, e.width, e.height)
err := e.enc.Encode(yuv)
if err != nil {
log.Println("err encoding ", img.Image, " using h264")
}
e.Output <- encoder.OutFrame{Data: e.buf.Bytes(), Timestamp: img.Timestamp}
e.buf.Reset()
}
close(e.Output)
close(e.done)
}
// Release release memory and stop loop
func (e *Encoder) release() {
close(e.Input)
<-e.done
err := e.enc.Close()
if err != nil {
log.Println("Failed to close H264 encoder")
}
}
func (e *Encoder) GetInputChan() chan encoder.InFrame { return e.Input }
func (e *Encoder) GetOutputChan() chan encoder.OutFrame { return e.Output }
func (e *Encoder) Stop() { e.release() }

View file

@ -0,0 +1,33 @@
package h264
type Options struct {
// Constant Rate Factor (CRF)
// This method allows the encoder to attempt to achieve a certain output quality for the whole file
// when output file size is of less importance.
// The range of the CRF scale is 051, where 0 is lossless, 23 is the default, and 51 is worst quality possible.
Crf uint8
// film, animation, grain, stillimage, psnr, ssim, fastdecode, zerolatency.
Tune string
// ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow, placebo.
Preset string
// baseline, main, high, high10, high422, high444.
Profile string
LogLevel int32
}
type Option func(*Options)
func WithOptions(arg Options) Option {
return func(args *Options) {
args.Crf = arg.Crf
args.Tune = arg.Tune
args.Preset = arg.Preset
args.Profile = arg.Profile
args.LogLevel = arg.LogLevel
}
}
func Crf(arg uint8) Option { return func(args *Options) { args.Crf = arg } }
func Tune(arg string) Option { return func(args *Options) { args.Tune = arg } }
func Preset(arg string) Option { return func(args *Options) { args.Preset = arg } }
func Profile(arg string) Option { return func(args *Options) { args.Profile = arg } }
func LogLevel(arg int32) Option { return func(args *Options) { args.LogLevel = arg } }

120
pkg/encoder/h264/x264.go Normal file
View file

@ -0,0 +1,120 @@
package h264
// #include <stdlib.h>
import "C"
import (
"fmt"
"io"
x264 "github.com/sergystepanov/x264-go/v2/x264c/external"
)
type H264 struct {
ref *x264.T
w io.Writer
width int32
lumaSize int32
chromaSize int32
csp int32
nnals int32
nals []*x264.Nal
// keep monotonic pts to suppress warnings
pts int64
}
func NewH264Encoder(w io.Writer, width, height int, options ...Option) (encoder *H264, err error) {
opts := &Options{
Crf: 12,
Tune: "zerolatency",
Preset: "superfast",
Profile: "baseline",
}
for _, opt := range options {
opt(opts)
}
param := x264.Param{}
if opts.Preset != "" && opts.Tune != "" {
if x264.ParamDefaultPreset(&param, opts.Preset, opts.Tune) < 0 {
return nil, fmt.Errorf("x264: invalid preset/tune name")
}
} else {
x264.ParamDefault(&param)
}
if opts.Profile != "" {
if x264.ParamApplyProfile(&param, opts.Profile) < 0 {
return nil, fmt.Errorf("x264: invalid profile name")
}
}
// legacy encoder lacks of this param
param.IBitdepth = 8
param.ICsp = x264.CspI420
param.IWidth = int32(width)
param.IHeight = int32(height)
param.ILogLevel = opts.LogLevel
param.Rc.IRcMethod = x264.RcCrf
param.Rc.FRfConstant = float32(opts.Crf)
encoder = &H264{
csp: param.ICsp,
lumaSize: int32(width * height),
chromaSize: int32(width*height) / 4,
nals: make([]*x264.Nal, 1),
w: w,
width: int32(width),
}
var picIn x264.Picture
x264.PictureInit(&picIn)
if encoder.ref = x264.EncoderOpen(&param); encoder.ref == nil {
err = fmt.Errorf("x264: cannot open the encoder")
return
}
if ret := x264.EncoderHeaders(encoder.ref, encoder.nals, &encoder.nnals); ret > 0 {
_, err = encoder.w.Write(C.GoBytes(encoder.nals[0].PPayload, C.int(ret)))
}
return
}
func (e *H264) Encode(yuv []byte) (err error) {
var picIn, picOut x264.Picture
picIn.Img.ICsp = e.csp
picIn.Img.IPlane = 3
picIn.Img.IStride[0] = e.width
picIn.Img.IStride[1] = e.width / 2
picIn.Img.IStride[2] = e.width / 2
picIn.Img.Plane[0] = C.CBytes(yuv[:e.lumaSize])
picIn.Img.Plane[1] = C.CBytes(yuv[e.lumaSize : e.lumaSize+e.chromaSize])
picIn.Img.Plane[2] = C.CBytes(yuv[e.lumaSize+e.chromaSize:])
picIn.IPts = e.pts
e.pts++
defer func() {
C.free(picIn.Img.Plane[0])
C.free(picIn.Img.Plane[1])
C.free(picIn.Img.Plane[2])
}()
if ret := x264.EncoderEncode(e.ref, e.nals, &e.nnals, &picIn, &picOut); ret > 0 {
_, err = e.w.Write(C.GoBytes(e.nals[0].PPayload, C.int(ret)))
// ret should be equal to writer writes
}
return
}
func (e *H264) Close() error {
x264.EncoderClose(e.ref)
return nil
}

View file

@ -1,115 +0,0 @@
package h264encoder
import (
"bytes"
"log"
"runtime/debug"
"github.com/gen2brain/x264-go"
"github.com/giongto35/cloud-game/v2/pkg/encoder"
)
const chanSize = 2
// H264Encoder yuvI420 image to vp8 video
type H264Encoder struct {
Output chan encoder.OutFrame
Input chan encoder.InFrame
done chan struct{}
buf *bytes.Buffer
enc *x264.Encoder
// C
width int
height int
fps int
}
// NewH264Encoder create h264 encoder
func NewH264Encoder(width, height, fps int) (encoder.Encoder, error) {
v := &H264Encoder{
Output: make(chan encoder.OutFrame, 5*chanSize),
Input: make(chan encoder.InFrame, chanSize),
done: make(chan struct{}),
buf: bytes.NewBuffer(make([]byte, 0)),
width: width,
height: height,
fps: fps,
}
if err := v.init(); err != nil {
return nil, err
}
return v, nil
}
func (v *H264Encoder) init() error {
opts := &x264.Options{
Width: v.width,
Height: v.height,
FrameRate: v.fps,
Tune: "zerolatency",
Preset: "veryfast",
Profile: "baseline",
//LogLevel: x264.LogDebug,
}
enc, err := x264.NewEncoder(v.buf, opts)
if err != nil {
panic(err)
}
v.enc = enc
go v.startLooping()
return nil
}
func (v *H264Encoder) startLooping() {
defer func() {
if r := recover(); r != nil {
log.Println("Warn: Recovered panic in encoding ", r)
log.Println(debug.Stack())
}
}()
for img := range v.Input {
err := v.enc.Encode(img.Image)
if err != nil {
log.Println("err encoding ", img.Image, " using h264")
}
v.Output <- encoder.OutFrame{Data: v.buf.Bytes(), Timestamp: img.Timestamp}
v.buf.Reset()
}
close(v.Output)
close(v.done)
}
// Release release memory and stop loop
func (v *H264Encoder) release() {
close(v.Input)
// Wait for loop to stop
<-v.done
log.Println("Releasing encoder")
err := v.enc.Close()
if err != nil {
log.Println("Failed to close H264 encoder")
}
}
// GetInputChan returns input channel
func (v *H264Encoder) GetInputChan() chan encoder.InFrame {
return v.Input
}
// GetInputChan returns output channel
func (v *H264Encoder) GetOutputChan() chan encoder.OutFrame {
return v.Output
}
// GetDoneChan returns done channel
func (v *H264Encoder) Stop() {
v.release()
}

View file

@ -1,4 +1,4 @@
package vpxencoder
package vpx
import (
"fmt"
@ -61,8 +61,8 @@ type VpxEncoder struct {
vpxCodexIter C.vpx_codec_iter_t
}
// NewVpxEncoder create vp8 encoder
func NewVpxEncoder(w, h, fps, bitrate, keyframe int) (encoder.Encoder, error) {
// NewEncoder create vp8 encoder
func NewEncoder(w, h, fps, bitrate, keyframe int) (encoder.Encoder, error) {
v := &VpxEncoder{
Output: make(chan encoder.OutFrame, 5*chanSize),
Input: make(chan encoder.InFrame, chanSize),
@ -161,9 +161,7 @@ func (v *VpxEncoder) startLooping() {
// Release release memory and stop loop
func (v *VpxEncoder) release() {
close(v.Input)
// Wait for loop to stop
<-v.done
log.Println("Releasing encoder")
C.vpx_img_free(&v.vpxImage)
C.vpx_codec_destroy(&v.vpxCodexCtx)
}

View file

@ -4,13 +4,13 @@ package util
import (
"image"
"unsafe"
"github.com/giongto35/cloud-game/v2/pkg/encoder"
)
// https://stackoverflow.com/questions/9465815/rgb-to-yuv420-algorithm-efficiency
/*
#cgo CFLAGS: -O3
void rgba2yuv(void * destination, void * source, int width, int height, int stride) {
const int image_size = width * height;
unsigned char * rgba = source;
@ -62,10 +62,3 @@ func RgbaToYuvInplace(rgba *image.RGBA, yuv []byte, width, height int) {
stride := rgba.Stride - width*4
C.rgba2yuv(unsafe.Pointer(&yuv[0]), unsafe.Pointer(&rgba.Pix[0]), C.int(width), C.int(height), C.int(stride))
}
// GetVideoEncoder returns video encoder based on some qualification.
// Actually Android is only supporting VP8 but H264 has better encoding performance
// TODO: Better use useragent attribute from frontend
func GetVideoEncoder(isMobile bool) encoder.VideoCodec {
return encoder.VPX
}

View file

@ -11,7 +11,6 @@ import (
webrtcConfig "github.com/giongto35/cloud-game/v2/pkg/config/webrtc"
"github.com/giongto35/cloud-game/v2/pkg/encoder"
"github.com/giongto35/cloud-game/v2/pkg/util"
itc "github.com/giongto35/cloud-game/v2/pkg/webrtc/interceptor"
"github.com/gofrs/uuid"
"github.com/pion/interceptor"
@ -127,7 +126,7 @@ func (w *WebRTC) StartClient(isMobile bool, iceCB OnIceCallback) (string, error)
// add video track
var codec webrtc.RTPCodecCapability
if util.GetVideoEncoder(isMobile) == encoder.H264 {
if w.cfg.Encoder.Video.Codec == encoder.H264.String() {
codec = webrtc.RTPCodecCapability{MimeType: "video/h264"}
} else {
codec = webrtc.RTPCodecCapability{MimeType: "video/vp8"}

View file

@ -10,7 +10,6 @@ import (
"github.com/giongto35/cloud-game/v2/pkg/config/worker"
"github.com/giongto35/cloud-game/v2/pkg/cws/api"
"github.com/giongto35/cloud-game/v2/pkg/emulator/libretro/manager/remotehttp"
"github.com/giongto35/cloud-game/v2/pkg/encoder"
"github.com/giongto35/cloud-game/v2/pkg/environment"
"github.com/giongto35/cloud-game/v2/pkg/games"
"github.com/giongto35/cloud-game/v2/pkg/webrtc"
@ -174,11 +173,11 @@ func (h *Handler) detachRoom(roomID string) {
// createNewRoom creates a new room
// Return nil in case of room is existed
func (h *Handler) createNewRoom(game games.GameMetadata, roomID string, videoCodec encoder.VideoCodec) *room.Room {
func (h *Handler) createNewRoom(game games.GameMetadata, roomID string) *room.Room {
// If the roomID doesn't have any running sessions (room was closed)
// we spawn a new room
if !h.isRoomBusy(roomID) {
newRoom := room.NewRoom(roomID, game, videoCodec, h.onlineStorage, h.cfg)
newRoom := room.NewRoom(roomID, game, h.onlineStorage, h.cfg)
// TODO: Might have race condition (and it has (:)
h.rooms[newRoom.ID] = newRoom
return newRoom

View file

@ -8,9 +8,7 @@ import (
webrtcConfig "github.com/giongto35/cloud-game/v2/pkg/config/webrtc"
"github.com/giongto35/cloud-game/v2/pkg/cws"
"github.com/giongto35/cloud-game/v2/pkg/cws/api"
"github.com/giongto35/cloud-game/v2/pkg/encoder"
"github.com/giongto35/cloud-game/v2/pkg/games"
"github.com/giongto35/cloud-game/v2/pkg/util"
"github.com/giongto35/cloud-game/v2/pkg/webrtc"
"github.com/giongto35/cloud-game/v2/pkg/worker/room"
)
@ -144,7 +142,7 @@ func (h *Handler) handleGameStart() cws.PacketHandler {
Path: startPacket.Path,
}
room := h.startGameHandler(gameMeta, resp.RoomID, resp.PlayerIndex, peerconnection, util.GetVideoEncoder(false))
room := h.startGameHandler(gameMeta, resp.RoomID, resp.PlayerIndex, peerconnection)
session.RoomID = room.ID
// TODO: can data race
h.rooms[room.ID] = room
@ -259,7 +257,7 @@ func (h *Handler) handleGameMultitap() cws.PacketHandler {
}
// startGameHandler starts a game if roomID is given, if not create new room
func (h *Handler) startGameHandler(game games.GameMetadata, existedRoomID string, playerIndex int, peerconnection *webrtc.WebRTC, videoCodec encoder.VideoCodec) *room.Room {
func (h *Handler) startGameHandler(game games.GameMetadata, existedRoomID string, playerIndex int, peerconnection *webrtc.WebRTC) *room.Room {
log.Printf("Loading game: %v\n", game.Name)
// If we are connecting to coordinator, request corresponding serverID based on roomID
// TODO: check if existedRoomID is in the current server
@ -268,7 +266,7 @@ func (h *Handler) startGameHandler(game games.GameMetadata, existedRoomID string
if room == nil {
log.Println("Got Room from local ", room, " ID: ", existedRoomID)
// Create new room and update player index
room = h.createNewRoom(game, existedRoomID, videoCodec)
room = h.createNewRoom(game, existedRoomID)
room.UpdatePlayerIndex(peerconnection, playerIndex)
// Wait for done signal from room

View file

@ -6,9 +6,9 @@ import (
encoderConfig "github.com/giongto35/cloud-game/v2/pkg/config/encoder"
"github.com/giongto35/cloud-game/v2/pkg/encoder"
"github.com/giongto35/cloud-game/v2/pkg/encoder/h264encoder"
"github.com/giongto35/cloud-game/v2/pkg/encoder/h264"
"github.com/giongto35/cloud-game/v2/pkg/encoder/opus"
vpxencoder "github.com/giongto35/cloud-game/v2/pkg/encoder/vpx-encoder"
"github.com/giongto35/cloud-game/v2/pkg/encoder/vpx"
"github.com/giongto35/cloud-game/v2/pkg/webrtc"
)
@ -68,15 +68,21 @@ func (r *Room) broadcastAudio(audio []byte) {
}
// startVideo processes imageChannel images with an encoder (codec) then pushes the result to WebRTC.
func (r *Room) startVideo(width, height int, videoCodec encoder.VideoCodec) {
func (r *Room) startVideo(width, height int, video encoderConfig.Video) {
var enc encoder.Encoder
var err error
log.Println("Video Encoder: ", videoCodec)
if videoCodec == encoder.H264 {
enc, err = h264encoder.NewH264Encoder(width, height, 1)
log.Println("Video codec:", video.Codec)
if video.Codec == encoder.H264.String() {
enc, err = h264.NewEncoder(width, height, h264.WithOptions(h264.Options{
Crf: video.H264.Crf,
Tune: video.H264.Tune,
Preset: video.H264.Preset,
Profile: video.H264.Profile,
LogLevel: int32(video.H264.LogLevel),
}))
} else {
enc, err = vpxencoder.NewVpxEncoder(width, height, 20, 1200, 5)
enc, err = vpx.NewEncoder(width, height, 20, 1200, 5)
}
defer func() {

View file

@ -0,0 +1,57 @@
package room
import (
"image"
col "image/color"
"math/rand"
"testing"
"github.com/giongto35/cloud-game/v2/pkg/encoder"
"github.com/giongto35/cloud-game/v2/pkg/encoder/h264"
"github.com/giongto35/cloud-game/v2/pkg/encoder/vpx"
)
func BenchmarkH264(b *testing.B) {
benchmarkEncoder(1920, 1080, encoder.H264, b)
}
func BenchmarkVP8(b *testing.B) {
benchmarkEncoder(1920, 1080, encoder.VPX, b)
}
func benchmarkEncoder(w, h int, codec encoder.VideoCodec, b *testing.B) {
var enc encoder.Encoder
if codec == encoder.H264 {
enc, _ = h264.NewEncoder(w, h)
} else {
enc, _ = vpx.NewEncoder(w, h, 20, 1200, 5)
}
defer enc.Stop()
in, out := enc.GetInputChan(), enc.GetOutputChan()
image1 := genTestImage(w, h, rand.New(rand.NewSource(int64(1))).Float32())
image2 := genTestImage(w, h, rand.New(rand.NewSource(int64(2))).Float32())
for i := 0; i < b.N; i++ {
im := image1
if i%2 == 0 {
im = image2
}
in <- encoder.InFrame{Image: im}
<-out
}
}
func genTestImage(w, h int, seed float32) *image.RGBA {
img := image.NewRGBA(image.Rectangle{Max: image.Point{X: w, Y: h}})
for x := 0; x < w; x++ {
for y := 0; y < h; y++ {
var color col.Color
color = col.RGBA{R: uint8(seed * 255), G: uint8(seed * 255), B: uint8(seed * 255), A: 0xff}
img.Set(x, y, color)
}
}
return img
}

View file

@ -123,7 +123,7 @@ func NewVideoImporter(roomID string) chan nanoarch.GameFrame {
}
// NewRoom creates a new room
func NewRoom(roomID string, game games.GameMetadata, videoCodec encoder.VideoCodec, onlineStorage *storage.Client, cfg worker.Config) *Room {
func NewRoom(roomID string, game games.GameMetadata, onlineStorage *storage.Client, cfg worker.Config) *Room {
if roomID == "" {
roomID = generateRoomID(game.Name)
}
@ -211,7 +211,7 @@ func NewRoom(roomID string, game games.GameMetadata, videoCodec encoder.VideoCod
room.director.SetViewport(encoderW, encoderH)
// Spawn video and audio encoding for webRTC
go room.startVideo(encoderW, encoderH, videoCodec)
go room.startVideo(encoderW, encoderH, cfg.Encoder.Video)
go room.startAudio(gameMeta.AudioSampleRate, cfg.Encoder.Audio)
go room.startVoice()
room.director.Start()

View file

@ -235,8 +235,9 @@ func getRoomMock(cfg roomMockConfig) roomMock {
if err := coreManager.Sync(); err != nil {
log.Printf("error: cores sync has failed, %v", err)
}
conf.Encoder.Video.Codec = cfg.codec.String()
room := NewRoom(cfg.roomName, cfg.game, cfg.codec, storage.NewInitClient(), conf)
room := NewRoom(cfg.roomName, cfg.game, storage.NewInitClient(), conf)
// loop-wait the room initialization
var init sync.WaitGroup