A Non-Linear Video Editor that actually works on your GPU. Features minimal re-encoding, Vegas-like controls, editing 4k without hiccups and basic compositing with partial transparency (cross)fade in/outs included, and color-keying.
Find a file
chaiman cf20a0164e Две ошибки раздельного звука
Первая: при выключенных дорожках картинки не играло вообще ничего. Поток
картинки опустевал, уплощение возвращало неудачу, и предпросмотр
останавливался вместе со звуком, которого выключение не касалось.

Теперь длина берётся по обоим видам дорожек сразу, и недостающее
заполняется чёрным и тишиной. Оба потока всегда одной длины.

Вторая: звук был слышен там, где на монтаже его нет. mpv добавляет
внешний список кусков ещё одной дорожкой, а какую играть, решает сам, и
мог взять звук, встроенный в саму картинку.

Теперь после загрузки внешняя дорожка выбирается явно. Если её не
оказалось, звук выключается совсем: играть вместо него звук из картинки
нельзя — на монтаже его там нет.

Проверено: при выключенных дорожках картинки получается чёрный кадр и
сохранившийся звук (-21 dB); внешний список кусков выбирается верно и в
ту, и в другую сторону.
2026-08-20 11:00:41 +03:00
src Две ошибки раздельного звука 2026-08-20 11:00:41 +03:00
tests Две ошибки раздельного звука 2026-08-20 11:00:41 +03:00
vendor/mpv-headers Нелинейный видеоредактор без перекодирования 2026-08-19 18:36:16 +03:00
.gitignore Нелинейный видеоредактор без перекодирования 2026-08-19 18:36:16 +03:00
build.sh Нелинейный видеоредактор без перекодирования 2026-08-19 18:36:16 +03:00
nlve.lpi Нелинейный видеоредактор без перекодирования 2026-08-19 18:36:16 +03:00
nlve.lpr Нелинейный видеоредактор без перекодирования 2026-08-19 18:36:16 +03:00
README.md Этап 5: прозрачность, переходы и затухания 2026-08-20 10:11:20 +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 не является именем файла и молча пропускалось.

Управление

Действие Клавиши
Пуск и пауза Пробел
В начало / конец монтажа Home / End
Разрезать по курсору S
Разрезать точно (перекодирует стык) Shift+S
Удалить выделенное Delete
Выделить всё Ctrl+A
Схлопнуть дыру под курсором Ctrl+Shift+R
Отменить, повторить Ctrl+Z, Ctrl+Y
Масштаб таймлайна Ctrl + колесо мыши
Прокрутка по дорожкам колесо мыши
Прокрутка по времени Shift + колесо, средняя кнопка
Прокрутка журнала вбок Shift + колесо над журналом
Растянуть форму звука по высоте Shift + стрелки вверх и вниз
Выключить дорожку / оставить только её кнопки М и С в заголовке
Громкость клипа тянуть ручку на клипе вверх-вниз
Автосхлопывание вкл/выкл Ctrl+K
Связать или развязать клипы Ctrl+G
Отменить прилипание на время Shift при перетаскивании
Вписать проект целиком Ctrl+0
Новый, открыть, сохранить проект Ctrl+N, Ctrl+O, Ctrl+S
Добавить файлы Ctrl+I
Собрать ролик Ctrl+E

Файлы попадают в проект через меню «Файл» или перетаскиванием прямо в окно программы. Место броска решает, что произойдёт: бросили над монтажом — файл и добавится в проект, и ляжет на дорожку под указателем; в любом другом месте — только добавится в проект.

Файл из библиотеки кладётся на монтаж перетаскиванием либо через контекстное меню списка. В нём: добавить на дорожку (подменю со списком дорожек), кадрирование и поворот, убрать со всех дорожек (файл остаётся в проекте), удалить из проекта. Дыры при этом намеренно не схлопываются: убрать клип и переставить всё остальное — два разных намерения.

Если бросить клип ниже всех дорожек, заведётся новая.

В заголовке каждой дорожки слева — её название, число клипов и две кнопки: М выключает дорожку целиком (она не идёт ни в предпросмотр, ни в сборку), С оставляет только её, заглушив остальные. Щелчок по заголовку делает дорожку выбранной — на неё кладёт файл кнопка.

Клип переносится перетаскиванием, в том числе на другую дорожку и на пустое место под последней — тогда дорожка тоже добавится. За края клипа он растягивается и укорачивается. Картинку можно растянуть на любую длину, видео — только в пределах исходника.

Щелчок по клипу переставляет курсор воспроизведения. Именно щелчок: если кнопку нажали и потянули, курсор остаётся на месте, в том числе во время воспроизведения.

Прилипание. Края клипов притягиваются друг к другу и к курсору. Кнопка внизу включает и выключает его, а Shift во время перетаскивания меняет решение на противоположное: при включённом прилипании временно отключает, при выключенном — временно включает. Так одна клавиша полезна в обоих режимах, и не нужно помнить, какой сейчас.

Схлопывание. Кнопка внизу показывает и режим, и состояние: утоплена — автосхлопывание включено, и тогда при перетаскивании клипа всё, что начинается не раньше него, едет следом. Стрелка рядом открывает выбор режима: только текущая дорожка или все. Ctrl+K включает и выключает.

Режим действует и при выключенном автосхлопывании — по нему работает Ctrl+Shift+R, который схлопывает дыру под курсором. То есть выключенная кнопка означает лишь «не двигать ничего само собой при перетаскивании».

Во время воспроизведения курсор держится посередине окна, а монтаж едет под ним. При нажатии на пуск, если курсор оказался за краем видимой части, вид подтягивается к нему — масштаб при этом не меняется.

На каждом клипе проходит линия громкости с ручкой посередине видимой его части: тянешь ручку вверх и вниз — меняется громкость, от тишины до двойной. Ручка стоит посередине именно видимой части, а не всего клипа: до середины часового клипа, уехавшей за край экрана, мышью не дотянуться.

Громкость, отличная от обычной, — единственное место, где звук перекодируется: изменить громкость, не тронув сам звук, нельзя. Видео при этом по-прежнему копируется. Проверено: клип с громкостью 30% дал в ролике уровень на 10.5 дБ ниже исходного при ожидаемых 10.46 дБ.

Полоски по краям клипа показывают тип реза: серая — притянут к опорному кадру, голубая — точный, стык будет перекодирован при сборке. Если край подвинуть руками и он не попадёт на опорный кадр, рез сам становится точным: раз человек выбрал конкретный кадр, притягивать его к соседнему неправильно.

В списке файлов слева показывается кадр из каждого файла, его параметры и длительность. Кадры делаются в фоне и запоминаются в кэше проекта.

На клипах видна форма звука. Считается она по заранее разобранным пикам — сто значений в секунду, каждое есть наибольший модуль отсчёта за свои десять миллисекунд. Рисовать по настоящим отсчётам нельзя: в секунде их 48 тысяч, а в клипе на экране сотни пикселей.

Разбор идёт в фоне и запоминается в кэше проекта. Для записи на 892 с это около 13 секунд — столько занимает чтение шести гигабайт с диска, и быстрее не выйдет; при следующем открытии проекта пики берутся готовыми.

Shift со стрелками вверх и вниз растягивает форму по высоте. Звук бывает тихим, и угадывать за человека, во сколько раз растянуть картинку, неправильно. Усиление меняет только рисунок, самого звука не касается, и запоминается между запусками.

Файл не совпадает с проектом

Проект имеет одно разрешение и один набор кодеков на всё — иначе куски не склеить копированием. Файл с другими параметрами можно добавить, но его куски скопировать не выйдет.

Пересобираются только они. Остальной материал по-прежнему копируется без потерь и почти мгновенно. Ролик целиком не перекодируется никогда.

Как именно кадр ложится в кадр проекта, решает окно «Кадрирование и поворот»: вписать целиком с полями, заполнить кадр с обрезкой или растянуть, плюс поворот на 90, отражение, увеличение и смещение.

Слева в окне — кадр источника с рамкой: её тянут мышью за середину и за правый нижний угол, и она держит пропорции кадра проекта. Справа — настоящий результат, пропущенный через ту же цепочку фильтров, что пойдёт в дело.

Настройка принадлежит клипу, а открывается кнопкой в правом нижнем видимом углу самого клипа. Именно видимом: у клипа длиной в час настоящий угол почти всегда за краем экрана. Два куска одного файла можно кадрировать по-разному.

У файла в библиотеке хранится такая же настройка, но она лишь заготовка: её берёт новый клип при добавлении. Правится она из контекстного меню библиотеки.

По умолчанию кадр вписывается целиком. Растяжение надо выбрать руками: раньше оно применялось молча, и вертикальное видео размазывалось по ширине.

«Приведение к параметрам проекта» делает ту же работу один раз при добавлении: файл перекодируется целиком, зато каждая последующая сборка остаётся быстрой.

Что и почему происходит с каждым файлом, видно в окне «Проект → Сведения о проекте»: параметры проекта, параметры каждого файла, выбранное вписывание и вердикт — копируется, пересобирается или из него делается клип. Там же кнопка полного ответа 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 рядом с программой. Нужно ровно для случая, когда программа падает до появления окна: иначе всё, что она о себе рассказала, пропадает вместе с окном.

Наложение, переходы и прозрачность

Ползунок на клипе картинки задаёт непрозрачность, на звуковом — громкость. Клип с непрозрачностью меньше единицы смешивается с тем, что под ним.

Два клипа на одной дорожке, наползшие друг на друга, дают переход: один плавно перетекает в другой. На разных дорожках перекрытие означает не переход, а наложение — там по-прежнему побеждает верхний, если он непрозрачен.

Различие не косметическое. Если бы кроссфейдом считалось любое перекрытие, то в проекте, где клипы на разных дорожках перекрываются постоянно, каждое такое место пришлось бы пересобирать.

У клипа есть плавное появление и уход. Для картинки это затемнение, для звука — нарастание громкости.

Всё это стоит перекодирования — но только своего участка. Перед сборкой программа сообщает, сколько секунд из скольких придётся пересобрать, и даёт отказаться.

Звук при наложении сводится прямо в отсчётах, без фильтров ffmpeg: доля каждого слоя считается на каждый отсчёт, и длина результата получается ровно такой, какая нужна.

Как собирается ролик

  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 после загрузки библиотеки.

Аргументы 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 Кадры-миниатюры для списка файлов
src/uWaveform.pas Пики звука для формы волны на клипах
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 — эталон для биндингов

Чего пока нет

Раздельные звуковые дорожки. Звук идёт с того же клипа, что и картинка. Громкость у клипа своя, но смешивать звук с нескольких дорожек нельзя: это потребовало бы второго прохода по всему материалу. Модель данных к разделению готова, само разделение не сделано.

Осциллограмма звука на клипах.

Переходы, эффекты, изменение скорости. Всё это требует перекодирования и в замысел не входит.