نتیجہ ایسا لگ رہا تھا جیسے میں کسی نئے 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 کے لحاظ سے جان بوجھ کر مہنگا تھا: 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 ہر بار ہوتی ہے جب کوئی فائل مانگتا ہے۔
Real-time video میں تھوڑا bitrate بچانے کے لیے بہت زیادہ CPU خرچ کرنا بری trade ہو سکتی ہے۔ میری files offline encode ہوتی ہیں اور پھر بار بار serve ہوتی ہیں۔ اس model میں encoding کے دس منٹ بچانا معاشی طور پر بے معنی ہو سکتا ہے اگر تیز encode ہر آئندہ request کو بڑا کر دے۔
اسی لیے -preset veryslow میرے لیے مناسب ہے۔ اگر x264 ایک بہتر representation تلاش کرنے کے لیے CPU استعمال کر سکتا ہے تو میں یہ cost ایک بار دینے کو تیار ہوں۔ Browser encoder کا search دوبارہ نہیں چلاتا؛ وہ صرف تیار bitstream کو decode کرتا ہے۔
قاعدہ سادہ ہو گیا: جو قدم ایک بار ہوتا ہے وہاں computation خرچ کرو، اور جو قدم بار بار ہوتا ہے وہاں bytes کے معاملے میں سخت رہو۔
میں نئے codec کے پیچھے جانے کے بجائے H.264 پر کیوں رہا
میں یہ دعویٰ نہیں کر رہا کہ H.264 سب سے 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 کو list کرتی ہے اور Android 6.0 اور بعد کے لیے Main Profile decoder ضروری قرار دیتی ہے؛ اس کی playback recommendations میں 1280×720 at 30 fps بھی HD H.264 configuration کے طور پر شامل ہے۔ دیکھیں Android supported media formats۔
اس کا مطلب یہ نہیں کہ جدید devices صرف Main Profile یا Level 3.1 تک محدود ہیں۔ مثال کے طور پر Apple کی HLS guidance عام طور پر Main یا Baseline کے مقابلے میں High Profile کو ترجیح دیتی ہے۔ میں نے Main@3.1 اس لیے چنا کہ ایک static MP4 کے لیے جان بوجھ کر modest decoder envelope چاہیے تھا، نہ کہ اس لیے کہ Apple اسے لازم قرار دیتا ہے۔
میں نے وہ pixels encode کرنا چھوڑ دیے جن کی ضرورت ہی نہیں تھی
Resolution سب سے بڑے levers میں سے ایک تھا۔ میرا ceiling تقریباً landscape کے لیے 1280×720، portrait کے لیے 720×1280 اور square یا mixed-orientation content کے لیے تقریباً 960×960 بن گیا۔
زیادہ اہم اصول: صرف ceiling تک پہنچنے کے لیے upscale نہ کرو۔
اگر source 900×600 ہے تو اسے 1280×720 کرنا detail واپس نہیں لاتا۔ یہ صرف زیادہ samples بناتا ہے جنہیں encoder کو describe کرنا پڑتا ہے۔ 1920×1080 source کو 720p class تک کم کیا جا سکتا ہے، جبکہ 900×600 source تقریباً 900×600 رہ سکتا ہے۔ Ceiling maximum ہے، target نہیں۔
یہ سادہ لگتا ہے، مگر غیرضروری pixels ہٹانا بہت سے obscure encoder tweaks سے زیادہ فرق ڈال سکتا ہے۔
میں نے ان frames کے لیے bandwidth دینا چھوڑ دیا جو source میں واقعی تھے ہی نہیں
Frame rate بھی multiplier ہے۔ اگر animation میں تقریباً 16 useful visual states per second ہیں تو اسے 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 کو بھی ترجیح دیتا ہوں۔ VFR بذات خود خراب نہیں؛ CFR صرف میرے pipeline میں timestamps، frame counts، duration checks، seeking اور بعد کی validation کو آسان بناتا ہے۔
کسی ایک FPS number سے زیادہ اہم اصول یہ ہے: ایسی temporal information کے لیے bandwidth مت دو جو source میں موجود ہی نہیں۔
CRF 28 workload کا فیصلہ ہے، جادوئی نمبر نہیں
میں ہر clip کو ایک ہی target bitrate کی طرف force نہیں کرنا چاہتا تھا۔ تقریباً static illustration اور complex motion scene کو قابل قبول نظر آنے کے لیے ایک جتنے 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 پر blind copy نہیں کروں گا جہاں fidelity bandwidth سے زیادہ اہم ہو۔
میرے پاس کوئی universal perceptual score بھی نہیں جو ثابت کرے کہ CRF 28 transparent ہے۔ اپنے workload کے بارے میں میں صرف اتنا کہہ سکتا ہوں: files بہت چھوٹی ہوئیں اور عام playback میں مجھے اب بھی نارمل لگیں۔ یہ practical observation ہے، CRF 28 کو visually lossless کہنے کا دعویٰ نہیں۔
veryslow encoder کے لیے مہنگا ہے، decoder کے لیے لازماً نہیں
میرا preset -preset veryslow ہے۔ Slow preset x264 کو efficient prediction اور coding decisions تلاش کرنے کا زیادہ موقع دیتا ہے۔ قیمت encoding CPU اور وقت ہے۔
اہم فرق یہ ہے کہ 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 معمولی اور predictable رہے۔
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 کی recommendation دیتی ہے۔ میں اس HLS rule کو short static progressive MP4 files پر blind copy نہیں کرتا۔
میں تقریباً یہ بھی استعمال کرتا ہوں:
-maxrate:v 4M
-bufsize:v 8M
یہ unusual bitrate spikes کے خلاف ceiling ہیں۔ ان کا مطلب “ہر چیز کو 4 Mbps پر encode کرو” نہیں۔ Normal rate allocation کی ذمہ داری CRF کی رہتی ہے، اس لیے easy clips بہت چھوٹی ہو سکتی ہیں۔
MP4 container کو بھی جان بوجھ کر boring رکھا
میں واضح طور پر avc1 استعمال کرتا ہوں۔ Apple کی موجودہ HLS documentation avc3 کے بجائے avc1 جیسے sample formats کو ترجیح دیتی ہے۔ اس سے میری 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 بہتر نہیں کرتا۔ یہ 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 کا مرکزی حصہ تقریباً یوں ہے:
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 کو صرف اس لیے 30 fps پر force نہیں کرنا چاہیے کہ example میں -g 150 ہے۔
Command policy کا implementation ہے، policy خود نہیں۔
Files کئی گنا چھوٹی کیوں ہوئیں
کوئی ایک magic flag نہیں تھا۔
Reduction کئی فیصلوں کو جمع کرنے سے آئی، اور ہر decision نے مختلف waste ہٹایا: غیرضروری pixels، غیرضروری frames، fixed-bitrate سوچ، cheap encoder settings، ضرورت سے زیادہ frequent keyframes اور ایسے streams جن کی مجھے ضرورت نہیں تھی۔
اسی لیے “یہ file H.264 ہے” کہنا اس کے size کے بارے میں بہت کم بتاتا ہے۔ ایک ہی source کے دو H.264 encodes بہت مختلف ہو سکتے ہیں کیونکہ codec name resolution، frame rate، rate control، preset، GOP structure، profile یا source preparation نہیں بتاتا۔
میرے case میں codec کے اردگرد کے یہ فیصلے بدلنا codec بدلنے سے زیادہ اہم ثابت ہوا۔
یہ نتیجہ کیا ثابت نہیں کرتا
میں نے ہر setting کو controlled experiment میں الگ isolate نہیں کیا، اس لیے میں ایمانداری سے نہیں بتا سکتا کہ savings کا ٹھیک کتنے فیصد حصہ veryslow، CRF 28، resolution reduction یا frame-rate reduction سے آیا۔
میں یہ بھی نہیں کہہ سکتا کہ ہر CRF 28 output perceptually transparent ہے۔ “واضح quality drop نہیں تھا” اس illustrated workload کے لیے normal viewing sizes پر میری observation ہے، کسی بھی video کے لیے scientific guarantee نہیں۔
اور میں یہ نہیں کہہ رہا کہ single H.264 file ہر site کے لیے صحیح architecture ہے۔ Multiple renditions، adaptive streaming، HDR، 4K اور codec negotiation trade-offs بدل دیتے ہیں۔
نتیجہ اس سے محدود مگر زیادہ مفید ہے: short illustrated اور animated clips کی bandwidth-first، mobile-heavy library میں، جہاں encoding time سستا اور predictable playback اہم ہے، اس profile نے میری files کو کئی گنا چھوٹا کیا اور عام playback میں وہ نارمل دکھائی دیتی رہیں۔
اب میں جو اصول استعمال کرتا ہوں
پہلے video optimization کو بنیادی طور پر encoder-settings problem سمجھتا تھا۔ اب اسے lifetime-cost problem سمجھتا ہوں۔
Encoder شاید ایک بار چلے۔ Bytes network سے ہزاروں یا لاکھوں بار گزر سکتے ہیں۔
یہ “مہنگا” ہونے کا مطلب بدل دیتا ہے۔
میں CPU ایک بار خرچ کرنے میں خوش ہوں۔ میں اس بات پر کہیں کم راضی ہوں کہ ہر آئندہ request میں upscale سے بنے pixels، کوئی مفید motion نہ دینے والے frames یا content سے زیادہ bitrate بھیجتا رہوں۔
Codec boring رہا: MP4 میں H.264۔ Optimization اس کے اردگرد ہوئی۔
اس workload کے لیے سبق کسی ایک FFmpeg flag سے زیادہ واضح ہے: اس cost کو optimize کرو جو بار بار ادا کرتے ہو، نہ کہ اس cost کو جو صرف ایک بار ادا ہوتی ہے۔