iPhone Safariで動画をインライン再生する方法への反応の中に「音声は?」との声がちらほらありました。
スマホは常にサイレントモードにしてて殆ど音出さないのですっかり頭から抜けていましたが、実験済みでしたので今回は音声付きでインライン再生する方法の話をば。
##音声をAudio APIを使って同時に再生
最初に思いつくのは動画からDemuxして分離した音声をAudio APIを使って同時に再生する方法でしょう。
実際これで足りる場合もあります。が、足りない場合もあります。
##VideoとAudioに同時に再生命令を送っても、同じ速さで再生されるかは保証されない
昔々にAviUTLだのTMPGEncだのでエンコしてた人なんかはわかると思いますが、分離された音声と映像は結構かんたんにズレます。
まして今回はVideoをまともに再生していないわけで。
ですからナレーションとBGMのみのCMなんかは違和感もないでしょうが、人物が喋っていたりすると口パクがズレる可能性が高くなります。
##音声を映像に同期する
通常、音声と映像を同期させる場合、映像を見ながら音声を映像に合わせる必要がありますが、今回の場合はそもそもVideoは再生せずにcurrentTimeを動かしていますので、普通にAudioを再生して
video.currentTime = audio.currentTime
とすれば同期が取れます。かんたん!
##さらにシンプルに
ところで、規格がそうなっているのかはわかりませんが、Audio APIはソースに動画ファイルを入れると音声だけ再生されます。
つまり、
動画を音声として再生してフレームレートごとにaudioのcurrentTimeをvideoに代入すればいい!
##Sample Code
(function(){
var btn = document.querySelector('button');
btn.disabled = true;
var audio = new Audio();
var video = document.createElement('video');
video.style.display = 'none';
document.body.appendChild(video);
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var togglePlay;
var ua = navigator.userAgent;
if(/(iPhone|iPod)/.test(ua)) { // iPadもVideoはインライン再生可能である
ctx.scale(0.5,0.5);
var prms1 = new Promise(function(resolve, reject) {
video.addEventListener('canplay',function(){
resolve();
});
video.addEventListener('error',function(){
reject();
alert('failed loading video');
});
});
var prms2 = new Promise(function(resolve, reject) {
audio.addEventListener('canplay',function(){
resolve();
});
audio.addEventListener('error',function(){
reject();
alert('failed loading audio');
});
});
Promise.all([prms1,prms2]).then(function(){
btn.disabled = false;
});
video.src = 'movie.mp4';
video.load();
audio.src = 'movie.mp4'; // 動画ファイルを突っ込んでも再生される
audio.load();
togglePlay = function(){
if(audio.paused){
audio.play();
(function loop(){
video.currentTime = audio.currentTime;
ctx.drawImage(video, 0, 0, 640, 360);
if(!audio.paused){
requestAnimationFrame( loop );
}
})();
} else {
audio.pause();
}
};
} else { // AndroidとかiPadなら素直にVideoタグで再生
canvas.parentNode.insertBefore(video,canvas);
video.style.display = 'block';
video.addEventListener('canplay',function(){
btn.disabled = false;
});
video.src = 'movie.mp4';
video.load();
canvas.parentNode.removeChild(canvas);
togglePlay = function(){
if(video.paused){
video.play();
} else {
video.pause();
}
};
}
btn.addEventListener('click',togglePlay);
})();
##DEMO
http://jsrun.it/hadakadenkyu/aYPw
##補足
- タップ無しで再生出来ません。つまり自動再生が出来ません。
- WebGLでもイケます。
- 音が要らなくても寧ろvolumeを0にしてこっちでやった方がVideoの代わりにAudioを操作するだけなので筋が良いかもしれない。