Two-Pass-Encoding mit ffmpeg und x264
Einleitung
Ich wollte heute endlich mal ein paar Videos, die noch im veralteten MPEG-2-Format gespeichert sind in das viel effizientere Format h.264 komvertieren. Dabei wollte ich natürlich die Reibungsverluste, die bei einer solchen Konvertierung zwangsläufig entstehen, möglichst gering halten, weshalb ich unbedingt Two-Pass-Encoding verwenden wollte. Falls jemand nicht weiß, was das ist: Beim Two-Pass-Encoding wird der Umkodierungsprozess zwei mal durchlaufen: Beim 1. Mal merkt sich der Encoder, an welchen Stellen eine hohe Bitrate nötig ist (Szenen mit viel Bewegung oder Details) und wo er auch mit weniger auskommt, ohne dass Details verloren gehen. Erst im zweiten Durchlauf findet der eigentliche Konvertierungsprozess statt, bei dem dann die Informationen aus dem ersten Durchlauf verwendet werden.
Als Konvertierung-Programm wählte ich ffmpeg, von dem ich aufgrund seiner Vielzahl an Optionen und der großen Anzahl unterstützter Formate schon länger Fan bin.
Soweit so gut, allerdings stellte sich die Sache als etwas komplizierter heraus als geplant: Denn ausgerechnet mit x264, dem Encoder für h.264, harmoniert ffmpeg beim Two-Pass-Encoding nicht so wirklich. Die Probleme fangen damit an, dass x264 partout nicht das von ffmpeg erzeugte Log-File benutzen will, sondern stehts ein eigenes anlegt, was sich leider immer gleich heißt und sich nicht umbenennen lässt. Dieses Log-File ist aber nötig, denn gerade da werden die Statistiken des 1. Durchlaufes gespeichert. Solange man imemr nur ein Video zur selben Zeit konvertiert, ist das zwar kein Problem, schön ist es aber trotzdem nicht.
Gravierender ist schon eher, dass die von ffmpeg mitgelieferten "Presets" trotz entsprechender Namen wie "[...]_firstpass.ffpreset" nicht Two-Pass kompatibel sind, weil die Einstellungen für den 1. und den 2. Pass sich in bestimmten Kern-Einstellungen unterscheiden, die x264 gerne einheitlich hätte. Das führt dazu, dass der Encoder streikte und ich einige Stunden lang geflucht habe, weil die Ursache aufgrund einiger wenig aussagekräftiger, teils irreführenden Fehlermeldungen seitens ffmpeg und x264 nur schwer zu erkennen war.
Ebenfalls problematisch waren einige Anleitungen im Netz, die schlicht falsch oder nicht mehr aktuell waren oder mit der Windows-Version von ffmpeg nicht funktionierten. Das erste Problem waren die Presets. Im Netz findet man häufig folgendes:
ffmpeg -i "old_video.mpg" [...] -vpre fast "new_video.mkv"
Das funktioniert aber nicht. Jedenfalls nicht unter der aktuellen Version unter Windows. Als Meldung erhält man hierbei:
"File for preset 'fast' not found"Die Presets sind Dateien, die im Unterordner .\presets von ffmpeg liegen, daher war der naheliegenste Versuch, einen der Dateinamen in diesem Ordner als Wert für -vpre einzusetzen. Doch egal wie man es auch versucht, mit Dateiendung, ohne Dateiendung, mit absolutem Pfad, ohen absoluten Pfad... es geht nicht. Man muss nämlich eine andere Option verwenden. Und zwar -fpre in Verbdinung mit dem vollständigen Dateipfad des Presets. Richtig gehört, -fpre. Unterscheidet sich sogar um einen ganzen Buchstaben!
Die Fehlermeldungen
"Cannot read file 'ffmpeg2pass-2.log': No such file or directory"Diese Meldung war absolut irreführend, da diese Datei immer leer ist und vom Encoder überhaupt nicht verwendet wird. Diese Meldung hat - wie ich durch das Internet herausgefunden habe - nicht nur mich ganz schön in die Irre geleitet. Ich weiß mittlerweile allerdings schon gar nciht mehr, wodurch sie hervorgerufen wurde. Irgendwie bin ich sie losgeworden.
Dann der Satz, der heute wahrscheinlich am häufigsten auf meinem Monitor zu lesen war:
"Error while opening encoder for output stream #0.0"Diese Meldung ist immerhin nicht so irreführend, allerdings auch kaum aussagekräftig. Irreführend wiederum ist allerdings der Nachsatz:
"maybe incorrect parameters such as bit_rate, rate, width or height"Die Betonung liegt hierbei wohl auf "maybe", denn ich kann nun mit Sicherheit sagen, dass es mit keinem der aufgezählten Parameter zu tun hatte.
Etwas mehr Sinn ergab die Fehlermeldung weiter oben von x264, die mir zuerst gar nicht aufgefallen war:
"different b_pyramid setting than first pass (2 vs 0)"
Kryptischer geht es kaum, aber immerhin etwas, womit ich Google füttern konnte. Was eine "b_pyramid" ist, weiß ich jetzt zwra imemr noch nicht, aber immerhin weiß ich, dass man sie getrost abstellen kann. Wenn man nur wüsste wie! Denn ich hatte diesen Parameter ja nirgens angegeben. Eigentlich gab es jetzt nur noch zwei Möglichkeiten: 1. Die Angabe steckt in einer der verwendeten Presets, 2. Software-Bug. Nachdem ich mit Notepad++ die Presets nach dem Wort "b_pyramid" durchsucht hatte und nicht fündig wurde, war ich kurz davor, das Programm in die Ecke zu schmeißen. Doch dann viel mir doch noch etwas ins Auge. Und zwar das Wort... "bpyramid".
Ja, richtig, ohne Unterstrich. Warum? Das wissen wohl nur die Entwickler selbst. Ich kann nur vermuten, dass die Entwickler leichte sadistische Neigungen haben. Was auch erklären würde, warum sie das Programm lieber in C statt C++ entwickeln... wobei, das ist ja eigentlich eher Masochismus. Naja, vielleicht sind sie ja auch beides...
Übrigens gibt es auch andere Parameter, die völlig anders heißen. So gab es z.B. noch diese Fehlermeldung:
"different weightp setting than first pass (2 vs 0)"Im Preset-File heißt diese Eigenschaft aber völlig anders, nämlich: -wpredp. Wie ich das herausgefunden habe? Raten. Mein Glück, dass es nur wenige Parameter gab, die in den Preset-Files jeweils von 2 auf 0 wechselten.
Das Script
Jedenfalls habe ich es schlussendlich geschafft, und es ist ein relativ benutzbares Batch-Script dabei entstanden - ich will den ganzen Spaß ja nicht noch mal durchmachen müssen, wenn ich mal wieder was konvertieren will. Und da es sicherlich auch noch einige andere da draußen gibt, die am Two-Pass-Encoding verzweifeln, will ich euch das Script nicht vorenthalten:
@echo off REM set directory for log file set temp_dir=%temp%\ffmpeg_x264 set working_dir=%temp_dir%\%random% REM Change this to the path where your presets are located set preset_dir=c:\program files\ffmpeg\presets REM fetch parameters set input_file=%1 set output_file=%2 set pass_1_preset=%3 set pass_1_bit_rate=%4 set pass_1_bit_rate_tolerance=%5 set pass_2_preset=%6 set pass_2_bit_rate=%7 set pass_2_bit_rate_tolerance=%8 REM create temp working dir to workaround x264's bug md "%temp_dir%" md "%working_dir%" REM assuming the temp directory lies on C: - couldn't find a solution that REM extracts the drive letter from the file path automatically. You may have to REM adjust this. c: cd "%working_dir%" REM 1st pass @echo on ffmpeg -y -i "%input_file%" -an -pass 1 -vcodec libx264 -fpre &"%preset_dir%\%pass_1_preset%.ffpreset" -b %pass_1_bit_rate% -bt %pass_1_bit_rate_tolerance% -threads 4 -f matroska -flags2 -bpyramid -wpredp 2 NUL @echo off REM 2nd pass @echo on ffmpeg -i "%input_file%" -acodec copy -pass 2 -vcodec libx264 -fpre "%preset_dir%\%pass_2_preset%.ffpreset" -b %pass_2_bit_rate% -bt %pass_2_bit_rate_tolerance% -threads 4 -flags2 -bpyramid -wpredp 2 "%output_file%" @echo off REM remove temporary directory del /S /Q "%working_dir%" rd "%working_dir%" exitSpeichert den Code einfach als "ffmpeg_x264_twopass_encode.cmd" oder unter einem kürzeren Namen, falls ihr einen findet.
Beispiel:
Aufgerufen wird das Script beispielsweise so (bewegt den Curser über die unterstrichenen Teile für eine Erklärung):
ffmpeg_x264_twopass_encode
"d:\videos\old_file.mpg"
"d:\videos_new_file.mkv"
libx264-veryfast_firstpass
800k
500k
libx264-slow
800k
500k
Viel Spaß beim Konvertieren! Natürlich könnt ihr die Parameter des ffmpeg-Aufrufs im Script auch noch verändern, wenn ihr z.B. ein anderes Ausgabeformat als Matroska haben wollt, oder das Video vorher noch deinterlacen wollt. Die benötigten Parameter findet ihr in der Doumentation von ffmpeg.
Blog-Einträge abonnieren