Echo Spotとraspberry piでセキュリティーカメラを作る

Echo SpotやEcho Showの様にディスプレイを持つEchoは動画を再生させることが出来ます。

既にAlexa向けのカメラは[USでいっぱい発売](https://www.amazon.com/s/ref=lp_898400_nr_p_n_amazon_certified_0?fst=as%3Aoff&rh=n%3A172282%2Cn%3A%21493964%2Cn%3A502394%2Cn%3A7161091011%2Cn%3A898400%2Cp_n_amazon_certified%3A16741513011&bbn=898400&ie=UTF8&qid=1521626286&rnid=16741512011)されていますが、これを自作してみようと思います。
カメラはラズパイ純正のカメラを利用しています。

## 全体像はこんな感じ

<img width="595" alt="スクリーンショット 2018-03-21 19.28.19.png" src="https://qiita-image-store.s3.amazonaws.com/0/36184/2e15c044-525a-fd35-9830-14172e9d2698.png">

## 完成品が動いてる所

[![Alexa Skill for camera](http://img.youtube.com/vi/nquF0SOXNdQ/0.jpg)](http://www.youtube.com/watch?v=nquF0SOXNdQ)

### サンプルソース
https://github.com/sparkgene/echo-pi-camera

## Video Appで再生できる動画やオーディオ

ディスプレイ付Echoで使うdisplay templateの詳細については、この記事を見てください。
[Echo Showの表示を試してみる]
(https://qiita.com/sparkgene/items/75af623c47c57169624f)

[Supported Video Formats and Resolutions](https://developer.amazon.com/docs/custom-skills/videoapp-interface-reference.html#supported-video-formats-and-resolutions)


|Streaming format|Accompanying audio format|
|----------------|-------------------------|
|HLS, MPEG-TS|AAC|
|SmoothStreaming (SS), MP4, M4A|AAC, Dolby, Dolby Digital Plus|

- httpsを利用して動画が配信されれなければならない
- サポートしている拡張子：.mp4, .m3u8, .ts
- 対応コーデック：MPEG4 または H.264
- 推奨動画サイズ：640x480 または 1280x720 
- 最大動画サイズ:1280x720

この条件をみたすようにraspberry piをセットアップします。

## ラズパイのセットアップ

ラズパイにカメラを繋いで、そこから配信することで、echoからその映像が見れるようにします。
こちらの記事にやり方が公開されていたので、そちらを参考にしました。

[Raspberry Pi のカメラモジュールでハイビジョン撮影して iOS に streaming 配信する](https://qiita.com/UedaTakeyuki/items/dbb466fd47fa32a57a4c)

ラズパイは「2017-11-29-raspbian-stretch.img」を使っています。

```
$ uname -a
Linux raspberrypi 4.9.59-v7+ #1047 SMP Sun Oct 29 12:19:23 GMT 2017 armv7l GNU/Linux
```

### ffmpegのインストール

```
$ sudo su
$ cd /usr/src
$ git clone --depth 1 git://source.ffmpeg.org/ffmpeg.git
$ cd ffmpeg
$ ./configure
$ make -j4
$ make install
```

### nginxのインストール

```
$ apt-get install nginx
$ nginx -v

nginx version: nginx/1.10.3

$ chown -R pi /var/www/html
$ chgrp -R pi /var/www/html
```

ここで問題となるのが、echoではhttpsでのアクセスを必要とするので、SSL証明書が必要になります。
ラズパイ自体がインターネットからアクセスできるのであれば、直接Let's Encryptの設定ができるのですが、今回は外部に公開していないので、別のサーバで発行してそれをコピーする方法でSSL証明書を作成しました。

また、証明書を発行する際にドメインも必要なので、AWSのRoute53でローカルのIPと紐付けたレコードを追加しています。

### ec2上でSSL証明書を発行

```
$ sudo su
$ yum install nginx
$ service nginx start
$ wget https://dl.eff.org/certbot-auto
$ chmod a+x certbot-auto
$ ./certbot-auto --no-bootstrap certonly --webroot -w /usr/share/nginx/html -d <使うドメイン> --email <自分のメアド>
```
これで、`etc/letsencrypt/live/<使うドメイン>/` の中に以下の証明書が作られるので、それを全部ラズパイにコピーします。

- cert.pem
- chain.pem
- fullchain.pem
- privkey.pem

### ラズパイ側

```
$ mkdir /etc/nginx/certs
$ cd /etc/nginx/certs
```

ココに、作成したSSL証明書を置きます。

サンプルのソースをgitからcloneしてそれを使用します。

```
$ cd /opt
$ git clone git@github.com:sparkgene/echo-pi-camera.git
$ cp /opt/echo-pi-camera/nginx/stream.conf /etc/nginx/sites-available/stream.conf
$ ln -s  /etc/nginx/sites-available/stream.conf /etc/nginx/sites-enabled/stream.conf
```

`/etc/nginx/sites-enabled/stream.conf` に自分のドメインを書きます。

```
server {
  listen 443 ssl;
  server_name <自分のドメイン>;
```

ラズパイにアクセスできるネットワーク上から実際にドメイン指定でアクセスできるかを確認します。

```
$ curl -I https://<自分のドメイン>/

HTTP/1.1 200 OK
Server: nginx/1.10.3
Date: Wed, 21 Mar 2018 07:10:09 GMT
Content-Type: text/html
Content-Length: 612
Last-Modified: Wed, 21 Mar 2018 05:26:01 GMT
Connection: keep-alive
ETag: "5ab1ece9-264"
Accept-Ranges: bytes
```

これで、動画を配信する準備はできました。

### Alexa Skillの作成

Alexa Skillはカスタムスキルを利用しますが、まだecho spotはまだ日本語対応していないので、US英語向けのSkillとして作成します。

skillの作成ではask cliを使っいますので、事前にAWSのクレデンシャルの登録をします。
ask cliで必要な権限は以下で公開されているので、この権限を持つアクセスキーを作成して、プロファイルを作成します。
https://developer.amazon.com/ja/docs/smapi/set-up-credentials-for-an-amazon-web-services-account.html

作業は、PC上で行ないます。

```
$ aws credential -p ask-cli
```

次に[ask cli](https://www.npmjs.com/package/ask-cli)をインストールしていなければ、インストールして初期設定を済ませます。

```
$ npm install -g ask-cli
$ ask init
$ ask init -l

  Profile              Associated AWS Profile
  [default]                 ** NULL **
  [us-alexa]                "ask-cli"
```
プロフィル名は適宜自分の環境に合わせて下さい。

サンプルソースをコピーます。

```
$ git clone git@github.com:sparkgene/echo-pi-camera.git
$ cd echo-pi-camera/echo-pi-camera
```

`lambda/custom/lambda_function.py` に映像のURLが書かれているので自分の環境に合わせて書き換えて下さい。

```
VIDEO_URL = "https://<自分のドメイン>/stream.m3u8"
```

askコマンドで、デプロイします。

```
$ ask deploy -p us-alexa
-------------------- Create Skill Project --------------------
Profile for the deployment: [us-alexa]
Skill Id: amzn1.ask.skill.
Skill deployment finished.
Model deployment finished.
Lambda deployment finished.
Your skill is now deployed and enabled in the development stage.
Try invoking the skill by saying “Alexa, open {your_skill_invocation_name}” or simulate an invocation via the `ask simulate` command.
```

完了すると、skillとlambdaファンクションが作られます。
lambdaファンクションで、runtimeが `nodejs` になってしまうのが原因わからないのですが（誰か教えて）、これを`python 3.6`に変えます。

### ラズパイ上で配信を開始

```
$ cd /opt/echo-pi-camera
$ sh ./stream.sh

ffmpeg version git-2018-03-21-7e0dc72 Copyright (c) 2000-2018 the FFmpeg developers
  built with gcc 6.3.0 (Raspbian 6.3.0-18+rpi1) 20170516
  configuration:
  libavutil      56. 11.100 / 56. 11.100
  libavcodec     58. 14.100 / 58. 14.100
  libavformat    58. 10.100 / 58. 10.100
  libavdevice    58.  2.100 / 58.  2.100
  libavfilter     7. 13.100 /  7. 13.100
  libswscale      5.  0.102 /  5.  0.102
  libswresample   3.  0.101 /  3.  0.101
Input #0, h264, from 'pipe:':
  Duration: N/A, bitrate: N/A
    Stream #0:0: Video: h264 (High), yuv420p(progressive), 720x404, 25 fps, 25 tbr, 1200k tbn, 50 tbc
[stream_segment,ssegment @ 0x2b7b310] Opening '/var/www/html/segments/00000000.ts' for writing
Output #0, stream_segment,ssegment, to '/var/www/html/segments/%08d.ts':
  Metadata:
    encoder         : Lavf58.10.100
    Stream #0:0: Video: h264 (High), yuv420p(progressive), 720x404, q=2-31, 25 fps, 25 tbr, 90k tbn, 25 tbc
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
[stream_segment,ssegment @ 0x2b7b310] Timestamps are unset in a packet for stream 0. This is deprecated and will stop working in the future. Fix your code to set the timestamps properly
[stream_segment,ssegment @ 0x2b7b310] Opening '/var/www/html/stream.m3u8.tmp' for writing
[stream_segment,ssegment @ 0x2b7b310] Opening '/var/www/html/segments/00000001.ts' for writing
[stream_segment,ssegment @ 0x2b7b310] Opening '/var/www/html/stream.m3u8.tmp' for writing
[stream_segment,ssegment @ 0x2b7b310] Opening '/var/www/html/segments/00000002.ts' for writing
```

配信が始まっている状態で、echoに対して `alexa, open pi camera` と話しかけると、映像が表示されます。

こんな感じのアクセスログが残ってました。

```
192.168.3.32 - - [21/Mar/2018:09:20:37 +0000] "GET /stream.m3u8 HTTP/1.1" 200 275 "-" "Dalvik/2.1.0 (Linux; U; Android 5.1.1; AEORK Build/LVY48F)"
192.168.3.32 - - [21/Mar/2018:09:20:37 +0000] "GET /00000003.ts HTTP/1.1" 200 389536 "-" "Dalvik/2.1.0 (Linux; U; Android 5.1.1; AEORK Build/LVY48F)"
192.168.3.32 - - [21/Mar/2018:09:20:37 +0000] "GET /00000004.ts HTTP/1.1" 200 392356 "-" "Dalvik/2.1.0 (Linux; U; Android 5.1.1; AEORK Build/LVY48F)"
192.168.3.32 - - [21/Mar/2018:09:20:38 +0000] "GET /00000005.ts HTTP/1.1" 200 217140 "-" "Dalvik/2.1.0 (Linux; U; Android 5.1.1; AEORK Build/LVY48F)"
192.168.3.32 - - [21/Mar/2018:09:20:38 +0000] "GET /00000003.ts HTTP/1.1" 200 389536 "-" "Dalvik/2.1.0 (Linux; U; Android 5.1.1; AEORK Build/LVY48F)"
```

ffmpegのオプションで `-segment_time 2` と `-segment_list_size 5` を指定してるから、 `stream.m3u8` に２秒のtsファイルが最大５個として順次書き換えられていきます。echo側からの読み込みは `stream.m3u8` の先頭に書かれているtsファイルを順次読み込んでいくので、その分の遅延が発生します（最大、１０秒ぐらい）。
これをもっとリアルタイムにするには、もっとチューニングが必要そうな感じですね。

## Smart Home Skill

今回はカスタムSkillとして作成しましたが、実際はSmartHome Skillでも作れるので、自然な発話で考えると、SmartHome Skillの方が良いのかなと思います。
https://developer.amazon.com/docs/device-apis/alexa-camerastreamcontroller.html
今度は、SmartHome版にもチャレンジしようと思います。

## 参考にしたサイト

https://qiita.com/UedaTakeyuki/items/dbb466fd47fa32a57a4c
https://www.ffmpeg.org/ffmpeg-formats.html#segment_002c-stream_005fsegment_002c-ssegment
https://qiita.com/MashMorgan/items/56498f276c54406b1928