最近またLocal Player (Chrome Player)を実装しなおしています.
一応説明しておくと, Local Playerは, 完全にローカルで動作する音楽プレイヤーです.
シンプルさを求め(実装がめんどくさいだけ), 操作しやすく(これは大事), 良い感じのプレイヤーです.
半年前に, ソースコードがスパゲッティになって, 開発を中断していましたが, 最近また書きなおし始めたのです.
音楽プレイヤーをブラウザー上で実装するのには, 音楽ファイルをJavaScriptで読み込まなければなりません.
そこでHTML5ですよ!!!
<audio src="url/to/musicfile.mp3" type="audio/mp3" />
みたいな感じで, 音楽を再生できます.
詳細は他のページに譲ります.
さて, 音楽ファイルを再生するには, ローカルファイルからurlをaudio要素のsrcにファイルをぶち込まなければならないわけですが, どうすればいいでしょう.
まぁ素直な実装は, File APIのFileReaderでreadAsDataURLを使うことでしょう.
実際, Local Playerは今までこの方法をとっていました.
だいたいこんな感じの実装です
// file は<input>から入ってきたやつ var reader = new FileReader(); reader.onerror = function(e) { console.dir(e); }; reader.onload = function(e) { var audio = new Audio(e.target.result); audio.volume = 0.5; audio.addEventListener('ended', function() { delete audio; next_music(); }); audio.play(); }; reader.readAsDataURL(file);
極めて普通ですね.
教科書そのまんまって感じです.
deleteはいらないと思いますが, e.target.resultが馬鹿でかい文字列となっており, メモリーがやばいことになります.
この実装を用いたLocal Playerで, 音楽を再生して, 10秒再生したらすぐ次のファイルへとスキップする, という動作を繰り返した時, CPU使用率とメモリー使用率は次のようになります.
曲を開始した瞬間, 5MBの音楽ファイルをreadAsDataURLして, メモリーが一時的に上がります.
大体, 山の大きさがメモリーの5%くらい, つまり, およそ100MBの文字列がJavaScriptの中でできては消え... を繰り返してるのです.
こんなんだと, 1秒ごとにスキップして行ったらどんどんメモリーは食うはCPUはしんどいわ...
しかも, 「曲をスキップする」というのは音楽プレイヤーとしては, よくあることなのです.
で, 一年間ほど悩んでいました.
今日, 神様のお告げが
ξ*‘ ー‘)<( それcreateObjectURLでデキルヨ )
え...???
どう違うの???
取り敢えず実装してみよう...
var createObjectURL = window.URL && window.URL.createObjectURL ? function(file) { return window.URL.createObjectURL(file); } : window.webkitURL && window.webkitURL.createObjectURL ? function(file) { return window.webkitURL.createObjectURL(file); } : undefined; if (!createObjectURL) return; var audio = new Audio(createObjectURL(file)); audio.volume = 0.5; audio.addEventListener('ended', function() { delete audio; next_music(); }); audio.play();
だいたいこんな感じの実装
はい, とってもとーっても簡単ですね (にこにこ
取り敢えずさっきと同じ条件で, 音楽を読み込んで次々とスキップしてみます
.......
いやいやいやいや...
メモリー...一定じゃん...
これ, ブログ見てる人には分からないでしょうが, ちゃんと音楽も聞こえてますし, スキップしてますし...
分かりやすいように, 音楽を再生する瞬間にCPUにワザと負荷を与えるような感じのコードを入れてみます.
for (var i = 0; i < 2e4; i++) { console.log(i); };
これで, さっきと同じ条件で行ってみます.
ちゃんと音楽流れてるんですよ? ホントに. ホントです.
メモリー全然食ってないじゃん...\ヤベェ/
まとめ
createObjectURLは凄い*1
何が凄いって, 全然ふっつーの関数呼び出しで書いてるのに, 裏ではurl先のローカルファイルをstream(?)的に読み込んでる.
あ, いや, 読み込んでるのはaudio elementの方なのかな...
これまでは, readAsDataURLで一気に読み込んでいたため, 50MBある1時間超のファイルを再生できませんでした.
それがcreateObjectURLによって解決されてしまったのです.
さらに, これまでできなかった動画再生も可能になります!!! ← なりました!!!
createObjectURL, どういう実装になってるんでしょう?
ちゃんと音楽や動画のシークもできるし, すごく不思議です.
不思議なのは, USB接続の外付けHDDなどにある音楽を, 再生して直ぐにUSBをぶっこぬいても, そのまま音楽が聞こえ続けることなんです.
フッ, またV8の闇に取り込まれそうになってしまったぜ...
追記(2012/11/21)
Local Playerはmanifest_version: 2の波についていけなくてStoreから削除されました.
*1:どうしよう, 凄いって思った次の日には, このURLが一時的なものだということに気がついて萎えた. みんなココで困ってる. 例えばHow to save the window.URL.createObjectURL() result for future use? createObjectURLに関するセキュリティとか勉強したい