Предпросмотр показывает кадрирование клипа
Клип со своим кадрированием играл в окне во весь кадр: цепочка фильтров у mpv одна на весь список кусков, а включить её на одном куске и выключить на следующем нечем — из фильтров ffmpeg по времени переключается только наложение. Обошлось без цепочки вовсе. У вывода mpv своя обрезка, и делает её видеокарта: курсор зашёл в клип с кадрированием — выставили, вышел — сняли. Ни перестройки цепочки, ни копирования кадров из памяти видеокарты в обычную. Рамку источника считает теперь один код на две стороны: по ней окно «Кадрирование и поворот» рисует рамку, и её же плеер отдаёт как обрезку. Проверка в tests/lfmcheck сверяет рамку с фильтром сборки: увеличение 2.77 даёт 970x546 посередине, и это ровно то, что вырезает crop=2688:1512:2379:1338 после scale=7446:4188. Проверено глазами на невидимом экране, на настоящем материале с дрона: картинка в окне совпала с готовым роликом, на границе с обычным клипом обрезка снимается и возвращается. Сверено со сборкой из прошлого коммита — там на том же месте кадр без кадрирования. Не покрыто и записано в BACKLOG: отражение (свойства для него нет), растяжение и участки с наложением — там кадр уже сведён цепочкой.
This commit is contained in:
parent
2160c33790
commit
235cd00e9a
8 changed files with 329 additions and 70 deletions
|
|
@ -82,6 +82,27 @@ function OverlayFilter(const AT: TMediaTransform;
|
|||
ASrcWidth, ASrcHeight, ADstWidth, ADstHeight: Integer;
|
||||
out AX, AY, AWidth, AHeight: Integer): string;
|
||||
|
||||
{ Какая часть исходника видна в кадре проекта — прямоугольник в точках
|
||||
самого исходника.
|
||||
|
||||
Та же настройка, записанная с другой стороны: вместо «как вписать» —
|
||||
«что останется». Окно кадрирования рисует по ней рамку, а плеер отдаёт
|
||||
её mpv как обрезку на выводе, и оба обязаны считать одинаково — иначе
|
||||
окно обещает одно, а предпросмотр показывает другое.
|
||||
|
||||
False — прямоугольником не описать: при растяжении берётся весь кадр и
|
||||
сжимается неравномерно. }
|
||||
function TransformSourceRect(const AT: TMediaTransform;
|
||||
ASrcWidth, ASrcHeight, ADstWidth, ADstHeight: Integer;
|
||||
out ARect: TRect): Boolean;
|
||||
|
||||
{ Можно ли показать это преобразование обрезкой на выводе плеера.
|
||||
|
||||
Обрезка умеет только выбрать кусок кадра. Поворот у mpv задаётся
|
||||
отдельным свойством и потому разрешён, а отражение — нет: свойства для
|
||||
него нет, и такой клип по-прежнему показывается как есть. }
|
||||
function TransformFitsOutputCrop(const AT: TMediaTransform): Boolean;
|
||||
|
||||
{ Фильтр цветового ключа: этот цвет становится прозрачным.
|
||||
|
||||
Один на всех: и сборка, и предпросмотр должны выбивать одинаково, иначе
|
||||
|
|
@ -153,6 +174,56 @@ begin
|
|||
(ASrcWidth = ADstWidth) and (ASrcHeight = ADstHeight);
|
||||
end;
|
||||
|
||||
function TransformSourceRect(const AT: TMediaTransform;
|
||||
ASrcWidth, ASrcHeight, ADstWidth, ADstHeight: Integer;
|
||||
out ARect: TRect): Boolean;
|
||||
var
|
||||
KFit, KFill, K: Double;
|
||||
SW, SH, CW, CH, CX, CY: Double;
|
||||
begin
|
||||
ARect := Rect(0, 0, ASrcWidth, ASrcHeight);
|
||||
Result := False;
|
||||
if (ASrcWidth <= 0) or (ASrcHeight <= 0) or
|
||||
(ADstWidth <= 0) or (ADstHeight <= 0) then
|
||||
Exit;
|
||||
{ Растяжение рамкой не описывается: берётся весь кадр и жмётся
|
||||
неравномерно. }
|
||||
if AT.Fit = fmStretch then
|
||||
Exit;
|
||||
|
||||
KFit := Min(ADstWidth / ASrcWidth, ADstHeight / ASrcHeight);
|
||||
KFill := Max(ADstWidth / ASrcWidth, ADstHeight / ASrcHeight);
|
||||
if AT.Fit = fmFill then
|
||||
K := KFill
|
||||
else
|
||||
K := KFit;
|
||||
if AT.Zoom > 0 then
|
||||
K := K * AT.Zoom;
|
||||
if K <= 0 then
|
||||
Exit;
|
||||
|
||||
{ Кадр, растянутый до рабочего масштаба, и та его часть, что помещается
|
||||
в кадр проекта. Смещение задаётся в долях кадра проекта и прижимается
|
||||
к краям: выйти за пределы картинки нельзя. }
|
||||
SW := ASrcWidth * K;
|
||||
SH := ASrcHeight * K;
|
||||
CW := Min(SW, ADstWidth);
|
||||
CH := Min(SH, ADstHeight);
|
||||
CX := (SW - CW) / 2 + AT.OffsetX * ADstWidth;
|
||||
CY := (SH - CH) / 2 + AT.OffsetY * ADstHeight;
|
||||
CX := Max(0, Min(SW - CW, CX));
|
||||
CY := Max(0, Min(SH - CH, CY));
|
||||
|
||||
ARect := Rect(Round(CX / K), Round(CY / K),
|
||||
Round((CX + CW) / K), Round((CY + CH) / K));
|
||||
Result := True;
|
||||
end;
|
||||
|
||||
function TransformFitsOutputCrop(const AT: TMediaTransform): Boolean;
|
||||
begin
|
||||
Result := (not AT.FlipH) and (not AT.FlipV) and (AT.Fit <> fmStretch);
|
||||
end;
|
||||
|
||||
{ Дробное число через точку: у ffmpeg разделитель всегда точка, а в
|
||||
русской локали Format вставил бы запятую и развалил бы всю цепочку. }
|
||||
function FilterNum(const AValue: Double): string;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue