পাঠ ৪৮ · ৫১-এর মধ্যে · মডিউল ১১
Home / Courses / System Design / ভিডিও স্ট্রিমিং প্ল্যাটফর্ম

কেস স্টাডি: ভিডিও স্ট্রিমিং প্ল্যাটফর্ম ডিজাইন করা

Case study: designing a video streaming platform
১১ মিনিট পড়া উচ্চ · Advanced Python কোডসহ সম্পূর্ণ বাংলায়

এই পাঠে যা শিখবেন

  • ভিডিও প্ল্যাটফর্মের আপলোড/ট্রান্সকোডিং পাইপলাইন কেন একটি ক্লাসিক অ্যাসিনক্রোনাস ব্যাচ-জব সমস্যা (L22, L24)
  • অ্যাডাপটিভ বিটরেট স্ট্রিমিং (HLS/DASH)-এর হাই-লেভেল কার্যপ্রণালী
  • CDN (L20) কেন ভিডিওর জন্য বিশেষভাবে গুরুত্বপূর্ণ
  • Python দিয়ে pick_quality() বাস্তবায়ন করে দেখানো কীভাবে পরিবর্তনশীল ব্যান্ডউইথে কোয়ালিটি ওঠানামা করে

১ · রিকোয়ারমেন্ট

ফাংশনাল
ভিডিও আপলোড, একাধিক কোয়ালিটিতে ট্রান্সকোড, অ্যাডাপটিভ স্ট্রিমিং প্লেব্যাক, ভিউ কাউন্ট।
নন-ফাংশনাল
বিশাল স্টোরেজ, বিপুল গ্লোবাল রিড/স্ট্রিম ভলিউম, দর্শকের বিভিন্ন নেটওয়ার্ক কন্ডিশনে মসৃণভাবে খাপ খাওয়ানো।

লক্ষ্য করুন — এখানে রাইট (আপলোড) তুলনামূলক বিরল কিন্তু ভারী (একটি ভিডিও কয়েক মিনিটের প্রসেসিং দাবি করতে পারে), অথচ রিড (স্ট্রিমিং) অতি ঘন ঘন কিন্তু প্রতিটি ছোট ছোট সেগমেন্ট রিকোয়েস্টে ভাগ — এই অসামঞ্জস্যই আর্কিটেকচারকে দুই ভাগে ভাগ করে দেয়।

২ · ক্যাপাসিটি অনুমান — আপলোড বনাম স্ট্রিম রিড

L02-এর পদ্ধতিতে: ধরি প্রতিদিন ৫,০০,০০০টি নতুন ভিডিও আপলোড হয়, প্রতিটি গড়ে ৫০০ MB (একাধিক রেজোলিউশনে ট্রান্সকোড করার আগের কাঁচা ফাইল)। আর প্রতিদিন ৫০ কোটি (৫০,০০,০০,০০০) ভিডিও-ভিউ হয়, প্রতিটি ভিউ গড়ে ৩ মিনিট স্ট্রিম করে ৫ Mbps গড় বিটরেটে।

Python
uploads_per_day = 500_000
avg_upload_size_mb = 500

upload_storage_per_day_tb = (uploads_per_day * avg_upload_size_mb) / 1_000_000  # MB -> TB

views_per_day = 500_000_000
avg_watch_minutes = 3
avg_bitrate_mbps = 5

# মোট স্ট্রিমিং ব্যান্ডউইথ (বিট) = ভিউ x সেকেন্ড x Mbps
total_watch_seconds_per_day = views_per_day * avg_watch_minutes * 60
total_stream_megabits_per_day = total_watch_seconds_per_day * avg_bitrate_mbps
avg_stream_mbps_across_platform = total_stream_megabits_per_day / (24 * 60 * 60)

print(f"আপলোড স্টোরেজ/দিন: {upload_storage_per_day_tb:,.1f} TB")
print(f"গড় প্ল্যাটফর্ম-ওয়াইড স্ট্রিমিং থ্রুপুট: {avg_stream_mbps_across_platform:,.0f} Mbps")
print(f"গড় থ্রুপুট (Gbps-এ): {avg_stream_mbps_across_platform / 1000:,.1f} Gbps")

    
লক্ষ্য করুন — গড় স্ট্রিমিং থ্রুপুট (~হাজার হাজার Gbps) আপলোড স্টোরেজের (~২৫০ TB/দিন) তুলনায় সম্পূর্ণ ভিন্ন মাত্রার সমস্যা। আপলোড একটি স্টোরেজ/ব্যাচ-প্রসেসিং চ্যালেঞ্জ; স্ট্রিমিং একটি রিয়েল-টাইম ব্যান্ডউইথ-ডেলিভারি চ্যালেঞ্জ — দুটোরই সমাধান আলাদা।

৩ · হাই-লেভেল ডিজাইন

আপলোড হওয়া ভিডিও প্রথমে ব্লব স্টোরেজে যায়, তারপর একটি মেসেজ কিউ (L22) একটি অ্যাসিনক্রোনাস ট্রান্সকোডিংTranscodingএকটি ভিডিও ফাইলকে একাধিক রেজোলিউশন/বিটরেটে (240p, 480p, 720p, 1080p) রূপান্তর করা, যাতে প্লেয়ার নেটওয়ার্ক অনুযায়ী বেছে নিতে পারে। জব সারিবদ্ধ করে — এটি একটি ক্লাসিক ব্যাচ-জব ব্যবহার (L24)। প্রতিটি রেজোলিউশন ছোট ছোট সেগমেন্টে ভাগ হয়ে CDN-এ (L20) পুশ হয়। প্লেব্যাকের সময় প্লেয়ার নিকটতম CDN এজ থেকে সেগমেন্ট আনে, প্রতিবার pick_quality() চালিয়ে পরের সেগমেন্টের রেজোলিউশন ঠিক করে।

আপলোড Upload ব্লব স্টোরেজ Blob Storage কিউ + ট্রান্সকোড Queue (L22) + Transcode CDN এজ (L20) CDN Edge প্লেয়ার — pick_quality() Adaptive Bitrate Playback
আপলোড→ট্রান্সকোড ধাপ একবার ঘটে (ব্যাচ); CDN→প্লেব্যাক ধাপ লক্ষ লক্ষবার ঘটে (রিয়েল-টাইম)।

৪ · ডিপ ডাইভ — অ্যাডাপটিভ বিটরেট কোয়ালিটি সিলেকশন

HLS/DASH-এর মূল ধারণা: ভিডিও একাধিক কোয়ালিটি লেভেলে ছোট সেগমেন্টে (সাধারণত ২-১০ সেকেন্ড) এনকোড করা থাকে। প্লেয়ার ক্রমাগত সাম্প্রতিক ডাউনলোড থ্রুপুট পরিমাপ করে এবং পরবর্তী সেগমেন্ট আনার আগে সিদ্ধান্ত নেয় — বর্তমান উপলব্ধ ব্যান্ডউইথে কোন কোয়ালিটি নিরবচ্ছিন্নভাবে চলবে। নিচের কোড ঠিক এই সিদ্ধান্ত-লজিকটি বাস্তবায়ন করে — উপলব্ধ ব্যান্ডউইথের সর্বোচ্চ কোয়ালিটি বেছে নেয় যার প্রয়োজনীয়তা সেই ব্যান্ডউইথের মধ্যে পড়ে।

Python
# (কোয়ালিটি নাম, প্রয়োজনীয় ব্যান্ডউইথ Mbps) — বাড়ন্ত ক্রমে
quality_levels = [
    ("240p", 0.4),
    ("480p", 1.0),
    ("720p", 2.5),
    ("1080p", 5.0),
]

def pick_quality(available_bandwidth_mbps):
    chosen = quality_levels[0][0]   # ডিফল্ট: সবচেয়ে কম কোয়ালিটি
    for name, required_mbps in quality_levels:
        if required_mbps <= available_bandwidth_mbps:
            chosen = name            # যত বড় লেভেল ফিট হয়, তত ওপরে ওঠে
    return chosen

# একজন দর্শকের ব্যান্ডউইথ সময়ের সাথে ওঠানামা করছে (সিমুলেটেড রিডিং)
bandwidth_sequence_mbps = [6.0, 3.0, 0.8, 0.3, 1.5, 5.5]

print("সময়ের সাথে ব্যান্ডউইথ ও নির্বাচিত কোয়ালিটি:")
for step, bw in enumerate(bandwidth_sequence_mbps, start=1):
    quality = pick_quality(bw)
    print(f"  সেগমেন্ট {step}: ব্যান্ডউইথ {bw} Mbps -> কোয়ালিটি {quality}")

    
কোডটি চালিয়ে দেখুন — নেটওয়ার্ক খারাপ হলে (৬.০ → ৩.০ → ০.৮ → ০.৩ Mbps) কোয়ালিটি ১০৮০p থেকে ৭২০p, তারপর ২৪০p-এ নেমে আসে; আবার নেটওয়ার্ক ভালো হলে (০.৩ → ১.৫ → ৫.৫ Mbps) কোয়ালিটি ৪৮০p হয়ে ফের ১০৮০p-এ ফিরে যায় — প্লেব্যাক থামানো বা রিস্টার্ট করা ছাড়াই, কারণ প্রতিটি সিদ্ধান্ত শুধু পরবর্তী সেগমেন্টের জন্য নেওয়া হয়।

৫ · ট্রেড-অফ

বেশি কোয়ালিটি লেভেল অফার করলে প্লেব্যাক আরও মসৃণভাবে খাপ খায়, কিন্তু প্রতিটি লেভেলের জন্য আলাদা ট্রান্সকোড ও স্টোরেজ খরচ বাড়ে (একটি ভিডিও ৪-৫ গুণ বেশি স্টোরেজ নেয়)। খুব ঘন ঘন কোয়ালিটি বদলানো (প্রতি সেগমেন্টে) দর্শকের কাছে বিরক্তিকর লাগতে পারে, তাই বাস্তব প্লেয়ার সাধারণত কিছুটা "hysteresis" (একবার নামলে দ্রুত না ওঠা) যোগ করে। এবং CDN ছাড়া (L20) — সব ট্রাফিক অরিজিন সার্ভার থেকে সরাসরি সার্ভ করলে — এই থ্রুপুট মাত্রায় অরিজিন তাৎক্ষণিকভাবে সম্পৃক্ত হয়ে যাবে; তাই CDN এখানে অপশনাল অপ্টিমাইজেশন নয়, স্থাপত্যের একটি অপরিহার্য অংশ।

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিজে কিছুক্ষণ ভাবুন — তারপর "→ উত্তর" চাপুন।

প্র ০১ কেন ভিডিও ট্রান্সকোডিং একটি অ্যাসিনক্রোনাস মেসেজ কিউ (L22) দিয়ে করা হয়, সিঙ্ক্রোনাসভাবে (আপলোডের সাথে সাথে সরাসরি) নয়?

ট্রান্সকোডিং একটি ভারী, সময়সাপেক্ষ CPU কাজ (একটি লম্বা ভিডিওতে কয়েক মিনিট লাগতে পারে) — আপলোড রিকোয়েস্টকে এতক্ষণ ব্লক করে রাখা ইউজার এক্সপেরিয়েন্সের জন্য খারাপ এবং অ্যাপ সার্ভারের রিসোর্স আটকে রাখে। কিউ ব্যবহার করলে আপলোড রিকোয়েস্ট তাৎক্ষণিক সম্পন্ন হয় ("আপলোড হয়েছে, প্রসেস হচ্ছে"), আর ট্রান্সকোডিং আলাদা, স্বাধীনভাবে-স্কেলযোগ্য ওয়ার্কার পুলে ব্যাকগ্রাউন্ডে চলে — ঠিক L22-এ শেখা ডিকাপলিং সুবিধা।

প্র ০২ pick_quality() ফাংশনটি সবসময় সর্বোচ্চ সম্ভাব্য কোয়ালিটি বেছে নেয় যা বর্তমান ব্যান্ডউইথে ফিট করে। এটি কি সবসময় সঠিক সিদ্ধান্ত? কেন বাস্তব প্লেয়ার একটু "নিরাপত্তা মার্জিন" রাখে?

না — যদি প্লেয়ার ঠিক পরিমাপকৃত ব্যান্ডউইথের সীমানায় কোয়ালিটি বেছে নেয়, নেটওয়ার্কের সামান্য ওঠানামাতেই বাফারিং শুরু হতে পারে (ব্যান্ডউইথ কমে গেলে বর্তমান সেগমেন্ট ডাউনলোডের গতি প্রয়োজনের চেয়ে কমে যায়)। তাই বাস্তব প্লেয়ার সাধারণত পরিমাপকৃত থ্রুপুটের একটি অংশ (যেমন ৭০-৮০%) ব্যবহার করে সিদ্ধান্ত নেয়, এবং বাফারের বর্তমান অবস্থাও (কত সেকেন্ড আগে থেকে বাফার করা আছে) বিবেচনায় নেয় — শুধু তাৎক্ষণিক ব্যান্ডউইথ নয়।

প্র ০৩ ভিডিও স্ট্রিমিং প্ল্যাটফর্মে ক্যাশ ইনভ্যালিডেশন (L21) সাধারণত অন্য অনেক সিস্টেমের তুলনায় সহজ কেন?

একবার পাবলিশ হওয়া ভিডিও সেগমেন্ট সাধারণত অপরিবর্তনীয় (immutable) — একই ভিডিওর একই কোয়ালিটির একই সেগমেন্ট কখনো "বদলায়" না, নতুন ভার্সন থাকলে নতুন URL/আইডি পায়। এর মানে CDN/ক্যাশে এই কন্টেন্ট কার্যত চিরকালের জন্য (দীর্ঘ TTL দিয়ে) ক্যাশ করা যায় বিনা ঝুঁকিতে — L21-এর "কখন ইনভ্যালিডেট করব" সমস্যাটাই মূলত অস্তিত্বহীন হয়ে যায়, কারণ কন্টেন্ট বদলায় না, শুধু নতুন কন্টেন্ট যোগ হয়।

অনুশীলন

  1. পরিবর্তন করুন: কোড সেলে quality_levels-এ একটি নতুন লেভেল ("4K", 15.0) যোগ করুন এবং bandwidth_sequence_mbps-এ ২০.০ মান দিয়ে টেস্ট করুন। ফলাফল কী হবে?

    যেহেতু pick_quality() লুপে বাড়ন্ত ক্রমে সব লেভেল চেক করে এবং যেটাই ফিট করে সেটাকেই chosen-এ আপডেট করে, ২০.০ Mbps ব্যান্ডউইথে ১৫.০ ≤ ২০.০ শর্ত পূরণ হবে এবং ফাংশন "4K" রিটার্ন করবে — কোনো কোড পরিবর্তন ছাড়াই ফাংশনটি স্বয়ংক্রিয়ভাবে নতুন সর্বোচ্চ লেভেল সাপোর্ট করে, কারণ এটি লেভেলের সংখ্যার উপর নির্ভরশীল নয়, শুধু তালিকার উপর।

  2. চিন্তা করুন: একটি লাইভ স্ট্রিম (যেমন সরাসরি খেলা সম্প্রচার) বনাম একটি প্রি-রেকর্ডেড ভিডিওর মধ্যে ট্রান্সকোডিং পাইপলাইনের প্রধান পার্থক্য কী হবে?

    প্রি-রেকর্ডেড ভিডিওতে পুরো ফাইল আগে থেকেই থাকে, তাই ট্রান্সকোডিং একবার সম্পূর্ণ হয়ে যায় (ব্যাচ, L24) — দর্শক দেখার আগেই সব কোয়ালিটি প্রস্তুত থাকে। লাইভ স্ট্রিমে ভিডিও ক্রমাগত তৈরি হচ্ছে, তাই ট্রান্সকোডিং অবশ্যই রিয়েল-টাইমে, প্রতিটি ছোট সেগমেন্ট তৈরি হওয়ার সাথে সাথে (স্ট্রিম প্রসেসিং, L24) চলতে হবে — এখানে সামান্যতম ট্রান্সকোডিং ল্যাটেন্সিও দর্শকের কাছে সরাসরি বিলম্ব হিসেবে দেখা যায়।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

পূর্ববর্তী পাঠ
কেস স্টাডি: রাইড-শেয়ারিং সিস্টেম ডিজাইন করা