লাইভের রিলায়েবিলিটি
ক্যামেরা থেকে দর্শক পর্যন্ত প্রতিটা হপ কোথায় ভাঙে, আর কীভাবে ভাঙলেও দর্শক টের না পায় — রিডানডেন্ট কন্ট্রিবিউশন, ইনজেস্ট ফেইলওভার, ডিসকন্টিনিউটি, স্লেট, A/B পাইপলাইন, SCTE-35, আর বড় ইভেন্টের রিহার্সাল।
গল্পে বুঝি
ময়মনসিংহের একটা জেলা হাসপাতাল, গ্রীষ্মের দুপুর। অপারেশন থিয়েটারে ডা. হাইসাম একটা জরুরি সিজারিয়ান করছেন, আর ঠিক তখন লোডশেডিং। কিন্তু থিয়েটারের আলো এক মুহূর্তের জন্যও নেভে না। কারণ হাসপাতালের ইঞ্জিনিয়ার বিরুনি বিদ্যুতের তিন স্তর বানিয়ে রেখেছেন। প্রথম স্তর একটা IPS, যেটা গ্রিড চলে যাওয়ার সাথে সাথে, মিলিসেকেন্ডে, থিয়েটারের আলো আর মনিটর চালু রাখে, কিন্তু মাত্র আধা ঘণ্টা পারে। দ্বিতীয় স্তর একটা ডিজেল জেনারেটর, যেটা চালু হতে আধা মিনিট নেয়, কিন্তু চালু হলে সারা রাত চলে। তৃতীয় স্তর হলো হাসপাতাল দুটো আলাদা ফিডার লাইন থেকে বিদ্যুৎ নেয়, একটা শহরের এক সাবস্টেশন থেকে, আরেকটা আরেক দিক থেকে। একটা লাইনে গাছ পড়লে আরেকটা দিয়ে চলে।
এই তিন স্তরের মাঝখানে একটা স্বয়ংক্রিয় সুইচ বসানো, যেটা গ্রিড চলে গেলে IPS-এ, জেনারেটর তৈরি হলে জেনারেটরে, আর গ্রিড ফিরলে আবার গ্রিডে নিয়ে যায়। বিরুনি সুইচটায় একটা বুদ্ধি দিয়েছেন: গ্রিড ফিরে এলেও সুইচ সাথে সাথে ফেরে না, দুই মিনিট অপেক্ষা করে দেখে বিদ্যুৎ স্থির কিনা, কারণ গ্রীষ্মে বিদ্যুৎ প্রায়ই দশ সেকেন্ডের জন্য এসে আবার চলে যায়, আর বারবার সুইচ করলে যন্ত্রপাতি নষ্ট হয়। সুইচ করার মুহূর্তে আলো একবার সামান্য কেঁপে ওঠে; ডা. হাইসাম সেটা জানেন, তাই সেই মুহূর্তে কাঁচি চালান না, এক সেকেন্ড থামেন।
আর একটা জিনিস আছে যা যন্ত্র নয়। প্রতি মাসের প্রথম শুক্রবার বিরুনি ইচ্ছে করে মূল সুইচ বন্ধ করে দেখেন সব ঠিকঠাক বদলায় কিনা, জেনারেটরে তেল আছে কিনা। দেয়ালে একটা কাগজ লাগানো: “বিদ্যুৎ গেলে কে কী করবে”, কার ফোন নম্বর, জেনারেটর কোন চাবিতে চালু হয়। একবার এক নার্স জিজ্ঞেস করেছিলেন, “যদি সব স্তরই একসাথে ব্যর্থ হয়?” বিরুনি বললেন, “তাহলে থিয়েটারে টর্চ আছে, আর রোগীকে জানানোর জন্য একটা পরিষ্কার নিয়ম আছে। আঁধারে চুপ করে থাকার চেয়ে টর্চ জ্বালিয়ে বলা ভালো, আমরা আছি।”
মিলিয়ে নিই: মূল গ্রিড হলো প্রাইমারি কন্ট্রিবিউশন পাথ, দুই ফিডার লাইন হলো দুই আলাদা ISP বা বন্ডেড সেলুলার লিংক, IPS হলো ভেন্যুর UPS (তাৎক্ষণিক কিন্তু অল্প সময়), জেনারেটর হলো ব্যাকআপ এনকোডার বা ব্যাকআপ পাইপলাইন (চালু হতে সময় নেয় কিন্তু টেকে)। স্বয়ংক্রিয় সুইচ হলো ইনজেস্ট ফেইলওভার, আর দুই মিনিট অপেক্ষা করে ফেরা হলো hysteresis, যাতে ফেইলওভার দোলাচলে না পড়ে। সুইচের মুহূর্তে আলোর কাঁপুনি হলো EXT-X-DISCONTINUITY, যেটা প্লেয়ারকে আগে থেকে জানিয়ে দেয় “এখানে একটা জোড়া আছে”। টর্চ আর “আমরা আছি” ঘোষণা হলো স্লেট, মাসিক পরীক্ষা হলো রিহার্সাল ও failover drill, আর দেয়ালের কাগজ হলো runbook। বাস্তবে একটা বড় ক্রিকেট ম্যাচ বা নির্বাচনী সম্প্রচার ঠিক এই স্তরগুলো দিয়েই নিশ্চিত করে যে স্টেডিয়ামে একটা তার খুলে গেলেও লাখো দর্শকের স্ক্রিন কালো হয় না।
ক্যামেরা থেকে দর্শক: কোথায় কী ভাঙে
ভিডিও ট্র্যাকের লো-লেটেন্সি লাইভ চ্যাপ্টারের শেষ অংশে ইনজেস্ট ছিঁড়ে যাওয়া, টাইমস্ট্যাম্প লাফ, ট্রান্সকোডার আর origin পড়ার মৌলিক জবাব আছে। এই চ্যাপ্টার সেখান থেকে শুরু করে পুরো চেইনটা দেখে, ভেন্যু থেকে প্লেয়ার পর্যন্ত, আর বড় ইভেন্টের প্রস্তুতি পর্যন্ত।
| হপ | কীভাবে ভাঙে | দর্শক কী দেখে | প্রতিরক্ষা |
|---|---|---|---|
| ক্যামেরা ও সুইচার | তার খোলে, সুইচার হ্যাং | কালো ছবি বা ফ্রিজ, কিন্তু স্ট্রিম “সুস্থ” | কনটেন্ট-সচেতন মনিটরিং (কালো, নীরবতা, ফ্রিজ) |
| ভেন্যুর বিদ্যুৎ | লোডশেডিং, ভুল প্লাগ | হঠাৎ স্ট্রিম শেষ | এনকোডার, সুইচ, রাউটারে UPS; জেনারেটর |
| ভেন্যুর নেটওয়ার্ক | ISP ডাউন, আপলিংক ভিড় | বাফারিং, তারপর বিচ্ছিন্ন | দুই ISP, বন্ডেড সেলুলার, SRT |
| এনকোডার | ক্র্যাশ, অতিরিক্ত গরম | বিচ্ছিন্ন, তারপর টাইমস্ট্যাম্প লাফ | দ্বিতীয় এনকোডার (A/B) |
| কন্ট্রিবিউশন পাথ | প্যাকেট লস, রুট বদল | পিক্সেলেশন, অডিও কাটা | SRT-এর রিট্রান্সমিশন, দুই পাথ |
| ইনজেস্ট সার্ভার | প্রসেস ক্র্যাশ, রিজিয়ন ডাউন | স্ট্রিম শেষ | প্রাইমারি ও ব্যাকআপ ইনজেস্ট URL, আলাদা রিজিয়নে |
| ট্রান্সকোডার ও packager | ক্র্যাশ, CPU-তে পিছিয়ে পড়া | একটা rung হারায়, বা সব থামে | A/B পাইপলাইন, rung ভাগ করে চালানো |
| Origin | ডিস্ক ভরা, প্রসেস ক্র্যাশ | সবাই একসাথে আটকে | দুই origin, নির্ধারিত সেগমেন্ট নাম |
| CDN | একটা PoP বা পুরো ভেন্ডর খারাপ | একটা অঞ্চল বা সবাই আটকে | মাল্টি-CDN, Content Steering (আগের চ্যাপ্টার) |
| প্লেয়ার | ডিসকন্টিনিউটি না বোঝা, এরর থেকে না ফেরা | ফ্রিজ, এরর স্ক্রিন | ব্যাকআপ variant, রিট্রাই নীতি |
এই টেবিল থেকে মূল নীতিটা বেরিয়ে আসে: প্রতিটা হপে অন্তত দুটো পথ, আর পথ বদলের সময় প্লেয়ারকে সত্যটা জানানো।
ভেন্যু: সবচেয়ে অবহেলিত হপ
অনেক টিম ক্লাউডে তিন রিজিয়নে ইনজেস্ট বসায়, কিন্তু স্টেডিয়ামে এনকোডারটা চালায় একটা মাত্র বিদ্যুতের সকেট আর একটা মাত্র ইন্টারনেট সংযোগ থেকে। চেইনের সবচেয়ে দুর্বল হপ প্রায় সবসময় ভেন্যু।
বিদ্যুৎ। এনকোডার, নেটওয়ার্ক সুইচ, রাউটার, সেলুলার বন্ডিং ডিভাইস, সবকিছু UPS-এ। শুধু এনকোডার UPS-এ থাকলে আর রাউটার না থাকলে লোডশেডিংয়ের মুহূর্তে এনকোডার চালু থাকে কিন্তু কিছু পাঠাতে পারে না। UPS-এর ব্যাকআপ সময় জেনারেটর চালু হওয়ার সময়ের চেয়ে অনেক বেশি রাখুন।
নেটওয়ার্ক। দুটো আলাদা ISP, যাদের ফাইবার আলাদা পথে আসে (একই খুঁটিতে দুই ISP-এর তার থাকলে একটা গাছ দুটোই কাটে)। তার উপর একটা সেলুলার ব্যাকআপ: কয়েকটা আলাদা অপারেটরের SIM একটা বন্ডিং ডিভাইসে, যাতে একটা অপারেটরের টাওয়ার ভিড়ে আটকালে অন্যটা চলে। স্টেডিয়ামে পঞ্চাশ হাজার মানুষ একসাথে মোবাইল ডেটা ব্যবহার করলে সেলুলার আপলিংক ভয়ানকভাবে খারাপ হয়, তাই সেলুলারকে প্রাইমারি নয়, শেষ ভরসা ধরুন।
আপলিংকের হিসাব। দুই পাথে একই স্ট্রিম পাঠালে আপলিংক দ্বিগুণ লাগে। ৮ Mbps-এর কন্ট্রিবিউশন স্ট্রিম দুই পাথে মানে ১৬ Mbps, আর নিরাপদ মার্জিন হিসেবে প্রতিটা লিংকে স্ট্রিমের অন্তত দেড় থেকে দুই গুণ ব্যান্ডউইথ থাকা ভালো (এটা মোটামুটি নিয়ম, নিজের লিংক মেপে নিন)।
রিডানডেন্ট কন্ট্রিবিউশন: তিনটা নকশা
Design 1: one encoder, two destinations
[camera] -> [encoder] --path A--> [ingest primary (region 1)]
\--path B--> [ingest backup (region 2)]
protects against: network path, ingest server, ingest region
does NOT protect against: encoder crash, venue power
Design 2: two encoders, two destinations (A/B)
[camera] -> [splitter] -> [encoder A] --path A--> [ingest primary]
\-> [encoder B] --path B--> [ingest backup]
protects against: encoder crash too
requires: A and B in lock-step (same timestamps, same keyframe positions)
Design 3: one encoder, bonded links to one logical destination
[camera] -> [encoder] --SRT group: ISP1 + ISP2 + cellular--> [ingest]
protects against: any single link
does NOT protect against: encoder or ingest failure বড় ইভেন্টে সাধারণত দুই আর তিন একসাথে: দুই এনকোডার, প্রত্যেকে বন্ডেড লিংকে, দুই আলাদা রিজিয়নের ইনজেস্টে।
SRT connection bonding
কন্ট্রিবিউশন প্রোটোকল চ্যাপ্টারে SRT দেখেছি। এখানে তার একটা ফিচার কাজে আসে: libsrt-এর socket group বা connection bonding। Haivision-এর SRT ডকস (২০২৬ সালের অক্টোবরে দেখা) এর ধরনগুলো বর্ণনা করে:
- Broadcast: একই স্ট্রিম সব লিংকে একসাথে পাঠায়, রিসিভার ডুপ্লিকেট প্যাকেট ফেলে দেয়। একটা লিংক মরলে শূন্য বিরতি, কিন্তু ব্যান্ডউইথ লিংকের সংখ্যা গুণ।
- Main/Backup: একটা লিংক ব্যবহার করে, অন্যগুলো প্রস্তুত রাখে; লিংকের অগ্রাধিকার
weightদিয়ে। ব্যান্ডউইথ সাশ্রয়ী, তবে সুইচে সামান্য সময় লাগে। - Balancing: প্রতিটা প্যাকেট একটা লিংকে, সব লিংক মিলিয়ে ব্যবহার। ডকস অনুযায়ী এটা এখনো under development।
ডকস বলে listener সকেটে SRTO_GROUPCONNECT ফ্ল্যাগ না থাকলে বন্ডেড কানেকশন প্রত্যাখ্যাত হয়, আর একটা ভাঙা লিংক নিজে থেকে ফেরে না, অ্যাপ্লিকেশনকে আবার কানেক্ট করতে হয়। আপনার এনকোডার আর ইনজেস্ট সার্ভার দুটোই এই ফিচার সাপোর্ট করে কিনা, আর কোন বিল্ডে, সেটা আগে নিশ্চিত হোন; সব SRT বাস্তবায়ন এটা দেয় না।
ইনজেস্ট ফেইলওভার: কখন সুইচ করবেন, কখন ফিরবেন
রিসিভিং দিকে দুটো স্ট্রিম আসছে, একটা “সক্রিয়”। প্রশ্ন দুটো: কখন ব্যাকআপে যাবেন, আর কখন প্রাইমারিতে ফিরবেন।
সুইচের শর্ত শুধু “কানেকশন বন্ধ” হলে যথেষ্ট নয়। কানেকশন খোলা কিন্তু কিছু আসছে না, বা আসছে কিন্তু ছবিটা কালো, এগুলো আরও বিপজ্জনক কারণ আরও নীরব। ভালো শর্তের তালিকা: N সেকেন্ড কোনো প্যাকেট নেই; বিটরেট প্রত্যাশার একটা ভগ্নাংশের নিচে; কীফ্রেম আসছে না; ছবি কালো বা ফ্রিজ; অডিও নীরব (খেলার মাঠে দীর্ঘ নীরবতা অস্বাভাবিক)।
ফেরার শর্ত সুইচের চেয়ে কঠোর হওয়া উচিত। এটাই hysteresis: প্রাইমারি ফিরে এলেও কিছু সময় স্থির থাকার প্রমাণ চাই, নইলে একটা দোদুল্যমান লিংক প্রতি কয়েক সেকেন্ডে সুইচ ঘটাবে, আর প্রতিটা সুইচে সম্ভবত একটা ডিসকন্টিনিউটি।
// Picks the active input with hysteresis: fail over fast, fail back slowly.
type Selector struct {
Active string // "primary" or "backup"
FailAfter time.Duration // primary unhealthy this long -> switch to backup
RecoverAfter time.Duration // primary healthy this long -> switch back
unhealthySince time.Time
healthySince time.Time
}
func (s *Selector) Observe(now time.Time, primaryHealthy bool) string {
if primaryHealthy {
s.unhealthySince = time.Time{}
if s.healthySince.IsZero() {
s.healthySince = now
}
if s.Active == "backup" && now.Sub(s.healthySince) >= s.RecoverAfter {
s.Active = "primary"
}
return s.Active
}
s.healthySince = time.Time{}
if s.unhealthySince.IsZero() {
s.unhealthySince = now
}
if s.Active == "primary" && now.Sub(s.unhealthySince) >= s.FailAfter {
s.Active = "backup"
}
return s.Active
} FailAfter কয়েক সেকেন্ড, RecoverAfter এক-দুই মিনিট, একটা সাধারণ শুরু। আর অনেক টিম লাইভ চলাকালীন স্বয়ংক্রিয়ভাবে ফেরে না: ব্যাকআপে চলে গেলে ইভেন্ট শেষ পর্যন্ত ব্যাকআপেই থাকে, বা অপারেটর হাতে ফেরান এমন মুহূর্তে যখন বিরতি চলছে। প্রতিটা সুইচ একটা ঝুঁকি, তাই অপ্রয়োজনীয় সুইচ এড়ানোই লক্ষ্য।
প্লেয়ার কীভাবে সুইচ টিকে থাকে
সুইচ হলো, এখন প্রশ্ন হলো দর্শক কী দেখবে। তিনটা স্তর, ভালো থেকে খারাপ:
১. অদৃশ্য সুইচ: টাইমস্ট্যাম্প মিলে আছে
যদি A আর B এনকোডার একই টাইমকোড উৎস থেকে টাইমস্ট্যাম্প নেয় আর একই মুহূর্তে কীফ্রেম বসায়, তাহলে সেগমেন্টের সীমানায় সুইচ করলে প্লেয়ারের কাছে কিছুই বদলায় না। প্লেলিস্টে কোনো বিশেষ ট্যাগ লাগে না। এটাই আদর্শ, আর এর জন্য এনকোডার দুটোকে লক-স্টেপে চালাতে হয়: একই ঘড়ি (NTP বা PTP দিয়ে সিঙ্ক করা), একই GOP দৈর্ঘ্য, আর কীফ্রেম ঘড়ির নির্দিষ্ট গুণিতকে (যেমন প্রতি দুই সেকেন্ডের ঠিক শুরুতে)। সব এনকোডার এটা পারে না; পেশাদার হার্ডওয়্যার ও সফটওয়্যার এনকোডারের ডকসে “output locking” বা “epoch locking” জাতীয় ফিচার খুঁজুন।
২. ঘোষিত সুইচ: EXT-X-DISCONTINUITY
টাইমস্ট্যাম্প না মিললে প্লেয়ারকে জানাতেই হবে। rfc8216bis খসড়া বলে ফাইল ফরম্যাট, ট্র্যাকের সংখ্যা-ধরন-আইডেন্টিফায়ার, বা টাইমস্ট্যাম্পের ধারা বদলালে EXT-X-DISCONTINUITY থাকতেই হবে (MUST), আর এনকোডিং প্যারামিটার বদলালে থাকা উচিত। Apple-এর authoring spec (২০২৬ সালের অক্টোবরে দেখা) এর সাথে যোগ করে তিনটা নিয়ম যা লাইভে প্রায়ই ভাঙে:
- সব variant আর rendition-এ ডিসকন্টিনিউটি একই সময়ে থাকতে হবে।
- লাইভ কনটেন্টে যদি কখনো
EXT-X-DISCONTINUITYআসার সম্ভাবনা থাকে, তাহলেEXT-X-DISCONTINUITY-SEQUENCEট্যাগ সবসময় থাকতে হবে। কারণ পুরনো সেগমেন্ট প্লেলিস্টের জানালা থেকে বেরিয়ে গেলে প্লেয়ার এই নম্বর দিয়েই বোঝে কোন সেগমেন্ট কোন “অধ্যায়ে”। - ডিসকন্টিনিউটিতে কোডেক বদলানো (যেমন HEVC থেকে H.264) এড়ানো উচিত।
# illustrative live media playlist across a failover from encoder A to B
#EXTM3U
#EXT-X-VERSION:7
#EXT-X-TARGETDURATION:4
#EXT-X-MEDIA-SEQUENCE:46051
#EXT-X-DISCONTINUITY-SEQUENCE:2
#EXT-X-MAP:URI="init_a.mp4"
#EXT-X-PROGRAM-DATE-TIME:2026-10-25T14:02:56.000Z
#EXTINF:4.000,
seg_46051.m4s
#EXTINF:4.000,
seg_46052.m4s
#EXT-X-DISCONTINUITY
#EXT-X-MAP:URI="init_b.mp4"
#EXT-X-PROGRAM-DATE-TIME:2026-10-25T14:03:05.120Z
#EXTINF:4.000,
seg_46053.m4s লক্ষ্য করুন ডিসকন্টিনিউটির পরে একটা নতুন EXT-X-PROGRAM-DATE-TIME, যাতে ওয়াল-ক্লক টাইমলাইন আবার নোঙর পায় (পরের চ্যাপ্টারে দেখব কেন এটা সিঙ্কের জন্য জরুরি), আর নতুন এনকোডারের কোডেক কনফিগ আলাদা হলে নতুন EXT-X-MAP।
৩. প্লেয়ার নিজেই পথ বদলায়: ব্যাকআপ variant
ইনজেস্ট বা origin পুরো মরে গেলে প্লেলিস্টই আসবে না, তখন ডিসকন্টিনিউটি ট্যাগ লেখারও কেউ নেই। তখন প্লেয়ারের নিজের হাতে একটা বিকল্প থাকা দরকার। Apple-এর spec বলে stream failover সাপোর্ট করা উচিত, যেমন multivariant playlist-এ ডুপ্লিকেট স্ট্রিম তালিকাভুক্ত করে: একই অ্যাট্রিবিউটের দুটো variant, আলাদা origin-এ। প্লেয়ার একটায় এরর পেলে অন্যটায় যায়। আধুনিক বিকল্প হলো আগের চ্যাপ্টারের Content Steering, যেখানে pathway-গুলোর অগ্রাধিকার সার্ভার থেকে নিয়ন্ত্রিত।
# illustrative: same rung listed twice, two origins
#EXT-X-STREAM-INF:BANDWIDTH=3200000,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2"
https://origin-a.baytalhikma.org/eid/720p/index.m3u8
#EXT-X-STREAM-INF:BANDWIDTH=3200000,RESOLUTION=1280x720,CODECS="avc1.64001f,mp4a.40.2"
https://origin-b.baytalhikma.org/eid/720p/index.m3u8 DASH-এ একই কাজ করে MPD-তে একাধিক BaseURL এলিমেন্ট, যেখান থেকে প্লেয়ার বিকল্প উৎস বাছে। কোন প্লেয়ার কোন অ্যাট্রিবিউট দিয়ে অগ্রাধিকার ঠিক করে, সেটা প্লেয়ার আর প্রোফাইল অনুযায়ী আলাদা; নিজের প্লেয়ারের ডকসে মিলিয়ে নিন।
স্লেট: আঁধারে চুপ না থেকে “আমরা আছি”
উৎস হারিয়ে গেলে দুটো পথ। এক, সেগমেন্ট বানানো বন্ধ করা। তখন প্লেলিস্ট থেমে যায়, প্লেয়ার বাফার শেষ করে আটকে যায়, অনেক প্লেয়ার কিছুক্ষণ পর এরর দিয়ে হাল ছাড়ে, আর উৎস ফিরলেও দর্শককে রিফ্রেশ করতে হয়। দুই, স্লেট: ট্রান্সকোডার উৎসের বদলে একটা স্থির ছবি আর নীরবতা দিয়ে একই ছন্দে সেগমেন্ট বানাতে থাকে। টাইমলাইন চলতে থাকে, প্লেয়ার চলতে থাকে, দর্শক দেখে “সংযোগ ফিরছে, সাথে থাকুন”, আর উৎস ফিরলে স্বয়ংক্রিয়ভাবে খেলা ফিরে আসে।
স্লেটের মূল শর্ত হলো এটা লাইভ ladder-এর সাথে হুবহু মেলা: একই রেজোলিউশন, একই প্রোফাইল ও লেভেল, একই ফ্রেম রেট, একই অডিও স্যাম্পল রেট ও চ্যানেল লেআউট। না মিললে স্লেটে ঢোকা আর বেরোনো দুটোই ডিসকন্টিনিউটি চায়, আর Apple-এর spec অনুযায়ী ফ্রেম রেট বা চ্যানেল লেআউট বদলানো ডিসকন্টিনিউটি ছাড়া চলেই না।
# Pre-render a slate clip that matches the 720p30 rung, with silent stereo AAC at 48 kHz.
ffmpeg -loop 1 -i slate-720p.png \
-f lavfi -i anullsrc=r=48000:cl=stereo \
-c:v libx264 -profile:v high -level:v 3.1 -pix_fmt yuv420p \
-r 30 -g 60 -keyint_min 60 -sc_threshold 0 -tune stillimage -b:v 400k \
-c:a aac -b:a 64k \
-t 4 slate-720p-4s.mp4 আরেকটা বিকল্প হলো EXT-X-GAP। খসড়া স্পেক অনুযায়ী এই ট্যাগ বলে পরের সেগমেন্টের URI-তে কোনো মিডিয়া নেই আর ক্লায়েন্টের সেটা লোড করা উচিত নয়। এতে টাইমলাইন চলে কিন্তু কোনো ছবি তৈরি করতে হয় না। সমস্যা হলো প্লেয়ারগুলো gap-কে ভিন্ন ভিন্নভাবে সামলায় (কেউ শেষ ফ্রেম ধরে রাখে, কেউ লাফ দেয়), আর দর্শক কোনো ব্যাখ্যা দেখে না। দর্শকের দিক থেকে স্লেট বেশি সৎ।
A/B পাইপলাইন: দুই origin, এক টাইমলাইন
ট্রান্সকোডার আর packager-এর রিডানডেন্সির সবচেয়ে পরিষ্কার নকশা হলো দুটো সম্পূর্ণ আলাদা পাইপলাইন, প্রত্যেকটা নিজের origin-এ লেখে, আর CDN বা প্লেয়ার দুটোর যেকোনোটা থেকে নিতে পারে। এটা কাজ করে শুধু একটা শর্তে: দুই পাইপলাইন একই নামের সেগমেন্টে একই সময়ের কনটেন্ট রাখবে।
সেটা নিশ্চিত করার উপায় হলো সেগমেন্ট নম্বর কোনো কাউন্টার থেকে না নিয়ে টাইমস্ট্যাম্প থেকে হিসাব করা:
segment_number = floor(presentation_time_since_epoch / segment_duration)
segment_duration = 4 s
a frame at 14:03:05.120 UTC on 2026-10-25
-> seconds since epoch ~ 1792936985.12
-> segment 448234246 (both pipelines compute the same number) যদি A আর B একই টাইমস্ট্যাম্প পায় (লক-স্টেপ এনকোডার থেকে, বা একই ইনজেস্ট থেকে) আর একই নিয়মে কীফ্রেম ও সেগমেন্ট সীমানা বসায়, তাহলে seg_448234246.m4s দুই origin-এ একই কনটেন্ট। CDN-এর origin failover তখন মাঝপথে A থেকে B-তে গেলেও প্লেয়ার কিছু টের পায় না। দুই পাইপলাইন একটা কাউন্টার নিজের মতো চালালে, একটা পাইপলাইন রিস্টার্ট হলেই নম্বর আলাদা হয়ে যায়, আর failover মানে হয় একই নামে ভিন্ন কনটেন্ট, যা ডিসকন্টিনিউটির চেয়েও খারাপ কারণ কেউ সেটা ঘোষণা করে না।
SCTE-35: বিজ্ঞাপনের সংকেত, শুরু থেকে শেষ
রিলায়েবিলিটির আলোচনায় SCTE-35 কেন? কারণ বড় সম্প্রচারে বিজ্ঞাপনের বিরতিই আয়, আর এর সংকেত পাঁচটা হপ পার হয়ে আসে। যেকোনো হপে, বিশেষত ব্যাকআপ পাথে, সংকেতটা হারালে বিজ্ঞাপন বসে না বা ভুল জায়গায় বসে, আর এই ব্যর্থতা কোনো “স্ট্রিম ডাউন” অ্যালার্টে ধরা পড়ে না।
শিকলটা:
- প্লেআউট বা অটোমেশন সিস্টেম ঠিক করে কখন বিরতি। সে এনকোডারকে জানায় SCTE-104 বার্তা দিয়ে, যা সাধারণত SDI সিগন্যালের ancillary ডেটায় বা IP দিয়ে যায়।
- এনকোডার SCTE-104 পড়ে MPEG-TS-এ একটা আলাদা PID-এ SCTE-35 splice বার্তা বসায়, আর বিরতির মুহূর্তে একটা কীফ্রেম রাখে যাতে ঠিক সেখানে কাটা যায় (একে segment conditioning বলে)।
- Packager SCTE-35 পড়ে HLS প্লেলিস্টে লেখে। স্ট্যান্ডার্ড উপায় হলো
EXT-X-DATERANGE, যেখানে RFC 8216 (অনুচ্ছেদ 4.3.2.7.1) নিয়ম দেয়:out_of_network_indicator= 1 থাকা splice বার্তা যায়SCTE35-OUTঅ্যাট্রিবিউটে, = 0 থাকা যায়SCTE35-IN-এ, আর অন্য কমান্ডSCTE35-CMD-এ, মান হিসেবে পুরোsplice_info_section-এর হেক্স। একটা splice_insert-এর out আর in একইIDশেয়ার করে। DASH-এ একই তথ্য যায় MPD-এর EventStream-এ বা সেগমেন্টের ভেতরেemsgবক্সে। - SSAI সার্ভার বা প্লেয়ার সেই চিহ্ন দেখে বিজ্ঞাপন বসায় (VOD আর প্লেয়ার ট্র্যাকে বিজ্ঞাপনের দিকটা)।
# illustrative: a 120-second break signalled with EXT-X-DATERANGE (hex shortened)
#EXT-X-PROGRAM-DATE-TIME:2026-10-25T14:30:00.000Z
#EXT-X-DATERANGE:ID="break-7",START-DATE="2026-10-25T14:30:00.000Z",PLANNED-DURATION=120.0,SCTE35-OUT=0xFC30250000...
#EXTINF:4.000,
seg_448234650.m4s
...
#EXT-X-DATERANGE:ID="break-7",START-DATE="2026-10-25T14:30:00.000Z",DURATION=120.0,SCTE35-IN=0xFC30250000... খসড়া স্পেক অনুযায়ী SCTE35-OUT থাকা ট্যাগে PLANNED-DURATION থাকতে পারে, আর প্লেলিস্টে কোনো EXT-X-DATERANGE থাকলে অন্তত একটা EXT-X-PROGRAM-DATE-TIME থাকতেই হবে। অনেক সিস্টেমে আপনি EXT-X-CUE-OUT / EXT-X-CUE-IN জাতীয় ট্যাগও দেখবেন; এগুলো ব্যাপক প্রচলিত কিন্তু ভেন্ডরদের প্রথা, HLS স্পেকের অংশ নয়, তাই আপনার SSAI কোনটা বোঝে আগে দেখে নিন।
ব্যাকআপ পাথে SCTE-35 পরীক্ষা করুন। সবচেয়ে সাধারণ ঘটনা: প্রাইমারি এনকোডার SCTE-35 পাস করে, ব্যাকআপ এনকোডারের কনফিগে সেই অপশন বন্ধ। ফেইলওভারের পরের সব বিরতি হারিয়ে যায়, আর কেউ ম্যাচ শেষে বিজ্ঞাপনের রিপোর্ট দেখার আগে টের পায় না। রিহার্সালে ব্যাকআপ পাথে একটা পরীক্ষামূলক বিরতি চালিয়ে প্লেলিস্টে SCTE35-OUT এসেছে কিনা দেখুন।
রিহার্সাল আর runbook
বিরুনির মাসিক পরীক্ষার মতোই, যে ফেইলওভার কখনো পরীক্ষা হয়নি সেটা আসলে ফেইলওভার নয়, একটা আশা। বড় ইভেন্টের আগে:
রিহার্সাল একই কনফিগে। একই এনকোডার, একই ladder, একই ইনজেস্ট, একই CDN, একই ভেন্যু হলে আরও ভালো। আর রিহার্সালে ইচ্ছাকৃতভাবে ভাঙুন: প্রাইমারি ISP-এর তার খুলুন, এনকোডার A বন্ধ করুন, একটা origin থামান, প্রতিবার দেখুন দর্শকের প্লেয়ারে কী হয়, কত সেকেন্ড লাগে, ডিসকন্টিনিউটি ঠিকমতো এসেছে কিনা, বিজ্ঞাপনের সংকেত টিকেছে কিনা।
Runbook এক পাতায়। লাইভ চলাকালীন কেউ লম্বা ডকুমেন্ট পড়বে না। প্রতিটা লক্ষণের পাশে: কে দায়িত্বে, কোন ড্যাশবোর্ড, কোন একটা কাজ।
EID LIVE 2026 - RUNBOOK (one page)
Decision owner on the day: Farabi (stream lead). Only Farabi switches A/B.
Comms: #eid-live channel; phone bridge open from T-60 min.
Symptom Check first Action
Black or frozen picture venue switcher, encoder switch camera source; if encoder: switch to B
Ingest A no packets 5s ingest dashboard auto-failover to B; do NOT fail back during the event
Viewers stalling, one region CDN per-PoP 5xx steer region to CDN-B
Viewers stalling, everywhere origin A/B health fail CDN to origin B; confirm playlists advancing
Ad break missing playlist SCTE35-OUT trigger manual break from playout; note time for ad ops
Venue power lost UPS minutes remaining call venue electrician; slate goes up automatically
Timeline: T-60 slate live | T-30 full ladder warm | T-10 final checks | T-0 programme ঘটনার পরে রিভিউ। প্রতিটা ফেইলওভার, এমনকি সফলগুলোও, লিখে রাখুন: কখন, কেন, কত সেকেন্ড, দর্শক কী দেখল। পরের ইভেন্টের রিহার্সালের তালিকা এখান থেকেই আসে।
ডিবাগিং: এই চ্যাপ্টারের সমস্যাগুলো
| লক্ষণ | নিশ্চিত করার উপায় | সমাধান |
|---|---|---|
| ফেইলওভারের পরে প্লেয়ার জমে যায় বা ভাঙা ছবি দেখায় | সুইচের মুহূর্তের প্লেলিস্ট দেখুন: EXT-X-DISCONTINUITY আছে কিনা; ffprobe -show_packets দিয়ে দুই পাশের সেগমেন্টের PTS তুলনা করুন | টাইমস্ট্যাম্প না মিললে ডিসকন্টিনিউটি দিন; নয়তো এনকোডার লক-স্টেপ করুন |
| কিছু rendition-এ ঠিক, কিছুতে ফ্রিজ | প্রতিটা media playlist-এ ডিসকন্টিনিউটি একই সেগমেন্টে কিনা দেখুন | সব variant-এ একই মুহূর্তে ডিসকন্টিনিউটি (Apple-এর নিয়ম) |
| পুরনো সেগমেন্ট জানালা থেকে বেরোনোর পর প্লেয়ার টাইমলাইন গুলিয়ে ফেলে | প্লেলিস্টে EXT-X-DISCONTINUITY-SEQUENCE আছে কিনা | লাইভে ট্যাগটা সবসময় রাখুন, প্রতি বেরিয়ে যাওয়া ডিসকন্টিনিউটিতে বাড়ান |
| ফেইলওভার প্রতি কয়েক সেকেন্ডে আসা-যাওয়া করছে | সিলেক্টরের লগে সুইচের সময় দেখুন | hysteresis: ফেরার শর্ত কঠোর করুন বা ইভেন্টে স্বয়ংক্রিয় ফেরা বন্ধ |
| উৎস চলে গেলে সবার প্লেয়ারে এরর, উৎস ফিরলেও না ফেরে | প্লেলিস্ট ওই সময়ে থেমে ছিল কিনা দেখুন | স্লেট চালু করুন; প্লেলিস্ট কখনো থামবে না |
| স্লেটে ঢোকা বা বেরোনোর সময় অডিও উধাও | ffprobe দিয়ে স্লেট আর লাইভের অডিও স্যাম্পল রেট ও চ্যানেল তুলনা করুন | স্লেট ladder-এর সাথে হুবহু মেলান, নয়তো ডিসকন্টিনিউটি দিন |
| CDN-এর origin failover-এর পর ভুল কনটেন্ট বা লাফ | দুই origin-এ একই নামের সেগমেন্ট ডাউনলোড করে হ্যাশ বা PTS তুলনা করুন | সেগমেন্ট নম্বর টাইমস্ট্যাম্প থেকে হিসাব; কাউন্টার নয় |
| ব্যাকআপে যাওয়ার পর বিজ্ঞাপন বসছে না | ব্যাকআপ পাথের প্লেলিস্টে SCTE35-OUT বা EXT-X-DATERANGE খুঁজুন; TS-এ SCTE-35 PID আছে কিনা | ব্যাকআপ এনকোডারে SCTE-35 পাসথ্রু চালু; রিহার্সালে পরীক্ষা |
EXT-X-DATERANGE থাকা প্লেলিস্ট কিছু প্লেয়ার প্রত্যাখ্যান করে | প্লেলিস্টে EXT-X-PROGRAM-DATE-TIME আছে কিনা | DATERANGE থাকলে অন্তত একটা PDT বাধ্যতামূলক |
| লোডশেডিংয়ে এনকোডার চালু কিন্তু স্ট্রিম বন্ধ | ভেন্যুতে রাউটার আর সুইচ UPS-এ আছে কিনা | পুরো চেইন UPS-এ; রিহার্সালে মূল সুইচ বন্ধ করে দেখুন |
মূল শিক্ষা
- প্রতিটা হপে অন্তত দুটো পথ, ভেন্যু সহ। ক্লাউডে তিন রিজিয়ন আর ভেন্যুতে এক সকেট হলে দুর্বলতম হপ ভেন্যুই।
- দ্রুত ব্যর্থ, ধীরে ফেরা। hysteresis ছাড়া ফেইলওভার দোলাচলে পড়ে, আর প্রতিটা সুইচ একটা ঝুঁকি।
- লক-স্টেপ এনকোডার সুইচকে অদৃশ্য করে। একই ঘড়ি, একই GOP, একই কীফ্রেম অবস্থান।
- না মিললে সত্যটা বলুন।
EXT-X-DISCONTINUITYসব variant-এ একই মুহূর্তে,EXT-X-DISCONTINUITY-SEQUENCEসবসময়, আর ডিসকন্টিনিউটির পরে নতুন PDT। - প্লেলিস্ট কখনো থামাবেন না। উৎস না থাকলে স্লেট, যা ladder-এর সাথে হুবহু মেলে।
- A/B পাইপলাইনে সেগমেন্ট নম্বর টাইমস্ট্যাম্প থেকে, যাতে দুই origin-এ একই নামে একই কনটেন্ট থাকে।
- SCTE-35 পাঁচ হপ পার হয়, আর ব্যাকআপ পাথে হারানো সবচেয়ে সাধারণ ব্যর্থতা।
- যে ফেইলওভার পরীক্ষা হয়নি, সেটা আশা। রিহার্সালে ইচ্ছাকৃতভাবে ভাঙুন, আর runbook এক পাতায় রাখুন।