LoginSignup
51
28

More than 5 years have passed since last update.

SkyWayを使ってVirtualとRealでビデオ通話

Last updated at Posted at 2018-12-17

この記事はリンク情報システムの2018年アドベントカレンダーのリレー記事です。
engineer.hanzomon のグループメンバによってリレーされます。(リンク情報システムのFacebookはこちらから)

1.はじめに

さてアドベントカレンダー参加するとしてとりあえずVRいじりたいけどなにをしようか・・・と考えていたところでskywayなるものを使えば簡単にWebRTCができるよ!というのをどこかで見かけました。
(たぶんQiitaのトレンドかな・・・?)
WebRTCてなんじゃいってレベルだったんですが、ビデオ通話とかに使えるんですね・・・。
なるほど・・・では、たぶん誰かやってるだろうけどブラウザとVR空間でそういうのやってみようてな感じです。

で、やってみようはいいんですがWebRTCなんて触ったことないしUnityもよーわからんので3段階に分けてなんとかそれっぽいものが動けばと思います。

1.skyway使ってブラウザ同士でWebRTC
2.Unityでアバター動かしたり色々する
3.1と2をくっつける?

3までたどり着くことはできるのか・・・?

2.ブラウザ同士でWebRTC

ということでまずはブラウザ同士でがんばるところです。
新しいことしたいということで興味はあったけど触ったことないNode.jsを触ってみたいと思います。
ついでに触ったことなかったHerokuも触ってみることにしました。
どんどんわからないことが増えていますが気にしない。
新しいものに触れるのは楽しいですからね。

開発環境を整える

まずはNode.jsのインストールから。以下の記事を参考にさせていただきました。

nodistでNode.jsをバージョン管理

バージョンは推奨のLTSである8.12.0、npmは4.0.5となりました。
ついでにフレームワークとしてExpressもいれておきます。

npm init
npm install express --save

次にHerokuの登録、CLIのインストールを行います。
Git必要!と言われたのでGitもインストールします。
わからないことにGitが増えました。←New!
いいタイミングでまとめを上げてくださっている方がいたのでそれを見て少しお勉強します。
svnは使っているのですぐ覚えるでしょう・・・たぶん・・・

Git/GitHubレベル別オススメ学習サイトまとめ完全保存版【2018.11】

作成

とりあえず新しいものに手を出すときはチュートリアルをやれと偉い人が言ってました。
というわけでまずはskywayのチュートリアルをやってみます。

JavaScript SDK チュートリアル

skyway1.JPG

動きました。skyway完全に理解した。

skywayを完全に理解したところで色々作っていきます。
とりあえず必要そうなものは

・相手側のIDの検索画面
・ID検索するためにIDを保持するDB
・通話画面

でしょうか。

DBの追加

ID検索の前にDBがないと検索ができないのでまずDBをHerokuのアプリケーションに追加します。
HerokuのアプリケーションページからResourcesタブを選択、Add-onsのあたりにあるFind more add-onsボタンを押します。
skyway2.JPG

次にHeroku Postgresを選択し、Install Heroku Postgresボタンを押します。
Add-on Planは今回あまり使う予定はないのでFreeのHobby Dev、App to provision toにアプリケーション名を入力するとProvision Add-onが活性化するので押します。
skyway4.JPG
これでとりあえず使えるようにはなりました。

ということでCLIからSQLを流します。
ResourcesのAdd-onにPostgresが追加されているのでクリックするとDATAページが開きます。
そこでSettingsタブのDatabase CredentialsのView Credentials...をクリックするとDBの情報がいろいろと書いてあります。
skyway5.JPG
ここのHeroku CLIの文字列をコマンドプロンプトにコピペするとDBに接続できます。
あとは普通にSQLを流すだけ・・・。

webrtc-vr-test::DATABASE=> create table skyway_ids (
webrtc-vr-test::DATABASE(> id serial not null,
webrtc-vr-test::DATABASE(> skyway_id varchar(16) not null,
webrtc-vr-test::DATABASE(> primary key (id)
webrtc-vr-test::DATABASE(> );
CREATE TABLE
webrtc-vr-test::DATABASE=> select * from skyway_ids;
 id | skyway_id
----+-----------
(0 行)

画面とか諸々の作成

さっそくExpressよくわからん・・・ってなったのでとりあえずexpress-generatorをインストールしてアプリケーションを作成します。テンプレートはPugを利用します。
Pugの書き方も勉強しておきましょう。

Windows10 + Express & Pug(旧Jade)

npm install express-generator -g
express --view=pug app-name
cd app-name
npm install

画面構成は
 ・/index:部屋立て or 部屋検索への入口
 ・/room: 通話画面(idをDBに突っ込んで相手の接続を待つ)
 ・/search: id検索画面(idをDBから検索して相手に接続する)
という感じにシンプルにします。

まずはindexから。

index.pug
extends layout

block content
  h1= title
  button(id="room")
    a(href="/room") 部屋立てる
  button(id="search")
    a(href="/search") 部屋検索

skyway6.JPG

簡素だ・・・・・・。CSSもいじってないのですごいシンプル。
まぁ見た目は後から如何様にもなりましょう。

次にroomのルーターの設定を。

routes/room.js
var express = require('express');
var router = express.Router();

router.get('/', function(req, res, next) {
    res.render('room', { title: 'app' });
});

module.exports = router;
app.js
//以下追記
var roomRouter = require('./routes/room');
app.use('/room', roomRouter);

で、画面を作る前にDB接続があるのでpostgreSQL接続用にpgをインストールします。

npm install pg --save

インストールが終わったら画面の作成へ。

room.pug
extends layout

block js
    script(type="text/javascript" src="https://code.jquery.com/jquery-3.2.1.min.js")
    script(type="text/javascript" src="https://cdn.webrtc.ecl.ntt.com/skyway-latest.js")
    script(type="text/javascript" src="../javascripts/roomScript.js")
block content
    div(id="video-container")
        video(id="their-video" autoplay)
        video(id="my-video" muted="true" autoplay)
    div(id="button-container")
        p id:
            span(id="my-id")
        button(id="end" type="button")
            a(href="/") end call

roomScript.jsはpeer idをDBに登録する部分以外はskywayチュートリアルとほとんど変わらないので登録部分だけ。

javascripts/roomScript.js
peer.on('open', function(){
    $('#my-id').text(peer.id);
    $.ajax({
        url: '/add/' + peer.id,
        type: 'GET',
        dataType: 'json',
        timeout: 3000,
    })
    .done(function(data) {
    })
    .fail(function () {
        console.error('ajax error');
    })
});

で、addをルーターに追加します。

routes/add.js
var express = require('express');
var router = express.Router();

router.get('/:skyway_id', function(req, res, next) {
    const { Client } = require('pg');
    const client = new Client({
        user: 'user',
        host: 'host',
        database: 'database',
        password: 'password',
        port: 5432,
        ssl: true
    });
    client.connect();
    client.query('INSERT INTO skyway_ids(skyway_id) VALUES($1)', [req.params['skyway_id']])
        .then(res => {
            client.end();
        })
        .catch(e => console.error(e.stack));
    res.send('success');
});

module.exports = router;

どこ書けばいいのかわからなかったのでとりあえずここに書きましたが、
ルーターにDB処理書くのはどうなんだろう・・・?

次は検索画面です。

search.pug
extends layout

block content
    table
        each id in ids
            -var url = '/room/' + id.skyway_id
            tr
                td= id.skyway_id
                td
                    button: a(href=url) 接続
routes/search.js
var express = require('express');
var router = express.Router();


router.get('/', function(req, res, next) {
    const { Client } = require('pg');
    const client = new Client({
        user: 'user',
        host: 'host',
        database: 'database',
        password: 'password',
        port: 5432,
        ssl: true
    });
    client.connect();
    client.query('SELECT skyway_id FROM skyway_ids')
        .then(result => {
            res.render('search', { title: 'WebRTCTest', ids: result.rows });
            client.end();
        })
        .catch(e => console.error(e.stack));

});

module.exports = router;

これで全画面終わりですが、searchから接続するときに/room/:skyway_idで接続するようにしたのでその部分をroomに追加しておきます。

room.pug
//追記
    unless id == undefined
        script(type='text/javascript').
            peer.on('open', function () {
                const skyway_id = '#{id}';
                const call = peer.call(skyway_id, localStream);
                setupCallEventHandlers(call);
            });
routes/room.js
//追記
router.get('/:skyway_id', function (req, res, next) {
    res.render('room', { title: 'WebRTCTest', id: req.params['skyway_id']});
});

これでとりあえずビデオ通話ができるようにはなりました!IDが削除されずに永遠に検索欄に表示されてますが・・・。
最終的に画面遷移は下のような感じに。
skyway10.JPG

3.Unityでアバターを動かす

なんとかWeb側が動くようになったのでUnity側をいじっていきます。
ということでアバターのセットアップなんですが・・・全部書いてくれている方がいるので特に書くことがないです。
(そろそろバーチャルモーションキャプチャーも使ってみたい・・・)

UniVRM+SteamVR+Final IKで始めるVTuber

もちろん今回はVTuberになることが目的ではないので結構色々と省いていますが・・・。
(リップシンクもまばたきも入れてないし表情も設定してない)
そんなわけでさくっとセットアップはおわりました。
20181212191217_1.jpg

4.ブラウザとVRでWebRTC

やっと本題です。
本題までが長かった・・・・・・。
とりあえずブラウザでできればなんかVR内でもいけるんじゃない?くらいのかるーい気持ちでいたんですがどうやらそういうわけにもいかなさそう。
色々と調べてみると・・・ありました。

SkyWay WebRTC GWを利用してUnityでWebRTC Videoをレンダリングする
中身解説 -SkyWay WebRTC GWを利用してUnityでWebRTC Videoをレンダリングする-

やりたかったことそのまんまですやん。
とはいえ素人の私では読んでてさっぱりなので一歩ずつ追っていこうと思います。

WebRTCGatewayを触ってみる

・skyway-webrtc-gatewayのDL

これはそのままですね。

skyway-webrtc-gateway

Windowsなのでgateway_windows.exeをDLします。
今のところこれが何なのかわかってませんが。

・mrayGStreamerUnityの準備

とりあえず下記からDLしておきます。

mrayGStreamerUnity

gstreamer 1.0 runtimes 64bit をDL&インストールしなさいとのことなのでします。
1.11.xと書いてあったのでとりあえず今回は1.11.91をインストールしました。
VS runtimes はもうあるのでとばして・・・
インストールしたgstreamerのbinフォルダにPATHを通しておきます。
これで準備はOK・・・かな?

・サンプルを動かしてみる

せっかくサンプルがあるのでとりあえず動かしてみます。

SkyWay-WebRTC-GW-Unity-Sample

が、WebRTCGWがエラーを吐いて落ちてしまいました・・・・・・。

#
# Fatal error in c:\dev\webrtc\src\webrtc\media\engine\webrtcvoiceengine.cc, line 264
# last system error: 0
# Check failed: 0 == voe_wrapper_->base()->Init(adm_.get(), nullptr, decoder_factory_) (0 vs. -1)
#
#

調べてみたところオーディオ系のなにかっぽいことまではわかりましたが詳しいことはよくわからず・・・。

・オーディオドライバ系の再インストール → ダメ
・既定のサウンドデバイスを色々と変えてみる → ダメ

わからんーーーーーとここで2時間くらい悩んでたところで原因らしきものを発見。
仮想オーディオデバイス君・・・?君か・・・?
CableInputとかVoiceMeeterBananaとかを無効にすると・・・
skyway11.JPG
やったぜ。
壁を映してるのでなんかそういうテクスチャみたいになってますが無事に動きました。

実際につかってみる

・準備

Assets/PluginsフォルダにダウンロードしておいたmrayGStreamerUnityのUnity\UnityTests\Assetsフォルダ内にあるGStreamerUnityフォルダと、Plugins\x86_64フォルダを突っ込む。
次にAsset StoreからUniRxもインポート。
skyway13.JPG
Pluginsフォルダ内にMiniJSONフォルダを作成して下記からDLしたファイルをつっこむ。

MiniJSON.cs
skyway14.JPG

見た感じ必要そうなのはこれくらいかな・・・?

・ブラウザと通信する

とりあえず鏡の位置をちょっと横にずらしておきます。
(鏡があるとこに相手側の映像を映したいので)
ぐりぐり動かしてPosition(X:3.08,Y:1.09,Z:-0.6)、Rotation(X:0,Y:120,Z:0)としました。
skyway15.JPG

映像表示用のPlaneを作ります。
sceneない右クリック→3D Object→Planeを選択。
ぐりぐりと動かしていい感じの場所に持っていきます。
大体こんなとこで。
skyway20.JPG

位置が決まったところでGstreamerのコンポーネントを突っ込みます。
GstreamerUnity\Components内にあるCustomPipelinePlayerをPlaneのInspector内にD&Dで突っ込みます。
Target Material入力欄横の◎をクリックしてDefault-Materialを選択、Pipelineはサンプル通りに以下のものを入力します。

udpsrc port=7000 caps="application/x-rtp,paylocad=(int)100" ! rtpjitterbuffer ! rtph264depay ! avdec_h264 output-corrupt=false ! videoconvert ! appsink name=videoSink

次にskyway周りのスクリプトです。
Assets内にScriptsフォルダを作成し、サンプルのSkyWayRestApiをつっこみます。
今回は入力欄やボタンがない(VRでそのあたりの実装をどうするのかよくわからなかった)のですこしその辺をシンプルにします。

//Unityから直接叩かれるクラス
public class SkyWayRestApi : MonoBehaviour
{
    public string targetId;
    public string key;
    public string domain;
    public string peerId;
    public bool turn;

    void Start()
    {
        //SkyWay WebRTC Gateway操作用インスタンス生成
        var _restApi = new RestApi(key, domain, peerId, turn);

        //SkyWayサーバと接続されたときに発火させるイベント
        _restApi.OnOpen += () =>
        {
            _restApi.Call(targetId);
        };
    }

    void Update()
    {
    }
}

最初から相手のID指定しておいていきなり接続する形です。
で、これをまたPlaneにD&Dで突っ込み、IdやKeyを編集します。
skyway22.JPG
これで実行してみます。
20181216160942_1.jpg
ブラウザ側の映像をVRに流し込むことはできました。

てことであとはVR側の映像の送信だけなんですが。
さっぱりわからず時間の関係もあり断念・・・。
gstreamerで流し込むのは何となくわかりましたがどこからカメラの映像とってくるのかいまいちわからず・・・。

5.おわりに

色々触りましたが中途半端に全部よくわからんって感じで終わってしまった感じがありますね・・・。
heroku使う必要も特になかったし!
何もわからない状態から扱うには少しハードルが高すぎたかなという感じだったので、
Unityのお勉強ちゃんとしてからまたこのあたりリベンジしていきたいですね。

明日は@somu4さんです!

51
28
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
51
28