# OCV5 — видео для OpenComputers

Три файла:

| файл | где | что |
|---|---|---|
| `ocv_encode.py` | ПК | видео → папка `index.ocv` + `partNNNN.ocv` |
| `ocv.lua` | OC | плеер |
| `ocv_get.lua` | OC | скачать папку с HTTP-сервера |

Требуется **GPU Tier 3 + Screen Tier 3**.

## Быстрый старт

```powershell
pip install opencv-python numpy
python ocv_encode.py input.mp4 video --preview 100 300
```

`preview_N.png` в папке видео — пиксель-в-пиксель то, что покажет экран OC. Удобно подбирать параметры, не заходя в игру.

На OC:

```sh
ocv_get http://192.168.1.103/video/ video
ocv video          # -l  зациклить
```

Space — пауза, Q / Ctrl+C — выход. После просмотра плеер печатает статистику: если `пропущено выводов` заметно больше нуля, машина не успевает.

## Параметры энкодера

| флаг | по умолчанию | |
|---|---|---|
| `--width / --height` | 160 / 50 | размер в символах, максимум Tier 3 |
| `--fps` | 20 | больше 20 бессмысленно: OC рисует по тикам |
| `--max-sets` | 0 (без лимита) | бюджет `gpu.set` на кадр. Если плеер не успевает — 1000, 800, 600… |
| `--sharpen` | 0.5 | резкость после даунскейла, 0 — выкл |
| `--stability` | 6 | подавление шума исходника: меньше мерцания, меньше команд, меньше размер |
| `--gray` | — | ч/б, 18 уровней серого |
| `--chunk-mb` | 1.0 | размер part-файла |

## Как это устроено

**Палитра.** Tier 3 GPU физически показывает только 256 цветов: куб 6×8×5 (240 шт.) + 16 изменяемых. Плеер ставит в изменяемые 16 ровных серых (вместе с чёрным и белым куба — 18 уровней с шагом 15). Энкодер квантует сразу в эту палитру, поэтому GPU ничего не округляет повторно.

**Дизеринг.** Двухцветный ordered dither (Bayer 8×8): каждый цвет раскладывается на смесь двух ближайших цветов палитры, смешивание считается в линейном свете. Плоская область получает ровно 2 цвета — чисто визуально и идеально ложится в одну пару bg/fg. Паттерн статичен во времени, неподвижные участки не мерцают.

**Команды вместо пикселей.** Половинки символа — два «пикселя»: верх = bg, низ = fg. При заданных bg/fg символы `' ' '▄' '▀' '█'` покрывают все 4 комбинации, поэтому одна `gpu.set` рисует любую полосу ячеек, чьи цвета ⊆ {bg, fg}. Энкодер заранее строит минимальный поток `setBackground / setForeground / set`, сгруппированный по парам цветов, символы упакованы по 2 бита. Плеер просто исполняет поток в back buffer (он не тратит call budget) и выводит кадр одним `bitblt`.

**Бюджет.** С `--max-sets` энкодер рисует в первую очередь самые заметные изменения, остальное доезжает в следующих кадрах (с ростом приоритета, чтобы ничего не застревало). Плеер гарантированно держит FPS ценой лёгкого шлейфа в быстрых сценах.

**Стриминг.** Кадры читаются с диска по одному, без загрузки part-файла целиком — нет фризов на границах чанков.

## Объём

160×50 @ 20 fps — 100–200 КБ/с (зависит от динамики; `--max-sets` заметно уменьшает). Tier 3 HDD (4 МБ) — 20–40 с, RAID из трёх — 1–2 мин. Для длинных роликов: `--width 80 --height 25` (в ~4 раза меньше) или `--fps 10`.

## Формат OCV5

`index.ocv`, 64 байта, big-endian: `"OCV5"`, `u16 width`, `u16 height`, `u16 fps×100`, `u32 frames`, `u16 parts`, 16×RGB палитра.

`partNNNN.ocv`: кадры подряд, `u32 длина` + команды:

```
01 c          setBackground(PAL[c])
02 c          setForeground(PAL[c])
03 bg fg      оба
04 x y n ...  gpu.set(x, y, n символов), 2 бита на символ: 0=' ' 1='▄' 2='▀' 3='█'
```

Индексы цвета: 0–15 — палитра, 16–255 — куб (`16 + r·40 + g·5 + b`).
