ब्लॉग पर वापस जाएं
13 अगस्त 2026Sergei Solod10 मिनट पढ़ें

मैंने H.264 वीडियो को बिना साफ़ दिखने वाली क्वालिटी गिरावट के कई गुना छोटा कैसे किया

Codec वही H.264 रहा, लेकिन मैंने encoding policy को CRF 28, x264 veryslow, 720p-class resolution ceiling और केवल उपयोगी frame rate के आसपास दोबारा बनाया। फाइलें कई गुना छोटी हुईं, जबकि सामान्य playback में वीडियो सामान्य दिखता रहा। असली सीख यह थी कि encoding की लागत एक बार आती है, bandwidth की लागत हर view पर दोहरती है।

H.264FFmpegवीडियो कम्प्रेशनवेब परफ़ॉर्मेंसx264

नतीजा वैसा था जैसा मैं आम तौर पर किसी नए codec पर जाने से उम्मीद करता: मेरी video files कई गुना छोटी हो गईं, लेकिन सामान्य playback अब भी स्वाभाविक दिखता था और सामान्य viewing size पर मुझे कोई साफ़ quality drop नहीं दिखा।

लेकिन मैं AV1, HEVC या VP9 पर नहीं गया था। मैं अभी भी MP4 के अंदर H.264 इस्तेमाल कर रहा था।

बदला codec नहीं, उसके आसपास की पूरी encoding policy थी। मैंने इसे एक खास workload के लिए फिर से बनाया: छोटे illustrated और animated clips, mobile-heavy audience, बार-बार चुकाई जाने वाली मुख्य लागत bandwidth, और एक बार होने वाले offline encode में कितना समय लगे इसकी लगभग कोई चिंता नहीं।

जिस baseline पर मैं पहुँचा वह playback के लिए जानबूझकर conservative और encoding के लिए जानबूझकर expensive था: H.264 Main Profile @ Level 3.1, avc1, 8-bit yuv420p, CRF 28, x264 veryslow, 720p-class resolution ceiling, आम तौर पर 30 fps से अधिक नहीं उपयोगी frame rate, सीमित references और B-frames, और progressive MP4 delivery के लिए faststart

सबसे महत्वपूर्ण optimization कोई FFmpeg flag नहीं था

सबसे बड़ा बदलाव cost के बारे में मेरी सोच में था।

Encoding एक बार होती है। Delivery हर बार होती है जब कोई file request करता है।

Real-time video में थोड़ी bitrate बचाने के लिए बहुत ज्यादा CPU खर्च करना खराब trade हो सकता है। मेरी files offline encode होती हैं और फिर बार-बार serve होती हैं। इस model में encoding के दस मिनट बचाना आर्थिक रूप से बेकार हो सकता है, अगर तेज encode भविष्य के हर request को बड़ा बना देता है।

इसीलिए -preset veryslow मेरे लिए सही है। अगर x264 अधिक efficient representation खोजने के लिए CPU इस्तेमाल कर सकता है, तो मैं वह cost एक बार देने को तैयार हूँ। Browser encoder की search दोबारा नहीं करता; वह सिर्फ final bitstream decode करता है।

मेरी rule सरल हो गई: जो step एक बार होता है उसमें computation खर्च करो, और जो step बार-बार होता है उसमें bytes के मामले में कंजूस बनो

नए codec के पीछे भागने के बजाय मैं H.264 पर क्यों रहा

मैं यह दावा नहीं करता कि H.264 सबसे compression-efficient codec है। वह नहीं है। नए codecs तब आकर्षक हो सकते हैं जब delivery system कई renditions रख सके और हर client के लिए सही option चुन सके।

मेरी constraint अलग थी: एक URL, एक file, एक codec, और mobile-heavy audience पर जितना संभव हो उतना कम playback drama।

इस काम के लिए MP4 के अंदर H.264 अभी भी बहुत सुरक्षित baseline है। Apple फिलहाल web developers को Safari में static video के लिए H.264-encoded MP4 files इस्तेमाल करने की सलाह देता है। Android की मौजूदा documentation भी MP4 में H.264 support दिखाती है और Android 6.0 और बाद में Main Profile decoder आवश्यक बताती है; उसकी playback recommendation में 1280×720 at 30 fps को HD H.264 configuration भी कहा गया है। देखें Android supported media formats

इसका मतलब यह नहीं कि modern devices Main Profile या Level 3.1 तक सीमित हैं। उदाहरण के लिए Apple की HLS guidance आम तौर पर Main या Baseline से High Profile को prefer करती है। मैंने Main@3.1 इसलिए चुना क्योंकि single static MP4 के लिए जानबूझकर modest decoder envelope चाहिए था, न कि इसलिए कि Apple इसे require करता है।

मैंने उन pixels को encode करना बंद किया जिन्हें होना ही नहीं चाहिए था

Resolution सबसे बड़े levers में से एक था। मेरा ceiling landscape के लिए लगभग 1280×720, portrait के लिए 720×1280, और square या mixed-orientation content के लिए करीब 960×960 हो गया।

ज्यादा महत्वपूर्ण rule: सिर्फ ceiling तक पहुँचने के लिए upscale मत करो

अगर source 900×600 है तो उसे 1280×720 करने से detail वापस नहीं आती। इससे केवल encoder के लिए describe करने को अधिक samples बनते हैं। 1920×1080 source को 720p class तक reduce किया जा सकता है, जबकि 900×600 source लगभग 900×600 रह सकता है। Ceiling maximum है, target नहीं।

यह सरल लगता है, लेकिन unnecessary pixels हटाना कई obscure encoder tweaks से अधिक असर डाल सकता है।

Source में वास्तव में नहीं होने वाले frames के लिए bandwidth देना बंद किया

Frame rate भी multiplier है। अगर animation में प्रति सेकंड लगभग 16 useful visual states हैं, उसे 30 या 60 fps पर store करना अपने आप motion बेहतर नहीं करता। अक्सर यह बस repeated या synthesized temporal samples बढ़ाता है जिन्हें फिर भी represent करना पड़ता है।

मेरी policy source की useful cadence बनाए रखना और सामान्यतः 30 fps या उससे नीचे रहना है। इस तरह के material में 12, 15, 16, 18, 20, 24, 25 या 30 fps सभी सही हो सकते हैं, अगर वे वास्तव में source को describe करते हैं।

Generated output में मैं साफ CFR भी prefer करता हूँ। VFR अपने आप broken नहीं है; CFR मेरे pipeline में timestamps, frame counts, duration checks, seeking और बाद की validation को आसान बनाता है।

किसी एक FPS value से ज्यादा महत्वपूर्ण general principle है: ऐसी temporal information के लिए bandwidth मत दो जो source में है ही नहीं

CRF 28 workload का निर्णय है, magic number नहीं

मैं हर clip को एक ही target bitrate की ओर force नहीं करना चाहता था। लगभग static illustration और complex movement वाले scene को acceptable दिखने के लिए समान bits की जरूरत नहीं होती।

इसलिए x264 का CRF mode इस्तेमाल करता हूँ और इस bandwidth-first illustrated workload के लिए करीब -crf 28 पर पहुँचा। FFmpeg libx264 में CRF को constant-quality rate control के रूप में document करता है; देखें FFmpeg codec documentation

CRF 28 जानबूझकर aggressive है। Film grain, noisy camera footage, बहुत छोटे screen text या ऐसे workload पर जहाँ fidelity bandwidth से अधिक महत्वपूर्ण हो, इसे blindly copy नहीं करूँगा।

मेरे पास कोई universal perceptual score भी नहीं है जो prove करे कि CRF 28 transparent है। अपने workload के बारे में मैं सिर्फ इतना कह सकता हूँ: files बहुत छोटी हुईं और normal playback में मुझे अब भी सामान्य दिखीं। यह practical observation है, CRF 28 को visually lossless कहने का दावा नहीं।

veryslow encoder के लिए महँगा है, decoder के लिए अपने आप नहीं

मेरा preset -preset veryslow है। Slow preset x264 को efficient prediction और coding decisions खोजने का ज्यादा मौका देता है। कीमत encoding CPU और समय है।

महत्वपूर्ण distinction: encoder effort और decoder complexity एक चीज नहीं हैं

x264 को बहुत मेहनत करने दे सकता हूँ और final stream को अलग से constrain कर सकता हूँ। मेरा conservative output contract:

H.264 Main Profile
Level 3.1
8-bit yuv420p
avc1
refs = 4
B-frames = 5
B-pyramid = normal
open GOP = disabled

FFmpeg CRF, presets, tuning, profile restrictions, reference frames और B-frames को अलग expose करता है। मैं भी इन्हें वैसे ही देखता हूँ: encoder को कठिन search करने दो, playback side को साधारण रखो।

GOP और VBV guardrails हैं, मुख्य quality control नहीं

इन छोटे progressive clips के लिए maximum GOP लगभग पाँच seconds रखता हूँ: 30 fps पर करीब -g 150, 24 fps पर -g 120, और 16 fps पर -g 80

यह workload choice है, universal rule नहीं। Adaptive streaming की constraints अलग हैं; उदाहरण के लिए Apple HLS authoring guidance दो seconds में एक IDR recommend करती है। मैं उस HLS rule को short static progressive MP4 files पर blindly लागू नहीं करता।

मैं करीब ये values भी इस्तेमाल करता हूँ:

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

ये unusual bitrate spikes के खिलाफ ceiling हैं। इनका मतलब “हर चीज 4 Mbps पर encode करो” नहीं। Normal rate allocation CRF के पास रहता है, इसलिए easy clips बहुत छोटी हो सकती हैं।

MP4 container को भी जानबूझकर boring रखा

मैं explicit avc1 इस्तेमाल करता हूँ। Apple की current HLS documentation avc3 के बजाय avc1 जैसे sample formats prefer करती है। इससे files छोटी नहीं हुईं, लेकिन यह conventional H.264-in-MP4 output के लक्ष्य के अनुरूप है।

मैं -movflags +faststart भी इस्तेमाल करता हूँ। FFmpeg की format documentation बताती है कि faststart MP4 का moov index file की शुरुआत में ले जाता है। Android की HTTP streaming requirements भी MPEG-4 के लिए ftyp के बाद moov और फिर mdat चाहती हैं।

ftyp
moov
mdat

faststart compression improve नहीं करता। यह progressive HTTP playback को आसान बनाता है।

सामान्य SDR output के लिए 8-bit yuv420p इस्तेमाल करता हूँ और BT.709 limited/video range signal करता हूँ। Clip में audio नहीं है तो audio track नहीं बनाता। Illustrated content के लिए -tune animation भी इस्तेमाल करता हूँ; इसे content-specific choice मानता हूँ, universal compatibility contract नहीं।

मुख्य FFmpeg profile

30 fps illustrated source के लिए command का core हिस्सा लगभग ऐसा है:

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

Scaling और frame-rate stages जानबूझकर hard-code नहीं किए हैं। 900×600 source को सिर्फ इसलिए बड़ा नहीं करना चाहिए क्योंकि ceiling 1280×720 है, और naturally low-frame-rate animation को सिर्फ example में -g 150 होने के कारण 30 fps पर force नहीं करना चाहिए।

Command policy का implementation है, policy खुद नहीं।

Files कई गुना छोटी क्यों हुईं

कोई एक magic flag नहीं था।

Reduction कई decisions जोड़ने से आया, जिनमें हर एक ने अलग waste हटाया: unnecessary pixels, unnecessary frames, fixed-bitrate thinking, cheap encoder settings, जरूरत से ज्यादा frequent keyframes और ऐसे streams जिनकी मुझे जरूरत नहीं थी।

इसीलिए “यह file H.264 है” उसके size के बारे में आश्चर्यजनक रूप से कम बताता है। Same source के दो H.264 encodes बहुत अलग हो सकते हैं, क्योंकि codec name resolution, frame rate, rate control, preset, GOP structure, profile या source preparation नहीं बताता।

मेरे मामले में codec बदलने से ज्यादा महत्वपूर्ण उसके आसपास के ये decisions बदलना था।

यह result क्या prove नहीं करता

मैंने controlled experiment में हर setting को अलग isolate नहीं किया, इसलिए savings का exact प्रतिशत veryslow, CRF 28, resolution reduction या frame-rate reduction में से किससे आया, ईमानदारी से नहीं बता सकता।

यह भी नहीं कह सकता कि हर CRF 28 output perceptually transparent है। “साफ़ quality drop नहीं दिखा” normal viewing sizes पर इस illustrated workload के लिए मेरी observation है, arbitrary video के लिए scientific guarantee नहीं।

और मैं यह नहीं कह रहा कि single H.264 file हर site के लिए सही architecture है। Multiple renditions, adaptive streaming, HDR, 4K और codec negotiation trade-offs बदलते हैं।

Result इससे narrow और अधिक useful है: short illustrated और animated clips की bandwidth-first, mobile-heavy library में, जहाँ encoding time cheap और predictable playback महत्वपूर्ण है, इस profile ने files को कई गुना छोटा किया और normal playback में वे सामान्य दिखती रहीं।

अब मैं जो rule इस्तेमाल करता हूँ

पहले video optimization को मुख्यतः encoder-settings problem मानता था। अब इसे lifetime-cost problem मानता हूँ।

Encoder शायद एक बार चले। Bytes network से हजारों या लाखों बार गुजर सकती हैं।

इससे “expensive” का अर्थ बदल जाता है।

मैं CPU एक बार खर्च करने में खुश हूँ। लेकिन हर future request में upscale से बने pixels, उपयोगी motion न जोड़ने वाले frames या content से अधिक bitrate भेजने को बहुत कम तैयार हूँ।

Codec boring रहा: MP4 के अंदर H.264। Optimization उसके आसपास हुई।

इस workload के लिए lesson किसी individual FFmpeg flag से अधिक स्पष्ट है: उस cost को optimize करो जो बार-बार चुकाते हो, उस cost को नहीं जो एक बार चुकाते हो