複雑な家庭の事情により、どうしてもSkyWayをつかったWebRTC画面共有ツールをつくる必要があったため、詰まったことをメモ。
完全にSkyWay力技ネタなので、SkyWayのAPIリファレンスと見比べながら見てもらえると。
やろうとしたこと
- SkyWayで
- カメラを使用しない
- けれども画面だけ共有できるツール
をつくろうとしました。
自分の顔、相手の顔は見えなくていいのでただただ画面を共有したいというノリです。
これを実現するには
1.peer生成
↓
2.joinRoom(この時はstreamなし)
↓
3.後からgetDisplayMediaして画面をlocalStreamに追加
↓
4.replaceStreamでlocalStreamをルームのmediaStreamへ送信
↓
5.streamイベントを検知してmediaStreamを受信
という流れにすればいいんじゃないかと思い、↓のような雰囲気のコードを書きました。
結論として、これだとダメでした。
// Peer生成
const peer = new Peer({
key: this.skyway_apiKey,
debug: 3,
});
peer.on('open', (id) => {
this.localPeerId = id;
});
// sfuルームに参加
const room = peer.joinRoom(this.roomId, {
mode: 'sfu',
stream: this.localStream,
});
// 画面を取得してlocalStreamにセット
const myStream = navigator
.mediaDevices
.getDisplayMedia(
{ video: true }
)
this.localStream = await myStream
// mediaStreamを更新
room.replaceStream(this.localStream)
// streamイベントを検知した際の処理
room.on('stream', async stream => {
if ( typeof(stream) === 'object') {
const remoteVideo = document.createElement('video');
remoteVideo.srcObject = stream;
remoteVideo.playsInline = true;
remoteVideo.setAttribute('data-peer-id', stream.peerId);
videosContainer.append(remoteVideo);
await remoteVideo.play().catch(console.error);
} else {
console.log("streamなし");
}
});
※トリガーとか省略してるので本当に雰囲気だけのコードになってます
うまくいかなかったこと
【事象】streamを送信しても、mediaStreamを更新できない
上記のように実装して動かしてみたところ、4〜5を達成できず、画面共有ができませんでした。
で、4のreplaceStreamか、5のstreamイベント検知側に問題があるのかと思い試行錯誤したのですが、最終的に2のpeerJoinの段階で詰んでいたことが発覚。
【原因】ルームへの接続時にstreamが空だと"受信のみモード"で接続されてしまう説
リファレンスのPeer.callのparameters:streamの記載によると、
接続先のPeerへ送るメディアストリームです。 設定されていない場合は、受信のみモードで発信します。
とのこと。
sfuRoomの欄に記載があるわけではないのであくまで仮説でしかないんですが、
stream = ""
の状態でjoinRoomしてしまっていることがそもそもの失敗で、受信のみモードで参加しているからreplaceStreamがうまくいかなかった模様です。
解決するためにやったこと
事前にlocalStreamを準備してからjoinRoomをするようにすればいいのですが、カメラは使用したくないし、接続前にかならず画面を取得しないといけないのは使い勝手が悪い。
ならば"mediaStream形式の空データを作成して、それをstreamにセットしてしまえばいいんじゃないか"と思ったので、mediaStreamのモックデータを用意することにしました。
getMock() {
const mock = document.createElement('canvas');
this.localStream = mock.captureStream(10);
}
canvas要素を生成して、そのリアルタイムキャプチャをストリーミングとして取得する感じです。
で、localStreamが空だったり死んでたりする場合は、このモックデータを生成してからjoinRoomするように変更したところ、無事に成功。
if ((!this.localStream.active) || (this.localStream === "")){
this.getMock()
}
const room = peer.joinRoom(this.roomId, {
mode: 'sfu',
stream: this.localStream,
});
接続時は黒いブランク画面が共有されて、後で画面を取得したらちゃんと画面を共有できるようになりました。
カメラなしで画面だけ共有するアプリをつくっている人の記事が見当たらず困ったので、同じようなことをやろうとしている特異な人がいたら参考にしてもらえればと。