লো লেটেন্সি: কোন পথ, কখন, কতটা দামে
টিউন করা HLS, LL-HLS, LL-DASH, WebRTC/WHEP আর HESP পাশাপাশি — কত latency আসলে দরকার, কীভাবে মাপবেন, প্লেয়ার কীভাবে লাইভ এজের কাছে থাকে, আর CDN-কে কী কী পারতে হবে।
গল্পে বুঝি
মিরপুরের শেরে বাংলা স্টেডিয়ামে বাংলাদেশ-ভারত ম্যাচ। গ্যালারিতে বসা হাইসাম নিজের চোখে দেখল বলটা ছক্কা হয়ে উড়ে গেল, আর সাথে সাথে চিৎকার। বড় স্ক্রিনে রিপ্লে আসতে আধা সেকেন্ডও লাগল না, কারণ স্ক্রিনটা স্টেডিয়ামের ভেতরের তার দিয়ে সরাসরি ক্যামেরার সাথে জোড়া।
একই সময়ে মোহাম্মদপুরের এক বাসায় বিরুনি টিভিতে খেলা দেখছে। বোলার তখনো রানআপ নিচ্ছে, এমন সময় পাশের ফ্ল্যাট থেকে চিৎকার ভেসে এল, “ছক্কা!” পাশের ফ্ল্যাটের রাজি দেখছিল মোবাইলের একটা অ্যাপে, যেটা টিভির চেয়ে কয়েক সেকেন্ড এগিয়ে। আর বিরুনির ফোনে ফাতিমার মেসেজ এল “কী শট!”, ফাতিমা গ্যালারিতে। বিরুনির টিভিতে ছক্কাটা হলো তার পাঁচ সেকেন্ড পরে, কিন্তু মজাটা ততক্ষণে মাটি। পরের ওভারে বিরুনি টিভি বন্ধ করে রাজির অ্যাপটাই নামাল।
অথচ গলির মোড়ের চায়ের দোকানে বসে কিন্দি যে পুরনো রেডিওতে ধারাভাষ্য শুনছিল, তার কোনো অভিযোগ নেই। তার আশেপাশে কেউ আর কোনো পর্দায় খেলা দেখছে না, তাই দশ সেকেন্ড দেরিও তার কাছে “সরাসরি”। আর রাজির অ্যাপ যে এগিয়ে থাকে তার একটা দামও আছে: ভিড়ের সময় মাঝেমাঝে ছবি এক সেকেন্ডের জন্য আটকে যায়, কারণ অ্যাপটা হাতে জমা খুব কম রাখে।
মিলিয়ে নিই: স্টেডিয়ামের বড় স্ক্রিন হলো সাব-সেকেন্ড WebRTC, অল্প দর্শক কিন্তু প্রায় শূন্য দেরি। রাজির অ্যাপ হলো LL-HLS বা LL-DASH, বিশাল দর্শকের জন্য কয়েক সেকেন্ড, আর বিরুনির টিভি হলো সাধারণ HLS-এর মতো দীর্ঘ দেরি। পাশের ফ্ল্যাটের চিৎকার আর ফাতিমার মেসেজ হলো দ্বিতীয় পর্দার স্পয়লার, যেটাই আসলে ঠিক করে দেয় কত latency সহ্য করা যায়। কিন্দির রেডিও দেখায় যে তুলনা করার কিছু না থাকলে latency কোনো সমস্যা নয়। আর রাজির অ্যাপের মাঝেমাঝে আটকে যাওয়া হলো কম বাফারের দাম: বেশি rebuffer। বাস্তবে লো-লেটেন্সি নিয়ে প্রতিটা সিদ্ধান্ত এই প্রশ্ন দিয়ে শুরু হয়: দর্শক কিসের সাথে তুলনা করবে?
আগে প্রশ্ন: কত latency আসলে দরকার
“আমাদের লো লেটেন্সি লাগবে” কথাটা প্রায়ই প্রোডাক্টের কাছ থেকে আসে কোনো সংখ্যা ছাড়া। সংখ্যা ঠিক না করে শুরু করলে হয় দরকারের চেয়ে অনেক বেশি খরচ হয়, নয়তো দর্শক তবুও অভিযোগ করে। কন্টেন্টের ধরন দেখে লক্ষ্যটা মোটামুটি এভাবে ঠিক হয়:
| ব্যবহার | দর্শক কিসের সাথে তুলনা করে | যুক্তিসঙ্গত লক্ষ্য |
|---|---|---|
| ওয়াজ, কনসার্ট, ক্লাস রেকর্ডিংয়ের মতো একমুখী সম্প্রচার | কিছুর সাথে না | ১৫-৩০ সেকেন্ড চলে |
| খবর, টক শো | টিভি চ্যানেল | টিভির সমান, ৫-১০ সেকেন্ড |
| খেলা | টিভি, পাশের বাসার চিৎকার, সোশ্যাল মিডিয়া | টিভির সমান বা কম, ২-৬ সেকেন্ড |
| লাইভ কমার্স, Q&A, চ্যাটে উত্তর দেওয়া | নিজের পাঠানো চ্যাট মেসেজ | ২-৪ সেকেন্ড |
| নিলাম, বাজি, কুইজে উত্তর দেওয়া | অন্য দর্শকের সাথে ন্যায্যতা | এক সেকেন্ডের নিচে |
| অতিথিকে সরাসরি যুক্ত করা, দুজনের কথোপকথন | স্বাভাবিক কথোপকথন | ৫০০ ms-এর নিচে, ফোন কলের মতো |
সংখ্যাগুলো একটা সাধারণ ছবি, কোনো মানদণ্ড নয়। আসল লক্ষ্য ঠিক করুন আপনার দর্শকের প্রেক্ষাপট দেখে। আর একটা কথা বারবার প্রমাণিত: ৯৯% দর্শক শুধু দেখছে। যে কয়েকজন সত্যিই ইন্টারঅ্যাক্ট করছে (নিলামে দর হাঁকছে, মঞ্চে যুক্ত অতিথি), সাব-সেকেন্ড প্রায়ই শুধু তাদের দরকার।
“Latency” বলতে কোনটা মাপছেন
একই শব্দ তিনটা আলাদা জিনিস বোঝাতে ব্যবহার হয়, আর মিটিংয়ে এটা বিভ্রান্তির বড় উৎস:
- Glass-to-glass — ক্যামেরার লেন্সে আলো পড়া থেকে দর্শকের স্ক্রিনে সেই ফ্রেম দেখানো পর্যন্ত। দর্শক এটাই অনুভব করে, আর এটাই আসল লক্ষ্য।
- এনকোডার থেকে প্লেয়ার — এনকোডার যখন ফ্রেমটা পাঠাল থেকে প্লেয়ার দেখানো পর্যন্ত। ক্যামেরা আর ক্যাপচার কার্ডের দেরি এতে নেই।
- লাইভ এজ থেকে দূরত্ব — প্লেয়ার প্লেলিস্টের শেষ থেকে কত সেকেন্ড পেছনে। প্লেয়ার লাইব্রেরি সাধারণত এটাই রিপোর্ট করে। এটা ছোট দেখালেও glass-to-glass বড় হতে পারে, কারণ প্লেলিস্টে সেগমেন্ট পৌঁছানোর আগের সব দেরি এখানে গোনা হয় না।
latency কোন ধাপে কতটা খরচ হয়, তার ধাপে ধাপে বাজেট ভিডিও ট্র্যাকের চ্যাপ্টার ৯-এ আছে। এখানে দেখি নিজের সিস্টেমে কীভাবে মাপবেন।
পদ্ধতি ১: ছবিতে ঘড়ি বসিয়ে দেওয়া
সবচেয়ে বিশ্বাসযোগ্য মাপ সবচেয়ে সরল। এনকোডারের ইনপুটে একটা ঘড়ি সরাসরি ছবির উপরে বসিয়ে দিন, তারপর প্লেয়ারের স্ক্রিন আর একটা আসল ঘড়ি (বা NTP-মেলানো ফোন) পাশাপাশি রেখে একটা ছবি তুলুন। দুই ঘড়ির পার্থক্যই glass-to-glass latency।
# Burn the encoder's wall clock into the picture (needs an ffmpeg built with libfreetype)
ffmpeg -re -f lavfi -i testsrc2=size=1280x720:rate=30 \
-vf "drawtext=text='%{localtime\:%T}':fontsize=72:fontcolor=white:box=1:boxcolor=black@0.6:x=40:y=40" \
-c:v libx264 -preset veryfast -tune zerolatency -g 60 \
-f flv rtmp://127.0.0.1:1935/live/latency-test কয়েকটা সতর্কতা। drawtext ফিল্টার শুধু libfreetype সহ বিল্ড করা ffmpeg-এ থাকে; আমার পরীক্ষার মেশিনের বিল্ডে ছিল না, তাই এই কমান্ডটা এখানে চালানো যায়নি। আগে ffmpeg -filters | grep drawtext দিয়ে দেখে নিন। এখানে ঘড়ি সেকেন্ড পর্যন্ত, তাই মাপও সেকেন্ডের নির্ভুলতায়; কয়েক বার মেপে গড় নিন। আর OBS ব্যবহার করলে একটা “Text” সোর্সে সিস্টেম ঘড়ি দেখানোর প্লাগইন বা স্ক্রিপ্ট দিয়ে একই কাজ হয়।
পদ্ধতি ২: প্লেয়ার থেকে স্বয়ংক্রিয়ভাবে
ঘড়ির ছবি তুলে প্রতিটা দর্শকের latency মাপা যায় না। প্রোডাকশনে প্রতিটা প্লেয়ার নিজেই একটা আনুমানিক মাপ পাঠাতে পারে, যদি প্লেলিস্টে EXT-X-PROGRAM-DATE-TIME থাকে (আগের চ্যাপ্টার)। প্লেয়ার এখন যে ফ্রেম দেখাচ্ছে তার PDT আর দর্শকের ঘড়ির এখনকার সময়ের পার্থক্যই latency-র কাছাকাছি:
import Hls from 'hls.js';
const video = document.querySelector('video')!;
const hls = new Hls();
hls.loadSource('https://live.baytalhikma.org/match/master.m3u8');
hls.attachMedia(video);
setInterval(() => {
// hls.latency: estimated seconds behind the live edge
// playingDate: wall-clock time (from PROGRAM-DATE-TIME) of the frame on screen
const edge = hls.latency;
const glass = hls.playingDate ? (Date.now() - hls.playingDate.getTime()) / 1000 : null;
navigator.sendBeacon('/qoe', JSON.stringify({ edge, glass }));
}, 10_000); glass-এর মানটা ততটাই ঠিক যতটা ট্রান্সকোডার আর দর্শকের ঘড়ি মেলানো। দর্শকের ফোনের ঘড়ি কয়েক সেকেন্ড ভুল থাকা খুবই স্বাভাবিক, তাই একক দর্শকের মান বিশ্বাস না করে অনেক দর্শকের মিডিয়ান আর p95 দেখুন। প্লেয়ার থেকে QoE মেট্রিক কীভাবে সংগ্রহ করবেন তার বিস্তারিত ভিডিও প্লেয়ার ট্র্যাকের চ্যাপ্টার ১১-এ।
পাঁচটা পথ, পাশাপাশি
প্রতিটা প্রোটোকল ভেতরে কীভাবে কাজ করে (partial segment, blocking reload, chunked CMAF, WebRTC-র পরিবহন) সেটা ভিডিও ট্র্যাকের চ্যাপ্টার ৯-এ। এখানে প্রশ্ন অন্য: আপনার প্ল্যাটফর্মে কোনটা চালাতে কী লাগবে।
| পথ | সাধারণ latency (মোটামুটি) | ডিভাইস ও প্লেয়ার | CDN-এর কাছে কী চাই | origin সফটওয়্যার | DRM ও অ্যাড |
|---|---|---|---|---|---|
| টিউন করা HLS | ৬-১০ সেকেন্ড | সব জায়গায় | সাধারণ ক্যাশিং, প্লেলিস্টে ছোট TTL | যেকোনো ফাইল সার্ভার | পরিচিত পথ, সব চলে |
| LL-HLS | ২-৫ সেকেন্ড | Apple-এ নেটিভ; ব্রাউজারে hls.js-এর মতো লাইব্রেরি | blocking রিকোয়েস্ট, query string ক্যাশ কী-তে | LL-HLS জানে এমন সার্ভার | চলে, তবে অ্যাডে কাজ বেশি |
| LL-DASH | ২-৫ সেকেন্ড | Apple-এ নেটিভ নয়; ব্রাউজারে dash.js, Shaka | chunked transfer শেষ পর্যন্ত পাস করা | chunked CMAF লিখতে পারে | চলে |
| WebRTC (WHEP) | এক সেকেন্ডের নিচে | আধুনিক সব ব্রাউজারে আছে | সাধারণ CDN কাজে আসে না; SFU লাগে | SFU / মিডিয়া সার্ভার | সীমিত, নিজে বানাতে হয় |
| HESP | এক সেকেন্ডের কাছাকাছি | বিশেষ প্লেয়ার লাগে | সাধারণ HTTP CDN | বিশেষ প্যাকেজার | ভেন্ডরের উপর নির্ভর |
latency-র সংখ্যাগুলো ভালো টিউন করা সিস্টেমে মোটামুটি যা দেখা যায় তার একটা পরিসর, প্রতিশ্রুতি নয়; আপনার সিস্টেমে মেপে দেখুন। ডিভাইস সাপোর্টের তথ্য ২০২৬ সালের অক্টোবর অনুযায়ী সাধারণ অবস্থা; নির্দিষ্ট টিভি, সেট-টপ বক্স বা পুরনো ব্রাউজারের জন্য প্লেয়ারের নিজস্ব সাপোর্ট টেবিল দেখুন।
টিউন করা সাধারণ HLS: সবচেয়ে সস্তা জয়
বেশিরভাগ প্ল্যাটফর্ম ২০-৩০ সেকেন্ডে চলে শুধু ডিফল্ট সেটিংয়ের কারণে: ৬ সেকেন্ডের সেগমেন্ট, আর প্লেয়ার তিনটা সেগমেন্ট পেছনে। নতুন কোনো প্রোটোকল ছাড়াই এটা অর্ধেকের নিচে নামানো যায়:
- সেগমেন্ট ২ সেকেন্ড, keyframe প্রতি ২ সেকেন্ডে (আগের চ্যাপ্টারের কমান্ড)।
- প্লেলিস্টের CDN TTL সেগমেন্টের অর্ধেক বা তার কম, যাতে নতুন সেগমেন্টের খবর দ্রুত পৌঁছায়।
- প্লেয়ারকে বলুন তিন সেগমেন্টের বদলে একটু কম পেছনে থাকতে, যদি rebuffer সহ্য করা যায়।
এতে কোনো নতুন origin সফটওয়্যার, নতুন CDN ফিচার বা নতুন প্লেয়ার লাগে না, আর সব ডিভাইসে চলে। “লো লেটেন্সি” প্রজেক্ট শুরু করার আগে এটা করে দেখুন; অনেক ক্ষেত্রে এখানেই লক্ষ্য পূরণ হয়ে যায়।
LL-HLS প্রোডাকশনে
LL-HLS চালাতে তিনটা জিনিস একসাথে লাগে, আর একটা না থাকলে বাকিগুলো কাজে আসে না:
- একটা origin যা partial segment লেখে আর blocking রিকোয়েস্ট ধরে রাখতে পারে। সাধারণ ফাইল সার্ভার পারে না, কারণ প্লেয়ার যখন বলে “সিকোয়েন্স ১০৫-এর part ৩ এলে দাও”, সার্ভারকে উত্তর আটকে রাখতে হয়। MediaMTX-এর মতো মিডিয়া সার্ভার LL-HLS দেয় বলে তাদের ডকুমেন্টেশনে আছে; ২০২৬ সালের অক্টোবর অনুযায়ী, কোন ভার্সন থেকে আর কোন সেটিংয়ে, সেটা নিজের ভার্সনের ডকসে মিলিয়ে নিন।
- একটা CDN যা blocking রিকোয়েস্ট বোঝে। প্লেয়ার প্লেলিস্ট চায়
_HLS_msnআর_HLS_partquery প্যারামিটার দিয়ে। CDN যদি query string ক্যাশ কী থেকে বাদ দেয় (অনেক CDN ডিফল্টে বা “query string উপেক্ষা” সেটিংয়ে তাই করে), তাহলে সব দর্শক একই পুরনো প্লেলিস্ট পাবে। আর হাজারো দর্শক একই অপেক্ষমাণ রিকোয়েস্ট পাঠালে CDN-কে সেগুলো এক করে origin-এ একটাই পাঠাতে হবে (request collapsing)। এটা CDN ট্র্যাকের চ্যাপ্টার ৪ আর চ্যাপ্টার ৭-এর বিষয়। - একটা প্লেয়ার যা LL-HLS চালু রেখেছে। Apple-এর ডিভাইসে নেটিভ প্লেয়ার LL-HLS চালায়; ব্রাউজারে hls.js-এ
lowLatencyModeডিফল্টেই চালু (২০২৬ সালের অক্টোবরে hls.js-এর ডকস অনুযায়ী)।
LL-DASH প্রোডাকশনে
LL-DASH-এ partial segment আলাদা ফাইল নয়; সেগমেন্টটা তৈরি হতে হতেই HTTP chunked transfer দিয়ে পাঠানো হয়। ঝুঁকিটা লুকানো জায়গায়: পথের প্রতিটা হপকে chunk আসামাত্র সামনে পাঠাতে হবে। কোনো একটা প্রক্সি, লোড ব্যালান্সার বা CDN লেয়ার যদি পুরো response জমিয়ে তারপর পাঠায় (অনেক reverse proxy ডিফল্টে বাফার করে), তাহলে latency নীরবে আবার পুরো সেগমেন্টের সমান হয়ে যায়, কোনো এরর ছাড়া। nginx-এর পেছনে থাকলে origin location-এ proxy_buffering off লাগে, আর CDN-কে জিজ্ঞেস করুন তারা chunked transfer শেষ পর্যন্ত পাস করে কিনা।
dash.js-এ latency লক্ষ্য আর ধরে ফেলার সেটিং এভাবে বসে (dash.js-এর লো-লেটেন্সি উইকি থেকে, ২০২৬ সালের অক্টোবর অনুযায়ী):
player.updateSettings({
streaming: {
delay: {
liveDelay: 3 // target seconds behind live
},
liveCatchup: {
maxDrift: 0 // max deviation before the player seeks to live instead of catching up
// playbackRate: { min, max } bounds the catch-up speed; check your version's API docs
}
}
}); liveCatchup.playbackRate-এর min আর max ঠিক করে প্লেয়ার কতটা ধীর বা দ্রুত চালিয়ে লক্ষ্যে ফিরবে। উইকির উদাহরণে মানগুলো দেওয়া আছে, কিন্তু এগুলো কীভাবে ব্যাখ্যা হয় তা আমি নিশ্চিত করতে পারিনি, তাই এখানে সংখ্যা দিচ্ছি না; নিজের dash.js ভার্সনের API ডকুমেন্টেশনে মিলিয়ে নিন।
WebRTC আর WHEP
WebRTC দীর্ঘদিন সাব-সেকেন্ড দিত, কিন্তু প্রতিটা সার্ভারের নিজস্ব সিগন্যালিং ছিল, তাই এক ভেন্ডরের প্লেয়ার আরেক ভেন্ডরের সার্ভারে চলত না। দুটো প্রোটোকল এটা ঠিক করছে:
- WHIP (পাঠানোর জন্য) — এনকোডার একটা HTTP POST-এ নিজের SDP offer পাঠায়, সার্ভার answer ফেরত দেয়, তারপর মিডিয়া WebRTC-তে চলে। ২০২৫ সালের মার্চে RFC 9725 হিসেবে প্রকাশিত। OBS-সহ অনেক এনকোডার এটা দিয়ে পাঠাতে পারে (চ্যাপ্টার ২)।
- WHEP (দেখার জন্য) — একই ধরন, উল্টো দিকে: প্লেয়ার POST করে offer, সার্ভার answer দেয়। ২০২৬ সালের অক্টোবর অনুযায়ী এটা এখনো IETF-এর একটা Internet-Draft (draft-ietf-wish-whep-05), RFC নয়; তবে বেশ কিছু মিডিয়া সার্ভার আর প্লেয়ার এটা ইতিমধ্যে বাস্তবায়ন করেছে। চূড়ান্ত হওয়ার আগে খুঁটিনাটি বদলাতে পারে, তাই IETF Datatracker-এ বর্তমান অবস্থা দেখে নিন।
WHEP-এর শুরুর HTTP আদান-প্রদানটা দেখতে এরকম (উদাহরণ, সংক্ষিপ্ত):
POST /match/whep HTTP/1.1
Host: rtc.baytalhikma.org
Content-Type: application/sdp
v=0
o=- 4215775240449105457 2 IN IP4 127.0.0.1
...offer: recvonly audio + video...
HTTP/1.1 201 Created
Content-Type: application/sdp
Location: /match/whep/session/8f2c1a
v=0
...answer: sendonly audio + video, ICE candidates... এরপর ভিডিও আর HTTP-তে আসে না; UDP-তে সরাসরি SFU (Selective Forwarding Unit) থেকে আসে। এখানেই খরচের গল্পটা বদলে যায়:
- CDN ক্যাশ কাজে আসে না। প্রতিটা দর্শক SFU-র সাথে আলাদা একটা সংযোগ রাখে। দর্শক বাড়লে SFU বাড়াতে হয়, আর বড় দর্শকে SFU-র গাছ (cascading) বানাতে হয়: একটা মূল SFU থেকে অঞ্চলভিত্তিক SFU-তে, সেখান থেকে দর্শকে।
- খরচ দর্শকের সংখ্যার সাথে প্রায় সমানুপাতে বাড়ে। HTTP CDN-এ একটা সেগমেন্ট একবার ক্যাশ হলে হাজার জনকে দেওয়া যায়; SFU-তে প্রতিটা দর্শকের জন্য প্যাকেট আলাদা করে পাঠাতে হয়। প্রতি দর্শকের খরচ কত সেটা ভেন্ডর আর আপনার নিজের অবকাঠামোর উপর নির্ভর করে; নিজের সংখ্যা দিয়ে হিসাব করুন।
- কিছু নেটওয়ার্কে UDP বন্ধ। অফিস বা হোটেলের ফায়ারওয়াল UDP আটকালে TURN সার্ভার দিয়ে TCP বা TLS-এ relay করতে হয়, যাতে latency আর খরচ দুটোই বাড়ে।
MediaMTX WebRTC দিয়ে স্ট্রিম পড়তে দেয় বলে তার README-তে আছে; WHEP এন্ডপয়েন্টের ঠিকানা আর সেটিং তাদের ডকসে মিলিয়ে নিন।
HESP, সংক্ষেপে
HESP (High Efficiency Streaming Protocol) সাধারণ HTTP CDN দিয়েই এক সেকেন্ডের কাছাকাছি latency দেওয়ার চেষ্টা করে। কৌশলটা হলো দুটো স্ট্রিম: একটা প্রায় শুধু keyframe দিয়ে বানানো, যাতে প্লেয়ার যেকোনো মুহূর্তে ঢুকতে পারে, আর একটা সাধারণ দক্ষ স্ট্রিম, যেটা ঢোকার পর চলে। এর পেছনে একটা শিল্প জোট আর IETF-এ একটা খসড়া আছে, তবে প্লেয়ার আর প্যাকেজার সাপোর্ট HLS বা DASH-এর তুলনায় অনেক সীমিত। বিবেচনায় রাখুন, কিন্তু বেছে নেওয়ার আগে আপনার টার্গেট ডিভাইসে বর্তমান সাপোর্ট যাচাই করুন।
কোনটা বেছে নেবেন
বাস্তবে সবচেয়ে টেকসই নকশা প্রায়ই হাইব্রিড: একই এনকোডার, একই ট্রান্সকোডার, দুটো আউটপুট। বিশাল দর্শকের জন্য LL-HLS বা টিউন করা HLS, আর যে অল্প কয়েকজন সত্যিই ইন্টারঅ্যাক্ট করছে (মঞ্চে যুক্ত অতিথি, নিলামে দর হাঁকা দর্শক, মডারেটর) তাদের জন্য WebRTC। দুই আউটপুটে একই PROGRAM-DATE-TIME থাকলে পরে দুটোকে মেলানোও সহজ (চ্যাপ্টার ১০)।
লো লেটেন্সি চালু করার আগে একটা প্রশ্নের উত্তর লিখে নিন: আমরা কি বেশি rebuffer মেনে নিতে রাজি? latency কমানো মানে প্লেয়ারের বাফার কমানো। ২০ সেকেন্ডের বাফারে একটা ৫০০ ms-এর নেটওয়ার্ক ঝাঁকুনি কেউ টের পায় না; ৩ সেকেন্ডের বাফারে সেটা স্ক্রিনে ঘুরতে থাকা চাকা। লো-লেটেন্সি চালুর পর rebuffer রেশিও বেড়ে গেলে সেটা বাগ নয়, সেটাই দাম।
প্লেয়ার কীভাবে লাইভ এজের কাছে থাকে
latency শুধু সার্ভারের ব্যাপার নয়। একই স্ট্রিম দুটো প্লেয়ারে ৩ আর ১৫ সেকেন্ড দেখাতে পারে, শুধু প্লেয়ারের সেটিংয়ের জন্য। প্লেয়ারকে তিনটা সিদ্ধান্ত নিতে হয়:
- শুরুতে কত পেছনে বসবে। খুব কাছে বসলে প্রথম কয়েক সেকেন্ডেই বাফার শেষ।
- পিছিয়ে পড়লে কী করবে। একটা rebuffer হলে প্লেয়ার কয়েক সেকেন্ড পিছিয়ে যায়। হয় সামনে লাফ দেবে (দর্শক কয়েক সেকেন্ড মিস করবে), নয়তো একটু দ্রুত চালিয়ে ধীরে ধীরে ধরবে।
- বারবার আটকালে লক্ষ্য বদলাবে কিনা। দুর্বল নেটওয়ার্কে প্রতিবার আটকে যাওয়ার চেয়ে একটু বেশি পেছনে বসে থাকা ভালো।
hls.js-এ এই তিনটার সেটিং (২০২৬ সালের অক্টোবরে hls.js-এর API ডকুমেন্টেশন অনুযায়ী):
const hls = new Hls({
lowLatencyMode: true, // default true: use LL-HLS parts and start at PART-HOLD-BACK
liveSyncDuration: 3, // target seconds behind the live edge (or use liveSyncDurationCount)
liveMaxLatencyDuration: 8, // if further behind than this, seek forward to the target
maxLiveSyncPlaybackRate: 1.1, // default 1 = no catch-up; above 1 lets it speed up to catch up
liveSyncOnStallIncrease: 1 // add 1 s to the target after each stall
}); একটা জিনিস প্রায়ই চোখ এড়ায়: maxLiveSyncPlaybackRate-এর ডিফল্ট ১। মানে ডিফল্টে hls.js একটু দ্রুত চালিয়ে ধরে ফেলে না; পিছিয়ে পড়লে হয় পিছিয়েই থাকে, নয়তো liveMaxLatencyDuration পার হলে লাফ দেয়। ১.০৫ থেকে ১.১-এর মধ্যে একটা মান দিলে বেশিরভাগ দর্শক গতির পার্থক্য টের পায় না, আর একটা rebuffer-এর পর প্লেয়ার কয়েক সেকেন্ডে নিজে থেকেই লাইভের কাছে ফিরে আসে। গানের কনসার্টে কানে বাজতে পারে, তাই সেখানে আরও কম রাখুন। চলমান অবস্থায় hls.targetLatency দিয়ে লক্ষ্যটা পড়া ও বদলানো যায়।
liveSyncOnStallIncrease হলো তৃতীয় সিদ্ধান্তের উত্তর। দুর্বল নেটওয়ার্কের দর্শক প্রতিবার আটকালে লক্ষ্য একটু পিছিয়ে যায়, ফলে সে একটু বেশি latency পায় কিন্তু কম আটকায়। ভালো নেটওয়ার্কের দর্শক এর প্রভাব টেরই পায় না। প্লেয়ারের বাফার আর ABR কীভাবে কাজ করে তার বিস্তারিত ভিডিও প্লেয়ার ট্র্যাকের চ্যাপ্টার ৫-এ।
CDN-এর জন্য চেকলিস্ট
লো লেটেন্সির অর্ধেক কাজ CDN-এ, আর সমস্যাগুলো সাধারণত নীরব: স্ট্রিম চলে, কিন্তু latency কমে না। নতুন CDN বা নতুন কনফিগ চালুর আগে এগুলো যাচাই করুন:
- প্লেলিস্টের TTL ছোট। লাইভ প্লেলিস্ট সেগমেন্টের অর্ধেক দৈর্ঘ্য বা তার কম সময় ক্যাশ হোক; LL-HLS-এ blocking রিকোয়েস্টের উত্তর কয়েক সেকেন্ডের বেশি নয়।
- সেগমেন্টের TTL লম্বা। একটা সেগমেন্ট একবার লেখা হলে আর বদলায় না, তাই সেগুলো লম্বা সময় ক্যাশ করা নিরাপদ।
- LL-HLS-এর query প্যারামিটার ক্যাশ কী-তে আছে।
_HLS_msnআর_HLS_partবাদ দিলে সবাই পুরনো প্লেলিস্ট পায়। - request collapsing চালু। একই অপেক্ষমাণ রিকোয়েস্ট হাজারবার origin-এ না যায়।
- chunked transfer শেষ পর্যন্ত পাস হয়। LL-DASH-এর জন্য, প্রতিটা প্রক্সি লেয়ারে বাফারিং বন্ধ।
- HTTP/2 বা HTTP/3। অনেক ছোট রিকোয়েস্ট একটা কানেকশনে চলে, প্রতিবার নতুন কানেকশনের দেরি নেই।
- সেগমেন্ট 404-এর নেগেটিভ ক্যাশিং বন্ধ বা খুব ছোট। প্লেয়ার একটা সেগমেন্ট তৈরি হওয়ার এক মুহূর্ত আগে চেয়ে 404 পেলে, CDN যদি সেই 404 দশ সেকেন্ড ক্যাশ করে রাখে, সবাই দশ সেকেন্ড আটকে থাকবে।
লাইভ ডেলিভারিতে CDN-এর পুরো আলোচনা চ্যাপ্টার ৮-এ।
ডিবাগিং: এই চ্যাপ্টারের সমস্যাগুলো
| লক্ষণ | কীভাবে নিশ্চিত হবেন | সমাধান |
|---|---|---|
| LL-HLS চালু, তবুও latency ১০ সেকেন্ডের বেশি | প্লেলিস্টে EXT-X-PART আর EXT-X-SERVER-CONTROL আছে কিনা; প্লেয়ারে lowLatencyMode চালু কিনা; প্লেয়ার _HLS_msn দিয়ে রিকোয়েস্ট করছে কিনা (DevTools-এর Network ট্যাব) | origin, CDN আর প্লেয়ার তিনটাতেই LL-HLS চালু করুন; একটা না থাকলে বাকি দুটো কাজে আসে না |
| সব দর্শক একই পুরনো প্লেলিস্ট পাচ্ছে | curl -sI দিয়ে দুটো আলাদা _HLS_msn মানের রিকোয়েস্ট করুন; একই Age আর একই বডি | CDN ক্যাশ কী-তে LL-HLS query প্যারামিটার রাখুন |
| LL-DASH-এর latency একটা পুরো সেগমেন্টের সমান | curl -N দিয়ে সেগমেন্ট চান আর দেখুন বাইট টুকরো টুকরো আসছে নাকি একবারে | পথের প্রক্সি বা CDN বাফার করছে; nginx-এ proxy_buffering off, CDN-এ chunked transfer পাস চালু |
| latency কম, কিন্তু rebuffer অনেক বেড়ে গেছে | QoE ড্যাশবোর্ডে rebuffer রেশিও আর latency একসাথে দেখুন | লক্ষ্য একটু পিছিয়ে দিন, liveSyncOnStallIncrease দিন; বা মেনে নিন যে এটাই দাম |
| একটা rebuffer-এর পর প্লেয়ার আর লাইভে ফেরে না | প্লেয়ারের latency মেট্রিক প্রতিটা rebuffer-এর পর সিঁড়ির মতো বাড়ছে | hls.js-এ maxLiveSyncPlaybackRate ১-এর বেশি করুন; liveMaxLatencyDuration দিন |
| WebRTC কিছু দর্শকের কাছে একেবারেই চলে না | ব্রাউজারের chrome://webrtc-internals বা সমতুল্য পাতায় ICE candidate pair তৈরি হচ্ছে না | TURN সার্ভার যোগ করুন, TCP/TLS-এ ৪৪৩ পোর্টে relay |
| প্লেয়ার রিপোর্ট করছে ২ সেকেন্ড, কিন্তু ছবিতে ঘড়ির ফারাক ৬ সেকেন্ড | প্লেয়ারের মাপ লাইভ এজ থেকে; glass-to-glass নয় | ছবিতে ঘড়ি বসিয়ে মাপুন; প্লেলিস্টে পৌঁছানোর আগের ধাপগুলো (এনকোডার, ইনজেস্ট, ট্রান্সকোড) খুঁজুন |
প্লেয়ারের glass মান কিছু দর্শকের ক্ষেত্রে ঋণাত্মক বা অসম্ভব বড় | একক দর্শকের মান দেখুন | দর্শকের ঘড়ি ভুল; মিডিয়ান আর p95 ব্যবহার করুন, অদ্ভুত মান বাদ দিন |
মূল শিক্ষা
- আগে সংখ্যা ঠিক করুন। দর্শক কিসের সাথে তুলনা করবে, সেটাই বলে কত latency সহ্য করা যায়।
- কোন latency মাপছেন সেটা পরিষ্কার রাখুন। দর্শক অনুভব করে glass-to-glass; প্লেয়ার সাধারণত রিপোর্ট করে লাইভ এজ থেকে দূরত্ব।
- ছবিতে ঘড়ি বসানোই সবচেয়ে বিশ্বাসযোগ্য মাপ। প্রোডাকশনে PDT দিয়ে প্লেয়ার থেকে মাপুন, আর মিডিয়ান ও p95 দেখুন।
- টিউন করা সাধারণ HLS দিয়ে শুরু করুন। নতুন কিছু ছাড়াই ৬-১০ সেকেন্ডে নামানো যায়, সব ডিভাইসে চলে।
- LL-HLS আর LL-DASH-এ origin, CDN আর প্লেয়ার তিনটাই লাগে। একটা না থাকলে latency নীরবে আগের জায়গায় ফিরে যায়।
- WebRTC আসল সাব-সেকেন্ড দেয়, কিন্তু CDN-এর সুবিধা হারায়। খরচ দর্শকের সাথে বাড়ে; WHIP এখন RFC, WHEP এখনো খসড়া।
- হাইব্রিড প্রায়ই সঠিক উত্তর। বিশাল দর্শকের জন্য HTTP, যারা ইন্টারঅ্যাক্ট করে শুধু তাদের জন্য WebRTC।
- প্লেয়ারের সেটিংও latency ঠিক করে। hls.js-এ ধরে ফেলার গতি ডিফল্টে বন্ধ (
maxLiveSyncPlaybackRate১); চালু করুন। - কম latency মানে বেশি rebuffer। চালুর আগে এই দাম মেনে নেওয়ার সিদ্ধান্ত নিন।