動画の埋め込みで使う source 要素の codecs パラメータ付き type 属性って何者？ 「なにもわからない」から「チョットデキル」に

## はじめに : HTML 上での動画の扱い方のおさらい

HTML で動画を埋め込むには、`video` 要素を使用します。また、ブラウザが新しい動画形式 (あえてここでは曖昧な表現を使用します) に対応していないことを考慮するため、`video` 内に `source` 要素を使用して、複数の動画形式のリストを用意することができます。

ブラウザはそのリストから、`source` 要素の `type` 属性を参考にし、上から順番に「**デバイスで再生してもよい動画形式**」を選んで、そのファイルを再生しようとします。

※ここでいう「デバイスで再生してもよい動画形式」というのは、必ずしも「**最初に見つかったデバイスで再生できる動画形式**」が選ばれるわけではありません。例えば、再生可能であるものの GPU によるハードウェアデコードに対応していないファイルを (消費電力削減のため) スルーする、というようなブラウザが存在する可能性があります。

## codecs パラメーター付き type 属性とは

しかし、このような `type` 属性による動画形式の設定は、欠点があります。それは、「**設定できるのは『コンテナの種類』だけであり、『コーデックの種類』を指定することができない**」ということです。

`video/webm` とした場合、その動画ファイルが WebM コンテナであるということをブラウザに伝えることができます。しかし、実際にその WebM コンテナに格納されている映像や音声が、どのようなコーデックでエンコードされているかを知ることはできません。

```html
<source src="movie-av1.webm" type="video/webm">
<source src="movie-vp9.webm" type="video/webm">
```

このような AV1 と VP9 それぞれの WebM 動画ファイルが `source` で指定されているとするとします。AV1 コーデックに対応していないブラウザで、1 番目の動画を誤って選択してしまう可能性があります。

そこで役に立つのが、`type` 属性に追加する `codecs` パラメータです。`video/webm` のような MINE タイプに加え、`codecs` から始まる文字列を記述することで、**コンテナ内の映像・音声コーデックを指定することができます**。例えば、このように記述します。

```html
<source src="movie.webm" type="video/webm; codecs='vp9, vorbis'">
```

この場合、WebM コンテナの、VP9 コーデック、Vorbis コーデックの映像・音声を含んだ動画ファイルであるということになります。

この `codecs` の指定は、単なるコーデックの指定だけではなく、より詳細にプロファイルや色空間などの情報を設定することができます。

```html
<source src="movie.webm" type="video/webm; codecs='vp09.00.51.08'">
```

この場合、VP9 な映像が YUV420、レベル 5.1、色深度 8 bit であることを表現しています。う～ん、難しい！

ちなみに、オプショナルな値も存在します。VP9 の場合、全部盛りだと

```html
<source src="movie.webm" type="video/webm; codecs='vp09.00.51.08.01.01.01.01.00'">
```

といった文字列になります。これは分からない！

## 仕様を確認してみよう

`codecs` パラメーターの書き方は、[RFC 6381](https://datatracker.ietf.org/doc/html/rfc6381) でわかるようです。ただし、この RFC は当時主要だったコーデックでの書式のみ定義しているようなので、後発のコーデック用の定義はありません。こちらから [VP9](https://www.webmproject.org/vp9/mp4/#codecs-parameter-string) や [AV1](https://aomediacodec.github.io/av1-isobmff/#codecsparam) コーデックでの書式を確認することができます。

## チョットワカラナイ所

```html
<source src="movie.webm" type="video/webm; codecs='vp9'">
<source src="movie.webm" type="video/webm; codecs='vp09.00.51.08'">
```

このように、プロファイルレベルなどを詳しく記述する方法とそうでない方法の 2 つがありますが、疑問が残ります。シンプルな書き方では `vp9` ですが、詳しく書く書き方では `vp09.~~` と同じ VP9 でも書き方に差があります。

後者の `vp09.~~` という書き方は、先ほどリンクで紹介した [Codecs Parameter String](https://www.webmproject.org/vp9/mp4/#codecs-parameter-string) と同じ書き方ですし、特に問題はありません。では `vp9` という書き方はどこから来ているのでしょうか？ `vp09` ではダメなのでしょうか。

実際に `vp09` という指定がだめなのか確認してみましょう。デバイスでその動画ファイルが再生可能かを調べるには、`HTMLMediaElement.canPlayType()` メソッドを使用します。

```js
document.createElement('video').canPlayType('video/webm; codecs="vp9"');
// 'probably'
document.createElement('video').canPlayType('video/webm; codecs="vp09"');
// ''
```

`vp9` では `'probably'` が返ってきました。つまりデバイスで再生できる可能性が高いという意味です。`vp09` では `''` が返り、これはデバイスで再生できないことを意味します。つまり、`vp09` という書き方は、正しくないということになります。AV1 コーデックでも同様で、`av1` は `'probably'` ですが、`av01` は `''` になります。

`vp09.~~` や `av01.~~` という 4 文字のアルファベットから始まる表記は、RFC 6381 決まっていることなので良しとします。`vp9`、`av1` といった短い書き方を使用するときは、間違った文字列を指定してしまわないように気をつけないといけませんね。ちなみに、短い書き方での表現は [Chrome だとここで定義](https://source.chromium.org/chromium/chromium/src/+/main:media/base/video_codecs.cc)されているようです。

## 短い書き方では適切な動画ファイルが選ばれない？

同じデバイスでも、ブラウザによって挙動が異なります。Can I use によると[Firefox も Chrome も AV1 コーデックに対応している](https://caniuse.com/av1) ようですが、下記の JavaScript を実行すると差があります。

```js
document.createElement('video').canPlayType('video/webm; codecs="av1"');
// Firefox: 'probably'
// Chrome: ''
```

このため、source 要素で AV1 コーデックの動画ファイルを指定しても、**Chrome ではスルーされてしまいます**。RTX 3000 シリーズの GPU が積まれているパソコンでの結果なので、ハードウェアデコードも可能なはずです。

Chrome でも AV1 コーデックの動画ファイルを使わせるには、**短い記法ではなく、プロファイルなどの情報も含めた `codecs` を書く必要が (現時点では) ありそうです**。

## プロファイルとか、色深度とかよく分からないんですけど！

――というような人は、[MediaInfo](https://mediaarea.net/en/MediaInfo) などで確認することができます。

今回は試しに、AV1 コーデックの WebM ファイルを実際に `type` 属性で表現するまでをやってみましょう。

`codecs` の記法としては、
```
av01.<profile>.<level><tier>.<bitDepth>.<monochrome>.<chromaSubsampling>.
<colorPrimaries>.<transferCharacteristics>.<matrixCoefficients>.<videoFullRangeFlag>
```

ですが、実際のところ `<monochrome>` 以降はデフォルトの設定であれば省略することができるみたいですので、そうします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498509/8b779a96-0007-b8e6-8b28-e8c3e7c9fe9b.png)

こちらがサンプルの AV1 動画を MediaInfo した結果です。実際にパラメーターを組み立てていきます。

MediaInfo から、プロファイルが Main、レベルが 4.0、色深度が 8 bit であることがわかります。つまり、

```
type="video/webm; codecs='av01.0.08M.08'"
```

ということになります。レベルと `<level>` の関係は [AV1 の仕様書](https://aomediacodec.github.io/av1-spec/#levels) から分かります。「`<tier>` はどこから判断したんだ？」という話ですが、基本的には Tier 0 (Main) = `M` 以外になり得ないと考えていいと思います。

どうしても Tier 0 であるという確証が欲しい場合は、ffmpeg でこのようなコマンド <sup>([参考](https://issues.chromium.org/issues/40251103))</sup>を実行します (PowerShell)。

```powershell
ffmpeg -i input_av1.webm -c:v copy -bsf:v trace_headers -f null NUL 2>&1 | Select-String "seq_tier"
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498509/cc819c82-a1f0-eb9b-a695-3dc0dc16e767.png)

`0` ですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498509/c0248aa6-18a3-eef1-86d2-464308320d90.png)

ここまで指定して、やっと Chrome で `'probably'` が返ってきます。

## まとめ : HTML で動画を適切に扱うには？

****YouTube を使いましょう。****

……という冗談はさておき (ぜんぜん冗談になっていないですね)。

現状 Chrome だと省略された記法の AV1 コーデックが適切に選択されないという問題 (？) があるので、できればフルで指定して AV1 コーデックを読ませたいです。しかし、動画のプロファイルやレベルなどを逐一確認するのは時間が掛かると思われるので、理想は `vp9` `av1` だけで済ませたいです。

実際のところ、プロファイルとかレベルとか、**適当な値** (Invalid な値という意味ではなく、動画本来の値と違うもの) **でも多分動きます**。ブラウザがデバイスの処理性能に合わせて対応レベルを変えているとは考えづらいので。つまるところ**なおさら詳細な指定って無駄**だと思うんですけど、Chrome がなぁ……。

とりあえず Chrome が `av1` という書き方に対応していないことは将来的に変わると期待して、`av1` で指定、AV1 のフォールバックとして VP9 (`vp9`) という感じで良いかなと思います。もう流石に VP9 に対応していないブラウザを考慮しなくても良いでしょう。

MediaInfo とか ffprobe をウェブ上で動かしてファイルドロップで適切な `codecs` パラメーターを生成するオンラインツールとかあると便利そうだなと思いました。