NVIDIA Encoding klappt nicht.

Sound, Digitalkameras, TV+Video und Spiele.
wanne
Moderator
Beiträge: 7465
Registriert: 24.05.2010 12:39:42

Re: NVIDIA Encoding klappt nicht.

Beitrag von wanne » 23.08.2021 20:47:25

Knogle hat geschrieben: ↑ zum Beitrag ↑
20.08.2021 14:06:36
Der Command hier verreckt irgendwie immer.

Code: Alles auswählen

ffmpeg -y -vsync 0 -hwaccel cuda -hwaccel_output_format cuda -i 2021-07-15\ 20-11-48.mkv -c:a copy -c:v h264_nvenc -b:v 5M output2.mp4
Nur nochmal zur Wiederholung:
Ganz grundsätzlich gilt: Du tust decodieren, eventuell irgend welche Zwischenoperationen (Filter) (skalieren/Farbe ändern/weichzeichnen) und encodieren.
Ersteres gibst du vor -i an das mittlere bei -vf und das letzte vor dem Output.

Jetzt kann man das auf drei verschiedene Art und weisen machen:
Man schreibt Code für die CPU. (x86_64, ARM, ...) Man schreibt Code für die GPGPU (OpenCL ...) Oder man nutzt spezielle Hardware (NVENC/NVDEC), die meist auch in der Grafikkarte hängt. (In Wahrheit sind alles bis auf das erste immer Mischformen aus allen Varianten.)
Hier schön aufgezeichnet:
https://developer.nvidia.com/blog/wp-co ... ured-2.png

Für die Nutzung werden meist irgend welche Libs mit APIs genutzt. Und da wirds dann lustig, weil du nur noch bedingt weist, was unter der Haube passiert. – DirectX oder gstreamer können alle drei Varianten... CUDA ist für seine GPGPU-Möglichkeiten bekannt. NVDEC/NVDEC ist aber eben auch Bestandteil von CUDA – entsprechend meint -hwaccel cuda eben nvdec und nicht die Nutzung der GPGPU... Daneben wird das Bild beschönigt sein. Auf der Grafikkarte werden eventuell nvdec und CUDA-Cores die selben Ressourcen nutzen.

Aber von der Idee Her -hwaccel cuda sorgt dafür das per nvdec decodiert wird. -c:v h264_nvenc dafür, dass per nvenc encodiert wird -vf scale_npp das auf der GPGPU skaliert wird und -c:v h264_cuvid (vor dem -i) das auf den CUDA-Cores decodiert wird.

Meistens ist es IMHO eher ungeschickt Grafikkarte zum decoding zu nutzen. Die CPU ist mit der Ausnahme von einigen sehr speziellen h.265-szenarien eh schneller als alles, was irgend wie hinten dran kommt. (Platte, encoding...) Macht aber ganz gerne ärger. Es gibt aber einen sehr speziellen Fall:
Die Grafikkarte kann nur auf ihrem VMEM und PCIe zugreifen, während die CPU den RAM nutzt und keinen Zugriff auf den VMEM hat. Entsprechend musst du zwischen denen per PCIe (der deutlich langsamer als der VMEM ist) per CPU hin und her transferieren. Oft genug ist das hin und her transferieren das Bottleneck.

Wenn du also auf der GPU dekodierst eh nicht skalierst/filterst und auf der GPU encodierst kannst du deine Daten gleich im VMEM behalten statt hin und her zu transferieren. Das machst du mit -hwaccel_output_format cuda. Ähnliches hat man, wenn man direkt an den Bildschirm ausgeben will.

Jo, genau das ist mir auch aufgefallen. Die GPU Auslastung liegt bei maximal 50%.
Ein Kern der auf PCIe (oder was anderes) wartet kann nichts mehr anderes tun und gilt deswegen als ausgelastet. Vernünftige Software nutzt deswegen nicht mehr als einen CPU-Kern um auf den PCIe zu schreiben, damit die anderen was vernünftiges machen können. (Das ist btw. die Idee hinter Hyperthreadding. Während der eine virtuelle Kern z.B. auf den PCIe wartet kann der andere Rechnen.)
Bei den Nvidia-GPUs ist das meist dümmer implementiert und nur 2 oder 3 Kerne stimmen sich ab. Dann wartet die Hälfte oder ⅓, ⅕ oder so. – Entsprechend sehen die Auslastungen aus.

Hier lasse ich die CPU gar nichts machen, außer hin und her zu kopieren. (Und zwar maximal dumm zuerst zum decodeiren zur GPU und zurück und dann erneut fürs encodieren das selbe Spiel. Und da das Viedeo in der Mitte unkomprimiert ist, sind das an der Stelle enorme Datenmengen.)

Code: Alles auswählen

$ time ffmpeg -t 60 -y -c:v h264_cuvid -i /media/data/Film/tearsofsteel_4k.mov -an -c:v h264_nvenc -b:v 5M output2.mp4
[…]
real    0m12.470s
user    0m9.722s
sys     0m1.050s

$ calc 1.050+9.722
        10.772
=> Es wird 1.698s gerechnet und 10.772s hin und her transferiert.
Passend sieht auch die Auslastung auf den CUDA-Cores aus:

Code: Alles auswählen

$ nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.91.03    Driver Version: 460.91.03    CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce GTX 960     On   | 00000000:01:00.0  On |                  N/A |
|  0%   50C    P2    44W / 130W |    567MiB /  2001MiB |     47%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
Zum Vergleich: Wenn ich auf der CPU Decodiere:

Code: Alles auswählen

time ffmpeg -t 60 -y -i /media/data/Film/tearsofsteel_4k.mov -an -c:v h264_nvenc -b:v 5M outpu2.mp4
[…]
real    0m15.691s
user    1m38.455s
sys     0m1.201s

calc "(60+38.455+10.77)/8"
        13.653125
Hier sind wirklich die meiste Zeit 8 Kerne beschäftigt. Dafür langweilt sich die GPU, weil die CPU nicht schnell genug nachschieben kann:

Code: Alles auswählen

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.91.03    Driver Version: 460.91.03    CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce GTX 960     On   | 00000000:01:00.0  On |                  N/A |
|  0%   47C    P2    40W / 130W |    293MiB /  2001MiB |     23%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
Und jetzt eben die optimale Variante, die den unkomprimierten Stream nicht kopiert wird:

Code: Alles auswählen

time ffmpeg -t 60 -y -hwaccel cuda -hwaccel_output_format cuda -i /media/data/Film/tearsofsteel_4k.mov -an -c:v h264_nvenc -b:v 5M outpu2.mp4

real    0m12.182s
user    0m1.980s
sys     0m0.836s

+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.91.03    Driver Version: 460.91.03    CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce GTX 960     On   | 00000000:01:00.0  On |                  N/A |
|  0%   48C    P2    45W / 130W |    654MiB /  2001MiB |      7%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+
CPU und CUDA-Cores langweilen sich weil wirklich nur die spezialisierte Hardware arbeitet. Das ist dann auch etwas schneller. (Der Unterschied hält sich aber in Grenzen, weil das encodieren bei mir halt der dominierende Schritt ist. Bei deiner Karte dürfte das etwas mehr aus machen.)
=> Wenig CUDA-Core-Auslastung ist wünschenswert!

Ich verstehe nicht so ganz warum du mit dem Selbstkompilierten Zeug rum machst. Beim h.264-Encoding hat sich in den letzten paar jahren wirklich nichts mehr gemacht. Wenn du den ffmpeg aus den Repos mit dem cuda aus den Repos mit den Kernelmodulen aus den Repos benutzt hast du keine Probleme. AmEnde will der ffmpeg den du da zusammen compiliert hast nicht mit dem CUDA aus stable. Und dnach hast du inkompatibilitäten zwischen Treiber und cuda. Dazu hat man doch ne Distribution, damit die sich um solche kompatibilitäten kümmert.
Knogle hat geschrieben: ↑ zum Beitrag ↑
20.08.2021 14:06:36

Code: Alles auswählen

Impossible to convert between the formats supported by the filter 'Parsed_null_0' and the filter 'auto_scale_0'
Error reinitializing filters!
Failed to inject frame into filter network: Function not implemented
Error while processing the decoded data for stream #0:0
Conversion failed!
Im Normalfall passiert sowas, wenn du irgend welchen Output produzierst, den nvenc verstehen kann. (Falscher Farbraum...)

Wenn man sich aber das vorhergehende anguckt:

Code: Alles auswählen

Error while decoding stream #0:0: Invalid data found when processing input
[h264 @ 0x565039447500] No decoder surfaces left
Klingt das eher so, als ob dein nvdec nicht mit dem mkv zurecht kommt. Da du mit 2 verschiedenen Files probleme hast ist vermutlich irgend was mit dem nvdec kaputt... Probiere doch mal wirklich TOS Sintel oder BBB, um fehler im Ausgangsmaterial auszuschließen. Der NVDEC ist da empfindlich. Ich würde auf SW-Decoding umsteigen. IMHO den aufwand nicht wert.


Knogle hat geschrieben: ↑ zum Beitrag ↑
20.08.2021 14:06:36
Ich glaube VAAPI gibt es bei NVIDIA nicht oder?
Angeblich schon. ffmpeg hat die Combo aber nie supported.
rot: Moderator wanne spricht, default: User wanne spricht.

Antworten