Codec আর compression
Spatial আর temporal redundancy, I/P/B frame, GOP, motion estimation, transform আর quantisation — আর CBR/VBR/CRF-এর মধ্যে সিদ্ধান্তটা আসলে কীসের।
আগের চ্যাপ্টার একটা সংখ্যায় শেষ হয়েছিল: 1080p30-এ raw ভিডিও প্রায় ১.৫ Gbps, আর দর্শকের লাইনে আছে ২৫ Mbps। মানে প্রায় ৩০০:১ অনুপাতে ছোট করতে হবে। এত বড় অনুপাত কোনো সাধারণ compression দিয়ে হয় না — zip একটা ভিডিও ফাইলকে ২-৩ শতাংশও ছোট করতে পারে না, কারণ zip খোঁজে হুবহু পুনরাবৃত্তি, আর ছবিতে হুবহু পুনরাবৃত্তি প্রায় থাকেই না।
Video codec অন্য খেলা খেলে। সে lossless নয়, lossy — তথ্য ফেলে দেয়, এবং সচেতনভাবে ফেলে দেয়। কিন্তু কী ফেলবে তার পছন্দটা এলোমেলো নয়। প্রতিটা আধুনিক codec মূলত দুটো পর্যবেক্ষণের উপর দাঁড়িয়ে:
- Spatial redundancy — একটা frame-এর ভেতরে পাশাপাশি pixel-গুলো প্রায় একই রকম। আকাশের একটা অংশ দেখলে পাশের অংশটা প্রায় নিশ্চিতভাবে অনুমান করা যায়।
- Temporal redundancy — পরপর দুটো frame প্রায় অভিন্ন। ৩০fps-এ পরপর দুই frame-এর মধ্যে ব্যবধান ৩৩ মিলিসেকেন্ড; ওই সময়ে দৃশ্যের ৯৫ শতাংশ একই থাকে।
তার সাথে তৃতীয় একটা জিনিস — perceptual redundancy, অর্থাৎ চোখ যা লক্ষই করে না। এই তিনটে মিলিয়েই ৩০০:১।
গল্পে বুঝি
বাগদাদের একটা নকল-শালা। উস্তাদ আল-কিন্দির অধীনে ত্রিশজন লিপিকার বসে দিনরাত পাণ্ডুলিপি নকল করে। সবচেয়ে বড় কাজটা এসেছে এবার — “তারিখুল উমাম”, নয়শো পাতার একটা চিত্রিত ইতিহাসগ্রন্থ, যার বারোটা কপি লাগবে বারোটা শহরের গ্রন্থাগারের জন্য। হিসাব করে আল-কিন্দি দেখলেন, সব লিপিকারকে বছরের পর বছর খাটালেও কাগজ, কালি আর সময়ের খরচে দরবার রাজি হবে না।
তাই তিনি পাতাগুলো নিজে উল্টে দেখতে বসলেন — আর একটা জিনিস তাঁর চোখে পড়ল। বইটার বড় একটা অংশ যুদ্ধের বর্ণনা, যেখানে পাতার পর পাতা একই সেনাবাহিনীর ছবি, শুধু ঘোড়াগুলো একটু করে এগিয়েছে। ৪৭ নম্বর পাতা আর ৪৮ নম্বর পাতা পাশাপাশি ধরলে দেখা যায় — আকাশ এক, তাঁবু এক, পাহাড় এক, শুধু সামনের সারির ঘোড়াগুলো এক আঙুল ডানে সরেছে।
আল-কিন্দি নিয়ম বদলে দিলেন। এখন থেকে লিপিকাররা তিন ধরনের পাতা বানাবে।
পূর্ণ পাতা — গোটা ছবি শুরু থেকে আঁকা, আগের কোনো পাতার দিকে না তাকিয়ে। এগুলো দামি, সময়সাপেক্ষ, কিন্তু স্বয়ংসম্পূর্ণ।
পার্থক্য-পাতা — এখানে লিপিকার লেখে না “কী আছে”, লেখে “আগেরটার থেকে কী বদলেছে”। যেমন: “৪৭ নম্বর পাতার মতোই, তবে ডান দিকের ঘোড়ার দলটা এক আঙুল ডানে আর আধ আঙুল নিচে সরেছে, আর তাঁবুর ছায়া একটু গাঢ়।” পুরো একটা যুদ্ধদৃশ্য এইভাবে তিন লাইনে লেখা হয়ে যায়।
আর দুই-দিকের পাতা — সবচেয়ে চতুর জিনিস। এক লিপিকার আবিষ্কার করল, একটা ঘোড়া যদি ৪৭ নম্বরে বাঁয়ে আর ৫০ নম্বরে ডানে থাকে, তাহলে ৪৮ আর ৪৯ নম্বর পাতা দুটো আগের আর পরের দুটোর দিকে তাকিয়েই সবচেয়ে সংক্ষেপে লেখা যায় — “৪৭ আর ৫০-এর মাঝামাঝি”। এতে লেখা আরও ছোট হয়। কিন্তু এর দাম আছে: কর্মশালায় এখন ৫০ নম্বর পাতা ৪৮ নম্বরের আগে আঁকতে হবে, কারণ ৪৮ আঁকতে ৫০ লাগবে। আঁকার ক্রম আর বইয়ের ক্রম আলাদা হয়ে গেল।
এখানেই আরেকটা সমস্যা এল। এক গ্রন্থাগারিক ফাতিমা আল-ফিহরি চিঠি লিখলেন — “আমার পাঠক সবসময় শুরু থেকে পড়ে না, অনেকে মাঝখান থেকে খুলে পড়তে চায়। কিন্তু আপনার ৪৮০ নম্বর পাতা বুঝতে গেলে ৪৭৯ লাগে, তার জন্য ৪৭৮ লাগে — শেষমেশ প্রথম পাতা থেকে শুরু করতে হচ্ছে।” আল-কিন্দি নিয়ম যোগ করলেন: প্রতি ষাট পাতা পর পর একটা পূর্ণ পাতা বসবে, যতই অপচয় মনে হোক। এখন পাঠক যেকোনো জায়গা থেকে শুরু করলে সবচেয়ে বেশি ষাট পাতা পিছিয়ে একটা পূর্ণ পাতা পাবে। বইটা একটু মোটা হলো, কিন্তু ব্যবহারযোগ্য হলো।
আর কালি-বাজেট? দরবার বলল, “প্রতিটা কপিতে এত রতি কালির বেশি খরচ করা যাবে না।” আল-কিন্দি দুই ভাবে এটা সামলাতে পারতেন। এক, প্রতিটা পাতায় ঠিক সমান পরিমাণ কালি বরাদ্দ করা — এতে সাদামাটা মরুভূমির পাতায় কালি নষ্ট হয়, আর ভিড়ে ঠাসা বাজারের পাতায় ছবি জঘন্য হয়। দুই, প্রতিটা পাতায় সমান মান বজায় রাখা — সহজ পাতায় কম কালি, কঠিন পাতায় বেশি, মোট খরচ যা হওয়ার হবে। আল-কিন্দি দ্বিতীয়টাই বেছে নিলেন, আর দরবারকে বললেন — “আমি মান ঠিক করে দিচ্ছি, খরচটা তার ফল। উল্টোটা করলে আপনি খরচ পাবেন, কিন্তু মান পাবেন না।”
শেষে গুণমানের কাঁটা। ঠিক কতটা সূক্ষ্মভাবে আঁকা হবে? আল-কিন্দি একটা কাঁটা বানালেন, এক থেকে একান্ন। কাঁটা নিচের দিকে রাখলে লিপিকার চুলের প্রতিটা রেখা আঁকে; উপরে তুললে সে ছায়াগুলোকে বড় বড় চাপে সরল করে ফেলে। কাঁটা যত উপরে, কাগজ তত কম, ছবি তত রুক্ষ। গুরুত্বপূর্ণ কথা — এটা একটা কাঁটা, কোনো ধ্রুবক নয়। বইয়ের কোন কপি কোথায় যাচ্ছে তার উপর কাঁটার অবস্থান বদলায়।
মিলিয়ে নিই: পূর্ণ পাতা হলো I-frame (বা keyframe), আগেরটা দেখে লেখা পার্থক্য-পাতা হলো P-frame, আগে-পরে দুদিক দেখে লেখা পাতা হলো B-frame, “ঘোড়ার দল এক আঙুল ডানে সরেছে” — ওই সরণটাই motion vector আর সেটা খুঁজে বের করার কাজটাই motion estimation, যা মিলল না সেই বাকিটুকু হলো residual, প্রতি ষাট পাতায় একটা পূর্ণ পাতা হলো GOP আর তার শুরুর পাতাটা keyframe, মাঝখান থেকে খোলার দরকারটাই random access, আঁকার ক্রম আর বইয়ের ক্রমের পার্থক্য হলো decode order বনাম display order (অর্থাৎ DTS আর PTS), সমান-কালি নীতি হলো CBR, সমান-মান নীতি হলো CRF, আর এক-থেকে-একান্ন কাঁটাটা হলো quantisation parameter — ভিডিও compression-এর কেন্দ্রীয় নব।
Codec আসলে কী
“Codec” শব্দটা coder + decoder। এটা একটা ফাইল ফরম্যাট নয়, একটা অ্যালগরিদম — কীভাবে pixel-কে bit-এ পরিণত করা হবে এবং সেই bit থেকে আবার pixel-এ ফেরা হবে, তার নিয়মাবলি।
গুরুত্বপূর্ণ একটা কথা এখানেই বলে রাখা দরকার, কারণ এটা পরের চ্যাপ্টারের পুরো ভিত্তি: H.264 হলো codec, MP4 হলো container। একটা .mp4 ফাইলের ভেতরে H.264 থাকতে পারে, HEVC থাকতে পারে, AV1-ও থাকতে পারে। ফাইলের এক্সটেনশন দেখে codec বলা যায় না।
আরেকটা জিনিস: codec স্পেসিফিকেশন শুধু decoder-কে সংজ্ঞায়িত করে। স্ট্যান্ডার্ডে লেখা থাকে “এই bitstream পেলে decoder ঠিক এই pixel-গুলো বানাবে”। Encoder কীভাবে ওই bitstream বানাবে, সেটা স্ট্যান্ডার্ডে লেখা নেই। এই কারণেই x264 আর একটা সস্তা হার্ডওয়্যার encoder — দুটোই বৈধ H.264 তৈরি করে, কিন্তু একই bitrate-এ মান আকাশ-পাতাল আলাদা। Codec একটা, encoder অনেক।
Bitstream আসলে দেখতে কেমন
Encoder-এর আউটপুট একটানা byte-এর স্রোত নয়। সেটা NAL unit (network abstraction layer unit) নামের ছোট ছোট প্যাকেটের ক্রম, আর প্রতিটার একটা টাইপ আছে:
NAL type name what it carries
----------------------------------------------------------
7 SPS sequence parameter set: resolution, profile,
level, chroma format, frame-rate hints
8 PPS picture parameter set: entropy mode, QP defaults
5 IDR an I-frame that resets all references
1 non-IDR a P or B slice
6 SEI supplemental info: HDR metadata, timecode,
closed captions দুটো জিনিস এখানে ব্যবহারিকভাবে গুরুত্বপূর্ণ।
প্রথমত, SPS আর PPS ছাড়া কোনো decoder কিছুই করতে পারে না — এগুলো ছবি নয়, ছবি পড়ার নিয়ম। Live স্ট্রিমে এগুলো বারবার পাঠাতে হয়, নইলে মাঝপথে যোগ দেওয়া দর্শক কালো পর্দা দেখবে। “স্ট্রিম চলছে, কিন্তু নতুন দর্শক কিছু দেখছে না” — এই বাগটার এক নম্বর কারণ এটাই।
দ্বিতীয়ত, IDR আর সাধারণ I-frame এক জিনিস নয়। IDR মানে “এখান থেকে আগের সব reference ভুলে যাও” — একটা সম্পূর্ণ পুনরারম্ভ। একটা সাধারণ I-frame স্বয়ংসম্পূর্ণ হলেও তার পরের frame-গুলো তার আগের frame-এর দিকে তাকাতে পারে। Segment কাটার সময় আপনার দরকার IDR, শুধু I-frame নয় — আর এই পার্থক্যটাই এমন সব ব্যাখ্যাতীত playback বাগের কারণ যেখানে segment “দেখতে ঠিক” কিন্তু চলে না।
এই NAL unit-গুলো container-এ কীভাবে লেখা হবে তার আবার দুটো রীতি আছে — এবং সেটাই পরের চ্যাপ্টারের সবচেয়ে বড় ফাঁদ।
Spatial compression: একটা frame-এর ভেতরে
একটা I-frame-এ কোনো আগের frame নেই, তাই সব কাজ frame-এর ভেতরেই করতে হয়। ধাপগুলো:
১. Block-এ ভাগ। Frame-কে চৌকো ব্লকে ভাগ করা হয় — H.264-এ 16×16 macroblock, HEVC-তে 64×64 পর্যন্ত CTU যা গাছের মতো ছোট হতে পারে, AV1-এ 128×128 পর্যন্ত superblock। বড় সমতল এলাকা (আকাশ) বড় ব্লকে, খুঁটিনাটি এলাকা (মুখ, লেখা) ছোট ব্লকে — এই মানিয়ে নেওয়ার ক্ষমতাটাই নতুন codec-গুলোর একটা বড় লাভ।
২. Intra prediction। প্রতিটা ব্লকের জন্য codec আগে অনুমান করে, তার বাঁ আর উপরের ইতিমধ্যে decode হওয়া pixel থেকে। “এই ব্লকটা উপরের সারির মানগুলো নিচে টেনে দিলেই হয়ে যাবে” — এমন কয়েক ডজন দিকনির্দেশ থেকে সবচেয়ে ভালোটা বাছা হয়। তারপর সংরক্ষণ করা হয় শুধু কোন দিকনির্দেশ ব্যবহার হয়েছে, আর অনুমান থেকে আসল ছবির পার্থক্য।
৩. Transform। এই পার্থক্যটা (residual) DCT-জাতীয় একটা রূপান্তরে পাঠানো হয়। এতে ব্লকের তথ্য pixel থেকে frequency-তে বদলে যায় — উপরের-বাঁ কোণে থাকে “গড় উজ্জ্বলতা” জাতীয় নিম্ন-কম্পাঙ্ক তথ্য, নিচের-ডানে থাকে সূক্ষ্ম খুঁটিনাটি। এই ধাপে এক bit-ও হারায় না, কিন্তু তথ্যটা এমনভাবে সাজানো হয় যেখান থেকে ফেলে দেওয়া সহজ।
৪. Quantisation — এখানেই তথ্য হারায়। প্রতিটা frequency coefficient-কে একটা ধাপ-আকার দিয়ে ভাগ করে পূর্ণসংখ্যায় নামানো হয়। ধাপ যত বড়, তত বেশি coefficient শূন্য হয়ে যায়। আর যেহেতু চোখ সূক্ষ্ম খুঁটিনাটির চেয়ে বড় আকৃতিতে অনেক বেশি সংবেদনশীল, উচ্চ-কম্পাঙ্কের coefficient-গুলোকেই সবার আগে মেরে ফেলা হয়।
one 8x8 residual block, after transform (illustrative values)
-412 61 -18 5 -2 1 0 0
47 -22 9 -3 1 0 0 0
-13 8 -4 2 0 0 0 0
6 -3 1 0 0 0 0 0
-2 1 0 0 0 0 0 0
1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
after quantisation with a coarse step (high QP)
-26 4 -1 0 0 0 0 0
3 -1 0 0 0 0 0 0
-1 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0
64 numbers -> 6 non-zero numbers, then run-length + entropy coded ৫. Entropy coding। বাকি সংখ্যাগুলো zigzag ক্রমে সাজিয়ে (যাতে শূন্যগুলো একসাথে আসে) run-length এবং তারপর CABAC বা arithmetic coding দিয়ে চেপে দেওয়া হয়। এই ধাপটা lossless — শুধু যা আছে তাকেই কম bit-এ লেখা।
ধাপ ১, ২, ৩ আর ৫ কোনো তথ্য নষ্ট করে না। শুধু quantisation করে। ভিডিও compression-এ “মান” নিয়ে যত কথা, তার প্রায় সবটাই আসলে এই একটা ধাপের ধাপ-আকার নিয়ে কথা। এই ধাপ-আকারই QP (quantisation parameter)।
Quantisation বেশি হলে যেসব artifact দেখা যায়, সেগুলোরও নাম আছে, আর প্রতিটার কারণ উপরের প্রক্রিয়ার ভেতরেই:
- Blocking — ব্লকের কিনারা চোখে পড়ে, কারণ পাশাপাশি দুই ব্লক আলাদাভাবে quantise হয়েছে।
- Ringing / mosquito noise — ধারালো কিনারার চারপাশে কাঁপা কাঁপা ভাব, কারণ উচ্চ-কম্পাঙ্ক coefficient কেটে ফেলা হয়েছে।
- Banding — মসৃণ gradient ধাপে ধাপে ভাগ হয়ে যায়, কারণ মাঝামাঝি মানগুলো একই মানে গিয়ে ঠেকেছে।
In-loop filter: artifact-কে ছড়াতে না দেওয়া
Blocking artifact-এর একটা বাড়তি বিপদ আছে। Encoder পরের frame-গুলোর reference হিসেবে যে ছবিটা ব্যবহার করে, সেটা মূল উৎস নয় — decode হওয়ার পরের সংস্করণ। মানে একটা ব্লকের কিনারায় তৈরি হওয়া দাগ পরের frame-এ prediction-এর ভিত্তি হয়ে যায়, আর GOP জুড়ে সেটা জমতে থাকে।
তাই আধুনিক codec quantisation-এর পর একটা in-loop deblocking filter চালায় — ব্লকের সীমানায় মানগুলো নরম করে দেয়, আর সেই সংশোধিত ছবিটাই reference হিসেবে রাখে। “In-loop” মানে encoder আর decoder দুজনেই ঠিক একই filter চালায়, নইলে দুজনের reference আলাদা হয়ে যাবে।
HEVC এর সাথে যোগ করেছে SAO (sample adaptive offset), আর AV1 যোগ করেছে CDEF আর একটা loop restoration ধাপ। এগুলো কেবল “সুন্দর দেখানোর” কাজ নয় — এগুলো reference-এর মান ধরে রেখে পরের সব frame-এর compression ভালো করে।
RDO: encoder আসলে কীভাবে সিদ্ধান্ত নেয়
একটা ব্লকের জন্য encoder-এর হাতে অসংখ্য পছন্দ থাকে — কোন আকারে ভাগ করবে, intra না inter, কোন prediction mode, কোন reference, কোন motion vector। সে বাছে কীভাবে?
উত্তর হলো RDO (rate-distortion optimisation)। প্রতিটা প্রার্থীর জন্য সে দুটো জিনিস মাপে: এতে কত bit লাগবে (rate, R) আর মূল ছবির থেকে কতটা সরে গেল (distortion, D)। তারপর একটা যৌথ খরচ হিসাব করে:
cost = D + lambda * R
D distortion, e.g. sum of squared differences from the source
R bits this choice would consume
lambda the exchange rate between quality and bits, derived from QP যে প্রার্থীর cost সবচেয়ে কম, সেটাই জেতে। lambda আসে QP থেকে — উঁচু QP মানে bit দামি, তাই encoder সস্তা কিন্তু কম নিখুঁত পছন্দগুলোর দিকে ঝোঁকে; নিচু QP মানে bit সস্তা, তাই সে খুঁটিনাটি ধরে রাখার পছন্দ নেয়।
এই একটামাত্র সমীকরণ থেকে অনেক কিছু ব্যাখ্যা হয়ে যায়:
- Encoder preset আসলে কী —
veryslowমানে সে বেশি প্রার্থী পরীক্ষা করে,ultrafastমানে কম। একই codec, একই QP, শুধু খোঁজার পরিসর ভিন্ন। - কেন একই codec-এ encoder ভেদে মান আলাদা — RDO কতটা ভালো করে করা হচ্ছে, সেটাই মূল পার্থক্য।
- কেন hardware encoder পিছিয়ে থাকে — সিলিকনে বাঁধা বর্তনী সীমিত সংখ্যক প্রার্থীই দেখতে পারে।
Temporal compression: frame-এর মাঝখানে
এখানেই আসল লাভ। একটা I-frame যদি ১০০ KB হয়, একটা P-frame হতে পারে ৫ KB, একটা B-frame ২ KB। মানে ৯০ শতাংশ সাশ্রয় আসে temporal দিক থেকে।
তিন ধরনের frame:
| Frame | কী থেকে predict করে | আপেক্ষিক আকার | নষ্ট হলে যা হয় |
|---|---|---|---|
| I | কিছুই না, স্বয়ংসম্পূর্ণ | ১০০% | পুরো GOP অচল |
| P | আগের frame | ~৫-১৫% | তার পরের frame-গুলো ভাঙে |
| B | আগের ও পরের frame | ~২-৫% | শুধু ওই frame-টাই ভাঙে |
Motion estimation হলো সেই প্রক্রিয়া যেখানে encoder বর্তমান ব্লকটার সবচেয়ে কাছাকাছি মিল খোঁজে reference frame-এর আশেপাশে। সে একটা search window-র ভেতর ঘুরে ঘুরে দেখে, প্রতিটা প্রার্থীর সাথে পার্থক্য মাপে, আর সবচেয়ে কম পার্থক্যেরটা বেছে নেয়। ফলাফল দুটো জিনিস: একটা motion vector (কত pixel ডানে, কত pixel নিচে) আর একটা residual (মিলের পরও যা বাকি থাকল)।
এই খোঁজাটাই encoder-এর সময়ের সিংহভাগ খেয়ে ফেলে। Encoder preset (ultrafast থেকে veryslow) আসলে মূলত এই খোঁজাটা কত বড় এলাকায় এবং কত সূক্ষ্মভাবে হবে, সেটাই ঠিক করে।
motion search, conceptually
reference frame current frame
+-------------------+ +-------------------+
| . . . . . . . | | . . . . . . . |
| . [HORSE] . . | | . . [HORSE] . |
| . . . . . . . | | . . . . . . . |
+-------------------+ +-------------------+
best match found at offset (+12, -3) with 1/4-pixel precision
stored cost: 2 small numbers + a tiny residual
naive cost: the entire block, from scratch আধুনিক codec এই মূল ধারণাটাকে অনেক দূর নিয়ে গেছে: এক-চতুর্থাংশ বা এক-অষ্টমাংশ pixel নির্ভুলতা, একটা ব্লকের জন্য একাধিক reference frame, ব্লককে অসম আকারে ভাগ করে আলাদা vector, আর AV1-এ warped motion আর global motion — যেখানে ক্যামেরার প্যান বা জুমকে গোটা frame-এর একটা রূপান্তর হিসেবে ধরা হয়।
একাধিক reference আর hierarchical B-frame
পুরনো codec-এ একটা P-frame শুধু ঠিক আগের frame-এর দিকে তাকাতে পারত। আধুনিক codec একসাথে কয়েকটা frame reference হিসেবে ধরে রাখে, আর প্রতিটা ব্লক আলাদাভাবে বেছে নিতে পারে সে কোনটার দিকে তাকাবে।
এটা কেন লাভজনক, একটা উদাহরণেই পরিষ্কার: একজন বক্তার সামনে দিয়ে কেউ হেঁটে গেল। যে ব্লকগুলো ঢাকা পড়েছিল, সেগুলো আবার বেরিয়ে আসার পর ঠিক-আগের frame-এ নেই — কিন্তু তিন frame আগে ঠিকই ছিল। একাধিক reference থাকলে encoder সেখান থেকেই তুলে নেয়, নইলে তাকে ওই এলাকাটা নতুন করে intra-coded করতে হতো।
আরও কার্যকর একটা কৌশল হলো hierarchical B-frame বা B-pyramid, যেখানে কিছু B-frame নিজেরাই অন্য B-frame-এর reference হয়:
flat B-frames (older style)
I0 B B P3 B B P6
every B is a dead end; nothing predicts from it
hierarchical B-frames (pyramid)
I0 ................... P8 layer 0: reference frames
B4 layer 1: references I0 and P8, is itself a reference
B2 B6 layer 2: reference layer 0 and 1
B1 B3 B5 B7 layer 3: disposable, cheapest frames উপরের স্তরের frame-গুলো কাছাকাছি reference পায়, তাই তাদের residual খুবই ছোট। বাস্তবে একই মানে ১০-১৫ শতাংশ bitrate সাশ্রয় হয়। আর সবচেয়ে নিচের স্তরের frame-গুলো disposable — কেউ তাদের উপর নির্ভর করে না, তাই সেগুলো বাদ দিয়ে দিলেও stream চলে। এই বৈশিষ্ট্যটাই temporal scalability-র ভিত্তি: একই stream থেকে ৬০fps বা ৩০fps বের করা যায়, শুধু নিচের স্তরটা ফেলে দিয়ে।
GOP, keyframe আর random access
GOP (Group of Pictures) হলো এক I-frame থেকে পরের I-frame পর্যন্ত frame-গুলোর দল। GOP দৈর্ঘ্য একটা সরাসরি trade-off:
- লম্বা GOP — কম I-frame, তাই কম bitrate-এ বেশি ভালো মান।
- ছোট GOP — বেশি I-frame, তাই বেশি জায়গা, কিন্তু বেশি ঘনঘন seek পয়েন্ট আর error-এর দ্রুত পুনরুদ্ধার।
Streaming-এ এই সিদ্ধান্তটা আপনার হাতে থাকে না — segment-এর দৈর্ঘ্যই GOP-এর দৈর্ঘ্য ঠিক করে দেয়। প্রতিটা segment একটা keyframe দিয়ে শুরু হতেই হবে, নইলে player মাঝখান থেকে ওই segment চালাতে পারবে না। ৪ সেকেন্ডের segment মানে ৪ সেকেন্ডের (বা তার ভাগশেষ) GOP।
আরও গুরুত্বপূর্ণ: একটা ladder-এর সব rendition-এ keyframe ঠিক একই টাইমস্ট্যাম্পে পড়তে হবে। নইলে 1080p থেকে 480p-তে সুইচ করার সময় player-এর হাতে কোনো সাধারণ সীমানা থাকে না, আর সুইচে হয় ঝাঁকুনি হয়, নয় ফাঁকা যায়। এই কারণেই ladder-এর প্রতিটা encode-এ keyframe interval জোর করে বসানো হয়, দৃশ্য অনুযায়ী নয়।
Encoder-এর “scene-cut detection” নিজে থেকে দৃশ্য বদলের জায়গায় keyframe বসাতে চায় — যা মানের জন্য চমৎকার, কিন্তু ladder-এর সব rendition-এ ভিন্ন ভিন্ন জায়গায় পড়তে পারে। ABR-এর জন্য encode করার সময় fixed GOP জোর করে বসান (-g আর -force_key_frames), আর দৃশ্য-ভিত্তিক keyframe হয় বন্ধ রাখুন, নয়তো নিশ্চিত করুন সব rendition-এ ইনপুট ও সিদ্ধান্ত অভিন্ন।
Closed GOP বনাম open GOP: closed GOP-এ কোনো frame তার GOP-এর বাইরে তাকায় না — তাই GOP-টা কেটে আলাদা করে দিলেও চলে। Open GOP-এ শেষের দিকের B-frame পরের GOP-এর I-frame-এর দিকে তাকাতে পারে, যা একটু ভালো compression দেয় কিন্তু segment কাটাকে অনির্ভরযোগ্য করে তোলে। Streaming-এ সবসময় closed GOP।
Decode order আর display order
B-frame-এর দাম হলো ক্রমের গোলমাল — গল্পের সেই “৫০ নম্বর পাতা ৪৮-এর আগে আঁকতে হবে” ব্যাপারটা।
display order (what the viewer sees):
I0 B1 B2 P3 B4 B5 P6
decode order (what the decoder receives):
I0 P3 B1 B2 P6 B4 B5
PTS (present): 0 1 2 3 4 5 6
DTS (decode): -2 -1 0 1 2 3 4 Decoder P3 আগে পায়, কারণ B1 আর B2 বানাতে সেটা লাগবে। এই কারণেই চ্যাপ্টার ১-এর PTS আর DTS আলাদা, এবং এই কারণেই একটা decoder-এ reorder buffer থাকে — decode হয়ে যাওয়া frame কিছুক্ষণ ধরে রেখে সঠিক ক্রমে ছাড়তে হয়। এর ফলে সামান্য latency যোগ হয়, আর সেজন্যই খুব কম-latency-র live স্ট্রিমে অনেক সময় B-frame একেবারে বন্ধ রাখা হয়।
মান একটা নব, ধ্রুবক নয়
এটাই সেই ধারণা যেটা না বুঝলে বাকি সব সিদ্ধান্ত এলোমেলো লাগে। একটা encoder-কে আপনি “ভালো মানে encode করো” বলতে পারেন না। আপনি বলতে পারেন হয় “এই bitrate-এর মধ্যে থাকো”, নয় “এই মানটা ধরে রাখো” — দুটো একসাথে নয়, কারণ দ্বিতীয়টার খরচ কন্টেন্টের উপর নির্ভর করে।
একটা স্থির টকিং-হেড ভিডিও আর একটা হাতে ধরা ক্যামেরায় তোলা বৃষ্টির দৃশ্য — একই মানে encode করলে দ্বিতীয়টা পাঁচ গুণ বেশি জায়গা নেবে। কোনো একটা সংখ্যা দিয়ে দুটোকে একই বাক্সে ভরা যায় না।
Rate control মোড
| মোড | আপনি কী দেন | Encoder কী নিশ্চিত করে | কখন ব্যবহার |
|---|---|---|---|
| CQP | একটা স্থির QP | কিছুই না | পরীক্ষা-নিরীক্ষা, প্রায় কখনো প্রোডাকশনে নয় |
| CRF | একটা মানের লক্ষ্য | মোটামুটি সমান অনুভূত মান | VOD, archive, mezzanine |
| VBR (ABR) | গড় bitrate | গড়ে ওই bitrate | সাধারণ delivery |
| Capped CRF | মান + সর্বোচ্চ সীমা | মান, তবে সীমা ছাড়াবে না | ABR ladder — বাস্তবে সেরা ডিফল্ট |
| CBR | স্থির bitrate | প্রতি মুহূর্তে ওই bitrate | Live, broadcast, হার্ডওয়্যার লিংক |
CRF (Constant Rate Factor) হলো x264/x265-এর মান-ভিত্তিক মোড। ০ থেকে ৫১-র মধ্যে একটা সংখ্যা; ছোট মানে ভালো মান আর বড় ফাইল। ব্যবহারিক পরিসর ১৮ থেকে ২৮, ডিফল্ট ২৩। মোটা নিয়ম: CRF ৬ বাড়ালে ফাইল প্রায় অর্ধেক হয়। এটা QP-র মতো, তবে হুবহু নয় — CRF দ্রুত-চলমান দৃশ্যে QP একটু বাড়িয়ে দেয়, কারণ ওখানে চোখ খুঁটিনাটি কম ধরে।
CBR মানে encoder-কে প্রতি মুহূর্তে একটা নির্দিষ্ট হার ধরে রাখতে হয়। সহজ দৃশ্যে সে ইচ্ছাকৃতভাবে ফাঁকা বিট ভরে (padding), কঠিন দৃশ্যে মান নামিয়ে দেয়। এটা অপচয়ী, কিন্তু যেখানে ট্রান্সমিশন লাইনের ক্ষমতা স্থির (satellite, cable, live contribution), সেখানে কোনো বিকল্প নেই।
Capped CRF হলো বাস্তব জীবনের উত্তর: -crf 21 -maxrate 4M -bufsize 8M। সহজ দৃশ্যে ফাইল ছোট থাকে (CRF যা বলে), কঠিন দৃশ্যে সীমায় গিয়ে আটকে যায় (maxrate যা বলে)। ABR ladder-এ প্রতিটা ধাপের জন্য এটাই সবচেয়ে ভালো ডিফল্ট — চ্যাপ্টার ৫-এ ladder ডিজাইনে এটা আবার ফিরে আসবে।
one 3-minute clip, same source, x264, 1080p30
mode size avg bitrate notes
------------------------------------------------------------------
CRF 18 412 MB 18.3 Mbps near-transparent
CRF 23 (default) 118 MB 5.2 Mbps good VOD quality
CRF 28 41 MB 1.8 Mbps visible softness
CBR 5 Mbps 113 MB 5.0 Mbps flat rate, wasted on easy scenes
capped CRF 21 / max 6M 167 MB 7.4 Mbps quality-led, peak-safe Two-pass encoding
Two-pass তখনই অর্থবহ যখন আপনার লক্ষ্য একটা নির্দিষ্ট ফাইল আকার বা গড় bitrate। প্রথম pass-এ encoder পুরো ভিডিওটা বিশ্লেষণ করে একটা পরিসংখ্যান ফাইল লেখে — কোন অংশ কঠিন, কোন অংশ সহজ। দ্বিতীয় pass-এ সে ওই জ্ঞান নিয়ে বাজেট বণ্টন করে: কঠিন অংশে বেশি bit, সহজ অংশে কম।
# pass 1: analyse only, no output file
ffmpeg -i baghdad-lecture.mov -c:v libx264 -b:v 4M -pass 1 -an -f null /dev/null
# pass 2: encode using the collected statistics
ffmpeg -i baghdad-lecture.mov -c:v libx264 -b:v 4M -pass 2 -c:a aac -b:a 128k out.mp4 কিন্তু লক্ষ্য যদি আকার না হয়ে মান হয়, তাহলে two-pass কার্যত অর্থহীন — CRF এক pass-এই একই কাজ ভালোভাবে করে, অর্ধেক সময়ে। “Two-pass সবসময় ভালো” একটা ব্যাপক ভুল ধারণা।
VBV: সেই বাফার যেটা সবাই ভুলে যায়
-maxrate একা কাজ করে না। সে কাজ করে -bufsize-এর সাথে জুটি বেঁধে, আর এই জুটিটাই VBV (video buffering verifier) মডেল।
ধারণাটা এরকম: decoder-এর সামনে একটা নির্দিষ্ট আকারের বালতি আছে যা নির্দিষ্ট হারে ভরে। Encoder-কে নিশ্চিত করতে হবে বালতি কখনো উপচে না পড়ে, আর কখনো শুকিয়ে না যায়।
maxrate = how fast the bucket refills (bits per second)
bufsize = how big the bucket is (bits)
bufsize / maxrate = how many seconds the encoder may overspend
maxrate 4 Mbps, bufsize 4 Mb -> 1 second of slack: very tight, stable
maxrate 4 Mbps, bufsize 8 Mb -> 2 seconds: the usual streaming choice
maxrate 4 Mbps, bufsize 24 Mb -> 6 seconds: high quality, risky startup বালতি বড় রাখলে encoder কঠিন দৃশ্যে বেশি খরচ করে পরে পুষিয়ে নিতে পারে, তাই মান ভালো হয়। কিন্তু বড় বালতি মানে player-কেও শুরুতে বেশি বাফার করতে হয়, তাই startup সময় বাড়ে আর দুর্বল নেটওয়ার্কে rebuffer-এর ঝুঁকি বাড়ে।
-maxrate দিলে -bufsize না দেওয়া একটা নীরব বাগ — ffmpeg তখন একটা ডিফল্ট ধরে নেয় যা প্রায়ই আপনার চাওয়ার সাথে মেলে না। ব্যবহারিক নিয়ম: bufsize = maxrate × 2 দিয়ে শুরু করুন।
Lookahead আর adaptive quantisation
আরও দুটো জিনিস মানে বড় পার্থক্য গড়ে, অথচ ডিফল্টের বাইরে কেউ খুব একটা ভাবে না।
Lookahead — encoder সামনের কয়েক ডজন frame আগে থেকে দেখে নেয়। এতে সে বুঝতে পারে কোথায় দৃশ্য বদলাচ্ছে, কোন frame ভবিষ্যতে বেশি ব্যবহৃত হবে, আর কোথায় bit জমিয়ে রাখা দরকার। x264-এ rc-lookahead, আর এর সাথে যুক্ত mb-tree নামের কৌশলটা এমন ব্লকগুলোতে বেশি bit দেয় যেগুলো ভবিষ্যতে অনেক frame-এর reference হবে — একবার ভালো করে আঁকলে পরের পঞ্চাশটা frame সস্তায় হয়ে যায়।
Adaptive quantisation (AQ) — একই frame-এর ভেতরেই আলাদা এলাকায় আলাদা QP। সমতল এলাকায় (আকাশ, দেয়াল) সামান্য quantisation-ও চোখে পড়ে, আবার জটিল টেক্সচারে (ঘাস, ভিড়) অনেক বেশি quantisation-ও লুকিয়ে যায়। AQ সেই অনুযায়ী bit সরিয়ে দেয়। AQ বন্ধ করলে PSNR সামান্য বাড়ে কিন্তু দেখতে খারাপ লাগে — এটাই “মেট্রিক অপ্টিমাইজ করা” আর “চোখের জন্য অপ্টিমাইজ করা”-র পার্থক্যের সবচেয়ে পরিষ্কার উদাহরণ।
Preset আর tune
-preset হলো গতি আর দক্ষতার একটামাত্র নব — আসলে এটা RDO কত বড় পরিসরে খুঁজবে, motion search কত বিস্তৃত হবে, কতগুলো reference frame দেখা হবে, সেই সব সেটিংসের একটা প্যাকেজ।
| Preset | আপেক্ষিক গতি | একই CRF-এ আকার | কখন |
|---|---|---|---|
ultrafast | ~২৫× | +৭০% | live contribution, দ্রুত প্রুফ |
veryfast | ~৮× | +২৫% | ইউজার আপলোড, দ্রুত প্রথম rendition |
medium | ১× (ভিত্তি) | ভিত্তি | যুক্তিসঙ্গত ডিফল্ট |
slow | ~০.৫× | -৫% | VOD ladder |
veryslow | ~০.২× | -১০% | প্রিমিয়াম ক্যাটালগ, archive |
লক্ষণীয় — medium থেকে veryslow-এ যাওয়ায় পাঁচ গুণ সময় খরচ করে মাত্র ১০ শতাংশ লাভ, অথচ ultrafast থেকে medium-এ এসে আট গুণ সময়ে ৭০ শতাংশ লাভ। Preset-এর রিটার্ন দ্রুত কমে যায়, আর সেজন্যই বেশিরভাগ প্ল্যাটফর্ম medium আর slow-এর মাঝেই থিতু হয়।
-tune আবার কন্টেন্টের ধরন অনুযায়ী psychovisual সেটিংস বদলায়: film (সাধারণ লাইভ-অ্যাকশন), animation (বড় সমতল এলাকা, ধারালো কিনারা), grain (ফিল্ম গ্রেইন ধরে রাখা — খুব ব্যয়বহুল), stillimage, আর zerolatency (B-frame আর lookahead বন্ধ, live-এর জন্য)।
-tune grain মান ধরে রাখে বটে, কিন্তু bitrate বিস্ফোরিত করে — grain হলো সংজ্ঞা অনুযায়ীই এলোমেলো তথ্য, যা compress হয় না। পুরনো ফিল্ম ট্রান্সফার ছাড়া এটা ব্যবহার করবেন না; বরং AV1-এর film grain synthesis দেখুন, যেখানে grain সরিয়ে ফেলে decoder-এ কৃত্রিমভাবে ফিরিয়ে আনা হয় — এক ভগ্নাংশ খরচে একই অনুভূতি।
Codec-এর দুনিয়া
নতুনরা প্রশ্ন করে “সবচেয়ে ভালো codec কোনটা”। প্রশ্নটাই ভুল। সঠিক প্রশ্ন — এই কন্টেন্ট, এই দর্শক আর এই বাজেটের জন্য কোনটা? কারণ সিদ্ধান্তটা তিনটে স্বাধীন অক্ষে হয়:
- Compression efficiency — একই মানে কত কম bit।
- Compatibility — কত শতাংশ ডিভাইস এটা চালাতে পারবে, বিশেষ করে হার্ডওয়্যারে।
- Licensing আর encode খরচ — patent royalty দিতে হবে কি না, আর encode করতে কত CPU-ঘণ্টা লাগবে।
| Codec | সাল | H.264-এর তুলনায় দক্ষতা | Compatibility | License |
|---|---|---|---|---|
| H.264/AVC | ২০০৩ | ভিত্তি | কার্যত সর্বজনীন | MPEG LA pool, পরিপক্ব |
| HEVC/H.265 | ২০১৩ | ~৪০% কম bitrate | ভালো, তবে ব্রাউজারে দুর্বল | জটিল, একাধিক patent pool |
| VP9 | ২০১৩ | ~৪০% কম bitrate | সব বড় ব্রাউজার, Android | Royalty-free (Google) |
| AV1 | ২০১৮ | ~৫০% কম bitrate | নতুন ডিভাইস, দ্রুত বাড়ছে | Royalty-free (AOMedia) |
| VVC/H.266 | ২০২০ | ~৬৫% কম bitrate | কার্যত নেই | অমীমাংসিত |
কয়েকটা ব্যবহারিক সত্য যা টেবিলে ধরা পড়ে না:
H.264 মরেনি এবং শিগগিরই মরবে না। ২০ বছরের পুরনো একটা টেলিভিশন, একটা সস্তা সেট-টপ বক্স, একটা পুরনো ফোন — সবই H.264 baseline চালাতে পারে। যেকোনো গুরুত্বপূর্ণ প্ল্যাটফর্মের ladder-এ একটা H.264 fallback থাকে, এবং থাকবে।
হার্ডওয়্যার decode থাকা না থাকা সবচেয়ে বড় ব্যবহারিক পার্থক্য। সফটওয়্যারে AV1 চালানো যায়, কিন্তু ফোনের ব্যাটারি দ্রুত শেষ হয় আর ফোন গরম হয়। “ব্রাউজার সাপোর্ট করে” আর “ডিভাইস আরামে চালাতে পারে” — এক জিনিস নয়।
AV1-এর আসল বাধা encode-এর খরচ। শুরুর দিকে libaom H.264-এর চেয়ে শতগুণ ধীর ছিল। SVT-AV1 সেটা নাটকীয়ভাবে কমিয়েছে, কিন্তু এখনো AV1 encode H.264-এর চেয়ে অনেক দামি। তাই AV1 সবচেয়ে বেশি লাভ দেয় সেখানেই যেখানে একবার encode করে বহুবার stream হয় — জনপ্রিয় VOD ক্যাটালগ। একবার দেখা হবে এমন ইউজার আপলোডে AV1-এর হিসাব প্রায়ই মেলে না।
একই codec, ভিন্ন encoder — বিশাল পার্থক্য। x264 veryslow আর একটা GPU-র H.264 encoder — দুটোই H.264, কিন্তু একই bitrate-এ মান আলাদা। চ্যাপ্টার ৪-এ hardware বনাম software-এর হিসাবটা বিস্তারিত আছে।
বাস্তব প্ল্যাটফর্মে উত্তরটা প্রায় সবসময় “একটা নয়, দুটো”: জনপ্রিয় কন্টেন্টের জন্য AV1 বা HEVC (bandwidth বাঁচে), আর সবকিছুর জন্য H.264 fallback (সবাই চালাতে পারে)। প্রশ্নটা “কোনটা” নয়, “কোন কন্টেন্টে কোনটা, আর কতটুকু ক্যাটালগে”।
একটা হিসাব করে দেখা
“৫০% কম bitrate” শুনতে চমৎকার, কিন্তু সিদ্ধান্তটা টাকায় না নামালে অর্থহীন। ধরুন একটা প্ল্যাটফর্মে একটা জনপ্রিয় ভিডিও, 1080p, VMAF ৯৩ লক্ষ্য:
same title, same target quality (VMAF 93)
codec bitrate encode time 1M views x 10 min
------------------------------------------------------------
H.264 5.0 Mbps 6 min 375 TB egress
HEVC 3.1 Mbps 24 min 233 TB egress
AV1 2.6 Mbps 71 min 195 TB egress
saving of AV1 over H.264: 180 TB per million views
extra encode cost: about one CPU-hour, once এই টেবিলটাই সিদ্ধান্তের নিয়মটা বলে দেয়। এক মিলিয়ন ভিউয়ের একটা ভিডিওতে বাড়তি এক CPU-ঘণ্টা কিছুই না, কারণ বিনিময়ে ১৮০ টেরাবাইট egress বাঁচছে। কিন্তু যে ভিডিওটা তিনবার দেখা হবে, তার জন্য ওই এক ঘণ্টা খরচ করা সরাসরি লোকসান। Encode একবার, delivery বহুবার — এই অনুপাতটাই codec বাছাইয়ের গণিত।
ব্যবহারিক কমান্ডগুলো একই আকৃতির, শুধু encoder আর প্যারামিটারের নাম আলাদা:
# H.264 — the compatibility floor, present in every ladder
ffmpeg -i mezzanine.mov -c:v libx264 -preset slow -crf 21 \
-maxrate 5M -bufsize 10M -g 120 -keyint_min 120 -sc_threshold 0 \
-pix_fmt yuv420p -c:a aac -b:a 128k -movflags +faststart h264.mp4
# HEVC — roughly 40% fewer bits, weaker browser support
ffmpeg -i mezzanine.mov -c:v libx265 -preset slow -crf 24 \
-x265-params "keyint=120:min-keyint=120:scenecut=0" \
-tag:v hvc1 -c:a aac -b:a 128k -movflags +faststart hevc.mp4
# AV1 via SVT-AV1 — best efficiency, highest encode cost
ffmpeg -i mezzanine.mov -c:v libsvtav1 -preset 6 -crf 30 \
-svtav1-params "keyint=120:scd=0" \
-c:a libopus -b:a 96k av1.mp4 তিনটে কমান্ডে -crf-র মান আলাদা, আর সেটা ইচ্ছাকৃত — CRF স্কেল codec-ভেদে এক নয়। x264-এর CRF 21, x265-এর CRF 24 আর SVT-AV1-এর CRF 30 মোটামুটি একই অনুভূত মান দেয়। একই সংখ্যা কপি করে বসিয়ে দিলে ফল অপ্রত্যাশিত হবে, আর এটা সবচেয়ে ঘনঘন হওয়া ভুলগুলোর একটা।
Profile আর level: “চলে” বনাম “চলে না”
Codec বাছাই করেই কাজ শেষ নয়। প্রতিটা codec-এর ভেতরে profile আর level নামে দুটো মাত্রা আছে, আর ডিভাইসে ভিডিও না চলার সবচেয়ে সাধারণ কারণ এই দুটোর একটা।
Profile ঠিক করে কোন কোন কৌশল ব্যবহার করা যাবে:
| H.264 profile | কী থাকে | কোথায় চলে |
|---|---|---|
| Baseline | B-frame নেই, CABAC নেই | পুরনো ফোন, সবচেয়ে নিরাপদ |
| Main | B-frame, CABAC | প্রায় সব ডিভাইস |
| High | 8×8 transform, custom quant matrix | আধুনিক সব ডিভাইস, ডিফল্ট পছন্দ |
| High 10 / 4:4:4 | 10-bit, পূর্ণ chroma | সম্পাদনা ও mastering; ফোনে হার্ডওয়্যার decode নেই |
Level ঠিক করে কত বড় কাজ পর্যন্ত decoder-কে সামলাতে হবে — সর্বোচ্চ resolution, frame rate, bitrate আর reference frame-এর সংখ্যা। Level 4.0 মানে মোটামুটি 1080p30, Level 5.1 মানে 4K30।
# Pin both, so the output is playable where you promised it would be
ffmpeg -i mezzanine.mov -c:v libx264 -profile:v high -level:v 4.0 \
-pix_fmt yuv420p -crf 21 -preset slow out.mp4 Level নির্দিষ্ট করে দিলে encoder নিজেই reference frame-এর সংখ্যা আর অন্যান্য সীমা কমিয়ে আনে, যাতে আউটপুট ওই সীমার মধ্যে থাকে। এটা না দিলে encoder যা খুশি বেছে নেয়, আর সেটা ল্যাপটপে চললেও পাঁচ বছরের পুরনো স্মার্ট টিভিতে কালো পর্দা দেখাতে পারে।
“ফাইলটা আমার ল্যাপটপে চলে, ইউজারের টিভিতে চলে না” — এই টিকিটের ৯০ শতাংশের কারণ তিনটের একটা: profile খুব উঁচু, level খুব উঁচু, বা pixel format yuv420p নয়। তিনটেই encode-এর সময় স্পষ্টভাবে বেঁধে দিলে সমস্যাটাই তৈরি হয় না।
Slice, tile আর encoder-এর সমান্তরালতা
একটা 4K frame encode করা বিশাল কাজ, আর একটা CPU core-এ সেটা ধীর। তাই encoder ভেতরেই সমান্তরাল হয়, আর তার কয়েকটা পদ্ধতি আছে — প্রতিটার নিজস্ব দাম:
- Frame-level parallelism — একাধিক frame একসাথে encode করা। কার্যকর, কিন্তু নির্ভরতার কারণে সীমিত, আর latency বাড়ায়।
- Slice — একটা frame-কে আড়াআড়ি কয়েক টুকরো করা, প্রতিটা স্বাধীন। সহজ, কিন্তু compression কমে, কারণ slice-এর সীমানা পেরিয়ে prediction চলে না।
- Tile (HEVC, AV1) — frame-কে গ্রিডে ভাগ করা। Slice-এর চেয়ে ভালো, কিন্তু একই ধরনের খরচ।
- WPP (wavefront parallel processing) — সারি ধরে ধাপে ধাপে এগোনো, যেখানে প্রতিটা সারি উপরের সারির চেয়ে সামান্য পিছিয়ে চলে। Compression প্রায় অক্ষত থাকে, তাই HEVC-তে এটাই পছন্দের পথ।
মূল কথাটা মনে রাখার মতো: যেকোনো ভেতরের সমান্তরালতা কিছুটা compression দক্ষতা খরচ করে, কারণ স্বাধীন টুকরো মানে কম prediction। এই কারণেই বড় pipeline-এ frame-এর ভেতরে সমান্তরাল না করে সময়ের অক্ষে ভাগ করা হয় — একটা ভিডিওকে segment-এ ভেঙে আলাদা মেশিনে পাঠানো। সেই কৌশলটাই চ্যাপ্টার ৪-এর মূল বিষয়।
একটা packet হারালে কী হয়
Compression-এর নির্ভরতার শৃঙ্খলটার একটা সরাসরি পরিণতি আছে, যা প্রোডাকশনে বারবার দেখা যায়।
lost data in an I-frame -> the whole GOP is corrupt (up to a few seconds)
lost data in a P-frame -> that frame and everything after it, until the next I
lost data in a B-frame -> only that frame; nothing depends on it “স্ক্রিনে সবুজ-গোলাপি ব্লক ছড়িয়ে পড়ছে, তারপর হঠাৎ ঠিক হয়ে যাচ্ছে” — এটা ঠিক এই জিনিস। ভুলটা জমতে থাকে, আর পরের keyframe এসে সব মুছে দেয়। আর তাই “কতক্ষণ খারাপ দেখাবে” প্রশ্নের উত্তর সরাসরি GOP দৈর্ঘ্য।
HTTP-ভিত্তিক streaming-এ (HLS/DASH) এই সমস্যাটা প্রায় থাকে না, কারণ TCP হারানো ডেটা আবার আনে — সেখানে ক্ষতিটা হয় দৃশ্যমান artifact নয়, rebuffer। কিন্তু UDP-ভিত্তিক পথে (WebRTC, SRT, RTP) এটা প্রতিদিনের বাস্তবতা, আর সেখানেই ছোট GOP, periodic intra refresh আর ত্রুটি-সহনশীল encoding-এর মূল্য।
HDR, সংক্ষেপে
HDR কোনো আলাদা codec নয় — এটা HEVC বা AV1-এর ভেতরেই একটা কনফিগারেশন। যা লাগে:
- 10-bit encoding — 8-bit-এ PQ curve-এ banding অনিবার্য।
- BT.2020 primaries আর PQ বা HLG transfer — এবং সেগুলো সঠিকভাবে tag করা।
- Static বা dynamic metadata — HDR10-এ mastering display metadata, Dolby Vision বা HDR10+-এ দৃশ্য-ভিত্তিক metadata।
চ্যাপ্টার ১-এর সতর্কতাটা এখানে দ্বিগুণ প্রযোজ্য: HDR-এ tag ভুল হলে ছবি শুধু “একটু অন্যরকম” দেখায় না, একেবারে ধূসর আর ধুয়ে যাওয়া দেখায়। আর SDR ডিভাইসের জন্য একটা আলাদা SDR rendition রাখতেই হবে — tone mapping player-এর উপর ছেড়ে দিলে ফলাফল ডিভাইসভেদে পুরোপুরি ভিন্ন হয়।
সব মিলিয়ে: encoder-এর ভেতরের লুপ
এই ডায়াগ্রামে একটা জিনিস খেয়াল করার মতো: encoder নিজের ভেতরেই একটা decoder চালায়। কারণ পরের frame-গুলো যে reference দেখবে সেটা মূল উৎস নয়, decode হওয়ার পরের সংস্করণ — নইলে encoder আর decoder-এর হিসাব আলাদা হয়ে যাবে আর ভুল জমতে থাকবে। একে বলে decoder loop, আর এটাই লম্বা GOP-এ drift ঠেকায়।
মান কীভাবে মাপা হয়
“দেখতে ভালো লাগছে” দিয়ে প্ল্যাটফর্ম চালানো যায় না। তিনটে মেট্রিক ব্যবহারিকভাবে চলে:
- PSNR — গাণিতিক পার্থক্য, হিসাব করা সবচেয়ে সহজ, মানুষের অনুভূতির সাথে মিল সবচেয়ে কম। দুটো encode তুলনা করতে চলে, ভালো-খারাপ বিচারে নয়।
- SSIM — গঠন কতটা টিকে আছে তা মাপে, PSNR-এর চেয়ে ভালো।
- VMAF — Netflix-এর তৈরি, মানুষের দেওয়া স্কোর দিয়ে প্রশিক্ষিত মডেল। ০-১০০ স্কেল; ৯৩-এর উপরে সাধারণত “উৎসের থেকে আলাদা করা কঠিন”। আজ ইন্ডাস্ট্রিতে এটাই কার্যত মান।
ব্যবহারিক নিয়ম: মেট্রিককে তুলনা করার যন্ত্র হিসেবে ব্যবহার করুন, লক্ষ্য হিসেবে নয়। “এই CRF-এ VMAF ৯৪” খুব কাজের তথ্য; “সব ভিডিওতে VMAF ৯৫ চাই” একটা খরচের ফাঁদ, কারণ শেষ কয়েক পয়েন্টের দাম আগের সব পয়েন্টের চেয়ে বেশি।
Rate-distortion curve: কোথায় থামবেন
একই ক্লিপ কয়েকটা bitrate-এ encode করে মান মাপলে যে বক্ররেখাটা পাওয়া যায়, সেটাই গোটা ভিডিও ইন্ডাস্ট্রির অর্থনীতির আকৃতি:
VMAF
100 | . . . . . . . .
| . . . .
95 | . . .
| . .
90 | . .
| .
80 | .
| .
60 | .
+---------------------------------------------------- bitrate
0.5 1 2 3 4 5 6 8 10 Mbps
| | |
| | +-- +2 VMAF for +5 Mbps
| +-- the knee: best value per bit
+-- steep: every bit buys a lot তিনটে অঞ্চল, তিনটে আলাদা সিদ্ধান্ত:
- খাড়া অংশ — এখানে প্রতিটা বাড়তি bit বড় লাভ দেয়। Ladder-এর নিচের ধাপগুলো এই অঞ্চলে থাকে, আর এখানে কৃপণতা করলে দর্শক সরাসরি টের পায়।
- হাঁটু (knee) — প্রতি bit-এ সর্বোচ্চ মূল্য। Ladder-এর প্রধান ধাপগুলো এখানে বসানোই লক্ষ্য।
- সমতল অংশ — এখানে bitrate দ্বিগুণ করলেও দর্শক পার্থক্য বলতে পারে না। এখানে খরচ করা মানে সরাসরি টাকা পোড়ানো।
সমস্যা হলো, হাঁটুটা প্রতিটা ভিডিওতে আলাদা জায়গায়। একটা কথা-বলা মানুষের ভিডিওতে হাঁটু ১.৫ Mbps-এ, একটা খেলার ভিডিওতে ৬ Mbps-এ। সব ভিডিওতে একটাই স্থির bitrate দেওয়া মানে অর্ধেক ক্ষেত্রে অপচয়, বাকি অর্ধেকে খারাপ মান।
এই পর্যবেক্ষণ থেকেই per-title encoding জন্ম নিয়েছে: প্রতিটা টাইটেলের জন্য আলাদা করে এই বক্ররেখা বের করে ladder সাজানো। চ্যাপ্টার ৫-এ ladder ডিজাইন করার সময় এটাই কেন্দ্রীয় হাতিয়ার।
Audio codec, সংক্ষেপে
ভিডিওর তুলনায় audio-র ডেটা নগণ্য, তাই সিদ্ধান্তগুলোও সরল।
AAC হলো নিরাপদ ডিফল্ট। MP4-এ চলে, HLS-এ চলে, সব ডিভাইসে চলে। Stereo-র জন্য 128 kbps কার্যত স্বচ্ছ; কথা-প্রধান কন্টেন্টে 96 kbps-ও যথেষ্ট।
Opus কারিগরিভাবে স্পষ্ট উন্নত — কম bitrate-এ AAC-র চেয়ে অনেক ভালো, আর latency অনেক কম। WebRTC-তে এটাই একমাত্র বাস্তব পছন্দ, আর WebM-এ স্বাভাবিক সঙ্গী। বাধা একটাই: পুরনো Apple ইকোসিস্টেমে HLS-এর ভেতরে সমর্থন সীমিত ছিল, তাই VOD delivery-তে AAC এখনো রাজত্ব করছে।
আর কয়েকটা কথা মনে রাখার মতো: audio-র জন্য bitrate ladder বানাবেন না। পুরো ladder জুড়ে একটাই audio rendition রাখুন — এতে খরচ বাঁচে, আর player-এর সুইচিং সহজ হয়। আর audio-র keyframe নেই, কিন্তু packet-এর সীমানা আছে; segment কাটার সময় audio আর video-র সীমানা হুবহু মিলবে না, আর সেটাই স্বাভাবিক। এই সামান্য অমিলটা সামলানোই packaging-এর কাজ, যা পরের চ্যাপ্টারের বিষয়।
যেসব ভুল বারবার হয়
এক codec-এর CRF অন্য codec-এ কপি করা। x264-এর ২৩ আর x265-এর ২৩ এক জিনিস নয়। প্রতিটা encoder-এর স্কেল আলাদাভাবে ক্যালিব্রেট করা।
-maxrate দিয়ে -bufsize না দেওয়া। সীমাটা তখন যা খুশি তাই আচরণ করে, আর “কেন আমার bitrate cap মানছে না” ধরনের বহু ঘণ্টা ডিবাগিং এখান থেকেই আসে।
Ladder-এর প্রতিটা ধাপে keyframe জোর করে না বসানো। Encoder দৃশ্য অনুযায়ী keyframe বসালে ধাপে ধাপে সেগুলো ভিন্ন জায়গায় পড়ে, আর ABR সুইচ হয় ঝাঁকুনি দেয় নয়তো ফেল করে। -g, -keyint_min আর -sc_threshold 0 — তিনটেই লাগে।
উৎসের চেয়ে বড় resolution-এ encode করা। 720p উৎস থেকে 1080p rendition বানালে ফাইল বড় হয়, মান বাড়ে না। Ladder-এর সর্বোচ্চ ধাপ উৎসেই থামা উচিত।
yuv420p স্পষ্টভাবে না দেওয়া। উৎস 4:2:2 বা 4:4:4 হলে encoder সেটাই ধরে রাখতে পারে, আর ফলাফল বহু ডিভাইসে চলবেই না — বিশেষ করে পুরনো ফোন আর টিভিতে।
Two-pass-কে “সবসময় ভালো” ভাবা। মান-ভিত্তিক লক্ষ্যে এটা কেবল সময় নষ্ট।
মেট্রিককে লক্ষ্য বানানো। VMAF ৯৫ থেকে ৯৭-এ যাওয়ার bitrate খরচ প্রায়ই ৮০ থেকে ৯৫-এ যাওয়ার সমান, অথচ দর্শক পার্থক্যটা ধরতে পারে না।
মূল শেখা
- Codec তিন ধরনের redundancy খায়: spatial (frame-এর ভেতরে), temporal (frame-এর মাঝে) আর perceptual (চোখ যা ধরে না) — এর মধ্যে temporal-ই সবচেয়ে বড় লাভ দেয়
- গোটা পাইপলাইনে তথ্য হারায় শুধু quantisation ধাপে; transform আর entropy coding সম্পূর্ণ lossless
- I-frame স্বয়ংসম্পূর্ণ আর দামি, P-frame আগের দিকে তাকায়, B-frame দুদিকে তাকায় আর সবচেয়ে ছোট — কিন্তু B-frame decode order আর display order আলাদা করে দেয়, তাই PTS আর DTS আলাদা
- GOP দৈর্ঘ্য streaming-এ আপনার পছন্দ নয়, segment দৈর্ঘ্যের ফল; আর ladder-এর সব rendition-এ keyframe একই টাইমস্ট্যাম্পে পড়তেই হবে, নইলে ABR সুইচ ভাঙে
- মান একটা নব, ধ্রুবক নয় — “এই bitrate” আর “এই মান” দুটো আলাদা অনুরোধ, দুটো একসাথে পাওয়া যায় না
- CRF মান ধরে রাখে, CBR হার ধরে রাখে, capped CRF দুটোর ব্যবহারিক মিলন — ABR ladder-এ এটাই ডিফল্ট হওয়া উচিত
- Two-pass তখনই দরকার যখন লক্ষ্য একটা নির্দিষ্ট ফাইল আকার; লক্ষ্য মান হলে one-pass CRF একই ফল দেয় অর্ধেক সময়ে
- Codec বাছাই র্যাঙ্কিং নয়, তিন-অক্ষের trade-off: efficiency বনাম compatibility বনাম license ও encode খরচ — আর হার্ডওয়্যার decode থাকা-না-থাকাই সবচেয়ে বড় ব্যবহারিক পার্থক্য
বাস্তবে যেভাবে ব্যবহার হয়
- Netflix প্রতিটা টাইটেল, এমনকি প্রতিটা shot আলাদা করে বিশ্লেষণ করে per-title আর per-shot encoding চালায় — কারণ একটা স্থির অ্যানিমেশন আর একটা অ্যাকশন দৃশ্যে একই bitrate দেওয়া সরাসরি অপচয়; VMAF মেট্রিকটাও তাদেরই তৈরি এই কাজের জন্য
- YouTube আপলোডের সাথে সাথে H.264-এ transcode করে দ্রুত পাবলিশ করে, তারপর ভিডিওটা জনপ্রিয় হলে পেছনে VP9 আর AV1-এ আবার encode করে — encode-এর খরচ তখনই যুক্তিসঙ্গত যখন দর্শকসংখ্যা তা তুলে দেয়
- Twitch live-এ CBR আর ছোট GOP ব্যবহার করে, কারণ latency আর নেটওয়ার্কের স্থিরতা এখানে সর্বোচ্চ মানের চেয়ে গুরুত্বপূর্ণ; একই কারণে B-frame-ও সীমিত রাখা হয়
- WhatsApp আর Instagram আপলোডে আক্রমণাত্মকভাবে H.264 CRF ব্যবহার করে ফাইল ছোট করে, কারণ তাদের দর্শকের বড় অংশ ধীর মোবাইল নেটওয়ার্কে — এখানে compatibility আর আকার, দুটোই efficiency-র চেয়ে গুরুত্বপূর্ণ
- WebRTC ভিত্তিক কল (Meet, Zoom-এর ওয়েব পথ) অডিওতে Opus আর ভিডিওতে ছোট GOP, B-frame ছাড়া কনফিগারেশন ব্যবহার করে — কারণ এখানে প্রতিটা মিলিসেকেন্ড latency-র দাম আছে