Нелинейный видеоредактор без перекодирования
Куски видео вырезаются и склеиваются копированием потока: сборка ролика
занимает время копирования файла, а не время рендера. Композитинга нет
принципиально — при перекрытии побеждает клип с верхней дорожки целиком,
и картинкой, и звуком. Именно это ограничение и делает сборку копированием
возможной.
Что внутри:
- монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание,
схлопывание, громкость на клипе;
- предпросмотр прямо с монтажа через 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` не
|
|
|
|
|
|
является именем файла и молча пропускалось.
|
|
|
|
|
|
|
2026-08-31 00:13:50 +03:00
|
|
|
|
### Встраивание в рабочий стол
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
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 |
|
|
|
|
|
|
| Разрезать по курсору | S |
|
|
|
|
|
|
| Разрезать точно (перекодирует стык) | Shift+S |
|
|
|
|
|
|
| Удалить выделенное | Delete |
|
|
|
|
|
|
| Выделить всё | Ctrl+A |
|
|
|
|
|
|
| Схлопнуть дыру под курсором | Ctrl+Shift+R |
|
|
|
|
|
|
| Отменить, повторить | Ctrl+Z, Ctrl+Y |
|
|
|
|
|
|
| Масштаб таймлайна | Ctrl + колесо мыши |
|
|
|
|
|
|
| Прокрутка по дорожкам | колесо мыши |
|
|
|
|
|
|
| Прокрутка по времени | 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 |
|
Этап 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 |
|
|
|
|
|
|
|
|
|
|
|
|
Файлы попадают в проект через меню «Файл» или перетаскиванием прямо в окно
|
|
|
|
|
|
программы. Место броска решает, что произойдёт: бросили над монтажом — файл
|
|
|
|
|
|
и добавится в проект, и ляжет на дорожку под указателем; в любом другом
|
|
|
|
|
|
месте — только добавится в проект.
|
|
|
|
|
|
|
|
|
|
|
|
Файл из библиотеки кладётся на монтаж перетаскиванием либо через
|
|
|
|
|
|
контекстное меню списка. В нём: **добавить на дорожку** (подменю со
|
|
|
|
|
|
списком дорожек), **кадрирование и поворот**, **убрать со всех дорожек**
|
|
|
|
|
|
(файл остаётся в проекте), **удалить из проекта**. Дыры при этом намеренно не
|
|
|
|
|
|
схлопываются: убрать клип и переставить всё остальное — два разных
|
|
|
|
|
|
намерения.
|
|
|
|
|
|
|
|
|
|
|
|
Если бросить клип ниже всех дорожек, заведётся новая.
|
|
|
|
|
|
|
|
|
|
|
|
В заголовке каждой дорожки слева — её название, число клипов и две кнопки:
|
|
|
|
|
|
**М** выключает дорожку целиком (она не идёт ни в предпросмотр, ни в сборку),
|
|
|
|
|
|
**С** оставляет только её, заглушив остальные. Щелчок по заголовку делает
|
|
|
|
|
|
дорожку выбранной — на неё кладёт файл кнопка.
|
|
|
|
|
|
|
|
|
|
|
|
Клип переносится перетаскиванием, в том числе на другую дорожку и на
|
|
|
|
|
|
пустое место под последней — тогда дорожка тоже добавится. За края клипа
|
|
|
|
|
|
он растягивается и укорачивается. Картинку можно растянуть на любую
|
|
|
|
|
|
длину, видео — только в пределах исходника.
|
|
|
|
|
|
|
|
|
|
|
|
Щелчок по клипу переставляет курсор воспроизведения. Именно щелчок:
|
|
|
|
|
|
если кнопку нажали и потянули, курсор остаётся на месте, в том числе во
|
|
|
|
|
|
время воспроизведения.
|
|
|
|
|
|
|
Курсор прилипает к границам клипов
Курсор воспроизведения по щелчку вставал ровно туда, куда попала мышь.
В стык двух кусков так не попасть: на общем плане одна точка экрана —
это секунды материала, а ставят курсор чаще всего именно на стык.
Теперь он притягивается к границам клипов на любой дорожке и к началу
монтажа. Работает и при щелчке по клипу, и по линейке, и по пустому
месту, и при протяжке. Shift меняет решение на противоположное — как у
прилипания клипов.
Переключатель свой, со значком клея, рядом с магнитом: клипы липнут при
перетаскивании, курсор — при щелчке, это разные привычки, и одно бывает
нужно без другого. Состояние переживает перезапуск.
Расчёт открыт наружу и проверяется числами: семь проверок в lfmcheck,
от «стык рядом — притянуло» до «выключено — не притягивает».
2026-08-31 00:32:44 +03:00
|
|
|
|
**Прилипание клипов** (кнопка с магнитом). Края клипов притягиваются
|
|
|
|
|
|
друг к другу и к курсору. Shift во время перетаскивания меняет решение на
|
|
|
|
|
|
противоположное: при включённом прилипании временно отключает, при
|
|
|
|
|
|
выключенном — временно включает. Так одна клавиша полезна в обоих
|
|
|
|
|
|
режимах, и не нужно помнить, какой сейчас.
|
|
|
|
|
|
|
|
|
|
|
|
**Прилипание курсора** (кнопка с клеем). Курсор, поставленный щелчком,
|
|
|
|
|
|
притягивается к границам клипов на любой дорожке. Без этого в стык не
|
|
|
|
|
|
попасть: на общем плане одна точка экрана — это секунды материала, а
|
|
|
|
|
|
ставят курсор чаще всего именно на стык. Shift при щелчке работает так
|
|
|
|
|
|
же — меняет решение на противоположное.
|
|
|
|
|
|
|
|
|
|
|
|
Переключателя два, а не один, потому что это разные привычки: клипы
|
|
|
|
|
|
липнут при перетаскивании, курсор — при щелчке, и одно бывает нужно без
|
|
|
|
|
|
другого.
|
Нелинейный видеоредактор без перекодирования
Куски видео вырезаются и склеиваются копированием потока: сборка ролика
занимает время копирования файла, а не время рендера. Композитинга нет
принципиально — при перекрытии побеждает клип с верхней дорожки целиком,
и картинкой, и звуком. Именно это ограничение и делает сборку копированием
возможной.
Что внутри:
- монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание,
схлопывание, громкость на клипе;
- предпросмотр прямо с монтажа через libmpv и виртуальный список кусков,
без промежуточных файлов;
- сборка через ffmpeg с точными резами: перекодируется только огрызок от
реза до ближайшего опорного кадра, остальное копируется;
- разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее
опроса ffprobe;
- перепаковка контейнеров, кадрирование и поворот, сведения о проекте;
- свои цвета всего окна: системные элементы под Windows цвет не берут,
поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами;
- окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с
официальных источников;
- кросс-сборка под Windows и работа под Wine.
Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00
|
|
|
|
|
2026-09-11 20:44:32 +03:00
|
|
|
|
**Курсор посередине** (кнопка с целью, клавиша `\`). Включено — при
|
|
|
|
|
|
воспроизведении монтаж едет под курсором, и всегда видно, что будет
|
|
|
|
|
|
дальше. Выключено — окно не двигается вовсе, даже когда курсор ушёл за
|
|
|
|
|
|
край: отмотал Shift с колесом — там и останешься. Раньше в выключенном
|
|
|
|
|
|
состоянии окно всё равно подтягивалось к курсору, стоило тому выйти из
|
|
|
|
|
|
видимой части, и прокрутка отскакивала назад со второго поворота колеса.
|
|
|
|
|
|
|
Ключ и прозрачность видно прямо в предпросмотре
Оверлей показывался как есть, со своим фоном: чтобы увидеть, что выйдет
после сборки, приходилось собирать. Теперь выбитый цвет и приглушение
считаются в той же цепочке плеера, на лету.
Строки фильтров стали общими со сборкой (ColorKeyFilter, OpacityFilter
в uTransform): окно и готовый ролик больше не могут разойтись по тому,
как записан цвет или порог.
Переключатель в меню «Вид», состояние переживает перезапуск. Замер на
2688x1512, 600 кадров: основа 2.07 с, с наложением 2.43 с, с ключом и
прозрачностью 2.66 с — вчетверо быстрее реального времени, но на слабой
машине это заметнее, поэтому выключить можно.
В бэклог записано окно настроек: переключателей, которые стоят машинного
времени, набралось много, и живут они кто где — кнопками внизу, в меню,
а выбор кодировщика вообще нигде.
2026-08-31 00:46:58 +03:00
|
|
|
|
**Ключ и прозрачность в предпросмотре** (меню «Вид»). Включено — окно
|
|
|
|
|
|
показывает то же, что даст сборка: выбитый цвет и приглушение считаются
|
|
|
|
|
|
на лету. Выключено — оверлей виден целиком, со своим фоном: так понятно,
|
|
|
|
|
|
куда он встал и где его края.
|
|
|
|
|
|
|
|
|
|
|
|
Считается это по площади самого оверлея, а не всего кадра. Замер на
|
|
|
|
|
|
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+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, который схлопывает дыру под курсором. То есть выключенная
|
|
|
|
|
|
кнопка означает лишь «не двигать ничего само собой при перетаскивании».
|
|
|
|
|
|
|
|
|
|
|
|
Во время воспроизведения курсор держится посередине окна, а монтаж едет
|
|
|
|
|
|
под ним. При нажатии на пуск, если курсор оказался за краем видимой
|
|
|
|
|
|
части, вид подтягивается к нему — масштаб при этом не меняется.
|
|
|
|
|
|
|
|
|
|
|
|
На каждом клипе проходит линия громкости с ручкой посередине видимой его
|
|
|
|
|
|
части: тянешь ручку вверх и вниз — меняется громкость, от тишины до
|
|
|
|
|
|
двойной. Ручка стоит посередине именно видимой части, а не всего клипа:
|
|
|
|
|
|
до середины часового клипа, уехавшей за край экрана, мышью не дотянуться.
|
|
|
|
|
|
|
|
|
|
|
|
Громкость, отличная от обычной, — единственное место, где звук
|
|
|
|
|
|
перекодируется: изменить громкость, не тронув сам звук, нельзя. Видео при
|
|
|
|
|
|
этом по-прежнему копируется. Проверено: клип с громкостью 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 со стрелками вверх и вниз растягивает форму по высоте.** Звук
|
|
|
|
|
|
бывает тихим, и угадывать за человека, во сколько раз растянуть картинку,
|
|
|
|
|
|
неправильно. Усиление меняет только рисунок, самого звука не касается, и
|
|
|
|
|
|
запоминается между запусками.
|
|
|
|
|
|
|
Нелинейный видеоредактор без перекодирования
Куски видео вырезаются и склеиваются копированием потока: сборка ролика
занимает время копирования файла, а не время рендера. Композитинга нет
принципиально — при перекрытии побеждает клип с верхней дорожки целиком,
и картинкой, и звуком. Именно это ограничение и делает сборку копированием
возможной.
Что внутри:
- монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание,
схлопывание, громкость на клипе;
- предпросмотр прямо с монтажа через 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 одна на всю склейку, а заполнение с обрезкой,
|
|
|
|
|
|
поворот и смещение задаются для каждого файла отдельно — выбрать между ними
|
|
|
|
|
|
общий фильтр не может, поэтому он всегда вписывает целиком. Точную картинку
|
|
|
|
|
|
для такого файла показывает окно «Кадрирование и поворот».
|
|
|
|
|
|
|
|
|
|
|
|
## Цвета окна
|
|
|
|
|
|
|
|
|
|
|
|
Все цвета заданы в `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
|
|
|
|
## Наложение, переходы и прозрачность
|
|
|
|
|
|
|
|
|
|
|
|
Ползунок на клипе картинки задаёт **непрозрачность**, на звуковом —
|
|
|
|
|
|
громкость. Клип с непрозрачностью меньше единицы смешивается с тем, что
|
|
|
|
|
|
под ним.
|
|
|
|
|
|
|
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
|
|
|
|
Два клипа **на одной дорожке**, наползшие друг на друга, дают переход:
|
|
|
|
|
|
один плавно перетекает в другой. На разных дорожках перекрытие означает
|
|
|
|
|
|
не переход, а наложение — там по-прежнему побеждает верхний, если он
|
|
|
|
|
|
непрозрачен.
|
|
|
|
|
|
|
|
|
|
|
|
Различие не косметическое. Если бы кроссфейдом считалось любое
|
|
|
|
|
|
перекрытие, то в проекте, где клипы на разных дорожках перекрываются
|
|
|
|
|
|
постоянно, каждое такое место пришлось бы пересобирать.
|
|
|
|
|
|
|
|
|
|
|
|
У клипа есть плавное появление и уход. Для картинки это затемнение, для
|
|
|
|
|
|
звука — нарастание громкости.
|
|
|
|
|
|
|
|
|
|
|
|
**Всё это стоит перекодирования** — но только своего участка. Перед
|
|
|
|
|
|
сборкой программа сообщает, сколько секунд из скольких придётся
|
|
|
|
|
|
пересобрать, и даёт отказаться.
|
|
|
|
|
|
|
|
|
|
|
|
Звук при наложении сводится прямо в отсчётах, без фильтров 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 Мбит/с —
|
|
|
|
|
|
пересобранный кусок бросался в глаза среди скопированных соседей.
|
|
|
|
|
|
|
|
|
|
|
|
Кроме среднего задаётся потолок в полтора раза выше и буфер вдвое: без
|
|
|
|
|
|
них кодировщик разгоняется на сложных кадрах ровно настолько, насколько
|
|
|
|
|
|
позволит средний, и мелкие детали всё равно теряются.
|
|
|
|
|
|
|
|
|
|
|
|
Берётся именно наибольший, а не средний: пересобранный кусок стоит рядом
|
|
|
|
|
|
со скопированными, и если он окажется хуже их, разница видна именно на
|
|
|
|
|
|
нём. Лишнее место дешевле.
|
|
|
|
|
|
|
2026-08-31 17:39:40 +03:00
|
|
|
|
## Место на диске
|
|
|
|
|
|
|
|
|
|
|
|
Куски весят примерно столько же, сколько готовый ролик, а на длинном
|
|
|
|
|
|
материале и больше: на часовой съёмке набралось 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
|
|
|
|
## Прерванная сборка
|
|
|
|
|
|
|
|
|
|
|
|
Каждый доведённый до конца кусок помечается в списке рядом с самими
|
|
|
|
|
|
кусками (`готово.txt` в кэше). Если сборка не дошла до конца — упала,
|
|
|
|
|
|
была отменена или программу закрыли, — при следующей сборке в тот же файл
|
|
|
|
|
|
программа предложит продолжить с готовых кусков.
|
|
|
|
|
|
|
|
|
|
|
|
Проверяется не только наличие файла: сборку могли прервать посреди
|
|
|
|
|
|
записи, и файл оказался бы обрезанным. Поэтому куском считается только
|
|
|
|
|
|
тот, который успел попасть в список.
|
|
|
|
|
|
|
|
|
|
|
|
Первая строка списка — отпечаток плана: пути, места и длины всех кусков.
|
|
|
|
|
|
Не совпал — значит монтаж с тех пор менялся, и готовые куски не подойдут;
|
|
|
|
|
|
программа скажет об этом и соберёт заново.
|
|
|
|
|
|
|
|
|
|
|
|
## Сколько осталось
|
|
|
|
|
|
|
|
|
|
|
|
Окно сборки показывает, сколько прошло и сколько примерно осталось.
|
|
|
|
|
|
Оценка простая: если четверть пути заняла минуту, весь путь займёт
|
|
|
|
|
|
четыре. На деле скорость меняется — копируемые куски идут мгновенно,
|
|
|
|
|
|
пересобираемые медленно, — поэтому число называется «примерно» и
|
|
|
|
|
|
пересчитывается на каждом шаге. Даже такая оценка отвечает на главный
|
|
|
|
|
|
вопрос: успею ли я сходить за чаем.
|
|
|
|
|
|
|
Нелинейный видеоредактор без перекодирования
Куски видео вырезаются и склеиваются копированием потока: сборка ролика
занимает время копирования файла, а не время рендера. Композитинга нет
принципиально — при перекрытии побеждает клип с верхней дорожки целиком,
и картинкой, и звуком. Именно это ограничение и делает сборку копированием
возможной.
Что внутри:
- монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание,
схлопывание, громкость на клипе;
- предпросмотр прямо с монтажа через 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 мин и так далее), а нужный кусок из них вырезается —
|
|
|
|
|
|
тогда любое движение клипа не требует новой генерации.
|
|
|
|
|
|
3. Начало каждого куска притягивается к опорному кадру, потому что копировать
|
|
|
|
|
|
можно только с него. Конец притягивать не нужно — при копировании он и так
|
|
|
|
|
|
получается кадроточным.
|
|
|
|
|
|
4. При точном резе огрызок от точки реза до следующего опорного кадра
|
|
|
|
|
|
перекодируется, остальное копируется.
|
|
|
|
|
|
5. Куски склеиваются concat-демультиплексором, результат сверяется: сумма
|
|
|
|
|
|
пакетов кусков обязана совпасть с числом пакетов в готовом файле.
|
|
|
|
|
|
|
|
|
|
|
|
Напрашивающийся способ — один проход с директивами `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 ошибок. Способ выбирается
|
|
|
|
|
|
автоматически: один источник и никаких точных резов — прямая склейка,
|
|
|
|
|
|
иначе MPEG-TS.
|
|
|
|
|
|
|
|
|
|
|
|
**Лишний кадр на стыке точного реза.** При копировании `-to` работает как
|
|
|
|
|
|
«строго меньше», при перекодировании — как «меньше либо равно». Кадр на
|
|
|
|
|
|
границе попадал в ролик дважды: 601 вместо 600. Лечится отступом на
|
|
|
|
|
|
полкадра от границы.
|
|
|
|
|
|
|
|
|
|
|
|
**B-кадры во вставках.** Двунаправленные кадры от NVENC при переносе в
|
|
|
|
|
|
Matroska давали метки декодирования, идущие назад, и двадцать совпадающих
|
|
|
|
|
|
меток на шестьсот кадров. Коротким вставкам B-кадры не нужны, поэтому они
|
|
|
|
|
|
запрещены (`-bf 0`).
|
|
|
|
|
|
|
|
|
|
|
|
**Вставка под каждую длину дыры.** Сначала под каждую дыру делался чёрный
|
|
|
|
|
|
клип ровно её длины. Любое движение клипа меняло длину дыры, требовало
|
|
|
|
|
|
новой генерации, и до её готовности предпросмотр играл прежнюю расстановку.
|
|
|
|
|
|
Выглядело так, будто выключение дорожки или удаление клипа не действуют.
|
|
|
|
|
|
Лечится заготовками готовых длин, из которых вырезается нужный кусок.
|
|
|
|
|
|
|
|
|
|
|
|
**Первая загрузка начинала воспроизведение.** У незагруженного 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` после загрузки библиотеки.
|
|
|
|
|
|
|
2026-08-20 13:17:27 +03:00
|
|
|
|
**Цепочку смешивания надо снимать до загрузки.** Она ссылается на дорожки
|
|
|
|
|
|
по номерам, и оставшаяся от прошлого раза не даёт открыть новый файл
|
|
|
|
|
|
вовсе: проверено на живом 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. Записанный по привычке код даёт цвет с переставленными
|
|
|
|
|
|
красным и синим.
|
|
|
|
|
|
|
|
|
|
|
|
**Имена, занятые предками.** Локальная переменная перекрывает одноимённое
|
|
|
|
|
|
свойство класса: `Text` и `Position` заняты у `TControl`, `Finished` — у
|
|
|
|
|
|
`TThread`, `Name` и `Caption` — у формы. Компилятор ловит это как
|
|
|
|
|
|
«Duplicate identifier», но причину не объясняет.
|
|
|
|
|
|
|
|
|
|
|
|
## Консольный инструмент проверок
|
|
|
|
|
|
|
|
|
|
|
|
Часть вещей проверяется числами, а не глазами. Сборка: `./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 <проект> <выход>
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Команды возвращают ненулевой код при расхождении, поэтому годятся для
|
|
|
|
|
|
автоматической проверки.
|
|
|
|
|
|
|
|
|
|
|
|
## Структура
|
|
|
|
|
|
|
|
|
|
|
|
| Файл | Что это |
|
|
|
|
|
|
|---|---|
|
|
|
|
|
|
| `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` | Предупреждение о несовпадении параметров |
|
|
|
|
|
|
| `build.sh` | Сборка целями |
|
|
|
|
|
|
| `tests/` | Консольный инструмент проверок |
|
|
|
|
|
|
| `vendor/mpv-headers/` | Заголовки mpv 0.37.0 — эталон для биндингов |
|
|
|
|
|
|
|
|
|
|
|
|
## Чего пока нет
|
|
|
|
|
|
|
|
|
|
|
|
**Раздельные звуковые дорожки.** Звук идёт с того же клипа, что и картинка.
|
|
|
|
|
|
Громкость у клипа своя, но смешивать звук с нескольких дорожек нельзя:
|
|
|
|
|
|
это потребовало бы второго прохода по всему материалу. Модель данных к
|
|
|
|
|
|
разделению готова, само разделение не сделано.
|
|
|
|
|
|
|
|
|
|
|
|
**Осциллограмма звука на клипах.**
|
|
|
|
|
|
|
|
|
|
|
|
**Переходы, эффекты, изменение скорости.** Всё это требует перекодирования и
|
|
|
|
|
|
в замысел не входит.
|