iOS/Android で HTML5 の audio/video を任意のタイミングで再生する方法

HTML5 で追加された audio/video 属性によって、プラグインレスで動画や音声の再生が可能になったことはよく知られていますが、モバイル(スマホ)で、その再生タイミングの制約が厳しいことは案外知られていません。これは以前、当Blogでも HTML5 x Touch UI の UXを考える(補足) の User action event restrictions でもチラッと書きました。またしてもこの制約に苦しめられながら、なんとか解決策を見いだしたので、GW最中にも関わらず久しぶりのエントリーです。

まず、本家 Apple のドキュメントにも以下のようにあります。
iOS-Specific Considerations

In Safari on iOS (for all devices, including iPad), where the user may be on a cellular network and be charged per data unit, preload and autoplay are disabled. No data is loaded until the user initiates it. This means the JavaScript play() and load() methods are also inactive until the user initiates playback, unless the play() or load() method is triggered by user action. In other words, a user-initiated Play button works, but an onLoad="play()" event does not.

(超意訳)

iOS の Safari では、ユーザーが携帯網で重量課金されるかもしれないから、preload と autoplay は効かないよ。ユーザーが何かアクションしないと、JavaScript での play() や load() もダメ。つまり、再生ボタンを押して play() を呼べるけど、onload で play() しても無効だから。

このユーザーアクションというのが結構厳しい。touch/mouseイベントの直接のハンドラ内でしか効きません。setTimeoutなどのタイマーを挟むともうアウトです。

例えば、以下のようなコードは再生できます。http://jsfiddle.net/dsuket/jTh3v/

var playBtn = document.getElementById('play');
playBtn.addEventListener('click', function() {
    audio.play();
}, false);

しかし、これにタイマー挟むと再生されません。 http://jsfiddle.net/dsuket/Ny9Xz/ (PCブラウザでは再生できます)

var playBtn = document.getElementById('play');
playBtn.addEventListener('click', function() {
    setTimeout(function(){
        audio.play();
    }, 2000);
}, false);

これは setTimeout だけじゃなく、例えば transitionEnd などのアニメーション関連のイベントなどもダメです。そうすると、アニメーション終了時のタイミングで音声を再生したい場合に非常に困る。

そこで、以下のようにすると再生できます。http://jsfiddle.net/dsuket/rWpM7/

var playBtn = document.getElementById('play');
playBtn.addEventListener('click', function() {
    audio.load();
    setTimeout(function(){
        audio.play();
    }, 2000);
}, false);

ポイントは、setTimeout の前に load() することです。play() メソッドは内部で自動的に load が呼ばれるのですが、タイマーハンドラの中ではこの load が抑止されているようです。そこで、ユーザーイベントが発生した段階ですぐに load() だけ呼び出し、後は任意のタイミングで play() すればOK!

なお、ドキュメントonload のタイミングで自動再生したい、というのはやはりできません。そこで、少し画面フローを工夫して、最初にスプラッシュ画面を用意し、タップしてもらうようにします。そのタップイベントハンドラの中で、オーディオファイルをロードします。HTML5 Webアプリでよく使われる Single Page Applicationの場合、ここで必要なオーディオファイルを全部ロードしてしまうと良さそうです。そうすれば後は好きなタイミングで音声を再生できます。

audioについての説明でしたが、videoも同様の問題があります。検証できてませんが、同じようにすれば対応できそうです。

ただ、audio/videoの自動再生や再生タイミングはOSバージョンでコロコロかわります。そもそも iOS4.1、Android 2.3 までは setTimeout の中でも再生できました。それが iOS 5、Andoroid 4 あたりから再生できなくなりました。バージョンが違えばこの load を切り離す方法もまた使えなくなるかもしれません。十分検証には気をつけてください。


参考: