# 帳票設計書 1-サムネイル画像

## 概要

本ドキュメントは、StaxRipにおけるサムネイル画像（Thumbnail Sheet）出力機能の設計書である。ビデオファイルから複数のフレームを抽出し、グリッド状に配置したサムネイルシートを生成する機能について記述する。

### 本帳票の処理概要

サムネイル画像機能は、ビデオファイルの内容を視覚的に確認できるよう、複数のフレームをグリッド形式で1枚の画像にまとめて出力する機能である。

**業務上の目的・背景**：ビデオファイルのコンテンツを素早く把握するため、また共有・アーカイブ時の視覚的インデックスとして利用される。エンコード前後のビデオ内容の確認、動画ライブラリの管理、動画共有サイトへのサムネイル添付などに活用される。

**帳票の利用シーン**：エンコード完了後の品質確認時、動画ファイルのアーカイブ作成時、フォーラムやSNSでの動画内容共有時、バグレポート添付時など。

**主要な出力内容**：
1. ヘッダー情報（ファイル名、ファイルサイズ、再生時間、ビットレート、フレーム数）
2. ビデオストリーム情報（解像度、フレームレート、コーデック、HDR情報、色深度）
3. オーディオストリーム情報（言語、チャンネル数、ビットレート、サンプリングレート）
4. 字幕ストリーム情報（言語、形式）
5. グリッド状に配置されたビデオフレームのサムネイル
6. 各フレームのタイムスタンプ

**帳票の出力タイミング**：メインフォームのツールメニュー「Thumbnails...」選択時、またはプロジェクト設定で自動生成が有効な場合の処理完了時。

**帳票の利用者**：動画編集者、エンコード担当者、動画ライブラリ管理者、バグ報告者。

## 帳票種別

画像出力（サムネイルシート）

## 利用画面

| 画面No | 画面名 | URL/ルーティング | 出力操作 |
|--------|--------|-----------------|---------|
| - | MainForm | メインフォーム | Tools > Thumbnails... |
| - | MainForm_ShowOptions | オプション設定 | Customization > Thumbnails タブ |

## 出力形式

### 基本仕様

| 項目 | 内容 |
|-----|------|
| ファイル形式 | JPG / PNG / BMP / GIF / TIF（設定により選択） |
| 用紙サイズ | 可変（サムネイル幅・列数・行数により決定） |
| 向き | - |
| ファイル名 | %target_dir%/{ターゲット名}_Thumbnail.{拡張子}（マクロ展開可） |
| 出力方法 | ファイル保存 |
| 文字コード | - |

### 画像固有設定

| 項目 | 内容 |
|-----|------|
| サムネイル幅 | 0〜1920ピクセル（デフォルト: 600） |
| 列数 | 1〜50（デフォルト: 4） |
| 行数 | 1〜80（デフォルト: 6）または時間間隔モード |
| 行モード | Fixed（固定行数）/ TimeInterval（時間間隔） |
| 間隔 | 1〜1800秒（デフォルト: 60秒、TimeIntervalモード時） |
| スペーサー | 0〜1000%（デフォルト: 20%、サムネイル幅の10%を基準） |
| 画質（JPG） | 1〜100（デフォルト: 70） |

## 帳票レイアウト

### レイアウト概要

サムネイルシートは、上部のヘッダー情報部と下部のサムネイルグリッド部で構成される。

```
┌─────────────────────────────────────┐
│            ヘッダー部                │
│  （ファイル情報・メディア情報）       │
├─────────────────────────────────────┤
│  ┌───┐ ┌───┐ ┌───┐ ┌───┐          │
│  │   │ │   │ │   │ │   │  ...     │
│  │T1 │ │T2 │ │T3 │ │T4 │          │
│  └───┘ └───┘ └───┘ └───┘          │
│  ┌───┐ ┌───┐ ┌───┐ ┌───┐          │
│  │   │ │   │ │   │ │   │  ...     │
│  │T5 │ │T6 │ │T7 │ │T8 │          │
│  └───┘ └───┘ └───┘ └───┘          │
│             ...                     │
└─────────────────────────────────────┘
```

### ヘッダー部

| No | 項目名 | 説明 | データ取得元 | 表示形式 |
|----|-------|------|-------------|---------|
| 1 | Filename | ファイル名 | MediaInfo.GetGeneral("FileName") | 文字列 |
| 2 | General | ファイルサイズ・再生時間・フレーム数・ビットレート | MediaInfo複数項目 | bytes (MB/GB) / HH:MM:SS / frames / Mb/s |
| 3 | Video | ビデオストリーム情報 | MediaInfo.GetVideo() | codec / WxH px / fps / kb/s / Profile / HDR / bit depth / ColorSpace |
| 4 | Audio | オーディオストリーム情報 | MediaInfo.GetAudio() | lang / channels / kb/s / Hz / format |
| 5 | Text | 字幕ストリーム情報 | MediaInfo.GetText() | lang / format / [DEFAULT] / [FORCED] |

### 明細部（サムネイルグリッド）

| No | 項目名 | 説明 | データ取得元 | 表示形式 | 列幅 |
|----|-------|------|-------------|---------|-----|
| 1 | サムネイル画像 | ビデオフレーム画像 | VapourSynthフレームサーバー | Bitmap | thumbWidth |
| 2 | タイムスタンプ | フレームの再生時刻 | serverPos / frameServer.FrameRate | HH:MM:SS | - |

### フッター部

フッター部は存在しない。

## 出力条件

### 抽出条件

| 条件名 | 説明 | 必須 |
|-------|------|-----|
| ソースファイル存在 | 指定されたビデオファイルが存在すること | Yes |
| VapourSynth有効 | VapourSynthパッケージが正常にインストールされていること | Yes |

### ソート順

| 優先度 | 項目 | 昇順/降順 |
|-------|------|---------|
| 1 | フレーム番号 | 昇順 |

### 改ページ条件

改ページなし（1ファイル = 1画像）

## データベース参照仕様

### 参照テーブル一覧

本機能はデータベースを使用しない。MediaInfoライブラリを通じてビデオファイルのメタデータを取得する。

| テーブル名 | 用途 | 結合条件 |
|-----------|------|---------|
| - | - | - |

### 外部データソース

| ソース | 用途 | 取得方法 |
|--------|------|---------|
| MediaInfo | メディアファイルのメタデータ取得 | MediaInfo.GetGeneral/GetVideo/GetAudio/GetText |
| FrameServer | ビデオフレームのピクセルデータ取得 | FrameServerFactory.Create() / GetFrame() |
| ObjectStorage | サムネイラー設定の取得 | Project.ThumbnailerSettings |

## 計算仕様

### 計算項目一覧

| 項目名 | 計算式 | 端数処理 | 備考 |
|-------|-------|---------|------|
| サムネイル高さ | thumbWidth / DAR | 4の倍数に切り捨て | DAR = DisplayAspectRatio |
| 画像幅 | (spacer + thumbWidth) * columns + spacer | - | - |
| 画像高さ | (spacer + thumbHeight) * rows + thumbVerticalOffset | - | - |
| フレーム位置 | (totalFrames / thumbCount) * (index + 1) - (totalFrames / thumbCount) / 2 | 整数切り捨て | 均等間隔 |
| スペーサー | thumbWidth / 10 * (spacerPercent / 100) | 整数 | - |
| 計算行数（TimeInterval） | (duration / interval) / columns | 整数切り捨て | TimeIntervalモード時のみ |

## 処理フロー

### 出力フロー

```mermaid
flowchart TD
    A[出力要求] --> B[パラメータ検証]
    B --> C{VapourSynth確認}
    C -->|NG| Z[処理終了]
    C -->|OK| D[MediaInfoからヘッダー情報取得]
    D --> E[VapourSynthスクリプト生成]
    E --> F[フレームサーバー起動]
    F --> G[画像バッファ作成]
    G --> H[ヘッダー描画]
    H --> I[並列フレーム取得・描画]
    I --> J[タイムスタンプ描画]
    J --> K[画像ファイル保存]
    K --> L[終了]
```

## エラー処理

### エラーケース一覧

| エラー種別 | 発生条件 | 表示メッセージ | 対処方法 |
|----------|---------|--------------|---------|
| ファイル不存在 | pSourcePathが存在しない | - | False返却で処理終了 |
| VapourSynth未検証 | Package.VapourSynth.VerifyOK(True)がFalse | VerifyOKダイアログ表示 | 処理中断 |
| スクリプトエラー | VapourSynthスクリプト実行エラー | - | script = Nothing で処理終了 |
| フレーム取得失敗 | GetFrameが0以外を返す | - | 該当サムネイルをスキップ |
| キャンセル | CancellationToken発火 | - | 例外スロー |

## パフォーマンス要件

| 項目 | 内容 |
|-----|------|
| 想定データ件数 | 1〜400サムネイル（列数50 * 行数80） |
| 目標出力時間 | ソースファイル長・解像度に依存 |
| 同時処理数 | セマフォによる最大3並列ソース処理、フレーム取得は最大4並列 |

## セキュリティ考慮事項

- ファイルパスはMacro.Expandで展開されるため、マクロインジェクションのリスクあり
- 出力先パスに書き込み権限が必要
- HDRコンテンツはトーンマップ処理後に出力（libplacebo使用時）

## 備考

- VapourSynthのffms2プラグインを使用してフレームを取得
- HDRコンテンツの場合、libplaceboによるトーンマップ処理が適用される
- フォント設定はThumbnailerSettingsで個別にカスタマイズ可能

---

## コードリーディングガイド

本帳票を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

サムネイル生成に必要な設定データと出力データの構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | Thumbnailer.vb | `Source/General/Thumbnailer.vb` | ThumbnailerRowMode列挙型（Line 10-13）で行モードの種類を確認 |
| 1-2 | Thumbnailer.vb | `Source/General/Thumbnailer.vb` | HeaderInfoクラス（Line 423-426）でヘッダー情報構造を確認 |
| 1-3 | Project.vb | `Source/General/Project.vb` | ThumbnailerSettingsプロパティでObjectStorage経由の設定取得方法を確認 |

**読解のコツ**: VB.NETのObjectStorageは動的な設定格納に使用される。GetInt/GetString/GetBoolメソッドでデフォルト値付きの設定取得パターンを理解する。

#### Step 2: エントリーポイントを理解する

処理の起点となるファイル・関数を特定する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | Thumbnailer.vb | `Source/General/Thumbnailer.vb` | Run/RunAsyncメソッド（Line 17-52）が外部呼び出しの入口 |
| 2-2 | GlobalCommands.vb | `Source/General/GlobalCommands.vb` | ShowThumbnailsメニューからの呼び出しを確認 |

**主要処理フロー**:
1. **Line 17-20**: 同期版Runメソッド - CancellationTokenSourceを作成しRunAsyncを呼び出し
2. **Line 22-52**: 非同期版RunAsync（複数ソース対応） - SemaphoreSlimで3並列制御
3. **Line 56-274**: 非同期版RunAsync（単一ソース） - 実際のサムネイル生成処理

#### Step 3: 設定読み込み処理を理解する

サムネイラーの各種設定がどのように読み込まれるか確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | Thumbnailer.vb | `Source/General/Thumbnailer.vb` | Line 72-149で各設定値の読み込みと制限値の適用を確認 |

**主要処理フロー**:
- **Line 74-78**: サムネイル幅・高さの計算（DARを考慮、4の倍数に調整）
- **Line 81-86**: 行数・列数・間隔・スペーサーの読み込み
- **Line 88-108**: タイムスタンプフォント設定の読み込み
- **Line 115-127**: ヘッダー設定の読み込み
- **Line 128-149**: 画像形式・品質・出力パスの決定

#### Step 4: ヘッダー情報取得処理を理解する

MediaInfoを使用したメディア情報取得処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | Thumbnailer.vb | `Source/General/Thumbnailer.vb` | GetHeaderInfosAsync（Line 324-420）でMediaInfoからの情報取得を確認 |

**主要処理フロー**:
- **Line 331-348**: 一般情報（ファイル名、サイズ、再生時間、ビットレート）の取得
- **Line 350-376**: ビデオストリーム情報の取得（複数ストリーム対応）
- **Line 378-394**: オーディオストリーム情報の取得（複数ストリーム対応）
- **Line 396-411**: 字幕ストリーム情報の取得

#### Step 5: フレーム取得・描画処理を理解する

VapourSynthを使用したフレーム取得と画像描画処理を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 5-1 | Thumbnailer.vb | `Source/General/Thumbnailer.vb` | PrepareVapoursynthVideoScriptAsync（Line 278-320）でスクリプト生成を確認 |
| 5-2 | Thumbnailer.vb | `Source/General/Thumbnailer.vb` | Line 181-267でフレーム取得・描画のParallel.For処理を確認 |

**主要処理フロー**:
- **Line 285-291**: VapourSynthスクリプトの基本設定（ffms2ソース、リサイズ）
- **Line 299-306**: HDRコンテンツのトーンマップ処理
- **Line 211-263**: Parallel.Forによる並列フレーム取得・タイムスタンプ描画・グリッド配置

### プログラム呼び出し階層図

```
GlobalCommands.ShowThumbnails()
    │
    └─ Thumbnailer.Run() / RunAsync()
           │
           ├─ RunAsync() [複数ソース版: SemaphoreSlimで3並列制御]
           │      │
           │      └─ RunAsync() [単一ソース版]
           │             │
           │             ├─ GetHeaderInfosAsync()
           │             │      └─ MediaInfo.GetGeneral/GetVideo/GetAudio/GetText()
           │             │
           │             ├─ PrepareVapoursynthVideoScriptAsync()
           │             │      └─ VideoScript / VideoFilter 生成
           │             │
           │             ├─ FrameServerFactory.Create()
           │             │      └─ FrameServer.GetFrame()
           │             │
           │             └─ Parallel.For() [フレーム描画: 最大4並列]
           │                    └─ Graphics.DrawImage/DrawPath/FillPath
           │
           └─ Bitmap.Save() [画像ファイル出力]
```

### データフロー図

```
[入力]                    [処理]                         [出力]

ビデオファイル ────────▶ MediaInfo解析 ───────────▶ ヘッダー情報
     │                        │
     │                        ▼
     │                   ObjectStorage ──────────▶ 設定値
     │                        │
     │                        ▼
     └──────────────────▶ VapourSynth
                         フレームサーバー ──────────▶ フレームBitmap
                              │
                              ▼
                         Graphics描画
                         (ヘッダー/サムネイル/タイムスタンプ)
                              │
                              ▼
                         Bitmap.Save() ──────────▶ 画像ファイル
                                                   (JPG/PNG/BMP/GIF/TIF)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| Thumbnailer.vb | `Source/General/Thumbnailer.vb` | ソース | サムネイル生成のメインロジック |
| GlobalCommands.vb | `Source/General/GlobalCommands.vb` | ソース | メニューからの呼び出し |
| Project.vb | `Source/General/Project.vb` | ソース | ThumbnailerSettings設定管理 |
| VideoScript.vb | `Source/Video/VideoScript.vb` | ソース | VapourSynthスクリプト生成 |
| FrameServer.vb | `Source/Video/FrameServer.vb` | ソース | フレームサーバー基底クラス |
| MainForm_ShowOptions.vb | `Source/Forms/MainForm_ShowOptions.vb` | ソース | サムネイラー設定UI |
| MediaInfo | 外部ライブラリ | ライブラリ | メディア情報取得 |
| libplacebo | 外部プラグイン | プラグイン | HDRトーンマップ処理 |
