Vulkan Video で MP4 の H.265/HEVC 動画をデコードする際のメモや注意点


## はじめに

[グラフィックス全般 Advent Calendar 2024](https://qiita.com/advent-calendar/2024/graphics) 10日目の記事になります.

## 所感

**非常に低レイヤ**.

MP4 から demux した動画ストリームを Vulkan のコマンドに渡して～ のように想像していると, かなり落胆することになると思われる.

デコードに用いる Vulkan Video のオブジェクトを作ったりコマンドを実行させたりする際に,
驚くほど多くの H.265 固有のパラメータを渡す必要がある.
これらは基本的には**自前で H.265 の bitstream からパース**して得る必要がある.

H.265 の仕様書[^h265_spec]と睨めっこしながらかなり苦労して Rust 用のパーサを用意した.
パーサとして完成とは言えないが, _Main_ や _Main 10_ profile の H.265 の bitstream を Vulkan Video でデコードするためには十分なものができたので, いちおう, 公開しておく:

https://github.com/James2022-rgb/vidsynt

Vulkan Video そのものの仕様で難解と言えるのは DPB(Decoded Picture Buffer) やその状態の管理周りくらいで,
あとは Vulkan を知っていればそれほど苦労することはないと感じた.

難しい部分は, H.265 など該当のコーデックそのものの仕様が複雑で膨大なことに起因していて,
Vulkan の仕様書とデコード/エンコードするコーデックそのものの仕様書を交互にたくさん読むことになる.

## Vulkan Video: 1フレームのデコードの流れ

### 大まかな流れ

```cpp

// video coding scope を開始.
vkCmdBeginVideoCodingKHR(...);

// 開始時やランダムアクセス(シーク)後ならば VkVideoSessionKHR をリセットする
if (is_first_picture) {
    // VK_VIDEO_CODING_CONTROL_RESET_BIT_KHR
    vkCmdControlVideoCodingKHR(...);
}

// フレームのデコード
vkCmdDecodeVideoKHR(...);

// video coding scope を終了.
vkCmdEndVideoCodingKHR(...);

```

### vkCmdBeginVideoCodingKHR 時
video coding scope を開始するコマンド.

#### video coding scope に bound reference picture たちを設定
video coding scope の bound reference picture とは, scope 中で使える reconstructed picture または reference picture として使用できる picture を表す.
(reconstructed picture は要するに今回のデコードの出力となる picture のこと)

`VkVideoReferenceSlotInfoKHR` として, `VkVideoBeginCodingInfoKHR::pReferenceSlots` に設定する.
その際, それぞれの picture に最初に割り当てられる DPB slot も指定する.

### vkCmdDecodeVideoKHR 時

#### H.265/HEVC 固有のパラメータ
`VkVideoDecodeH265PictureInfoKHR` を `VkVideoDecodeInfoKHR` の pNext-chain に含めることで,
H.265 固有の picture のパラメータを設定する.

後述の `VkVideoDecodeH265PictureInfoKHR::pSliceSegmentOffsets` もここ.

#### reference picture setup request
将来のデコードで reference picture となり得る picture の場合,
(これは NAL unit type で判断できる)
`VideoDecodeInfoKHR::pSetupReferenceSlot` を使って reference picture setup をリクエストする必要がある.

#### active reference の設定
このデコードがフレーム間予測に用いる reference picture を, `VideoDecodeInfoKHR::pReferenceSlots` で設定する必要がある.

## Vulkan Video の概念 メモ

DPB slot を active にする操作:
- reference picture setup request つきで reconstructed picture として使用する
  - 👉 DPB slot は その reconstructed picture (への _picture reference_) に associate される

DPB slot を inactive にする操作/状況:
- reconstructed picture として使用で coding operation が失敗する
- reference picture setup request **なし** で reconstructed picture として使用する
- frame に associate された DPB slot を reconstructed picture として使用する
- `vkCmdBeginVideoCodingKHR` で `VkVideoReferenceSlotInfoKHR::pPictureResource` を `nullptr`

(DPB slot の内容は invalid picture reference になる)

## H.265/HEVC の概念 メモ

### picture の種類

#### IRAP(Intra Random Access Point) picture
**総称**: I-frame であって, ランダムアクセスポイントとなる picture の種類.

以下が IRAP に該当する:
- IDR(Instantaneous Decoding Refresh) picture
- CRA(Clean Random Access) picture
- BLA(Broken Link Access) picture

#### IDR(Instantaneous Decoding Refresh) picture
NAL unit type でいうと `IDR_W_RADL` (== `19`) と `IDR_N_LP` (== `20`).

IRAP なので, I-frame.


bitstream の最初の picture は必ず IDR になる.

GOP 内で後続の (i.e. この picture を associated IRAP picture として持つ) P-frame や B-frame は この picture よりも復号順で先行する picture を参照しないことが保証される.
👉 よって, ランダムアクセスの対象としては最も都合がいい.

よって, IDR picture をデコードする際には DPB をクリアする.
また, POC(Pic Order Count) が `0` にリセットされる.

`IDR_W_RADL` は, RADL を associated picture として持ちうる (RASL は持たない).
`IDR_N_LP` は, LP(Leading Picture) を associated picture として持たないことが保証される.

#### CRA(Clean Random Access) picture
NAL unit type でいうと `CRA_NUT` (== `21`).

IRAP なので, I-frame.

空でない RPS (Reference Picture Set) を指定することがある.
(_unused for reference_ としてマークする)

CVS 内に RASL(Random Access **Skipped** Leading) picture を持つ場合がある.
これら RASL picture は CRA に複号順で先行する picture を参照するため,
CRA からデコードを開始した際にはデコードすることができない.

#### BLA(Broken Link Access) picture
⚠️ 筆者はこれを扱ったことがない.

NAL unit type でいうと `BLA_W_LP` (== `16`), `BLA_W_RADL` (== `17`), `BLA_N_LP` (== `18`)

IRAP なので, I-frame.

#### Trailing picture
NAL unit type でいうと `TRAIL_N` (== `0`), `TRAIL_R` (== `1`)

#### RADL(Random Access Decodable Leading) picture
NAL unit type でいうと `RADL_N` (== `6`), `RADL_R` (== `7`).

associated IRAP の leading picture (i.e. 出力順で先行する).

#### RASL(Random Access Skipped Leading) picture
NAL unit type でいうと `RASL_N` (== `8`), `RASL_R` (== `9`).

associated CRA/BLA の leading picture (i.e. 出力順で先行する).

CRA/BLA からデコードを開始する場合,
それより前にあるためデコードしていない or bistream 中にない picture を参照することになるため,
これら RASL picture はデコードできない.
よって CRA/BLA からデコードを開始する場合は, RASL picture はスキップする.

### VPS, SPS, PPS

#### PPS (Picture Parameter Set)
1つ以上の coded picture の coded slice segment NAL unit から参照されるパラメータを持つ.

#### SPS (Sequence Parameter Set)
1つ以上の PPS or active parameter sets SEI message を含む1つ以上の SEI NAL unit から参照されるパラメータを持つ.

#### VPS (Video Parameter Set)
1つ以上の SPS or active parameter sets SEI message を含む1つ以上の SEI NAL unit から参照されるパラメータを持つ.

:::note info
H.264 にはなく, H.265/HEVC で追加された概念.
:::

### VPS, SPS, PPS の在処

#### hvc1
hvc1 の場合, これらの NAL unit は _out-of-band_ in the sample entry である (i.e. MP4 ではコンテナに格納される).
この場合, これらの NAL unit は `trak` -> `mdia` -> `minf` -> `stbl` -> `stsd` -> `hvc1` -> `hvcC` に格納される `HEVCDecoderConfigurationRecord` 中 の array に含まれている.

この構造体そのものは, アクセス有料の _ISO/IEC 14496-15_[^iso_iec_14496_16] で定義されている[^virinext_hevc_bitstream_formats]が,
例えば [chromium のコード](https://chromium.googlesource.com/chromium/src/media/+/refs/heads/main/formats/mp4/hevc.h)で構造を参考にすることもできる.

[^iso_iec_14496_16]: [_ISO/IEC 14496-15:2022 - Information technology — Coding of audio-visual objects — Part 15: Carriage of network abstraction layer (NAL) unit structured video in the ISO base media file format_](https://www.iso.org/standard/83336.html)
[^virinext_hevc_bitstream_formats]: [_HEVC bitstream formats - Virinext Bitstream Analyzer_](https://virinext.com/hevc-bitstream-formats-and-decoder-configuration-record/)

#### hev1

hev1 の場合, これらの NAL unit は _out-of-band_ in the sample entry である and/or _in-band_ in the samples である(i.e. bitstream 内に含まれる), らしい.

つまり hvc1 と同様に MP4 コンテナに格納されている場合もあれば, bitstream 内に含まれる場合もある[^hevc1_hev1].

[^hevc1_hev1]: [_What's the difference between 'hvc1' and 'hev1' HEVC codec tags for fMP4? - Bitmovin Community_](https://community.bitmovin.com/t/whats-the-difference-between-hvc1-and-hev1-hevc-codec-tags-for-fmp4/101)

MP4 コンテナに格納されている場合, これらの NAL unit は `trak` -> `mdia` -> `minf` -> `stbl` -> `stsd` -> `hev1` -> `hvcC` に格納される `HEVCDecoderConfigurationRecord` 中 の array に含まれている.

bitstream 内に含まれる場合,
access unit の構造が (AUD), VPS, SPS, PPS, (P-SEI), [slice segment, ...] のようになっているはず.

##### Disclaimer
筆者は「bitstream 内に含まれる場合」の MP4 を扱っていない.

### VPS, SPS, PPS の関係

SPS は VPS の ID を, PPS は SPS の ID を参照する.

```mermaid
classDiagram
    VideoParameterSet <|-- SequenceParameterSet
    SequenceParameterSet <|-- PictureParameterSet
    class VideoParameterSet {
      vps_video_parameter_set_id: 自分のID
    }
    class SequenceParameterSet {
      sps_video_parameter_set_id: VPS の ID
      sps_seq_parameter_set_id: 自分のID
    }
    class PictureParameterSet {
      pps_pic_parameter_set_id: 自分の ID
      pps_seq_parameter_set_id: SPS の ID
    }
```

### GOP(Group Of Pictures)
正確には H.265/HEVC の仕様に存在する概念ではない(H.264 に存在する).

I-picture と, その次の I-picture の前までの, 複号順で後続する picture たちを指す.
先頭の I-picture が IRAP picture であることは求められていない.

### CVS(Coded Video Sequence)
IRAP picture と, その次の IRAP picture の前までの, 複号順で後続する (i.e. 前述の IRAP picture を associated IRAP picture として持つ) picture たちを指す.

ここでいう IRAP picture は, `NoRaslOutputFlag` == `1` であるもの.

つまり, P-predition や B-predition によって関係する picture たちに閉じたグループ.
associated IRAP picture を先頭とするため, 複号順で先頭に先行する picture を P-prediction や B-prediction で参照しない保証がある.

### POC(Picture Order Count)
CVS(Coded Video Sequence) 内で一意的な値であり, IDR-frame ではその値は `0` になる.
(またその際 DPB はクリアされ, 存在していた picture たちは unused for reference としてマークされる)

「動画ファイル全体で一意な値」**とは一般には言えない**[^motion_compensation_in_hevc].

GOP の先頭は I-frame だが, それは必ずしも IRAP picture ではない; CVS は1つまたは複数の GOP から構成されうる.

:::note info
じっさい, 下記のように GoPro HERO11 の吐く MP4 の H.265 bitstream では,
30 フレームごとに IDR があり CVS をなしている.
:::

[^motion_compensation_in_hevc]: [_Motion compensation in HEVC. he HEVC standard implements all of the… | by Elecard Company | Medium_](https://videocompressionguru.medium.com/motion-compensation-in-hevc-af4437f89186)

## ランダムアクセス(シーク) について

正常にデコードすることを考えると,
ランダムアクセスしたい位置から, 復号順の逆方向で最も近い IRAP picture を見つけてそこからデコードを開始する必要がある.
IRAP には IDR と CRA と BLA picture があるが, ここでは簡単のため IDR と CRA のみを考える.

### IDR が見つかれば簡単

デコーダのステートをフラッシュ/リセットすればいい:
- DPB の全ての slot を inactive にする
- POC(Pic Order Count) を `0` に戻す.

### ただし...

IDR が一番最初のフレームにしかない動画ファイルも実際には多い.
よってランダムアクセスの対象には CRA も考慮されなければならない.

:::note info
GoPro HERO11 で撮影した動画の MP4 の H.265 ストリームでは, 30フレームに1回必ず IDR がある!
:::

### CRA(Clean Random Access) picture へのランダムアクセスの扱い

#### RPS (Reference Picture Set)

CRA は I-frame であるとされているが, 実際には, 空でない RPS (Reference Picture Set) を指定するような syntax element を持つことがある.

ランダムアクセスの結果として CRA からの再生になる場合, その CRA は `NoRaslOutputFlag` == `1` を持っていることになる:

_8.1.3  Decoding process for a coded picture with nuh_layer_id equal to 0_[^h265_spec]:
> If the current picture is an IDR picture, a BLA picture, **the first picture in the bitstream in decoding order**, or the first 
> picture that follows an end of sequence NAL unit in decoding order, the variable **NoRaslOutputFlag is set equal to 1**.

`NoRaslOutputFlag` == `1` であるような IRAP picture の場合には, reference picture たちは "unused for reference" としてマークすることになる:

_8.3.2  Decoding process for reference picture set_[^h265_spec]:
> When the current picture is an **IRAP picture with NoRaslOutputFlag equal to 1**, all reference pictures with nuh_layer_id 
> equal to currPicLayerId currently in the DPB (if any) are **marked as "unused for reference"**. 

その際, 実装により _unavailable reference pictures_ というものが生成される模様:

_8.1.3  Decoding process for a coded picture with nuh_layer_id equal to 0_[^h265_spec]:
> When the current picture is a BLA picture or is a **CRA picture with NoRaslOutputFlag equal to 1**, the 
> **decoding process for generating unavailable reference pictures** specified in clause 8.3.3 **is invoked**, which 
> needs to be invoked only for the first slice segment of a picture. 

#### RASL(Random Access Skipped Leading) picture たち

CRA へのランダムアクセスから再生を開始した際, 次の CVS に達するまで,
現在の CVS に存在する RASL(Random Access Skipped Leading) picture はデコードできない.

:::note info
RASL picture は associated IRAP picture である当該 CRA picture に復号順で先行する picture を参照する可能性があるため.
:::

その名の通り, このケースではこれら RASL picture のデコードをスキップする.

## Vulkan Video における注意点

### Disclaimer

筆者は NVIDIA のドライバにおける実装でしか動作を確認していない.

### `VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR` と I-frame

まずはいちばん最初のフレームのデコードが動作するようにしたいところだが.

最初のフレームは必ず IDR でありつまり I-frame (i.e. reference picture を用いない) だが,
`VK_VIDEO_DECODE_CAPABILITY_DPB_AND_OUTPUT_COINCIDE_BIT_KHR ` の場合は,
I-frame のデコードの際も, DPB slot をちゃんと(出力先と同じものを)指定する必要がある (`VkVideoDecodeInfoKHR::pSetupReferenceSlot`).
当然だがその際の image layout は `VK_IMAGE_LAYOUT_VIDEO_DECODE_DPB_KHR` になる.

:::note warn
NVIDIA の実装においては, I-frame においても,
これを行わないとデコードが失敗することを確認している.

つまり, result status query の結果が `VK_QUERY_RESULT_STATUS_ERROR_KHR` になり,
デコード出力先の `VkImage` には何も書き込まれない.
:::

### 入力 bitstream
現状の仕様[^vk_spec_h265_decode_bistream]はこの点についてかなり underspecified だと感じられるため,
NVIDIA の実装でしか動作を確認していないことをここで繰り返しておく.

#### ⚠️ _Annex B_ で渡す

H.265/HEVC では (H.264/AVC でも),
NAL unit たちを格納する byte stream のフォーマットは2種類存在する[^virinext_hevc_bitstream_formats][^monotone_h264]:
- Length-prefixed byte stream
- _Annex B_ byte stream format
  H.265 の仕様書[^h265_spec] の _Annex B_ で定義される.

[^monotone_h264]: [_H.264 について（まとめ） 【H.264/Annex B/NAL file format/AVC/rtmp】 - モノトーンの伝説日記_](https://mntone.hateblo.jp/entry/2013/09/03/180431)

`VkVideoDecodeInfoKHR::srcBuffer` には, _Annex B_ byte stream,
つまり start code prefix (3バイトの `0x000001`) と NAL unit を交互に配置したバッファを指定する.

NAL unit は start code prefix がエスケープされた EBSP(Encapsulated Byte Sequence Payload) のまま用いる.

[^h265_spec]: [_ITU-T. H.265 High Efficiency Video Coding (September, 2023)_](https://www.itu.int/rec/T-REC-H.265-202309-S)

:::note
つまり MP4 のストリームのサンプルから読んだような length-prefixed byte stream をデコードしたい場合は,
_Annex B_ に変換する必要がある.
:::

`VkVideoDecodeH265PictureInfoKHR::pSliceSegmentOffsets` には `srcBuffer` 中の start code prefix たちのオフセットを設定する.

#### ⚠️`AUD` (Access Unit Delimiter) NAL unit はスキップする
`AUD` NAL unit は access unit に含まれていることもないこともあるが, これは入力として渡さない.

渡してしまうと, NVIDIA のドライバは result status query が仕様にない `1000331003` を出力する形で失敗する.
(この値は他のケースでも出力されることがある)

H.264/AVC の場合で `SPS`, `PPS` の NAL unit は渡しても問題ないとされている[^wicked_engine_vulkan_video_decoding]のとは, 対照的だが,
`VkVideoDecodeH265PictureInfoKHR::pSliceSegmentOffsets` の名前を考えると,
slice segment ではない NAL unit を渡してはならない, というのは納得はいく.

[^wicked_engine_vulkan_video_decoding]: [_Vulkan Video Decoding – Wicked Engine_](https://wickedengine.net/2023/05/vulkan-video-decoding/)

[^vk_spec_h265_decode_bistream]: [_42.13.1. H.265 Decode Bitstream Data Access, Vulkan® 1.3.285 - A Specification (with all registered extensions)_](https://renderdoc.org/vkspec_chunked/chap42.html#decode-h265-bitstream-data-access)

### H.265 の VCL NAL unit と `StdVideoDecodeH265PictureInfoFlags`

| `nal_unit_type` | Name               | Is IRAP| Is IDR | Is Reference |
|:----------------|:-------------------|:-------|:------:|:-:|
|`0`              |`TRAIL_N`           |        |        | |
|`1`              |`TRAIL_R`           |        |        |✅|
|`2`              |`TSA_N`             |        |        | |
|`3`              |`TSA_R`             |        |        |✅|
|`4`              |`STSA_N`            |        |        | |
|`5`              |`STSA_R`            |        |        |✅|
|`6`              |`RADL_N`            |        |        | |
|`7`              |`RADL_R`            |        |        |✅|
|`8`              |`RASL_N`            |        |        | |
|`9`              |`RASL_R`            |        |        |✅|
|`10`             |~~`RSV_VCL_N10`~~   |        |        | |
|`11`             |~~`RSV_VCL_R11`~~   |        |        |✅|
|`12`             |~~`RSV_VCL_N12`~~   |        |        | |
|`13`             |~~`RSV_VCL_R13`~~   |        |        |✅|
|`14`             |~~`RSV_VCL_N14`~~   |        |        | |
|`15`             |~~`RSV_VCL_R15`~~   |        |        |✅|
|`16`             |`BLA_W_LP`          |✅      |        |✅|
|`17`             |`BLA_W_RADL`        |✅      |        |✅|
|`18`             |`BLA_N_LP`          |✅      |        |✅|
|`19`             |`IDR_W_RADL`        |✅      |✅      |✅|
|`20`             |`IDR_N_LP`          |✅      |✅      |✅|
|`21`             |`CRA_NUT`           |✅      |        |✅|
|`22`             |~~`RSV_IRAP_VCL22`~~|✅      |        |✅|
|`23`             |~~`RSV_IRAP_VCL22`~~|✅      |        |✅|

_Is Reference_ は `StdVideoDecodeH265PictureInfoFlags::IsReference` に設定され,
これは H.265 における _reference picture setup_ を制御し, ひいては DPB slot の activation を制御する.

```rust
fn is_reference(nal_unit_type: u8) -> bool {
  if nal_unit_type < 16 {
    nal_unit_type & 1 == 1
  } else {
    true
  }
}
```

## 動画の回転について
たとえば GoPro HERO11 を 180° 回転して設置して撮影した動画では, bitstream をデコードして得られる画像が 180° 回転した状態になる.

正しい向きで動画を再生するためには, この回転を打ち消す必要がある.

表示するために必要な変換を表す行列が, 以下に含まれている:
- MP4 の `moov` -> `mvhd`
- MP4 の 該当 track の `trak` -> `tkhd`

前者は単位行列になっていることが多い模様.

## ツールたち

### NVIDIA Nsight
Vulkan Video に対応しており, `vkCmdDecodeVideoKHR` の出力先の状態を表示することができる.

### ffmpeg

`trace_headers` で [NAL unit たちをダンプすることができる](https://video.stackexchange.com/questions/23870/print-to-file-details-of-every-nal-unit-in-raw-h-264-file/23877#23877):

```shell
ffmpeg -i video.mp4 -c copy -bsf:v trace_headers -f null - 2> NALUs.txt
```

```text:NALUs.txt
...

[trace_headers @ 000002b4a4fd34c0] Packet: 61163 bytes, key frame, pts 128000, dts 126464, duration 512.
[trace_headers @ 000002b4a4fd34c0] Slice Segment Header
[trace_headers @ 000002b4a4fd34c0] 0           forbidden_zero_bit                                          0 = 0
[trace_headers @ 000002b4a4fd34c0] 1           nal_unit_type                                          010101 = 21
[trace_headers @ 000002b4a4fd34c0] 7           nuh_layer_id                                           000000 = 0
[trace_headers @ 000002b4a4fd34c0] 13          nuh_temporal_id_plus1                                     001 = 1
[trace_headers @ 000002b4a4fd34c0] 16          first_slice_segment_in_pic_flag                             1 = 1
[trace_headers @ 000002b4a4fd34c0] 17          no_output_of_prior_pics_flag                                0 = 0
[trace_headers @ 000002b4a4fd34c0] 18          slice_pic_parameter_set_id                                  1 = 0
[trace_headers @ 000002b4a4fd34c0] 19          slice_type                                                011 = 2
[trace_headers @ 000002b4a4fd34c0] 22          slice_pic_order_cnt_lsb                              11111010 = 250
[trace_headers @ 000002b4a4fd34c0] 30          short_term_ref_pic_set_sps_flag                             0 = 0
[trace_headers @ 000002b4a4fd34c0] 31          num_negative_pics                                       00101 = 4
[trace_headers @ 000002b4a4fd34c0] 36          num_positive_pics                                           1 = 0
[trace_headers @ 000002b4a4fd34c0] 37          delta_poc_s0_minus1[0]                                    010 = 1
[trace_headers @ 000002b4a4fd34c0] 40          used_by_curr_pic_s0_flag[0]                                 0 = 0
[trace_headers @ 000002b4a4fd34c0] 41          delta_poc_s0_minus1[1]                                    010 = 1
[trace_headers @ 000002b4a4fd34c0] 44          used_by_curr_pic_s0_flag[1]                                 0 = 0
[trace_headers @ 000002b4a4fd34c0] 45          delta_poc_s0_minus1[2]                                    011 = 2
[trace_headers @ 000002b4a4fd34c0] 48          used_by_curr_pic_s0_flag[2]                                 0 = 0
[trace_headers @ 000002b4a4fd34c0] 49          delta_poc_s0_minus1[3]                                    010 = 1
[trace_headers @ 000002b4a4fd34c0] 52          used_by_curr_pic_s0_flag[3]                                 0 = 0

...
```


### Virinext Bitstream Analyzer
https://virinext.com/

プロプライエタリ.

コンテナファイルや elementary stream を入力として, NAL unit たちの情報を閲覧するためのツール.
AAC などオーディオストリームにも対応している.

有料だがデモ版で最初の10フレームまで機能を利用できる:
![Virinext Bitstream Analyzer](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100016/8cf252ef-da47-2da9-9401-655eb261efaf.png)

### hevcesbrowser

https://github.com/virinext/hevcesbrowser

bitstream 内の NAL unit たちの情報を見ることができる.
Virinext Bitstream Analyzer と違い, コンテナファイルは開けないようなので注意.
ffmpeg などで動画コンテナから bitstream を抽出する必要がある.

### GitlHEVCAnalyzer
https://github.com/lheric/GitlHEVCAnalyzer

CTU(Coding Tree Unit) たちなどの可視化ができるとのことだが,
こちらの H.265/HEVC ではクラッシュしてしまい動作を確認できず.


## 参考資料

### Vulkan Video の使用例
どれも希少なもの.

#### [てっくま氏](https://www.technicalife.net/) によるコードとスライド
- H.264/AVC のデコードのサンプル:

https://github.com/techmadot/vulkan_video_mini_sample

- [_コンパクトなVulkan入門書籍/ Vulkan Videoへのチャレンジ_ - Vulkan Meetup Japan 2024](https://www.khronos.org/developers/linkto/vulkan-vulkan-video)
  Vulkan Video の概要, その難しさの特徴について.

#### 記事

- _Vulkan Video Decoding – Wicked Engine_[^wicked_engine_vulkan_video_decoding]
  Wicked Engine での H.264 のデコードの実装についての記事.

- [_Lynne's compiled musings | Vulkan Video decoding_](https://lynne.ee/vulkan-video-decoding.html)
  ffmpeg の Vulkan Video デコード/エンコード の開発者である Lynne 氏による記事.
  プラットフォームやベンダ独自のデコード/エンコード API などエコシステムの状況についても詳しい.

- [_Vulkan Video Decode: First Frames_](https://poniesandlight.co.uk/reflect/island_video_decoder/)
  H.264 の最初のフレームをデコードできるまでの苦労が綴られている.

### Khronos Group からの Vulkan Video の情報

- [_Vulkan Video Core API Introduction_](https://www.khronos.org/assets/uploads/apis/Vulkan-Video-Deep-Dive-Apr21.pdf)
VPS, SPS, PPS の関係もわかりやすく示されている (Page 7).

### 動画の符号化の基本
- [_Video compression basics – RasterGrid_](https://www.rastergrid.com/blog/multimedia/2021/05/video-compression-basics/)
  Vulkan Video の仕様の策定にも関わっている RasterGrid による, 動画圧縮の基礎についての解説.

### H.265 の bitstream, CVS, GOP, access unit の構造
- [_【HEVC学習】2. HEVCのビットストリーム構成 | Kirakuni Tech Diary_](https://www.kirakuni100.tech/codec-hevc-bitstream/)
- [_情報源符号化部 H.265 | MPEG-H HEVC 規格の概要 (社団法人 電波産業会)_](https://www.soumu.go.jp/main_content/000230399.pdf)
- [_HEVC（MPEG-H/ITU-T H.265）技術解説 - ビットストリームの構成とその機能 - 映像情報メディア学会誌 Vol. 67, No. 7_](https://www.jstage.jst.go.jp/article/itej/67/7/67_549/_pdf)
- [_Overview of the High Efficiency Video Coding (HEVC) Standard | IEEE Journals & Magazine | IEEE Xplore_](https://ieeexplore.ieee.org/document/6316136?arnumber=6316136)
  論文なのでかなりフォーマルだけど.

### H.265 の Motion compensation (動き補償) や reference picture について

- _Motion compensation in HEVC. he HEVC standard implements all of the… | by Elecard Company | Medium_[^motion_compensation_in_hevc]

### byte stream について

- [_H.264 - redstrange Wiki*_](https://wikiwiki.jp/redstrange/H.264)
  H.264/AVC の byte stream の解説なので H.265 のそれとは異なるが, 参考になる.

### MP4 について

- [_フレームレートとタイムコード、タイムスタンプ #Video - Qiita_](https://qiita.com/sunfish-shogi/items/4dc020954d46f0e998a6)
  正しい時間でフレームを見せるにはこの辺りを気にする必要がある.

### その他

- [_H.265/HEVC特許暗黒時代 #コーデック - Qiita_](https://qiita.com/yohhoy/items/c2579097a507b1fbdddb)
  H.265 の特許周りのドロドロした状況について.