Накладываемая картинка показывается в предпросмотре готовым куском
В окне картинки не было вовсе, хотя в ролик она попадала. Плеер получал сам файл, а его декодер на этом webp спотыкался: «webp: invalid TIFF header in Exif data» — накладывать стало нечего. У неподвижного кадра есть и вторая беда: в списке кусков у него нет длины. Теперь наложению отдаётся заготовка — обычное видео нужной длины, сделанное тем же путём, что и остальные вставки, только цепочкой без полей: поля тут вредны, чёрное закрыло бы нижнее видео. Масштаб уже внутри куска, плееру остаётся поставить его на место. Заготовки наложения собираются отдельным списком: уплощение ниже заполняет список недостающего заново и затёрло бы их. Проверено на том самом файле: в окне картинка легла поверх видео, в ролике слева нижнее видео (253, 0, 0), справа картинка. Числовые проверки в flattest: кусок собирается слоями, у верхнего слоя полей нет, предпросмотру отдан кусок из кэша, и он заказан к созданию.
This commit is contained in:
parent
3d3d02368c
commit
766f1ad196
3 changed files with 106 additions and 4 deletions
|
|
@ -730,6 +730,13 @@ Linux потом искала по нему `.dll`.
|
|||
что-то лежит, участок собирается наложением, и чёрным полям взяться
|
||||
неоткуда. Заготовка с полями остаётся для случая, когда класть не на
|
||||
что.
|
||||
|
||||
В предпросмотре накладываемая картинка тоже показывается заготовкой,
|
||||
только без полей. Своих декодеров плееру хватает не на всё: настоящий
|
||||
webp из сети дал «webp: invalid TIFF header in Exif data», и картинка
|
||||
пропадала из окна, хотя в ролик попадала. У неподвижного кадра есть и
|
||||
вторая беда — в списке кусков у него нет длины. Заготовка же обычное
|
||||
видео нужной длины.
|
||||
3. Границы кусков кладутся на кадровую сетку проекта: между кадрами
|
||||
времени нет, а звук набирается с точностью до отсчёта, и доли кадра
|
||||
разводят их в стороны.
|
||||
|
|
|
|||
|
|
@ -228,6 +228,18 @@ uses
|
|||
поверх нижнего, а само правило описано ниже, рядом с предпросмотром. }
|
||||
function IsOverlayClip(AProject: TProject; AClip: TClip): Boolean; forward;
|
||||
|
||||
{ Сцепить две цепочки фильтров через запятую. Пустые не дают ни лишней
|
||||
запятой, ни пустого звена: и то, и другое ffmpeg не прощает. }
|
||||
function JoinFilters(const A, B: string): string;
|
||||
begin
|
||||
if A = '' then
|
||||
Result := B
|
||||
else if B = '' then
|
||||
Result := A
|
||||
else
|
||||
Result := A + ',' + B;
|
||||
end;
|
||||
|
||||
const
|
||||
{ Длительность округляется до миллисекунд: иначе крошечное расхождение
|
||||
в дробной части плодило бы почти одинаковые файлы. }
|
||||
|
|
@ -1414,6 +1426,10 @@ var
|
|||
RangeA, RangeB: TDoubleArray;
|
||||
RangeCount: Integer;
|
||||
Ov: TPreviewOverlay;
|
||||
ImgReq: TGenRequest;
|
||||
{ Заготовки картинок для наложения. Отдельным списком, потому что
|
||||
уплощение ниже заполняет AMissing заново и затёрло бы их. }
|
||||
MissOv: TGenRequests;
|
||||
|
||||
{ Занято ли это время другим оверлеем. }
|
||||
function OverlapsTaken(const AFrom, ATo: Double): Boolean;
|
||||
|
|
@ -1441,6 +1457,7 @@ begin
|
|||
if Total <= 0 then
|
||||
Exit;
|
||||
|
||||
MissOv := nil;
|
||||
Hidden := TFPList.Create;
|
||||
try
|
||||
N := 0;
|
||||
|
|
@ -1513,16 +1530,58 @@ begin
|
|||
Item.Info.Width, Item.Info.Height,
|
||||
AProject.Master.Width, AProject.Master.Height,
|
||||
Ov.X, Ov.Y, Ov.Width, Ov.Height);
|
||||
|
||||
{ Картинку плееру отдаём не файлом, а готовым куском.
|
||||
|
||||
Своих декодеров у плеера хватает не на всё: настоящий webp из
|
||||
сети вызвал «webp: invalid TIFF header in Exif data», и
|
||||
накладывать стало нечего — картинка пропала из окна, хотя в
|
||||
ролик попадала. У неподвижного кадра есть и вторая беда: в
|
||||
списке кусков у него нет длины. Кусок же — обычное видео
|
||||
нужной длины, которое играется как всё остальное.
|
||||
|
||||
Масштаб уже внутри куска, поэтому плееру остаётся только
|
||||
поставить его на место; ключ и приглушение добавятся ниже. }
|
||||
if (Clip.Kind = ckImage) or (Item.Info.Kind = mkImage) then
|
||||
begin
|
||||
ImgReq := Default(TGenRequest);
|
||||
ImgReq.Kind := gkImage;
|
||||
ImgReq.SourcePath := Item.EffectivePath;
|
||||
ImgReq.Duration := BucketFor(Ov.TlEnd - Ov.TlStart);
|
||||
{ Цепочка без полей: поля тут и вредны — чёрное поле закрыло
|
||||
бы нижнее видео, ради которого всё и затевается. }
|
||||
ImgReq.Filter := Ov.Filter;
|
||||
ImgReq.TargetPath := GeneratedPath(AProject.CacheDir, ImgReq);
|
||||
ImgReq.TlStart := Ov.TlStart;
|
||||
ImgReq.TlEnd := Ov.TlEnd;
|
||||
Ov.FilePath := ImgReq.TargetPath;
|
||||
Ov.SrcStart := 0;
|
||||
Ov.Filter := '';
|
||||
if not FileExists(ImgReq.TargetPath) then
|
||||
begin
|
||||
{ Одна и та же картинка под одним и тем же кадрированием —
|
||||
один кусок, сколько бы раз она ни лежала на монтаже. }
|
||||
I := 0;
|
||||
while (I < Length(MissOv)) and
|
||||
(MissOv[I].TargetPath <> ImgReq.TargetPath) do
|
||||
Inc(I);
|
||||
if I >= Length(MissOv) then
|
||||
begin
|
||||
SetLength(MissOv, Length(MissOv) + 1);
|
||||
MissOv[High(MissOv)] := ImgReq;
|
||||
end;
|
||||
end;
|
||||
end;
|
||||
{ Порядок тот же, что при сборке: сперва вписали в кадр, потом
|
||||
выбили цвет, потом приглушили. Иначе смягчение края разошлось
|
||||
бы с тем, что даст готовый ролик. }
|
||||
if AWithKey then
|
||||
begin
|
||||
if Clip.ColorKey.Enabled then
|
||||
Ov.Filter := Ov.Filter + ',' + ColorKeyFilter(Clip.ColorKey.Color,
|
||||
Clip.ColorKey.Similarity, Clip.ColorKey.Blend);
|
||||
if OpacityFilter(Clip.Opacity) <> '' then
|
||||
Ov.Filter := Ov.Filter + ',' + OpacityFilter(Clip.Opacity);
|
||||
Ov.Filter := JoinFilters(Ov.Filter,
|
||||
ColorKeyFilter(Clip.ColorKey.Color,
|
||||
Clip.ColorKey.Similarity, Clip.ColorKey.Blend));
|
||||
Ov.Filter := JoinFilters(Ov.Filter, OpacityFilter(Clip.Opacity));
|
||||
end;
|
||||
|
||||
SetLength(AOverlays, N + 1);
|
||||
|
|
@ -1559,6 +1618,13 @@ begin
|
|||
SetLength(AMissing, Length(AMissing) + 1);
|
||||
AMissing[High(AMissing)] := MissA[I];
|
||||
end;
|
||||
{ Заготовки накладываемых картинок добавляются здесь, а не на месте:
|
||||
уплощение выше заполняет AMissing с нуля и затёрло бы их. }
|
||||
for I := 0 to High(MissOv) do
|
||||
begin
|
||||
SetLength(AMissing, Length(AMissing) + 1);
|
||||
AMissing[High(AMissing)] := MissOv[I];
|
||||
end;
|
||||
end;
|
||||
|
||||
end.
|
||||
|
|
|
|||
|
|
@ -1122,6 +1122,35 @@ begin
|
|||
Check(Missing[0].TargetPath <> PlainPath,
|
||||
'кусок с кадрированием — отдельный файл');
|
||||
end;
|
||||
|
||||
{ Под картинкой появляется видео — и она должна лечь поверх него, а
|
||||
не закрыть его своими чёрными полями. }
|
||||
Proj.AddTrack('V2');
|
||||
Proj.AddClip(Proj.Tracks[1], Media, 0, 5, 0);
|
||||
FlattenStream(Proj, tkVideo, 0, Segs, Missing);
|
||||
Check((Length(Segs) = 1) and (Length(Segs[0].Layers) = 2),
|
||||
'кусок собирается слоями: нижнее видео и картинка поверх');
|
||||
if (Length(Segs) = 1) and (Length(Segs[0].Layers) = 2) then
|
||||
Check(Pos('pad=', Segs[0].Layers[1].FitFilter) = 0,
|
||||
'у верхнего слоя полей нет');
|
||||
|
||||
{ Предпросмотру картинка отдаётся готовым куском, а не самим файлом:
|
||||
своих декодеров у плеера хватает не на всё, а у неподвижного кадра
|
||||
нет длины. }
|
||||
FlattenPreview(Proj, Segs, AudioSegs, Overlays, Missing, True);
|
||||
Check(Length(Overlays) = 1, 'картинка ушла в накладываемые');
|
||||
if Length(Overlays) = 1 then
|
||||
begin
|
||||
Check(Pos('.nlve-cache', Overlays[0].FilePath) > 0,
|
||||
'показывается готовый кусок, а не сам файл');
|
||||
Check(Pos('pad=', Overlays[0].Filter) = 0,
|
||||
'полей в цепочке наложения нет');
|
||||
PlainPath := '';
|
||||
for I := 0 to High(Missing) do
|
||||
if Missing[I].TargetPath = Overlays[0].FilePath then
|
||||
PlainPath := Missing[I].TargetPath;
|
||||
Check(PlainPath <> '', 'этот кусок заказан к созданию');
|
||||
end;
|
||||
finally
|
||||
Proj.Free;
|
||||
end;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue