今更ながら<video>, <audio>タグのtype属性の謎に迫る

HTML5で`<video>`タグや`<audio>`タグ、あるいは、複数種類のファイルを`<source>`タグで併記する時に使われるのが、`type`属性。HTMLでのマークアップだけであれば、例えば、

```html:type属性の例
<video>
  <source src="video.webm" type="video/webm">
  <source src="video.mp4" type="video/mp4">
</video>
```

のように書けば通常は問題なく使えたりします。

では、JavaScriptで扱うとき、例えば、`canPlayType()`メソッドでブラウザ側が対応しているコーデックの種類を判別する時は、同様にMIME Typeを指定すれば良いのかというと、必ずしもそうとは言い切れず、もう少し詳しい書き方が求められる場合も考えられます。

# 予備知識
##「ファイルフォーマット」と「コーデック」

動画や音声のデジタルデータを単に圧縮したり(エンコード)、再生時に元通りに復元したり(デコード)する方式(あるいはその装置)のことを「**コーデック**」と呼びます。(コーデック(codec)は、符号化するcoderと復号するdecoderを組み合わせた造語です。)

一方、一般的には、単にコーデックで圧縮してバイナリデータに変換するだけではなく、動画と音声のデータを一つのファイルに束ねたり、その他デコーダの初期化やシーク等に必要なデータを格納したりするために、コーデックが出力したバイナリデータをさらに体系化された共通フォーマットに格納して一つのファイルにまとめます。

「WebM (あるいはMatroska)」や「MP4 (あるいはISO BMFF)」と呼ばれるものはこの「**ファイルフォーマット**」を指します(専門用語としては「*システムフォーマット*」と呼ばれたりもします)。

言い換えれば、この「ファイルフォーマット」は一つの箱であり、その箱の中には様々な「コーデック」で圧縮された動画や音声のデータを入れることができるようになっているわけです。実際にも、例えば、WebMでは映像コーデックとしてVP8やVP9、音声コーデックとしてOgg VorbisやOpusを扱うことが出来ます。

## HTML5におけるメディアタイプの表記方法

これを踏まえて、HTML5での`<video>`, `<audio>`, `<source>`等のタグにおいて、`src`属性で指定されたメディアファイルがどのようなフォーマットなのかを明示するために用いる`type`属性では、ファイルフォーマットの種類を表すMIME Typeと、その中に格納されているメディアストリームのコーデックの両方を明記するように定められています。具体的には、

```html:WebMでのMIME&#32;TypeとcodecsのHTMLマークアップ例
<video src="video.webm" type="video/webm; codecs='vp8,vorbis'"></video>
```

```html:MP4でのMIME&#32;TypeとcodecsのHTMLマークアップ例
<video src="video.mp4" type="video/mp4; codecs='avc1.640029,mp4a.40.2'"></video>
```

のようにします。

さて、WebMは非常に明快なのでよいとして、MP4の場合、意味不明な数字や16進数が出てきて、どう扱って良いのか困ってしまうのではないでしょうか。

# type属性のcodecsの詳細に迫る
## まずは、身も蓋もない結論から

結論から先に出してしまうと、動画コーデックがH.264の場合は`codecs`に`avc1.640029`を、音声コーデックがAACの場合は`codecs`に`mp4a.40.2`を追加すれば、だいたい問題にならないようです。

## canPlayType()でコーデックの対応チェックを行う場合

HTMLで直接マークアップするのではなく、JavaScriptでDOM操作を行い、動的に動画や音声をHTMLに追加して再生する際に、ブラウザ側で対応しているコーデックをチェックするには、次のようにします。

```js:canPlayType()の例
var media = document.createElement("video");
if(media.canPlayType("video/mp4, codecs='avc1.640029'") == "probably") {
  // H.264対応ブラウザの場合の処理
}
else {
  // H.264非対応ブラウザの場合の処理
}
```

ここで、`canPlayType()`の戻り値は次のように定められています。

* (空文字列): ブラウザ側が非対応であることを示す
* **`maybe`**: 主に`codecs`が明記されていない場合等、指定されたタイプだけでは対応/非対応が曖昧である場合に、ファイルフォーマットとしては対応しているが、コーデックの対応までは必ずしも保証されないことを示す
* **`probably`**: ブラウザ側が確実に対応していることを示す

よって、MIME Typeだけを示すとmaybeとなるが、`codecs`を明記すると、非対応と示されたり、`maybe`や`probably`となったりする場合が有り得るわけです。

例えば、H.264が広く普及した今となってはほとんど使われなくなった、一世代前の動画コーデックであるMPEG-4 (Part 2)が再生可能かどうかを各ブラウザで試してみると、

```js:コーデック対応チェックの例(MPEG-4)
var video = document.createElement("video");
console.log(video.canPlayType("video/mp4; codecs='mp4v.20.240'"));
```

```js:Firefox(Mac)での結果
""
```

```js:Chrome(Mac)での結果
"maybe"
```

```js:Safari(Mac)での結果
"probably"
```

のようになったりします。

今回の例は少々古いコーデックですので実用上は参考にならないかもしれませんが、今後新しいコーデックが登場したりする場合にこのような手順が使えることを覚えておくと役に立つかもしれません。

## そろそろ小難しい話を

では、そろそろ`type`属性の`codecs='...'`の部分には具体的に何を書けばよいのかを少し詳しく書いてみます。

まず、WebMの場合は、`vp8`や`vp9`、`vorbis`、`opus`等を書くだけですので、非常にシンプルです。なお、WebMに限りませんが、動画と音声のコーデックの両方を書く場合はカンマ区切りで続けて書きます。

ややこしいのはMP4の場合です。

そもそも、`type`属性の書式は[HTML5のW3C勧告(2014/8/21時点では勧告候補)](http://www.w3.org/TR/html5/embedded-content-0.html#attr-source-type)で定義されていますが、具体的には、まずMIME Typeを記述し、`codecs='...'`の中に、[RFC 4281](http://tools.ietf.org/html/rfc4281)で定められた書式でコーデックを列挙することとなっています。

で、RFC 4281...ですが、現在は[RFC 6381](http://tools.ietf.org/html/rfc6381)で置き換わっています。こちらを読むと、MP4フォーマットの場合は、

```
[コーデック識別子(4文字)].[ObjectTypeIndication]
※ 16進数の場合、先頭の0xは省略する。
```

となっています。ここで、[コーデック識別子](http://www.mp4ra.org/codecs.html)と[ObjectTypeIndication (OTI)](http://www.mp4ra.org/object.html)は[MP4 Registration Authority (MP4RA)](http://www.mp4ra.org/)で規定されている値を使うことになっています。(このMP4RAのサイトがつながりにくくて困るのだけど...)

ただ、ここに書かれている種別は、H.264やAACといった一般的な呼称ではなく、ISO/IEC国際標準の文書番号で記載されているため、一般の方々には非常にわかりにくくなっています...

さらに、RFC 6381によると、

* MPEG-4 Audio(`mp4a`), Video(`mp4v`)の場合は、OTIの後ろに`.[1桁の数字]`を書いて、コーデックの詳細を示す
* H.264(`avc1`等)の場合は、OTIとしてH.264ストリームのある部分に含まれる6桁の16進数を書く

というように定められており、かなり細かくなっています。
で、ここから先を説明すると、さらにさらに非常に長くなってしまいますので、大幅に端折って説明します。

MP4で格納されるコーデックは、例えば音声であればAAC-LC (Low Complexity)やHE-AAC (High-Efficiency AAC)があったり、AAC以外のものもあったり、動画であればMPEG-4 (Part 2)やH.264があったり、さらには、コーデックの実装に柔軟性を持たせるため回路の複雑さや画面サイズの規模に応じてプロファイルやレベルといった定義があったりします。

従って、どこまでのプロファイルやレベルに対応するかのチェックを行えるように、このような細かいコーデックの記述法が定義されたわけですが...

現在のモダンブラウザで使う限りは、そこまで細かいチェックは行っておらず、実際にはコーデックのおおまかな種類(`avc1`(H.264)や`mp4a.40`(AAC)等)まで記述すれば十分だったりします。