Zurück zum Blog
13. August 2026Sergei Solod11 Min. Lesezeit

Wie ich ein Produktionsvideo mit H.264 von ~280 MB auf ~50 MB verkleinert habe

Ein konkretes Video aus dem Produktivbetrieb schrumpfte nach der überarbeiteten H.264-Konfiguration von ungefähr 280 MB auf ungefähr 50 MB. Die neue Strategie kombiniert CRF 28, x264 veryslow, eine Auflösungsobergrenze der 720p-Klasse, sinnvolle Bildraten und moderate Anforderungen an den Decoder. In einer früheren Stufe war dasselbe Beispiel bereits von etwa 350 auf 238 MB geschrumpft; eine Bestandsprüfung zeigte außerdem, dass H.264-Dateien mit mehreren Megabit pro Sekunde in der alten Bibliothek keineswegs selten waren.

H.264FFmpegx264VideokomprimierungWebseitenleistungMedienoptimierung

Der Wert, der diese Optimierung für mich endgültig real machte, war einfach: Ein konkretes Produktionsvideo schrumpfte nach der neuen H.264-Richtlinie von ungefähr 280 MB auf 50 MB. Das sind etwa 5,6× weniger beziehungsweise rund 230 MB oder 82% eingesparte Dateigröße.

Dafür wechselte ich nicht auf AV1, HEVC oder VP9. Die Auslieferung blieb H.264 in MP4. Geändert wurde die Richtlinie um den Codec: weniger unnötige Pixel, weniger überflüssige zeitliche Abtastwerte, ein deutlich weniger konservatives Qualitätsziel, mehr Rechenarbeit bei der einmaligen Kodierung und ein bewusst begrenzter Dekoder-Vertrag.

Der Anwendungsfall war sehr spezifisch: kurze illustrierte und animierte Inhalte, bei ungefähr 80% der Zugriffe von Mobilgeräten, Bandbreite als wiederkehrender Kostenfaktor und Vorab-Kodierung, bei der Rechenzeit billig ist im Vergleich dazu, jahrelang übergroße Dateien auszuliefern.

Vereinfacht sahen die alte und die neue Richtlinie so aus:

frühere Konfiguration
H.264 Main @ Level 4.0
CRF 19
Voreinstellung slow
bis 1920x1080 / 1080x1920
30 fps
refs = 3
B-Bilder = 3
GOP ≈ 2 Sekunden
VBV ≈ 10M / 20M

neue Konfiguration
H.264 Main @ Level 3.1
CRF 28
Voreinstellung veryslow
720p-Klasse als Obergrenze, keine Hochskalierung
sinnvolle CFR, normalerweise <= 30 fps
refs = 4
B-Bilder = 5
GOP ≈ 5 Sekunden
VBV ≈ 4M / 8M
yuv420p / avc1 / faststart

Das gemessene Ergebnis: aus ~280 MB wurden ~50 MB

Ich habe mehrere reale Produktionsmessungen. Sie stammen aber nicht alle aus demselben Versuch. Diese Kategorien sauber zu trennen ist wichtiger als die größtmögliche Prozentzahl zu finden.

MessungVorherNachherReduktion
Dasselbe konkrete Video~280 MB~50 MB~5,6× kleiner / ~82% weniger
Frühere Stufe derselben Datei~350 MB~238 MB~1,47× kleiner / 32% weniger

Die erste Zeile ist der sauberste Beleg für den Titel: dasselbe konkrete Video vor und nach der neueren Richtlinie, ungefähr 280 MB gegenüber ungefähr 50 MB. Für genau dieses Paar konnte ich in den wiedergefundenen alten Protokollen keine erhaltene Dauer-/Bitrate-Zeile finden. Deshalb erfinde ich keine. Die Größenänderung allein zeigt bereits einen Faktor von ungefähr 5,6.

Der Fall ~350→238 MB war eine frühere Optimierungsstufe desselben damaligen Beispiels. Das Ergebnis lag bei ungefähr 1264×720, 30 fps, rund 500 Sekunden, ohne Ton und etwa 3,8 Mbps. Die Rechnung passt: 3,8 Mbps über ungefähr 500 Sekunden ergeben etwa 238 MB. Gegenüber ~350 MB waren das bereits 32% weniger, aber für mein Bandbreitenziel immer noch zu groß.

Auch der alte Bestand zeigte, dass große H.264-Dateien kein einzelner Ausreißer waren. Eine Bestandsprüfung umfasste 238 Produktionsvideos mit zusammen 6,37 GB: 117 H.264 und 121 AV1. 101 Dateien waren mindestens 20 MB groß, 34 mindestens 50 MB. Einige große H.264-Dateien lagen bei:

GrößeDauerMittlere Bitrate
121,0 MB4:253,83 Mbps
101,2 MB5:192,659 Mbps
92,78 MB4:442,735 Mbps
90,10 MB3:553,206 Mbps
89,74 MB4:412,676 Mbps

Es handelt sich um unterschiedliche Inhalte; die Tabelle liefert daher Kontext und keinen A/B-Test. Sie zeigt aber, dass das alte Beispiel mit 3,8 Mbps kein Ausreißer war: Mehrere große H.264-Dateien der früheren Bibliothek lagen tatsächlich ungefähr zwischen 2,6 und 3,8 Mbps.

Die wichtigste Optimierung war kein FFmpeg-Schalter

Der entscheidende Wechsel betraf meine Kostenrechnung.

Kodierung passiert einmal. Die Übertragung der Datei passiert bei jedem Abruf.

Bei Echtzeitvideo kann es ein schlechter Tausch sein, viel mehr CPU für etwas weniger Bitrate zu verwenden. Meine Dateien werden dagegen vorab kodiert und anschließend immer wieder ausgeliefert. In diesem Modell ist es wirtschaftlich fast egal, bei der Kodierung zehn Minuten zu sparen, wenn die schnellere Kodierung jeden späteren Abruf vergrößert.

Deshalb ergibt -preset veryslow für mich Sinn. Ich bin bereit, einmal CPU zu bezahlen, wenn x264 dadurch eine effizientere Darstellung findet. Der Browser wiederholt diese aufwendige Suche nicht; er decodiert nur den fertigen Bitstream.

Meine Regel wurde: Rechenzeit beim einmaligen Schritt großzügig einsetzen und bei den wiederkehrenden Bytes geizig sein.

Warum ich bei H.264 blieb, statt dem neuesten Codec hinterherzulaufen

Ich behaupte nicht, dass H.264 heute die beste Kompression bietet. Neuere Codecs können sehr attraktiv sein, wenn die Auslieferungsarchitektur mehrere Varianten vorhalten und pro Endgerät auswählen kann.

Meine Anforderung war eine andere: eine URL, eine Datei, ein Codec und möglichst wenig Wiedergabeprobleme bei einem stark mobilen Publikum.

Für diesen Zweck bleibt H.264 in MP4 eine sehr sichere Grundlage. Apple empfiehlt Webentwicklern aktuell H.264-codierte MP4-Dateien für statische Videos in Safari. Android listet H.264 in MP4 und verlangt ab Android 6.0 einen Main-Profil-Dekoder; in den H.264-Wiedergabeempfehlungen werden für HD außerdem 1280×720 bei 30 fps genannt, mit dem Hinweis, dass HD nicht auf allen Geräten verfügbar ist. Siehe Androids unterstützte Medienformate.

Das bedeutet nicht, dass moderne Geräte auf Main-Profil oder Stufe 3.1 beschränkt wären. Apple bevorzugt für HLS beispielsweise grundsätzlich High-Profil gegenüber Main oder Baseline-Profil. Ich verwende Main@3.1, weil ich für eine einzelne statische MP4-Datei bewusst bescheidene Dekoder-Anforderungen wollte – nicht weil Apple dieses Profil verlangt.

Ich hörte auf, Pixel zu codieren, die ich nicht brauchte

Die Auflösung war einer der größten Hebel. Meine Obergrenze liegt ungefähr bei 1280×720 im Querformat, 720×1280 im Hochformat und etwa 960×960 für quadratisches oder gemischtes Material.

Wichtiger ist die Regel: nie nur deshalb hochskalieren, um diese Obergrenze zu erreichen.

Eine 900×600-Quelle auf 1280×720 zu vergrößern, erzeugt keine neuen Details. Sie erzeugt lediglich mehr Abtastwerte, die der Kodierer beschreiben muss. 1920×1080 kann ich auf 720p-Klasse reduzieren; 900×600 darf ungefähr 900×600 bleiben. Die Grenze ist ein Maximum, kein Ziel.

Das klingt banal, kann aber mehr sparen als viele exotische Codec-Feinabstimmungen.

Diese Obergrenze ist kein willkürliches rundes Maß. Ein Bild mit 1920×1080 enthält 2.073.600 Pixel, 1280×720 dagegen 921.600. Der Schritt von 1080p auf 720p entfernt also schon vor den eigentlichen Kompressionsentscheidungen ungefähr 55,6% der räumlichen Abtastwerte.

Auch 540p habe ich als allgemeine Vorgabe geprüft. 960×540 enthält jedoch nur 518.400 Pixel: 43,75% weniger als 1280×720, also nur noch 56,25% der 720p-Abtastwerte. Bei illustriertem Material beschreiben diese Werte feine Linien, Augen, Haare, Finger, Gesichter und harte Konturen. Wenn ich noch weniger Bytes brauche, teste ich lieber zuerst einen etwas höheren CRF, bevor ich pauschal weitere 43,75% der räumlichen Information verwerfe. Die Quantisierung kann ich bei der nächsten Kodierung ändern; durch Herunterskalieren entfernte Details sind bereits verloren.

Deshalb ist die 720p-Klasse meine vorsichtige allgemeine Obergrenze, nicht die Behauptung, 540p sei schlecht. Für eine konkrete Datei kann eine gemessene 540p-Fassung durchaus gewinnen. Ich mache diesen irreversiblen räumlichen Schnitt nur nicht ohne Daten zur globalen Regel.

Ich bezahle nicht mehr für Bilder, die in der Quelle gar nicht vorhanden waren

Auch die Bildrate multipliziert den Aufwand. Wenn eine Animation ungefähr 16 tatsächlich unterschiedliche visuelle Zustände pro Sekunde besitzt, macht eine Speicherung mit 30 oder 60 fps die Bewegung nicht automatisch besser. Häufig entstehen vor allem wiederholte oder synthetisierte zeitliche Abtastwerte, die trotzdem codiert werden müssen.

Meine Richtlinie ist, die sinnvolle natürliche Kadenz beizubehalten und normalerweise nicht über 30 fps zu gehen. Bei diesem Material können 12, 15, 16, 18, 20, 24, 25 oder 30 fps sinnvoll sein, wenn sie die Quelle tatsächlich beschreiben.

Für die erzeugte Ausgabe bevorzuge ich zudem eine saubere konstante Bildrate. VFR ist nicht grundsätzlich fehlerhaft; CFR vereinfacht in meiner Verarbeitungskette lediglich Zeitstempel, Bildzahlen, Dauerprüfungen, das Springen im Video und spätere Prüfung.

Das allgemeine Prinzip ist wichtiger als eine konkrete Zahl: keine Bandbreite für zeitliche Information bezahlen, die in der Quelle nicht vorhanden ist.

CRF 28 ist eine Anwendungsfall-Entscheidung, keine magische Zahl

Ich wollte nicht jedes Kurzvideo auf dieselbe Zielbitrate zwingen. Eine nahezu statische Illustration und eine Szene mit komplexer Bewegung benötigen unterschiedlich viele Bits, um akzeptabel auszusehen.

Deshalb nutze ich den CRF-Modus von x264 und landete für diesen bandbreitenorientierten illustrierten Anwendungsfall ungefähr bei -crf 28. FFmpeg dokumentiert CRF bei libx264 als qualitätskonstante Ratensteuerung; siehe die FFmpeg-Codec-Dokumentation.

CRF 28 ist bewusst aggressiv. Ich würde diesen Wert nicht blind auf Filmkorn, verrauschtes Kameramaterial, winzigen Text in Bildschirmaufnahmen oder einen Anwendungsfall übertragen, bei dem Bildtreue wichtiger als Bandbreite ist.

Ich habe auch keinen universellen Wahrnehmungswert, der beweist, dass CRF 28 transparent ist. Für meinen Anwendungsfall kann ich enger formulieren: Die Dateien wurden deutlich kleiner und sahen bei normaler Wiedergabe für mich weiterhin normal aus. Das ist eine praktische Beobachtung, keine Behauptung visueller Verlustfreiheit.

Veryslow ist teuer für den Kodierer, nicht automatisch für den Dekoder

Meine Voreinstellung ist -preset veryslow. Eine langsamere Voreinstellung gibt x264 mehr Gelegenheit, effizientere Vorhersage- und Kodierungsentscheidungen zu suchen. Der Preis ist Kodierzeit und CPU.

Wichtig ist: Kodierer-Aufwand und Dekoder-Komplexität sind nicht dasselbe.

Ich kann x264 sehr lange suchen lassen und den fertigen Bitstrom trotzdem separat begrenzen. Meine konservativen Ausgabevorgaben lauten:

H.264, Profil Main
Level 3.1
8-Bit yuv420p
avc1
refs = 4
B-Bilder = 5
B-pyramid = normal
offene GOP = deaktiviert

FFmpeg stellt CRF, Voreinstellungen, Feinabstimmung, Profilbeschränkungen, Referenzbilder und B-Bilder getrennt bereit. Genau so behandle ich sie: Der Kodierer darf aufwendig suchen, die Wiedergabeseite soll gewöhnlich bleiben.

GOP und VBV sind Leitplanken, nicht die eigentliche Qualitätssteuerung

Für diese kurzen progressiven Kurzvideos verwende ich einen maximalen GOP von ungefähr fünf Sekunden: etwa -g 150 bei 30 fps, -g 120 bei 24 fps oder -g 80 bei 16 fps.

Das ist eine Entscheidung für meinen Anwendungsfall, keine allgemeine Regel. Adaptive Übertragung hat andere Anforderungen; Apple empfiehlt für HLS beispielsweise IDRs alle zwei Sekunden. Diese HLS-Regel übernehme ich nicht blind für kurze statische progressive MP4-Dateien.

Zusätzlich nutze ich ungefähr:

-maxrate:v 4M
-bufsize:v 8M

Das sind Obergrenzen gegen ungewöhnliche Bitrate-Spitzen. Sie bedeuten nicht „alles mit 4 Mbps codieren“. CRF steuert weiterhin die normale Bitverteilung, daher dürfen einfache Kurzvideos sehr klein werden.

Auch den MP4-Container hielt ich bewusst konventionell

Ich verwende ausdrücklich avc1. Apples aktuelle HLS-Dokumentation empfiehlt Abtastformate wie avc1 statt avc3. Das verkleinert die Datei nicht wesentlich, passt aber zum Ziel eines konventionellen H.264 in MP4.

Außerdem nutze ich -movflags +faststart. Laut FFmpeg-Formatdokumentation verschiebt faststart den MP4-Index moov an den Dateianfang. Android verlangt für HTTP-Übertragung bei MPEG-4 ebenfalls, dass moov nach ftyp, aber vor mdat liegt.

ftyp
moov
mdat

Faststart verbessert nicht die Kompression. Es macht progressive HTTP-Wiedergabe unkomplizierter.

Für normales SDR nutze ich 8-bit yuv420p und signalisiere BT.709 mit begrenztem Videobereich. Wenn ein Kurzvideo keinen Ton braucht, erzeuge ich keinen Tonspur. Für illustriertes Material nutze ich zusätzlich -tune animation; das ist für mich eine inhaltsspezifische Wahl und kein Teil des universellen Kompatibilitätsvertrags.

Das zentrale FFmpeg-Profil

Für eine illustrierte 30-fps-Quelle sieht der Kern des Befehls ungefähr so aus:

ffmpeg -i input \
  -c:v libx264 \
  -preset veryslow \
  -tune animation \
  -crf 28 \
  -profile:v main \
  -level:v 3.1 \
  -pix_fmt yuv420p \
  -tag:v avc1 \
  -refs 4 \
  -bf 5 \
  -g 150 \
  -maxrate:v 4M \
  -bufsize:v 8M \
  -x264-params "open-gop=0:b-pyramid=normal:nal-hrd=none" \
  -color_range tv \
  -color_primaries bt709 \
  -color_trc bt709 \
  -colorspace bt709 \
  -an \
  -movflags +faststart \
  output.mp4

Skalierung und Bildrate sind hier absichtlich nicht fest verdrahtet. Eine 900×600-Quelle sollte nicht nur deshalb vergrößert werden, weil die Obergrenze 1280×720 beträgt, und eine Animation mit natürlich niedriger Bildrate sollte nicht auf 30 fps gezwungen werden, nur weil das Beispiel -g 150 verwendet.

Der Befehl implementiert die Richtlinie; er ist nicht selbst die Richtlinie.

Warum die Dateien um ein Mehrfaches kleiner wurden

Es gab keinen Wunder-Schalter.

Die Einsparung entstand durch mehrere Entscheidungen, die jeweils eine andere Art von Verschwendung entfernten: unnötige Pixel, unnötige Bilder, ein zu konservatives Qualitätsziel, auf Geschwindigkeit statt Effizienz ausgelegte Kodierer-Einstellungen, zu häufige Schlüsselbilder und Datenströme, die ich nicht brauchte.

Darum sagt „die Datei ist H.264“ erstaunlich wenig über ihre Größe. Zwei H.264-Kodierungen derselben Quelle können stark voneinander abweichen, weil der Codecname weder Auflösung noch Bildrate, Bitratensteuerung, Voreinstellung, GOP-Struktur, Profil oder Quellenaufbereitung beschreibt.

In meinem Fall waren diese Entscheidungen rund um den Codec wichtiger als ein Codecwechsel.

Was dieses Ergebnis nicht beweist

Ich habe nicht jede Einstellung in einem isolierten kontrollierten Versuch getestet. Deshalb kann ich keinen ehrlichen exakten Anteil der Einsparung veryslow, CRF 28, der geringeren Auflösung oder der reduzierten Bildrate einzeln zuschreiben.

Ich kann auch nicht behaupten, dass jede CRF-28-Ausgabe wahrnehmbar transparent ist. „Kein offensichtlicher Qualitätsverlust“ ist meine Beobachtung für diesen illustrierten Anwendungsfall bei normalen Darstellungsgrößen, keine wissenschaftliche Garantie für beliebiges Video.

Und ich behaupte nicht, dass eine einzelne H.264-Datei für jede Website die richtige Architektur ist. Mehrere Varianten, Adaptive Übertragung, HDR, 4K und Codec-Aushandlung verändern die Abwägungen.

Was ich tatsächlich sagen kann, ist enger gefasst: Für eine bandbreitenorientierte Bibliothek kurzer illustrierter und animierter Kurzvideos mit stark mobilem Publikum, bei der Kodierzeit billig und vorhersehbare Wiedergabe wichtig ist, machte dieses Profil meine Dateien um ein Mehrfaches kleiner, ohne dass die normale Wiedergabe sichtbar litt.

Wie dieses Ergebnis meine Optimierungsstrategie verändert hat

Früher betrachtete ich Videooptimierung hauptsächlich als Problem der Kodierer-Einstellungen. Heute betrachte ich sie als Lebenszyklus-Kostenproblem.

Der Kodierer läuft vielleicht einmal. Die Bytes können tausend- oder millionenfach durchs Netz gehen.

Damit bekommt „teuer“ eine andere Bedeutung.

Ich gebe gerne einmal CPU aus. Ich bin viel weniger bereit, bei jedem zukünftigen Abruf hochskalierte Pixel, Bilder ohne zusätzliche nützliche Bewegung oder unnötige Bitrate zu übertragen.

Der Codec blieb langweilig: H.264 in MP4. Die Optimierung fand darum herum statt.

Für meinen Anwendungsfall ist die wichtigste Regel klarer als jeder einzelne FFmpeg-Schalter: Optimiere die Kosten, die du immer wieder bezahlst, nicht die, die du einmal bezahlst.