Картинка собиралась в промежуточный файл, а потом сшивалась со звуком в
конечный. На часовом ролике это 91 ГБ в кэше плюс 91 ГБ рядом и лишний
полный проход копирования — ради того, чтобы приписать звуковую дорожку.
Теперь звук собирается раньше картинки, а склейка сразу кладёт всё вместе
в конечный файл. Тем же путём пошла и сборка с перекодированием.
Промежуточного файла нет вовсе, проход на один меньше.
Куски после удачной сборки удаляются, и в журнале сказано, сколько места
освободилось: на часовой съёмке их набирается больше сотни гигабайт, а
нужны они только чтобы продолжить прерванную сборку. После неудачной
сборки они остаются — с них и продолжают. Настройка keep_parts оставляет
их всегда.
Копирование оформления: Ctrl+Shift+C берёт с выделенного клипа
кадрирование, поворот, увеличение, смещение, прозрачность, ключ,
затухания и громкость, Ctrl+Shift+V применяет их ко всем выделенным. Вид
клипа и текст не переносятся: свойства копируют между разными клипами.
Порядок дорожек: слева в заголовке две стрелки, у крайних дорожек
соответствующая нарисована тускло и не нажимается. Двойной щелчок по
названию переименовывает дорожку — на монтаже из десятка дорожек «карта»
и «титры» читаются лучше, чем V1 и Т1. То же есть и в меню по правому
щелчку. Ввод имени — своим тёмным окном: системный InputQuery выглядел бы
белым пятном.
Окно сборки показывает, сколько прошло и сколько примерно осталось.
Оценка по пройденной доле; скорость на деле меняется, поэтому «примерно»
и пересчёт на каждом шаге.
Прерванная сборка продолжается с готовых кусков. Каждый доведённый до
конца кусок помечается в списке рядом с ними: одного наличия файла мало —
сборку могли прервать посреди записи. Первая строка списка — отпечаток
плана; не совпал, значит монтаж менялся, и всё собирается заново.
Проверено: четыре куска взяты готовыми, два пересобраны, склейка не
потеряла ни одного кадра; при изменённом монтаже готовые куски отвергнуты.
Миникарта была видна в окне, но не в ролике. Предпросмотр рисовал оверлей
поверх всего, не глядя на порядок дорожек, а сборка живёт по правилу
«верхний клип побеждает целиком»: фотография лежала выше оверлея и в
ролике его закрывала. Теперь окно считает так же — оверлей показывается
только там, где его не закрывает сплошной клип с дорожки выше, а закрытый
посередине разбивается на несколько окон показа.
Текст не появлялся в окне. Разметка лежит в файле с одним именем на весь
проект, а плеер сравнивал имена: имя прежнее — перечитывать нечего.
Правка доходила до окна только вместе с перезагрузкой списка кусков.
Теперь сравнивается сама разметка, и при изменении файл перечитывается.
Отдельно проверено, что цепочка lavfi-complex субтитрам не мешает.
Две дорожки Т2. Имя новой дорожке давалось по счёту дорожек своего вида
плюс единица, и после удаления средней счёт совпадал с занятым именем.
Имя подбирается свободное, а повторы из старых проектов разводятся при
чтении.
Проверки 11 и 12 в nlvetool flattest.
Вжигание не доходило до сгенерированных вставок. Картинки и чёрное
отбирались первой же веткой сборки — до проверки на пересборку — и
копировались целиком вместе с отметкой о разметке. Галка вжигания стояла,
кусок в журнале был помечен «с текстом», а в ролике надписи не было.
Причину показал журнал сборки рядом с файлом: помечено «пересобирается»,
а команда — копирование.
Теперь вставка с разметкой идёт общей веткой пересборки, и время у неё
берётся своё: она начинается с нуля, а не с середины монтажа.
У линейки появился верхний этаж в двенадцать точек — про отметки.
Протяжка по нему отмечает участок, даже если отметок не было; щелчок без
движения их снимает. Уголки по краям тянутся, как и раньше.
Заголовок дорожки: правый щелчок открывает меню — поднять, опустить,
удалить (с клипами — с подтверждением). Заголовок можно и потащить:
порядок дорожек есть порядок перекрытия, и менять его перетаскиванием
привычнее, чем через меню.
В живом проекте фотография оказалась на текстовой дорожке, а надпись — на
дорожке картинки. Уплощение обходит дорожки по видам, и такой клип не
видит ни сборка, ни предпросмотр: на монтаже он нарисован, в готовом
ролике его нет, и ни одной жалобы по дороге. Именно это и было причиной
пропавших фотографии и текста, а не ошибка со стоп-кадром.
Теперь перетаскивание на дорожку чужого вида не проходит, а файл из
списка, брошенный на такую дорожку, ложится на подходящую — с записью в
журнал, куда именно. Новая дорожка, заведённая перетаскиванием вниз,
заводится того вида, какой нужен клипу.
При открытии проекта такие клипы пересчитываются, и программа предлагает
перенести их на подходящие дорожки, сохранив время. Молча не переносим:
клип мог оказаться там осознанно, и внезапный переезд — сюрприз хуже
пропажи.
Проверка 10 в nlvetool flattest: картинка на текстовой дорожке в
уплощение не попадает, после переноса попадает и остаётся на своём месте.
Отметки участка стали видимыми уголками на линейке — жёлтые треугольники
по краям, за них можно тянуть. Клавишами это выходило долго: поставил,
посмотрел, промахнулся, ставь заново. Край прилипает к границам клипов,
как и курсор, и края не пропускаются друг за друга.
Клавиша \ и кнопка с целью переключают, держать ли курсор посередине
монтажа при воспроизведении. Раньше монтаж ехал под курсором всегда, а
это мешает, когда разглядываешь конкретное место.
В окне сборки вместо надписи про участок появилась галка «только
отмеченный участок». Надпись легко проглядеть, а вопрос из тех, где
ошибка стоит часа работы: соберёшь кусок вместо ролика или наоборот.
Снятая галка собирает весь монтаж, отметки при этом остаются.
Журнал сборки пишется рядом с готовым файлом и под тем же именем. Общий
журнал программы перезаписывается следующим запуском, а разбираться,
почему в ролике не то, приходится потом. В журнале: время, проект,
участок, кадр и битрейт, что с текстом, и полный список кусков — с
пометками, где слои, где текст и что пересобирается. Строка пишется
сразу, чтобы после падения осталась последняя.
Отмеченный участок. Клавиши I и O ставят начало и конец по курсору,
полоса под линейкой показывает отмеченное, сборка собирает только его.
Без этого проверить правку на длинном монтаже нельзя: каждая проверка
стоит столько же, сколько идёт весь ролик. Отметки живут в проекте.
Разметка текста при этом берётся с начала участка, иначе надписи легли бы
со сдвигом.
Фотография пропадала из ролика. Кусок с наложением подаёт слои в ffmpeg
как есть, а одиночная картинка даёт ровно один кадр: кусок в три секунды
получался длиной 0.016667 с. Теперь у слоя есть признак «это картинка», и
она повторяется ключом -loop с частотой кадров проекта.
Текст пропадал там же. Вжигание помечает кусок разметкой, но ветка
наложения про неё не знала. Пока оверлей лежит на всём ролике, этой
веткой идут все куски — и текста не было нигде. Теперь разметка вжигается
последним звеном цепочки, поверх сведённой картинки.
Качество пересобранных кусков. Битрейт не задавался вовсе, и кодировщик
брал своё значение по умолчанию: 5.9 Мбит/с там, где исходник 56. Теперь
пересборка целится в наибольший битрейт среди файлов проекта, с потолком
в полтора раза выше и буфером вдвое. Тем же битрейтом делаются вставки из
фотографий.
Проверки: обрезка списка кусков по участку (nlvetool flattest, 9),
сборка фотографии под наложением и вжигание текста поверх наложения
(nlvetool render ... burn).
После разреза хвост оставался без цветового ключа: перенос настроек был
написан перечислением полей, а ключ появился позже и в перечень не попал.
Хвост переставал быть наложением, становился обычным победителем и
закрывал собой всё, что ниже — вместо компаса в углу окно показывало
пурпурную плашку во весь кадр.
Перечисление заменено на CopySettingsFrom: одно место, где сказано, что
переносится при копировании клипа, а что делает клип этим клипом. Иначе
та же ловушка досталась бы следующему свойству.
Проверка в lfmcheck: рез клипа с ключом, прозрачностью, громкостью и
кадрированием — всё остаётся у обеих половин, связки у них разные и
непустые.
Проекты, разрезанные прежней сборкой, правка задним числом не чинит:
у хвоста ключ надо включить заново.
Оверлей показывался как есть, со своим фоном: чтобы увидеть, что выйдет
после сборки, приходилось собирать. Теперь выбитый цвет и приглушение
считаются в той же цепочке плеера, на лету.
Строки фильтров стали общими со сборкой (ColorKeyFilter, OpacityFilter
в uTransform): окно и готовый ролик больше не могут разойтись по тому,
как записан цвет или порог.
Переключатель в меню «Вид», состояние переживает перезапуск. Замер на
2688x1512, 600 кадров: основа 2.07 с, с наложением 2.43 с, с ключом и
прозрачностью 2.66 с — вчетверо быстрее реального времени, но на слабой
машине это заметнее, поэтому выключить можно.
В бэклог записано окно настроек: переключателей, которые стоят машинного
времени, набралось много, и живут они кто где — кнопками внизу, в меню,
а выбор кодировщика вообще нигде.
Курсор воспроизведения по щелчку вставал ровно туда, куда попала мышь.
В стык двух кусков так не попасть: на общем плане одна точка экрана —
это секунды материала, а ставят курсор чаще всего именно на стык.
Теперь он притягивается к границам клипов на любой дорожке и к началу
монтажа. Работает и при щелчке по клипу, и по линейке, и по пустому
месту, и при протяжке. Shift меняет решение на противоположное — как у
прилипания клипов.
Переключатель свой, со значком клея, рядом с магнитом: клипы липнут при
перетаскивании, курсор — при щелчке, это разные привычки, и одно бывает
нужно без другого. Состояние переживает перезапуск.
Расчёт открыт наружу и проверяется числами: семь проверок в lfmcheck,
от «стык рядом — притянуло» до «выключено — не притягивает».
Внутри проект — обычный JSON, и система считала его просто текстом:
открывала редактором и рисовала значок текстового файла.
Теперь у него свой тип application/x-nlve-project, свой значок (лист с
загнутым углом и знаком программы) и открытие двойным щелчком. Тип
объявлен наследником application/json, но не простого текста — иначе
редактор снова оказался бы программой по умолчанию.
Всё, что нужно для встраивания в рабочий стол, лежит в packaging и
ставится скриптом tools/install-desktop.sh в домашний каталог, без прав
администратора. Раньше пункт меню и значки существовали только на одной
машине и нигде не были записаны.
Клипу задаётся цвет, который при сборке становится прозрачным. Окно
ключа показывает кадр: цвет берётся щелчком по нему, выбитое показано
клеткой, допуск и смягчение края — ползунками. Открывается из окна
кадрирования и из меню «Правка», на монтаже клип помечен квадратиком
выбиваемого цвета.
Предпросмотр перестал сводить такие участки в файлы. Плеер играет две
дорожки одна поверх другой: оверлей во весь ролик больше не заставляет
пересобирать весь фильм после каждой правки. Замерено: наложение стоит
0.4 с на 10 с материала 2688x1512, вчетверо быстрее реального времени.
Починено в сборке: уменьшенный клип обкладывался чёрными полями, и они
закрывали собой нижнее видео — оставалась видна только дырка, выбитая
ключом. Теперь верхний слой кладётся по координатам, тем же, по которым
его показывает предпросмотр.
Смещение по горизонтали и вертикали работает и там, где картинка меньше
кадра проекта: раньше уменьшенный клип нельзя было сдвинуть в угол.
Проверки форм теперь создают каждое окно из файла формы: элемент,
забытый в классе, вскрывается на сборке, а не при открытии окна.
Схлопывание при удалении. При включённом автосхлопывании удаление куска
сдвигает всё, что правее, влево до первого касания — дыра не остаётся, и
сборке не приходится заполнять её чернотой.
Сдвиг считается по всем дорожкам сразу, а не по каждой отдельно: иначе
развалилось бы выверенное соответствие картинки и звука. Насколько можно
сдвинуть, решает та дорожка, где запас меньше всего. Дыра не обязана быть
пустой везде: клип, который её перекрывает, никуда не едет, и запас на
его дорожке считается от его конца, поэтому сдвиг сам останавливается там,
где упёрся.
Проверено: клип 0..4 с парным звуком разрезан на 2 с, удалён первый
кусок. Хвост уехал в ноль, следующий клип встал вплотную, дыры нет.
Внутренние ошибки. Окно «Access violation» без единого слова о том, где
именно, не даёт ничего, а при воспроизведении оно повторяется на каждый
кадр и его невозможно закрыть. Теперь ошибка перехватывается: в журнал
пишется разбор стека с именами файлов и номерами строк — сборка идёт с
отладочными сведениями. Окно показывается только на первую ошибку,
дальнейшие лишь считаются.
Звуковые дорожки предпросмотра хранятся номерами, а не указателями.
Указатели были ловушкой: отмена и открытие проекта пересоздают все
дорожки, а список продолжал показывать на освобождённую память —
и обращаются к нему на каждое обновление времени, то есть десятки раз в
секунду.
Del удалял весь исходный клип вместо выделенного куска, и звук вместе с
ним. Причина: при резе хвост наследовал номер связки головы, а удаление
расширяет выделение на всю связку — обе половины оказывались одной
связкой и умирали вместе. Дальнейшие резы наследовали тот же номер, так
что от удаления одного куска исчезали все.
Теперь хвост получает новый номер связки. Один на весь рез: все хвосты,
отрезанные от одной связки, остаются связанными между собой — картинка
со своим звуком, как и было задумано.
Проверено на монтаже: клип 0..4 с парным звуком разрезан на 2 с, удалён
первый кусок. Было — исчезал весь клип и весь его звук. Стало — остались
хвост 2..4 со своим звуком в новой связке и нетронутый соседний клип
4..8.
Проекты, разрезанные прежней сборкой, останутся со сцепленными
половинами: у них один номер связки на всё. Развязать их можно Ctrl+G.
Окно сборки теперь спрашивает не только имя файла: куда собирать, в каком
контейнере и что делать с текстом — это один вопрос, ответ на любую часть
меняет допустимое в остальных.
При MP4 вжигание становится обязательным, и галка запирается. Причина
измерена, а не выдумана: отдельной дорожкой субтитров со всем оформлением
умеет только Matroska, в MP4 их кладут как mov_text, а он не хранит ни
цвета, ни обводки, ни положения. Переключение на MKV возвращает прежний
ответ человека — тот, что он давал до того, как MP4 решил за него.
Вжигание. Куски, которые задевает надпись, помечаются к пересборке, и
каждому подкладывается своя разметка: времена сдвинуты к началу куска и
обрезаны по его длине. Иначе никак — фильтр subtitles рисует по времени
того файла, который ему дали, а кусок начинается со своего нуля. Текст
дорисовывается последним, уже по кадру проекта: раньше он попал бы под
масштабирование и уехал бы вместе с картинкой.
Отдельная дорожка. Разметка вкладывается в готовый файл кодеком ass при
сшивании, ничего не перекодируется.
Предупреждение о пересборке теперь считается после подготовки текста:
раньше оно показывалось до неё и не знало о вжигаемых участках.
Проверено сборкой двух роликов из одного монтажа с надписью на 2..6
секунде. С вжиганием: кадры на 1 и 7 секундах чистые, на 3 и 5 — с
надписью, предупреждение назвало 8 секунд из 15.5. Отдельной дорожкой: в
файле три потока, третий ass с названием «Надписи», картинка чистая.
Своя ошибка, найденная по дороге: установка галки кодом вызывает тот же
обработчик, что и щелчок, и навязанное при MP4 значение записывалось как
осознанный выбор — при возврате на MKV галка оставалась стоять.
Окно хода сборки переименовано в «Ход сборки»: два окна с заголовком
«Сборка ролика» путали и меня, и человека.
ffmpeg умеет рисовать текст, но не тем фильтром, о котором думают.
drawtext в местной сборке отсутствует: с версии 7.1 ему нужен harfbuzz.
Зато есть libass — и он для задачи лучше. Проверено отрисовкой: цвет,
обводка, тень, шрифт по имени, размер, поля и девять положений. Девять
положений — родное понятие ASS, ровно та сетка, которую просили.
Чего libass не делает — не уменьшает шрифт, чтобы текст влез. Это считаем
мы: сами переносим по словам и сами подбираем размер, а libass получает
готовые переводы строк, чтобы нарисовать ровно то, что мы померили.
Размер ищется делением отрезка пополам. Прикидка «уменьшить во столько же
раз, во сколько не влезли» давала слишком мелко: после переноса строк
становится меньше, и места остаётся вдоволь — на длинной надписи вместо
29 помещался 37. Замер готового блока: ровно 144 точки при разрешённых
144, то есть пятая часть кадра 720p.
Текст живёт на своей дорожке, как звук: правило «верхний клип побеждает»
на него не распространяется, он лежит поверх и ничего не закрывает.
Предпросмотр ничего не стоит. mpv рисует внешний файл ASS поверх нашего
виртуального списка кусков — правка текста видна сразу, без пересборки
участка, в отличие от прозрачности. Подключение сделано в двух местах:
при смене файла и при загрузке нового списка кусков, потому что тогда mpv
забывает все внешние дорожки.
Редактор: текст, шрифт из установленных в системе, размер, три цвета
образцами, толщина обводки и тени, сетка три на три, доля высоты. Рядом
сказано, во что превратится надпись, — считает тот же код, что и сборка
разметки.
Замер ширины подключается снаружи: точный требует холста, а холст —
оконного набора. Служебная программа без экрана обходится оценкой, окно
ставит точный. Из-за этого же из модели убран TColor: она не должна
тянуть за собой оконную библиотеку.
Сборки ролика с текстом пока нет — это следующий этап, он расписан в
IMPLEMENTATION_PLAN.md.
Ссылка в ~/.local/bin и ярлык в меню запускают программу не по её
настоящему пути. ExtractFilePath(ParamStr(0)) при этом указывал на
каталог ссылки, и рядом с ней оказались бы и журнал, и файл
зависимостей, и поиск ffmpeg «рядом с программой».
Появился ProgramDir в uAppTypes: он раскрывает ссылки, пока они не
кончатся, с ограничением на случай закольцованных. Под Windows ссылок
такого рода нет, и путь берётся как есть.
Заменены все четыре места, где каталог программы вычислялся вручную:
журнал, поиск инструментов, каталог зависимостей и путь заготовки в
проекте.
Проверено: запуск через ~/.local/bin/nlve из домашнего каталога кладёт
журнал в каталог программы, а не рядом со ссылкой.
./build.sh macos делает две сборки: nlve_intel для Intel и nlve_arm для
Apple Silicon. Имя цели в проекте одно на все системы, и сборка под
darwin затирает линуксовую — поэтому каждый файл переименовывается сразу,
а сборка под эту систему в конце делается заново.
Обёртку .app, которую делает lazbuild, удаляем: внутри неё ссылка на файл
с именем nlve, которого после переименования уже нет.
BACKLOG.md — то, что известно и сознательно не сделано. Про macOS там
записано главное: собирается, но предпросмотра не будет, пока встраивание
mpv не научится работать через NSView; окно зависимостей советует маку
apt вместо brew; проверить нечем, потому что мака нет. Заодно туда
переехали лицензионный вопрос с libmpv и три мелочи: значок exe, кэш
наложений и четыре секунды на открытие окна кадрирования.
Падение. Окно зависимостей роняло программу под Wine: «stack overflow»,
шестнадцать мегабайт стека съедались целиком. Причина — качели между
таблицей и своими полосами прокрутки: появление вертикальной полосы
сужает таблицу, от этого перестаёт помещаться содержимое по ширине,
появляется горизонтальная, от неё убавляется высота — и решение о
вертикальной принимается заново. В комментарии стояло обещание, что
второго круга не будет; под Wine круг не сходился никогда.
Теперь глубина пересчёта ограничена тремя кругами. Лишняя полоса
прокрутки не беда, падение — беда.
Искалось на невидимом экране в отдельном префиксе Wine: программа сама
писала след по шагам, и он показал, что окно создаётся целиком, а падает
на показе. Дальше отключением по одному проверены обработчики
перерисовки рамки, подновление полосы меню по таймеру и значок окна —
ни при чём.
Напоминание. Если не хватает ffmpeg или libmpv, при запуске появляется
окно: чего именно нет, и три кнопки — открыть окно зависимостей,
отложить, больше не напоминать. Последнее запоминается в настройках.
Для этого появился ThemedChoiceDlg: диалог со своими кнопками, готовых
«Да / Нет / Отмена» тут не хватало.
Высота надписи в обоих диалогах считалась по общей ширине текста и не
видела переводов строк — сообщение обрезалось снизу. Теперь каждая
строка считается отдельно.
Текст переносится сам, по ширине окна, и переливается заново при
изменении размера. Считать переносы заранее нельзя: текст когда-нибудь
поедет в переводы, а там длина строки другая.
Появился uArticleView — страница текста со ссылками. Журнал для этого не
годился: он показывает сообщения программы, где перенос сбил бы
выравнивание, и строк не переносит принципиально.
Разметка нарочно бедная: «# » — заголовок, пустая строка — отступ, два
пробела — отступ списка, http:// и https:// — ссылка. Больше ничего не
нужно: это окно «О программе», а не браузер.
Ссылки нажимаются: под указателем меняются цвет и курсор, по нажатию
открывается браузер. Ссылка никогда не переносится — из двух половин её
не собрать.
Вкладки названы «Ресурсы» и «Возможности».
Тексты переписаны короче: строка — одна мысль, без рассуждений.
Значки заменены на присланные. Они вшиты в программу модулем uIconData,
который собирается из icons_online командой tools/make_icons.py — правятся
исходные картинки, а не он. Размер выбран под кнопку: двадцать четыре
точки в клетке двадцать восемь, уменьшение делает convert, пропорции
сохранены — все исходники квадратные.
Цвет значку задаётся при отрисовке: берётся прозрачность точки, а цвет —
тот, каким сейчас рисуется кнопка. Поэтому один значок годится и для
тёмной кнопки, и для оранжевой, и для недоступной. Перекрашенные
запоминаются: цветов ровно три.
Значок программы стал значком окна. Значок самого файла под Windows
задаётся не отсюда, а ресурсом при сборке — это осталось как было.
Окно «О программе» (Справка → О программе). Две вкладки: «Библиотеки и
авторство» и «Что умеет». Первая обязательна — бесплатная лицензия
Icons8 требует ссылку на каждый значок; заодно там перечислено всё, на
чём программа держится, и под какой лицензией. Списки разные по
системам: под Linux ffmpeg и libmpv ставит пакетный менеджер, под
Windows их скачивает сама программа, и это разные сборки.
Вкладки свои, а не TPageControl: системные вкладки под Windows остались
бы светлой полосой поверх тёмного окна.
Текст показывается нашим журналом — его можно выделить и скопировать,
так что ссылку из списка получится взять в браузер. Перенос по словам
свой: ссылки не рвутся, отступ списка сохраняется на продолжениях.
Свои рисованные значки из icons/ убраны: они больше не используются, и
держать рядом два набора значило бы путать.
Главное. Каждый щелчок по нашей кнопке срабатывал дважды. Click вызывает
сама LCL при отпускании внутри кнопки — TControl.WMLButtonUp, с той же
проверкой «внутри», какую я написал руками в MouseUp. Мой вызов
оказывался вторым. На кнопке-переключателе это значит «ничего не
произошло»: первое срабатывание меняло состояние, второе возвращало
обратно. Свой вызов убран.
Найдено на невидимом экране: на один щелчок мышью в журнал легли две
записи об одном обработчике, тогда как то же действие с клавиатуры давало
одну. После правки три щелчка дают три переключения, а магнит послушно
меняет цвет туда и обратно.
Второе. Журнал, спрятанный до первого показа окна, не получал своего
виджета вовсе, и вернуть его было нечем: свойство переключалось, а на
экране ничего не менялось. Теперь состояние применяется на показе окна,
когда дескриптор уже есть.
Третье. Правка файла формы не сработала молча — строка не совпала, а я
это не проверил. Кнопка прилипания осталась широкой, с надписью, и
наехала на соседние. Исправлено. Заодно выяснилось, что файл формы
пересобирается только вместе со своим модулем: правки одного лишь lfm в
двоичный файл не попадают.
Значок прилипания переделан в подкову магнита: линия со стрелками смысла
не передавала.
Переключение журнала добавлено в меню «Вид» и на Ctrl+L.
Двойной щелчок по кнопке журнала. Причина оказалась не в кнопке: журнал
лежал ниже полосы состояния. Спрятали журнал — полоса уехала вниз на сто
тридцать пикселей, кнопка ушла из-под курсора, и следующий щелчок в то же
место попадал уже мимо. Порядок исправлен: полоса состояния теперь самая
нижняя, журнал над ней, и при переключении ничего не движется.
Прилипание, схлопывание и журнал стали квадратными кнопками со значками и
собраны в один набор. Надписи переехали в подсказки — целиком, вместе с
состоянием: у значка нет другого способа рассказать о себе. Подсказка
всплывает через десятую долю секунды и держится пятнадцать.
Значки рисуются кодом, а не берутся из файла: программа не должна
зависеть от того, лежит ли рядом с ней картинка. Рисунки придуманы в
icons/*.svg — оттуда их можно перегнать в png чем угодно, проверено
командой convert.
Свойства из файлов форм теперь проверяются отдельной программой по
сведениям о типах. Файл формы разбирается при запуске, а не при сборке:
опечатка в имени свойства или в значении перечисления даёт не ошибку
компиляции, а падение окна на старте. Проверка включена в build.sh.
Полоска выполнения теперь показывает всё, что делается в стороне от
окна: сборку наложений и затуханий, разбор звука, миниатюры. Раньше она
знала только про звук и миниатюры, а самое долгое — пересборку участков
с прозрачностью — не показывала вовсе.
Как она растёт. Настоящая доля известна только вехами: вставка готова,
файл разобран. Между вехами не происходит ничего видимого, и полоска,
стоящая на месте, выглядит как зависшая программа. Поэтому между вехами
она ползёт сама — к следующей вехе, но никогда её не достигая: каждый
шаг покрывает малую долю оставшегося расстояния. Работа идёт как
задумано — веха приходит быстро и полоска прыгает к ней; затянулась —
шаги мельчают, и заполнение само собой замедляется. Назад не идёт
никогда, полной без настоящего конца не становится.
Расчёт вынесен в uAppTypes отдельной функцией именно затем, чтобы его
можно было прогнать числами, и прогоняется: проверки в
tests/progress/progtest.lpr включены в build.sh. Замер: до половины пути
десять шагов таймера, до трёх четвертей — ещё десять.
Участок монтажа, который сейчас пересобирается, помечается косой
штриховкой во всю высоту дорожек. Сплошная заливка закрыла бы клипы, а
рамка на узком участке была бы неразличима; штрих наклонный, чтобы его
не спутать с курсором и границами клипов — те все вертикальные.
Кнопка «Журнал» прячет и показывает нижнюю полосу с сообщениями,
состояние переживает перезапуск.
Системный TStatusBar не взят намеренно: его рисует система, и под
Windows он остался бы светлой полосой — ровно та беда, из-за которой уже
переписаны кнопки, полосы прокрутки, журнал и меню. Всё сделано на своей
нижней панели, теми же средствами.
Плеер играет список кусков, а список накладывать не умеет: там побеждает
верхний клип целиком. Поэтому участки с прозрачностью и переходами
заранее сводятся в отдельные файлы кэша, и предпросмотр играет уже их.
Файл считается один раз, имя выводится из описания слоёв — при следующем
открытии ждать нечего.
Считает их тот же код, что и сборка ролика. Разными путями считать одно
и то же нельзя: предпросмотр разошёлся бы с результатом.
Для сборки подстановка не применяется: там наложение считается заново с
параметрами вывода, и промежуточный файл был бы лишней потерей качества.
Проверено на монтаже с клипом в 50% прозрачности, где оба слоя из одного
файла: кадр наложения ближе к полусумме двух слоёв (отклонение 13.8), чем
к любому из них по отдельности (26.0 и 18.4).
Попутно найдено и исправлено: наложения кодировались процессорным x265,
потому что ffmpeg выбирает кодировщик по имени кодека, а мы передавали
«hevc». Замерено: 9.6 с материала за 11.2 с работы. Теперь и здесь
берётся предпочтительный кодировщик, аппаратный.
В служебную программу добавлена команда preview: список кусков ровно
такой, каким его видит предпросмотр, вместе с готовыми наложениями.
Затухания. Механика была написана давно и работала в сборке, но увидеть
или задать её было нечем. Теперь у клипа два верхних угла — ручки:
левая тянет появление, правая уход. Взятое затухание рисуется срезанным
углом, так это устроено в монтажных программах. Ручки перехватывают
нажатие раньше растягивания за край: они мелкие, промахнуться мимо них
легче, а взяться за край можно ниже.
Перекрытие двух клипов одной дорожки — это переход, и он теперь помечен
косым крестом во всю высоту. Раньше о нём можно было узнать только по
предупреждению перед сборкой.
Сборка с перекодированием. Отдельный пункт меню и отдельное окно:
размер кадра от 720p до 4K, поток в килобитах, кодек HEVC или H.264.
Перекодирование идёт одним проходом на весь ролик — пересобирать куски
по отдельности нельзя, на стыках была бы видна смена качества.
Кодирование на видеокарте, и распаковка тоже: замерено на паре кусков
2688x1512 — 9.18 с на процессоре против 1.77 с с аппаратной распаковкой,
впятеро. Полностью карточный путь через scale_cuda оказался медленнее
(5.74 с) и не взят. Если аппаратная распаковка не пойдёт, сборка
повторяется без неё: медленно лучше, чем никак. Окно честно
предупреждает, когда аппаратного кодировщика в ffmpeg нет вовсе.
Журнал в окне сборки заменён на свой: у системного поля оставались
светлые полосы прокрутки и не работала прокрутка вбок с Shift. Это было
последнее такое место.
До этого места не дотянуться ни отрисовкой пунктов (это не пункт), ни
подложкой через SetMenuInfo: под Wine пункты выходят тёмными, а полоса
остаётся светлой. Полоса живёт в неклиентской части окна, поэтому
закрашиваем прямо туда: правый край последнего названия берётся из
GetMenuItemRect, границы полосы из GetMenuBarInfo, всё что правее —
заливается нашей кистью.
Закраска подновляется на перерисовке рамки окна и по таймеру: система
трогает полосу и по другим поводам, а рисуем мы поверх неё.
Прямоугольник и цвет те же, поэтому подновление не видно.
Заодно закраска сама отчитывается в журнал: какие прямоугольники выдала
система и куда мы рисовали. Полоса упорно остаётся светлой, а гадать по
снимкам экрана дальше некуда.
Журнал закрытия показал место точно: последняя запись «Закрытие: закрываю
плеер», дальше ничего. Значит, зависание внутри mpv_terminate_destroy —
она не просто отпускает клиента, а ждёт, пока ядро mpv свернёт работу и
добьёт свои потоки. Под Linux это мгновенно, под Wine не кончалось вовсе,
причём даже когда ничего не открывали.
Теперь плеер отсоединяется через mpv_destroy: она отпускает клиента и
возвращает управление сразу, а ядро без клиентов сворачивается само.
Команда «завершиться» перед этим намеренно не посылается — её тоже
выполняет ядро, и встать она могла бы точно так же. Если в библиотеке
mpv_destroy не окажется, остаётся прежний путь с ожиданием, и в журнале
будет видно, что пошли по нему.
Отсюда следствие: libmpv больше не выгружается при выходе. Потоки ядра в
этот момент могут ещё жить внутри неё, и выдёргивать её из-под них
нельзя. Программа всё равно закрывается, память освободит система.
Плеер получил свою строчку в журнале — теперь видно и отсоединение, и
завершение, а не только шаги окна.
Звук пропадал кусками, и причина оказалась не в громкости и не в
раскладке дорожек: обе были верны. Дыры на звуковой дорожке заполнялись
тем же чёрным клипом, что и дыры на видеодорожке, а опорные кадры в нём
стоят через 4.171 с. Перемотка внутри такой вставки откатывается к
ближайшему опорному кадру, дорожка начинает отдавать звук на несколько
секунд раньше соседних, смешивание равняется по ней — и содержимое
соседней дорожки в это окно не попадает.
Замерено на файле, без запуска окна. Смешивание трёх дорожек проекта:
старт 0 с: -23.3 dB старт 24 с: -91.0 dB (полная тишина)
старт 12 с: -10.1 dB старт 30 с: -18.0 dB
При этом та же дорожка A2 отдельно на 24-й секунде даёт -11.7 dB, то
есть содержимое на месте. Смешивание A2 с A4 работает, A1 с A2 — нет:
у A1 в этой точке идёт длинная чёрная вставка, и перемотка в ней
откатывается на 3.1 с назад.
Теперь у звуковых дорожек своя вставка — anullsrc в звуковом кодеке
проекта, без видеопотока. В звуке опорным является каждый кадр, и
отката нет. После правки во всех проверенных точках звук есть:
0 с -24.1 7 с -19.0 12 с -11.9 18 с -17.5 24 с -11.7
25 с -9.6 30 с -9.5 40 с -15.6 60 с -19.5 72 с -15.9
Сборка ролика от этого не страдает: она декодирует куски в отсчёты, и
файл без картинки ей подходит так же.
Заодно в служебную программу добавлена команда audio: раскладка
звуковых дорожек проекта на куски вместе со строками edl://. Ею это и
было найдено.
Подложка. SetMenuInfo работает по дескриптору меню, а тот появляется
вместе с дескриптором окна — то есть после создания формы. Оба прежних
вызова попадали на несозданный дескриптор и молча ничего не делали.
Теперь вызов идёт из показа окна.
Громкости. В журнал пишется, какая дорожка какую громкость получила и
какая цепочка смешивания ушла в mpv. Снимками экрана это не разобрать:
в одном месте одного и того же клипа звук есть, в соседнем нет, и без
цепочки видно только следствие.
Закрытие. Каждый шаг отмечается в журнале: остановка предпросмотра,
ожидание потоков миниатюр и разбора звука, сохранение настроек,
закрытие плеера, выгрузка libmpv. Под Wine программа зависает при
выходе, а по молчаливому журналу шаг не найти.
Цвет подложки задаётся кистью через SetMenuInfo по дескриптору меню, а
дескриптор появляется только когда меню отдано окну. Вызов стоял до
присваивания, условие HandleAllocated не выполнялось, и подложка не
задавалась ни разу: пункты рисовались нашими руками, а полоса правее
последнего названия оставалась светлой.
Теперь вызов идёт дважды и оба раза нужны: до присваивания — расставить
обработчики отрисовки к первому показу, после — задать подложку. Полоса
меню живёт в неклиентской части окна и сама себя не перерисовывает,
поэтому следом идёт DrawMenuBar.
Самодельная полоса из кнопок стоила поведения: наведение на соседнее
название не перекидывало туда раскрытый список, не работал переход по
меню с Alt и стрелками. Подражать настоящему меню своими руками пришлось
бы до бесконечности, а причина отказа была одна — цвет.
Цвет решается иначе. Под gtk2 своими руками меню не рисуется, и это
записано в самом LCL: OwnerDraw, OnDrawItem и OnMeasureItem там помечены
как неподдерживаемые (interfaces/gtk2/issues.xml), вызывает DoDrawItem
только win32. Зато gtk принимает описание стиля текстом —
gtk_rc_parse_string, разбирается один раз до создания окон. Под win32
наоборот: строк стиля нет, зато отрисовка пунктов работает, в том числе
для названий в самой полосе.
Проверено отдельным пробником (probe/menu): настоящее TMainMenu под gtk2
берёт наши цвета и в полосе, и в раскрытом списке, включая цвет
выделенного пункта.
Отрисовка пунктов теперь отличает полосу от раскрытого списка: в полосе
не нужно место под галочку и сочетание клавиш, иначе название разошлось
бы с шириной, которую мы же и посчитали.
Остаток полосы правее последнего названия под Windows по-прежнему рисует
система — это уступка, а не решение.
Общая громкость плеера выставлялась по громкости единственной звуковой
дорожки и обратно уже не возвращалась. Клип с выключенным звуком —
обычное дело, и стоило монтажу на миг остаться с одной такой дорожкой,
как плеер немел до конца работы: на всём монтаже, сколько бы дорожек
потом ни появилось. Теперь в ветке смешивания громкость всегда полная —
громкость каждой дорожки задана внутри цепочки. Случай «дорожек нет
вовсе» тоже снимает и цепочку, и приглушение.
Замер: одна дорожка с громкостью 0 даёт точный ноль, после возврата
дорожек звук возвращается на -11.4 dB. Раньше он не возвращался.
Список меню открывается через таймер, а не прямо из обработчика щелчка.
Пока обработчик не завершился, указатель захвачен кнопкой, и раскрытому
списку захват не достаётся. Внешне это выглядело как «меню открывается
через раз»: замерено 175 мс, не открылось, 175 мс, не открылось. Показ
по нажатию, как в настоящих полосах меню, под gtk2 не открывает список
ни разу. Теперь восемь щелчков подряд — восемь открытий по 168 мс, в том
числе во время фонового разбора звука.
Рамка фокуса больше не рисуется на кнопках, которые фокус не берут:
на пунктах меню она выглядела как испорченный пункт.
Фоновая работа видна в нижней полосе: надпись с числом файлов и своя
полоска выполнения. Разбор звука читает файл целиком, на шести
гигабайтах это больше десяти секунд, и молчать об этом нельзя.
Вид клипа «звук» не умел записываться: в файле проекта он превращался в
«видео». После открытия такой клип терял форму звука, получал ненужную
кнопку кадрирования, а ползунок начинал править прозрачность вместо
громкости. Найдено прямо в файле проекта пользователя.
Уже записанные проекты чинятся при чтении: клип на звуковой дорожке
обязан быть звуковым.
Вторая правка: списки кусков звуковых дорожек ссылаются на вставки
тишины, но о недостающих никто не сообщал, и они не создавались. Список
тогда указывает на несуществующий файл, плеер его не открывает, и
дорожка молчит целиком. Теперь недостающие собираются вместе с
остальными.
Третья: громкости дорожек и сами дорожки собирались разными проходами и
могли разъехаться — тогда дорожка с нулевой громкостью заглушила бы
чужую. Теперь громкости берутся ровно по тому списку, из которого
построены списки кусков.
Проверено на проекте пользователя: виды клипов после чтения верные,
список звуковой дорожки A2 играет сам по себе (-16 dB), и вся связка с
смешиванием тоже (-8.8 dB).
Поворот и отражение применялись к исходному кадру, а обрезка шла после
них. Из-за этого рамка показывала одно место, а вырезалось другое.
Теперь наоборот: сначала выбирается кусок, потом он поворачивается и
отражается. Если после поворота кусок лёг поперёк кадра проекта, он
вписывается целиком с полями.
Проверено: с отражением левый и правый края вырезанного куска меняются
местами, то есть отражается именно он, а не оригинал.
Вторая причина расхождения — предел ползунков смещения. Рамку можно было
утащить туда, куда ползунок не дотягивается: рамка рисовалась от
неограниченного значения, а фильтр считался от обрезанного. Предел
расширен вчетверо, а рамка теперь пересчитывается обратно из настройки —
если та упёрлась, это сразу видно.
Отдельно: разбор звука больше не держит главный поток. Перезапуск ждал
окончания текущего файла, а это чтение гигабайтов — окно всё это время не
отвечало, и меню не открывалось вовсе. Теперь проход доигрывает сам и
запускается заново, а идёт он с наинизшим приоритетом.
Чёрное окно и полная тишина оказались следствием того, что цепочка
смешивания звуковых дорожек оставалась от прошлой загрузки. Она
ссылается на дорожки по номерам, а в новом файле таких номеров нет.
Проверено на живом mpv через канал управления: с оставшейся цепочкой
следующая загрузка даёт ноль дорожек, размер кадра недоступен, не играет
ничего. После снятия — кадр 320, звук выбран.
Снимать после загрузки поздно: ломается сама загрузка. Теперь снимается
перед ней.
Проверена вся последовательность: две звуковые дорожки, затем одна, затем
без внешнего звука. Кадр на месте во всех трёх случаях.
Правило «верхний побеждает» я перенёс на звук механически, и это была
ошибка: оно придумано для картинки, где и делает возможной сборку
копированием. Для звука оно бессмысленно — под голос нельзя было положить
фоновую музыку. И стоит смешивание ничего: звук всё равно перекодируется.
В сборке куски со звуком нескольких дорожек сводятся тем же путём, что
переходы: слоями, каждый со своей громкостью.
В предпросмотре сложнее: плеер играет одну звуковую дорожку за раз.
Поэтому каждая дорожка монтажа подаётся своим списком кусков, а смесь
собирается цепочкой фильтров плеера. Громкость клипов подставляется в ту
же цепочку — иначе предпросмотр врал бы про баланс.
Проверено: два тона на разных дорожках, второй с громкостью 50%. В
готовом ролике -21.2 и -27.1 dB, в предпросмотре -21.2 и -27.2. Совпало.
Найдена по дороге ошибка в собственном условии: я считал слои без
победителя и потому обнулял список, когда звучали ровно две дорожки.
Найдена настоящая причина обеих жалоб на звук. Свойство audio-files,
заданное перед загрузкой, не подключает внешний список кусков вовсе:
проверено на живом mpv через его канал управления — после установки
свойства дорожек по-прежнему две, обе не внешние. Играл звук, встроенный
в саму картинку.
Отсюда обе стороны: звук был слышен там, где на монтаже его нет (шёл из
видеофайла), и не был слышен там, где есть (в этом месте картинкой была
чёрная вставка, а она молчит).
Правильный способ — команда audio-add с признаком select после загрузки.
Проверено: дорожек становится четыре, внешняя звуковая выбрана,
внутренняя снята с выбора.
Если подключить не удалось, в журнал пишется предупреждение. Глушить звук
на этот случай нельзя — прошлая попытка так сделать оставила без звука
весь монтаж.
Вчерашняя правка глушила звук, когда перебор дорожек не находил внешнюю.
Задумывалось как защита: играть звук из картинки вместо отдельной дорожки
монтажа нельзя. Вышло наоборот — звука не стало нигде.
Перебор может не сработать по причинам, до которых из программы не
дознаться, а mpv и сам предпочитает внешний список, когда тот открылся.
Поэтому выбор оставлен ему, а в журнал пишется, что произошло: если звук
окажется не тот, будет с чего начинать.
Проверено на точном списке кусков, который строит предпросмотр: картинка
2..8 с, звук сдвинут на 4..10 с. Воспроизведение дало тишину до четвёртой
секунды и тон после — ровно то, что на монтаже.
Первая: при выключенных дорожках картинки не играло вообще ничего. Поток
картинки опустевал, уплощение возвращало неудачу, и предпросмотр
останавливался вместе со звуком, которого выключение не касалось.
Теперь длина берётся по обоим видам дорожек сразу, и недостающее
заполняется чёрным и тишиной. Оба потока всегда одной длины.
Вторая: звук был слышен там, где на монтаже его нет. mpv добавляет
внешний список кусков ещё одной дорожкой, а какую играть, решает сам, и
мог взять звук, встроенный в саму картинку.
Теперь после загрузки внешняя дорожка выбирается явно. Если её не
оказалось, звук выключается совсем: играть вместо него звук из картинки
нельзя — на монтаже его там нет.
Проверено: при выключенных дорожках картинки получается чёрный кадр и
сохранившийся звук (-21 dB); внешний список кусков выбирается верно и в
ту, и в другую сторону.
Ползунок на клипе картинки теперь задаёт непрозрачность, на звуковом —
громкость. Уплощение выдаёт для таких мест не одного победителя, а слои.
Слои появляются в трёх случаях: клип полупрозрачен и сквозь него виден
нижний; два клипа на одной дорожке наползли друг на друга и должны
перетечь один в другой; у клипа задано плавное появление или уход.
Перекрытие на разных дорожках переходом не считается — там по-прежнему
побеждает верхний. Иначе в проекте, где дорожки перекрываются постоянно,
каждое такое место пришлось бы пересобирать.
Картинка собирается фильтрами ffmpeg: постоянная доля через overlay,
переход через xfade, затухание через fade. Звук сводится прямо в
отсчётах — доля каждого слоя считается на каждый отсчёт, и длина
результата получается ровно такой, какая нужна.
Перед сборкой программа сообщает, сколько секунд из скольких придётся
пересобрать, и даёт отказаться.
Проверено замерами:
- красный с непрозрачностью 50% поверх синего дал (127, 0, 126);
- переход на одной дорожке: синий плавно перешёл в красный за секунду;
- звуковой переход: -24 dB, -27.5, -35, -51, тишина;
- появление за 2 секунды: пересобрано 2 секунды из 6, остальное
скопировано.
Найдено по дороге: участок с наложением склеивался с соседним куском и
терял слои — в условии склейки они не учитывались.
Ctrl+G связывает выделенные клипы и развязывает их, если они уже в одной
связке. Связка двигается, режется и удаляется целиком: половина связки —
это разъехавшийся звук, который потом не найти.
Форма звука переехала на звуковые клипы: на клипе картинки ей не место,
звук у него теперь свой и живёт на своей дорожке. Кнопка кадрирования, наоборот,
осталась только у картинки.
Новая дорожка картинки встаёт перед первой звуковой, а не в конец списка:
порядок дорожек задаёт, кто побеждает при перекрытии.
Найдена и исправлена настоящая ошибка: при резе новый кусок терял
громкость и кадрирование — они просто не переносились на хвост.
У дорожки появился вид — картинка или звук, у клипа принадлежность к
связке. Проект заводит две дорожки картинки и две звука; при добавлении
файла со звуком клип кладётся парой, связанной общим номером.
Уплощение теперь делается двумя проходами: отдельно для картинки,
отдельно для звука. Одним проходом такое не выразить — в один и тот же
момент картинка берётся с одной дорожки, а звук с другой.
Сборка соответственно тоже раздваивается. Картинка собирается прежним
способом, копированием. Звук собирается в сырой поток и сжимается одним
проходом: куски дописываются друг за другом байтами, и длина каждого
считается из длительности, а не берётся у ffmpeg. Поэтому стык получается
с точностью до отсчёта, а не до звукового кадра, и звук не уезжает от
картинки. Тишина в дырах пишется нулями, без запуска ffmpeg вовсе.
Готовые потоки сшиваются копированием.
Предпросмотр получает второй список кусков как внешнюю звуковую дорожку.
Проекты, записанные раньше, переносятся при чтении: звук там шёл вместе с
клипом картинки и без пары просто пропал бы.
Проверено сборкой:
- картинка из молчащего синего файла 0..6 с, звук из другого файла с 2 по
6 с. В результате тишина первые две секунды и тон дальше — звук встал
туда, куда положен, а не туда, где картинка;
- обычный парный случай: клип с 2 по 6 с, тишина до него и тон в нём;
- проверки уплощения пройдены.
Настройка кадрирования переехала с файла на клип: два куска одного файла
бывает нужно кадрировать по-разному. У файла осталась такая же, но лишь
как заготовка — её берёт новый клип при добавлении. Проекты, записанные
раньше, читаются: клип без своей настройки берёт её у файла.
Открывается кнопкой в правом нижнем видимом углу клипа. Именно видимом:
у клипа длиной в час настоящий угол почти всегда за краем экрана — та же
задача, что уже решена для ручки громкости.
В окне слева появился кадр источника с рамкой, которую тянут мышью за
середину и за правый нижний угол. Рамка держит пропорции кадра проекта и
пересчитывается в увеличение и смещение; ползунки и рамка следуют друг за
другом.
Кусок пересобирается теперь не только когда файл не совпадает с проектом,
но и когда у клипа своё кадрирование.
Проверено сборкой: первый кусок без кадрирования скопирован, второй с
увеличением вдвое пересобран через scale=1280:720,crop=640:360:384:180.
154 пакета вошло, 154 вышло, декодирование без ошибок.
Заодно: правый щелчок по списку файлов теперь выделяет файл под курсором.
Без этого меню без выделения отключалось целиком и выглядело так, будто
его нет. Цвет формы звука сменён на пастельно-жёлтый.
Для каждого файла один раз строится список пиков: сто значений в секунду,
каждое есть наибольший модуль отсчёта за свои десять миллисекунд. Рисовать
по настоящим отсчётам нельзя — в секунде их 48 тысяч, а в клипе на экране
сотни пикселей.
Разбор идёт в отдельном потоке и запоминается в кэше проекта. Через
временный файл, а не по трубе: запуск внешних программ режет вывод на
строки, а тут поток байтов, где перевод строки — такой же отсчёт.
Shift со стрелками вверх и вниз растягивает форму по высоте. Звук бывает
тихим, и угадывать за человека нужное усиление неправильно. Меняется
только рисунок, значение сохраняется между запусками.
Проверено: файл «4 с тона, 4 с тишины, 4 с тона» даёт форму в первой и
последней трети и пустоту посередине. Полная амплитуда даёт 254 из 255.
Разбор записи на 892 с занимает 12.7 с — это чтение 6.3 ГБ с диска,
быстрее не выйдет. Первоначальный критерий «меньше двух секунд» в плане
был занижен и исправлен по замеру.
Заодно снят риск четвёртого этапа: mpv принимает edl:// как внешнюю
звуковую дорожку и берёт звук именно из неё. Проверено встречными
списками — картинке «тишина, потом тон», звуку «тон, потом тишина».
Записаны четыре решения, от которых зависит остальное: звук при
раздельном аудио всегда перекодируется, кроссфейд возникает сам только в
пределах одной дорожки, при перекрытии разных дорожек верхний клип
побеждает целиком, кадрирование переезжает на клип с заготовкой у файла.
Пять этапов с критериями готовности и проверками. Порядок третьего и
четвёртого жёсткий: связки без раздельных дорожек бессмысленны, а
раздельное аудио без связок неуправляемо.
Полосы прокрутки рисует система, и цвет им задать нечем. Единственный
способ получить их в своих цветах — не пользоваться системными вовсе.
Таблица при этом остаётся обычной: у неё выключаются свои полосы, а
прокрутка задаётся свойствами TopRow и LeftCol.
По дороге найдены и исправлены три ошибки, две из них — от раскраски окон:
- контекстное меню библиотеки перестало открываться: раскраска
принудительно создавала системный дескриптор всплывающего меню
задолго до показа. Теперь дескриптор только читается;
- окно кадрирования падало при открытии: смена режима отрисовки
выпадающего списка вызывает его обработчик изменения, а тот обращался
к таймеру, который создавался ниже по коду;
- полосы не появлялись вовсе: размер содержимого таблицы брался из
GridWidth и GridHeight, а с выключенными полосами те возвращают не
полный размер. Теперь считается сложением ширин столбцов и высот строк.
Многострочные поля в сведениях о проекте и в перепаковке заменены своим
журналом: у них оставались системные полосы. Заодно ответ ffprobe стало
можно выделить и скопировать.
Удалён неиспользуемый uScrollAid: горизонтальная прокрутка колесом теперь
внутри журнала и одинакова на обеих системах. Список юнитов в файле
проекта приведён в соответствие с тем, что лежит на диске.
Куски видео вырезаются и склеиваются копированием потока: сборка ролика
занимает время копирования файла, а не время рендера. Композитинга нет
принципиально — при перекрытии побеждает клип с верхней дорожки целиком,
и картинкой, и звуком. Именно это ограничение и делает сборку копированием
возможной.
Что внутри:
- монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание,
схлопывание, громкость на клипе;
- предпросмотр прямо с монтажа через libmpv и виртуальный список кусков,
без промежуточных файлов;
- сборка через ffmpeg с точными резами: перекодируется только огрызок от
реза до ближайшего опорного кадра, остальное копируется;
- разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее
опроса ffprobe;
- перепаковка контейнеров, кадрирование и поворот, сведения о проекте;
- свои цвета всего окна: системные элементы под Windows цвет не берут,
поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами;
- окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с
официальных источников;
- кросс-сборка под Windows и работа под Wine.
Первый коммит: до этого история не велась.