みかんのゆるふわ技術ブログ

Raspberry PiやIoT関係のことを書き残していきます

Janus + Vue.jsで自前のWebRTCフロントエンドを作ろう!~その2:画面作り編

前回に引き続き、JanusというオープンソースのWebRTCサーバーを使った動画ストリーミングの画面をVue.jsを使って作っていきます。

www.mikan-tech.net

前回は準備編ということで、Vue.jsプロジェクトを作成してJanusクライアントライブラリを組み込むところまでできました。 今回はいよいよ画面を作っていきましょう! 作った画面のソースコードは一番下に貼り付けています。

2020/10/01 編集するファイル名を明記など、若干説明を追加・修正しました。また、GitHubへのリンクも貼りました。

画面作り

本家Meetecho社が開発するJanusクライアントライブラリ(MITライセンス)は様々なAPIが用意されていますが、 今回は、最低限のもののみ使ってみます。 エラーハンドリングとかはあまり考慮しません。 例えば、本家のサンプルではブラウザーがWebRTCに対応しているかチェックして、非対応ならその旨表示するようになっていますが、 これから作るサンプルはそういうのを入れていません。実用にされる場合はご注意ください。

詳しい使い方やAPIリファレンスは、本家のドキュメントを参照してください。

janus.conf.meetecho.com

ここから先は、Vue.jsのプロジェクト作成で自動生成されたsrc/App.vueを編集…というか、全面書き換えしていきます。

Janusクライアントライブラリのimportと初期化

前回の方法で取り込んだ本家のJanusクライアントライブラリは、次のようにして使えます。

import { Janus } from 'janus-gateway'

これでJanusオブジェクトが取り込めました。 このJanusオブジェクトですが、他のAPIを呼び出す前に、まずJanus.init()を呼ぶ必要があります。

Janus.init({
  debug: true,
  dependencies: Janus.useDefaultDependencies(),
  callback: () => {
    // Now you can do "new Janus()"
  }
})

Janus.init()が完了しコールバックが呼ばれた後に、new Janus()でサーバへの接続を行いjanusインスタンスを作ります。

サーバへの接続

Janusゲートウェイサーバへの接続は、次のように行います。 例として、以前の記事でJanusゲートウェイをインストールしたRaspberry Piに接続します。

const janus = new Janus({
  'http://raspberrypi.local:8088/janus',
  success: () => {
    // You can attach a plugin here
  },
  error: (error) => {
    Janus.error('Failed to connect to janus server', error)
  },
  destroyed: () => {
    window.location.reload()
  }
})

このように、JanusゲートウェイのURLを指定してnew Janus()を行います。本来はコールバックがたくさん定義されていますが、ここでは3つのみ実装しました。

プラグインのアタッチ

先ほどのnew Janus()success()のコールバックが呼ばれると、Janusサーバへの接続が完了し、WebRTC通信ができるようになります。 Janusでは、ビデオチャットやストリーミングの機能をプラグインで提供しています。 今回はストリーミングのプラグインをアタッチします。プラグイン名はjanus.plugin.streamingです。以下のようにします。

this.janus.attach({
  plugin: "janus.plugin.streaming",
  opaqueId: 'thisisopaqueid',
  success: (plugin) => {
    // You can use plugin's functions here
  },
  error: (error) => {
    Janus.error('Error attaching plugin... ', error)
  },
  onmessage: (msg, jsep) => {
    // Please handle msg and jsep!
  },
  onremotestream: (stream) => {
    // You've got a remote stream
  },
  oncleanup: () => {
    // You should cleanup
  }
})

メッセージハンドリング

ストリーミングの開始・終了など様々な操作をJanusゲートウェイとのメッセージやイベントのやり取りで行います。 クライアントがメッセージを受信すると、先ほどのonmessage(msg, jsep)が呼ばれます。 ここに、ストリーミングプラグインとのやり取りを実装します。

例えば、本家のサンプルを参考に、最低限だけ抽出してみました。

onmessage: (msg, jsep) => {
  if (msg && msg.result) {
    Janus.log(msg.result)
  } else if (msg && msg.error) {
    Janus.error(msg.error)
  }
  if (jsep) {
    let stereo = (jsep.sdp.indexOf("stereo=1") !== -1);
    this.plugin.createAnswer({
      jsep: jsep,
      media: { audioSend: false, videoSend: false },
      customizeSdp: (jsep) => {
        if(stereo && jsep.sdp.indexOf("stereo=1") === -1) {
          jsep.sdp = jsep.sdp.replace("useinbandfec=1", "useinbandfec=1;stereo=1");
        }
      },
      success: (jsep) => {
        this.plugin.send({ message: { request: "start" }, jsep: jsep });
      },
      error: (error) => {
        Janus.error("WebRTC error", error);
      }
    })
  }
}

接続が完了し、ストリーミングの映像が送られてくると、 プラグインのアタッチの所に出てきたonremotestream(stream)が呼ばれます。 このstreamをHTML5のvideo要素で表示します。

スポンサーリンク

Vue.jsへの組み込み

Janusクライアント

私のサンプルでは、Vue.jsのmounted()を使って、Janus.init()を呼びました。

mounted() {
  Janus.init({
    debug: true,
    dependencies: Janus.useDefaultDependencies(),
    callback: () => {
      this.connect(JANUS_URL)
    }
  })
}

コールバックで、methodsに実装したconnect()を呼び出します。

methods: {
  connect(server) {
    this.janus = new Janus({
      server,
      success: () => {
        this.attachPlugin()
      },
      error: (error) => {
        this.onError('Failed to connect to janus server', error)
      },
      destroyed: () => {
        window.location.reload()
      }
    })
}

この続きで、methodsに実装したattachPlugin()を呼んで…とネストしていくように作りました。 単純な作りで、あまりかっこよくはないかもしれませんが…。

ビデオストリームの表示

HTML5のvideo要素にstreamをバインドするには、次のようにします。

<video autoplay :srcObject.prop="stream"></video>

ここで、streamはvue.jsのdataに含めておきます。

export default {
  data() {
    return {
      stream: null
    }
  }
}

このstreamに、Janusのonremotestream(stream)コールバックで渡されるstreamを入れてやれば、ストリームが再生できます。

ソースコード

最終的にsrc/App.vueは次のようになりました。

ソースコード全体はこちらに置きました。

github.com

動作確認

実際に動作させてみます。以前の記事で用意したRaspberry Piのストリーミングサーバーに繋いでみます。

www.mikan-tech.net

f:id:kimura_khs:20200827215737p:plain

このような画面になり、Rapberry PiのUSBカメラの映像を別のパソコンからブラウザで見ることができました。