প্লেয়ার ডিবাগিং প্লেবুক
আন্দাজ না করে লেয়ার ধরে ধরে — কোন প্লেব্যাক পথ চলছে, নেটওয়ার্ক, ম্যানিফেস্ট, কোডেক ও MSE, DRM, বাফার ও ABR, রেন্ডারিং, ডিভাইস। সাথে লক্ষণ থেকে সমাধানের পূর্ণ ক্যাটালগ আর একটা পরীক্ষা করা কনসোল স্নিপেট।
গল্পে বুঝি
ধানমন্ডির একটা ফ্ল্যাটের ড্রয়িংরুমের TV চলছে না। বাড়ির মালিক রুশদ ইলেকট্রিশিয়ান হাইসামকে ডাকলেন, আর হাইসাম আসার আগেই নিজে TV-টা দুবার বন্ধ-চালু করেছেন, রিমোটের ব্যাটারি বদলেছেন, আর পাশের দোকান থেকে নতুন একটা প্লাগ কিনে এনেছেন। কিছুই হয়নি। হাইসাম এসে প্রথমে TV ছুঁলেন না। তিনি জিজ্ঞেস করলেন, “ফ্ল্যাটের বাকি লাইট-ফ্যান চলছে? শুধু এই ঘরে, নাকি সব ঘরে? কবে থেকে? শেষ কী বদলানো হয়েছিল?”
উত্তর শুনে তিনি নিচে গেলেন মিটারের কাছে। মিটারে বিদ্যুৎ আসছে। তারপর ফ্ল্যাটের মেইন ব্রেকার: অন। তারপর ডিস্ট্রিবিউশন বোর্ড, যেখানে প্রতিটা ঘরের আলাদা ব্রেকার: ড্রয়িংরুমেরটা অন। তারপর দেয়ালের সুইচবোর্ড, টেস্টার দিয়ে: সুইচের আগে বিদ্যুৎ আছে, পরে নেই। সুইচটা ভেতরে পুড়ে গেছে। নতুন সুইচ লাগাতেই TV চলল। পুরো কাজে লাগল বিশ মিনিট, আর তার পনেরো মিনিট গেল কোথায় সমস্যা নেই সেটা নিশ্চিত হতে।
যাওয়ার আগে হাইসাম একটা কথা বললেন: “আপনি TV দিয়ে শুরু করেছিলেন, কারণ TV-টাই চোখে পড়ে। কিন্তু TV হলো লাইনের শেষ মাথা। আমি শুরু করি যেখান থেকে বিদ্যুৎ আসে, আর প্রতিটা ধাপে টেস্টার লাগাই। যে ধাপের আগে আছে কিন্তু পরে নেই, সমস্যা সেখানেই।” তিনি আরও বললেন, পাশের ফ্ল্যাটের ফাতিমার AC নাকি শুধু গরমের দুপুরে বন্ধ হয়, আর সেটা একদম আলাদা সমস্যা: ওই মডেলের AC-র নিজের একটা স্বভাব, ওয়্যারিংয়ের দোষ নয়।
মিলিয়ে নিই: রুশদের TV বন্ধ-চালু করা হলো পেজ রিফ্রেশ আর ক্যাশ ক্লিয়ার করে দেখা, যা কখনো কখনো কাজ করে কিন্তু কিছুই শেখায় না। “বাকি ঘর চলছে? কবে থেকে? কী বদলেছে?” হলো লক্ষণ সংগ্রহ। মিটার হলো নেটওয়ার্ক আর CDN, মেইন ব্রেকার ম্যানিফেস্ট আনা আর CORS, ডিস্ট্রিবিউশন বোর্ড ম্যানিফেস্টের ভেতরটা, ওয়্যারিং কোডেক আর MSE append, সুইচ DRM, সকেট বাফার আর ABR, আর TV নিজে রেন্ডারিং ও আউটপুট (HDCP, কালো স্ক্রিন)। টেস্টার হলো DevTools, media-internals, প্লেয়ারের ডিবাগ লগ। আর ফাতিমার AC হলো ডিভাইসের নিজস্ব স্বভাব: স্মার্ট TV বা পুরনো ফোনের সীমাবদ্ধতা, যা পুরো সিস্টেমের দোষ নয়। এই চ্যাপ্টার হাইসামের পদ্ধতিটা প্লেয়ারের জন্য লিখে রাখে।
প্রথম প্রশ্ন: কোন প্লেব্যাক পথ চলছে?
কোনো কমান্ড চালানোর আগে জানতে হবে ব্রাউজার আসলে কোন পথে ভিডিও চালাচ্ছে, কারণ তিনটা পথের সমস্যা আর টুল সম্পূর্ণ আলাদা।
| পথ | কীভাবে চিনবেন | কে দায়িত্বে |
|---|---|---|
নেটিভ (video.src সরাসরি .m3u8 বা .mp4) | video.currentSrc একটা সাধারণ URL | ব্রাউজারের নিজের প্লেয়ার; JavaScript লাইব্রেরির লগ এখানে কিছুই বলবে না |
| MSE (hls.js, Shaka, dash.js) | video.currentSrc শুরু হয় blob: দিয়ে | লাইব্রেরি; তার ডিবাগ লগ আর ইভেন্টই মূল সূত্র |
| EME (DRM, নেটিভ বা MSE-র উপর) | video.mediaKeys আছে | লাইসেন্স সার্ভার, CDM আর ডিভাইসের নিরাপত্তা স্তর |
Chrome এখন HLS নেটিভ চালায়, আর এটা পুরনো অনেক কোডের আচরণ বদলে দিয়েছে। অনেক বছর ধরে নিয়ম ছিল: শুধু Safari .m3u8 নেটিভ চালায়, বাকি সব ব্রাউজারে hls.js লাগে। তাই অনেক পেজ এভাবে লেখা: “যদি canPlayType('application/vnd.apple.mpegurl') কিছু ফেরত দেয়, নেটিভ চালাও।” Mux-এর ২০২৫ সালের অক্টোবরের ইনসিডেন্ট রিপোর্ট অনুযায়ী Chrome 141-এ কিছু ইউজারের কাছে আর 142-এ ডিফল্টভাবে নেটিভ HLS চালু হয়েছে। এই চ্যাপ্টারের জন্য Chrome 155-এ পরীক্ষা করে দেখা গেছে canPlayType ফেরত দেয় "maybe" আর video.src = 'master.m3u8' সরাসরি চলে। ফলে ওই পুরনো চেক এখন Chrome-কেও নেটিভ পথে পাঠায়। তখন hls.js-এর ABR, CMCD, লেভেলের ইভেন্ট, আপনার অ্যানালিটিক্সের রেন্ডিশন ডেটা, সব চুপচাপ উধাও। Mux সেই সময় একটা Chrome বাগের কারণে কোড 3 বা 4-এর এরর বাড়তে দেখেছিল, আর MSE পথকে অগ্রাধিকার দেওয়ার পরামর্শ দিয়েছিল। আপনার কোডে চেকের ক্রম দেখুন: MSE থাকলে আগে MSE, নেটিভ শুধু যেখানে MSE নেই। (২০২৬ সালের অক্টোবর অনুযায়ী; Chrome-এর রিলিজ নোট আর chromestatus দেখে নিশ্চিত হোন।)
টুলবক্স
| টুল | কোথায় | কী দেখায় |
|---|---|---|
| DevTools Media প্যানেল | Chrome DevTools → More tools → Media | প্রতিটা প্লেয়ারের প্রপার্টি, ইভেন্ট, মেসেজ লগ, বাফারের টাইমলাইন; JSON হিসেবে এক্সপোর্ট |
chrome://media-internals | Chrome/Edge-এর ঠিকানা বারে | একই ধরনের তথ্য, সব ট্যাবের প্লেয়ার একসাথে; ডিকোডারের নাম আর হার্ডওয়্যার ডিকোড হচ্ছে কিনা |
| DevTools Network | সব ব্রাউজারে | ম্যানিফেস্ট, সেগমেন্ট, কী, লাইসেন্স রিকোয়েস্ট; স্ট্যাটাস, CORS এরর, সময় |
| hls.js ডিবাগ | new Hls({ debug: true }) | প্রতিটা লোড, পার্স, append, লেভেল বদল, এরর কনসোলে |
| Shaka ডিবাগ | ডিবাগ বিল্ড লোড করে লগ লেভেল বাড়ান | একই ধরনের বিস্তারিত লগ |
| Safari Web Inspector | Safari → Develop মেনু; iPhone USB দিয়ে যুক্ত করে একই মেনু থেকে | iOS-এর আসল ডিভাইসের কনসোল আর নেটওয়ার্ক |
adb logcat | Android, USB ডিবাগিং চালু করে | নেটিভ অ্যাপের প্লেয়ারের লগ; Media3-তে EventLogger যোগ করলে প্রতিটা ইভেন্ট |
| Apple mediastreamvalidator | Apple-এর HTTP Live Streaming Tools প্যাকেজ (Apple Developer-এর ডাউনলোড পেজ থেকে) | একটা HLS স্ট্রিম Apple-এর নিয়ম মানে কিনা |
curl, ffprobe | টার্মিনাল | ম্যানিফেস্টের হেডার আর বডি; একটা সেগমেন্টের ভেতরে কী আছে |
DevTools-এর Media প্যানেল খোলার পথ Chrome-এর ডকস থেকে যাচাই করা। Apple-এর টুলের পেজ এই চ্যাপ্টার লেখার সময় পড়া যায়নি, তাই প্যাকেজের নাম আর ডাউনলোডের জায়গা Apple Developer সাইটে নিজে মিলিয়ে নিন (২০২৬ সালের অক্টোবর অনুযায়ী)।
সাত লেয়ারের পদ্ধতি
প্রতিটা লেয়ারে একটা প্রশ্ন। প্রশ্নের উত্তর “হ্যাঁ, ঠিক আছে” না হওয়া পর্যন্ত পরের লেয়ারে যাবেন না।
লেয়ার ১: নেটওয়ার্ক আর CORS। ম্যানিফেস্ট আর প্রথম সেগমেন্ট কি 200 দিয়ে আসছে, আর ব্রাউজার কি সেগুলো পড়তে পারছে? Network প্যানেলে লাল রিকোয়েস্ট বা কনসোলে “blocked by CORS policy” থাকলে এখানেই থামুন। টার্মিনাল থেকে একই URL curl -sI দিয়ে দেখুন; সেখানে চললে আর ব্রাউজারে না চললে সমস্যা প্রায় নিশ্চিতভাবে CORS, কারণ curl CORS মানে না। CDN-এর দিকের সমস্যা হলে CDN ডিবাগিং প্লেবুক-এ যান।
curl -sI -H 'Origin: https://baytalhikma.org' https://cdn.cordoba.example/v/42/master.m3u8 | grep -iE '^(HTTP|access-control|content-type|cache-control|age)' HTTP/2 200
content-type: application/vnd.apple.mpegurl
access-control-allow-origin: https://baytalhikma.org
cache-control: public, max-age=60
age: 12 (উদাহরণ।) access-control-allow-origin না থাকলে বা অন্য অরিজিন দেখালে ব্রাউজার রেসপন্স ফেলে দেবে, যদিও স্ট্যাটাস 200।
লেয়ার ২: ম্যানিফেস্ট ঠিক আছে কি? ম্যানিফেস্ট খুলে পড়ুন। master-এ CODECS আছে? media playlist-এ EXT-X-TARGETDURATION কোনো সেগমেন্টের চেয়ে ছোট নয় তো? লাইভে EXT-X-MEDIA-SEQUENCE বাড়ছে? সেগমেন্টের URI আপেক্ষিক হলে কোথায় গিয়ে পড়ছে? Apple ডিভাইসে সমস্যা হলে mediastreamvalidator চালান।
লেয়ার ৩: কোডেক আর MSE append। ব্রাউজার কি এই কোডেক চালাতে পারে, আর append কি সফল হচ্ছে? কনসোলে MediaSource.isTypeSupported('video/mp4; codecs="…"') দিয়ে ম্যানিফেস্টের ঠিক কোডেক স্ট্রিংটা পরীক্ষা করুন। media-internals-এ append বা ডিমাক্সের এরর খুঁজুন। hls.js-এ bufferAppendError বা MANIFEST_INCOMPATIBLE_CODECS_ERROR এই লেয়ারের।
লেয়ার ৪: DRM। কী সিস্টেম কি পাওয়া গেছে (requestMediaKeySystemAccess সফল?), লাইসেন্স রিকোয়েস্ট গেছে আর 200 এসেছে? লাইসেন্স সার্ভারের এররের বডি পড়ুন, ওখানেই সাধারণত আসল কারণ লেখা থাকে। DRM চ্যাপ্টার-এ লাইসেন্স ফ্লো বিস্তারিত।
লেয়ার ৫: বাফার আর ABR। প্লেয়ার কি আটকে আছে কারণ ডেটা আসছে না, নাকি ডেটা আছে কিন্তু প্লেহেড ভুল জায়গায়? নিচের স্নিপেট বা Media প্যানেলের টাইমলাইনে buffered আর currentTime তুলনা করুন। ABR কোন লেভেলে আছে আর থ্রুপুট কত মাপছে?
লেয়ার ৬: রেন্ডারিং আর আউটপুট। ডেটা ডিকোড হচ্ছে (totalVideoFrames বাড়ছে) কিন্তু স্ক্রিন কালো? তাহলে সমস্যা আউটপুটে: HDCP, হার্ডওয়্যার ডিকোডার, CSS (ভিডিওর উপর কোনো এলিমেন্ট), বা ভিডিওর আকার শূন্য।
লেয়ার ৭: ডিভাইসের স্বভাব। উপরের সব ঠিক, কিন্তু শুধু একটা ডিভাইসে, একটা OS ভার্সনে, একটা TV ব্র্যান্ডে সমস্যা? তাহলে ওই ডিভাইসের সীমা: মেমরি, পুরনো ব্রাউজার ইঞ্জিন, সমর্থিত কোডেক লেভেল, বা অ্যাপের নিজস্ব নিয়ম (মোবাইল ও TV চ্যাপ্টার)।
একটা কনসোল স্নিপেট
যেকোনো পেজের কনসোলে পেস্ট করুন। পেজের প্রতিটা video এলিমেন্টের অবস্থা এক নজরে দেখায়, আর পেজ যদি hls.js ইনস্ট্যান্স window.hls-এ রাখে, লেভেলগুলোও। কোনো লাইব্রেরি লাগে না।
(() => {
const RS = ['HAVE_NOTHING', 'HAVE_METADATA', 'HAVE_CURRENT_DATA', 'HAVE_FUTURE_DATA', 'HAVE_ENOUGH_DATA'];
const NS = ['NETWORK_EMPTY', 'NETWORK_IDLE', 'NETWORK_LOADING', 'NETWORK_NO_SOURCE'];
const ME = ['', 'MEDIA_ERR_ABORTED', 'MEDIA_ERR_NETWORK', 'MEDIA_ERR_DECODE', 'MEDIA_ERR_SRC_NOT_SUPPORTED'];
const ranges = (r) => Array.from({ length: r.length }, (_, i) => `${r.start(i).toFixed(2)}-${r.end(i).toFixed(2)}`).join(', ') || 'none';
document.querySelectorAll('video').forEach((v, n) => {
const b = v.buffered, t = v.currentTime;
let ahead = 0;
for (let i = 0; i < b.length; i++) if (t >= b.start(i) && t <= b.end(i)) ahead = b.end(i) - t;
const q = v.getVideoPlaybackQuality?.();
console.log(`video #${n}`, {
src: v.currentSrc.slice(0, 80) || '(none)',
mse: v.currentSrc.startsWith('blob:'),
drm: Boolean(v.mediaKeys),
readyState: RS[v.readyState],
networkState: NS[v.networkState],
error: v.error ? `${ME[v.error.code]} ${v.error.message}` : 'none',
paused: v.paused, ended: v.ended, seeking: v.seeking,
time: `${t.toFixed(2)} / ${v.duration}`,
rate: v.playbackRate, muted: v.muted, volume: v.volume,
size: `${v.videoWidth}x${v.videoHeight}`,
buffered: ranges(b), aheadSec: +ahead.toFixed(2), seekable: ranges(v.seekable),
frames: q ? `${q.droppedVideoFrames} dropped of ${q.totalVideoFrames}` : 'n/a',
textTracks: Array.from(v.textTracks, (tt) => `${tt.kind}:${tt.language}:${tt.mode}`).join(' ') || 'none'
});
});
const h = window.hls; // hls.js instance, if the page exposes one
if (h?.levels) {
console.table(h.levels.map((l, i) => ({ level: i, height: l.height, kbps: Math.round(l.bitrate / 1000), codecs: `${l.videoCodec},${l.audioCodec}`, current: i === h.currentLevel, loading: i === h.nextLoadLevel })));
console.log('hls.js estimate kbps', Math.round(h.bandwidthEstimate / 1000), 'live latency', h.latency);
}
})(); আগের চ্যাপ্টারের ছোট প্লেয়ারের উপর হেডলেস Chrome-এ চালানো আসল আউটপুট:
video #0 {
"src": "blob:http://127.0.0.1:8765/1620a585-abea-41a0-9226-e05d49654d79",
"mse": true,
"drm": false,
"readyState": "HAVE_ENOUGH_DATA",
"networkState": "NETWORK_LOADING",
"error": "none",
"paused": false,
"time": "3.75 / Infinity",
"size": "640x360",
"buffered": "0.00-23.96",
"aheadSec": 20.2,
"seekable": "0.00-23.96",
"frames": "0 dropped of 116",
"textTracks": "none"
} এই আউটপুটটা একটা আসল বাগ ধরেছিল: VOD ভিডিওর duration ছিল Infinity আর seekable থেমে ছিল বাফারের শেষে, অর্থাৎ seek বার দিয়ে ভিডিওর শেষ অংশে যাওয়াই যেত না। প্লেয়ার MediaSource.duration সেট করছিল না। ঠিক করার পর একই স্নিপেট দেখাল "time": "3.78 / 30" আর "seekable": "0.00-30.00"। স্নিপেটের প্রতিটা লাইন এরকম একটা প্রশ্নের উত্তর দেয়, তাই অভিযোগ এলে প্রথমে এটা চালিয়ে আউটপুটটা টিকিটে জুড়ে দিন।
কীভাবে পড়বেন: readyState HAVE_METADATA-তে আটকে থাকা মানে মেটাডেটা এসেছে কিন্তু প্রথম ফ্রেমের ডেটা নেই। aheadSec শূন্য অথচ buffered খালি নয় মানে প্লেহেড বাফারের বাইরে। frames বাড়ছে অথচ স্ক্রিন কালো মানে লেয়ার ৬। mse: false অথচ আপনার পেজ hls.js ব্যবহার করে বলে জানেন, মানে আপনি আসলে নেটিভ পথে আছেন।
লক্ষণ থেকে সমাধান: পূর্ণ ক্যাটালগ
শুরুই হয় না
| লক্ষণ | সম্ভাব্য কারণ | নিশ্চিত হবেন যেভাবে | সমাধান |
|---|---|---|---|
| প্লে চাপলে কিছুই হয় না, কোনো এরর নেই | অটোপ্লে আটকেছে, বা play()-এর প্রত্যাখ্যান কেউ ধরেনি | কনসোলে NotAllowedError; video.play()-এর Promise-এ .catch আছে কিনা | মিউট করে অটোপ্লে (Chrome-এ সবসময় অনুমোদিত), নয়তো প্রত্যাখ্যান ধরে প্লে বাটন দেখান |
| স্পিনার ঘোরে, Network-এ ম্যানিফেস্ট লাল | CORS, টোকেনের মেয়াদ শেষ, ভুল URL | Network-এ স্ট্যাটাস আর কনসোলের CORS বার্তা; curl -sI -H 'Origin: …' | CDN-এ Access-Control-Allow-Origin; টোকেন নতুন করে নেওয়া |
| ম্যানিফেস্ট আসে, সেগমেন্ট বা কী-তে CORS এরর | ম্যানিফেস্ট আর সেগমেন্ট আলাদা হোস্টে, একটায় CORS নেই | Network-এ কোন রিকোয়েস্ট লাল | সেগমেন্ট, init, কী, ক্যাপশন, সবার হোস্টে একই CORS নিয়ম |
MEDIA_ERR_SRC_NOT_SUPPORTED (কোড 4) | ব্রাউজার এই ফরম্যাট নেটিভ চালায় না; বা ভুল Content-Type; বা সেগমেন্টের বদলে একটা HTML এরর পেজ | স্নিপেটে mse; curl -sI দিয়ে content-type; রেসপন্সের প্রথম বাইট | নেটিভ চালাতে না পারলে MSE লাইব্রেরি; সার্ভারের MIME টাইপ ঠিক করা |
hls.js: MANIFEST_INCOMPATIBLE_CODECS_ERROR | ম্যানিফেস্টের কোনো রেন্ডিশনের কোডেক এই ব্রাউজার চালায় না | ম্যানিফেস্টের CODECS নিয়ে MediaSource.isTypeSupported | সমর্থিত কোডেকে একটা রেন্ডিশন রাখুন (সাধারণত H.264) |
Safari-তে MSE পথে sourceopen কখনো আসে না | ManagedMediaSource সক্রিয় হয় শুধু disableRemotePlayback = true হলে বা AirPlay-এর জন্য একটা HLS source থাকলে (MDN) | কনসোলে sourceopen লিসেনার কখনো ফায়ার করে কিনা | video.disableRemotePlayback = true, অথবা বিকল্প HLS source দিন |
| iPhone-এ ভিডিও সাথে সাথে পুরো স্ক্রিনে চলে যায় বা চলেই না | playsinline নেই; বা পুরনো iPhone-এ MSE নেই | মার্কআপ দেখুন; 'ManagedMediaSource' in window আর 'MediaSource' in window | playsinline যোগ করুন; MSE না থাকলে নেটিভ HLS |
| প্রথম ফ্রেম আসতে অনেক দেরি | প্রথম সেগমেন্ট বড়, শুরুতে উঁচু রেন্ডিশন, DRM লাইসেন্স ধীর, CDN MISS | Network-এর waterfall: ম্যানিফেস্ট → init → প্রথম সেগমেন্ট → লাইসেন্স | নিচু রেন্ডিশন থেকে শুরু, ছোট প্রথম সেগমেন্ট, লাইসেন্স আগেভাগে (স্টার্টআপ চ্যাপ্টার) |
শব্দ আছে, ছবি নেই (বা উল্টো)
| লক্ষণ | সম্ভাব্য কারণ | নিশ্চিত হবেন যেভাবে | সমাধান |
|---|---|---|---|
শব্দ আছে, স্ক্রিন কালো, frames বাড়ছে না | ভিডিও ট্র্যাক ডিকোড হচ্ছে না: প্রোফাইল বা লেভেল ডিভাইসের সীমার বাইরে | media-internals-এ ভিডিও ডিকোডারের এরর; ffprobe-এ প্রোফাইল ও লেভেল | ডিভাইসের সীমার ভেতরে একটা রেন্ডিশন; ম্যানিফেস্টের CODECS সঠিক লিখুন |
শব্দ আছে, স্ক্রিন কালো, frames বাড়ছে | ডিকোড হচ্ছে কিন্তু দেখানো যাচ্ছে না: HDCP, CSS, ভিডিওর আকার শূন্য | স্নিপেটে size আর frames; Elements-এ ভিডিওর উপরে কিছু আছে কিনা | নিচের HDCP সারি দেখুন; CSS ঠিক করুন |
| কিছু ডিভাইসে শুধু শব্দ, কিছুতে ঠিক | উঁচু প্রোফাইল (High 4.2, 10-bit, HEVC) পুরনো ডিভাইস চালায় না | ডিভাইস ধরে এরর ভাগ করুন; ওই ডিভাইসে isTypeSupported | ল্যাডারের নিচে একটা সর্বজনীন রেন্ডিশন রাখুন |
| ছবি আছে, শব্দ নেই | অডিও কোডেক অসমর্থিত (যেমন AC-3), মিউট, বা আলাদা অডিও গ্রুপ লোড হয়নি | স্নিপেটে muted, volume; Network-এ অডিও সেগমেন্ট আসছে কিনা | AAC রেন্ডিশন রাখুন; অডিও গ্রুপের URI দেখুন |
| শব্দ আর ছবি সিঙ্কে নেই | প্যাকেজিংয়ে অডিও আর ভিডিওর শুরুর টাইমস্ট্যাম্প আলাদা, VFR সোর্স, এডিট লিস্ট উপেক্ষা | দুটো ট্র্যাকের প্রথম টাইমস্ট্যাম্প ffprobe -show_packets দিয়ে তুলনা | প্যাকেজারে দুটো ট্র্যাক একই সময় থেকে শুরু; সোর্স CFR-এ রূপান্তর |
চলতে চলতে আটকায়
| লক্ষণ | সম্ভাব্য কারণ | নিশ্চিত হবেন যেভাবে | সমাধান |
|---|---|---|---|
| বারবার রিবাফার | থ্রুপুট রেন্ডিশনের চেয়ে কম, ABR খুব আশাবাদী, CDN ধীর | QoE ডেটায় ওই সেশন (চ্যাপ্টার ১১); CMCD sid দিয়ে CDN লগ | ABR-এর নিরাপত্তা গুণক বাড়ান; CDN-এর MISS দেখুন |
| লাইভে অনন্ত স্পিনার, এনকোডার চলছে | প্লেলিস্ট নতুন সেগমেন্ট পাচ্ছে না (origin বা প্যাকেজার থেমে আছে), বা CDN পুরনো প্লেলিস্ট ক্যাশ করছে | curl দিয়ে প্লেলিস্ট দুবার, কয়েক সেকেন্ড ব্যবধানে: MEDIA-SEQUENCE বাড়ছে? Age কত? | প্লেলিস্টের TTL সেগমেন্টের দৈর্ঘ্যের অর্ধেকের কম; প্যাকেজার দেখুন |
| লাইভ চালু, স্পিনার, এরর নেই, বাফার ভর্তি | প্লেহেড বাফারের বাইরে (লাইভের মিডিয়া টাইম ০ থেকে শুরু নয়) | স্নিপেটে time বনাম buffered | প্লেহেড বাফারের শুরুতে আনুন (চ্যাপ্টার ১২) |
hls.js: bufferStalledError | বাফার ফুরিয়েছে; বা বাফারে ছোট ফাঁক | buffered-এ একাধিক রেঞ্জ আছে কিনা | ফাঁক থাকলে প্যাকেজিংয়ে সেগমেন্টের সীমানা মেলান; লাইব্রেরির gap-jump সেটিং |
লম্বা সেশনে append ব্যর্থ, QuotaExceededError বা bufferFullError | পেছনের বাফার কখনো মোছা হয় না | এররের নাম; buffered-এর শুরু অনেক পেছনে | পেছনের বাফার সীমিত করুন (hls.js-এ backBufferLength) |
| কোয়ালিটি নিচেই আটকে থাকে | থ্রুপুট মাপ ভুল (ছোট সেগমেন্ট, CORS preflight), প্লেয়ারের আকারের ক্যাপ, Media Capabilities বাদ দিয়েছে | স্নিপেটে লেভেল আর estimate kbps; প্লেয়ারের আকার | ক্যাপ নিয়ম দেখুন; থ্রুপুট মাপে কী ঢুকছে দেখুন |
| কোয়ালিটি ঘনঘন ওঠানামা করে | ABR অস্থির, শুধু থ্রুপুটের উপর নির্ভর | লেভেল বদলের ইভেন্ট গুনুন | ওঠার জন্য বাফারের শর্ত; হাইব্রিড ABR (বাফার ও ABR) |
| উঁচু রেন্ডিশন বেছেছে কিন্তু স্ক্রিনে নিচুটাই | বাফারে আগের রেন্ডিশন জমা | লেভেল বদলের সময় থেকে বাফারের দৈর্ঘ্য | স্বাভাবিক; দ্রুত চাইলে সামনের বাফার বদলানো |
seek, ক্যাপশন, DRM, আউটপুট
| লক্ষণ | সম্ভাব্য কারণ | নিশ্চিত হবেন যেভাবে | সমাধান |
|---|---|---|---|
| seek বার ভিডিওর শেষ পর্যন্ত যায় না, দৈর্ঘ্য দেখায় না | MSE-তে duration সেট হয়নি | স্নিপেটে Infinity আর seekable | mediaSource.duration সেট করুন |
| seek করলে একটু আগে বা পরে গিয়ে পড়ে | সেগমেন্ট কীফ্রেম দিয়ে শুরু হয় না, বা নেটিভ পথে দ্রুত seek কীফ্রেমে যায় | ffprobe -show_frames দিয়ে সেগমেন্টের প্রথম ফ্রেম key_frame=1? | প্রতিটা সেগমেন্ট কীফ্রেম দিয়ে শুরু; EXT-X-INDEPENDENT-SEGMENTS |
| ক্যাপশন দেখায় না | track-এর ফাইল আরেক অরিজিনে অথচ video-তে crossorigin নেই; ভুল MIME; ফাইল WEBVTT দিয়ে শুরু নয়; ট্র্যাকের mode disabled | স্নিপেটে textTracks; Network-এ .vtt রিকোয়েস্ট আর content-type | crossorigin অ্যাট্রিবিউট আর CORS; text/vtt; mode = 'showing' (ক্যাপশন চ্যাপ্টার) |
| ক্যাপশন আছে কিন্তু সময় সরে আছে | HLS-এ X-TIMESTAMP-MAP নেই বা ভুল, তাই WebVTT-র সময় মিডিয়ার সময়ের সাথে মেলে না | .vtt ফাইলের হেডার পড়ুন | প্যাকেজারে টাইমস্ট্যাম্প ম্যাপ ঠিক করুন |
| DRM: লাইসেন্স রিকোয়েস্টে 403 | টোকেনের মেয়াদ শেষ, ভুল কনটেন্ট আইডি, ইউজারের অধিকার নেই | লাইসেন্স রেসপন্সের বডি পড়ুন | টোকেন নতুন করুন; লাইসেন্স সার্ভারের লগ |
| DRM: লাইসেন্স 200, তবু চলে না | নিরাপত্তা স্তর (robustness) কম, ডিভাইস উঁচু রেজোলিউশনের অনুমতি পায় না | requestMediaKeySystemAccess-এর কনফিগ; কোন রেন্ডিশনে ব্যর্থ | ওই স্তরের জন্য অনুমোদিত রেন্ডিশনে সীমিত করুন |
| বাইরের মনিটরে বা প্রজেক্টরে কালো স্ক্রিন, ল্যাপটপে ঠিক | HDCP সমর্থন নেই এমন কেবল বা মনিটর; DRM নীতি আউটপুট আটকায় | শুধু DRM কনটেন্টে হয় কিনা; অন্য কেবলে চেষ্টা | ইউজারকে স্পষ্ট বার্তা; নীতি অনুযায়ী নিচু রেজোলিউশন |
| স্ক্রিন রেকর্ডিং বা স্ক্রিনশেয়ারে ভিডিও কালো | DRM-এর স্বাভাবিক সুরক্ষা | একই কনটেন্ট DRM ছাড়া রেকর্ড হয় কিনা | এটা বাগ নয়; সাপোর্টের উত্তর তৈরি রাখুন |
শুধু কিছু জায়গায়
| লক্ষণ | সম্ভাব্য কারণ | নিশ্চিত হবেন যেভাবে | সমাধান |
|---|---|---|---|
| Chrome-এ চলে, Safari-তে না | নেটিভ HLS পথ (Safari) কঠোর: কোডেক স্ট্রিং, টার্গেট ডিউরেশন, ক্যাপশন ফরম্যাট | Safari Web Inspector-এর কনসোল; mediastreamvalidator | Apple-এর HLS নিয়ম মেনে প্যাকেজ |
| Safari-তে চলে, Chrome আপডেটের পর অদ্ভুত আচরণ | Chrome এখন নেটিভ HLS চালায়, আপনার কোড নেটিভ পথ বেছে নিচ্ছে | স্নিপেটে mse: false | MSE আগে, নেটিভ পরে |
| ডেস্কটপে চলে, iOS-এ না | playsinline নেই, অটোপ্লে নীতি, MSE নেই, কম মেমরি | আসল iPhone USB দিয়ে Web Inspector-এ | উপরের iOS সারিগুলো |
| শুধু একটা TV ব্র্যান্ডে ক্র্যাশ বা বিকৃত ছবি | কম মেমরি, ছোট MSE কোটা, পুরনো ব্রাউজার ইঞ্জিন, কোডেক লেভেলের সীমা | ওই TV-র রিমোট ডিবাগিং বা লগ; ডিভাইস ধরে QoE | বাফার গোল কমান, ছোট রেন্ডিশন, ওই মডেলের জন্য আলাদা কনফিগ |
| incognito-তে চলে, সাধারণ উইন্ডোতে না | এক্সটেনশন (অ্যাড ব্লকার সেগমেন্ট বা লাইসেন্স URL আটকায়), পুরনো ক্যাশ, সার্ভিস ওয়ার্কার | এক্সটেনশন বন্ধ করে; Application প্যানেলে সার্ভিস ওয়ার্কার | URL প্যাটার্ন বদলান; সার্ভিস ওয়ার্কার আপডেট |
| SPA-তে কয়েকটা ভিডিও দেখার পর ট্যাব ধীর বা ক্র্যাশ | পুরনো প্লেয়ার ধ্বংস হয় না | DevTools Memory; Media প্যানেলে মৃত প্লেয়ার জমছে | রাউট বদলে hls.destroy() বা player.destroy() |
একটা ভালো বাগ রিপোর্টে কী থাকে
“ভিডিও চলে না” দিয়ে কিছু করা যায় না। সাপোর্ট টিম আর ইউজারের জন্য একটা ছোট ফর্ম রাখুন, আর প্রতিটা অভিযোগে এই জিনিসগুলো চান:
- পেজের URL আর ভিডিওর আইডি।
- ডিভাইস, OS ভার্সন, ব্রাউজার আর তার ভার্সন (Chrome-এ
chrome://version)। - ঠিক কখন (সময় অঞ্চলসহ), যাতে লগ খোঁজা যায়।
- প্লেয়ারের সেশন আইডি (
sid), যা QoE বিকন আর CDN লগ দুটোতেই আছে (চ্যাপ্টার ১১)। প্লেয়ারের এরর স্ক্রিনে এটা দেখালে ইউজার সহজে কপি করতে পারে। - উপরের কনসোল স্নিপেটের আউটপুট।
- সম্ভব হলে DevTools Media প্যানেলের JSON এক্সপোর্ট, বা Network প্যানেলের HAR ফাইল।
প্লেয়ারের এরর স্ক্রিনে একটা ছোট কোড দেখান, শুধু “কিছু একটা ভুল হয়েছে” নয়। যেমন E-NET-403 · 45f4227c: প্রথম অংশ কোন লেয়ার আর কোন এরর, দ্বিতীয় অংশ সেশন আইডির শুরু। ইউজারের কাছে এটা অর্থহীন নয়, সাপোর্টে পাঠানোর মতো একটা জিনিস। আর আপনার কাছে এটা লগের সরাসরি ঠিকানা। ইউজারের জন্য বার্তাটা সাধারণ ভাষায় রাখুন (“ভিডিওটা এই মুহূর্তে চালানো যাচ্ছে না, একটু পরে আবার চেষ্টা করুন”), কোডটা নিচে ছোট করে।
ইনসিডেন্টের সময় অভ্যাস
- আগে পরিধি, তারপর কারণ। QoE ড্যাশবোর্ডে দেখুন: সব ইউজার, নাকি একটা দেশ, একটা CDN, একটা ব্রাউজার ভার্সন, একটা প্লেয়ার রিলিজ? পরিধিই অর্ধেক উত্তর দেয়।
- শেষ কী বদলেছে? প্লেয়ার রিলিজ, প্যাকেজার কনফিগ, CDN নিয়ম, DRM কী, ব্রাউজারের নতুন ভার্সন। Chrome-এর নেটিভ HLS-এর মতো ঘটনা দেখায় যে “আমরা কিছু বদলাইনি” মানে “কিছুই বদলায়নি” নয়।
- একবারে একটা পরিবর্তন। দুটো একসাথে বদলালে কোনটা কাজ করল জানবেন না।
- লাইব্রেরির ডিবাগ লগ চালু করা যায় এমন একটা গোপন সুইচ রাখুন, যেমন URL-এ
?playerdebug=1, যাতে প্রোডাকশনে ইউজারের ডিভাইসেই লগ দেখা যায়। - ঠিক হওয়ার পর একটা পরীক্ষা যোগ করুন। এই চ্যাপ্টারের
duration: Infinityবাগটার মতো প্রতিটা ঠিক করা সমস্যা পরের বার একটা স্বয়ংক্রিয় চেক হয়ে ফিরুক।
মূল শিক্ষা
- প্রথম প্রশ্ন সবসময়: কোন প্লেব্যাক পথ?
currentSrc-এblob:মানে MSE,mediaKeysমানে DRM, বাকিটা নেটিভ। - Chrome এখন HLS নেটিভ চালায়; MSE আগে, নেটিভ পরে, নইলে লাইব্রেরি আর অ্যানালিটিক্স চুপচাপ বাদ পড়ে।
- লাইনের শুরু থেকে শেষ: নেটওয়ার্ক ও CORS, ম্যানিফেস্ট, কোডেক ও append, DRM, বাফার ও ABR, রেন্ডারিং, ডিভাইস। যে লেয়ারের আগে ঠিক আর পরে ভুল, সমস্যা সেখানে।
curl-এ চলে, ব্রাউজারে না = প্রায় সবসময় CORS।framesবাড়ছে কিনা দেখে ডিকোডের সমস্যা আর আউটপুটের সমস্যা আলাদা করুন।bufferedবনামcurrentTimeলাইভের অর্ধেক “অনন্ত স্পিনার” ব্যাখ্যা করে।- লাইসেন্স সার্ভারের এররের বডি পড়ুন; DRM-এর আসল কারণ ওখানেই।
- এরর স্ক্রিনে কোড আর সেশন আইডি দেখান, যাতে প্রতিটা অভিযোগ লগের ঠিকানা হয়ে আসে।
- প্রতিটা ঠিক করা বাগ একটা পরীক্ষা হয়ে ফিরুক।