nlve/src/uMismatchForm.pas
chaiman b59cdb1352 Нелинейный видеоредактор без перекодирования
Куски видео вырезаются и склеиваются копированием потока: сборка ролика
занимает время копирования файла, а не время рендера. Композитинга нет
принципиально — при перекрытии побеждает клип с верхней дорожки целиком,
и картинкой, и звуком. Именно это ограничение и делает сборку копированием
возможной.

Что внутри:

- монтажный стол со своей отрисовкой: дорожки, масштаб, прилипание,
  схлопывание, громкость на клипе;
- предпросмотр прямо с монтажа через libmpv и виртуальный список кусков,
  без промежуточных файлов;
- сборка через ffmpeg с точными резами: перекодируется только огрызок от
  реза до ближайшего опорного кадра, остальное копируется;
- разбор заголовка MP4 для поиска опорных кадров — в 500 раз быстрее
  опроса ffprobe;
- перепаковка контейнеров, кадрирование и поворот, сведения о проекте;
- свои цвета всего окна: системные элементы под Windows цвет не берут,
  поэтому кнопки, полоса меню, журнал и полосы прокрутки нарисованы сами;
- окно зависимостей: недостающие ffmpeg, ffprobe и libmpv скачиваются с
  официальных источников;
- кросс-сборка под Windows и работа под Wine.

Первый коммит: до этого история не велась.
2026-08-19 18:36:16 +03:00

166 lines
5.6 KiB
ObjectPascal
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
uMismatchForm — предупреждение о том, что файл не совпадает с проектом.
Показывается в момент, когда файл кладут на монтаж, а не когда его
добавляют в проект: до попадания на монтаж он ни с чем не склеивается,
и повода тревожить пользователя нет.
Три исхода:
«Добавить как есть» — клип помечается, и при сборке ролика его придётся
перекодировать. Быстро сейчас, медленно потом.
«Привести к проекту» — файл один раз перекодируется в параметры проекта,
дальше всё копируется. Медленно сейчас, быстро потом и всегда.
«Отмена» — не добавлять.
}
unit uMismatchForm;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, Forms, Controls, Graphics, StdCtrls, Grids, ExtCtrls,
uMediaProbe, uTheme;
type
TMismatchChoice = (mcCancel, mcAsIs, mcNormalize);
{ TMismatchForm }
TMismatchForm = class(TForm)
btnAsIs: TThemedButton;
btnCancel: TThemedButton;
btnNormalize: TThemedButton;
grid: TStringGrid;
lblHead: TLabel;
lblTail: TLabel;
pnlBottom: TPanel;
pnlTop: TPanel;
procedure btnAsIsClick(Sender: TObject);
procedure btnCancelClick(Sender: TObject);
procedure btnNormalizeClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
FChoice: TMismatchChoice;
public
procedure Fill(const AFileName: string; const AMismatches: TMismatches);
property Choice: TMismatchChoice read FChoice;
end;
{ Показывает диалог и возвращает решение пользователя. }
function AskAboutMismatch(AOwner: TComponent; const AFileName: string;
const AMismatches: TMismatches): TMismatchChoice;
implementation
{$R *.lfm}
const
ColWhat = 0;
ColExpected = 1;
ColActual = 2;
ColEffect = 3;
{ TMismatchForm }
procedure TMismatchForm.FormCreate(Sender: TObject);
begin
{ Цвета задаём сами: системная тема может быть светлой, и тогда
тёмный таймлайн оказался бы в белой рамке. }
ApplyDarkTheme(Self);
FChoice := mcCancel;
grid.ColCount := 4;
grid.RowCount := 1;
grid.FixedRows := 1;
grid.Cells[ColWhat, 0] := 'Параметр';
grid.Cells[ColExpected, 0] := 'В проекте';
grid.Cells[ColActual, 0] := 'В файле';
grid.Cells[ColEffect, 0] := 'Последствие';
grid.ColWidths[ColWhat] := 200;
grid.ColWidths[ColExpected] := 170;
grid.ColWidths[ColActual] := 170;
grid.ColWidths[ColEffect] := 260;
end;
procedure TMismatchForm.Fill(const AFileName: string;
const AMismatches: TMismatches);
var
I: Integer;
Blocking: Boolean;
begin
lblHead.Caption := 'Файл ' + ExtractFileName(AFileName) +
' не совпадает с параметрами проекта.';
grid.RowCount := Length(AMismatches) + 1;
for I := 0 to High(AMismatches) do
begin
grid.Cells[ColWhat, I + 1] := AMismatches[I].What;
grid.Cells[ColExpected, I + 1] := AMismatches[I].Expected;
grid.Cells[ColActual, I + 1] := AMismatches[I].Actual;
if AMismatches[I].Blocking then
grid.Cells[ColEffect, I + 1] := 'склейка без перекодирования невозможна'
else
grid.Cells[ColEffect, I + 1] := 'к сведению';
end;
Blocking := HasBlocking(AMismatches);
if Blocking then
lblTail.Caption :=
'Куски этого файла нельзя будет скопировать — при сборке они будут ' +
'пересобраны. Пересобираются только они: остальной материал ' +
'по-прежнему копируется без потерь и почти мгновенно.' + LineEnding +
'Приведение файла к параметрам проекта делает ту же работу один раз ' +
'сейчас, зато каждая последующая сборка остаётся быстрой.'
else
lblTail.Caption :=
'Эти расхождения склейке не мешают — они приведены для сведения.';
{ Приводить к проекту имеет смысл только тогда, когда есть что приводить. }
btnNormalize.Enabled := Blocking;
if Blocking then
btnAsIs.Caption := 'Добавить как есть (пересобирать его куски при сборке)'
else
btnAsIs.Caption := 'Добавить';
end;
procedure TMismatchForm.btnAsIsClick(Sender: TObject);
begin
FChoice := mcAsIs;
ModalResult := mrOK;
end;
procedure TMismatchForm.btnNormalizeClick(Sender: TObject);
begin
FChoice := mcNormalize;
ModalResult := mrOK;
end;
procedure TMismatchForm.btnCancelClick(Sender: TObject);
begin
FChoice := mcCancel;
ModalResult := mrCancel;
end;
function AskAboutMismatch(AOwner: TComponent; const AFileName: string;
const AMismatches: TMismatches): TMismatchChoice;
var
Form: TMismatchForm;
begin
{ Расхождений нет — спрашивать не о чем. }
if Length(AMismatches) = 0 then
Exit(mcAsIs);
Form := TMismatchForm.Create(AOwner);
try
Form.Fill(AFileName, AMismatches);
Form.ShowModal;
Result := Form.Choice;
finally
Form.Free;
end;
end;
end.