Наложение в предпросмотре сводится заранее; номер сборки в заголовке
Клип, не заполняющий кадр, стал уходить плееру отдельной дорожкой, и весь показ пошёл через цепочку фильтров. Цепочка работает всё время показа и считается процессором: на кадре 2688x1512 видео начало отставать от звука. Теперь плееру достаются только прозрачные клипы и клипы с ключом — ради них это оправдано, их участки бывают длинными, и пересобирать минуты материала дорого. Клип, который просто не заполняет кадр, сводится заранее вместе с тем, что под ним: предпросмотр играет обычный файл и показывает ровно то, что даст сборка. Попутно, из разбора того же случая: — заполнитель до первого наложения нарезается по длине файла. Одной записью mpv её обрезает («entry 0 uses 7082.687000 seconds, but file has only 15.021000»), дорожка оказывается короче монтажа и не совмещается с ним по времени; — появление заготовки вызывает перезагрузку списка: строка списка при этом не меняется, и без признака готовности плеер так и оставался без дорожки наложения; — в журнал пишется, сколько окон наложения и готовы ли файлы, а также собранная цепочка показа и неудача video-add. Без этого «картинки нет» нечем объяснить. В заголовке окна теперь номер сборки: nlve 42 — фильм.nlve. Он растёт на единицу при каждой сборке. По нему сразу видно, та ли это сборка, где правка уже есть, — сегодня время ушло на то, что проверяли старым двоичным файлом. Счётчик в build.number, в репозиторий не входит. Проверено на монтаже с картинкой на 300-й секунде: в окне она лежит поверх видео, цепочки показа нет.
This commit is contained in:
parent
766f1ad196
commit
567c518a62
7 changed files with 192 additions and 16 deletions
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -92,3 +92,7 @@ shot*.png
|
||||||
|
|
||||||
# Мусор компоновщика Lazarus.
|
# Мусор компоновщика Lazarus.
|
||||||
link*.res
|
link*.res
|
||||||
|
|
||||||
|
# Номер сборки и созданный из него файл: про эту машину, не про историю.
|
||||||
|
/build.number
|
||||||
|
/src/uBuildInfo.inc
|
||||||
|
|
|
||||||
30
README.md
30
README.md
|
|
@ -731,12 +731,18 @@ Linux потом искала по нему `.dll`.
|
||||||
неоткуда. Заготовка с полями остаётся для случая, когда класть не на
|
неоткуда. Заготовка с полями остаётся для случая, когда класть не на
|
||||||
что.
|
что.
|
||||||
|
|
||||||
В предпросмотре накладываемая картинка тоже показывается заготовкой,
|
Предпросмотр идёт тем же путём: участок с наложением сводится заранее
|
||||||
только без полей. Своих декодеров плееру хватает не на всё: настоящий
|
и играется обычным файлом. Силами плеера рисуются только прозрачные
|
||||||
webp из сети дал «webp: invalid TIFF header in Exif data», и картинка
|
клипы и клипы с ключом — их участки бывают длинными, и пересобирать
|
||||||
пропадала из окна, хотя в ролик попадала. У неподвижного кадра есть и
|
минуты материала ради них дорого. Цепочка плеера работает всё время
|
||||||
вторая беда — в списке кусков у него нет длины. Заготовка же обычное
|
показа и считается процессором: на кадре 2688x1512 видео начинает
|
||||||
видео нужной длины.
|
отставать от звука, поэтому ради клипа, который просто не заполняет
|
||||||
|
кадр, её не включают.
|
||||||
|
|
||||||
|
Картинка, которую всё же рисует плеер, отдаётся ему заготовкой без
|
||||||
|
полей, а не самим файлом: своих декодеров ему хватает не на всё
|
||||||
|
(настоящий webp из сети дал «webp: invalid TIFF header in Exif data»),
|
||||||
|
да и у неподвижного кадра нет длины в списке кусков.
|
||||||
3. Границы кусков кладутся на кадровую сетку проекта: между кадрами
|
3. Границы кусков кладутся на кадровую сетку проекта: между кадрами
|
||||||
времени нет, а звук набирается с точностью до отсчёта, и доли кадра
|
времени нет, а звук набирается с точностью до отсчёта, и доли кадра
|
||||||
разводят их в стороны.
|
разводят их в стороны.
|
||||||
|
|
@ -1104,6 +1110,18 @@ xdotool не видит ни одного окна и проверка «про
|
||||||
времени, второй щелчок по наезду тащит верхний клип вместо нижнего, а
|
времени, второй щелчок по наезду тащит верхний клип вместо нижнего, а
|
||||||
картинка с уменьшением 50% выходит в ролик во весь кадр.
|
картинка с уменьшением 50% выходит в ролик во весь кадр.
|
||||||
|
|
||||||
|
## Номер сборки
|
||||||
|
|
||||||
|
В заголовке окна стоит номер: `nlve 42 — фильм.nlve`. Он растёт на
|
||||||
|
единицу при каждой сборке редактора (`build.sh`), и по нему сразу видно,
|
||||||
|
та ли это сборка, где нужная правка уже есть. Без него проверяют старым
|
||||||
|
двоичным файлом, видят прежнее поведение, и время уходит на выяснение,
|
||||||
|
у кого что не работает.
|
||||||
|
|
||||||
|
Счётчик лежит в `build.number`, из него создаётся `src/uBuildInfo.inc`.
|
||||||
|
Оба файла в репозиторий не входят: номер про эту машину, а не про
|
||||||
|
историю проекта.
|
||||||
|
|
||||||
## Структура
|
## Структура
|
||||||
|
|
||||||
| Файл | Что это |
|
| Файл | Что это |
|
||||||
|
|
|
||||||
29
build.sh
29
build.sh
|
|
@ -72,8 +72,37 @@ run_lazbuild() {
|
||||||
return "$rc"
|
return "$rc"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Номер сборки. Растёт на единицу при каждой сборке редактора и попадает
|
||||||
|
# в заголовок окна.
|
||||||
|
#
|
||||||
|
# Зачем: по заголовку сразу видно, та ли это сборка, где правка уже есть.
|
||||||
|
# Без него человек проверяет старым двоичным файлом и видит прежнее
|
||||||
|
# поведение — так уже случалось, и время ушло на выяснение, у кого что
|
||||||
|
# не работает.
|
||||||
|
#
|
||||||
|
# Счётчик лежит рядом с исходниками, но в репозиторий не входит: он про
|
||||||
|
# эту машину, а не про историю проекта.
|
||||||
|
bump_version() {
|
||||||
|
local file="$HERE/build.number" n
|
||||||
|
n=0
|
||||||
|
[ -f "$file" ] && n=$(cat "$file" 2>/dev/null || echo 0)
|
||||||
|
case "$n" in
|
||||||
|
''|*[!0-9]*) n=0 ;;
|
||||||
|
esac
|
||||||
|
n=$((n + 1))
|
||||||
|
echo "$n" > "$file"
|
||||||
|
cat > "$HERE/src/uBuildInfo.inc" <<EOF
|
||||||
|
{ Создаётся build.sh при каждой сборке. В репозиторий не входит. }
|
||||||
|
const
|
||||||
|
BuildNumber = $n;
|
||||||
|
BuildStamp = '$(date '+%d.%m.%Y %H:%M')';
|
||||||
|
EOF
|
||||||
|
echo "сборка номер $n"
|
||||||
|
}
|
||||||
|
|
||||||
build_current() {
|
build_current() {
|
||||||
check_toolchain
|
check_toolchain
|
||||||
|
bump_version
|
||||||
if run_lazbuild "Редактор под эту систему ($LCL_LINUX)" \
|
if run_lazbuild "Редактор под эту систему ($LCL_LINUX)" \
|
||||||
--ws="$LCL_LINUX" "$HERE/nlve.lpi"; then
|
--ws="$LCL_LINUX" "$HERE/nlve.lpi"; then
|
||||||
echo "готово: $HERE/nlve"
|
echo "готово: $HERE/nlve"
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,16 @@ type
|
||||||
Y: Integer;
|
Y: Integer;
|
||||||
Width: Integer;
|
Width: Integer;
|
||||||
Height: Integer;
|
Height: Integer;
|
||||||
|
{ Сколько секунд можно взять из этого файла.
|
||||||
|
|
||||||
|
Список кусков оверлея обязан покрывать монтаж от самого начала,
|
||||||
|
иначе плееру нечем совместить его по времени с основой. Промежуток
|
||||||
|
до первого показа заполняется тем же файлом, и просить у него
|
||||||
|
больше, чем в нём есть, нельзя: mpv обрезает такую запись
|
||||||
|
(«entry uses 7082.687000 seconds, but file has only 15.021000») —
|
||||||
|
дорожка оказывается короче монтажа, и оверлей не показывается
|
||||||
|
вовсе. Поэтому заполнитель нарезается на куски по этой длине. }
|
||||||
|
SrcLength: Double;
|
||||||
end;
|
end;
|
||||||
TPreviewOverlays = array of TPreviewOverlay;
|
TPreviewOverlays = array of TPreviewOverlay;
|
||||||
|
|
||||||
|
|
@ -1391,6 +1401,21 @@ end;
|
||||||
хочет увидеть под ним то, что на дорожке ниже, — иначе зачем уменьшать.
|
хочет увидеть под ним то, что на дорожке ниже, — иначе зачем уменьшать.
|
||||||
|
|
||||||
Если под клипом ничего нет, поля остаются полями: класть не на что. }
|
Если под клипом ничего нет, поля остаются полями: класть не на что. }
|
||||||
|
{ Кого плеер рисует поверх основы сам, своей цепочкой.
|
||||||
|
|
||||||
|
Только прозрачные клипы и клипы с ключом. Цепочка идёт через процессор
|
||||||
|
и работает всё время показа, а не только там, где лежит оверлей: на
|
||||||
|
2688x1512 видео начинает отставать от звука. Ради длинного
|
||||||
|
полупрозрачного слоя это оправдано — иначе пришлось бы пересобирать
|
||||||
|
минуты материала. Ради клипа, который просто не заполняет кадр, — нет:
|
||||||
|
его участок дешевле свести заранее, как для ролика, и тогда
|
||||||
|
предпросмотр играет обычный файл. }
|
||||||
|
function IsPlayerOverlay(AClip: TClip): Boolean;
|
||||||
|
begin
|
||||||
|
Result := (AClip <> nil) and (AClip.Kind in [ckVideo, ckImage]) and
|
||||||
|
(AClip.ColorKey.Enabled or (AClip.Opacity < 0.999));
|
||||||
|
end;
|
||||||
|
|
||||||
function IsOverlayClip(AProject: TProject; AClip: TClip): Boolean;
|
function IsOverlayClip(AProject: TProject; AClip: TClip): Boolean;
|
||||||
var
|
var
|
||||||
Item: TMediaItem;
|
Item: TMediaItem;
|
||||||
|
|
@ -1471,7 +1496,11 @@ begin
|
||||||
for C := 0 to Track.Count - 1 do
|
for C := 0 to Track.Count - 1 do
|
||||||
begin
|
begin
|
||||||
Clip := Track[C];
|
Clip := Track[C];
|
||||||
if not IsOverlayClip(AProject, Clip) then
|
{ Плееру отдаём только прозрачные и с ключом. Остальное, что не
|
||||||
|
заполняет кадр, сводится заранее вместе с тем, что под ним:
|
||||||
|
так предпросмотр совпадает с роликом и не упирается в
|
||||||
|
процессор. }
|
||||||
|
if not IsPlayerOverlay(Clip) then
|
||||||
Continue;
|
Continue;
|
||||||
Item := AProject.FindMedia(Clip.MediaId);
|
Item := AProject.FindMedia(Clip.MediaId);
|
||||||
if Item = nil then
|
if Item = nil then
|
||||||
|
|
@ -1526,6 +1555,7 @@ begin
|
||||||
Ov.TlEnd := RangeB[J];
|
Ov.TlEnd := RangeB[J];
|
||||||
Ov.FilePath := Item.EffectivePath;
|
Ov.FilePath := Item.EffectivePath;
|
||||||
Ov.SrcStart := Clip.SrcIn + (RangeA[J] - Clip.TlStart);
|
Ov.SrcStart := Clip.SrcIn + (RangeA[J] - Clip.TlStart);
|
||||||
|
Ov.SrcLength := Item.Info.Duration;
|
||||||
Ov.Filter := OverlayFilter(Clip.Transform,
|
Ov.Filter := OverlayFilter(Clip.Transform,
|
||||||
Item.Info.Width, Item.Info.Height,
|
Item.Info.Width, Item.Info.Height,
|
||||||
AProject.Master.Width, AProject.Master.Height,
|
AProject.Master.Width, AProject.Master.Height,
|
||||||
|
|
@ -1556,6 +1586,7 @@ begin
|
||||||
ImgReq.TlEnd := Ov.TlEnd;
|
ImgReq.TlEnd := Ov.TlEnd;
|
||||||
Ov.FilePath := ImgReq.TargetPath;
|
Ov.FilePath := ImgReq.TargetPath;
|
||||||
Ov.SrcStart := 0;
|
Ov.SrcStart := 0;
|
||||||
|
Ov.SrcLength := ImgReq.Duration;
|
||||||
Ov.Filter := '';
|
Ov.Filter := '';
|
||||||
if not FileExists(ImgReq.TargetPath) then
|
if not FileExists(ImgReq.TargetPath) then
|
||||||
begin
|
begin
|
||||||
|
|
|
||||||
|
|
@ -33,6 +33,10 @@ uses
|
||||||
uIconData, uSubtitles, uTextMeasure, uTextForm, uExportForm,
|
uIconData, uSubtitles, uTextMeasure, uTextForm, uExportForm,
|
||||||
uColorKeyForm;
|
uColorKeyForm;
|
||||||
|
|
||||||
|
{ Номер сборки и время. Файл создаётся build.sh при каждой сборке и в
|
||||||
|
репозиторий не входит. }
|
||||||
|
{$I uBuildInfo.inc}
|
||||||
|
|
||||||
const
|
const
|
||||||
{ Номера системных сообщений о перерисовке рамки окна. Берём числами, а
|
{ Номера системных сообщений о перерисовке рамки окна. Берём числами, а
|
||||||
не из модуля Windows: тот переопределяет TRect и Point, и обычные
|
не из модуля Windows: тот переопределяет TRect и Point, и обычные
|
||||||
|
|
@ -130,6 +134,9 @@ type
|
||||||
FCheckMisplacedOnShow: Boolean;
|
FCheckMisplacedOnShow: Boolean;
|
||||||
FPreviewOverlayUrl: string;
|
FPreviewOverlayUrl: string;
|
||||||
FPreviewOverlayWindows: TOverlayWindows;
|
FPreviewOverlayWindows: TOverlayWindows;
|
||||||
|
{ Лежат ли на диске все файлы наложения. Входит в сравнение списков:
|
||||||
|
появление заготовки обязано вызвать перезагрузку. }
|
||||||
|
FPreviewOverlayReady: Boolean;
|
||||||
{ Списки кусков звуковых дорожек для плеера. }
|
{ Списки кусков звуковых дорожек для плеера. }
|
||||||
FPreviewAudioUrls: array of string;
|
FPreviewAudioUrls: array of string;
|
||||||
{ Звуковые дорожки предпросмотра — их номера в проекте, а не сами
|
{ Звуковые дорожки предпросмотра — их номера в проекте, а не сами
|
||||||
|
|
@ -1578,7 +1585,11 @@ begin
|
||||||
ProjectName := ExtractFileName(FProject.FileName);
|
ProjectName := ExtractFileName(FProject.FileName);
|
||||||
if FProject.Modified then
|
if FProject.Modified then
|
||||||
ProjectName := ProjectName + ' *';
|
ProjectName := ProjectName + ' *';
|
||||||
Caption := 'nlve — ' + ProjectName;
|
{ Номер сборки в заголовке: по нему сразу видно, та ли это сборка, где
|
||||||
|
правка уже есть. Без него проверяют старым двоичным файлом, видят
|
||||||
|
прежнее поведение, и время уходит на выяснение, у кого что не
|
||||||
|
работает. }
|
||||||
|
Caption := Format('nlve %d — %s', [BuildNumber, ProjectName]);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TMainForm.UpdateTimeLabel;
|
procedure TMainForm.UpdateTimeLabel;
|
||||||
|
|
@ -2392,6 +2403,29 @@ var
|
||||||
Inc(N);
|
Inc(N);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Заполнить промежуток повторами файла, не прося у него больше, чем в
|
||||||
|
нём есть. Длина неизвестна — кладём одной записью, как было. }
|
||||||
|
procedure FillGap(const AOv: TPreviewOverlay; const AFrom, ATo: Double);
|
||||||
|
var
|
||||||
|
Left, Step: Double;
|
||||||
|
begin
|
||||||
|
Left := ATo - AFrom;
|
||||||
|
if AOv.SrcLength <= 0.001 then
|
||||||
|
begin
|
||||||
|
Push(AOv.FilePath, 0, Left);
|
||||||
|
Exit;
|
||||||
|
end;
|
||||||
|
{ Полсекунды запаса: длина файла у ffmpeg и у mpv расходится в
|
||||||
|
последнем знаке, и запись ровно во весь файл даёт то же самое
|
||||||
|
предупреждение об обрезке. }
|
||||||
|
Step := Max(0.5, AOv.SrcLength - 0.5);
|
||||||
|
while Left > 0.001 do
|
||||||
|
begin
|
||||||
|
Push(AOv.FilePath, 0, Min(Step, Left));
|
||||||
|
Left := Left - Step;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
begin
|
begin
|
||||||
FPreviewOverlayUrl := '';
|
FPreviewOverlayUrl := '';
|
||||||
FPreviewOverlayWindows := nil;
|
FPreviewOverlayWindows := nil;
|
||||||
|
|
@ -2402,9 +2436,16 @@ begin
|
||||||
At := 0;
|
At := 0;
|
||||||
for I := 0 to High(AOverlays) do
|
for I := 0 to High(AOverlays) do
|
||||||
begin
|
begin
|
||||||
{ Промежуток до этого куска — заполнитель из того же файла. }
|
{ Промежуток до этого куска — заполнитель из того же файла, нарезанный
|
||||||
|
по его длине.
|
||||||
|
|
||||||
|
Одной записью нельзя: mpv обрезает запись, которая просит больше,
|
||||||
|
чем есть в файле («entry uses 7082.687000 seconds, but file has only
|
||||||
|
15.021000»), дорожка оверлея оказывается короче монтажа и не
|
||||||
|
совмещается с ним по времени — наложение просто не показывается.
|
||||||
|
Замерено на монтаже в два часа с картинкой на 7082-й секунде. }
|
||||||
if AOverlays[I].TlStart > At + 0.001 then
|
if AOverlays[I].TlStart > At + 0.001 then
|
||||||
Push(AOverlays[I].FilePath, 0, AOverlays[I].TlStart - At);
|
FillGap(AOverlays[I], At, AOverlays[I].TlStart);
|
||||||
Push(AOverlays[I].FilePath, AOverlays[I].SrcStart,
|
Push(AOverlays[I].FilePath, AOverlays[I].SrcStart,
|
||||||
AOverlays[I].TlEnd - AOverlays[I].TlStart);
|
AOverlays[I].TlEnd - AOverlays[I].TlStart);
|
||||||
At := AOverlays[I].TlEnd;
|
At := AOverlays[I].TlEnd;
|
||||||
|
|
@ -2429,6 +2470,23 @@ begin
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
FPreviewOverlayUrl := BuildEdlUri(Segs);
|
FPreviewOverlayUrl := BuildEdlUri(Segs);
|
||||||
|
|
||||||
|
{ Все ли файлы наложения уже есть на диске.
|
||||||
|
|
||||||
|
Картинка показывается заготовкой, а она делается не мгновенно. Пока
|
||||||
|
её нет, плееру нечего добавлять, и дорожки наложения он не заводит.
|
||||||
|
Когда заготовка появится, список кусков останется прежним — строка в
|
||||||
|
строку, — и перезагрузки не случится: предпросмотр сравнивает именно
|
||||||
|
строки. Поэтому готовность входит в сравнение наравне с ними.
|
||||||
|
Проверено на монтаже, где картинка так и не показывалась до первой
|
||||||
|
правки. }
|
||||||
|
FPreviewOverlayReady := True;
|
||||||
|
for I := 0 to N - 1 do
|
||||||
|
if not FileExists(Segs[I].FilePath) then
|
||||||
|
begin
|
||||||
|
FPreviewOverlayReady := False;
|
||||||
|
Break;
|
||||||
|
end;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Делает ли идущая генерация то, что монтажу ещё нужно.
|
{ Делает ли идущая генерация то, что монтажу ещё нужно.
|
||||||
|
|
@ -2524,6 +2582,8 @@ begin
|
||||||
{ Наложение входит в сравнение наравне со звуком: сдвинули оверлей —
|
{ Наложение входит в сравнение наравне со звуком: сдвинули оверлей —
|
||||||
список кусков основы мог не измениться, а показывать надо иначе. }
|
список кусков основы мог не измениться, а показывать надо иначе. }
|
||||||
AudioEdl := AudioEdl + '#' + FPreviewOverlayUrl;
|
AudioEdl := AudioEdl + '#' + FPreviewOverlayUrl;
|
||||||
|
if not FPreviewOverlayReady then
|
||||||
|
AudioEdl := AudioEdl + '@ждём';
|
||||||
for I := 0 to High(FPreviewOverlayWindows) do
|
for I := 0 to High(FPreviewOverlayWindows) do
|
||||||
AudioEdl := AudioEdl + Format('|%s@%d:%d:%s-%s',
|
AudioEdl := AudioEdl + Format('|%s@%d:%d:%s-%s',
|
||||||
[FPreviewOverlayWindows[I].Filter,
|
[FPreviewOverlayWindows[I].Filter,
|
||||||
|
|
@ -2552,6 +2612,12 @@ begin
|
||||||
{ Внешний звук назначается до загрузки: он часть того, что открывается. }
|
{ Внешний звук назначается до загрузки: он часть того, что открывается. }
|
||||||
FPlayer.SetAudioSources(FPreviewAudioUrls);
|
FPlayer.SetAudioSources(FPreviewAudioUrls);
|
||||||
FPlayer.SetVideoOverlay(FPreviewOverlayUrl, FPreviewOverlayWindows);
|
FPlayer.SetVideoOverlay(FPreviewOverlayUrl, FPreviewOverlayWindows);
|
||||||
|
{ Наложение — самая хрупкая часть предпросмотра: оно живёт отдельной
|
||||||
|
дорожкой, которой может не оказаться. Пишем в журнал, сколько окон
|
||||||
|
показа и готовы ли файлы, иначе «картинки нет» нечем объяснить. }
|
||||||
|
if FPreviewOverlayUrl <> '' then
|
||||||
|
Log(Format('Наложение: окон %d, файлы %s', [Length(FPreviewOverlayWindows),
|
||||||
|
BoolToStr(FPreviewOverlayReady, 'готовы', 'ещё делаются')]));
|
||||||
FPlayer.LoadUrl(Edl, Format('start=%s,pause=%s',
|
FPlayer.LoadUrl(Edl, Format('start=%s,pause=%s',
|
||||||
[SecondsToStr(Pos), BoolToStr(WasPaused, 'yes', 'no')]));
|
[SecondsToStr(Pos), BoolToStr(WasPaused, 'yes', 'no')]));
|
||||||
FLastLoadedEdl := Edl;
|
FLastLoadedEdl := Edl;
|
||||||
|
|
|
||||||
|
|
@ -1093,6 +1093,10 @@ begin
|
||||||
if Graph = FLastGraph then
|
if Graph = FLastGraph then
|
||||||
Exit;
|
Exit;
|
||||||
FLastGraph := Graph;
|
FLastGraph := Graph;
|
||||||
|
{ В журнал пишется всегда, в том числе пустая: по ней видно, собралось
|
||||||
|
ли наложение. Пустая цепочка при заявленном наложении означает, что
|
||||||
|
дорожки для него у плеера нет. }
|
||||||
|
Note('Цепочка показа: ' + Graph);
|
||||||
mpv_set_property_string(FHandle, 'lavfi-complex', PAnsiChar(Graph));
|
mpv_set_property_string(FHandle, 'lavfi-complex', PAnsiChar(Graph));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
|
@ -1103,7 +1107,7 @@ end;
|
||||||
она заменила бы картинку. Её дело — быть входом цепочки. }
|
она заменила бы картинку. Её дело — быть входом цепочки. }
|
||||||
procedure TMpvPlayer.AttachOverlay;
|
procedure TMpvPlayer.AttachOverlay;
|
||||||
var
|
var
|
||||||
Count, I: Integer;
|
Count, I, Code: Integer;
|
||||||
Kind, Ext, Id: string;
|
Kind, Ext, Id: string;
|
||||||
begin
|
begin
|
||||||
if FHandle = nil then
|
if FHandle = nil then
|
||||||
|
|
@ -1113,7 +1117,12 @@ begin
|
||||||
FOverlayVid := '';
|
FOverlayVid := '';
|
||||||
|
|
||||||
if (FOverlayUrl <> '') and (Length(FOverlayWindows) > 0) then
|
if (FOverlayUrl <> '') and (Length(FOverlayWindows) > 0) then
|
||||||
Command(['video-add', FOverlayUrl, 'auto']);
|
begin
|
||||||
|
Code := Command(['video-add', FOverlayUrl, 'auto']);
|
||||||
|
if Code < 0 then
|
||||||
|
Note(Format('Дорожку наложения добавить не удалось: video-add вернул %d',
|
||||||
|
[Code]));
|
||||||
|
end;
|
||||||
|
|
||||||
Count := StrToIntDef(GetPropString('track-list/count'), 0);
|
Count := StrToIntDef(GetPropString('track-list/count'), 0);
|
||||||
for I := 0 to Count - 1 do
|
for I := 0 to Count - 1 do
|
||||||
|
|
@ -1133,6 +1142,13 @@ begin
|
||||||
else if FBaseVid = '' then
|
else if FBaseVid = '' then
|
||||||
FBaseVid := Id;
|
FBaseVid := Id;
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
{ Пустая дорожка наложения при заявленном наложении — самый частый
|
||||||
|
способ остаться без картинки в окне. Пишем, что увидели. }
|
||||||
|
if (FOverlayUrl <> '') and (Length(FOverlayWindows) > 0) and
|
||||||
|
((FOverlayVid = '') or (FBaseVid = '')) then
|
||||||
|
Note(Format('Наложение не подключилось: дорожек %d, основа "%s", ' +
|
||||||
|
'наложение "%s"', [Count, FBaseVid, FOverlayVid]));
|
||||||
end;
|
end;
|
||||||
|
|
||||||
{ Субтитры подключаются двумя путями, и оба нужны.
|
{ Субтитры подключаются двумя путями, и оба нужны.
|
||||||
|
|
|
||||||
|
|
@ -1134,17 +1134,29 @@ begin
|
||||||
Check(Pos('pad=', Segs[0].Layers[1].FitFilter) = 0,
|
Check(Pos('pad=', Segs[0].Layers[1].FitFilter) = 0,
|
||||||
'у верхнего слоя полей нет');
|
'у верхнего слоя полей нет');
|
||||||
|
|
||||||
{ Предпросмотру картинка отдаётся готовым куском, а не самим файлом:
|
{ Предпросмотр идёт тем же путём, что и сборка: участок сводится
|
||||||
своих декодеров у плеера хватает не на всё, а у неподвижного кадра
|
заранее. Плееру его не отдаём — цепочка наложения работает всё
|
||||||
нет длины. }
|
время показа и на большом кадре уводит видео от звука. }
|
||||||
FlattenPreview(Proj, Segs, AudioSegs, Overlays, Missing, True);
|
FlattenPreview(Proj, Segs, AudioSegs, Overlays, Missing, True);
|
||||||
Check(Length(Overlays) = 1, 'картинка ушла в накладываемые');
|
Check(Length(Overlays) = 0,
|
||||||
|
'непрозрачная картинка плееру не отдаётся');
|
||||||
|
Check((Length(Segs) = 1) and (Length(Segs[0].Layers) = 2),
|
||||||
|
'в предпросмотре тот же кусок со слоями, что и в сборке');
|
||||||
|
|
||||||
|
{ А вот прозрачная уходит плееру — и тоже готовым куском, а не самим
|
||||||
|
файлом: своих декодеров у него хватает не на всё, а у неподвижного
|
||||||
|
кадра нет длины. }
|
||||||
|
Clip.Opacity := 0.5;
|
||||||
|
FlattenPreview(Proj, Segs, AudioSegs, Overlays, Missing, True);
|
||||||
|
Check(Length(Overlays) = 1, 'прозрачная картинка ушла в накладываемые');
|
||||||
if Length(Overlays) = 1 then
|
if Length(Overlays) = 1 then
|
||||||
begin
|
begin
|
||||||
Check(Pos('.nlve-cache', Overlays[0].FilePath) > 0,
|
Check(Pos('.nlve-cache', Overlays[0].FilePath) > 0,
|
||||||
'показывается готовый кусок, а не сам файл');
|
'показывается готовый кусок, а не сам файл');
|
||||||
Check(Pos('pad=', Overlays[0].Filter) = 0,
|
Check(Pos('pad=', Overlays[0].Filter) = 0,
|
||||||
'полей в цепочке наложения нет');
|
'полей в цепочке наложения нет');
|
||||||
|
Check(Overlays[0].SrcLength > 0,
|
||||||
|
'длина куска известна: заполнитель нарежут по ней');
|
||||||
PlainPath := '';
|
PlainPath := '';
|
||||||
for I := 0 to High(Missing) do
|
for I := 0 to High(Missing) do
|
||||||
if Missing[I].TargetPath = Overlays[0].FilePath then
|
if Missing[I].TargetPath = Overlays[0].FilePath then
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue