Help us understand the problem. What is going on with this article?

ライブ配信レイアウトを作るNode.jsのフレームワーク

こんにちは。普段フロントエンド(とか)エンジニアをしていますが、趣味でRTAをやっている繋がりでオンラインやオフラインのゲームイベントの運営もやってたりします。

この記事、別の話をしようと思っていましたが、悩んだ結果、運営をやっている繋がりでNodeCGというフレームワークを使ったりmaintainerをしたりしている話をしようと思います。

追記:NodeCGのDiscordサーバーに日本語チャットを作ってあるので気軽に参加してください https://discord.gg/jgQRuJK

NodeCG

https://nodecg.com
https://github.com/nodecg/nodecg

NodeCG

一言で言うと「ライブ配信のレイアウトをWeb技術で作るフレームワーク」です。

ちょっと何言っているのかわからないですね。

噛み砕くと

  • Twitch、YouTube Live、ニコニコ生放送などのライブ配信で
  • 動的でリッチな見た目の画面表示を作るための
  • 大枠の仕組みを提供するフルスタックフレームワーク

です。

まだどんなものかよくわからないので、サンプルを動かしてみましょう。下のはGamesDoneQuick1というRTAイベントで使われた、NodeCGで作られたレイアウトです。(https://github.com/gamesdonequick/sgdq18-layouts)
Kapture 2018-12-08 at 3.31.39.gif

ついでなので、実際のイベントで動いているところを見てみましょう
https://youtu.be/tsUvZ9yiN_U?t=1433

おおざっぱな説明

バックエンドにexpressが使われ、HTML+CSS+JSでブラウザにレイアウトを表示します。

NodeCGでレイアウトを構築するときにつくる、フロントエンドとバックエンドがセットになったものをbundleと呼んでいます。1つまたは複数のbundleを組み合わせて1つのNodeCGアプリを作るイメージです。

1つのbundleは

  • dashboard: レイアウトを操作するページ
  • graphics: 表示するレイアウトのページ
  • extension: サーバーサイド (今回は省略)

の3つに分かれています。

dashboardで操作すると、NodeCGのサーバーサイドを通してgraphicsに反映され、追加でサーバーサイドのロジックを書きたい場合extensionを定義する、といったものです。

nodecg bundle architecture (1).png

つくってみる

せっかくなので何か作ってみましょう。必要なものはNode 8以上です。

NodeCGをcloneして準備する

なかなかセットアップが原始的ですが、許してください。2018年ですが本当にbowerという文字が見えています。許してください。(ここでしか使う必要はないです。)

$ npm i -g nodecg-cli bower
$ nodecg setup
$ mkdir bundles/hoge-layout
$ cd $_
$ npm init -y

bundleの設定を書く

bundles/hoge-layoutのほうのpackage.jsonに設定を書いていきます。https://nodecg.com/tutorial-5_manifest.html

package.json
{
  "nodecg": {
    "compatibleRange": "^1.0.0",
    "dashboardPanels": [
      {
        "name": "sample-panel",
        "title": "Sample Panel",
        "file": "sample-panel.html"
      }
    ],
    "graphics": [
      {
        "file": "index.html",
        "width": 1280,
        "height": 720
      }
    ]
  }
}

dashboardを書く

dashboardディレクトリを作り、dashboardに数字をカウントアップするボタンを付けてみます。

dashboard/sample-panel.html
<!DOCTYPE html>
<html>
<body>
    <button id="increment">+1</button>
    <script>
        const incrementEl = document.getElementById('increment')
        const countReplicant = nodecg.Replicant('count')

        incrementEl.addEventListener('click', () => {
            countReplicant.value += 1
        })
    </script>
</body>
</html>

graphicsを書く

graphicsディレクトリを作り、数字を表示するレイアウトを作ってみます。

graphics/index.html
<!DOCTYPE html>
<html>
<body>
    <div id="count"></div>
    <script>
        const countEl = document.getElementById('count')
        const countReplicant = nodecg.Replicant('count', {defaultValue: 0})

        countReplicant.on('change', newValue => {
            countEl.innerText = newValue
        })
    </script>
</body>
</html>

動かす

$ cd ../..
$ node .

これでNodeCGが起動するので、localhost:9090でdashboardを出してみます。
Screen Shot 2018-12-08 at 01.42.41.png

右上のGRAPHICSから、配信レイアウトを出せます
Screen Shot 2018-12-08 at 01.43.56.png
...他に何も書いていないので数字しかいません。実際にレイアウトを作る際は、HTML、CSS、SVGなどを駆使して上の動画みたいなレイアウトを作ります。

さておき、dashboardのボタンを押してみましょう。
Kapture 2018-12-08 at 1.52.14.gif
いい感じでリアルタイムに動いています。

実際の配信での使い方

今までブラウザでレイアウトを表示してきましたが、実施の配信で使うときはブラウザに写しません。何を使うかというと配信ソフトについているブラウザソースの機能です。

Screen Shot 2018-12-08 at 03.39.53.png
Screen Shot 2018-12-08 at 03.40.24.png

例えばOBS Studioは、内蔵のChromiumを使って配信に直接表示することができ、ブラウザに写してそれをキャプチャ、といった煩わしい手順がいらないです。こういった理由で、このような動的なレイアウトはNodeCGでなくてもHTML+CSS+JSが使われることが多いです。

すこし詳しく説明

全機能紹介するとキリがないので、抜粋しつつ

機能

dashboardとgraphicsの中のJSでは、window.nodecgが定義されていて、いろいろなプロパティがいますが、例で使ったのはnodecg.Replicantです。
これはNodeCGの中にある簡易的なデータベースのためのAPIで、代入された値を保存して同時に変更をlistenerに伝える仕組みです。dashboardで数を増やしたら、その瞬間サーバーを通してgraphicsに'changed'イベントが伝えられ、新しい値が伝わります。
Replicantは実際にファイルに保存されるため、何かの不具合でサーバーが落ちたとしても値が保存されます。(やり直しがないライブ配信ではかなり大事な機能です)

他にもいろいろ機能があります

  • message: データのやり取りでなく、イベントを直接やり取りできる機能
  • sound: 音声ファイルをアップロードしておき、キューを送ることでレイアウトで音を再生できる機能
  • asset: 画像や動画ファイルをアップロードして、レイアウトやダッシュボードから参照できる機能

など。

基本的に薄く、unopinionatedに作られているため、例のようにplainなHTMLで作ったり、Reactで書いたりVueで書いたりなんでもできます。 (フレームワークの部分はPolymer 2でできていますが、中身を作る際は関係ありません)

中身の技術

Replicantの代入、変更、mutateに対してJSのProxyを挟むことで、変更を検知して各場所にWebSocketを通じて通知することで、すべての場所で同じデータを見ているような仕組みになっています。。

Proxyは興味深く、IE11で使用できずPolyfillも不可能なためまだ流行っていませんが、IE11を最初からサポートしていないNodeCGではこのような使い方ができています。(将来VueがリアクティビティをProxyで作り直すことも発表されています。)

(dashboardで値の変更)→(proxyがサーバーに通知)→(サーバーがgraphicsに通知)→(graphicsの表示が変更)

フロントエンドのリアクティブなフレームワークがHTMLとJSの間でリアクティビティを実現しているのと同じような関係を、クライアントとサーバーの壁を超えて実現しているあたり面白い仕組みだと思っています。

Maintainerとして

個人的な話をすると、1年半くらい前、このフレームワークを使って関わっているイベントのレイアウトを作るのが、本格的にプログラミングを始めるきっかけでした。
その後、TypeScriptの型定義をつけたり、テストのリファクタリングしたりしていたら、maintainerにならないかと誘われました。それまでは作者のLangeが1人でメンテナンスしてきたこともあり、まだまだ発展途上なフレームワークなので、できるだけ貢献していきたいです。

あと、海外では幅広いイベントで使われているのですが、日本で使っているのが自分ともう1人しか知らないので、もっと流行ってほしいですね!


  1. 同時視聴10万人以上、1週間で2億円以上寄付金を集めるマンモスチャリティイベント。RTAを披露して視聴者から寄付金を募る。 

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした