【かみしば2】なんだかんだでiOS対応できました!
本当はもっとすぐにブログを書くつもりだったのですが、ボイスロイドゲームジャムがあって忙しかったためすっかり忘れておりました(;´Д`) ボイスロイドゲームジャムについてはまた後日詳しくまとめたいです。
これまでの開発
iOSネイティブアプリでは、OpenCVが正常に動作しない(iPod G5のマシンスペックが足りないせいか、他のせいなのかは不明)ので、もういっそのことサーバーで処理して、ブラウザアプリにしてしまえ!というモチベーションで開発してきました。
上記事まででは、AndroidおよびWindowsでは正しく再生されることが確認できたものの私の持っているiPod touch G5はiOS9であり、WebRTC(ブラウザ上でのカメラ利用)はiOS10~だったのです(´;ω;`) という訳で、まともなiOSデバイスをお持ちの方に協力を募集しておりました。
結果だけ言うと、多少修正は必要でしたが何とか動きました!
修正点を見ていきましょう。
iOSかどうかの切り分け
ユーザーエージェントを見ます。なんのブラウザかという情報が詰まっているユーザーデータです。
function checkIsIOS(): boolean{ return /iP(hone|(o|a)d)/.test(navigator.userAgent); }
iOS流、getUserMedia
一番肝を冷やされた部分です。Androidでは以下のようにしました。
// TypeScript // A navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia; let handle:CallPgdet = this; // B navigator.getUserMedia({ video: {width: 360, height: 480, frameRate: 2}, audio: false}, this.frameCallBack.bind(handle), console.log );
Aについて、 webkitGetUserMedia を確認しているので、iOS(Safari)対応もばっちり科のように見えます。ダメです。webkitGetUserMediaには「何やらおかしな使えないもの」が入っているようです。なのでこの部分はバッサリ消します。
Bについて、引数オブジェクト(MediaStreamConstraints)のvideoフィールドですが、カメラの向きを設定するfacingMode : { exact : "environment" }
を定義します。逆に、width/height/frameRateを定義するとうまく動いてくれません。
ちゃんとした原因も調べたいところですが手元にデバイスが無いので…
最終的にこのようになりました。
if(checkIsIOS()){ //alert("設定:iOS"); videoConfig = { facingMode : { exact : "environment" } }; }else{ //alert("設定:Android") navigator.getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || window.navigator.mozGetUserMedia; videoConfig = { width: 360, height: 480, frameRate: 2 }; } // ここでBindせずに this.frameCallBack を渡してしまう // とthisがイベントの発火元になってしまう let handle:CallPgdet = this; navigator.getUserMedia( <MediaStreamConstraints>{video: videoConfig, audio: false}, this.frameCallBack.bind(handle), (err) => alert(err) );
Audioの再生は制限アリ
これはiOS9(iPod touch G5)のSafariでもおなじなのですが、iOS Safariでは、HTML5のaudio/videoが自動でロード/再生できません!ユーザーの意思確認のない通信はダメということみたいですが、ユーザーによるアクション(タップ)のイベントのコールバック内で1度でも再生すれば後は好き勝手出来るみたいです。これじゃ制限の意味なくね?
Appleに文句を言ってもしょうがないので、受け入れます。
とは言え、幸いにもWEBアプリならユーザーは意識の外でタップしてくれます。なので、タップのイベントの初期化を全部してしまいます。
window.addEventListener("load",()=>{ document.addEventListener("click", ()=>{ if(callPgdet == null){ callPgdet = new CallPgdet(); } }); ... // コンストラクタの中 if(checkIsIOS()){ this.audio.load(); this.audio.play(); } ... // 音声切替部分 audio.pause(); audio.src = `/static/narrator/packages/${pack}/0.mp3`; audio.load(); ... // 再生部分 const HAVE_ENOUGH_DATA = 4; if(audio.readyState == HAVE_ENOUGH_DATA){ audio.pause() audio.currentTime = start; audio.play(); }
という訳で正式にリリースしまーす!
かみしば2
リリースしました!https://t.co/YsBrIaNUSR
— 金西ち18「かみしば」同人誌読上げアプリ (@kamishiba_ws) November 5, 2018
(まぁ未だに使ってみようと思ってくださる方は居ないかもですが…なんというか、口にしたことは履行したいじゃないですか。できなかったじゃ悔しいし)