nlve/README.md

1165 lines
97 KiB
Markdown
Raw Normal View History

Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
# nlve
Нелинейный видеоредактор без перекодирования. Куски видео вырезаются и
склеиваются копированием потока, поэтому сборка ролика занимает время
копирования файла, а не время рендера.
Композитинга нет принципиально: когда клипы перекрываются во времени,
побеждает тот, что на верхней дорожке — целиком, и картинкой, и звуком.
Именно это ограничение и делает возможной сборку копированием.
## Сборка
```
./build.sh редактор под эту систему
./build.sh windows редактор под Windows (кросс-сборка)
./build.sh tool консольный инструмент проверок
./build.sh test инструмент плюс прогон проверок
./build.sh all всё перечисленное
./build.sh run собрать и запустить
./build.sh clean убрать промежуточные файлы
```
Используется тулчейн fpcupdeluxe, а не системные fpc и Lazarus: системный
Lazarus другой версии, и собранные им модули LCL с нашими несовместимы.
Другой корень задаётся переменной: `FPCUP=/путь ./build.sh`.
То же самое вручную:
```
/home/chaiman/fpcupdeluxe/lazarus/lazbuild --primary-config-path=/home/chaiman/fpcupdeluxe/config_lazarus nlve.lpi
/home/chaiman/fpcupdeluxe/lazarus/lazbuild --primary-config-path=/home/chaiman/fpcupdeluxe/config_lazarus --cpu=x86_64 --os=win64 --ws=win32 nlve.lpi
```
Требования во время работы: `libmpv.so.2` (пакет `mpv`), `ffmpeg` и `ffprobe`
в `PATH`. Линковки с libav* нет — ffmpeg вызывается внешним процессом.
## Запуск
```
./nlve # пустой проект
./nlve <проект.nlve> # открыть проект
./nlve <видео> <картинка> ... # добавить файлы в новый проект
```
Окно перепаковки открывается из меню «Файл». Здесь раньше значился запуск
`./nlve remux <файл>...` — в коде этого нет и не было: слово `remux` не
является именем файла и молча пропускалось.
### Встраивание в рабочий стол
```
tools/install-desktop.sh # берёт ./nlve рядом с репозиторием
tools/install-desktop.sh /путь/к/nlve
```
Скрипт кладёт пункт меню, значки и описание типа файла — всё в домашний
каталог, без прав администратора. После этого `*.nlve` перестают быть
просто текстом: у них своё имя типа (`application/x-nlve-project`), свой
значок и открытие двойным щелчком. Проверяется так:
```
xdg-mime query filetype проект.nlve # application/x-nlve-project
xdg-mime query default application/x-nlve-project
```
Внутри проект — обычный JSON, и тип объявлен наследником
`application/json`. Наследником простого текста делать не стали: тогда
файл снова открывался бы редактором по умолчанию.
Файловый менеджер может показать новый значок не сразу — ему нужно
перечитать базу.
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
## Управление
| Действие | Клавиши |
|---|---|
| Пуск и пауза | Пробел |
| В начало / конец монтажа | Home / End |
Работа не теряется: автосохранение, видимое сохранение, запасной файл Сохранение теперь отвечает в строке состояния: «Проект сохранён: имя, время». Раньше об этом знал только журнал, а он по умолчанию закрыт — нажал Ctrl+S и не знаешь, дошло ли до файла. Проект пишется во временный файл рядом и подставляется целиком; прежний остаётся как .bak. Оборванная запись больше не может оставить огрызок вместо проекта. Автосохранение: пока есть несохранённое, раз в полминуты рядом с проектом пишется копия .autosave. При открытии программа сравнивает её время с временем проекта и, если копия новее, показывает оба времени и предлагает её открыть. После сохранения проекта копия убирается. Журнал рядом с программой больше не переписывается при каждом запуске: запуски дописываются с заголовком, файл урезается при переросте. Прошлый запуск — как раз тот, в котором обычно и надо разбираться. Клавиша \ подтягивает окно к курсору и ставит его посередине: её жмут после реза или выделения. Переключатель «курсор посередине» остался кнопкой с целью.
2026-09-14 16:06:40 +03:00
| Показать, где курсор (окно едет к нему) | \ |
Выделение областью, рез всех дорожек, курсор не улетает Рамка выделения: протяжка по пустому месту дорожек обтягивает клипы, Delete убирает всё, что она задела. Щелчок без движения по-прежнему ставит курсор. Рез по курсору (S) режет все дорожки, а не ту, что под указателем мыши: стоило указателю оказаться над пустой дорожкой, и клавиша молча не делала ничего. Теперь рез отвечает числом разрезанных клипов. Курсор после правки монтажа улетал в конец: при перезагрузке списка кусков mpv сбрасывает отсчёт времени, программа ждала перемотки, не дожидалась и подтягивала курсор к плееру. На паузе курсор теперь остаётся там, куда его поставил человек. Это и делало Ctrl+Shift+R неудобным: после каждого схлопывания приходилось целиться заново. Схлопывание дыры ищет её с допуском и по всем дорожкам, а не только под мышью, и называет дорожку в строке состояния. Ответ на команду держится в строке состояния несколько секунд: раньше его через долю секунды затирало сообщение о пересборке предпросмотра. Проверки: рамка, рез всех дорожек, допуск при схлопывании (lfmcheck).
2026-09-14 15:27:43 +03:00
| Разрезать по курсору (все дорожки) | S |
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
| Удалить выделенное | Delete |
| Выделить всё | Ctrl+A |
Выделение областью, рез всех дорожек, курсор не улетает Рамка выделения: протяжка по пустому месту дорожек обтягивает клипы, Delete убирает всё, что она задела. Щелчок без движения по-прежнему ставит курсор. Рез по курсору (S) режет все дорожки, а не ту, что под указателем мыши: стоило указателю оказаться над пустой дорожкой, и клавиша молча не делала ничего. Теперь рез отвечает числом разрезанных клипов. Курсор после правки монтажа улетал в конец: при перезагрузке списка кусков mpv сбрасывает отсчёт времени, программа ждала перемотки, не дожидалась и подтягивала курсор к плееру. На паузе курсор теперь остаётся там, куда его поставил человек. Это и делало Ctrl+Shift+R неудобным: после каждого схлопывания приходилось целиться заново. Схлопывание дыры ищет её с допуском и по всем дорожкам, а не только под мышью, и называет дорожку в строке состояния. Ответ на команду держится в строке состояния несколько секунд: раньше его через долю секунды затирало сообщение о пересборке предпросмотра. Проверки: рамка, рез всех дорожек, допуск при схлопывании (lfmcheck).
2026-09-14 15:27:43 +03:00
| Выделить областью | протяжка по пустому месту дорожек |
Выделение по времени, рез по краям промежутка, курсор идёт за резом Режимов выделения стало два, переключаются клавишей D и кнопкой внизу, режим показан её значком. Рамкой по клипам — как было. По времени — протяжка отмечает промежуток по всем дорожкам сразу, он остаётся виден пунктиром, и S режет все дорожки по обоим его краям. Выделенной после этого остаётся ровно середина: следующим действием её обычно удаляют, а выделение на старых клипах убрало бы не то. Курсор теперь идёт за резом: после S встаёт на место реза (в режиме по времени — на начало промежутка), после удаления куска — на место стыка, после схлопывания дыры — туда же. Раньше в это место приходилось попадать мышью заново. Рез кладётся на кадровую сетку: между кадрами времени нет, и рез на доле кадра оставлял бы в монтаже время, которого не бывает. Выделение рамкой требует настоящего перекрытия, а не касания краями: сосед, кончающийся ровно на краю рамки, попадал в выделение из-за разницы в последнем знаке дробей. Кнопка журнала и строка состояния сдвинуты вправо: переключатель выделения встал в один ряд с прилипанием и схлопыванием. Значки — icons8, набор ios-glyphs, авторство дописано в attribution.txt. Набора «ios26» на их раздаче нет (404), взят ближайший по виду к нынешним. Проверки: рез по курсору и по краям промежутка, кадровая сетка, выделение середины, курсор после удаления и после схлопывания.
2026-09-29 23:58:30 +03:00
| Режим выделения: рамкой или по времени | D |
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
| Схлопнуть дыру под курсором | Ctrl+Shift+R |
| Отменить, повторить | Ctrl+Z, Ctrl+Y |
| Масштаб таймлайна, от точки под мышкой | Ctrl + колесо мыши |
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
| Прокрутка по дорожкам | колесо мыши |
| Прокрутка по времени | Shift + колесо, средняя кнопка |
| Прокрутка журнала вбок | Shift + колесо над журналом |
Этап 1: форма звука на клипах Для каждого файла один раз строится список пиков: сто значений в секунду, каждое есть наибольший модуль отсчёта за свои десять миллисекунд. Рисовать по настоящим отсчётам нельзя — в секунде их 48 тысяч, а в клипе на экране сотни пикселей. Разбор идёт в отдельном потоке и запоминается в кэше проекта. Через временный файл, а не по трубе: запуск внешних программ режет вывод на строки, а тут поток байтов, где перевод строки — такой же отсчёт. Shift со стрелками вверх и вниз растягивает форму по высоте. Звук бывает тихим, и угадывать за человека нужное усиление неправильно. Меняется только рисунок, значение сохраняется между запусками. Проверено: файл «4 с тона, 4 с тишины, 4 с тона» даёт форму в первой и последней трети и пустоту посередине. Полная амплитуда даёт 254 из 255. Разбор записи на 892 с занимает 12.7 с — это чтение 6.3 ГБ с диска, быстрее не выйдет. Первоначальный критерий «меньше двух секунд» в плане был занижен и исправлен по замеру. Заодно снят риск четвёртого этапа: mpv принимает edl:// как внешнюю звуковую дорожку и берёт звук именно из неё. Проверено встречными списками — картинке «тишина, потом тон», звуку «тон, потом тишина».
2026-08-19 21:23:24 +03:00
| Растянуть форму звука по высоте | Shift + стрелки вверх и вниз |
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
| Выключить дорожку / оставить только её | кнопки М и С в заголовке |
| Громкость клипа | тянуть ручку на клипе вверх-вниз |
| Автосхлопывание вкл/выкл | Ctrl+K |
Копирование клипов: Ctrl+C и Ctrl+V Отбивку или текстовый блок проще размножить копией, чем заводить заново через меню и настраивать оформление с нуля. Копируются выделенные клипы, вставляются по курсору. Связанные берутся вместе с парой — у картинки со звуком иначе уехала бы половина, — но связка у копий своя: копия и подлинник не должны ездить вместе. Взаимное расположение сохраняется, набор сдвигается к курсору. Дорожка выбирается сама: сначала та, откуда копировали, если это время на ней свободно; потом любая подходящая по виду; в крайнем случае заводится новая. Наложить копию на соседа по дорожке нельзя — это означало бы переход, которого никто не заказывал. В модели появился TClip.CopyAllFrom: полная копия, кроме места на монтаже. Проверка в lfmcheck перебирает перенесённые свойства и отдельно убеждается, что место и связка не переносятся.
2026-09-11 21:14:52 +03:00
| Копировать и вставить клипы | Ctrl+C, Ctrl+V |
| Скопировать и применить оформление | Ctrl+Shift+C, Ctrl+Shift+V |
Этап 5: прозрачность, переходы и затухания Ползунок на клипе картинки теперь задаёт непрозрачность, на звуковом — громкость. Уплощение выдаёт для таких мест не одного победителя, а слои. Слои появляются в трёх случаях: клип полупрозрачен и сквозь него виден нижний; два клипа на одной дорожке наползли друг на друга и должны перетечь один в другой; у клипа задано плавное появление или уход. Перекрытие на разных дорожках переходом не считается — там по-прежнему побеждает верхний. Иначе в проекте, где дорожки перекрываются постоянно, каждое такое место пришлось бы пересобирать. Картинка собирается фильтрами ffmpeg: постоянная доля через overlay, переход через xfade, затухание через fade. Звук сводится прямо в отсчётах — доля каждого слоя считается на каждый отсчёт, и длина результата получается ровно такой, какая нужна. Перед сборкой программа сообщает, сколько секунд из скольких придётся пересобрать, и даёт отказаться. Проверено замерами: - красный с непрозрачностью 50% поверх синего дал (127, 0, 126); - переход на одной дорожке: синий плавно перешёл в красный за секунду; - звуковой переход: -24 dB, -27.5, -35, -51, тишина; - появление за 2 секунды: пересобрано 2 секунды из 6, остальное скопировано. Найдено по дороге: участок с наложением склеивался с соседним куском и терял слои — в условии склейки они не учитывались.
2026-08-20 10:11:20 +03:00
| Связать или развязать клипы | Ctrl+G |
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
| Отменить прилипание на время | Shift при перетаскивании |
| Вписать проект целиком | Ctrl+0 |
| Новый, открыть, сохранить проект | Ctrl+N, Ctrl+O, Ctrl+S |
| Добавить файлы | Ctrl+I |
| Собрать ролик | Ctrl+E |
Файлы попадают в проект через меню «Файл» или перетаскиванием прямо в окно
программы. Место броска решает, что произойдёт: бросили над монтажом — файл
и добавится в проект, и ляжет на дорожку под указателем; в любом другом
месте — только добавится в проект.
Файл из библиотеки кладётся на монтаж перетаскиванием либо через
контекстное меню списка. В нём: **добавить на дорожку** (подменю со
списком дорожек), **кадрирование и поворот**, **убрать со всех дорожек**
(файл остаётся в проекте), **удалить из проекта**. Дыры при этом намеренно не
схлопываются: убрать клип и переставить всё остальное — два разных
намерения.
Если бросить клип ниже всех дорожек, заведётся новая.
В заголовке каждой дорожки слева — её название, число клипов и две кнопки:
**М** выключает дорожку целиком (она не идёт ни в предпросмотр, ни в сборку),
**С** оставляет только её, заглушив остальные. Щелчок по заголовку делает
дорожку выбранной — на неё кладёт файл кнопка.
Клип переносится перетаскиванием, в том числе на другую дорожку и на
пустое место под последней — тогда дорожка тоже добавится. За края клипа
он растягивается и укорачивается. Картинку можно растянуть на любую
длину, видео — только в пределах исходника.
Если тащить к краю окна, монтаж едет следом сам: иначе при большом
увеличении кусок упирается в край, и его приходится переносить в
несколько приёмов — отпустить, прокрутить, взять снова.
Файл из списка проекта встаёт **на курсор**, а не в конец дорожки. Если
место занято, монтаж раздвигается: всё от курсора и правее едет вправо
на длину нового клипа, на всех дорожках сразу — звук и надписи обязаны
уехать вместе с картинкой. Строка состояния говорит, сколько клипов
отодвинуто и на сколько.
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
Щелчок по клипу переставляет курсор воспроизведения. Именно щелчок:
если кнопку нажали и потянули, курсор остаётся на месте, в том числе во
время воспроизведения.
**Прилипание клипов** (кнопка с магнитом). Края клипов притягиваются
друг к другу и к курсору. Shift во время перетаскивания меняет решение на
противоположное: при включённом прилипании временно отключает, при
выключенном — временно включает. Так одна клавиша полезна в обоих
режимах, и не нужно помнить, какой сейчас.
**Прилипание курсора** (кнопка с клеем). Курсор, поставленный щелчком,
притягивается к границам клипов на любой дорожке. Без этого в стык не
попасть: на общем плане одна точка экрана — это секунды материала, а
ставят курсор чаще всего именно на стык. Shift при щелчке работает так
же — меняет решение на противоположное.
Переключателя два, а не один, потому что это разные привычки: клипы
липнут при перетаскивании, курсор — при щелчке, и одно бывает нужно без
другого.
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
Работа не теряется: автосохранение, видимое сохранение, запасной файл Сохранение теперь отвечает в строке состояния: «Проект сохранён: имя, время». Раньше об этом знал только журнал, а он по умолчанию закрыт — нажал Ctrl+S и не знаешь, дошло ли до файла. Проект пишется во временный файл рядом и подставляется целиком; прежний остаётся как .bak. Оборванная запись больше не может оставить огрызок вместо проекта. Автосохранение: пока есть несохранённое, раз в полминуты рядом с проектом пишется копия .autosave. При открытии программа сравнивает её время с временем проекта и, если копия новее, показывает оба времени и предлагает её открыть. После сохранения проекта копия убирается. Журнал рядом с программой больше не переписывается при каждом запуске: запуски дописываются с заголовком, файл урезается при переросте. Прошлый запуск — как раз тот, в котором обычно и надо разбираться. Клавиша \ подтягивает окно к курсору и ставит его посередине: её жмут после реза или выделения. Переключатель «курсор посередине» остался кнопкой с целью.
2026-09-14 16:06:40 +03:00
**Курсор посередине** (кнопка с целью). Включено — при
воспроизведении монтаж едет под курсором, и всегда видно, что будет
дальше. Выключено — окно не двигается вовсе, даже когда курсор ушёл за
край: отмотал Shift с колесом — там и останешься. Раньше в выключенном
состоянии окно всё равно подтягивалось к курсору, стоило тому выйти из
видимой части, и прокрутка отскакивала назад со второго поворота колеса.
**Ключ и прозрачность в предпросмотре** (меню «Вид»). Включено — окно
показывает то же, что даст сборка: выбитый цвет и приглушение считаются
на лету. Выключено — оверлей виден целиком, со своим фоном: так понятно,
куда он встал и где его края.
Считается это по площади самого оверлея, а не всего кадра. Замер на
2688x1512 (600 кадров, десять секунд материала): основа 2.07 с, с
наложением 2.43 с, с ключом и прозрачностью 2.66 с. То есть наложение
стоит 0.36 с, ключ добавляет ещё 0.23 с — вчетверо быстрее реального
времени. На слабой машине это может быть заметнее, потому выключатель и
есть.
Текст вжигается и в фотографию; дорожки и полоса участка Вжигание не доходило до сгенерированных вставок. Картинки и чёрное отбирались первой же веткой сборки — до проверки на пересборку — и копировались целиком вместе с отметкой о разметке. Галка вжигания стояла, кусок в журнале был помечен «с текстом», а в ролике надписи не было. Причину показал журнал сборки рядом с файлом: помечено «пересобирается», а команда — копирование. Теперь вставка с разметкой идёт общей веткой пересборки, и время у неё берётся своё: она начинается с нуля, а не с середины монтажа. У линейки появился верхний этаж в двенадцать точек — про отметки. Протяжка по нему отмечает участок, даже если отметок не было; щелчок без движения их снимает. Уголки по краям тянутся, как и раньше. Заголовок дорожки: правый щелчок открывает меню — поднять, опустить, удалить (с клипами — с подтверждением). Заголовок можно и потащить: порядок дорожек есть порядок перекрытия, и менять его перетаскиванием привычнее, чем через меню.
2026-08-31 12:01:12 +03:00
**Отмеченный участок.** У линейки два этажа. Верхний, в двенадцать точек,
— про отметки: протяжка по нему отмечает участок, даже если отметок не
было, а щелчок без движения их снимает. По краям участка стоят жёлтые
уголки, за них тянут. Нижний этаж — про курсор, как и раньше.
Клавиши тоже остались: `I` ставит начало по курсору, `O` — конец; оба
пункта есть в меню «Правка», там же снятие отметок. Пока он отмечен, сборка
Участок сборки, качество пересборки и пропавшие куски Отмеченный участок. Клавиши I и O ставят начало и конец по курсору, полоса под линейкой показывает отмеченное, сборка собирает только его. Без этого проверить правку на длинном монтаже нельзя: каждая проверка стоит столько же, сколько идёт весь ролик. Отметки живут в проекте. Разметка текста при этом берётся с начала участка, иначе надписи легли бы со сдвигом. Фотография пропадала из ролика. Кусок с наложением подаёт слои в ffmpeg как есть, а одиночная картинка даёт ровно один кадр: кусок в три секунды получался длиной 0.016667 с. Теперь у слоя есть признак «это картинка», и она повторяется ключом -loop с частотой кадров проекта. Текст пропадал там же. Вжигание помечает кусок разметкой, но ветка наложения про неё не знала. Пока оверлей лежит на всём ролике, этой веткой идут все куски — и текста не было нигде. Теперь разметка вжигается последним звеном цепочки, поверх сведённой картинки. Качество пересобранных кусков. Битрейт не задавался вовсе, и кодировщик брал своё значение по умолчанию: 5.9 Мбит/с там, где исходник 56. Теперь пересборка целится в наибольший битрейт среди файлов проекта, с потолком в полтора раза выше и буфером вдвое. Тем же битрейтом делаются вставки из фотографий. Проверки: обрезка списка кусков по участку (nlvetool flattest, 9), сборка фотографии под наложением и вжигание текста поверх наложения (nlvetool render ... burn).
2026-08-31 10:37:25 +03:00
собирает только его — это единственный способ проверить правку на длинном
монтаже, не тратя на каждую проверку столько же времени, сколько идёт сам
ролик. Отметки живут в файле проекта: участок относится к монтажу, а не к
привычкам человека.
Свойства клипа, порядок дорожек, оценка времени и продолжение сборки Копирование оформления: Ctrl+Shift+C берёт с выделенного клипа кадрирование, поворот, увеличение, смещение, прозрачность, ключ, затухания и громкость, Ctrl+Shift+V применяет их ко всем выделенным. Вид клипа и текст не переносятся: свойства копируют между разными клипами. Порядок дорожек: слева в заголовке две стрелки, у крайних дорожек соответствующая нарисована тускло и не нажимается. Двойной щелчок по названию переименовывает дорожку — на монтаже из десятка дорожек «карта» и «титры» читаются лучше, чем V1 и Т1. То же есть и в меню по правому щелчку. Ввод имени — своим тёмным окном: системный InputQuery выглядел бы белым пятном. Окно сборки показывает, сколько прошло и сколько примерно осталось. Оценка по пройденной доле; скорость на деле меняется, поэтому «примерно» и пересчёт на каждом шаге. Прерванная сборка продолжается с готовых кусков. Каждый доведённый до конца кусок помечается в списке рядом с ними: одного наличия файла мало — сборку могли прервать посреди записи. Первая строка списка — отпечаток плана; не совпал, значит монтаж менялся, и всё собирается заново. Проверено: четыре куска взяты готовыми, два пересобраны, склейка не потеряла ни одного кадра; при изменённом монтаже готовые куски отвергнуты.
2026-08-31 14:55:19 +03:00
**Дорожки.** Слева в заголовке две стрелки: поднять и опустить. У верхней
дорожки некуда вверх, у нижней вниз — такая стрелка нарисована тускло и
не нажимается. Заголовок можно и просто потащить мышью. Двойной щелчок по
названию переименовывает дорожку: на монтаже из десятка дорожек «карта»,
«звук мотора» и «титры» читаются несравнимо лучше, чем V1, A2 и Т1.
Правый щелчок по заголовку открывает меню: поднять, опустить,
переименовать, удалить. Удаление дорожки с клипами спрашивает
подтверждение и называет число клипов.
Копирование клипов: Ctrl+C и Ctrl+V Отбивку или текстовый блок проще размножить копией, чем заводить заново через меню и настраивать оформление с нуля. Копируются выделенные клипы, вставляются по курсору. Связанные берутся вместе с парой — у картинки со звуком иначе уехала бы половина, — но связка у копий своя: копия и подлинник не должны ездить вместе. Взаимное расположение сохраняется, набор сдвигается к курсору. Дорожка выбирается сама: сначала та, откуда копировали, если это время на ней свободно; потом любая подходящая по виду; в крайнем случае заводится новая. Наложить копию на соседа по дорожке нельзя — это означало бы переход, которого никто не заказывал. В модели появился TClip.CopyAllFrom: полная копия, кроме места на монтаже. Проверка в lfmcheck перебирает перенесённые свойства и отдельно убеждается, что место и связка не переносятся.
2026-09-11 21:14:52 +03:00
**Копирование клипов.** Ctrl+C копирует выделенные клипы, Ctrl+V кладёт
копии по курсору. Отбивку или текстовый блок так размножить проще, чем
заводить заново и настраивать оформление с нуля.
Связанные клипы копируются с парой: у картинки со звуком иначе уехала бы
половина. Связка у копий своя — копия и подлинник не ездят вместе.
Взаимное расположение сохраняется: несколько клипов встают тем же узором,
сдвинутым к курсору.
Дорожка выбирается сама: сначала та, откуда копировали, если это время на
ней свободно; потом любая подходящая по виду; в крайнем случае заводится
новая. Класть копию поверх соседа нельзя — на одной дорожке это означает
переход, а его никто не заказывал.
Свойства клипа, порядок дорожек, оценка времени и продолжение сборки Копирование оформления: Ctrl+Shift+C берёт с выделенного клипа кадрирование, поворот, увеличение, смещение, прозрачность, ключ, затухания и громкость, Ctrl+Shift+V применяет их ко всем выделенным. Вид клипа и текст не переносятся: свойства копируют между разными клипами. Порядок дорожек: слева в заголовке две стрелки, у крайних дорожек соответствующая нарисована тускло и не нажимается. Двойной щелчок по названию переименовывает дорожку — на монтаже из десятка дорожек «карта» и «титры» читаются лучше, чем V1 и Т1. То же есть и в меню по правому щелчку. Ввод имени — своим тёмным окном: системный InputQuery выглядел бы белым пятном. Окно сборки показывает, сколько прошло и сколько примерно осталось. Оценка по пройденной доле; скорость на деле меняется, поэтому «примерно» и пересчёт на каждом шаге. Прерванная сборка продолжается с готовых кусков. Каждый доведённый до конца кусок помечается в списке рядом с ними: одного наличия файла мало — сборку могли прервать посреди записи. Первая строка списка — отпечаток плана; не совпал, значит монтаж менялся, и всё собирается заново. Проверено: четыре куска взяты готовыми, два пересобраны, склейка не потеряла ни одного кадра; при изменённом монтаже готовые куски отвергнуты.
2026-08-31 14:55:19 +03:00
**Копирование свойств клипа.** Ctrl+Shift+C берёт с выделенного клипа всё
оформление — кадрирование, поворот, увеличение, смещение, прозрачность,
цветовой ключ, затухания, громкость, — Ctrl+Shift+V применяет их ко всем
выделенным. Вид клипа и его текст при этом не переносятся: свойства
копируют между разными клипами, и превращать картинку в надпись незачем.
Текст вжигается и в фотографию; дорожки и полоса участка Вжигание не доходило до сгенерированных вставок. Картинки и чёрное отбирались первой же веткой сборки — до проверки на пересборку — и копировались целиком вместе с отметкой о разметке. Галка вжигания стояла, кусок в журнале был помечен «с текстом», а в ролике надписи не было. Причину показал журнал сборки рядом с файлом: помечено «пересобирается», а команда — копирование. Теперь вставка с разметкой идёт общей веткой пересборки, и время у неё берётся своё: она начинается с нуля, а не с середины монтажа. У линейки появился верхний этаж в двенадцать точек — про отметки. Протяжка по нему отмечает участок, даже если отметок не было; щелчок без движения их снимает. Уголки по краям тянутся, как и раньше. Заголовок дорожки: правый щелчок открывает меню — поднять, опустить, удалить (с клипами — с подтверждением). Заголовок можно и потащить: порядок дорожек есть порядок перекрытия, и менять его перетаскиванием привычнее, чем через меню.
2026-08-31 12:01:12 +03:00
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
**Схлопывание.** Кнопка внизу показывает и режим, и состояние: утоплена —
автосхлопывание включено, и тогда при перетаскивании клипа всё, что
начинается не раньше него, едет следом. Стрелка рядом открывает выбор
режима: только текущая дорожка или все. Ctrl+K включает и выключает.
Режим действует и при выключенном автосхлопывании — по нему работает
Ctrl+Shift+R, который схлопывает дыру под курсором. То есть выключенная
кнопка означает лишь «не двигать ничего само собой при перетаскивании».
Во время воспроизведения курсор держится посередине окна, а монтаж едет
под ним. При нажатии на пуск, если курсор оказался за краем видимой
части, вид подтягивается к нему — масштаб при этом не меняется.
На каждом клипе проходит линия громкости с ручкой посередине видимой его
части: тянешь ручку вверх и вниз — меняется громкость, от тишины до
двойной. Ручка стоит посередине именно видимой части, а не всего клипа:
до середины часового клипа, уехавшей за край экрана, мышью не дотянуться.
Ручку затухания можно взять и во второй раз Зона захвата совпадала с нарисованным квадратиком — девять точек на девять. Пока затухание нулевое, ручка стоит в углу клипа и целиться есть куда; стоит её сдвинуть, она висит посреди клипа сама по себе, и попасть в неё почти нельзя. Зона захвата теперь шире нарисованного: по шесть точек в стороны и семь вниз, внутрь клипа, — рука уводит курсор туда, а не выше клипа. Если на узком клипе зоны обеих ручек налезли друг на друга, берётся та, к которой ближе. Заодно над зоной меняется курсор. Раньше над ручкой он был такой же, как над всем клипом, и где она — узнать было нельзя. Проверено протяжкой мыши на невидимом экране (tests/fadegrab.sh): первый захват за угол даёт затухание 1.387 с, второй — с прицелом на пять точек ниже и четыре в стороне от середины ручки — доводит до 2.261 с. На сборке из прошлого коммита второй захват не делает ничего.
2026-10-01 16:13:37 +03:00
**Затухания тянутся за ручки в верхних углах клипа.** Взять их можно и
промахнувшись: зона захвата шире нарисованного квадратика и уходит внутрь
клипа. Пока затухание нулевое, ручка стоит в самом углу и целиться есть
куда; стоит её сдвинуть — она висит посреди клипа сама по себе, и попасть
в девять точек почти нельзя. Над зоной захвата меняется курсор — по нему
и видно, где взять (`tests/fadegrab.sh`).
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
Громкость, отличная от обычной, — единственное место, где звук
перекодируется: изменить громкость, не тронув сам звук, нельзя. Видео при
этом по-прежнему копируется. Проверено: клип с громкостью 30% дал в ролике
уровень на 10.5 дБ ниже исходного при ожидаемых 10.46 дБ.
Полоски по краям клипа показывают тип реза: серая — притянут к опорному
кадру, голубая — точный, стык будет перекодирован при сборке. Если край
подвинуть руками и он не попадёт на опорный кадр, рез сам становится
точным: раз человек выбрал конкретный кадр, притягивать его к соседнему
неправильно.
В списке файлов слева показывается кадр из каждого файла, его параметры
и длительность. Кадры делаются в фоне и запоминаются в кэше проекта.
Этап 1: форма звука на клипах Для каждого файла один раз строится список пиков: сто значений в секунду, каждое есть наибольший модуль отсчёта за свои десять миллисекунд. Рисовать по настоящим отсчётам нельзя — в секунде их 48 тысяч, а в клипе на экране сотни пикселей. Разбор идёт в отдельном потоке и запоминается в кэше проекта. Через временный файл, а не по трубе: запуск внешних программ режет вывод на строки, а тут поток байтов, где перевод строки — такой же отсчёт. Shift со стрелками вверх и вниз растягивает форму по высоте. Звук бывает тихим, и угадывать за человека нужное усиление неправильно. Меняется только рисунок, значение сохраняется между запусками. Проверено: файл «4 с тона, 4 с тишины, 4 с тона» даёт форму в первой и последней трети и пустоту посередине. Полная амплитуда даёт 254 из 255. Разбор записи на 892 с занимает 12.7 с — это чтение 6.3 ГБ с диска, быстрее не выйдет. Первоначальный критерий «меньше двух секунд» в плане был занижен и исправлен по замеру. Заодно снят риск четвёртого этапа: mpv принимает edl:// как внешнюю звуковую дорожку и берёт звук именно из неё. Проверено встречными списками — картинке «тишина, потом тон», звуку «тон, потом тишина».
2026-08-19 21:23:24 +03:00
На клипах видна форма звука. Считается она по заранее разобранным пикам —
сто значений в секунду, каждое есть наибольший модуль отсчёта за свои
десять миллисекунд. Рисовать по настоящим отсчётам нельзя: в секунде их
48 тысяч, а в клипе на экране сотни пикселей.
Разбор идёт в фоне и запоминается в кэше проекта. Для записи на 892 с это
около 13 секунд — столько занимает чтение шести гигабайт с диска, и
быстрее не выйдет; при следующем открытии проекта пики берутся готовыми.
**Shift со стрелками вверх и вниз растягивает форму по высоте.** Звук
бывает тихим, и угадывать за человека, во сколько раз растянуть картинку,
неправильно. Усиление меняет только рисунок, самого звука не касается, и
запоминается между запусками.
Работа не теряется: автосохранение, видимое сохранение, запасной файл Сохранение теперь отвечает в строке состояния: «Проект сохранён: имя, время». Раньше об этом знал только журнал, а он по умолчанию закрыт — нажал Ctrl+S и не знаешь, дошло ли до файла. Проект пишется во временный файл рядом и подставляется целиком; прежний остаётся как .bak. Оборванная запись больше не может оставить огрызок вместо проекта. Автосохранение: пока есть несохранённое, раз в полминуты рядом с проектом пишется копия .autosave. При открытии программа сравнивает её время с временем проекта и, если копия новее, показывает оба времени и предлагает её открыть. После сохранения проекта копия убирается. Журнал рядом с программой больше не переписывается при каждом запуске: запуски дописываются с заголовком, файл урезается при переросте. Прошлый запуск — как раз тот, в котором обычно и надо разбираться. Клавиша \ подтягивает окно к курсору и ставит его посередине: её жмут после реза или выделения. Переключатель «курсор посередине» остался кнопкой с целью.
2026-09-14 16:06:40 +03:00
## Сохранение и автосохранение
**Сохранение видно.** После Ctrl+S внизу появляется «Проект сохранён:
имя, время». Раньше об этом говорила только строчка в журнале, а журнал
по умолчанию закрыт: нажал — и непонятно, дошло до файла или нет.
**Запись не оставляет огрызков.** Проект пишется во временный файл рядом
и подставляется на место настоящего, только когда записан целиком.
Прежний файл при этом становится запасным — `имя.nlve.bak`. Падение или
кончившееся место посреди записи больше не оставляют половину проекта.
**Копия пишется сама.** Пока в проекте есть несохранённое, раз в полминуты
рядом появляется `имя.nlve.autosave`. Это не сохранение: файл проекта не
трогается, признак «изменён» остаётся.
При открытии проекта программа смотрит, нет ли рядом копии новее его
самого. Есть — значит прошлая работа до файла не дошла, и программа
показывает оба времени и предлагает открыть копию. Отказ копию не
удаляет: она лежит, пока проект не сохранят заново. После сохранения
копия не нужна и убирается.
Кнопки внизу: подсказка не перехватывает щелчок, щелчки не теряются Две беды у всех кнопок нижней полосы. Всплывающая подсказка появлялась прямо поверх кнопки, о которой рассказывает, и перехватывала щелчок: нажать кнопку было нельзя. Подсказка переехала в строку состояния — она в той же полосе, ничего не закрывает и меняется сразу после щелчка, не дожидаясь нового наведения. Строка состояния теперь занимает всё свободное место справа, пока рядом не показан ход работы, а самые длинные подсказки укорочены: раньше их обрезало на полуслове. Второй щелчок подряд виджетсет отдавал как двойной, и кнопка его не видела: быстрая очередь щелчков срабатывала через раз. У кнопки убран csDoubleClicks, а на случай, если двойной всё же дойдёт, он считается за обычный. Проверено: tests/lfmcheck, раздел «Кнопки нижней панели», и tests/buttonclicks.sh мышью на невидимом экране — по всем шести переключателям очереди из 1-5 щелчков отрабатываются полностью.
2026-09-30 15:44:29 +03:00
## Кнопки внизу
Переключатели в нижней полосе — прилипание, прилипание курсора, курсор
посередине, автосхлопывание с выбором режима, режим выделения, журнал.
**Подсказка идёт в строку состояния, а не во всплывающее окно.**
Всплывающее окно появлялось прямо поверх кнопки, о которой
рассказывало, и перехватывало щелчок: нажать кнопку было нельзя, пока
подсказка не уйдёт. Теперь наведение мыши пишет ту же фразу в строку
состояния справа от кнопок — она ничего не закрывает. Щелчок по кнопке
подсказку тут же переписывает: «Журнал виден» становится «Журнал
спрятан».
Строка состояния занимает всё свободное место справа и ужимается только
на время, пока рядом показан ход работы.
**Каждый щелчок считается.** Раньше второй щелчок подряд оконная
библиотека отдавала как двойной, и до кнопки он не доходил: нажимаешь
журнал три раза быстро — срабатывает один. Теперь двойных щелчков
кнопкам не приходит вовсе, и любая очередь щелчков отрабатывается вся
(`tests/lfmcheck`, раздел «Кнопки нижней панели»; `tests/buttonclicks.sh`
проверяет то же самое мышью на невидимом экране).
Выделение по времени, рез по краям промежутка, курсор идёт за резом Режимов выделения стало два, переключаются клавишей D и кнопкой внизу, режим показан её значком. Рамкой по клипам — как было. По времени — протяжка отмечает промежуток по всем дорожкам сразу, он остаётся виден пунктиром, и S режет все дорожки по обоим его краям. Выделенной после этого остаётся ровно середина: следующим действием её обычно удаляют, а выделение на старых клипах убрало бы не то. Курсор теперь идёт за резом: после S встаёт на место реза (в режиме по времени — на начало промежутка), после удаления куска — на место стыка, после схлопывания дыры — туда же. Раньше в это место приходилось попадать мышью заново. Рез кладётся на кадровую сетку: между кадрами времени нет, и рез на доле кадра оставлял бы в монтаже время, которого не бывает. Выделение рамкой требует настоящего перекрытия, а не касания краями: сосед, кончающийся ровно на краю рамки, попадал в выделение из-за разницы в последнем знаке дробей. Кнопка журнала и строка состояния сдвинуты вправо: переключатель выделения встал в один ряд с прилипанием и схлопыванием. Значки — icons8, набор ios-glyphs, авторство дописано в attribution.txt. Набора «ios26» на их раздаче нет (404), взят ближайший по виду к нынешним. Проверки: рез по курсору и по краям промежутка, кадровая сетка, выделение середины, курсор после удаления и после схлопывания.
2026-09-29 23:58:30 +03:00
## Два режима выделения
Кнопка внизу (справа от схлопывания) и клавиша `D`. Режим показан
значком: рамка — выделение клипов, промежуток со стрелкой — выделение по
времени.
**Рамкой по клипам.** Протяжка по пустому месту дорожек выделяет всё, что
рамка задела. Обычное выделение: выделил, нажал Delete.
**По времени.** Протяжка отмечает промежуток от и до — по всем дорожкам
сразу, сколько бы их ни было. Промежуток виден пунктиром и после того,
как кнопку отпустили: по нему режет `S`.
Зачем второй режим: вырезать из фильма кусок целиком. Отметил промежуток,
нажал `S` — все дорожки разрезаны в двух местах, по краям промежутка, и
выделенной осталась ровно середина. Дальше Delete, и при включённом
автосхлопывании дыра закрывается сама.
Края промежутка кладутся на кадровую сетку: между кадрами времени нет.
**Курсор идёт за резом.** После `S` он встаёт ровно на место реза (в
режиме по времени — на начало промежутка), после удаления куска и
схлопывания — на место стыка. Дальше с этим местом и работают: следующий
рез, схлопывание дыры, просмотр перехода. Раньше курсор оставался там, где
был, и в место реза приходилось попадать мышью заново.
Починка проекта: клипы на кадровую сетку, пункт в меню Меню «Проект» → «Проверить и починить проект...». Показывает найденное и чинит только по согласию, отменяется через Ctrl+Z. Ищутся клипы мимо кадровой сетки, клипы на дорожках чужого вида и повторяющиеся имена дорожек. Первое — наследство: в старых проектах времена записаны как попало (2.457599 с при 59.94 к/с — это 147.31 кадра), и пока монтаж просит доли кадра, предпросмотр и сборка округляют их каждый по-своему. Сдвиг при починке не больше полкадра. Края отмеченного участка при сборке тоже кладутся на сетку: подрезка возвращала в крайние куски доли кадра, которые уплощение только что убрало. Проверено на копии настоящего проекта: 82 клипа, после починки мимо сетки ни одного, клипы остались встык, общая длина изменилась на 0.028 с за два часа; Ctrl+Z возвращает как было. Плюс проверка в lfmcheck, включая клип короче кадра — он не должен схлопнуться в ничто.
2026-09-16 20:39:33 +03:00
## Починка проекта
Меню «Проект» → **Проверить и починить проект...**. Показывает, что нашла,
и чинит только по согласию; отменяется через Ctrl+Z.
Что ищется:
- **клипы мимо кадровой сетки.** Между кадрами времени нет, а в старых
проектах времена записаны как попало: 2.457599 с при 59.94 к/с — это
147.31 кадра. Картинка всё равно выйдет целым числом кадров, звук
наберётся с точностью до отсчёта, и предпросмотр со сборкой не смогут
совпасть в точности. Починка двигает клип не больше чем на полкадра;
на проекте из 82 клипов общая длина изменилась на 0.028 с, то есть на
полтора кадра за два часа;
- **клипы на дорожках чужого вида.** Такой клип нарисован на монтаже, но
в готовый ролик не попадает. То же самое предлагается при открытии
проекта, здесь — по требованию;
- **дорожки с повторяющимися именами.**
Для синхронности звука с картинкой чинить проект не нужно: рез правится
при сборке, и старый проект соберётся сходящимся. Починка — про то, чтобы
монтаж не хранил невозможных времён.
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
## Файл не совпадает с проектом
Проект имеет одно разрешение и один набор кодеков на всё — иначе куски не
склеить копированием. Файл с другими параметрами можно добавить, но его
куски скопировать не выйдет.
Пересобираются **только они**. Остальной материал по-прежнему копируется
без потерь и почти мгновенно. Ролик целиком не перекодируется никогда.
Этап 2: кадрирование на клипе с перетаскиваемой рамкой Настройка кадрирования переехала с файла на клип: два куска одного файла бывает нужно кадрировать по-разному. У файла осталась такая же, но лишь как заготовка — её берёт новый клип при добавлении. Проекты, записанные раньше, читаются: клип без своей настройки берёт её у файла. Открывается кнопкой в правом нижнем видимом углу клипа. Именно видимом: у клипа длиной в час настоящий угол почти всегда за краем экрана — та же задача, что уже решена для ручки громкости. В окне слева появился кадр источника с рамкой, которую тянут мышью за середину и за правый нижний угол. Рамка держит пропорции кадра проекта и пересчитывается в увеличение и смещение; ползунки и рамка следуют друг за другом. Кусок пересобирается теперь не только когда файл не совпадает с проектом, но и когда у клипа своё кадрирование. Проверено сборкой: первый кусок без кадрирования скопирован, второй с увеличением вдвое пересобран через scale=1280:720,crop=640:360:384:180. 154 пакета вошло, 154 вышло, декодирование без ошибок. Заодно: правый щелчок по списку файлов теперь выделяет файл под курсором. Без этого меню без выделения отключалось целиком и выглядело так, будто его нет. Цвет формы звука сменён на пастельно-жёлтый.
2026-08-19 23:10:08 +03:00
Как именно кадр ложится в кадр проекта, решает окно «Кадрирование и
поворот»: вписать целиком с полями, заполнить кадр с обрезкой или
растянуть, плюс поворот на 90, отражение, увеличение и смещение.
Слева в окне — кадр источника с рамкой: её тянут мышью за середину и за
правый нижний угол, и она держит пропорции кадра проекта. Справа —
настоящий результат, пропущенный через ту же цепочку фильтров, что пойдёт
в дело.
**Настройка принадлежит клипу**, а открывается кнопкой в правом нижнем
видимом углу самого клипа. Именно видимом: у клипа длиной в час настоящий
угол почти всегда за краем экрана. Два куска одного файла можно
кадрировать по-разному.
У файла в библиотеке хранится такая же настройка, но она лишь заготовка:
её берёт новый клип при добавлении. Правится она из контекстного меню
библиотеки.
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
По умолчанию кадр вписывается целиком. Растяжение надо выбрать руками:
раньше оно применялось молча, и вертикальное видео размазывалось по ширине.
«Приведение к параметрам проекта» делает ту же работу один раз при
добавлении: файл перекодируется целиком, зато каждая последующая сборка
остаётся быстрой.
Что и почему происходит с каждым файлом, видно в окне «Проект → Сведения
о проекте»: параметры проекта, параметры каждого файла, выбранное
вписывание и вердикт — копируется, пересобирается или из него делается
клип. Там же кнопка полного ответа ffprobe.
### Что показывает предпросмотр
Предпросмотр показывает **кадр проекта**, а не кадр исходника. Файл другого
соотношения сторон виден с полями по краям — ровно так, как он попадёт в
готовый ролик. Проверено сравнением: кадр предпросмотра и кадр сборки
совпадают бит в бит.
Само по себе это не бесплатно, поэтому включается только когда есть что
вписывать. Разное разрешение при одинаковом соотношении сторон (скажем,
3840x2160 в проекте 2688x1512) ничего не требует: mpv сам впишет такой
файл в панель, и картинка совпадёт до пикселя.
Когда вписывание нужно, декодирование переключается на вариант с
копированием кадров в обычную память — цепочка фильтров не умеет работать
с кадрами, лежащими в памяти видеокарты. Аппаратное декодирование при этом
сохраняется, видно в журнале: `nvdec` без вписывания, `nvdec-copy` с ним.
Замер на материале 2688x1512 при 60 к/с: 327 кадров в секунду без фильтра,
230 с настоящим масштабированием — запас четырёхкратный.
Предпросмотр показывает кадрирование клипа Клип со своим кадрированием играл в окне во весь кадр: цепочка фильтров у mpv одна на весь список кусков, а включить её на одном куске и выключить на следующем нечем — из фильтров ffmpeg по времени переключается только наложение. Обошлось без цепочки вовсе. У вывода mpv своя обрезка, и делает её видеокарта: курсор зашёл в клип с кадрированием — выставили, вышел — сняли. Ни перестройки цепочки, ни копирования кадров из памяти видеокарты в обычную. Рамку источника считает теперь один код на две стороны: по ней окно «Кадрирование и поворот» рисует рамку, и её же плеер отдаёт как обрезку. Проверка в tests/lfmcheck сверяет рамку с фильтром сборки: увеличение 2.77 даёт 970x546 посередине, и это ровно то, что вырезает crop=2688:1512:2379:1338 после scale=7446:4188. Проверено глазами на невидимом экране, на настоящем материале с дрона: картинка в окне совпала с готовым роликом, на границе с обычным клипом обрезка снимается и возвращается. Сверено со сборкой из прошлого коммита — там на том же месте кадр без кадрирования. Не покрыто и записано в BACKLOG: отражение (свойства для него нет), растяжение и участки с наложением — там кадр уже сведён цепочкой.
2026-10-01 00:04:57 +03:00
**Кадрирование клипа окно показывает.** Цепочка фильтров у mpv одна на всю
склейку, а кадрирование у каждого клипа своё, и включать его по времени в
цепочке нечем: из фильтров ffmpeg по времени переключается только
наложение. Зато у вывода есть своя обрезка, и делает её видеокарта даром.
Курсор зашёл в клип со своим кадрированием — обрезка выставляется, вышел —
снимается. Ни лишней цепочки, ни копирования кадров из памяти видеокарты.
Рамку считает один и тот же код, что рисует её в окне «Кадрирование и
поворот», — иначе окно обещало бы одно, а предпросмотр показывал другое
(`TransformSourceRect` в `uTransform`, проверка в `tests/lfmcheck`).
Чего это не покрывает: отражение (свойства для него у вывода нет),
растяжение (там кадр жмётся неравномерно, рамкой не описать) и участки с
наложением — там кадр уже сведён цепочкой, и обрезка резала бы сведённое
целиком, вместе с нижним слоем. Для таких клипов точную картинку
по-прежнему показывает окно «Кадрирование и поворот».
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
## Цвета окна
Все цвета заданы в `uTheme` и применяются принудительно, а не берутся у
системы. Причина простая: под Linux вид назначает тема GTK, под Windows —
тема оформления, и на светлой теме тёмный таймлайн оказывается вставленным
в белую рамку.
Что для этого пришлось сделать:
- **Свои кнопки** (`TThemedButton`). Системная кнопка под Windows свой цвет
не отдаёт: LCL рисует её через службу оформления, и та кладёт светлую
подложку поверх заданного цвета. Проверено по исходникам LCL —
`TCustomSpeedButton.PaintBackground` всегда вызывает
`ThemeServices.DrawElement`. Поведение обычной кнопки сохранено:
`Default`, `Cancel`, `ModalResult`, работа по Enter и Esc.
- **Своя полоса меню** из плоских кнопок со всплывающими списками.
Системную полосу рисует Windows, и её фон нашим цветом не закрашивается:
`SetMenuInfo` меняет подложку пунктов, но не самой полосы. Сочетания
клавиш при этом не теряются — их ищет `TMenu.IsShortCut`, вызываемый из
`TMainForm.FormShortCut`. Ценой стал переход по меню клавишей Alt: его
больше нет.
- **Своё окно сообщения** (`ThemedMessageDlg`) вместо `MessageDlg`.
- **Отрисовка пунктов меню и строк выпадающих списков** своими руками.
Чего сделать нельзя, и это надо знать:
- полосы прокрутки рисует система, и цвет им задать нечем. Единственный
способ получить их в своих цветах — не пользоваться системными вовсе.
Так и сделано: журнал (`uLogView`) и таблицы (`uThemedScroll`) обходятся
своими. Осталась системная полоса у списка файлов слева — там её нечем
выключить, не переписав сам список;
- рамка окна и её кнопки принадлежат оконному менеджеру;
- под gtk2 пункты меню своими руками не рисуются — в LCL это прямо
помечено как неподдерживаемое (`interfaces/gtk2/issues.xml`), там их
цвет назначает тема GTK. Под Windows рисуются, там это и нужно.
## Зависимости и запуск под Wine
Программе нужны `ffmpeg`, `ffprobe` и `libmpv`. Под Linux они ставятся
пакетным менеджером. Под Windows ставить нечем, поэтому файлы кладутся
рядом с программой, а меню «Файл → Зависимости...» умеет их скачать.
Список и адреса лежат в `deps-windows.json` или `deps-linux.json` рядом с
программой — имя своё для каждой системы. Общий файл приводил к тому, что
запущенная под Wine программа записывала windows-список, а сборка под
Linux потом искала по нему `.dll`.
Где имя файла сборки меняется с каждой датой, адрес не задаётся: программа
спрашивает последний выпуск у GitHub. Архивы `.7z` распаковываются внешним
`7zr.exe` — он тоже есть в списке и качается одним файлом, поэтому
замкнутого круга не возникает.
Под Wine программа узнаёт себя по наличию `wine_get_version` в `ntdll` и
настраивается сразу:
- **вывод через OpenGL**, а не Direct3D 11: последний в Wine падает при
создании контекста, ещё до первого кадра;
- **аппаратное декодирование выключено**. Дело не в бесполезных попытках:
для DXVA2 mpv создаёт устройство Direct3D 9, а Wine при его создании
сбрасывает гамму экрана в единицу — настройку всей системы. Замерено:
с `auto-safe` гамма 0.89 превращалась в 1.0 при каждом открытии
проекта, с `no` остаётся нетронутой.
На настоящей Windows обе настройки остаются обычными.
Журнал дублируется в `nlve.log` рядом с программой. Нужно ровно для
случая, когда программа падает до появления окна: иначе всё, что она о
себе рассказала, пропадает вместе с окном.
Этап 5: прозрачность, переходы и затухания Ползунок на клипе картинки теперь задаёт непрозрачность, на звуковом — громкость. Уплощение выдаёт для таких мест не одного победителя, а слои. Слои появляются в трёх случаях: клип полупрозрачен и сквозь него виден нижний; два клипа на одной дорожке наползли друг на друга и должны перетечь один в другой; у клипа задано плавное появление или уход. Перекрытие на разных дорожках переходом не считается — там по-прежнему побеждает верхний. Иначе в проекте, где дорожки перекрываются постоянно, каждое такое место пришлось бы пересобирать. Картинка собирается фильтрами ffmpeg: постоянная доля через overlay, переход через xfade, затухание через fade. Звук сводится прямо в отсчётах — доля каждого слоя считается на каждый отсчёт, и длина результата получается ровно такой, какая нужна. Перед сборкой программа сообщает, сколько секунд из скольких придётся пересобрать, и даёт отказаться. Проверено замерами: - красный с непрозрачностью 50% поверх синего дал (127, 0, 126); - переход на одной дорожке: синий плавно перешёл в красный за секунду; - звуковой переход: -24 dB, -27.5, -35, -51, тишина; - появление за 2 секунды: пересобрано 2 секунды из 6, остальное скопировано. Найдено по дороге: участок с наложением склеивался с соседним куском и терял слои — в условии склейки они не учитывались.
2026-08-20 10:11:20 +03:00
## Наложение, переходы и прозрачность
Ползунок на клипе картинки задаёт **непрозрачность**, на звуковом —
громкость. Клип с непрозрачностью меньше единицы смешивается с тем, что
под ним.
Звуковые дорожки смешиваются, а не побеждают друг друга Правило «верхний побеждает» я перенёс на звук механически, и это была ошибка: оно придумано для картинки, где и делает возможной сборку копированием. Для звука оно бессмысленно — под голос нельзя было положить фоновую музыку. И стоит смешивание ничего: звук всё равно перекодируется. В сборке куски со звуком нескольких дорожек сводятся тем же путём, что переходы: слоями, каждый со своей громкостью. В предпросмотре сложнее: плеер играет одну звуковую дорожку за раз. Поэтому каждая дорожка монтажа подаётся своим списком кусков, а смесь собирается цепочкой фильтров плеера. Громкость клипов подставляется в ту же цепочку — иначе предпросмотр врал бы про баланс. Проверено: два тона на разных дорожках, второй с громкостью 50%. В готовом ролике -21.2 и -27.1 dB, в предпросмотре -21.2 и -27.2. Совпало. Найдена по дороге ошибка в собственном условии: я считал слои без победителя и потому обнулял список, когда звучали ровно две дорожки.
2026-08-20 12:10:57 +03:00
**Звуковые дорожки смешиваются.** Правило «верхний побеждает» придумано
для картинки — оно и делает возможной сборку копированием. Для звука оно
бессмысленно: без смешивания нельзя положить фоновую музыку под голос. И
стоит оно ничего, потому что звук всё равно перекодируется.
Плеер играет одну звуковую дорожку за раз, поэтому в предпросмотре смесь
собирается его же цепочкой фильтров: каждая дорожка монтажа подаётся
своим списком кусков, дальше `amix`. Громкость клипов подставляется в ту
же цепочку — иначе предпросмотр врал бы про баланс. Проверено: в
предпросмотре и в готовом ролике уровни совпадают до десятой доли
децибела.
Этап 5: прозрачность, переходы и затухания Ползунок на клипе картинки теперь задаёт непрозрачность, на звуковом — громкость. Уплощение выдаёт для таких мест не одного победителя, а слои. Слои появляются в трёх случаях: клип полупрозрачен и сквозь него виден нижний; два клипа на одной дорожке наползли друг на друга и должны перетечь один в другой; у клипа задано плавное появление или уход. Перекрытие на разных дорожках переходом не считается — там по-прежнему побеждает верхний. Иначе в проекте, где дорожки перекрываются постоянно, каждое такое место пришлось бы пересобирать. Картинка собирается фильтрами ffmpeg: постоянная доля через overlay, переход через xfade, затухание через fade. Звук сводится прямо в отсчётах — доля каждого слоя считается на каждый отсчёт, и длина результата получается ровно такой, какая нужна. Перед сборкой программа сообщает, сколько секунд из скольких придётся пересобрать, и даёт отказаться. Проверено замерами: - красный с непрозрачностью 50% поверх синего дал (127, 0, 126); - переход на одной дорожке: синий плавно перешёл в красный за секунду; - звуковой переход: -24 dB, -27.5, -35, -51, тишина; - появление за 2 секунды: пересобрано 2 секунды из 6, остальное скопировано. Найдено по дороге: участок с наложением склеивался с соседним куском и терял слои — в условии склейки они не учитывались.
2026-08-20 10:11:20 +03:00
Два клипа **на одной дорожке**, наползшие друг на друга, дают переход:
один плавно перетекает в другой. На разных дорожках перекрытие означает
не переход, а наложение — там по-прежнему побеждает верхний, если он
Клип, не заполняющий кадр, ложится поверх нижнего, а не закрывает его Уменьшенная и сдвинутая картинка выходила в ролик с чёрными полями, и нижнюю дорожку закрывал не сам клип, а эти поля. Так же вело себя и уменьшенное видео. Между тем уменьшают клип ровно затем, чтобы под ним было видно другое. Теперь «не заполняет кадр» означает то же, что «не сплошной»: наравне с выбитым цветом и прозрачностью. Такой клип уходит в накладываемые — предпросмотр рисует его поверх нижнего силами плеера, сборка собирает участок слоями, где у верхнего слоя цепочка без полей и место, куда он встаёт. Класть не на что — поля остаются полями, как и были. Решение одно на оба пути: IsOverlayClip, и в нём TransformFillsFrame считает по тем же числам, что уходят в фильтр. Заодно две вещи, всплывшие по дороге: — у картинки своя ветка уплощения, и слои там не строились вовсе: клип с прозрачностью в окне накладывался, а в ролике нет. Теперь строятся. — в сборке ветка «готовая вставка» стояла раньше ветки слоёв, и кусок со слоями уходил в неё за несуществующей заготовкой. Заготовка для такого куска больше и не заказывается: это были бы минуты впустую. Проверка tests/imagecrop.sh получила второй случай: под картинкой видео, вокруг неё обязано быть видно его. На прежней сборке там чёрное.
2026-10-16 16:17:30 +03:00
сплошной.
Сплошным клип перестаёт быть по трём причинам: выбит цвет, задана
прозрачность или **он не заполняет кадр**. Последнее — то же самое,
сказанное иначе: уменьшенный или сдвинутый клип занимает часть кадра, а
вокруг остаётся пустое место, которое сборка обкладывает чёрными полями.
Поле непрозрачно, и нижнюю дорожку закрывает не сам клип, а его поля —
поэтому такой клип кладётся поверх нижнего, а не вместо него. Уменьшают
клип ровно затем, чтобы увидеть под ним другое. Класть не на что —
остаются поля.
Отсюда же берётся картинка в углу кадра и два видео рядом: достаточно
уменьшить и сдвинуть клипы на разных дорожках.
Этап 5: прозрачность, переходы и затухания Ползунок на клипе картинки теперь задаёт непрозрачность, на звуковом — громкость. Уплощение выдаёт для таких мест не одного победителя, а слои. Слои появляются в трёх случаях: клип полупрозрачен и сквозь него виден нижний; два клипа на одной дорожке наползли друг на друга и должны перетечь один в другой; у клипа задано плавное появление или уход. Перекрытие на разных дорожках переходом не считается — там по-прежнему побеждает верхний. Иначе в проекте, где дорожки перекрываются постоянно, каждое такое место пришлось бы пересобирать. Картинка собирается фильтрами ffmpeg: постоянная доля через overlay, переход через xfade, затухание через fade. Звук сводится прямо в отсчётах — доля каждого слоя считается на каждый отсчёт, и длина результата получается ровно такой, какая нужна. Перед сборкой программа сообщает, сколько секунд из скольких придётся пересобрать, и даёт отказаться. Проверено замерами: - красный с непрозрачностью 50% поверх синего дал (127, 0, 126); - переход на одной дорожке: синий плавно перешёл в красный за секунду; - звуковой переход: -24 dB, -27.5, -35, -51, тишина; - появление за 2 секунды: пересобрано 2 секунды из 6, остальное скопировано. Найдено по дороге: участок с наложением склеивался с соседним куском и терял слои — в условии склейки они не учитывались.
2026-08-20 10:11:20 +03:00
Щелчок в месте наезда перебирает клипы Наезд двух клипов на одной дорожке — заказ на переход, то есть обычное дело. Нажатие же всегда попадало в верхний по списку, и нижний клип нельзя было ни выделить, ни потащить: чтобы до него добраться, приходилось сперва убирать верхний. Правило такое. Нажатие берёт тот из накрытых клипов, который уже выделен, иначе верхний. Щелчок без движения по уже выделенному переводит выделение на следующий, после последнего снова на верхний. Переключение сделано на отпускании намеренно: пока кнопку не отпустили, неизвестно, щелчок это или начало протяжки, а менять клип под рукой посреди протяжки нельзя. Благодаря этому протяжка тащит именно то, что перед ней выбрали щелчками. Строка состояния говорит, сколько клипов под указателем: перебор, о котором не сказали, — тайное знание. Отбор вынесен в TTrack.ClipsAt, числовая проверка — в lfmcheck. tests/pickcycle.sh щёлкает дважды и тащит: на прежней сборке уезжает верхний клип, а нижний едет за ним схлопыванием.
2026-10-16 15:43:01 +03:00
В месте наезда под указателем два клипа. Первый щелчок берёт верхний,
повторный по тому же месту переводит выделение на следующий, после
последнего снова на верхний; строка состояния говорит, сколько их там.
Нажатие для перетаскивания берёт именно выделенный — иначе нижний клип
нельзя было бы ни выделить, ни потащить, не убрав сперва верхний.
Этап 5: прозрачность, переходы и затухания Ползунок на клипе картинки теперь задаёт непрозрачность, на звуковом — громкость. Уплощение выдаёт для таких мест не одного победителя, а слои. Слои появляются в трёх случаях: клип полупрозрачен и сквозь него виден нижний; два клипа на одной дорожке наползли друг на друга и должны перетечь один в другой; у клипа задано плавное появление или уход. Перекрытие на разных дорожках переходом не считается — там по-прежнему побеждает верхний. Иначе в проекте, где дорожки перекрываются постоянно, каждое такое место пришлось бы пересобирать. Картинка собирается фильтрами ffmpeg: постоянная доля через overlay, переход через xfade, затухание через fade. Звук сводится прямо в отсчётах — доля каждого слоя считается на каждый отсчёт, и длина результата получается ровно такой, какая нужна. Перед сборкой программа сообщает, сколько секунд из скольких придётся пересобрать, и даёт отказаться. Проверено замерами: - красный с непрозрачностью 50% поверх синего дал (127, 0, 126); - переход на одной дорожке: синий плавно перешёл в красный за секунду; - звуковой переход: -24 dB, -27.5, -35, -51, тишина; - появление за 2 секунды: пересобрано 2 секунды из 6, остальное скопировано. Найдено по дороге: участок с наложением склеивался с соседним куском и терял слои — в условии склейки они не учитывались.
2026-08-20 10:11:20 +03:00
Различие не косметическое. Если бы кроссфейдом считалось любое
перекрытие, то в проекте, где клипы на разных дорожках перекрываются
постоянно, каждое такое место пришлось бы пересобирать.
Наезд не остаётся незамеченным: после правки строка состояния называет
дорожку, величину перекрытия и сами клипы. Переход заказывают мышью, а
промахнуться легко — на общем плане одна точка экрана это секунды, — и
раньше он появлялся сюрпризом: человек искал, почему сборка опять не
Крест перехода там, где клипы не накладываются На монтаже рисовался косой крест через целый клип, строка состояния обещала переход, а наложения не было: ролик собирался верно. Крест уходил сам после Ctrl+Z, после любого действия с отменой и после схлопывания. Это был не наезд, а сбитый порядок клипов в списке дорожки. Список отсортирован по времени начала, и соседи по списку обязаны быть соседями по времени — на этом стоят и отрисовка креста, и сообщение о наезде. Перетаскивание же двигает не одну дорожку: связка ведёт звук за картинкой, схлопывание ведёт соседей, в режиме «все дорожки» — сразу на нескольких. Сортировалась только та дорожка, с которой тащили. Клип, уехавший с конца монтажа в середину, оставался в списке чужой дорожки последним, и её соседями по списку становились клипы, далёкие друг от друга по времени. Отмена и схлопывание лечили ровно потому, что пересортировывают списки. Сборка всё это время была права: она идёт по границам и сортирует дорожку сама. Теперь порядок возвращается всем дорожкам, чьи клипы поехали (SortMovedTracks). Оба места, считающие соседей по списку, заодно пропускают пару, идущую не по времени: страховка от того же симптома в другом месте. Проверка tests/dragorder.sh тащит связку за звук с конца монтажа в середину и читает порядок из сохранённого файла. На прежней сборке дорожка картинки остаётся в порядке 0, 14, 7.991 — проверка падает.
2026-10-16 14:01:51 +03:00
кончается. Меряется наезд той же мерой, что и в сборке: короче половины
кадра — не наезд, и говорить о нём незачем.
Косой крест наезда и это сообщение берут соседей из списка клипов
дорожки, а он отсортирован по времени. Держать этот порядок обязан
каждый, кто клипы двигает: перетаскивание ведёт за собой связку на
звуковой дорожке и соседей при схлопывании, и все их дорожки
пересортировываются (`SortMovedTracks`). Пока сортировалась только та
дорожка, с которой тащили, монтаж рисовал крест через целый клип там,
где ничего не накладывалось, и обещал переход, которого никто не
заказывал.
Сама сборка при этом не начинается с первым же движением. Список кусков
пересобирается сразу, а ffmpeg запускается, когда монтаж постоял тихо
полторы секунды (`GenerateQuietMs`) и клип никто не держит мышью. Иначе
переход считался бы заново на каждое движение руки, а посчитанное
отправлялось бы в корзину.
Этап 5: прозрачность, переходы и затухания Ползунок на клипе картинки теперь задаёт непрозрачность, на звуковом — громкость. Уплощение выдаёт для таких мест не одного победителя, а слои. Слои появляются в трёх случаях: клип полупрозрачен и сквозь него виден нижний; два клипа на одной дорожке наползли друг на друга и должны перетечь один в другой; у клипа задано плавное появление или уход. Перекрытие на разных дорожках переходом не считается — там по-прежнему побеждает верхний. Иначе в проекте, где дорожки перекрываются постоянно, каждое такое место пришлось бы пересобирать. Картинка собирается фильтрами ffmpeg: постоянная доля через overlay, переход через xfade, затухание через fade. Звук сводится прямо в отсчётах — доля каждого слоя считается на каждый отсчёт, и длина результата получается ровно такой, какая нужна. Перед сборкой программа сообщает, сколько секунд из скольких придётся пересобрать, и даёт отказаться. Проверено замерами: - красный с непрозрачностью 50% поверх синего дал (127, 0, 126); - переход на одной дорожке: синий плавно перешёл в красный за секунду; - звуковой переход: -24 dB, -27.5, -35, -51, тишина; - появление за 2 секунды: пересобрано 2 секунды из 6, остальное скопировано. Найдено по дороге: участок с наложением склеивался с соседним куском и терял слои — в условии склейки они не учитывались.
2026-08-20 10:11:20 +03:00
У клипа есть плавное появление и уход. Для картинки это затемнение, для
звука — нарастание громкости.
**Всё это стоит перекодирования** — но только своего участка. Перед
сборкой программа сообщает, сколько секунд из скольких придётся
пересобрать, и даёт отказаться.
Звук при наложении сводится прямо в отсчётах, без фильтров ffmpeg: доля
каждого слоя считается на каждый отсчёт, и длина результата получается
ровно такой, какая нужна.
Чёрные вставки стоят копейки, текст режет кусок Дыра в сорок минут требовала двухчасового чёрного файла: лестница готовых длин доходила до восьми часов, и NVENC кодировал такое минутами. Отсюда «Делаю чёрную вставку 1 из 3», которое не кончается. Теперь длины две, 15 и 60 секунд, длинная дыра набирается повторами минутного файла, а само чёрное кодируется мегабитом вместо полусотни. Промежуток короче половины кадра дырой больше не считается: между клипами, сдвинутыми встык мышью, оставались микросекунды. Мышь при этом кладёт клипы на кадровую сетку — между кадрами времени не существует. Правка монтажа прекращает генерацию, ставшую ненужной: раньше закрытая дыра не отменяла уже начатую работу. Ctrl+Shift+R работает без мыши: дыра ищется на активной дорожке, если указатель не над дорожками, и команда отвечает в строке состояния, а не молчит. Вжигание текста режет кусок на части и пересобирает только середину. Надпись в тридцать секунд внутри сорокаминутного куска стоила сорока минут пересборки. Часть после надписи помечается точным резом, иначе копия пошла бы от опорного кадра и картинка сдвинулась бы. Проверки: flattest 13 и 14, схлопывание без мыши в lfmcheck, сборка с надписью внутри длинного куска — 1.000000 с пересборки вместо 6.
2026-09-11 19:35:26 +03:00
## Текст режет кусок, а не пересобирает его целиком
Вжигание надписи не делает весь кусок пересобираемым. Кусок режется на
части: до надписи, под надписью и после. Пересобирается только середина,
остальное копируется. Надпись в тридцать секунд внутри сорокаминутного
куска раньше стоила сорока минут пересборки — половины ролика.
Часть, которая начинается не с начала куска, помечается точным резом:
копия пошла бы от ближайшего опорного кадра и сдвинула бы картинку.
Сборка перекодирует огрызок до опорного кадра, а дальше снова копирует —
доли секунды вместо минут.
Обрезки короче секунды не копируются, а дожигаются вместе с надписью:
возни с опорными кадрами там больше, чем выгоды.
Участок сборки, качество пересборки и пропавшие куски Отмеченный участок. Клавиши I и O ставят начало и конец по курсору, полоса под линейкой показывает отмеченное, сборка собирает только его. Без этого проверить правку на длинном монтаже нельзя: каждая проверка стоит столько же, сколько идёт весь ролик. Отметки живут в проекте. Разметка текста при этом берётся с начала участка, иначе надписи легли бы со сдвигом. Фотография пропадала из ролика. Кусок с наложением подаёт слои в ffmpeg как есть, а одиночная картинка даёт ровно один кадр: кусок в три секунды получался длиной 0.016667 с. Теперь у слоя есть признак «это картинка», и она повторяется ключом -loop с частотой кадров проекта. Текст пропадал там же. Вжигание помечает кусок разметкой, но ветка наложения про неё не знала. Пока оверлей лежит на всём ролике, этой веткой идут все куски — и текста не было нигде. Теперь разметка вжигается последним звеном цепочки, поверх сведённой картинки. Качество пересобранных кусков. Битрейт не задавался вовсе, и кодировщик брал своё значение по умолчанию: 5.9 Мбит/с там, где исходник 56. Теперь пересборка целится в наибольший битрейт среди файлов проекта, с потолком в полтора раза выше и буфером вдвое. Тем же битрейтом делаются вставки из фотографий. Проверки: обрезка списка кусков по участку (nlvetool flattest, 9), сборка фотографии под наложением и вжигание текста поверх наложения (nlvetool render ... burn).
2026-08-31 10:37:25 +03:00
## Качество пересобираемых кусков
Куски, которые нельзя скопировать, целятся в наибольший битрейт среди
файлов проекта. Без этого кодировщик берёт своё значение по умолчанию, и
оно скромное: на съёмке 2688x1512 с 56 Мбит/с NVENC выдавал 5.9 Мбит/с —
пересобранный кусок бросался в глаза среди скопированных соседей.
Кроме среднего задаётся потолок в полтора раза выше и буфер вдвое: без
них кодировщик разгоняется на сложных кадрах ровно настолько, насколько
позволит средний, и мелкие детали всё равно теряются.
Берётся именно наибольший, а не средний: пересобранный кусок стоит рядом
со скопированными, и если он окажется хуже их, разница видна именно на
нём. Лишнее место дешевле.
## Место на диске
Куски весят примерно столько же, сколько готовый ролик, а на длинном
материале и больше: на часовой съёмке набралось 110 ГБ. Поэтому:
- **картинка не собирается в промежуточный файл.** Звук собирается
раньше, и склейка сразу кладёт всё вместе в конечный файл. Раньше
получалось два полных ролика на диске одновременно — 91 ГБ в кэше и 91
ГБ рядом — и лишний проход копирования ради одной звуковой дорожки;
- **после удачной сборки куски удаляются,** и в журнале сказано, сколько
места освободилось. После неудачной — остаются: с них продолжают.
Оставить их можно настройкой `keep_parts` в файле настроек: пригодится,
когда разбираешься, из чего собрался ролик.
Чёрные вставки стоят копейки, текст режет кусок Дыра в сорок минут требовала двухчасового чёрного файла: лестница готовых длин доходила до восьми часов, и NVENC кодировал такое минутами. Отсюда «Делаю чёрную вставку 1 из 3», которое не кончается. Теперь длины две, 15 и 60 секунд, длинная дыра набирается повторами минутного файла, а само чёрное кодируется мегабитом вместо полусотни. Промежуток короче половины кадра дырой больше не считается: между клипами, сдвинутыми встык мышью, оставались микросекунды. Мышь при этом кладёт клипы на кадровую сетку — между кадрами времени не существует. Правка монтажа прекращает генерацию, ставшую ненужной: раньше закрытая дыра не отменяла уже начатую работу. Ctrl+Shift+R работает без мыши: дыра ищется на активной дорожке, если указатель не над дорожками, и команда отвечает в строке состояния, а не молчит. Вжигание текста режет кусок на части и пересобирает только середину. Надпись в тридцать секунд внутри сорокаминутного куска стоила сорока минут пересборки. Часть после надписи помечается точным резом, иначе копия пошла бы от опорного кадра и картинка сдвинулась бы. Проверки: flattest 13 и 14, схлопывание без мыши в lfmcheck, сборка с надписью внутри длинного куска — 1.000000 с пересборки вместо 6.
2026-09-11 19:35:26 +03:00
## Дыры и вставки
Промежуток короче половины кадра проекта дырой не считается: между
клипами, сдвинутыми встык мышью, остаются микросекунды, и заказывать под
них чёрную вставку не нужно. Мышь при этом кладёт клипы на кадровую
сетку — между кадрами времени не существует.
Готовые вставки делаются двух длин, 15 и 60 секунд. Дыра длиннее минуты
набирается повторами одного и того же минутного файла: и список кусков
предпросмотра, и список склейки повторы принимают. Раньше лестница длин
доходила до восьми часов, и дыра в сорок минут требовала двухчасового
чёрного файла — программа выглядела зависшей.
Чёрное кодируется отдельным скромным потоком в мегабит: целиться в
битрейт съёмки там нечему.
Если правка монтажа сделала идущую генерацию ненужной, она прекращается:
программа проверяет, нужна ли ещё та вставка, что делается сейчас.
Свойства клипа, порядок дорожек, оценка времени и продолжение сборки Копирование оформления: Ctrl+Shift+C берёт с выделенного клипа кадрирование, поворот, увеличение, смещение, прозрачность, ключ, затухания и громкость, Ctrl+Shift+V применяет их ко всем выделенным. Вид клипа и текст не переносятся: свойства копируют между разными клипами. Порядок дорожек: слева в заголовке две стрелки, у крайних дорожек соответствующая нарисована тускло и не нажимается. Двойной щелчок по названию переименовывает дорожку — на монтаже из десятка дорожек «карта» и «титры» читаются лучше, чем V1 и Т1. То же есть и в меню по правому щелчку. Ввод имени — своим тёмным окном: системный InputQuery выглядел бы белым пятном. Окно сборки показывает, сколько прошло и сколько примерно осталось. Оценка по пройденной доле; скорость на деле меняется, поэтому «примерно» и пересчёт на каждом шаге. Прерванная сборка продолжается с готовых кусков. Каждый доведённый до конца кусок помечается в списке рядом с ними: одного наличия файла мало — сборку могли прервать посреди записи. Первая строка списка — отпечаток плана; не совпал, значит монтаж менялся, и всё собирается заново. Проверено: четыре куска взяты готовыми, два пересобраны, склейка не потеряла ни одного кадра; при изменённом монтаже готовые куски отвергнуты.
2026-08-31 14:55:19 +03:00
## Прерванная сборка
Продолжение прерванной сборки потоком — с недописанного файла Потоком кэша кусков нет, и продолжать было не с чего. Но недописанный файл сам по себе — уже собранная часть фильма: в нём видно, сколько целых кадров успело записаться. Программа отступает до границы последнего целого куска, переносит эту часть и досылает остаток. Дописать в конец существующего MKV нельзя, поэтому продолжение пишется в новый файл. MP4 продолжать нельзя вовсе: оглавление в конце, и оборванный файл не открывается — об этом говорится прямо. Готовый файл теперь цел до последнего: сборка идёт в файл рядом и занимает место готового, только когда дописана. Сорвавшаяся попытка не отбирает уже собранное — из двух недописанных остаётся тот, где кадров больше, и считаются они даже после запроса остановки. Отдельно пришлось снять сдвиг, который приёмник накладывает на весь ролик: перенесённая часть несла его в себе, и куски за ней задваивали кадр. Замерено: обрыв в четырёх разных местах, продолженная сборка каждый раз совпала с собранной за один раз попиксельно. В nlvetool у render появились --cancel-after=<сек> и сочетание слов: cache resume проверяет, что старый путь через кэш не сломался.
2026-09-30 16:37:28 +03:00
Если сборка не дошла до конца — упала, была отменена или программу
закрыли, — при следующей сборке в тот же файл программа предложит
продолжить.
**При сборке потоком** продолжают с самого недописанного файла: он и есть
уже собранная часть фильма, и в нём видно, сколько целых кадров успело
записаться. Программа отступает назад до границы последнего целого куска,
переносит эту часть в новый файл и досылает остаток. Собранное таким
образом совпадает с собранным за один раз попиксельно.
Дописать в конец существующего MKV нельзя — Matroska этого не даёт, —
поэтому продолжение всегда пишется в новый файл, и на диске в этот момент
лежат оба. Места нужно вдвое больше готового ролика.
**MP4 продолжать нельзя вовсе:** оглавление пишется в конец, и оборванный
файл не открывается — `moov atom not found`. Программа говорит об этом
прямо и собирает заново. Если сборка длинная, лучше собрать в MKV, а потом
переупаковать в MP4 окном ремукса: это копирование потоков, без
перекодирования.
Готовый файл до последнего цел: сборка идёт в файл рядом и занимает место
готового, только когда дописана. Сорвавшаяся попытка не отбирает то, что
уже было собрано, — из двух недописанных остаётся тот, где кадров больше.
**При сборке через кэш кусков** продолжают иначе: каждый доведённый до
конца кусок помечается в списке рядом с самими кусками (`готово.txt`).
Проверяется не только наличие файла — сборку могли прервать посреди
записи; куском считается только тот, который успел попасть в список.
Свойства клипа, порядок дорожек, оценка времени и продолжение сборки Копирование оформления: Ctrl+Shift+C берёт с выделенного клипа кадрирование, поворот, увеличение, смещение, прозрачность, ключ, затухания и громкость, Ctrl+Shift+V применяет их ко всем выделенным. Вид клипа и текст не переносятся: свойства копируют между разными клипами. Порядок дорожек: слева в заголовке две стрелки, у крайних дорожек соответствующая нарисована тускло и не нажимается. Двойной щелчок по названию переименовывает дорожку — на монтаже из десятка дорожек «карта» и «титры» читаются лучше, чем V1 и Т1. То же есть и в меню по правому щелчку. Ввод имени — своим тёмным окном: системный InputQuery выглядел бы белым пятном. Окно сборки показывает, сколько прошло и сколько примерно осталось. Оценка по пройденной доле; скорость на деле меняется, поэтому «примерно» и пересчёт на каждом шаге. Прерванная сборка продолжается с готовых кусков. Каждый доведённый до конца кусок помечается в списке рядом с ними: одного наличия файла мало — сборку могли прервать посреди записи. Первая строка списка — отпечаток плана; не совпал, значит монтаж менялся, и всё собирается заново. Проверено: четыре куска взяты готовыми, два пересобраны, склейка не потеряла ни одного кадра; при изменённом монтаже готовые куски отвергнуты.
2026-08-31 14:55:19 +03:00
Первая строка списка — отпечаток плана: пути, места и длины всех кусков.
Не совпал — значит монтаж с тех пор менялся, и готовые куски не подойдут;
программа скажет об этом и соберёт заново.
## Сколько осталось
Окно сборки показывает, сколько прошло и сколько примерно осталось.
Оценка простая: если четверть пути заняла минуту, весь путь займёт
четыре. На деле скорость меняется — копируемые куски идут мгновенно,
пересобираемые медленно, — поэтому число называется «примерно» и
пересчитывается на каждом шаге. Даже такая оценка отвечает на главный
вопрос: успею ли я сходить за чаем.
Показатель работы: число и полоса показывают одно и то же Полоса ползла по догадке, а подпись показывала замер — и человек видел полосу, заполненную на девять десятых, рядом с нулём процентов. Что в этот момент происходит, по такой паре понять нельзя. Теперь доля одна на обоих. Берётся она у самого ffmpeg: сборка участка с затуханием идёт минуту и дольше, а «кусок 1 из 1» всё это время не меняется, поэтому ffmpeg спрашивают ключом -progress, докуда он дошёл. Пока он не ответил ни разу, полоса трогается с места на пару процентов и там стоит — пустая полоса и есть правда о только что начатой работе. Догадка заодно перестала закрывать промежуток до вехи целиком: предел — девять десятых, остаток виден глазом. Проверка на это добавлена в tests/progress. Полоса в окне сборки была системной, и под gtk2 заполнялась ступенями по восемь кусков на ширину. Теперь там та же своя полоса, что и внизу главного окна, — заполняется сплошь. Чтобы она читалась из файла формы, у неё опубликованы Anchors, Align и Visible: читатель формы берёт только опубликованные свойства, и без этого окно падало при открытии. Проверено глазами на невидимом экране: «Собираю наложение 1 из 1 — 2%» при полосе в 2%, через четыре секунды 69% при полосе в 69%.
2026-10-01 15:50:10 +03:00
**Полоса и число — одно и то же число.** Показатель работы внизу главного
окна подписан процентами, и это ровно та доля, которую он нарисовал.
Раньше полоса ползла по догадке, а подпись показывала замер: человек
видел полосу, заполненную на девять десятых, рядом с нулём процентов, и
понять по такой паре было нельзя ничего.
**Доля берётся у ffmpeg, а не угадывается.** Сборка участка с наложением
или затуханием может идти минуту и дольше, и всё это время «кусок 1 из 1»
не меняется. Поэтому ffmpeg спрашивают, докуда он дошёл (`-progress`), и
доля считается по его ответу. Пока он не ответил ни разу, полоса трогается
с места на пару процентов и там стоит: пустая полоса и есть правда о
только что начатой работе.
**Обе полосы рисует один и тот же код.** Полоса в окне сборки раньше была
системной (`TProgressBar`), и под gtk2 она заполнялась ступенями — восемь
кусков на всю ширину. Теперь и там, и внизу главного окна стоит своя
`TThemedProgress`, которая заполняется сплошь.
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
## Как собирается ролик
1. Монтаж уплощается в последовательность непересекающихся кусков.
Перекрытия разрешаются в пользу верхней дорожки, дыры превращаются в
чёрные вставки.
2. Картинки и дыры материализуются в клипы в параметрах проекта. Иначе их
нечем показать: подсунуть mpv картинку или генератор прямо в списке
кусков нельзя, он на этом зависает. Заготовки делаются готовых длин
(15 с, 60 с, 5 мин и так далее), а нужный кусок из них вырезается —
тогда любое движение клипа не требует новой генерации.
Щелчок в месте наезда перебирает клипы Наезд двух клипов на одной дорожке — заказ на переход, то есть обычное дело. Нажатие же всегда попадало в верхний по списку, и нижний клип нельзя было ни выделить, ни потащить: чтобы до него добраться, приходилось сперва убирать верхний. Правило такое. Нажатие берёт тот из накрытых клипов, который уже выделен, иначе верхний. Щелчок без движения по уже выделенному переводит выделение на следующий, после последнего снова на верхний. Переключение сделано на отпускании намеренно: пока кнопку не отпустили, неизвестно, щелчок это или начало протяжки, а менять клип под рукой посреди протяжки нельзя. Благодаря этому протяжка тащит именно то, что перед ней выбрали щелчками. Строка состояния говорит, сколько клипов под указателем: перебор, о котором не сказали, — тайное знание. Отбор вынесен в TTrack.ClipsAt, числовая проверка — в lfmcheck. tests/pickcycle.sh щёлкает дважды и тащит: на прежней сборке уезжает верхний клип, а нижний едет за ним схлопыванием.
2026-10-16 15:43:01 +03:00
Своё кадрирование картинки — увеличение, смещение, поворот — запекается
прямо в эту заготовку, той же цепочкой фильтров, что показывает окно
«Кадрирование и поворот». Поэтому в кэше лежит столько заготовок,
сколько разных кадрирований у этой картинки, и предпросмотр с роликом
показывают одно и то же.
Клип, не заполняющий кадр, ложится поверх нижнего, а не закрывает его Уменьшенная и сдвинутая картинка выходила в ролик с чёрными полями, и нижнюю дорожку закрывал не сам клип, а эти поля. Так же вело себя и уменьшенное видео. Между тем уменьшают клип ровно затем, чтобы под ним было видно другое. Теперь «не заполняет кадр» означает то же, что «не сплошной»: наравне с выбитым цветом и прозрачностью. Такой клип уходит в накладываемые — предпросмотр рисует его поверх нижнего силами плеера, сборка собирает участок слоями, где у верхнего слоя цепочка без полей и место, куда он встаёт. Класть не на что — поля остаются полями, как и были. Решение одно на оба пути: IsOverlayClip, и в нём TransformFillsFrame считает по тем же числам, что уходят в фильтр. Заодно две вещи, всплывшие по дороге: — у картинки своя ветка уплощения, и слои там не строились вовсе: клип с прозрачностью в окне накладывался, а в ролике нет. Теперь строятся. — в сборке ветка «готовая вставка» стояла раньше ветки слоёв, и кусок со слоями уходил в неё за несуществующей заготовкой. Заготовка для такого куска больше и не заказывается: это были бы минуты впустую. Проверка tests/imagecrop.sh получила второй случай: под картинкой видео, вокруг неё обязано быть видно его. На прежней сборке там чёрное.
2026-10-16 16:17:30 +03:00
Заготовка нужна не всегда: если картинка не заполняет кадр, а под ней
что-то лежит, участок собирается наложением, и чёрным полям взяться
неоткуда. Заготовка с полями остаётся для случая, когда класть не на
что.
Накладываемая картинка показывается в предпросмотре готовым куском В окне картинки не было вовсе, хотя в ролик она попадала. Плеер получал сам файл, а его декодер на этом webp спотыкался: «webp: invalid TIFF header in Exif data» — накладывать стало нечего. У неподвижного кадра есть и вторая беда: в списке кусков у него нет длины. Теперь наложению отдаётся заготовка — обычное видео нужной длины, сделанное тем же путём, что и остальные вставки, только цепочкой без полей: поля тут вредны, чёрное закрыло бы нижнее видео. Масштаб уже внутри куска, плееру остаётся поставить его на место. Заготовки наложения собираются отдельным списком: уплощение ниже заполняет список недостающего заново и затёрло бы их. Проверено на том самом файле: в окне картинка легла поверх видео, в ролике слева нижнее видео (253, 0, 0), справа картинка. Числовые проверки в flattest: кусок собирается слоями, у верхнего слоя полей нет, предпросмотру отдан кусок из кэша, и он заказан к созданию.
2026-10-16 16:36:11 +03:00
В предпросмотре накладываемая картинка тоже показывается заготовкой,
только без полей. Своих декодеров плееру хватает не на всё: настоящий
webp из сети дал «webp: invalid TIFF header in Exif data», и картинка
пропадала из окна, хотя в ролик попадала. У неподвижного кадра есть и
вторая беда — в списке кусков у него нет длины. Заготовка же обычное
видео нужной длины.
Звук больше не отстаёт от картинки Рез всегда точный. Раньше начало куска притягивалось к ближайшему опорному кадру, а звук брался из непритянутой точки: по журналам сборки средняя притяжка 0.24 с, худшая 0.99 с, и на длинном куске этот сдвиг держится все его минуты. Плата за точность — доли секунды перекодирования на рез. Второй вид реза (Shift+S) стал тем же самым и убран. Разбор заголовка MP4 не учитывал список правок (elst): метки опорных кадров уезжали на задержку показа, на пробе два кадра. Сверка быстрого пути с ffprobe теперь совпадает точно, в том числе на записи с B-кадрами. Куску заказывается точное число кадров, а не только время: при копировании ffmpeg отмеряет конец по времени декодирования, и у записи с B-кадрами кусок выходил длиннее. Склейке длительность каждого куска задаётся явно. Последний пакет скопированного куска показывается позже прочих, контейнер считал его в длительность, и время картинки растягивалось — 0.05 с на кусок. Звука в кусках картинки больше нет: он не используется, а в MPEG-TS ещё и сбивает выравнивание склейки на 36 мс. Границы кусков ложатся на кадровую сетку: картинка выходит целым числом кадров, звук набирается до отсчёта, и доли кадра разводили их в стороны. Разогрев кодировщика звука компенсируется: сжатый AAC начинается со служебных отсчётов, MP4 помечает их пропускаемыми, Matroska — нет, и звук ехал на 21.3 мс позже. Величина меряется пробным сжатием. Продолжение прерванной сборки чинится заодно: готовность проверяется у каждой части, а не у куска целиком. При точном резе частей две, и вторая при продолжении терялась. В журнал сборки добавлена проверка синхронности: сколько кадров вышло и сколько просил монтаж, с вердиктом и худшей частью. Замер (tests/syncprobe.sh, исходник со вспышкой и щелчком на каждой секунде): было до 0.6 с и росло по ходу ролика, стало 0.02-0.04 с без накопления при точности измерения 0.017 с.
2026-09-16 18:28:29 +03:00
3. Границы кусков кладутся на кадровую сетку проекта: между кадрами
времени нет, а звук набирается с точностью до отсчёта, и доли кадра
разводят их в стороны.
4. Рез всегда точный: огрызок от точки реза до ближайшего опорного кадра
перекодируется, остальное копируется. Копировать можно только с
опорного кадра, поэтому «резать где угодно и ничего не перекодировать»
не бывает — но платить приходится долями секунды на рез.
5. Каждому куску заказывается точное число кадров (`-frames:v`), а не
только время: при копировании ffmpeg отмеряет конец по времени
декодирования, и у записи с B-кадрами кусок выходит длиннее
запрошенного.
Сборка потоком: куски идут сразу в готовый файл, без кэша Раньше каждый кусок монтажа вырезался в отдельный файл, и только потом список этих файлов шёл на склейку. Кэш весил столько же, сколько готовый фильм — замерено 110 ГБ на часе материала, — а диск писал всё дважды. Теперь каждый кусок режется своим ffmpeg прямо в MPEG-TS на стандартный вывод, все потоки подряд переливаются в один принимающий ffmpeg, а тот пишет готовый файл. Место куску задаёт -output_ts_offset, считаемый в кадрах от начала. На диск ложится только результат и звуковая дорожка. Через кэш по-прежнему собирается то, что потоком не выходит: кодек, не переносимый в MPEG-TS, и пересборка всего ролика под площадку. Сверено на четырёх монтажах, включая настоящий материал с дрона (HEVC 2688x1512, 55 Мбит/с, файлы с шестью потоками): картинка совпала попиксельно, PSNR бесконечен на каждом кадре, длина и размер те же. Сверка шла и с прошлым выпуском, а не только нового пути со старым. На отрывке в 17.5 с: 14.2 с и 421 КБ на диске против 17.5 с и 152 МБ. По дороге найдены две ловушки, обе записаны в README: -avoid_negative_ts make_zero отменяет -output_ts_offset, а -fflags +igndts у приёмника сминает кадры на стыках.
2026-09-30 16:12:00 +03:00
6. Куски идут в готовый файл потоком. Каждый режется своим ffmpeg прямо
в MPEG-TS на стандартный вывод, все потоки подряд переливаются в один
принимающий ffmpeg, а тот пишет готовый файл. Место куску в общем
потоке задаёт `-output_ts_offset`, считаемое в кадрах от начала. На
диск не ложится ничего, кроме результата и звуковой дорожки.
7. Результат сверяется дважды: сумма кадров кусков обязана совпасть с
числом пакетов в готовом файле, а число кадров — с тем, что просил
монтаж. Второе и есть проверка синхронности звука с картинкой; вердикт
пишется в журнал сборки.
Звук собирается раньше картинки: приёмник берёт его вторым входом. В
кусках картинки звука нет — звуковая дорожка собирается отдельно и точнее,
а лишний поток в куске MPEG-TS ещё и сбивает тайминг склейки: замерено
36 мс на стык.
**Через кэш кусков** собирается то, что потоком не выходит: когда кодек
проекта не переносится в MPEG-TS или когда весь ролик пересобирается под
площадку одним кодировщиком. Тогда каждый кусок пишется в отдельный файл,
а склейка идёт concat-демультиплексором с явно указанной длительностью
каждого. Этот путь и был единственным раньше; оба дают побитово один и тот
же ролик — сверено на материале с дрона, PSNR бесконечен на каждом кадре.
Разница в цене: на отрывке в 17.5 с кэш весил 152 МБ против 421 КБ, а
времени ушло 17.5 с против 14.2 с.
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
Напрашивающийся способ — один проход с директивами `inpoint` и `outpoint`,
без временных файлов — проверен и отвергнут: он давал 574 кадра вместо 600
и не пересчитывал метки времени.
## Ловушки, на которые уже наступили
Все перечисленные ниже вещи найдены замерами, а не рассуждениями.
**Локаль.** libmpv отказывается создаваться, если числовая часть локали
процесса не `C`: `mpv_create` возвращает nil. В русской локали десятичный
разделитель запятая, и разбор чисел внутри mpv ломается. Лечится вызовом
`ForceCNumericLocale` до `mpv_create` (`uMpvLib`).
**Панель без окна.** Под gtk2 панель LCL не имеет собственного окна X11, и
запрос окна возвращает окно формы. Отдав такой идентификатор mpv, получаем
видео поверх всего интерфейса. Поэтому под X11 создаётся собственное
дочернее окно по границам панели (`EnsureVideoWindow` в `uMpvPlayer`).
**Относительные пути в списке склейки.** Путь с префиксом `file:` внутри
списка concat разбирается относительно текущего рабочего каталога, а не
относительно самого списка. Все пути приводятся к абсолютным сразу.
**Смешивание кусков от разных кодировщиков.** MP4 и Matroska хранят набор
параметров кодека один на дорожку. Как только рядом оказываются
скопированный кусок из камеры и созданная нами вставка, декодер применяет
параметры первого ко всем: замерено 384 ошибки декодера HEVC при верном
числе кадров. Лечится склейкой через промежуточный MPEG-TS, где параметры
повторяются перед каждым опорным кадром — стало 0 ошибок. Способ выбирается
автоматически: один источник и никаких точных резов — прямая склейка,
Звук больше не отстаёт от картинки Рез всегда точный. Раньше начало куска притягивалось к ближайшему опорному кадру, а звук брался из непритянутой точки: по журналам сборки средняя притяжка 0.24 с, худшая 0.99 с, и на длинном куске этот сдвиг держится все его минуты. Плата за точность — доли секунды перекодирования на рез. Второй вид реза (Shift+S) стал тем же самым и убран. Разбор заголовка MP4 не учитывал список правок (elst): метки опорных кадров уезжали на задержку показа, на пробе два кадра. Сверка быстрого пути с ffprobe теперь совпадает точно, в том числе на записи с B-кадрами. Куску заказывается точное число кадров, а не только время: при копировании ffmpeg отмеряет конец по времени декодирования, и у записи с B-кадрами кусок выходил длиннее. Склейке длительность каждого куска задаётся явно. Последний пакет скопированного куска показывается позже прочих, контейнер считал его в длительность, и время картинки растягивалось — 0.05 с на кусок. Звука в кусках картинки больше нет: он не используется, а в MPEG-TS ещё и сбивает выравнивание склейки на 36 мс. Границы кусков ложатся на кадровую сетку: картинка выходит целым числом кадров, звук набирается до отсчёта, и доли кадра разводили их в стороны. Разогрев кодировщика звука компенсируется: сжатый AAC начинается со служебных отсчётов, MP4 помечает их пропускаемыми, Matroska — нет, и звук ехал на 21.3 мс позже. Величина меряется пробным сжатием. Продолжение прерванной сборки чинится заодно: готовность проверяется у каждой части, а не у куска целиком. При точном резе частей две, и вторая при продолжении терялась. В журнал сборки добавлена проверка синхронности: сколько кадров вышло и сколько просил монтаж, с вердиктом и худшей частью. Замер (tests/syncprobe.sh, исходник со вспышкой и щелчком на каждой секунде): было до 0.6 с и росло по ходу ролика, стало 0.02-0.04 с без накопления при точности измерения 0.017 с.
2026-09-16 18:28:29 +03:00
иначе MPEG-TS. Резы теперь всегда точные, так что на деле это почти всегда
MPEG-TS.
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
**Лишний кадр на стыке точного реза.** При копировании `-to` работает как
«строго меньше», при перекодировании — как «меньше либо равно». Кадр на
границе попадал в ролик дважды: 601 вместо 600. Лечится отступом на
полкадра от границы.
**B-кадры во вставках.** Двунаправленные кадры от NVENC при переносе в
Matroska давали метки декодирования, идущие назад, и двадцать совпадающих
меток на шестьсот кадров. Коротким вставкам B-кадры не нужны, поэтому они
запрещены (`-bf 0`).
Звук больше не отстаёт от картинки Рез всегда точный. Раньше начало куска притягивалось к ближайшему опорному кадру, а звук брался из непритянутой точки: по журналам сборки средняя притяжка 0.24 с, худшая 0.99 с, и на длинном куске этот сдвиг держится все его минуты. Плата за точность — доли секунды перекодирования на рез. Второй вид реза (Shift+S) стал тем же самым и убран. Разбор заголовка MP4 не учитывал список правок (elst): метки опорных кадров уезжали на задержку показа, на пробе два кадра. Сверка быстрого пути с ffprobe теперь совпадает точно, в том числе на записи с B-кадрами. Куску заказывается точное число кадров, а не только время: при копировании ffmpeg отмеряет конец по времени декодирования, и у записи с B-кадрами кусок выходил длиннее. Склейке длительность каждого куска задаётся явно. Последний пакет скопированного куска показывается позже прочих, контейнер считал его в длительность, и время картинки растягивалось — 0.05 с на кусок. Звука в кусках картинки больше нет: он не используется, а в MPEG-TS ещё и сбивает выравнивание склейки на 36 мс. Границы кусков ложатся на кадровую сетку: картинка выходит целым числом кадров, звук набирается до отсчёта, и доли кадра разводили их в стороны. Разогрев кодировщика звука компенсируется: сжатый AAC начинается со служебных отсчётов, MP4 помечает их пропускаемыми, Matroska — нет, и звук ехал на 21.3 мс позже. Величина меряется пробным сжатием. Продолжение прерванной сборки чинится заодно: готовность проверяется у каждой части, а не у куска целиком. При точном резе частей две, и вторая при продолжении терялась. В журнал сборки добавлена проверка синхронности: сколько кадров вышло и сколько просил монтаж, с вердиктом и худшей частью. Замер (tests/syncprobe.sh, исходник со вспышкой и щелчком на каждой секунде): было до 0.6 с и росло по ходу ролика, стало 0.02-0.04 с без накопления при точности измерения 0.017 с.
2026-09-16 18:28:29 +03:00
**Звук отставал от картинки — четыре причины сразу.** Разбор занял день, и
каждая находка мерялась, а не угадывалась (`tests/syncprobe.sh`: исходник
со вспышкой и щелчком на каждой секунде, монтаж с резами не на опорных
кадрах, поиск обоих следов в готовом ролике).
1. *Притянутый рез.* Начало куска картинки сдвигалось на ближайший опорный
кадр, а звук брался из непритянутой точки: в журналах пользователя
средняя притяжка 0.24 с, худшая 0.99 с. Лечится точным резом всегда.
2. *Список правок MP4.* Разбор заголовка не учитывал блок `elst`, и метки
опорных кадров уезжали на задержку показа — на пробе два кадра. Рез
просил перекодировать до метки, которая опорным кадром не была.
3. *Конец куска при копировании.* `-to` отмеряет по времени декодирования,
и у записи с B-кадрами кусок выходил длиннее запрошенного. Лечится
явным числом кадров (`-frames:v`).
4. *Длительность куска в склейке.* Последний пакет скопированного куска
показывается позже всех прочих, контейнер считает его в длительность, и
склейка растягивала время — 0.05 с на кусок. Лечится директивой
`duration` в списке склейки. Туда же: звук внутри кусков картинки сбивал
выравнивание на 36 мс, теперь его там нет.
Плюс «разогрев» кодировщика звука: сжатый AAC начинается со служебных
отсчётов, MP4 помечает их пропускаемыми, Matroska такой пометки не хранит —
звук ехал на 21.3 мс позже. Величина меряется пробным сжатием и вычитается.
Итог замера на образце: было до 0.6 с и росло по ходу ролика, стало 0.02-0.04 с
без накопления, при точности самого измерения 0.017 с.
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
**Вставка под каждую длину дыры.** Сначала под каждую дыру делался чёрный
клип ровно её длины. Любое движение клипа меняло длину дыры, требовало
новой генерации, и до её готовности предпросмотр играл прежнюю расстановку.
Выглядело так, будто выключение дорожки или удаление клипа не действуют.
Лечится заготовками готовых длин, из которых вырезается нужный кусок.
**Первая загрузка начинала воспроизведение.** У незагруженного mpv признак
паузы снят, поэтому первый же список кусков ехал сам. Лечится опцией
`pause=yes` при создании плеера.
**Молчаливое растяжение кадра.** Приведение файла к проекту задавало размер
ключом `-s`, а он растягивает кадр не считаясь с соотношением сторон.
Вертикальное видео превращалось в размазанное по ширине. Теперь размер
задаётся цепочкой фильтров, а вписывание выбирается осознанно.
**Фильтр mpv и кадры в памяти видеокарты.** Цепочка фильтров ffmpeg
работает с обычной памятью, а при аппаратном декодировании кадры остаются
в памяти видеокарты. Граф фильтров тогда просто не собирается: `Impossible
to convert between the formats supported by the filter 'mpv_src_in0'`.
Лечится переключением на `hwdec=auto-copy`, пока фильтр нужен.
Ловушка была не в этом, а в том, как я это проверял: из командной строки
mpv сам выбрал `nvdec-copy`, всё работало, и ошибки не было видно. Она
вылезла только на снимке окна работающего редактора. Проверять такое надо
там, где оно живёт, а не в удобной обстановке.
**Скрипт сборки соврал об успехе.** `PIPESTATUS` живёт ровно до следующей
команды, а в разборе результата стояло двоеточие в ветке `then` — оно и
затирало состояние конвейера. Сборка падала, скрипт докладывал «готово».
Читать `PIPESTATUS` надо сразу и в переменную.
**Модуль Windows конфликтует именами.** Он объявляет свои `TRect`, `Point`
и `RegisterClass`, а имена в Паскале нечувствительны к регистру — обычные
вызовы LCL начинают разбираться как системные, с невнятными ошибками вида
«ожидалось `)`». Подключать его целиком не нужно: `SetMenuInfo` и
`MENUINFO` объявлены в `uTheme` напрямую.
**Знак «▼» в шрифте Windows.** Вместо стрелки выходил пустой квадрат.
Треугольник рисуется сам (`TThemedButton.Arrow`).
**Раскраска до построения меню.** `ApplyDarkTheme` вызывался в начале
`FormCreate`, а меню строится в середине — и цветов не получало. Вызов
должен идти последним.
**Исключения вещественной арифметики.** Паскаль по умолчанию требует
сообщать о каждой неточности вычислений. libmpv и ffmpeg внутри считают
как им удобно и на это не рассчитывают: при создании плеера вылезало
«Invalid floating point operation», и предпросмотр не запускался. Лечится
`SetExceptionMask` после загрузки библиотеки.
**Цепочку смешивания надо снимать до загрузки.** Она ссылается на дорожки
по номерам, и оставшаяся от прошлого раза не даёт открыть новый файл
вовсе: проверено на живом mpv — после такой загрузки дорожек ноль, кадра
нет, не играет ничего. Выглядит это как чёрное окно и полная тишина, и
на плеер не похоже совсем.
Снимать её после загрузки поздно: ломается сама загрузка.
Звуковая дорожка подключается командой, а не свойством Найдена настоящая причина обеих жалоб на звук. Свойство audio-files, заданное перед загрузкой, не подключает внешний список кусков вовсе: проверено на живом mpv через его канал управления — после установки свойства дорожек по-прежнему две, обе не внешние. Играл звук, встроенный в саму картинку. Отсюда обе стороны: звук был слышен там, где на монтаже его нет (шёл из видеофайла), и не был слышен там, где есть (в этом месте картинкой была чёрная вставка, а она молчит). Правильный способ — команда audio-add с признаком select после загрузки. Проверено: дорожек становится четыре, внешняя звуковая выбрана, внутренняя снята с выбора. Если подключить не удалось, в журнал пишется предупреждение. Глушить звук на этот случай нельзя — прошлая попытка так сделать оставила без звука весь монтаж.
2026-08-20 11:58:29 +03:00
**Внешняя звуковая дорожка не подключается свойством.** Задать
`audio-files` перед загрузкой недостаточно: список дорожек после
загрузки остаётся прежним, внешний файл не подключается вовсе, и играет
звук, встроенный в саму картинку. Проверено на живом mpv через его канал
управления: после установки свойства дорожек по-прежнему две, обе не
внешние.
Из-за этого звук был слышен там, где на монтаже его нет. Попытка
защититься, отключая звук при ненайденной внешней дорожке, сделала
хуже — звука не стало нигде.
Правильный способ — команда `audio-add` с признаком `select` после
загрузки: она добавляет дорожку, выбирает её и снимает выбор с
внутренней. Проверено: дорожек становится четыре, внутренняя звуковая
помечается невыбранной.
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
**Аргументы loadfile.** В mpv 0.38 у команды появился четвёртый аргумент —
номер позиции в списке, и настройки файла сдвинулись на пятое место.
Прежний вызов стал ошибкой «The loadfile option must be an integer», и под
Wine с новой библиотекой не грузилось ничего. Гадать по номеру версии не
нужно: те же настройки задаются свойствами перед загрузкой, и называются
они одинаково во всех версиях.
**Модуль Windows конфликтует именами.** Он объявляет свои `TRect`,
`Point`, `DeleteFile`, `GetTempFileName` и `RegisterClass`, а имена в
Паскале нечувствительны к регистру — обычные вызовы LCL начинают
разбираться как системные, с невнятными ошибками вида «ожидалось `)`».
Подключать его целиком не нужно: `SetMenuInfo` и `MENUINFO` объявлены в
`uTheme` напрямую. За сессию эта ловушка сработала четыре раза.
**Порядок байт в TColor.** LCL хранит цвет как `$00BBGGRR`, а не как
привычный RGB. Записанный по привычке код даёт цвет с переставленными
красным и синим.
Доля растёт ровно: строка «progress=continue» больше не обнуляет место Показатель висел на пяти процентах, потом прыгал к семидесяти девяти, потом сразу готово. Виноват не ffmpeg: замерено, что он отдаёт ход безупречно ровно — блок каждые полсекунды, 7%, 14%, 20%, 27% и дальше без единого скачка. Блок заканчивается строкой «progress=continue», в которой места нет: она сообщает только, что работа продолжается, и разбор честно оставляет переданное число как было. Я передавал туда ноль, поэтому каждый блок заканчивался обнулением доли. Показатель стоял и дёргался вверх лишь тогда, когда опрос попадал между строкой со временем и этой. Теперь отсчёт начинается с уже известного места — как это давно делает сборка ролика. Проверено глазами на невидимом экране: шесть снимков подряд через полторы секунды дают 54, 65, 74, 82, 91, 99 процентов, полоса растёт вместе с числом. Ловушку закрепляет проверка в tests/lfmcheck: «progress=continue» и «progress=end» узнаются, но времени не трогают.
2026-10-01 16:24:59 +03:00
**Последняя строка блока о ходе работы обнуляет место.** ffmpeg шлёт ход
блоками, и заканчивается блок строкой `progress=continue`, в которой
места нет — она сообщает только, что работа продолжается. Разбор честно
оставляет переданное число как было, поэтому передавать туда надо уже
известное место, а не ноль. С нулём доля обнуляется в конце каждого
блока: показатель стоит на месте и дёргается вверх лишь тогда, когда
опрос попадёт между строкой со временем и этой — «висит на пяти
процентах, потом скачет до семидесяти девяти». Сам ffmpeg при этом
отдаёт ход безупречно ровно: блок каждые полсекунды, замерено.
Сведение слоёв распаковывает исходник видеокартой Затухание в полторы-две минуты собиралось минутами, и всё это время предпросмотр ждёт. Виноваты оказались не фильтры: на десяти секундах 2688x1512 цепочка со сведением стоит 13.5 с, а вовсе без фильтров — 12.7 с. Время уходит на разбор HEVC процессором. Ключ -hwaccel cuda перед входом убирает почти всё: настоящее затухание на семьдесят секунд собирается 18.3 с вместо 75.9 с, картинка бит в бит та же (одинаковый md5 потока, 4196 кадров там и там). Просится он только там, где кодировать будет та же карта — то есть выбран NVENC. На машине без неё ключ не появляется вовсе. Сверх того у подготовки вставок есть откат: не пошло — повторяем на процессоре, как это уже делает склейка с перекодированием. Сборка ролика пока по-старому: там кусок с наложением идёт в общий поток, и повторить его после отказа посреди работы нельзя — данные уже ушли в трубу. Это отдельная задача.
2026-10-01 15:11:43 +03:00
**Разбор HEVC на процессоре — самое дорогое в сведении слоёв.** Не
фильтры, как кажется: на десяти секундах 2688x1512 цепочка со сведением
стоит 13.5 с, а без всяких фильтров — 12.7 с. Всё время уходит на разбор
исходника. Ключ `-hwaccel cuda` перед входом убирает его почти целиком:
затухание на семьдесят секунд собирается 18.3 с вместо 75.9 с, картинка
бит в бит та же. Просить его стоит только там, где кодирует та же карта
(выбран NVENC): иначе на машине без неё пришлось бы ловить отказ.
Сборка вставки не начинается заново от каждой правки Затухание в семьдесят секунд не собиралось никогда: сборка сведённого куска шла полторы минуты, и любая правка в это время сбрасывала её на ноль. В журнале это видно как «Расстановка изменилась» и тот же самый запуск ffmpeg по кругу. Причина — в вопросе, который задавался. «Нужна ли ещё эта работа» проверялось по списку недостающего, а недостающее у нас — это то, чего нет на диске. У куска, который делается прямо сейчас, файл уже есть: ffmpeg создаёт его в первую же секунду. Кусок в список не попадал, ответ был «не нужна», работа отменялась. Теперь уплощение отдельно отдаёт список нужного — независимо от того, лежит файл на диске или ещё пишется. Заодно закрыта та же дыра со вставками тишины: проверка их вообще не видела, потому что звуковые дорожки считаются отдельным проходом. Проверено обеими сторонами (tests/genrestart.sh, на настоящем материале, затухание 70 с): пять резов в стороне от затухания — один запуск и ни одной отмены, было шесть запусков и десять отмен. Удалили всё — работа прекращается за две секунды, как и прежде.
2026-10-01 00:47:28 +03:00
**Список недостающего — не список нужного.** Недостающее у нас — это то,
чего нет на диске. Но у куска, который делается прямо сейчас, файл уже
есть: ffmpeg создаёт его в первую же секунду и пишет до конца. Проверка
«нужна ли ещё эта работа» спрашивала по списку недостающего — и отвечала
«не нужна» про то, что делается. Работа отменялась в разгаре и начиналась
заново. На коротком затухании это проходило незаметно, на затухании в
семьдесят секунд сборка не заканчивалась никогда: любая правка сбрасывала
её. Теперь уплощение отдельно отдаёт список нужного, независимо от того,
лежит файл на диске или ещё пишется (`tests/genrestart.sh`).
**MousePos у колеса мыши — не экранный.** По описанию `DoMouseWheel`
получает точку в координатах экрана, но под gtk2 в ней уже клиентские:
`ScreenToClient` переводит их второй раз, и точка уезжает влево ровно на
левый край элемента. У масштаба таймлайна это выглядело так, будто он
берётся не от мыши: замерено на невидимом экране — мышь в 700, 1000 и
1300, опора в 260, 560 и 860, всегда на 440 левее. Лечится обращением к
`Mouse.CursorPos`: он экранный в любой системе, и перевод остаётся один.
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
**Имена, занятые предками.** Локальная переменная перекрывает одноимённое
свойство класса: `Text` и `Position` заняты у `TControl`, `Finished` — у
`TThread`, `Name` и `Caption` — у формы. Компилятор ловит это как
«Duplicate identifier», но причину не объясняет.
Честное предупреждение о контейнере и отметка недописанной сборки У MP4 два неудобства, о которых человек узнавал поздно: прерванную сборку в нём продолжить нельзя вовсе, а перекодированный стык точного реза воспроизводится правильно не везде. Теперь об обоих говорится до начала работы, одним окном с кнопками «Собрать в MKV», «Всё равно mp4», «Не собирать». Признак точного реза заодно исправлен: смотрели на отметку edPrecise, а резы стали точными всегда — предупреждение молчало. Теперь признак — клип, начинающийся не с начала файла. Продолжить прерванную сборку предлагается по отметке рядом с файлом, а не по его наличию: оборванный MKV открывается и играет, и отличить его от готового больше нечем. Отметка ставится при неудаче, снимается при успехе и хранит число записанных кадров — его и показывают в вопросе. Ещё: на каждом стыке кусков приёмник писал «Packet corrupt», по паре строк на кусок. Данные целы, но журнал забивался, и настоящую ошибку в нём было не найти. Лечится ключом -mpegts_flags +initial_discontinuity у отправителя: сверено по кадрам и по md5, результат тот же.
2026-09-30 16:48:31 +03:00
**Стык кусков в потоке выглядит как порча.** Каждый кусок — это
отдельный поток MPEG-TS со своим счётчиком непрерывности, и на стыке
приёмник пишет «Packet corrupt», по паре строк на кусок. Данные при этом
целы, но журнал сборки забивается жалобами, из-за которых не найти
настоящую ошибку. Лечится ключом `-mpegts_flags +initial_discontinuity`
у отправителя: он помечает начало куска как разрыв, жалобы исчезают, а
результат тот же — сверено по кадрам и по md5.
Продолжение прерванной сборки потоком — с недописанного файла Потоком кэша кусков нет, и продолжать было не с чего. Но недописанный файл сам по себе — уже собранная часть фильма: в нём видно, сколько целых кадров успело записаться. Программа отступает до границы последнего целого куска, переносит эту часть и досылает остаток. Дописать в конец существующего MKV нельзя, поэтому продолжение пишется в новый файл. MP4 продолжать нельзя вовсе: оглавление в конце, и оборванный файл не открывается — об этом говорится прямо. Готовый файл теперь цел до последнего: сборка идёт в файл рядом и занимает место готового, только когда дописана. Сорвавшаяся попытка не отбирает уже собранное — из двух недописанных остаётся тот, где кадров больше, и считаются они даже после запроса остановки. Отдельно пришлось снять сдвиг, который приёмник накладывает на весь ролик: перенесённая часть несла его в себе, и куски за ней задваивали кадр. Замерено: обрыв в четырёх разных местах, продолженная сборка каждый раз совпала с собранной за один раз попиксельно. В nlvetool у render появились --cancel-after=<сек> и сочетание слов: cache resume проверяет, что старый путь через кэш не сломался.
2026-09-30 16:37:28 +03:00
**Приёмник двигает весь ролик.** `-avoid_negative_ts make_zero` у
приёмника сдвигает поток так, чтобы самая ранняя метка декодирования
стала нулём, — а у первого куска она уходит в минус на задержку
кодировщика, и весь ролик встаёт на 21 мс позже. Само по себе это
безобидно, но продолжение прерванной сборки несёт этот сдвиг в
перенесённой части, и куски за ней, размещённые по счёту кадров от нуля,
налезают на неё на кадр. Поэтому при переносе сдвиг снимается
отрицательным `-output_ts_offset`, а приёмник накладывает его заново.
Сборка потоком: куски идут сразу в готовый файл, без кэша Раньше каждый кусок монтажа вырезался в отдельный файл, и только потом список этих файлов шёл на склейку. Кэш весил столько же, сколько готовый фильм — замерено 110 ГБ на часе материала, — а диск писал всё дважды. Теперь каждый кусок режется своим ffmpeg прямо в MPEG-TS на стандартный вывод, все потоки подряд переливаются в один принимающий ffmpeg, а тот пишет готовый файл. Место куску задаёт -output_ts_offset, считаемый в кадрах от начала. На диск ложится только результат и звуковая дорожка. Через кэш по-прежнему собирается то, что потоком не выходит: кодек, не переносимый в MPEG-TS, и пересборка всего ролика под площадку. Сверено на четырёх монтажах, включая настоящий материал с дрона (HEVC 2688x1512, 55 Мбит/с, файлы с шестью потоками): картинка совпала попиксельно, PSNR бесконечен на каждом кадре, длина и размер те же. Сверка шла и с прошлым выпуском, а не только нового пути со старым. На отрывке в 17.5 с: 14.2 с и 421 КБ на диске против 17.5 с и 152 МБ. По дороге найдены две ловушки, обе записаны в README: -avoid_negative_ts make_zero отменяет -output_ts_offset, а -fflags +igndts у приёмника сминает кадры на стыках.
2026-09-30 16:12:00 +03:00
**Сдвиг к нулю отменяет сдвиг по времени.** При сборке потоком место
куску задаёт `-output_ts_offset`, но `-avoid_negative_ts make_zero`
считается уже после него и двигает кусок обратно в ноль — ключ просто
перестаёт действовать. Замерено: все шесть кусков встали в начало,
картинка разошлась с эталоном (PSNR 18 дБ) при верном числе кадров и
тех же пакетах. В потоке сдвиг к нулю выключен; отрицательные метки в
самом начале подбирает приёмник, у него make_zero свой.
**`-fflags +igndts` сминает кадры на стыках.** С ним ffmpeg выбрасывает
пришедшие метки декодирования и считает свои, а материал с B-кадрами
хранится не в том порядке, в каком показывается. На стыке появлялось по
три кадра с одной меткой, картинка расходилась с эталоном. Без этого
ключа метки идут шаг в шаг, и ни одной жалобы на непоследовательность.
Кнопки внизу: подсказка не перехватывает щелчок, щелчки не теряются Две беды у всех кнопок нижней полосы. Всплывающая подсказка появлялась прямо поверх кнопки, о которой рассказывает, и перехватывала щелчок: нажать кнопку было нельзя. Подсказка переехала в строку состояния — она в той же полосе, ничего не закрывает и меняется сразу после щелчка, не дожидаясь нового наведения. Строка состояния теперь занимает всё свободное место справа, пока рядом не показан ход работы, а самые длинные подсказки укорочены: раньше их обрезало на полуслове. Второй щелчок подряд виджетсет отдавал как двойной, и кнопка его не видела: быстрая очередь щелчков срабатывала через раз. У кнопки убран csDoubleClicks, а на случай, если двойной всё же дойдёт, он считается за обычный. Проверено: tests/lfmcheck, раздел «Кнопки нижней панели», и tests/buttonclicks.sh мышью на невидимом экране — по всем шести переключателям очереди из 1-5 щелчков отрабатываются полностью.
2026-09-30 15:44:29 +03:00
**Двойной щелчок съедает второй щелчок.** Пока у элемента есть
`csDoubleClicks`, второй щелчок подряд приходит не как `Click`, а как
`DblClick`, и обработчик кнопки его не видит: человек щёлкает три раза,
срабатывает один. Лечится двумя строками в конструкторе:
`ControlStyle := ControlStyle - [csDoubleClicks]` и `DblClick`,
переопределённый в `Click`, — на случай, если двойной всё же дойдёт.
**Всплывающая подсказка перехватывает щелчок.** Окно подсказки в gtk2
встаёт поверх кнопки, о которой рассказывает, и щелчок попадает в него,
а не в кнопку. У кнопок в тесной полосе `ShowHint` выключен, а текст
подсказки идёт в строку состояния (`UseStatusHints` в `uMainForm`).
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
## Консольный инструмент проверок
Часть вещей проверяется числами, а не глазами. Сборка: `./build.sh tool`.
```
./tests/nlvetool kf <файл> индекс опорных кадров
./tests/nlvetool kfcheck <файл> сверка быстрого пути с ffprobe
./tests/nlvetool flattest проверка уплощения монтажа
./tests/nlvetool edl <файл> <старт> <длина> [...]
./tests/nlvetool export <файл> <выход> <старт> <длина> [...]
./tests/nlvetool export --cancel-after=<сек> ... проверка остановки
./tests/nlvetool remux <файл>... <расширение>
./tests/nlvetool build <проект> <видео> [<картинка>]
./tests/nlvetool buildcut <проект> <видео>
./tests/nlvetool render <проект> <выход>
Звук больше не отстаёт от картинки Рез всегда точный. Раньше начало куска притягивалось к ближайшему опорному кадру, а звук брался из непритянутой точки: по журналам сборки средняя притяжка 0.24 с, худшая 0.99 с, и на длинном куске этот сдвиг держится все его минуты. Плата за точность — доли секунды перекодирования на рез. Второй вид реза (Shift+S) стал тем же самым и убран. Разбор заголовка MP4 не учитывал список правок (elst): метки опорных кадров уезжали на задержку показа, на пробе два кадра. Сверка быстрого пути с ffprobe теперь совпадает точно, в том числе на записи с B-кадрами. Куску заказывается точное число кадров, а не только время: при копировании ffmpeg отмеряет конец по времени декодирования, и у записи с B-кадрами кусок выходил длиннее. Склейке длительность каждого куска задаётся явно. Последний пакет скопированного куска показывается позже прочих, контейнер считал его в длительность, и время картинки растягивалось — 0.05 с на кусок. Звука в кусках картинки больше нет: он не используется, а в MPEG-TS ещё и сбивает выравнивание склейки на 36 мс. Границы кусков ложатся на кадровую сетку: картинка выходит целым числом кадров, звук набирается до отсчёта, и доли кадра разводили их в стороны. Разогрев кодировщика звука компенсируется: сжатый AAC начинается со служебных отсчётов, MP4 помечает их пропускаемыми, Matroska — нет, и звук ехал на 21.3 мс позже. Величина меряется пробным сжатием. Продолжение прерванной сборки чинится заодно: готовность проверяется у каждой части, а не у куска целиком. При точном резе частей две, и вторая при продолжении терялась. В журнал сборки добавлена проверка синхронности: сколько кадров вышло и сколько просил монтаж, с вердиктом и худшей частью. Замер (tests/syncprobe.sh, исходник со вспышкой и щелчком на каждой секунде): было до 0.6 с и росло по ходу ролика, стало 0.02-0.04 с без накопления при точности измерения 0.017 с.
2026-09-16 18:28:29 +03:00
./tests/nlvetool syncproj <проект> <видео> монтаж для замера синхронности
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
```
Команды возвращают ненулевой код при расхождении, поэтому годятся для
автоматической проверки.
Звук больше не отстаёт от картинки Рез всегда точный. Раньше начало куска притягивалось к ближайшему опорному кадру, а звук брался из непритянутой точки: по журналам сборки средняя притяжка 0.24 с, худшая 0.99 с, и на длинном куске этот сдвиг держится все его минуты. Плата за точность — доли секунды перекодирования на рез. Второй вид реза (Shift+S) стал тем же самым и убран. Разбор заголовка MP4 не учитывал список правок (elst): метки опорных кадров уезжали на задержку показа, на пробе два кадра. Сверка быстрого пути с ffprobe теперь совпадает точно, в том числе на записи с B-кадрами. Куску заказывается точное число кадров, а не только время: при копировании ffmpeg отмеряет конец по времени декодирования, и у записи с B-кадрами кусок выходил длиннее. Склейке длительность каждого куска задаётся явно. Последний пакет скопированного куска показывается позже прочих, контейнер считал его в длительность, и время картинки растягивалось — 0.05 с на кусок. Звука в кусках картинки больше нет: он не используется, а в MPEG-TS ещё и сбивает выравнивание склейки на 36 мс. Границы кусков ложатся на кадровую сетку: картинка выходит целым числом кадров, звук набирается до отсчёта, и доли кадра разводили их в стороны. Разогрев кодировщика звука компенсируется: сжатый AAC начинается со служебных отсчётов, MP4 помечает их пропускаемыми, Matroska — нет, и звук ехал на 21.3 мс позже. Величина меряется пробным сжатием. Продолжение прерванной сборки чинится заодно: готовность проверяется у каждой части, а не у куска целиком. При точном резе частей две, и вторая при продолжении терялась. В журнал сборки добавлена проверка синхронности: сколько кадров вышло и сколько просил монтаж, с вердиктом и худшей частью. Замер (tests/syncprobe.sh, исходник со вспышкой и щелчком на каждой секунде): было до 0.6 с и росло по ходу ролика, стало 0.02-0.04 с без накопления при точности измерения 0.017 с.
2026-09-16 18:28:29 +03:00
Отдельно — замер синхронности звука и картинки:
```
./tests/syncprobe.sh весь прогон: образец, сборка, замер
./tests/syncprobe.sh measure ФАЙЛ замер готового файла
```
Как он устроен. Делается исходник, где ровно на каждой секунде в кадре
белая вспышка, а в звуке щелчок. Из него собирается монтаж, у которого
начала кусков заведомо не попадают на опорные кадры. В готовом ролике
ищутся времена вспышек и щелчков: если они разошлись — звук уехал от
картинки, и видно, на сколько и с какого куска.
Времена щелчков считаются по самим отсчётам (`tests/clicktimes.py`), а не
оконным анализом: окно двигается кратно своему размеру, и его собственный
уход выглядит как накапливающийся рассинхрон, которого нет. На это уже
попались один раз.
Щелчок в месте наезда перебирает клипы Наезд двух клипов на одной дорожке — заказ на переход, то есть обычное дело. Нажатие же всегда попадало в верхний по списку, и нижний клип нельзя было ни выделить, ни потащить: чтобы до него добраться, приходилось сперва убирать верхний. Правило такое. Нажатие берёт тот из накрытых клипов, который уже выделен, иначе верхний. Щелчок без движения по уже выделенному переводит выделение на следующий, после последнего снова на верхний. Переключение сделано на отпускании намеренно: пока кнопку не отпустили, неизвестно, щелчок это или начало протяжки, а менять клип под рукой посреди протяжки нельзя. Благодаря этому протяжка тащит именно то, что перед ней выбрали щелчками. Строка состояния говорит, сколько клипов под указателем: перебор, о котором не сказали, — тайное знание. Отбор вынесен в TTrack.ClipsAt, числовая проверка — в lfmcheck. tests/pickcycle.sh щёлкает дважды и тащит: на прежней сборке уезжает верхний клип, а нижний едет за ним схлопыванием.
2026-10-16 15:43:01 +03:00
Ещё десять проверок гоняют саму программу на невидимом экране
(`Xvfb :99`). Рабочий экран они не занимают:
Сборка вставки не начинается заново от каждой правки Затухание в семьдесят секунд не собиралось никогда: сборка сведённого куска шла полторы минуты, и любая правка в это время сбрасывала её на ноль. В журнале это видно как «Расстановка изменилась» и тот же самый запуск ffmpeg по кругу. Причина — в вопросе, который задавался. «Нужна ли ещё эта работа» проверялось по списку недостающего, а недостающее у нас — это то, чего нет на диске. У куска, который делается прямо сейчас, файл уже есть: ffmpeg создаёт его в первую же секунду. Кусок в список не попадал, ответ был «не нужна», работа отменялась. Теперь уплощение отдельно отдаёт список нужного — независимо от того, лежит файл на диске или ещё пишется. Заодно закрыта та же дыра со вставками тишины: проверка их вообще не видела, потому что звуковые дорожки считаются отдельным проходом. Проверено обеими сторонами (tests/genrestart.sh, на настоящем материале, затухание 70 с): пять резов в стороне от затухания — один запуск и ни одной отмены, было шесть запусков и десять отмен. Удалили всё — работа прекращается за две секунды, как и прежде.
2026-10-01 00:47:28 +03:00
```
./tests/buttonclicks.sh <окно> <x> <y> каждый щелчок по кнопке считается
./tests/zoomanchor.py <окно> масштаб берётся от точки под мышкой
./tests/genrestart.sh [<путь к nlve>] сборка вставки не сбрасывается зря
./tests/fadegrab.sh [<путь к nlve>] ручку затухания видно и во второй раз
./tests/insertat.sh [<путь к nlve>] файл из списка встаёт на курсор
./tests/dragscroll.sh [<путь к nlve>] монтаж едет следом за клипом у кромки
./tests/genquiet.sh [<путь к nlve>] тяжёлая сборка ждёт конца правки
Крест перехода там, где клипы не накладываются На монтаже рисовался косой крест через целый клип, строка состояния обещала переход, а наложения не было: ролик собирался верно. Крест уходил сам после Ctrl+Z, после любого действия с отменой и после схлопывания. Это был не наезд, а сбитый порядок клипов в списке дорожки. Список отсортирован по времени начала, и соседи по списку обязаны быть соседями по времени — на этом стоят и отрисовка креста, и сообщение о наезде. Перетаскивание же двигает не одну дорожку: связка ведёт звук за картинкой, схлопывание ведёт соседей, в режиме «все дорожки» — сразу на нескольких. Сортировалась только та дорожка, с которой тащили. Клип, уехавший с конца монтажа в середину, оставался в списке чужой дорожки последним, и её соседями по списку становились клипы, далёкие друг от друга по времени. Отмена и схлопывание лечили ровно потому, что пересортировывают списки. Сборка всё это время была права: она идёт по границам и сортирует дорожку сама. Теперь порядок возвращается всем дорожкам, чьи клипы поехали (SortMovedTracks). Оба места, считающие соседей по списку, заодно пропускают пару, идущую не по времени: страховка от того же симптома в другом месте. Проверка tests/dragorder.sh тащит связку за звук с конца монтажа в середину и читает порядок из сохранённого файла. На прежней сборке дорожка картинки остаётся в порядке 0, 14, 7.991 — проверка падает.
2026-10-16 14:01:51 +03:00
./tests/dropgap.sh [<путь к nlve>] клип садится в дыру без наезда
./tests/dragorder.sh [<путь к nlve>] связка переехала — порядок цел
Щелчок в месте наезда перебирает клипы Наезд двух клипов на одной дорожке — заказ на переход, то есть обычное дело. Нажатие же всегда попадало в верхний по списку, и нижний клип нельзя было ни выделить, ни потащить: чтобы до него добраться, приходилось сперва убирать верхний. Правило такое. Нажатие берёт тот из накрытых клипов, который уже выделен, иначе верхний. Щелчок без движения по уже выделенному переводит выделение на следующий, после последнего снова на верхний. Переключение сделано на отпускании намеренно: пока кнопку не отпустили, неизвестно, щелчок это или начало протяжки, а менять клип под рукой посреди протяжки нельзя. Благодаря этому протяжка тащит именно то, что перед ней выбрали щелчками. Строка состояния говорит, сколько клипов под указателем: перебор, о котором не сказали, — тайное знание. Отбор вынесен в TTrack.ClipsAt, числовая проверка — в lfmcheck. tests/pickcycle.sh щёлкает дважды и тащит: на прежней сборке уезжает верхний клип, а нижний едет за ним схлопыванием.
2026-10-16 15:43:01 +03:00
./tests/pickcycle.sh [<путь к nlve>] щелчок перебирает наехавшие клипы
Сборка вставки не начинается заново от каждой правки Затухание в семьдесят секунд не собиралось никогда: сборка сведённого куска шла полторы минуты, и любая правка в это время сбрасывала её на ноль. В журнале это видно как «Расстановка изменилась» и тот же самый запуск ffmpeg по кругу. Причина — в вопросе, который задавался. «Нужна ли ещё эта работа» проверялось по списку недостающего, а недостающее у нас — это то, чего нет на диске. У куска, который делается прямо сейчас, файл уже есть: ffmpeg создаёт его в первую же секунду. Кусок в список не попадал, ответ был «не нужна», работа отменялась. Теперь уплощение отдельно отдаёт список нужного — независимо от того, лежит файл на диске или ещё пишется. Заодно закрыта та же дыра со вставками тишины: проверка их вообще не видела, потому что звуковые дорожки считаются отдельным проходом. Проверено обеими сторонами (tests/genrestart.sh, на настоящем материале, затухание 70 с): пять резов в стороне от затухания — один запуск и ни одной отмены, было шесть запусков и десять отмен. Удалили всё — работа прекращается за две секунды, как и прежде.
2026-10-01 00:47:28 +03:00
```
Щелчок в месте наезда перебирает клипы Наезд двух клипов на одной дорожке — заказ на переход, то есть обычное дело. Нажатие же всегда попадало в верхний по списку, и нижний клип нельзя было ни выделить, ни потащить: чтобы до него добраться, приходилось сперва убирать верхний. Правило такое. Нажатие берёт тот из накрытых клипов, который уже выделен, иначе верхний. Щелчок без движения по уже выделенному переводит выделение на следующий, после последнего снова на верхний. Переключение сделано на отпускании намеренно: пока кнопку не отпустили, неизвестно, щелчок это или начало протяжки, а менять клип под рукой посреди протяжки нельзя. Благодаря этому протяжка тащит именно то, что перед ней выбрали щелчками. Строка состояния говорит, сколько клипов под указателем: перебор, о котором не сказали, — тайное знание. Отбор вынесен в TTrack.ClipsAt, числовая проверка — в lfmcheck. tests/pickcycle.sh щёлкает дважды и тащит: на прежней сборке уезжает верхний клип, а нижний едет за ним схлопыванием.
2026-10-16 15:43:01 +03:00
Отдельно от них `tests/imagecrop.sh` — сквозная, без окна: делает
картинку, кладёт её клипом с уменьшением и смещением, собирает секунду
Клип, не заполняющий кадр, ложится поверх нижнего, а не закрывает его Уменьшенная и сдвинутая картинка выходила в ролик с чёрными полями, и нижнюю дорожку закрывал не сам клип, а эти поля. Так же вело себя и уменьшенное видео. Между тем уменьшают клип ровно затем, чтобы под ним было видно другое. Теперь «не заполняет кадр» означает то же, что «не сплошной»: наравне с выбитым цветом и прозрачностью. Такой клип уходит в накладываемые — предпросмотр рисует его поверх нижнего силами плеера, сборка собирает участок слоями, где у верхнего слоя цепочка без полей и место, куда он встаёт. Класть не на что — поля остаются полями, как и были. Решение одно на оба пути: IsOverlayClip, и в нём TransformFillsFrame считает по тем же числам, что уходят в фильтр. Заодно две вещи, всплывшие по дороге: — у картинки своя ветка уплощения, и слои там не строились вовсе: клип с прозрачностью в окне накладывался, а в ролике нет. Теперь строятся. — в сборке ветка «готовая вставка» стояла раньше ветки слоёв, и кусок со слоями уходил в неё за несуществующей заготовкой. Заготовка для такого куска больше и не заказывается: это были бы минуты впустую. Проверка tests/imagecrop.sh получила второй случай: под картинкой видео, вокруг неё обязано быть видно его. На прежней сборке там чёрное.
2026-10-16 16:17:30 +03:00
ролика и смотрит на точки готового кадра. Случая два: класть не на что —
вокруг картинки чёрные поля; под картинкой видео — вокруг неё видно его,
а не поля. Ей нужны `tests/nlvetool` (`./build.sh test`), ffmpeg и python
с PIL.
Щелчок в месте наезда перебирает клипы Наезд двух клипов на одной дорожке — заказ на переход, то есть обычное дело. Нажатие же всегда попадало в верхний по списку, и нижний клип нельзя было ни выделить, ни потащить: чтобы до него добраться, приходилось сперва убирать верхний. Правило такое. Нажатие берёт тот из накрытых клипов, который уже выделен, иначе верхний. Щелчок без движения по уже выделенному переводит выделение на следующий, после последнего снова на верхний. Переключение сделано на отпускании намеренно: пока кнопку не отпустили, неизвестно, щелчок это или начало протяжки, а менять клип под рукой посреди протяжки нельзя. Благодаря этому протяжка тащит именно то, что перед ней выбрали щелчками. Строка состояния говорит, сколько клипов под указателем: перебор, о котором не сказали, — тайное знание. Отбор вынесен в TTrack.ClipsAt, числовая проверка — в lfmcheck. tests/pickcycle.sh щёлкает дважды и тащит: на прежней сборке уезжает верхний клип, а нижний едет за ним схлопыванием.
2026-10-16 15:43:01 +03:00
Последним восьми нужен монтаж в `test/поток/`: `длинный-фейд.nlve` — один
клип с затуханием секунд на семьдесят (такой кусок сводится около
полутора минут, и всё это время человек продолжает работать),
`пара.nlve` — два клипа без затуханий. Путь к первому задаётся
переменной `NLVE_FADE_PROJECT`. В репозиторий они не входят, потому что
ссылаются на съёмку, которой здесь нет. `genrestart.sh` съёмку
действительно открывает и, не найдя её, честно говорит об этом и
отказывается считать себя пройденной; остальным хватает самого монтажа.
Экран поднимается через `xvfb-run`, и у него свой файл доступа. Проверки
находят его сами по строке запуска `Xvfb`, так что отдельно задавать
`XAUTHORITY` не нужно; чужой файл из окружения они не берут — с ним
xdotool не видит ни одного окна и проверка «проходит», ничего не
проверив. `zoomanchor.py` целится мышью в точку 1300, поэтому ему нужен
экран шире: `Xvfb :98 -screen 0 1600x1000x24` и `ЭКРАН=:98`.
Каждую из них стоит прогонять и на прежней сборке: проверка, которая
Щелчок в месте наезда перебирает клипы Наезд двух клипов на одной дорожке — заказ на переход, то есть обычное дело. Нажатие же всегда попадало в верхний по списку, и нижний клип нельзя было ни выделить, ни потащить: чтобы до него добраться, приходилось сперва убирать верхний. Правило такое. Нажатие берёт тот из накрытых клипов, который уже выделен, иначе верхний. Щелчок без движения по уже выделенному переводит выделение на следующий, после последнего снова на верхний. Переключение сделано на отпускании намеренно: пока кнопку не отпустили, неизвестно, щелчок это или начало протяжки, а менять клип под рукой посреди протяжки нельзя. Благодаря этому протяжка тащит именно то, что перед ней выбрали щелчками. Строка состояния говорит, сколько клипов под указателем: перебор, о котором не сказали, — тайное знание. Отбор вынесен в TTrack.ClipsAt, числовая проверка — в lfmcheck. tests/pickcycle.sh щёлкает дважды и тащит: на прежней сборке уезжает верхний клип, а нижний едет за ним схлопыванием.
2026-10-16 15:43:01 +03:00
проходит везде, не проверяет ничего. Все десять на сборке до своей правки
Крест перехода там, где клипы не накладываются На монтаже рисовался косой крест через целый клип, строка состояния обещала переход, а наложения не было: ролик собирался верно. Крест уходил сам после Ctrl+Z, после любого действия с отменой и после схлопывания. Это был не наезд, а сбитый порядок клипов в списке дорожки. Список отсортирован по времени начала, и соседи по списку обязаны быть соседями по времени — на этом стоят и отрисовка креста, и сообщение о наезде. Перетаскивание же двигает не одну дорожку: связка ведёт звук за картинкой, схлопывание ведёт соседей, в режиме «все дорожки» — сразу на нескольких. Сортировалась только та дорожка, с которой тащили. Клип, уехавший с конца монтажа в середину, оставался в списке чужой дорожки последним, и её соседями по списку становились клипы, далёкие друг от друга по времени. Отмена и схлопывание лечили ровно потому, что пересортировывают списки. Сборка всё это время была права: она идёт по границам и сортирует дорожку сама. Теперь порядок возвращается всем дорожкам, чьи клипы поехали (SortMovedTracks). Оба места, считающие соседей по списку, заодно пропускают пару, идущую не по времени: страховка от того же симптома в другом месте. Проверка tests/dragorder.sh тащит связку за звук с конца монтажа в середину и читает порядок из сохранённого файла. На прежней сборке дорожка картинки остаётся в порядке 0, 14, 7.991 — проверка падает.
2026-10-16 14:01:51 +03:00
падают. Замерено: клип у кромки уезжает на 0.7 с против 4.2 с, файл из
списка падает в конец дорожки вместо курсора, сборка начинается через
Щелчок в месте наезда перебирает клипы Наезд двух клипов на одной дорожке — заказ на переход, то есть обычное дело. Нажатие же всегда попадало в верхний по списку, и нижний клип нельзя было ни выделить, ни потащить: чтобы до него добраться, приходилось сперва убирать верхний. Правило такое. Нажатие берёт тот из накрытых клипов, который уже выделен, иначе верхний. Щелчок без движения по уже выделенному переводит выделение на следующий, после последнего снова на верхний. Переключение сделано на отпускании намеренно: пока кнопку не отпустили, неизвестно, щелчок это или начало протяжки, а менять клип под рукой посреди протяжки нельзя. Благодаря этому протяжка тащит именно то, что перед ней выбрали щелчками. Строка состояния говорит, сколько клипов под указателем: перебор, о котором не сказали, — тайное знание. Отбор вынесен в TTrack.ClipsAt, числовая проверка — в lfmcheck. tests/pickcycle.sh щёлкает дважды и тащит: на прежней сборке уезжает верхний клип, а нижний едет за ним схлопыванием.
2026-10-16 15:43:01 +03:00
0.2 с вместо 1.6 с, клип в дыру садится с наездом 0.007383 с, дорожка
Крест перехода там, где клипы не накладываются На монтаже рисовался косой крест через целый клип, строка состояния обещала переход, а наложения не было: ролик собирался верно. Крест уходил сам после Ctrl+Z, после любого действия с отменой и после схлопывания. Это был не наезд, а сбитый порядок клипов в списке дорожки. Список отсортирован по времени начала, и соседи по списку обязаны быть соседями по времени — на этом стоят и отрисовка креста, и сообщение о наезде. Перетаскивание же двигает не одну дорожку: связка ведёт звук за картинкой, схлопывание ведёт соседей, в режиме «все дорожки» — сразу на нескольких. Сортировалась только та дорожка, с которой тащили. Клип, уехавший с конца монтажа в середину, оставался в списке чужой дорожки последним, и её соседями по списку становились клипы, далёкие друг от друга по времени. Отмена и схлопывание лечили ровно потому, что пересортировывают списки. Сборка всё это время была права: она идёт по границам и сортирует дорожку сама. Теперь порядок возвращается всем дорожкам, чьи клипы поехали (SortMovedTracks). Оба места, считающие соседей по списку, заодно пропускают пару, идущую не по времени: страховка от того же симптома в другом месте. Проверка tests/dragorder.sh тащит связку за звук с конца монтажа в середину и читает порядок из сохранённого файла. На прежней сборке дорожка картинки остаётся в порядке 0, 14, 7.991 — проверка падает.
2026-10-16 14:01:51 +03:00
картинки после переноса связки остаётся в порядке 0, 14, 7.991 — не по
Щелчок в месте наезда перебирает клипы Наезд двух клипов на одной дорожке — заказ на переход, то есть обычное дело. Нажатие же всегда попадало в верхний по списку, и нижний клип нельзя было ни выделить, ни потащить: чтобы до него добраться, приходилось сперва убирать верхний. Правило такое. Нажатие берёт тот из накрытых клипов, который уже выделен, иначе верхний. Щелчок без движения по уже выделенному переводит выделение на следующий, после последнего снова на верхний. Переключение сделано на отпускании намеренно: пока кнопку не отпустили, неизвестно, щелчок это или начало протяжки, а менять клип под рукой посреди протяжки нельзя. Благодаря этому протяжка тащит именно то, что перед ней выбрали щелчками. Строка состояния говорит, сколько клипов под указателем: перебор, о котором не сказали, — тайное знание. Отбор вынесен в TTrack.ClipsAt, числовая проверка — в lfmcheck. tests/pickcycle.sh щёлкает дважды и тащит: на прежней сборке уезжает верхний клип, а нижний едет за ним схлопыванием.
2026-10-16 15:43:01 +03:00
времени, второй щелчок по наезду тащит верхний клип вместо нижнего, а
картинка с уменьшением 50% выходит в ролик во весь кадр.
Сборка вставки не начинается заново от каждой правки Затухание в семьдесят секунд не собиралось никогда: сборка сведённого куска шла полторы минуты, и любая правка в это время сбрасывала её на ноль. В журнале это видно как «Расстановка изменилась» и тот же самый запуск ffmpeg по кругу. Причина — в вопросе, который задавался. «Нужна ли ещё эта работа» проверялось по списку недостающего, а недостающее у нас — это то, чего нет на диске. У куска, который делается прямо сейчас, файл уже есть: ffmpeg создаёт его в первую же секунду. Кусок в список не попадал, ответ был «не нужна», работа отменялась. Теперь уплощение отдельно отдаёт список нужного — независимо от того, лежит файл на диске или ещё пишется. Заодно закрыта та же дыра со вставками тишины: проверка их вообще не видела, потому что звуковые дорожки считаются отдельным проходом. Проверено обеими сторонами (tests/genrestart.sh, на настоящем материале, затухание 70 с): пять резов в стороне от затухания — один запуск и ни одной отмены, было шесть запусков и десять отмен. Удалили всё — работа прекращается за две секунды, как и прежде.
2026-10-01 00:47:28 +03:00
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
## Структура
| Файл | Что это |
|---|---|
| `src/uAppTypes.pas` | Базовые типы, форматирование чисел без учёта локали |
| `src/uSettings.pas` | Настройки между запусками (JSON) |
| `src/uProcRunner.pas` | Запуск ffmpeg и ffprobe, разбор вывода, остановка |
| `src/uFfmpegCmd.pas` | Сборка аргументов ffmpeg, разбор его прогресса |
| `src/uMediaProbe.pas` | Что за файл нам дали и чем он отличается от проекта |
| `src/uKeyframeIndex.pas` | Где опорные кадры: разбор заголовка MP4 и запасной путь |
| `src/uProject.pas` | Модель монтажа, JSON проекта, отмена действий |
| `src/uFlatten.pas` | Монтаж в плоский список кусков |
| `src/uGenerate.pas` | Клипы для картинок и дыр |
| `src/uExport.pas` | Вырезание, склейка, сверка числа кадров |
| `src/uRemuxQueue.pas` | Очередь перепаковки |
| `src/uEdl.pas` | Сборка `edl://` для предпросмотра |
| `src/uMpvLib.pas` | Объявления libmpv, динамическая загрузка |
| `src/uMpvPlayer.pas` | Плеер: встраивание в окно, воспроизведение, события |
| `src/uTimelineCtrl.pas` | Компонент монтажного стола |
| `src/uTransform.pas` | Как кадр исходника ложится в кадр проекта |
| `src/uPanCropForm.pas` | Окно кадрирования и поворота |
| `src/uProjectInfoForm.pas` | Сведения о проекте и ответ ffprobe |
| `src/uThumbs.pas` | Кадры-миниатюры для списка файлов |
Этап 1: форма звука на клипах Для каждого файла один раз строится список пиков: сто значений в секунду, каждое есть наибольший модуль отсчёта за свои десять миллисекунд. Рисовать по настоящим отсчётам нельзя — в секунде их 48 тысяч, а в клипе на экране сотни пикселей. Разбор идёт в отдельном потоке и запоминается в кэше проекта. Через временный файл, а не по трубе: запуск внешних программ режет вывод на строки, а тут поток байтов, где перевод строки — такой же отсчёт. Shift со стрелками вверх и вниз растягивает форму по высоте. Звук бывает тихим, и угадывать за человека нужное усиление неправильно. Меняется только рисунок, значение сохраняется между запусками. Проверено: файл «4 с тона, 4 с тишины, 4 с тона» даёт форму в первой и последней трети и пустоту посередине. Полная амплитуда даёт 254 из 255. Разбор записи на 892 с занимает 12.7 с — это чтение 6.3 ГБ с диска, быстрее не выйдет. Первоначальный критерий «меньше двух секунд» в плане был занижен и исправлен по замеру. Заодно снят риск четвёртого этапа: mpv принимает edl:// как внешнюю звуковую дорожку и берёт звук именно из неё. Проверено встречными списками — картинке «тишина, потом тон», звуку «тон, потом тишина».
2026-08-19 21:23:24 +03:00
| `src/uWaveform.pas` | Пики звука для формы волны на клипах |
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
| `src/uTheme.pas` | Цвета окна, своя кнопка, своё окно сообщения |
| `src/uLogView.pas` | Журнал: своя отрисовка, свои полосы прокрутки |
| `src/uThemedScroll.pas` | Полоса прокрутки и обвязка для таблиц |
| `src/uDeps.pas` | Что нужно программе, где искать, как ставить |
| `src/uDownload.pas` | Загрузка по сети: WinHTTP под Windows, curl под Linux |
| `src/uDepsForm.pas` | Окно «Зависимости» |
| `src/uMainForm.pas` | Главное окно |
| `src/uProgressForm.pas` | Ход долгой операции |
| `src/uRemuxForm.pas` | Окно перепаковки |
| `src/uMismatchForm.pas` | Предупреждение о несовпадении параметров |
| `src/uTextForm.pas` | Надпись на клипе: текст и оформление |
| `src/uTextMeasure.pas` | Точный замер ширины текста для подбора размера |
| `src/uSubtitles.pas` | Текст поверх картинки: разметка для libass |
| `src/uColorKeyForm.pas` | Выбор цвета, который станет прозрачным |
| `src/uEncodeForm.pas` | Настройки сборки с перекодированием |
| `src/uExportForm.pas` | Куда собирать, в каком контейнере, что с текстом |
| `src/uAboutForm.pas` | О программе: возможности и чем она обязана чужому |
| `src/uArticleView.pas` | Страница текста со ссылками |
| `src/uFfmpegJob.pas` | Один запуск ffmpeg в потоке, с ходом работы и отменой |
| `src/uIconData.pas` | Значки, вшитые в программу |
Нелинейный видеоредактор без перекодирования Куски видео вырезаются и склеиваются копированием потока: сборка ролика занимает время копирования файла, а не время рендера. Композитинга нет принципиально — при перекрытии побеждает клип с верхней дорожки целиком, и картинкой, и звуком. Именно это ограничение и делает сборку копированием возможной. Что внутри: - монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание, схлопывание, громкость на клипе; - предпросмотр прямо с монтажа через libmpv и виртуальный список кусков, без промежуточных файлов; - сборка через ffmpeg с точными резами: перекодируется только огрызок от реза до ближайшего опорного кадра, остальное копируется; - разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее опроса ffprobe; - перепаковка контейнеров, кадрирование и поворот, сведения о проекте; - свои цвета всего окна: системные элементы под Windows цвет не берут, поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами; - окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с официальных источников; - кросс-сборка под Windows и работа под Wine. Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
| `build.sh` | Сборка целями |
| `tests/` | Консольный инструмент проверок |
| `vendor/mpv-headers/` | Заголовки mpv 0.37.0 — эталон для биндингов |
## Чего пока нет
**Раздельные звуковые дорожки.** Звук идёт с того же клипа, что и картинка.
Громкость у клипа своя, но смешивать звук с нескольких дорожек нельзя:
это потребовало бы второго прохода по всему материалу. Модель данных к
разделению готова, само разделение не сделано.
**Осциллограмма звука на клипах.**
**Переходы, эффекты, изменение скорости.** Всё это требует перекодирования и
в замысел не входит.