iOSのHTML5 Audio/VideoのcurrentTimeがおかしい
1年前に買ったSIMフリー版のiPhone6をauで使っていましたが、iijmioの「おうちでナンバーポータビリティ」でMNPしました。特に問題などなく使えています。4インチのを出して欲しいのでiPhone6sは見送りました。
前回、AndroidのHTML5 Audioのバグについて書きましたが、iOSのHTML5 Audio/Videoでも次の2つのバグを見つけました。
- currentTime に連続して同じ値を代入するとシークできない。
- 再生終了後、currentTime へ代入した直後に play() を呼んでも再生が始まらない。
今回は、これらの発生条件の詳細と回避方法について書きます。
なお、Audioを使っての説明となりますが、Videoでも同じです。
currentTime に連続して同じ値を代入するとシークできない
iOS8,iOS9で発生します。iOS7では発生しません。
1 2 3 4 5 6 7 8 9 |
[sourcecode lang="javascript" highlight="6,7"]var audio = new Audio("sample.m4a"); audio.play(); var seekButton = document.getElementById("seekButton"); seekButton.addEventListener(function() { audio.currentTime = 10; audio.currentTime = 10; }, false); [/sourcecode] |
普通、このように同じ値を連続して代入するコードを書くことはありませんが、複雑な処理を書いていると、少し離れた場所で同じ値を再度代入してしまうことが起きてしまうことはあると思います。
回避方法ですが、(1回目の)代入直後は seeking が true となっており、またそのとき currentTime の値を読み出すと代入した値がそのまま返ってくるようなので、(2回目の)代入直前に seeking と currentTime をチェックすればよさそうです。
1 2 3 4 5 6 7 |
[sourcecode lang="javascript" firstline="5" highlight="7"]seekButton.addEventListener(function() { audio.currentTime = 10; if (!audio.seeking || audio.currentTime !== 10) { audio.currentTime = 10; } }, false); [/sourcecode] |
なお、連続した代入でも値が異なれば大丈夫なようです。
1 2 3 4 5 |
[sourcecode lang="javascript" firstline="5" highlight="6,7"]seekButton.addEventListener(function() { audio.currentTime = 10; audio.currentTime = 10.000000000000002; // 倍精度浮動小数で10の次に大きい値 }, false); [/sourcecode] |
2回目の代入を setTimeout で行うのも問題ないようです。
1 2 3 4 5 6 7 |
[sourcecode lang="javascript" firstline="5" highlight="6,8"]seekButton.addEventListener(function() { audio.currentTime = 10; setTimeout(function() { audio.currentTime = 10; }, 0); }, false); [/sourcecode] |
再生終了後、currentTime へ代入した直後に play() を呼んでも再生が始まらない
ひとつ目と同様iOS8,iOS9で発生しiOS7では発生しませんが、こちらはMacのSafari9.0でも発生しました(Mac Safariの他のバージョンは未確認)。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
[sourcecode lang="javascript" highlight="7,8"]var audio = new Audio("sample.m4a"); audio.play(); var seekButton = document.getElementById("seekButton"); seekButton.addEventListener(function() { if (audio.ended) { audio.currentTime = 10; audio.play(); } else { alert("再生終了するまで待ってね"); } }, false); [/sourcecode] |
play() の呼び出しを setTimeout で行うと大丈夫なようです。
1 2 3 4 5 6 7 8 9 10 11 |
[sourcecode lang="text" firstline="5" highlight="7,9"]seekButton.addEventListener(function() { if (audio.ended) { audio.currentTime = 10; setTimeout(function() { audio.play(); }, 0); } else { alert("再生終了するまで待ってね"); } }, false); [/sourcecode] |
まとめ
これら2つのバグを見ると、currentTimeに代入した直後にさらに別の操作をすると何か問題を引き起こす可能性がありそうで、この2つだけでなく他にもバグがありそうな気がします。iOSのHTML5 Audio/VideoでのcurrentTimeの操作絡みで何か問題が起きた場合は疑ってみるとよいかもしれません。
※サンプルの音源は、著作権が切れたパブリックドメインのものを使用しています。
(ベートーヴェン ピアノソナタ第8番ハ短調「悲愴」 Op.13 第2楽章 Pヴィルヘルム・バックハウス 1958年録音)