前回の記事に続いてSkyWayでの実装です。
今回作成するのはミーティングアプリです。
2台以上の参加でミーティングが開始され以下のローカルカメラ画面の下に相手の画面が次々と出ていく作りになっています。
SkyWayでミーティングアプリの実装
こちらが実装のコードになります。
前回と同様App.jsから呼び出したContent.jsの1ファイルだけになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
// 'use strict'; import React, { Component } from 'react'; import { useState, useRef } from 'react' import Peer, { SfuRoom } from 'skyway-js'; const peer = new Peer({ key: "set your key", debug: 3, }); export default class Content extends React.Component { constructor(props) { super(props); } handleRef = (video) => { this.localVideo = video; }; render() { let localStream; // カメラ映像取得 navigator.mediaDevices.getUserMedia({video: true, audio: true}) .then( stream => { // // 成功時にvideo要素にカメラ映像をセットし、再生 console.log(this.localVideo) this.localVideo.srcObject = stream; this.localVideo.play(); // 着信時に相手にカメラ映像を返せるように、グローバル変数に保存しておく localStream = stream; }).catch( error => { // 失敗時にはエラーログを出力 console.error('mediaDevice.getUserMedia() error:', error); return; }); const handleCall = () => { const mediaConnection = peer.joinRoom("testRoomfejwaoeawij", {mode: 'sfu', stream: localStream}); console.log("click"); setEventListener(mediaConnection); }; // イベントリスナを設置する関数 const setEventListener = mediaConnection => { const leaveTrigger = document.getElementById('leave-trigger'); const messages = document.getElementById('messages'); const remoteVideos = document.getElementById('remote-streams'); mediaConnection.once('open', () => { messages.textContent += '=== meetingに参加しました ===\n'; }) mediaConnection.on('peerJoin', peerId => { messages.textContent += `=== ${peerId} が参加しました ===\n`; }); mediaConnection.on('stream', stream => { const newVideo = document.createElement('video'); newVideo.srcObject = stream; newVideo.playsInline = true; newVideo.setAttribute('data-peer-id', stream.peerId); remoteVideos.append(newVideo); newVideo.play() }); // for closing room members mediaConnection.on('peerLeave', peerId => { const remoteVideo = remoteVideos.querySelector( `[data-peer-id="${peerId}"]` ); remoteVideo.srcObject.getTracks().forEach(track => track.stop()); remoteVideo.srcObject = null; remoteVideo.remove(); messages.textContent += `=== ${peerId} が退室しました ===\n`; }); // for closing myself mediaConnection.once('close', () => { // sendTrigger.removeEventListener('click', onClickSend); messages.textContent += '== meetingから退室しました ===\n'; Array.from(remoteVideos.children).forEach(remoteVideo => { remoteVideo.srcObject.getTracks().forEach(track => track.stop()); remoteVideo.srcObject = null; remoteVideo.remove(); }); }); leaveTrigger.addEventListener('click', () => mediaConnection.close(), { once: true }); } return ( <div> <div> <pre className="messages" id="messages"></pre> <button id="make-call" onClick={handleCall}>meeting参加</button> <button id="leave-trigger">退室</button> </div> <video ref={this.handleRef} width="400px" autoPlay muted playsInline></video> <div className="remote-streams" id="remote-streams"></div> </div> ); } } |
相手が退室した(画面を落とす)時の動作なども入っており、その場合は相手方のカメラの画面も無くなります。
公式のサンプルではチャット機能などもありましたが、なるべくシンプルにするためミーティングだけの機能にしました。
実装の方法も様々あると思いますがかなりシンプルに実装ができましたので組み合わせでアプリなどの作成が容易になります。
今回はここまでで。
このブログは株式会社CoLabMixによる技術ブログです。
GCP、AWSなどでのインフラ構築・運用や、クローリング・分析・検索などを主体とした開発を行なっています。
Ruby on RailsやDjango、Pythonなどの開発依頼などお気軽にお声がけください。
開発パートナーを増やしたいという企業と積極的に繋がっていきたいです。