#!/bin/sh # Клип, брошенный в дыру ровно по своему размеру, встаёт в неё без # наезда. # # Границы клипов в монтаже лежат не на кадровой сетке: они приходят из # исходников. Клип прилипал к концу соседа, а потом округлялся к # ближайшему кадру — и уезжал с этой границы на полкадра. Влево это # наезд в семь миллисекунд: на экране ноль пикселей, а в модели заказ на # переход, и человек ищет, откуда взялся кроссфейд там, где ничего не # накладывается. # # Монтаж строится числами: клип A с концом не на сетке, дыра ровно по # длине клипа D, сам D в конце. D тащится в дыру. Результат читается из # сохранённого проекта. # # Запуск (экран :99 поднят): # tests/dropgap.sh [<путь к nlve>] set -e cd "$(dirname "$0")/.." BIN=${1:-$PWD/nlve} DISP=${NLVE_DISPLAY:-:99} SRC=$PWD/test/поток/пара.nlve PROJ=$PWD/test/поток/дыра-ровно.nlve [ -f "$SRC" ] || { echo "нет монтажа $SRC"; exit 2; } export DISPLAY=$DISP reachable() { timeout 5 xdotool getdisplaygeometry >/dev/null 2>&1; } if ! reachable; then for p in $(pgrep -x Xvfb); do A=$(tr '\0' '\n' < /proc/$p/cmdline | grep -A1 -x -- -auth | tail -1) [ -f "$A" ] || continue XAUTHORITY=$A; export XAUTHORITY reachable && break done fi reachable || { echo "нет доступа к экрану $DISP: подними его (xvfb-run) и повтори"; exit 2; } python3 - "$SRC" "$PROJ" <<'PY' import json, sys, copy d = json.load(open(sys.argv[1])) V = next(t for t in d['tracks'] if t['name'] == 'V1') A = next(t for t in d['tracks'] if t['name'] == 'A1') ов, оа = copy.deepcopy(V['clips'][0]), copy.deepcopy(A['clips'][0]) def клип(обр, ид, группа, старт, длина, вход): c = copy.deepcopy(обр) c.update(id=ид, group=группа, tl_start=старт, duration=длина, src_in=вход, fade_in=0.0, fade_out=0.0) return c шаг = 1001/60000 D = round(3.5/шаг)*шаг # длина клипа — ровно по кадрам конецA = 10.000700 # а граница соседа — нарочно между кадрами V['clips'] = [клип(ов, 'a_v', 'ga', 0.0, конецA, 100.0), клип(ов, 'b_v', 'gb', конецA + D, 10.0001, 200.0), клип(ов, 'd_v', 'gd', 30.0, D, 300.0)] A['clips'] = [клип(оа, 'a_a', 'ga', 0.0, конецA, 100.0), клип(оа, 'b_a', 'gb', конецA + D, 10.0001, 200.0), клип(оа, 'd_a', 'gd', 30.0, D, 300.0)] json.dump(d, open(sys.argv[2], 'w'), ensure_ascii=False) print(' дыра %.6f..%.6f, клип длиной %.6f с — ровно по размеру' % (конецA, конецA + D, D)) PY for p in $(pgrep -x nlve); do tr '\0' '\n' < /proc/$p/environ 2>/dev/null | grep -qx "DISPLAY=$DISP" && kill $p done sleep 1 SP=$PWD/test/поток/профиль-дыра-ровно rm -rf "$SP"; mkdir -p "$SP" rm -f "$PROJ.autosave" XDG_CONFIG_HOME=$SP XDG_CACHE_HOME=$SP XDG_DATA_HOME=$SP \ setsid "$BIN" "$PROJ" >/dev/null 2>&1 < /dev/null & sleep 14 X="timeout 8 xdotool" WID= for w in $($X search --name "дыра-ровно.nlve" 2>/dev/null); do xwininfo -id "$w" 2>/dev/null | grep -q IsViewable && { WID=$w; break; } done [ -n "$WID" ] || { echo "живое окно редактора не найдено"; exit 2; } $X windowmove "$WID" 0 0 windowsize "$WID" 1270 990 sleep 2 ROW=${NLVE_ROW:-505} # Два щелчка колесом от себя: в окно должен попасть и конец монтажа, где # лежит перетаскиваемый клип, и дыра. $X mousemove 700 $ROW $X keydown ctrl click 5 keyup ctrl; sleep 0.4 $X keydown ctrl click 5 keyup ctrl; sleep 1 # Тащим клип с конца к левому краю дыры. Целимся грубо: доводить должно # прилипание, в том и смысл. $X mousemove 1200 $ROW mousedown 1; sleep 0.4 $X mousemove 1000 $ROW; sleep 0.3 $X mousemove 850 $ROW; sleep 0.3 $X mousemove 815 $ROW; sleep 0.5 $X mouseup 1; sleep 1.5 $X key ctrl+s; sleep 2 for p in $(pgrep -x nlve); do tr '\0' '\n' < /proc/$p/environ 2>/dev/null | grep -qx "DISPLAY=$DISP" && kill $p done rm -f "$PROJ.autosave" python3 - "$PROJ" <<'PY' import json, sys d = json.load(open(sys.argv[1])) плохо = 0 for t in d['tracks']: кл = sorted(t['clips'], key=lambda c: c['tl_start']) if len(кл) < 3: continue for a, b in zip(кл, кл[1:]): зазор = b['tl_start'] - (a['tl_start'] + a['duration']) # Спрос строгий, до микросекунд: прилипание на то и прилипание, # чтобы край сел ровно на край. Промах в семь миллисекунд меньше # половины кадра и в ролике ничего не меняет, но монтаж от этого # перестаёт быть встык: наезд рисуется, пишется в строку # состояния и пугает переходом, которого не просили. if abs(зазор) > 1e-6: print(' ПЛОХО: на %s между %s и %s %s %.6f с' % (t['name'], a['id'], b['id'], 'наезд' if зазор < 0 else 'дыра', abs(зазор))) плохо = 1 print(' %s: %s' % (t['name'], ' '.join('%s %.6f' % (c['id'], c['tl_start']) for c in кл))) if плохо: raise SystemExit(1) print(' ok: клип сел в дыру вплотную, ни наезда, ни остатка') PY