ロボットやラズパイのカメラ映像をインターネット越しに安全にブラウザで視聴する方法（3/3）

1. [ロボットやラズパイのカメラ映像をインターネット越しに安全にブラウザで視聴する方法（1/3）](https://qiita.com/nmatsui/items/d1f7ceaa84cf805ee636) <br/> 概要編
2. [ロボットやラズパイのカメラ映像をインターネット越しに安全にブラウザで視聴する方法（2/3）](https://qiita.com/nmatsui/items/8bc9dc224fd9d288c56c) <br/> クラウド側環境構築編
3. [ロボットやラズパイのカメラ映像をインターネット越しに安全にブラウザで視聴する方法（3/3）](https://qiita.com/nmatsui/items/30adbeebd15b4c3de0ea) <br/> **ロボット側環境構築編** <-- 今回

# はじめに
前回は、VPNやWebRTCを使ったインターネット越しにカメラ映像を視聴するシステムのクラウド側環境をAzure上に構築しました。今回は、そのクラウド側環境へ接続するロボット側環境を構築し、実際にロボットのカメラ映像をインターネット越しにブラウザから視聴したいと思います。

# 各種バージョン
* クライアント側

||バージョン|
|:--|:--|
|OS|macOS Sierra 10.12.6|
|azure-cli|2.0.61|
|kubectl|1.14.0|

* Raspberry Pi （VPN Client）

||バージョン|
|:--|:--|
|モデル(Revision)|Raspberry Pi 3 Model B+ (a020d3)|
|OS|Raspbian Stretch (raspberrypi 4.14.98-v7+)|
|strongswan|5.7.2|

* 自律移動ロボット（[husarion ROSBot2](https://husarion.com/manuals/rosbot-manual/#rosbot-manual-overview)）

||バージョン|
|:--|:--|
|モデル|husarion ROSBot2.0|
|OS|Ubuntu 16.04.6 LTS (4.4.71husarion5)|
|ROS|Kinetic|


# 環境変数の設定
前回と同じ環境変数を定義しておきます（前回から引き続き同じTERMINALで作業をしている場合は、 `PASS_PHRASE` だけ追加で設定してください）。
## 個別に変更する必要がある環境変数
```bash:TERMINAL1
kurento-robot-camera-k8s$ export DOMAIN="example.com"
```
## 利用するAzureリージョン
```bash:TERMINAL1
kurento-robot-camera-k8s$ export REGION="japaneast"
```
## Azureのリソース名
```bash:TERMINAL1
kurento-robot-camera-k8s$ export DNS_ZONE_RG="dns-zone"
kurento-robot-camera-k8s$ export SYSTEM_RG="kurento"
kurento-robot-camera-k8s$ export GW_NAME="kurentogw"
```
## 自動生成するユーザー名やパスワード
```bash:TERMINAL1
kurento-robot-camera-k8s$ export PASS_PHRASE="$(cat /dev/urandom | \
  LC_CTYPE=C tr -dc 'a-zA-Z0-9' | head -c 16)"; echo ${PASS_PHRASE}
```

# Azureにログイン
`az` コマンドを用いてAzureにログインします。会社でAzureを利用している場合など、 "規定のディレクトリ" 以外にテナントが存在する場合は、適切なテナントを指定してください。

```bash:TERMINAL1_規定のディレクトリにログイン
kurento-robot-camera-k8s$ az login
```
```bash:TERMINAL1_テナントを指定してログイン（テナント名を適切に変更）
kurento-robot-camera-k8s$ export TENANT="example.onmicrosoft.com"
kurento-robot-camera-k8s$ az login --tenant ${TENANT}
```

# Raspberry Pi3にSSH
Raspberry Pi3をモバイルWiFiに接続し、SSHします。今回はPrivate IPとして `192.168.128.107` が割り振られました。

```bash:RaspberryPi3
pi@raspberrypi:~ $ ip addr show dev wlan0
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether b8:27:eb:73:7a:85 brd ff:ff:ff:ff:ff:ff
    inet 192.168.128.107/24 brd 192.168.128.255 scope global wlan0
       valid_lft forever preferred_lft forever
    inet6 fe80::6290:e9e8:3ec9:f8ac/64 scope link
       valid_lft forever preferred_lft forever
```

# Raspberry Piにstrongswanをインストール
Azure VPN GatewayとIPSec VPN (IKEv2)でVPN接続するために、Raspberry Pi 3 Model B+へVPN Clientツールである[strongswan](https://www.strongswan.org/)をインストールします。

今回利用している[Raspbian Stretch](https://www.raspberrypi.org/downloads/raspbian/)では、aptパッケージでstrongswanが提供されていますが、このprebuiltされたバイナリではAzure VPN Gatewayに接続できません。そのためソースコードから自力ビルドします。
> Azure VPN Gatewayに接続するためには、鍵交換情報をhttpsのURLから取得する必要があるのですが、debian系のstrongswan prebuilt パッケージは[curl pluginがデフォルトでdisable](https://wiki.strongswan.org/projects/strongswan/wiki/Curl)になっているためです。

## strongswanビルド用の環境変数を設定
IPSec VPN用のユーザー名とパスワードを環境変数としてRaspberry Pi3に定義しておきます。 `PASSWORD` は、適切に変更してください。

### 個別に変更する必要がある環境変数
```bash:RaspberryPi3
pi@raspberrypi:~ $ export USERNAME="client"
pi@raspberrypi:~ $ export PASSWORD="change_your_own_password"
```

## 関連するライブラリのインストール
```bash:RaspberryPi3
pi@raspberrypi:~ $ sudo apt install -y \
  build-essential \
  libgmp-dev \
  libldap2-dev \
  libcurl4-openssl-dev \
  libldns-dev \
  libunbound-dev \
  libsoup2.4-dev \
  libgcrypt11-dev \
  libpcsclite-dev \
  libpam0g-dev \
  libcap-dev
```
## strongswanのソースコード取得
```bash:RaspberryPi3
pi@raspberrypi:~ $ wget https://download.strongswan.org/strongswan-5.7.2.tar.bz2
pi@raspberrypi:~ $ tar xjf strongswan-5.7.2.tar.bz2
pi@raspberrypi:~ $ cd strongswan-5.7.2/
```
## configure & build
VPN Clientとして動作させるだけならば不要なオプションを指定している気もしますが、とりあえず動いたので今回はこれで。

```bash:RaspberryPi3
pi@raspberrypi:~/strongswan-5.7.2 $ ./configure \
--disable-static \
--prefix=/usr \
--exec-prefix=/usr \
--sysconfdir=/etc \
--localstatedir=/var \
--libdir=/usr/lib \
--libexecdir=/usr/lib \
--enable-addrblock \
--enable-agent \
--enable-attr-sql \
--enable-ccm \
--enable-certexpire \
--enable-cmd \
--enable-coupling \
--enable-ctr \
--enable-curl \
--enable-dhcp \
--enable-dnscert \
--enable-duplicheck \
--enable-eap-aka \
--enable-eap-aka-3gpp2 \
--enable-eap-dynamic \
--enable-eap-gtc \
--enable-eap-identity \
--enable-eap-md5 \
--enable-eap-mschapv2 \
--enable-eap-peap \
--enable-eap-radius \
--enable-eap-sim \
--enable-eap-sim-file \
--enable-eap-sim-pcsc \
--enable-eap-simaka-pseudonym \
--enable-eap-simaka-reauth \
--enable-eap-simaka-sql \
--enable-eap-tls \
--enable-eap-tnc \
--enable-eap-ttls \
--enable-error-notify \
--enable-farp \
--enable-gcm \
--enable-gcrypt \
--enable-imc-attestation \
--enable-imc-os \
--enable-imc-scanner \
--enable-imc-test \
--enable-imv-attestation \
--enable-imv-os \
--enable-imv-scanner \
--enable-imv-test \
--enable-integrity-test \
--enable-ipseckey \
--enable-ldap \
--enable-led \
--enable-load-tester \
--enable-lookip \
--enable-md4 \
--enable-ntru \
--enable-openssl \
--enable-pkcs11 \
--enable-radattr \
--enable-soup \
--enable-systime-fix \
--enable-test-vectors \
--enable-tnccs-11 \
--enable-tnccs-20 \
--enable-tnccs-dynamic \
--enable-tnc-ifmap \
--enable-tnc-imc \
--enable-tnc-imv \
--enable-tnc-pdp \
--enable-unbound \
--enable-unity \
--enable-whitelist \
--enable-xauth-eap \
--enable-xauth-generic \
--enable-xauth-noauth \
--enable-xauth-pam \
--disable-blowfish \
--disable-des \
--with-capabilities=libcap
```
```bash:RaspberryPi3
pi@raspberrypi:~/strongswan-5.7.2 $ make
pi@raspberrypi:~/strongswan-5.7.2 $ sudo make install
```
strongswanのフルビルドには、かなり時間がかかります。
（手元の環境では `make` に70分、 `make install` に5分ほどかかりました。）

# IPSec VPN用のルート証明書の作成と登録
Azure VPN GatewayにPoint-to-Siteで接続してくるクライアントを認証するために、X.509の自己署名ルート証明書とクライアント証明書を生成します。本来は、ルート証明書は専用の認証局から発行してもらうべきですが、今回はオレオレ証明書を使います。

## Raspberry Pi3でCA証明書のキーと証明書を生成
```bash:RaspberryPi3
pi@raspberrypi:~ $ mkdir secrets
```
```bash:RaspberryPi3
pi@raspberrypi:~ $ ipsec pki \
  --gen \
  --outform pem \
  > secrets/caKey.pem
```
```bash:RaspberryPi3
pi@raspberrypi:~ $ ipsec pki \
  --self \
  --in secrets/caKey.pem \
  --dn "CN=VPN CA" \
  --ca --outform pem \
  > secrets/caCert.pem
```
## Raspberry Pi3でCA証明書をbase64形式で表示
Raspberry Pi3で下記コマンドを実行すると、base64でエンコードされたCA証明書が表示されます。

```bash:RaspberryPi3
pi@raspberrypi:~ $ openssl x509 \
  -in secrets/caCert.pem \
  -outform der \
  | base64 -w0 ; echo
```

## PCからRaspberry Pi3のCA証明書をAzure VPN Gatewayに登録
上記手順で表示されたCA証明書を、 **MacOS側のTerminalから** `az` コマンドを用いてAzure VPN Gatewayに登録します。 `PUBLIC_CERT` は適切に置き換えてください。

```bash:TERMINAL1
kurento-robot-camera-k8s$ export PUBLIC_CERT="上記の手順で表示された、RaspberryPi3のCA証明書"
```
```bash:TERMINAL1
kurento-robot-camera-k8s$ az network vnet-gateway root-cert create \
  --name RaspberryPi3 \
  --resource-group ${SYSTEM_RG} \
  --gateway-name ${GW_NAME} \
  --public-cert-data ${PUBLIC_CERT}
```

# IPSec VPN用のクライアント証明書の作成とstrongswanの設定
Azure VPN GatewayにPoint-to-Siteで接続するために、生成した自己署名CA証明書を用いてクライアント証明書を作成します。またAzureより取得した情報を元に、strongswanの設定を行います。

## Raspberry Pi3でクライアント証明書を生成
```bash:RaspberryPi3
pi@raspberrypi:~ $ ipsec pki \
  --gen \
  --outform pem \
  > "secrets/${USERNAME}Key.pem"
```
```bash:RaspberryPi3
pi@raspberrypi:~ $ ipsec pki \
  --pub \
  --in "secrets/${USERNAME}Key.pem" \
  | ipsec pki \
  --issue \
  --cacert secrets/caCert.pem \
  --cakey secrets/caKey.pem \
  --dn "CN=${USERNAME}" \
  --san "${USERNAME}" \
  --flag clientAuth \
  --outform pem \
  > "secrets/${USERNAME}Cert.pem"
```
```bash:RaspberryPi3
pi@raspberrypi:~ $ openssl pkcs12 \
  -in "secrets/${USERNAME}Cert.pem" \
  -inkey "secrets/${USERNAME}Key.pem" \
  -certfile secrets/caCert.pem \
  -export \
  -out "secrets/${USERNAME}.p12" \
  -password "pass:${PASSWORD}"
```
## PCからAzure VPN ClientパッケージのURLを取得
**MacOS側のTerminal** から `az` コマンドを用いて、VPN ClientパッケージのURLを取得します。

```bash:TERMINAL1
kurento-robot-camera-k8s$ az network vnet-gateway vpn-client show-url \
  --name ${GW_NAME} \
  --resource-group ${SYSTEM_RG}
```
> エラーが発生する場合、一度 `az network vnet-gateway vpn-client generate --name ${GW_NAME} --resource-group ${SYSTEM_RG}` する必要があるかもしれません。

## Raspberry Pi3でVPN Clientパッケージを取得
上記のコマンドで表示されたURLを用いて、VPN Clientパッケージを取得します。

```bash:RaspberryPi3
pi@raspberrypi:~ $ wget -O vpnclient.zip "https://xxxxxxxxxxxxx.blob.core.windows.net/..."
pi@raspberrypi:~ $ unzip vpnclient.zip Generic/* -d secrets
```
## `VpnServerRoot.cer` をstrongswanのディレクトリにコピー
```bash:RaspberryPi3
pi@raspberrypi:~ $ sudo cp secrets/Generic/VpnServerRoot.cer /etc/ipsec.d/cacerts/
```
## クライアント証明書をstrongswanのディレクトリにコピー
```bash:RaspberryPi3
pi@raspberrypi:~ $ sudo cp secrets/client.p12 /etc/ipsec.d/private/
```
## Azure VPN Gatewayのエンドポイントを取得する
```bash:RaspberryPi3
pi@raspberrypi:~ $ export VPN_SERVER=$(grep -oP '(?<=VpnServer>)[^<]+' \
  secrets/Generic/VpnSettings.xml | tr -d '\r\n'); \
  echo ${VPN_SERVER}
```
## strongswanの設定ファイルに追記
```bash:RaspberryPi3
pi@raspberrypi:~ $ cat << __EOD__ | sudo tee -a /etc/ipsec.conf
conn azure
    keyexchange=ikev2
    type=tunnel
    leftfirewall=yes
    left=%any
    leftauth=eap-tls
    leftid=%client
    right=${VPN_SERVER}
    rightid=%${VPN_SERVER}
    rightsubnet=0.0.0.0/0
    leftsourceip=%config
    auto=add
__EOD__
```
```bash:RaspberryPi3
pi@raspberrypi:~ $ cat << __EOD__ | sudo tee -a /etc/ipsec.secrets
: P12 client.p12 '${PASSWORD}'
__EOD__
```
## パケット転送を許可
```bash:RaspberryPi3
pi@raspberrypi:~ $ echo "net.ipv4.ip_forward=1" | sudo tee -a /etc/sysctl.conf
pi@raspberrypi:~ $ sudo sysctl -p /etc/sysctl.conf
```

## strongswanを再起動
```bash:RaspberryPi3
pi@raspberrypi:~ $ sudo ipsec restart
```

# IPSec VPNで接続
では、Raspberry Pi3からAzure VPN GatewayへIKEv2でIPSec VPNでトンネルを敷設し、Raspberry Pi3がAKSの内部ネットワークと直接通信できることを確認してみましょう。
![fig13.png](https://qiita-image-store.s3.amazonaws.com/0/37648/407e486b-97a4-f80e-7aa5-2179cc6d63c5.png)


## VPN接続の開始
```bash:RaspberryPi3
pi@raspberrypi:~ $ sudo ipsec up azure
```
Raspberry Pi3のTERMINALに接続ログが流れます。正しく設定されていれば、最後に接続成功メッセージが表示されるはずです。

```bash:RaspberryPi3
...
connection 'azure' established successfully
```
## VPN側のIPアドレスの確認
Azure VPN Gatewayの起動時のパラメータ（ `--address-prefixes 10.255.0.0/24` ）で指定したように、 `10.255.0.0/24` のレンジからVPN Client用のPrivate IPが一つ（今回は `10.255.0.2/32` ）wlan0に払い出されます。今回は `10.255.0.2` が払い出されました。

```bash:RaspberryPi3
pi@raspberrypi:~ $ ip addr show dev wlan0
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether b8:27:eb:73:7a:85 brd ff:ff:ff:ff:ff:ff
    inet 192.168.128.107/24 brd 192.168.128.255 scope global wlan0
       valid_lft forever preferred_lft forever
    inet 10.255.0.2/32 scope global wlan0
       valid_lft forever preferred_lft forever
    inet6 fe80::6290:e9e8:3ec9:f8ac/64 scope link
       valid_lft forever preferred_lft forever
```
## 接続確認
それでは、VPNトンネルによってAzure AKSとRaspberry Pi3間の通信経路が確立したことを確認しましょう。

### loopbackコンテナPodのPrivate IPとPod名の確認
**MacOS側のTerminal** から `kubectl` コマンドを用いて、loopbackコンテナPodのPrivate IPとPod名を調べます。

```bash:TERMINAL1
kurento-robot-camera-k8s$ LOOPBACK_POD_IP=$(kubectl get pods \
  -l app=loopback \
  -o jsonpath='{.items[0].status.podIP}'); \
  echo ${LOOPBACK_POD_IP}
```
```bash:TERMINAL1
kurento-robot-camera-k8s$ LOOPBACK_POD_NAME=$(kubectl get pods \
  -l app=loopback \
  -o jsonpath='{.items[0].metadata.name}'); \
  echo ${LOOPBACK_POD_NAME}
```
### Raspberry Pi3からAzure AKS上のloopbackコンテナへ `ping`
上記で表示されたPrivate IPが `10.168.0.20` だったとすると、Raspberry Pi3からVPNトンネルを通って `10.168.0.20` まで `ping` が到達することが確認できます。

```bash:RaspberryPi3
pi@raspberrypi:~ $ ping 10.168.0.20
PING 10.168.0.20 (10.168.0.20) 56(84) bytes of data.
64 bytes from 10.168.0.20: icmp_seq=5 ttl=64 time=12.6 ms
64 bytes from 10.168.0.20: icmp_seq=6 ttl=64 time=12.9 ms
...
```
> VPNトンネル起動直後は、接続確立まで少し時間がかかります。

### Azure AKSのloopbackコンテナPodからRaspberry Pi3へ `ping`
同様に、loopbackコンテナ側からもRaspberry Pi3に割り振られたVPN Client用のPrivate IPで接続できます。

```text:TERMINAL1
kurento-robot-camera-k8s$ kubectl exec -it ${LOOPBACK_POD_NAME} /bin/ash
/opt/kurento-loopback # ping 10.255.0.2
PING 10.255.0.2 (10.255.0.2): 56 data bytes
64 bytes from 10.255.0.2: seq=2 ttl=63 time=13.428 ms
64 bytes from 10.255.0.2: seq=3 ttl=63 time=12.962 ms
...
```

# カメラ映像リアルタイム配信用のSignaling&APPコンテナ（mjpeg2many）を起動
それでは、VPNトンネルを経由して受信したカメラ映像を、WebRTCを経由してブラウザへ配信するアプリを起動します。手順はloopbackコンテナの起動と同様です。
![fig14.png](https://qiita-image-store.s3.amazonaws.com/0/37648/be134f3b-9904-7dcf-b5ab-6ea03cb99d20.png)
## mjpeg2manyコンテナのCluster IP Serviceを起動
```bash:TERMINAL1
kurento-robot-camera-k8s$ kubectl apply -f mjpeg2many/mjpeg2many-service.yaml
```
```bash:TERMINAL1
kurento-robot-camera-k8s$ kubectl get services -l app=mjpeg2many
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
mjpeg2many   ClusterIP   10.2.0.17    <none>        3000/TCP   28s
```
## mjpeg2manyコンテナの設定情報をSecretに登録
loopbackコンテナと同様のSTUN ServerとTURN Serverの情報だけでなく、WebSocketによるSignalingの際に必要となるパスフレーズも登録します。

```bash:TERMINAL1
kurento-robot-camera-k8s$ STUN_LIST=$(kubectl get secrets loopback-secrets \
  -o jsonpath='{.data.STUN_LIST}' \
  | base64 --decode)
```
```bash:TERMINAL1
kurento-robot-camera-k8s$ TURN_LIST=$(kubectl get secrets loopback-secrets \
  -o jsonpath='{.data.TURN_LIST}' \
  | base64 --decode)
```
```bash:TERMINAL1
kurento-robot-camera-k8s$ kubectl create secret generic mjpeg2many-secrets \
  --from-literal=PASS_PHRASE=${PASS_PHRASE} \
  --from-literal=STUN_LIST=${STUN_LIST} \
  --from-literal=TURN_LIST=${TURN_LIST}
```
## mjpeg2manyコンテナのPodを起動する
```bash:TERMINAL1
kurento-robot-camera-k8s$ kubectl apply -f mjpeg2many/mjpeg2many-deployment.yaml
```
```bash:TERMINAL1
kurento-robot-camera-k8s$ kubectl get pods -l app=mjpeg2many
NAME                          READY   STATUS    RESTARTS   AGE
mjpeg2many-7975b496d6-8n95c   1/1     Running   0          77s
```
## DNSにmjpeg2manyアプリ用のAレコードを登録する
```bash:TERMINAL1
kurento-robot-camera-k8s$ AMBASSADOR_PUBLIC_IP=$(kubectl get services \
  -l app=ambassador \
  -o jsonpath='{.items[0].status.loadBalancer.ingress[0].ip}'); \
echo ${AMBASSADOR_PUBLIC_IP}
```
```bash:TERMINAL1
kurento-robot-camera-k8s$ az network dns record-set a add-record \
  --resource-group ${DNS_ZONE_RG} \
  --zone-name "${DOMAIN}" \
  --record-set-name "mjpeg2many" \
  --ipv4-address "${AMBASSADOR_PUBLIC_IP}"
```
```bash:TERMINAL1
kurento-robot-camera-k8s$ nslookup mjpeg2many.${DOMAIN}
```

# Raspbery Pi3のカメラ映像をブラウザから視聴
Raspberry Pi3側とAzure AKS側の環境が整いましたので、まずはRaspberty Pi3に接続したUSBカメラの映像をインターネット越しにブラウザで視聴したいと思います。
![fig15.png](https://qiita-image-store.s3.amazonaws.com/0/37648/9aee7950-16ec-ac6f-f09d-ec7be0f4a037.png)

## Raspberry Pi3にUSBカメラを接続
Raspberry Pi3にUSBカメラを接続します。カメラデバイスが認識されていることを確認してください。今回は `/dev/video0` として認識されました。

```bash:RaspberryPi3
pi@raspberrypi:~ $ lsusb
Bus 001 Device 005: ID 046d:0825 Logitech, Inc. Webcam C270
Bus 001 Device 004: ID 0424:7800 Standard Microsystems Corp.
Bus 001 Device 003: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 002: ID 0424:2514 Standard Microsystems Corp. USB 2.0 Hub
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
```
```bash:RaspberryPi3
pi@raspberrypi:~ $ ls -al /dev/video*
crw-rw----+ 1 root video 81, 0  4月 10 10:17 /dev/video0
```

## Raspberry Pi3にmjpeg-streamerをインストール
Raspberry Pi3にUSBカメラ映像をMotion JPEGとして配信するために、 `mjpeg-streamer` をインストールします。

```bash:RaspberryPi3
pi@raspberrypi:~ $ sudo apt install -y cmake libv4l-dev libjpeg-dev imagemagick
pi@raspberrypi:~ $ git clone https://github.com/jacksonliam/mjpg-streamer.git
pi@raspberrypi:~ $ cd mjpg-streamer/mjpg-streamer-experimental
pi@raspberrypi:~/mjpg-streamer/mjpg-streamer-experimental $ make
```

## Raspberry Pi3のUSBカメラ映像をMotion JPEGで配信
`mjpeg-streamer` をPort 8080で起動して、Motion JPEGでカメラ映像を配信します。今回は FPSを20フレーム/秒、解像度を360x270としました。

```bash:RaspberryPi3
pi@raspberrypi:~/mjpg-streamer/mjpg-streamer-experimental $ ./mjpg_streamer \
  -i "./input_uvc.so -f 20 -r 360x270 -d /dev/video0 -n" \
  -o "./output_http.so -w ./www -p 8080"
```

## ブラウザからRaspberry Piのカメラ映像を視聴
ChromeやFirefox等のモダンブラウザを立ち上げ、 `https://mjpeg2many.${DOMAIN}` にアクセスしてください。 `secrets/auth-tokens.json` に定義されている `username` と `password` を使ってログインすると、mpeg2manyアプリのWeb画面が表示されます。
<img width="1392" alt="fig16.png" src="https://qiita-image-store.s3.amazonaws.com/0/37648/4144c554-1a44-1e2b-1717-b3b01675fd73.png">

mjpeg2manyコンテナを起動した際に指定した `PASS_PHRASE` 、及びRaspberry Pi3の mjpeg-streamerのエンドポイント ( `http://<<Azure AKS側から見たRaspberry PiのPrivate IP>>:8080/?action=stream` )を入力して `start` ボタンをクリックします。

```bash:TERMINAL1
kurento-robot-camera-k8s$ kubectl get secrets mjpeg2many-secrets \
  -o jsonpath='{.data.PASS_PHRASE}' \
  | base64 --decode
```
カメラ映像が表示されるまでに10〜20秒程度かかります。
<img width="1392" alt="fig17.png" src="https://qiita-image-store.s3.amazonaws.com/0/37648/5c93a550-25ee-b1df-2981-13d72ad8d24d.png">
一度映像が表示されれば、ほぼリアルタイムにRaspberry Pi3のカメラ映像が視聴できます。上手く表示できない場合は、FPSや解像度を小さくしてみてください。
<img width="1392" alt="fig18.png" src="https://qiita-image-store.s3.amazonaws.com/0/37648/9b92e02e-4ac4-6711-d18b-7ab2a0249999.png">
Raspberry Pi3のカメラ映像がブラウザで表示できることが確認できたら、 `CTRL-C` で `mjpeg-streamer` を終了してください。

# ロボットのカメラ映像をブラウザから視聴
それでは最後に、ロボットのカメラ映像をリアルタイムにブラウザから視聴しましょう。今回使うロボットは、[husarion](https://husarion.com/)の[ROSBot2](https://husarion.com/manuals/rosbot-manual/#rosbot-manual-overview)です。動輪を4つ持ち、Orbbec AstraのRGBDカメラとRpLidar A2のLIDARを備えています。センサーや動輪の制御を[ROS](http://wiki.ros.org/)で行うことができるため、プログラミングも容易です。
![ROSBot2](https://husarion.com/docs/assets/img/ROSbot_manual/colour_perspective.jpg)

この自律移動ロボットをRaspberry Pi3と同じWiFiネットワークに接続し、Raspberry Pi3のNATを経由してブラウザまでカメラ映像を届けます。
![fig19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37648/d22ffa57-a300-3c19-3766-95a7ae8796b2.png)

## ロボットのPrivate IP確認
モバイルWiFiにROSBot2を接続し、SSHしてIPアドレスを確認したところ、 `192.168.128.108` が割り当てられました。

```bash:ROSBot2
husarion@tis-rosbot2-02:~$ ip addr show dev wlan0
4: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether 40:9f:38:4e:df:f3 brd ff:ff:ff:ff:ff:ff
    inet 192.168.128.108/24 brd 192.168.128.255 scope global dynamic wlan0
       valid_lft 81290sec preferred_lft 81290sec
    inet6 fe80::e15a:99fe:1d7f:501d/64 scope link
       valid_lft forever preferred_lft forever
```

## Raspberry Pi3にNATの設定を追加
Raspberry Pi3の8080ポートに到着したパケットをROSBot2の8080ポートに転送するように、Raspberry Pi3にDNAT/SNATを設定します。

```bash:RaspberryPi3
pi@raspberrypi:~ $ sudo iptables -t nat -A PREROUTING \
  -m tcp -p tcp \
  --dst 10.255.0.2 --dport 8080 \
  -j DNAT \
  --to-destination 192.168.128.108:8080
```
```bash:RaspberryPi3
pi@raspberrypi:~ $ sudo iptables -t nat -A POSTROUTING \
  -m tcp -p tcp \
  --dst 192.168.128.108 --dport 8080 \
  -j SNAT \
  --to-source 192.168.128.107
```

## ROSBot2の制御プログラムを起動
ROSBot2の制御プログラムを起動し、その後ロボットのカメラ映像をMotion JPEGで配信するROSプログラムも起動します。

```bash:ROSBot2
husarion@tis-rosbot2-02:~$ sudo apt install -y ros-kinetic-web-video-server
```

```bash:ROSBot2
husarion@tis-rosbot2-02:~$ roslaunch rosbot_webui demo.launch
```

```bash:ROSBot2
husarion@tis-rosbot2-02:~$ rosrun web_video_server web_video_server
```

## ブラウザからロボットのカメラ映像を視聴
今度はエンドポイントを `http://<<Azure AKS側から見たRaspberry PiのPrivate IP>>:8080/stream?topic=/camera/rgb/image_raw&width=360&height=270&quality=20` として、 `start` します。
カメラ映像が表示されるまで40~60秒ぐらいかかりますが、ほぼリアルタイムにロボットのカメラ映像が視聴できます。上手く表示できない場合は、解像度やqualityを小さくしてみてください。

[![ROSBot2 camera stream on your browser](http://img.youtube.com/vi/oUhFFsa1pwA/0.jpg)](https://www.youtube.com/watch?v=oUhFFsa1pwA)
（動画がYouTubeで再生されます。）

なお次の動画は、ディスプレイ上に表示した時計をロボットのカメラで撮り、Raspberry Pi3とAzure AKS上のKurentoを経由してブラウザで表示させた映像を同時に表示したものです。Kurento経由で配信されている映像（ブラウザのRemote Streamの中）は、わずかながら遅延していることがわかります。

[![ROSBot2 camera stream on your browser](http://img.youtube.com/vi/4WaE9SuMe_w/0.jpg)](https://www.youtube.com/watch?v=4WaE9SuMe_w)
（動画がYouTubeで再生されます。）

# まとめ
ということで、第一回、第二回、今回と、ロボットやラズパイのカメラ映像をインターネット越しに安全にブラウザで視聴する方法について解説しました。今回のアーキテクチャには多くのSPOFがあり可用性や性能への課題を残していますが、クラウド上のメディアサーバを経由しているわりにリアルタイム性が高く安全な経路で映像が配信できる、面白いアーキテクチャだと思います。
もしご興味がありましたら、試してみていただけると幸いです。