HTML5 Videoの対話的実行ツールを作ってみた。

新しいことを覚えて自分のものにする最も確実な方法は、自分の手を動かして実際に確かめることと思っています。新しいプログラミング言語を覚えるときも、ただ単に本を読んでいるだけではなかなか身につきませんが、実際にキーボードを叩いて結果を確認するだけでも、すごく身近に感じられるようになります。恐らく、実行してすぐ結果を確認できるということが学習効果を高めるのでしょう。

今回、HTML5のビデオ要素について、対話的にJavaScriptコマンドを実行して結果を確認できるようなものを作ってみました。画面イメージは下のようなものです。左側にJavaScriptのコードを入力できるshellもどきの領域があり、右側にビデオとその下にいくつかのボタンがあります。

JavaScriptの入力領域はbashライクなコマンド編集機能をつけました。キーバインドemacsと同じです。Control-A,B,D,E,F,H,J,K,L,N,P,T,U,W,Yが使えます。ブラウザによってはpreventDefault()してもControlキーがそちらに取られてしまうものもあるようです。例えば、ChromeではC-nで別ウィンドウが開いてしまいます。その場合には、代わりに方向キーのDownが使えます。

入力した文字はJavaScriptでeval()され、結果が表示されます。videoという変数に右側に表示しているvideoオブジェクトがバインドされています。videoをいちいち入力しなくても済むように with (video) { eval(input) } がトップレベルになります。

ビデオの再生中の実行例を示します。

js> currentTime                                 // 現在の再生時刻
17.684701
js> pause()                                     // 一時停止
undefined
js> muted = true                                // 音声ミュート
true
js> currentTime = 30                            // 30秒の位置へジャンプ
30
js> src = "http://server/video.ogg"             // 指定したURLのビデオを再生
http://server/video.ogg

上記ではビデオ要素から投げられてくるイベントのログは省略しましたが、イベントも表示されます。

js> currentTime=20
20
Event(13:26:56.322): seeking
Event(13:26:56.894): suspend
Event(13:26:56.959): seeked
js>

currentTimeを20に設定して、先頭から20秒の位置にシークしようとすると、シーク開始にseekingが、シーク終了後にseekedイベントが発行されます。Eventの右の括弧の中の文字はイベント発生のtimeStampです(Firefoxは値がちょっと変)。ネットワークが非常の遅かったりすると、途中にsuspendイベントが複数発行されるようです。


イベントはW3Cでは以下のものが定義されています。一応、これらのイベントは全部拾えるようにしています。

abort              canplay          canplaythrough    cuechange
durationchange     emptied          ended             enter
error              exit             load              loadeddata
loadedmetadata     loadstart        pause             play
playing            progress         ratechange        seeked
seeking            stalled          suspend           timeupdate
volumechange       waiting 

このうち、progressとtimeupdateは再生中に頻繁に発生するため、shell領域には表示しないで、右側の再生時刻表示と、Progressカウンタに表示しています。

ビデオのsrc属性にURLをセットすると、別のビデオを再生することができますが、そのときには以下のようなイベントが発生することがわかります。どのイベントがどのような順番で発生するかはブラウザやビデオのコンテナ/コーデックによって多少異なるようです。

js> src="http://some.server/any/video.webm"
http://some.server/any/video.webm
Event(23:56:02.238): abort
Event(23:56:02.247): emptied
Event(23:56:02.251): loadstart
Event(23:56:02.372): durationchange
Event(23:56:02.372): loadedmetadata
Event(23:56:02.372): loadeddata
Event(23:56:02.372): canplay
Event(23:56:02.372): canplaythrough
Event(23:56:02.372): play
Event(23:56:02.372): playing
js>

一方、load, enter, exit, cuechangeはTextTrack(字幕など)に関するイベントですが、実装しているブラウザはまだないようです。

右下のplay(), pause()などのボタンは、コマンド入力と同じことをしているだけのものです。step()はコマ送りに近い動作をするもので、以下の関数です。

function step(tm) {
  tm = tm || 0.3;
  video.pause()
  var curr = video.currentTime + tm;
  video.currentTime = curr;
  return curr;
}

つまり、ポーズして引数のtm秒(デフォルトは0.3)だけ進めます。

Fast/Slow/Normalはそれぞれ、10倍速、0.3倍速、通常再生になります。playbackRate属性にそれぞれの値を代入することで実現できます。ただし、Chrome, Firefox, Operaで確認したところ、実際に再生速度が変わったのはChromeだけでした。

また、videoオブジェクトのプロパティの値を全て表示するdump()という関数も組み込んであります。この出力結果を見ると分かりますが、playedやseekableなどのTimeRangeが実装されているのもChromeだけでした。

どのフォーマットに対応しているかの確認も対話的にできるので便利です。以下はFirefoxの例です。

js> canPlayType("video/webm")
probably                                       // WebMは再生できる
js> canPlayType("video/ogg")
maybe                             // Oggは再生できるかも
js> canPlayType("video/ogg;codecs=theora")
probably                                       // OggでコーデックがTheoraなら再生できる
js> canPlayType("video/mp4")
                                               // MP4は再生できない
js>


実際の動作とソースコードはこちらからどうぞ。

キャプチャなどセキュリティに関するAPIは使っていないので、HTMLファイルとJavaScriptファイルをローカルにダウンロードして適当にビデオファイルを用意すれば、サーバを使わなくてもfile://で利用できます。ボタン[Video 1]〜[Video 5]に対するビデオのURLはJavaScript中で_$video1〜_$video5の変数に代入しているので、そこを変更すればお好みのビデオで動作を確認することができます。