Flask+ツイキャスAPI、VRChatでライブ配信を見る

# はじめに

この記事は[VRChat Advent Calendar 2018](https://qiita.com/advent-calendar/2018/vrchat)の4日目の記事です。
昨日の記事は[@joniburn](https://qiita.com/joniburn)さんによる、[BlenderでVRChat向けアバターの体型を改変する](https://qiita.com/joniburn/items/1e10c02c35fb19966071)です。


はじめまして。meihei([@app1e_s](https://twitter.com/app1e_s))です。

VRChat Advent Calendarでは、Unityやモデリング関連の記事が多いだろうなぁと思ったので
ちょっと路線を変えて、バックエンド寄りのことを書いていきたいと思います。

VRChatにはライブ配信を視聴する手段として、VRC_SyncVideoStreamというものが用意されています。
VRC_SyncVideoStreamにライブ配信のURLを設定することで、簡単に視聴することが出来ますが
そこにサーバーのURLを設定し、最終的にライブ配信のURLにリダイレクトすれば
サーバーで色々しても問題なく視聴することが出来ます。

完成形はこんな感じ。
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ツイキャス on VRChat <a href="https://t.co/m2NzLlYweJ">pic.twitter.com/m2NzLlYweJ</a></p>&mdash; 迷える兵士/meihei (@app1e_s) <a href="https://twitter.com/app1e_s/status/1075485462439968768?ref_src=twsrc%5Etfw">2018年12月19日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

ということで、この記事では「FlaskでツイキャスAPIを叩くサーバーを建てる」「VRChatでライブ配信を視聴する」の２つについて書いていきます。

## 概要
1. 環境
2. 仕組み
  1. Flaskとは？
  2. APIとは？
  3. HLS(m3u8)とは？
  4. VRC_SyncVideoStreamとは？
3. 実装
  1. ツイキャスでアプリケーション登録
  2. FlaskでAPIサーバー作る
  3. VRChatで設定
4. 結果
5. ソースコード

## 環境
- Python3.6
- Unity 5.6.3p1
- macOS 10.14

## 仕組み
![Untitled Diagram.png](https://qiita-image-store.s3.amazonaws.com/0/280373/9c518782-e12e-c016-c85d-90e88a1590b6.png)
1. VRC_SyncVideoStreamから設定してあるURL(Flaskサーバー)にアクセス
2. pythonでツイキャスのsearchAPIを叩いて、特定のカテゴリーの検索結果をもらう
3. 結果から、視聴者数の最も多い配信のURLをとりだす
4. そのURLにVRChat側がリダイレクトするように返す
5. リダイレクトして、VRC_SyncVideoStreamからツイキャスが視聴できるようになる

### Flaskとは？
python用の軽量ウェブフレームワークです。
VRChatからアクセスされたときに、プログラムを実行して、実行結果を返すサーバーを
pythonで簡単に書けるもの。と思ってもらえれば大丈夫です。

### APIとは？
Application Programming Interfaceの略です。
今回はツイキャスなので、ツイキャスというアプリケーションをプログラム(Flask)から操作したい時に
データのやり取りのルールを決めたインターフェイス(通り門的なもの)です。

[こちら](https://apiv2-doc.twitcasting.tv/#introduction)はツイキャスのAPIのリファレンスになりますが
単純に`Request`に対して、`Response`が返ってくると思って見てもらって大丈夫です。

### HLS(m3u8)とは？
HLSとは、HTTP Live Streamingの略で、ライブ配信の仕組みについての名称です。
m3u8とは、HLSで使われるファイルフォーマットです。
つまり、そういうライブ配信の仕様があるというだけのことです。

詳しく知りたい人は以下がオススメです。

#### 参考
- [HLSとは](https://qiita.com/STomohiko/items/eb223a9cb6325d7d42d9)
- [HTTP Live Streaming 動画配信のやり方 （概要）](https://bizvalley.co.jp/blog/1023.html)
- [動画配信技術 その1 - HTTP LIVE STREAMING(HLS)](https://blogs.akamai.com/jp/2013/02/-1---http-live-streaminghls.html)

### VRC_SyncVideoStreamとは？
VRC_SDKで用意されているVRC_SyncVideoPlayerの上位互換です。
これをComponentsに追加して、少し設定をするとm3u8も再生できます。
→VRChat内でのライブ配信の視聴を可能にしてくれます。

[VRChat 技術メモ帳](https://vrcworld.wiki.fc2.com/wiki/VRC_SyncVideoStream)に詳しい挙動などが載っています。

## 実装
### ツイキャスでアプリケーション登録
APIを叩く用のアプリケーションを登録します。
登録までの手順は、[こちら](https://qiita.com/yonex/items/dd8f815f54a27f8c8690#アクセストークンを取得して-api-にアクセスするまでのサンプル)にそって登録していきます。~~(それにしても、ドワンゴのAdvent CalendarでツイキャスAPIの記事…？)~~

そして、そこから`ClientID`と`ClientSecret`を取り出します。

このプログラムではユーザー単位での利用を想定していないので、ユーザーのアクセストークンを取得する必要もありません。

アプリケーション単位でツイキャスAPIを使う場合、`{ClientID}:{ClientSecret}`をBase64エンコードし、 ヘッダーに`Authorization: Basic {BASE64_ENCODED_STRING}`の形式で指定することで使えます。

```python:config.py
import base64

ClientID = "your client ID"
ClientSecret = "your client secret"

BASE64_ENCODED_STRING = base64.b64encode(("%s:%s" % (ClientID, ClientSecret)).encode("utf-8"))
```

これで、APIを利用するためのキーの設定は完了です。

```python
from config import BASE64_ENCODED_STRING

HEADER = {
    "X-Api-Version": "2.0", 
    "Authorization": "Basic %s" % BASE64_ENCODED_STRING.decode("utf-8")
}
```
といった形で読み込まれます。

### FlaskでAPIサーバー作る
まず、Flaskで簡単なHello Worldをして、それを拡張していきます。

```python:hello.py
from flask import Flask

app = Flask(__name__)


@app.route('/')
def index():
    return "Hello World"


if __name__ == '__main__':
    app.run(debug=True, host='0.0.0.0')
```
`app.route()`で、そこにアクセスされた時の処理を書いていきます。
つまり、localhostでサーバーを立てていれば、[http://0.0.0.0:5000/](http://0.0.0.0:5000/)にアクセスされたときに、`Hello World`と返します。

では次に、ツイキャスのAPIを叩く関数を作ります。

```python:app.py
from config import BASE64_ENCODED_STRING

BASE_URL = "https://apiv2.twitcasting.tv"
SEARCH_URL = BASE_URL + "/search/lives"

HEADER = {
    "X-Api-Version": "2.0", 
    "Authorization": "Basic %s" % BASE64_ENCODED_STRING.decode("utf-8")
}

def get_hls_url(sub_categorie):
    # TwitCastingのSearch APIを叩く
    res = requests.get(SEARCH_URL+"?limit=25&type=category&context=%s&lang=ja" % sub_categorie, headers=HEADER)
    if res.status_code != 200:
        return res.content
    # 結果からmovieオブジェクトのlistを取り出す
    movies = json.loads(res.text)['movies']
    # 最もトータルの視聴者数の多い配信を選択
    best_movie = sorted(movies, 
                        reverse=True, 
                        key=lambda m: m['movie']['total_view_count'])[0]
    return best_movie['movie']['hls_url']
```
[こちら](https://apiv2-doc.twitcasting.tv/#search-live-movies)の通りにパラメーターを用意すると、ツイキャス内でのカテゴリー検索ができ、その結果から最も人気な配信のhls_urlを返すようになっています。

そして、Flaskの方から呼び出せるようにします。

```python
@app.route('/api')
def tc2vrc_api():
    # カテゴリー -> 音楽：弾き語り(女性Vo)
    url = get_hls_url(sub_categorie="music_recite_girls_jp")
    return redirect(url)
```
これで、`http://localhost:5000/api`にアクセスすると、ツイキャスAPIを呼んで、結果のurlにリダイレクトするようになりました。

最後に、ターミナルで`python app.py`と打ってサーバーが建てられれば完成です。

### VRChatで設定
公式ドキュメントに[Starter Guide](https://docs.vrchat.com/docs/vrc_syncvideostream#section-starter-guide)があります。
一度この通りに作って、YouTube liveでライブ配信が流せるか確認します。

上手くいったら、`Video Search Root`の中を空にし、VideosのURLに先程立てたサーバーの
URLを貼ります。
![ss 2018-12-04 6.11.05.png](https://qiita-image-store.s3.amazonaws.com/0/280373/ee5e26bd-fd5b-5aea-f779-ea7be1e7227f.png)
サーバーのURLは、VRCとサーバーが同じPC上で動いているなら`http://localhost:5000/api`
違うPC(同じLAN上)ならば、そのPCのローカルIPアドレスを確認し、`http://{local-IP-address}:5000/api`で設定します。
IPアドレスの確認方法はMacなら[こちら](http://minto.tech/mac-ipaddress/)、Winなら[こちら](http://www.geolocation.co.jp/learn/program/01.html)。

これで、VRChatでツイキャスを視聴できるようになりました。

## 結果
サーバー側は以下のようなログが表示され、VRCからアクセスされている事がわかります。
![スクリーンショット 2018-12-20 4.41.56.png](https://qiita-image-store.s3.amazonaws.com/0/280373/dc7a7e8e-ff06-0adc-1205-ec17ad5427ff.png)

そして、ワールド側ではツイキャスから弾き語りの配信を流すということができました。
（ラジオ配信なので、配信感があまり感じられないですが。）
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ツイキャス on VRChat <a href="https://t.co/m2NzLlYweJ">pic.twitter.com/m2NzLlYweJ</a></p>&mdash; 迷える兵士/meihei (@app1e_s) <a href="https://twitter.com/app1e_s/status/1075485462439968768?ref_src=twsrc%5Etfw">2018年12月19日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">ツイキャon VRChat 2 <a href="https://t.co/wk4JE7wIWu">pic.twitter.com/wk4JE7wIWu</a></p>&mdash; 迷える兵士/meihei (@app1e_s) <a href="https://twitter.com/app1e_s/status/1075486141434945536?ref_src=twsrc%5Etfw">2018年12月19日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> 


## ソースコード
[こちら](https://gist.github.com/meihei3/a0942934846faeda7b0d4ab6b77307c8)

## その他
今回はツイキャスでやりましたが、それ以外のライブ配信サービスを使いたい、プログラム書きたくないって人は[YUKIMOCHI VRChat HLS Bridge](https://github.com/yukimochi/VRC_HLS)というのがオススメです。


私の記事は以上になります。
明日のVRChat Advent Calendarは[@akiakishitai](https://qiita.com/akiakishitai)さんの「[はじめてのシェーダー体験談](https://qiita.com/akiakishitai/items/aacb07f220155fbcf6f5)」です。お楽しみに。