Сведение слоёв распаковывает исходник видеокартой
Затухание в полторы-две минуты собиралось минутами, и всё это время предпросмотр ждёт. Виноваты оказались не фильтры: на десяти секундах 2688x1512 цепочка со сведением стоит 13.5 с, а вовсе без фильтров — 12.7 с. Время уходит на разбор HEVC процессором. Ключ -hwaccel cuda перед входом убирает почти всё: настоящее затухание на семьдесят секунд собирается 18.3 с вместо 75.9 с, картинка бит в бит та же (одинаковый md5 потока, 4196 кадров там и там). Просится он только там, где кодировать будет та же карта — то есть выбран NVENC. На машине без неё ключ не появляется вовсе. Сверх того у подготовки вставок есть откат: не пошло — повторяем на процессоре, как это уже делает склейка с перекодированием. Сборка ролика пока по-старому: там кусок с наложением идёт в общий поток, и повторить его после отказа посреди работы нельзя — данные уже ушли в трубу. Это отдельная задача.
This commit is contained in:
parent
d2a49343c3
commit
8d1cb9af5c
4 changed files with 109 additions and 5 deletions
|
|
@ -840,6 +840,14 @@ Wine с новой библиотекой не грузилось ничего.
|
|||
привычный RGB. Записанный по привычке код даёт цвет с переставленными
|
||||
красным и синим.
|
||||
|
||||
**Разбор HEVC на процессоре — самое дорогое в сведении слоёв.** Не
|
||||
фильтры, как кажется: на десяти секундах 2688x1512 цепочка со сведением
|
||||
стоит 13.5 с, а без всяких фильтров — 12.7 с. Всё время уходит на разбор
|
||||
исходника. Ключ `-hwaccel cuda` перед входом убирает его почти целиком:
|
||||
затухание на семьдесят секунд собирается 18.3 с вместо 75.9 с, картинка
|
||||
бит в бит та же. Просить его стоит только там, где кодирует та же карта
|
||||
(выбран NVENC): иначе на машине без неё пришлось бы ловить отказ.
|
||||
|
||||
**Список недостающего — не список нужного.** Недостающее у нас — это то,
|
||||
чего нет на диске. Но у куска, который делается прямо сейчас, файл уже
|
||||
есть: ffmpeg создаёт его в первую же секунду и пишет до конца. Проверка
|
||||
|
|
|
|||
|
|
@ -198,13 +198,19 @@ function ArgsEncodeAudio(const ASource, ATarget: string;
|
|||
|
||||
ASubFile — разметка текста, которую надо вжечь в этот кусок. Пусто — не
|
||||
вжигать. Без этого текст пропадал везде, где есть наложение: кусок шёл
|
||||
этой веткой, а она про субтитры не знала. }
|
||||
этой веткой, а она про субтитры не знала.
|
||||
|
||||
AHwDecode — распаковывать исходники видеокартой. На длинном затухании
|
||||
это разница между тремя минутами и одной: замерено на HEVC 2688x1512,
|
||||
13.5 с против 3.3 с на десяти секундах материала, картинка бит в бит
|
||||
та же. Признак необязательный: не пойдёт — вызывающий повторит без
|
||||
него, как это уже делает склейка с перекодированием. }
|
||||
function ArgsRenderLayers(const ALayers: array of TSegmentLayer;
|
||||
const ATarget: string; const ADuration: Double;
|
||||
AWidth, AHeight, AFpsNum, AFpsDen: Integer;
|
||||
const APixFmt, ACodec: string; AIsTs: Boolean;
|
||||
ABitrate: Int64 = 0; const ASubFile: string = '';
|
||||
AAt: Double = NotPiped): TArgs;
|
||||
AAt: Double = NotPiped; AHwDecode: Boolean = False): TArgs;
|
||||
|
||||
{ Сшивание готовой картинки с готовым звуком. Оба потока копируются.
|
||||
|
||||
|
|
@ -357,11 +363,27 @@ function ArgsRenderLayers(const ALayers: array of TSegmentLayer;
|
|||
const ATarget: string; const ADuration: Double;
|
||||
AWidth, AHeight, AFpsNum, AFpsDen: Integer;
|
||||
const APixFmt, ACodec: string; AIsTs: Boolean;
|
||||
ABitrate: Int64; const ASubFile: string; AAt: Double): TArgs;
|
||||
ABitrate: Int64; const ASubFile: string; AAt: Double;
|
||||
AHwDecode: Boolean): TArgs;
|
||||
var
|
||||
I: Integer;
|
||||
Graph, Fit, Src, OutLabel: string;
|
||||
Crossfade: Boolean;
|
||||
|
||||
{ Распаковка на видеокарте. Ключ входной, поэтому ставится перед
|
||||
каждым входом отдельно.
|
||||
|
||||
Просим её только там, где кодировать будет та же карта: раз NVENC
|
||||
выбран, значит карта есть и драйвер её видит. На машине без неё ключ
|
||||
не появится вовсе, и просить откат будет не о чем. }
|
||||
function HwArgs: TArgs;
|
||||
begin
|
||||
if AHwDecode and (Pos('nvenc', PreferredEncoder(ACodec)) > 0) then
|
||||
Result := ['-hwaccel', 'cuda']
|
||||
else
|
||||
Result := nil;
|
||||
end;
|
||||
|
||||
begin
|
||||
Result := ['-hide_banner', '-nostdin', '-loglevel', 'warning', '-y'];
|
||||
|
||||
|
|
@ -379,7 +401,7 @@ begin
|
|||
'-t', SecondsToStr(ADuration),
|
||||
'-i', ALayers[I].FilePath])
|
||||
else
|
||||
Result := Concat(Result,
|
||||
Result := Concat(Concat(Result, HwArgs),
|
||||
['-ss', SecondsToStr(ALayers[I].SrcStart),
|
||||
'-t', SecondsToStr(ADuration),
|
||||
'-i', ALayers[I].FilePath]);
|
||||
|
|
|
|||
|
|
@ -262,7 +262,8 @@ begin
|
|||
Args := ArgsRenderLayers(FRequests[I].Layers,
|
||||
FRequests[I].TargetPath, FRequests[I].Duration,
|
||||
FMaster.Width, FMaster.Height, FMaster.FpsNum, FMaster.FpsDen,
|
||||
FMaster.PixFmt, FMaster.VideoCodec, False, FMaster.VideoBitrate);
|
||||
FMaster.PixFmt, FMaster.VideoCodec, False, FMaster.VideoBitrate,
|
||||
'', NotPiped, True);
|
||||
else
|
||||
Continue;
|
||||
end;
|
||||
|
|
@ -270,6 +271,24 @@ begin
|
|||
Log('> ' + CommandToString(FfmpegExe, Args));
|
||||
Res := RunProcess(FfmpegExe, Args, nil, @ErrorLine, @CancelQuery);
|
||||
|
||||
{ Распаковка на видеокарте не пошла — повторяем на процессоре.
|
||||
|
||||
Она даёт вчетверо на длинном затухании, но зависит от драйвера и
|
||||
от того, знаком ли видеокарте кодек исходника. Отказываться из-за
|
||||
неё от сборки нельзя: медленно лучше, чем никак. }
|
||||
if (FRequests[I].Kind = gkLayers) and (not Res.Cancelled) and
|
||||
(Res.Started and (Res.ExitCode <> 0)) then
|
||||
begin
|
||||
Log('Распаковка на видеокарте не пошла, повторяю на процессоре.');
|
||||
Args := ArgsRenderLayers(FRequests[I].Layers,
|
||||
FRequests[I].TargetPath, FRequests[I].Duration,
|
||||
FMaster.Width, FMaster.Height, FMaster.FpsNum, FMaster.FpsDen,
|
||||
FMaster.PixFmt, FMaster.VideoCodec, False, FMaster.VideoBitrate,
|
||||
'', NotPiped, False);
|
||||
Log('> ' + CommandToString(FfmpegExe, Args));
|
||||
Res := RunProcess(FfmpegExe, Args, nil, @ErrorLine, @CancelQuery);
|
||||
end;
|
||||
|
||||
if Res.Cancelled then
|
||||
begin
|
||||
FCancelled := True;
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ uses
|
|||
{ Interfaces нужен, чтобы связалась вся LCL и заработала загрузка
|
||||
файлов форм. }
|
||||
Interfaces, Forms, Controls, Classes, SysUtils, Math, TypInfo, uTheme,
|
||||
uFfmpegCmd, uAppTypes,
|
||||
uMediaProbe, uProject, uTransform, uTimelineCtrl,
|
||||
uPanCropForm, uDepsForm, uMismatchForm, uProgressForm, uProjectInfoForm,
|
||||
uRemuxForm;
|
||||
|
|
@ -844,6 +845,59 @@ begin
|
|||
TransformFitsOutputCrop(T));
|
||||
end;
|
||||
|
||||
{ Сведение слоёв просит распаковку у видеокарты.
|
||||
|
||||
На длинном затухании это разница между тремя минутами и одной, а
|
||||
ключ входной: ffmpeg принимает его только перед тем входом, к которому
|
||||
он относится. Забыть его легко и незаметно — команда останется рабочей,
|
||||
просто медленной. }
|
||||
procedure CheckLayerHwDecode;
|
||||
var
|
||||
Layers: array[0..0] of TSegmentLayer;
|
||||
A: TArgs;
|
||||
I, Hw, Input: Integer;
|
||||
begin
|
||||
WriteLn;
|
||||
WriteLn('Распаковка слоёв видеокартой');
|
||||
WriteLn;
|
||||
|
||||
Layers[0] := Default(TSegmentLayer);
|
||||
Layers[0].FilePath := '/несуществующий/видео.mp4';
|
||||
Layers[0].SrcStart := 10;
|
||||
Layers[0].FadeFrom := 1;
|
||||
Layers[0].FadeTo := 0;
|
||||
|
||||
A := ArgsRenderLayers(Layers, '/выход.mkv', 70, 2688, 1512, 60000, 1001,
|
||||
'yuv420p', 'hevc', False, 0, '', NotPiped, False);
|
||||
Hw := -1;
|
||||
for I := 0 to High(A) do
|
||||
if A[I] = '-hwaccel' then
|
||||
Hw := I;
|
||||
CheckTrue('без просьбы ключа нет', Hw < 0);
|
||||
|
||||
A := ArgsRenderLayers(Layers, '/выход.mkv', 70, 2688, 1512, 60000, 1001,
|
||||
'yuv420p', 'hevc', False, 0, '', NotPiped, True);
|
||||
Hw := -1;
|
||||
Input := -1;
|
||||
for I := 0 to High(A) do
|
||||
begin
|
||||
if A[I] = '-hwaccel' then
|
||||
Hw := I;
|
||||
if (A[I] = '-i') and (Input < 0) then
|
||||
Input := I;
|
||||
end;
|
||||
|
||||
{ Просьба выполняется только там, где кодировать будет та же карта. На
|
||||
машине без неё проверять нечего, и это не поломка. }
|
||||
if Pos('nvenc', PreferredEncoder('hevc')) = 0 then
|
||||
WriteLn(' -- видеокарты для кодирования нет, проверять нечего')
|
||||
else
|
||||
begin
|
||||
CheckTrue('по просьбе ключ появляется', Hw >= 0);
|
||||
CheckTrue('и стоит перед входом, а не после', (Hw >= 0) and (Hw < Input));
|
||||
end;
|
||||
end;
|
||||
|
||||
procedure CheckButtonClicks;
|
||||
var
|
||||
Btn: TButtonProbe;
|
||||
|
|
@ -904,6 +958,7 @@ begin
|
|||
CheckSplitAllTracks;
|
||||
CheckGapTolerance;
|
||||
CheckSourceRect;
|
||||
CheckLayerHwDecode;
|
||||
CheckButtonClicks;
|
||||
|
||||
WriteLn;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue