Клип, не заполняющий кадр, ложится поверх нижнего, а не закрывает его

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

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

Решение одно на оба пути: IsOverlayClip, и в нём TransformFillsFrame
считает по тем же числам, что уходят в фильтр.

Заодно две вещи, всплывшие по дороге:

— у картинки своя ветка уплощения, и слои там не строились вовсе: клип с
  прозрачностью в окне накладывался, а в ролике нет. Теперь строятся.
— в сборке ветка «готовая вставка» стояла раньше ветки слоёв, и кусок со
  слоями уходил в неё за несуществующей заготовкой. Заготовка для такого
  куска больше и не заказывается: это были бы минуты впустую.

Проверка tests/imagecrop.sh получила второй случай: под картинкой видео,
вокруг неё обязано быть видно его. На прежней сборке там чёрное.
This commit is contained in:
4aiman 2026-10-16 16:17:30 +03:00
commit 3d3d02368c
6 changed files with 193 additions and 17 deletions

View file

@ -109,3 +109,73 @@ if плохо:
raise SystemExit(1)
print(' ok: картинка уменьшена и сдвинута вправо, как в окне кадрирования')
PY
# Второй случай: под картинкой лежит видео. Тогда пустое место вокруг неё
# не чёрное поле, а это самое видео: клип, не заполняющий кадр, ложится
# поверх нижнего, а не подменяет его собой.
echo
echo " то же, но с видео на дорожке ниже"
"$FF" -hide_banner -loglevel error -y -f lavfi -i color=c=red:s=1920x1080:rate=30 \
-t 2 -c:v libx264 -pix_fmt yuv420p -g 15 "$WORK/низ.mp4"
python3 - "$WORK" <<'PY'
import json, os, sys
W = sys.argv[1]
d = json.load(open(os.path.join(W, 'картинка.nlve')))
d['pool'].append({'id': 'm2', 'path': os.path.join(W, 'низ.mp4'),
'normalized_path': '',
'info': {'valid': True, 'kind': 'video', 'has_video': True,
'video_codec': 'h264', 'width': 1920,
'height': 1080, 'fps_num': 30, 'fps_den': 1,
'pix_fmt': 'yuv420p', 'duration': 2.0,
'has_audio': False}})
низ = {'id': 'c2', 'media': 'm2', 'kind': 'video', 'src_in': 0.0,
'tl_start': 0.0, 'duration': 1.0, 'edge_in': 'snap',
'edge_out': 'snap', 'group': '', 'volume': 1.0, 'opacity': 1.0,
'fade_in': 0.0, 'fade_out': 0.0,
'transform': {'fit': 'fit', 'rotation': 0, 'flip_h': False,
'flip_v': False, 'zoom': 1.0, 'offset_x': 0.0,
'offset_y': 0.0},
'text': ''}
# Вторая видеодорожка идёт в списке после первой, то есть ниже неё.
d['tracks'].insert(1, {'id': 't3', 'name': 'V2', 'kind': 'video',
'clips': [низ]})
json.dump(d, open(os.path.join(W, 'поверх.nlve'), 'w'), ensure_ascii=False)
PY
OUT2=$WORK/поверх.mkv
"$TOOL" render "$WORK/поверх.nlve" "$OUT2" >"$WORK/сборка2.log" 2>&1 ||
{ echo " сборка не прошла, смотри $WORK/сборка2.log"; tail -5 "$WORK/сборка2.log"; exit 1; }
"$FF" -hide_banner -loglevel error -y -i "$OUT2" -frames:v 1 "$WORK/готовый2.png"
python3 - "$WORK" <<'PY'
import sys, os
from PIL import Image
W = sys.argv[1]
im = Image.open(os.path.join(W, 'готовый2.png')).convert('RGB')
ш, в = im.size
def зелено(т):
r, g, b = т
return g > 80 and g > r + 40 and g > b + 40
def красно(т):
r, g, b = т
return r > 80 and r > g + 40 and r > b + 40
слева = im.getpixel((int(ш * 0.12), в // 2))
справа = im.getpixel((int(ш * 0.90), в // 2))
print(' слева %s, справа %s' % (слева, справа))
плохо = 0
if not красно(слева):
print(' ПЛОХО: слева не нижнее видео — картинка закрыла его полями')
плохо = 1
if not зелено(справа):
print(' ПЛОХО: справа нет картинки')
плохо = 1
if плохо:
raise SystemExit(1)
print(' ok: картинка легла поверх видео, чёрных полей нет')
PY

View file

@ -573,6 +573,11 @@ begin
else
KindName := 'видео';
end;
{ Слои видно сразу: по ним отличается кусок, который собирается
наложением, от куска, который просто копируется. }
if Length(ASegments[I].Layers) > 0 then
KindName := KindName + Format(' (слоёв: %d)',
[Length(ASegments[I].Layers)]);
WriteLn(Format(' [%d] %s из %s длина %s',
[I, KindName, SecondsToStr(ASegments[I].SrcStart),
SecondsToStr(ASegments[I].Duration)]));