SkyWay(WebRTC)を使ってスマホとPCでビデオ通話するサービスを作った時にハマった箇所のメモ
少し前に、iOS11のSafariがWebRTCに対応したので、iPhoneでのWebRTCの動作検証も兼ねて、前から作ってみようと思っていたSkyWay+GoogleMapのサービスを作ってみました。
パソコンで上記のページを開いて、QRコードをスマートフォンで読み込むと、パソコンとスマホでビデオ通話ができます。同時にスマートフォン側の位置が、GoogleMapで表示されます。現状は機能的にシンプルな感じですが、気まぐれに機能を拡張したいきたいと思っています。
地図表示は調べれば大量にサンプルがあるので問題無く作れましたが、iPhoneでビデオ通話する部分の実装は結構苦労しました。作っていてハマった箇所と、それに対して実施した対策、未解決の問題について書いていきたいと思います。
最初に
・GoogleMapについては詳しく書く必要は無いと思われるので省略します。
・私はWebRTCの深い部分の知識は無く、SkyWay頼みで作っている感じです。
検証端末
パソコン側
・Windows10 / Chrome 62
・MacOS 10 / Chrome 62 / Safari 11
スマホ / タブレット
・iPhone7(iOS 11.1.1 / Safari)
・Nexus9 (Android 7.1.1 / Chrome)
ハマった箇所1: 同一デバイスでの複数のMediaStream
Android(Chrome)の場合、一つのデバイスを複数のMediaStreamに利用できました。
iPhone(Safari)の場合:一つのデバイスを複数のMediaStreamに利用できない様子。
プレビュー用: MediaStreamConstraints = { video: true }
送信用: MediaStreamConstraints = { video: true, audio: true }
の2つのMediaStreamを作ってました。(そもそもこの方法が非効率?)
以下のように修正したところ、動作するようになりました。
MediaStreamConstraints = { video: true, audio: true } で1つだけ作成し
プレビューと送信用の両方に使用するようにしました。
(プレビュー用のVideoタグには、mutedを付けました。)
ハマった箇所2: MediaStreamConstraintsの指定
SkyWayJSの話になりますが、MeshRoomにJoinRoomする際に、Audioのみを指定していると音がでませんでした。
(今回の作ったサービスは1対1の接続なのでMeshRoom使わなくても良いのですが、楽したい為にMeshRoom使ってます。Peerで直接繋ぐと大丈夫かもしれません。)
対策として、使わなくてもVideoを指定する事にしました。
修正前:
パソコン側:MediaStreamConstraints = { audio: true }
スマホ側: MediaStreamConstraints = { video: true, audio: true }
修正後:
どちらも、 MediaStreamConstraints = { video: true, audio: true }
この件については、SkyWayの開発者コミュニティに同様の問題の報告が上がっています。MeshRoomやSFURoomに接続する時に VideoとAudioを使ってる人と、Audioだけの人が混在すると上手く動作しないケースがあるようです。
最初はスマートフォン側にはパソコン側の映像を表示しない方針で作っていたのですが、ダミーでvideoを送るくらいなら、映してしまおうと思い方針を変えました。
ハマった箇所3: iPhoneでの音量調整
iPhoneの場合、Video / Audioタグを使った場合、Volumeを変更しても音量が変わりませんでした。そのためiPhoneの場合は、WebAudioを使うようにしてみました。
但しWebAudioも、Android(Chrome)とiPhone(Safari)で挙動が違うようです。
Androidの場合はVideo/AudioタグのVolumeで音量調整ができていたので、Safariの場合のみWebAudioを使う実装にしました。
気になる点として、iPhoneの場合はハードウェアキーでの音量調整ができません。
画面上のスライドバーでのみ音量調整が可能な状態です。
追記)iOS11.2で、Video/Audioタグを使った場合でも
ハードウェアキーで音量調整ができようになったようです。
ハマった箇所4: TrackStartErrorの発生
Nexus9にて、getUserMedia 時にエラーが発生し、プレビュー画面が表示されないケースが頻発しました。確認してみると、MediaStreamError(name="TrackStartError")が発生していました。
エラーが発生している原因は不明なのですが、何度かリトライすると成功する感じなので、1秒毎にリトライ(5回まで)する事で対応しました。
デバックして確認すると1~2回のリトライで成功する感じでした。
ハマった箇所5: アプリ内ブラウザ(WebView)での挙動
LINE等のアプリから起動すると、標準ブラウザではなくアプリ内ブラウザ(WebView)で起動されます。その場合にgetUserMediaが動作しないケースがあるようです。
・iPhoneのLINEの場合、そもそも getUserMediaが無いというエラーが発生。
・AndroidのLINEの場合、権限が無いというエラーが発生。
※Androidの場合は起動できるアプリもあるようです。
SafariのWebViewは、getUserMediaは未実装らしいですし、Androidの場合は、各アプリ毎に権限の設定がされていてユーザー側からは変更できないと思います。
どうしようも無いので、getUserMediaに失敗した場合は
「標準ブラウザを起動してください」と表示するようにしました。
ハマった箇所6: iPhoneのフリーズ
iPhoneにてパソコン側から送られてきた映像をピンチアウト(2本の指での拡大)等をしてVideoタグがディスプレイの幅からはみでた場合に、iPhoneがフリーズする場合があります。(iOS側のバグと思われます)
この件については、SkyWayの公式サイトにも記述があります。
https://support.skyway.io/hc/ja/articles/236202427
作っているサービスは、PC側から送られる映像は小さく表示していたので、暫くこの問題に気がつきませんでした。
何度かフリーズさせてみた感じでは、PC側の映像の動かない状態で、左側にVideoタグがはみ出た場合に、フリーズする可能性が高いように感じました。仮想Webカメラ(MenyCam)を使ってテストする事が多いのですが、MenyCamの初期画面の場合にフリーズしやすい感じでした。
スマホがフリーズしてしまうのは流石に致命的な問題なので、暫定対応として拡大/縮小ができないようにしました。
この問題が完全に修正されるまで、怖くてサービスの公開はしづらい気がします。
修正されたとしても、iOSのバージョンチェックして、古いバージョンは弾くような実装にしないとまずい気もします。
追記)iOS11.2でフリーズする不具合が修正されたようです。
未解決の問題1: 端末ごとの対応Codec
以下の4パターンのうち、最後のパターンはPC側に映像が表示されませんでした。
1.PC側 chrome / Mobile側 iPhone ○
2.PC側 chrome / Mobile側 Nexus9 ○
3.PC側 safari / Mobile側 iPhone ○
4.PC側 safari / Mobile側 Nexus9 ×
これについてはコーデックの問題ではないか予想し、試しにMeshRoomの接続オプションで'H264'を指定してみたところ、パターン2とパターン4で表示されなくなりました。この事から、Nexus9がH264のエンコードに対応してないのでは?と思っています。(safariのWebRTCは、H264しかサポートしてないらしいです。)
裏付けをとりたいのですが、対応端末がH264のエンコードに対応しているかわかる資料や、確認する為の方法を見つけることができませんでした。可能であれば、JavaScript上でチェックできるようにしたいです。
追記:時雨堂のVさんからコメント頂きました。SDPで確認できるとの事です。
Vさんありがとうございました。
はじめまして。"対応端末がH264のエンコードに対応しているかわかる資料や、確認する為の方法を見つけることができませんでした。” こちらですがクライアント側で Offer を出したタイミングで SDP に H264 という文字列がなければ非対応と判断が可能です。参考になれば。
— V (@voluntas) 2017年11月21日
追記2:上記について確認してみました。
未解決の問題2: Safariでのデバイス名称取得について
マイクやカメラのデバイス名称の取得する為に、navigator.mediaDevicesを使用しているのですが、safariは未対応で名称取得できないようです。
https://developer.mozilla.org/ja/docs/Web/API/Navigator/mediaDevices
PC側で、仮想カメラ(MenyCam/CamTwist) や仮想マイク(NETDUETTO)を使いたいので、デバイス名称は表示したいんですよね。
未解決の問題3: アプリ切替時の映像表示
現状、スマホで別のアプリに切替えて戻ってきた時に、パソコン側の映像が消える場合があります。ただ数十秒待ってると表示されるようになります。
これについては、対策をこれから考えようと思います。
未解決の問題について
未解決の問題1と2については、PC側はChromeを使えば問題無いです。
Windowsユーザーの場合はSafariは利用できないですし、MacユーザーでもChromeは使えます。また、Safariは現状スクリーンシェアに対応していません。PCのスクリーンシェアをスマホに表示する事も考えているのでChromeの方が何かと都合が良いです。
上記のような理由で、PC側のブラウザはChrome推奨としています。
Safariでも使えなくは無いけれど機能制限あり。という方針で作ってます。
最後に
SafariがWebRTCに対応したのがつい最近です。WebRTC(及びSkyWayの)Safari対応の資料や記事がまだまだ少ない状況なのでけっこう苦労しました。また、もっと良い実装方法もあるように思いますし、Safari側の修正や、SkyWayのSafari対応で変わっていく部分もあると思います。
ただ、色々と試行錯誤した内容が残っているだけでも、これからWebRTC(SkyWay)を使ったスマホ向けサービスを作る人の参考になるかもしれないと思いましたので、書き残しておく事にしました。
何か気になる点がありましたら、コメントかTwitterの方に書いて頂ければと思います。