gStreamer/Pluginのビルド手順(Ubuntu, Windows)

# モチベーション

gStreamerはaptやインストーラーで入れると入らないパッケージがある。
ライセンス関係で入らないものや動作環境が限られるものがある。
今回は以下のpluginを利用するために自分でビルドする。

- OpenH264
    - Cisco製のH264エンコーダ
    - これはライセンスの関係でaptでは入らない
    - Windowsのインストーラーでは入ったが理由は調べていない
- NVCodec
    - NVENCを利用している動画のエンコーダ・デコーダ
        - H264, H265用
    - 動作環境が限られるのでaptでは入らないようだ

# 対象環境

Ubuntu19.10とWindows10

その他の環境でも方法は同じだがコンパイラの設定等の調整は多分必要。

# gStreamerのPluginについて

個別のplugin環境はgitで公開されているので、それぞれ個別にビルドすることは可能。pluginは3種類あって、https://github.com/GStreamer/gst-plugins-bad/blob/master/README にかなり文学的だが説明があり、概ね以下の通り。

- [gst-plugins-good]
    - 公式が利用を推奨するplugin
    - コードの品質がいい
    - ドキュメントまでちゃんとそろっている
    - ライセンスもLGPLである
- [gst-plugins-ugly]
    - コードの品質はgoodと同じくらいいいらしい
        - uglyというのはコードがきったねぇという意味ではない
    - ただしライセンスで縛られていて完全にフリーなコードではないので、利用に際しては注意が必要
        - 特に配布は困難
        - 特許が取られているものもある
    - そういうところが気に入らないのでuglyって名前にしている
- [gst-plugins-bad]
    - 見た目上うまく動いているかもしれんけど無保証
    - 何かが欠けていてgoodになれないものたち
        - 品質
        - テスト
        - レビュー
        - ドキュメント
    - これら全てが解決された段階でgoodかuglyに昇格する
    - フリーソフトウェアなので自分でパッチを当てろ
    - パッチを当ててうまく行ったらプルリクしてくれ。頼むぞ
    - gStreamerのコントリビューターになりたい場合、まずここから始めることができる

ということらしい。

# 具体的なビルド手順

これらのpluginをビルドしようとすると、autotoolsで依存ライブラリを揃えることになる。Ubuntuだと割とサックリできたが、後述のWindowsなんかだと色々面倒だった。gStreamerの公式のビルド環境として[Cerbero](https://github.com/GStreamer/cerbero)があり、これを利用するのが一番ラクなのでこの記事はCerberoについて記載。

## Ubuntu 19.10

### 既存のgStreamerとのバージョン合わせ

そもそもgStreamerが標準で入っているので、gStreamer自体のビルドは不要で、pluginの格納ディレクトリにビルドしたpluginを入れてやるだけで良い。但し本体とpluginのバージョンを合わせないといけない。

標準で入っているバージョンは1.16.1(2020/3/2時点)で、cerberoでmasterブランチから取ってくるコードのバージョンは1.17.0.1。マイナーバージョンまで合わせてやる必要があるので、Ubuntuに元々入っているgStreamerを利用する場合は1.16系をビルドすることになる。

古いバージョンのビルド方法についてちょっとググると、cerberoのバージョンを戻すと古いものがビルドされるという情報がいっぱい出てくる(http://mil-o.jp/yb/gstreamer-cerbero/)。

```shell-session
$git clone -b 1.14 https://github.com/GStreamer/cerbero
```

が、これではうまく行かなかった。ビルドスクリプトそのものが古いため、pythonの想定バージョンが2系だったりして、最新の環境ではうまくいかない。
メンテされていない古いビルドスクリプトを動かすよりも、新しいビルドスクリプトで古いものをビルドしたほうが良いに決まっているので、やり方を探す。

cerberoのビルド設定はrecipesとして管理されている。この中の[recipes/custom.py](https://github.com/GStreamer/cerbero/blob/master/recipes/custom.py#L14)の14行目が想定するgStreamerのバージョンなのでここを修正する。パッチ番号まで指定されているが、実際に1.16.0と指定しても1.16.2がビルドされたので、パッチ番号は自動的に最新のものを取ってくるようだ(強制的に1.16.0をビルドさせる方法は必要がなかったので調べていない)。

### nvcodecとopenh264のビルドのための環境構築

標準の状態でビルドしてもaptで入るものしかビルドされず、nvcodecとopenh264はビルドされない。ビルドするためにはまず環境の構築とvariantの設定が必要。

#### 依存ライブラリ入れ

- openh264

openh264はgithubのリポジトリから取ってきて入れる。但しcerberoがlibopenh264.so.4を決め打ちで読みに行っているので、シンボリックリンクを貼ってやる必要がある(↓の一番最後の行)

```shell-session
$git clone https://github.com/cisco/openh264
$cd openh264
$make && sudo make install
$sudo ln -s /usr/local/lib/libopenh264.so.2.1.0 /usr/lib/libopenh264.so.4 
```

- nvcodec

NVIDIAのGPUとCUDAが必要。GPUが入っているマシンを準備してUbuntuを入れる。

ドライバとcudaのtoolkitはaptで入る。

```shell-session
$sudo apt install nvidia-driver-435 nvidia-cuda-toolkit 
```

がダメで、Cerberoがcudaは10.1を要求してくるので10.1をNVIDIAのサイトからダウンロードしてきて入れる。
https://developer.nvidia.com/cuda-10.1-download-archive-base

一番新しい18.10のrunfile(local)をダウンロードする。NVIDIAのサイトには

```
Installation Instructions:
Run `sudo sh cuda_10.1.105_418.39_linux.run`
Follow the command-line prompts
```
と書いてあるが、Ubuntu19.10だとこれでは入らない。cudaのインストーラーがgcc8系を要求するのに対し、Ubuntu19.10はgcc9系しか入ってないので途中で止まってしまう。ググると、[古いgccを入れたら良いじゃん](https://wapxmas.github.io/how-to-install-cuda-on-ubuntu-19-10/)という記載が見つかったが、そんなことはしたくないのでもう少し探したところ、--overrideというオプションを付けるとgcc9系でもビルドできるらしい。

```shell-session
$chmod +x cuda_10.1.105_418.39_linux.run
$sudo ./cuda_10.1.105_418.39_linux.run --override
```

でインストール完了。ドライバを入れたのでLinuxを再起動する。

#### コンパイラのオプション

ビルドスクリプトの内部でconfigureする際にオプションを指定しておかないと、必要ないものとしてskipされてしまう。
ビルドスクリプトの動かし方を調べると、オプションはvariantとして指定できる。

```shell-session
$./cerbero-uninstalled                         
usage: cerbero-uninstalled [-h] [-t] [--list-variants] [-v VARIANTS]
                           [-c CONFIG] [-m MANIFEST]
                           [--self-update SELF_UPDATE]
                           {genvsprops,fetch,fetch-package,fetch-cache,bootstrap,fetch-bootstrap,deps,buildone,build-deps,build,gensdkshell,rdeps,wipe,bundle-source,package,add-package,graph,genlibfiles,packageinfo,list,list-packages,show-config,debug-packages,tag,run,checkpackage,shell,add-recipe,edit-cache,genxcconfig,check}
                           ...

Build and package a set of modules to distribute them in a SDK

positional arguments:
  {genvsprops,fetch,fetch-package,fetch-cache,bootstrap,fetch-bootstrap,deps,buildone,build-deps,build,gensdkshell,rdeps,wipe,bundle-source,package,add-package,graph,genlibfiles,packageinfo,list,list-packages,show-config,debug-packages,tag,run,checkpackage,shell,add-recipe,edit-cache,genxcconfig,check}
                        sub-command help
    genvsprops          Generate Visual Studio property sheets to use the SDK
                        from VS
    fetch               Fetch the recipes sources
    fetch-package       Fetch the recipes sources from a package
    fetch-cache         Fetch a cached build from GitLab CI based on cerbero
                        git revision.
    bootstrap           Bootstrap the build system installing all the
                        dependencies
    fetch-bootstrap     Fetch the sources required for bootstrap
    deps                List the dependencies of a recipe
    buildone            Build or rebuild a single recipe without its
                        dependencies
    build-deps          Build only the dependencies of the specified recipes
    build               Build a recipe
    gensdkshell         Create a script with the shell environment for the SDK
    rdeps               List the reverse dependencies of a recipe
    wipe                Wipes everything to restore the build system
    bundle-source       Bundle Source code of recipes and Cerbero
    package             Creates a distribution package
    add-package         Adds a new package
    graph               Create a graph of dependencies using dot from graphviz
    genlibfiles         Generate MSVC compatible library files (.lib)
    packageinfo         Print information about this package
    list                List all the available recipes
    list-packages       List all the available packages
    show-config         Show configuration settings
    debug-packages      Outputs debug information about package, like
                        duplicates files or files that do not belong to any
                        package
    tag                 Tag a git recipe or all git recipes using their
                        sdk-$version branch
    run                 Runs a command in the cerbero shell
    checkpackage        Run checks on a given package
    shell               Starts a shell with the build environment
    add-recipe          Adds a new recipe
    edit-cache          View and edit the local cache
    genxcconfig         Generate XCode config files to use the SDK from VS
    check               Run checks on a given recipe

optional arguments:
  -h, --help            show this help message and exit
  -t, --timestamps      Print timestamps with every message printed
  --list-variants       List available variants
  -v VARIANTS, --variants VARIANTS
                        Variants to be used for the build
  -c CONFIG, --config CONFIG
                        Configuration file used for the build
  -m MANIFEST, --manifest MANIFEST
                        Manifest file used to fixate git revisions
  --self-update SELF_UPDATE
                        Update cerbero git repository from manifest and exit.
```

variantの一覧は以下

```shell-session
$ ./cerbero-uninstalled --list         
Loading default configuration from /home/nakakura/.cerbero/cerbero.cbc
Available variants are: alsa, cdparanoia, debug, gi, intelmsdk, nvcodec, pulse, python, qt5, rpi, testspackage, unwind, v4l2, vaapi, visualstudio, werror, x11
```

デフォルトでonになっているものもあるので、必要なものだけ追加する。今回はnvcodecをつける必要がある。

```
$ ./cerbero-uninstalled bootstrap
$ ./cerbero-uninstalled build gstreamer-1.0
$ ./cerbero-uninstalled -v vnvcodec build gst-plugins-bad-1.0
```

これでgst-plugins-bad-1.0がビルドされる。ちなみにこれは設定ファイルを書いておくと並列でビルドすることができて高速化できる。設定ファイルは~/.cerbero/cerbero.cbc

```
$ cat ~/.cerbero/cerbero.cbc 
allow_parallel_build=True
num_of_cpus=8
```

なお、一旦ビルドしてしまってからオプションを変えて再度ビルドしようとした場合、wipeを実行しないと反映されないので注意(はまった)

### ビルドしたライブラリのインストール

pluginのディレクトリにファイルをコピーするだけ。pluginのディレクトリはgst-inspect-1.0で適当なライブラリを表示してやると書いてある。

```shell-session
$gst-inspect-1.0 autovideosink
...中略...
  Filename                 /usr/lib/x86_64-linux-gnu/gstreamer-1.0/libgstautodetect.so
...略...
```

/usr/lib/x86_64-linux-gnu/gstreamer-1.0/に入れればいいことがわかったので、入れてやる。ビルドしたバイナリはbuild/dist/linux_x86_64/の中にある。

```shell-session
$ ls build/dist/linux_x86_64/                
bin/  etc/  include/  lib/  libexec/  man/  share/  var/
```
binの中にはgst-launch等々の実行ファイルがあり、pluginはlibの中にある。

```shell-session
$cd build/dist/linux_x86_64/lib/gstreamer-1.0
$sudo cp libgstnv* /usr/lib/x86_64-linux-gnu/gstreamer-1.0/
$sudo cp libgstopenh264* /usr/lib/x86_64-linux-gnu/gstreamer-1.0/
```

これで無事に入ったはずなので確認する。

```shell-session
$ gst-inspect-1.0 openh264enc
Factory Details:
  Rank                     marginal (64)
  Long-name                OpenH264 video encoder
...
$gst-inspect-1.0 nvh264enc
Factory Details:
  Rank                     primary + 256 (512)
  Long-name                NVENC H.264 Video Encoder
...
```

Ok

### ハマりポイント

- cudaは10.1が必要(10.2じゃだめ)
- openh264encはlibopenh264.so.4が/usr/libにある必要がある
- variantを設定する前に一旦ビルドしていると、wipeをかけるかcerberoごと入れ直さないとビルドに反映されない

## Windows10

Windows10については、こちらの記事が神がかっているのでほぼそのまま手順に従えばいい。
https://qiita.com/maruo2/items/387e2322f72bf61c8e02

Windows環境に関してはまっさらからの構築なので、1.17.0.1をそのままビルドした。
今回試して追加でわかったのは以下

- Visual Studio2019のcommunity版でもビルドできた
- variantでvisualstudioを指定してやるとVisual Studioでビルドされ、指定しないとMSYSでビルドされる
    - MSYSは32bit環境だがちゃんと64bitのものがビルドされる
    - MSYSの方は(流れていったのでちゃんと読めなかったが)警告がたくさん表示されたので、何かしら怪しいかもしれない
   
# その他気づいたこと

- Ubuntuの1.16はバイナリがnvenc, nvdecに別れたが、Windowsの1.17はバイナリがnvcodecに統一されていた
    - 1.14を見るとソースコードもnvenc, nvdecに分かれているが、1.16はソースコードレベルではnvcodecというディレクトリだったので、統一される方向っぽい
    - Ubuntuのnvencはnvh264enc, nvh265encに内部的に別れているが、nvdecはnvdecとしてかなり使いづらいインタフェースだった
        - 1.17はnvh264dec, nvh265decと別れているので、1.16のnvcodecは変更中の過渡期っぽい
        
ということ。