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

SkyWayで後からstreamを送信しようとして詰まった際の対処

複雑な家庭の事情により、どうしても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イベント検知側に問題があるのかと思い試行錯誤したのですが、最終的に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,
      });

接続時は黒いブランク画面が共有されて、後で画面を取得したらちゃんと画面を共有できるようになりました。

カメラなしで画面だけ共有するアプリをつくっている人の記事が見当たらず困ったので、同じようなことをやろうとしている特異な人がいたら参考にしてもらえればと。

aktkro
スタートアップでよくプロダクトマネジメントをやってる者です。毎日個人開発でコードを書いたり、ニンテンドースイッチをしたり、firebaseの無料枠に感謝したりしながら生きてます。 https://twitter.com/aktkro
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
ユーザーは見つかりませんでした