この記事で達成すること
- Unreal EngineでYoutube ライブ配信のコメントをリアルタイムに取得できること
- Unreal EngineでWindowsのアプリケーションをキャプチャ・表示できること
検証環境
- Windows 11(64bit,i9-10980HK,メモリ64GB,RTX 3080 Laptop)
- Unreal Engine 5.0.3(以下、UE)
- Python 3.9
事前準備
1. Youtubeのライブ配信権限の申請とAPIキーの準備
以下のページを参考に実施してください ※最大24時間かかります、24時間後にまた来てください support.google.com
APIキーの取得はこちらが詳しいです qiita.com
2. Unreal Engineのインストール(5.0.3)
Unreal Engine(今回は5.0.3)をインストールしてください。(詳細は下記を参照)
3. 配信ソフトのインストール(Streamlabs Desktop)
配信にはOBSやSteamlabs Desktopを使います。既にインストール済みの方や、とりあえず後でもいいや、という方は読み飛ばしても大丈夫です。
なお、Streamlabs Desktop自体にもコメント取得機能がありますが、今回は使いません。
コメント取得から配信するまでの流れ
今回は以下の流れでキャプチャ→コメント取得→配信まで進めていきます。
- UEで配信したいアプリケーションウィンドウをキャプチャして3D空間に描画
- Pythonでライブ配信へのコメント取得
- PythonからUEへコメントを送信・描画
- UEアプリをStreamlabs(やOBS)でキャプチャ、Youtubeへ配信
実装
0.UEでプロジェクト作成
まずはEpic Launcherの上部ライブラリからインストールしたUE5.0.3を起動し、プロジェクトブラウザからサードパーソンプロジェクトを作成します。
以下のような画面が表示されれば準備完了です。これ以降、UE側はこのプロジェクトを使用します。
1.UEでウィンドウキャプチャ・描画
キャプチャ用UEプラグイン導入
ゲームなどのアプリケーションの画面をキャプチャするために、WindowCapture2DというUE用プラグインを使います。マーケットにもこのプラグインはありますが、UE5.0以降に非対応*1でインストールができません。そのため、GitHubから直接プラグインをダウンロードして利用します。以下にアクセス後、後に示す手順にてPluginを直接プロジェクトへコピーしてください。
- 上記ページのCode>Download ZIPからデータをダウンロード、解凍
- UEプロジェクトのルートフォルダをエクスプローラで開き、「Plugins」フォルダを作成
- GitHubからダウンロードしたデータ内のPluginフォルダの中身の「WindowCapture2D」フォルダを、さっき作成した「Plugins」フォルダ内へ移動
- プラグインのリビルドを促すダイアログが出るので、はいを選択(出ない場合はUEプロジェクトを起動してみたり、こちらを参考にVisual Studioをインストールして再度試してください)
- プロジェクトの編集>プラグインでプラグインブラウザを開き、インストール済みのプラグインにWindowCapture2Dが有りチェックされていればOK
ウィンドウキャプチャ用アクタの使い方および設定方法
ここからはWindowCapture2Dプラグインの使い方となるので、下記の開発者のクイックスタートを見てください。
WindowCapture2D quick start - YouTube
設定が完了して動作させると以下のような表示になります。 今回はMetaMe*2のウェブページを表示したEdgeをキャプチャしてみました。
UEが非アクティブでも処理落ちしないように設定
このままだとゲームをするためのウィンドウをアクティブ化した際にUE側が処理落ちしてしまい、配信・ゲームどころではありません。
これを回避する設定は簡単で、エディタの環境設定から「CPU」と検索して出てくる「エディタのパフォーマンス」設定のチェックを外すのみです。
2. Unreal Engine上でYoutubeライブのコメントを表示する
以下のような構成にしたいと思います
- Youtubeのライブ配信のコメントを取得するPythonサーバを立てる
- UEアプリで特定のキーを押すと、Pythonサーバからコメントを受け取りログ出力する
まず、1のPythonプログラムは以下のようなものを準備します。APIキーは自分のものを記述します。video_idはライブ配信時に都度書き換えます。
import datetime import urllib.request import json from pytz import timezone from websocket_server import WebsocketServer APIKEY = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' #事前準備にて取得したAPIキーを貼り付け video_id = 'xxxxxxxxxxxx' # 配信開始後に設定されるwatch?v=xxxxxxxxのx部分をコピペ param = { 'part': 'liveStreamingDetails', 'id': video_id, 'key': APIKEY } target_url = 'https://www.googleapis.com/youtube/v3/videos?' + (urllib.parse.urlencode(param)) req = urllib.request.Request(target_url) update_time = datetime.datetime.now(timezone('Asia/Tokyo')) def new_client(client, server): global APIKEY, target_url, req, param, update_time print("New client connected and was given id %d" % client['id']) server.send_message_to_all("Hey all, a new client has joined us") with urllib.request.urlopen(req) as json_ress: ress = json.load(json_ress) param = { 'part': 'id,snippet,authorDetails', 'liveChatId': ress['items'][0]['liveStreamingDetails']['activeLiveChatId'], 'key': APIKEY } target_url = 'https://www.googleapis.com/youtube/v3/liveChat/messages?' + (urllib.parse.urlencode(param)) req = urllib.request.Request(target_url) def client_left(client, server): print("Client(%d) disconnected" % client['id']) def message_received(client, server, message): if message == "get": global APIKEY, target_url, req, param, update_time server.send_message_to_all("GetComment") with urllib.request.urlopen(req) as json_ress: ress = json.load(json_ress) for res in ress['items']: comment_time_str = res['snippet']['publishedAt'] while len(comment_time_str) < 32: index = comment_time_str.find("+") comment_time_str = comment_time_str[:index] + '0' + comment_time_str[index:] comment_time = datetime.datetime.fromisoformat(comment_time_str) comment_time = comment_time.astimezone(timezone('Asia/Tokyo')) comment = res['authorDetails']['displayName'] + 'さん ' + res['snippet']['displayMessage'] if update_time > comment_time: # 取得済みコメントはスキップ continue else: update_time = comment_time + datetime.timedelta(microseconds=1) send_str = comment_time.strftime('%H:%M') + " " + comment print(send_str) server.send_message_to_all(send_str) PORT = 9001 server = WebsocketServer(port=PORT) server.set_fn_new_client(new_client) server.set_fn_client_left(client_left) server.set_fn_message_received(message_received) server.run_forever()
次に、2のUnrealEngine側の準備をします。
まずはWebSocketのプラグインをインストールします。
次にレベルブループリントを下図の様に開き、続いて後述する画像の様にノードを配置します。
ブループリントの書き方に関しては詳細は省きますが、チュートリアルや公式ドキュメントを参考にしてください。ノードを配置する | Unreal Engine ドキュメント
これらUEアプリとPythonサーバの動作としては、ZキーでPythonサーバに接続、Cキーでgetメッセージを送りPythonサーバから最新のコメントを受信してログ出力、Xキーで接続を閉じるという簡単なものになっています。
3. 動作確認
動作確認をしてみましょう。 確認手順は以下のようになります。
- Youtubeライブ配信を開始する
- VideoID(配信URLのwatch?v=xxxxxxxxのx部分)をコピペし、Pythonサーバを起動する
- Unreal Engineのプロジェクトを緑のプレイボタンから実行し、Zキーでサーバに接続する
- ライブ配信の方へコメントを書き込み、Cキーで取得してみる
- ログにコメントが出力されたらOK あとはこのUEのプレビューをStreamlabs Desktopで配信すれば、Vtuber風の配信ができそうです。 お疲れ様でした。
備考
- キャプチャ画面の色合いがおかしい場合、UEアプリのアクタのマテリアルを調節してみてください。
参考
- 去年のUnreal Engineアドベントカレンダーにコメント取得、表示に関する良記事がありましたので紹介します。取得したコメントをアセット化して物理的に落とすといった面白い表現ができそうです。 YouTube Data API を使ってUEでコメントを取得する #UnrealEngine - Qiita
*1:この記事を書いたのは実は1年前なのですが、2023年12月19日時点でマーケット版もUE5.0に対応しているようです。そちらも試してみてください。
*2:MetaMeについてはこちら https://note.metame.ne.jp/