Skip to content
·44 min read

ভিডিও মাস্টারি — পর্ব ৫: Vimeo কীভাবে করে

Falkor ট্রান্সকোডিং ইনফ্রা, স্পট ইনস্ট্যান্সে চাংকড এনকোডিং, এনকোডারের ভেতর থেকে VMAF মাপা, Artax দিয়ে প্রোগ্রেসিভ MP4 বানানো, DRM-এর তিন যুগ, আর স্টোরেজের খরচ কমানো।

Falkor ট্রান্সকোডিং ইনফ্রা, স্পট ইনস্ট্যান্সে চাংকড এনকোডিং, এনকোডারের ভেতর থেকে VMAF মাপা, Artax দিয়ে প্রোগ্রেসিভ MP4 বানানো, DRM-এর তিন যুগ, আর স্টোরেজের খরচ কমানো।


Vimeo-র সমস্যাটা নেটফ্লিক্সের উল্টো

নেটফ্লিক্সের ক্যাটালগে হাজার হাজার টাইটেল, আর প্রতিটা কোটি কোটি বার দেখা হয়। তাই একটা সিনেমার পেছনে হাজার ঘণ্টা CPU ঢেলে দিলেও সেটা যুক্তিসঙ্গত — প্রতি ভিউতে খরচটা প্রায় শূন্য, আর সাশ্রয় হওয়া ব্যান্ডউইথটা বিশাল।

Vimeo-তে যে কেউ আপলোড করে। কোটি কোটি ভিডিও, আর বেশিরভাগেরই দর্শক কয়েকশো — কখনো কয়েকজন। এখানে হিসাবটা পুরো উল্টে যায়:

  • এনকোডিং খরচ ভাগ হওয়ার মতো ভিউ নেই। ৭০০ মিনিট CPU খরচ করে একটা ভিডিওর নিখুঁত ল্যাডার বের করা, যেটা ১২ জন দেখবে — অর্থহীন।
  • স্টোরেজই স্থায়ী খরচ। ভিডিওটা কেউ না দেখলেও প্রতি মাসে বিল আসে, বছরের পর বছর।
  • আপলোডের পর কত দ্রুত চলবে, সেটাই পণ্যের অভিজ্ঞতা। কেউ আপলোড করে অপেক্ষা করছে।

তাই Vimeo-র ইঞ্জিনিয়ারিং লেখাগুলো পড়লে দেখা যায় ওদের প্রায় সব সিদ্ধান্তের পেছনে একটাই প্রশ্ন: এটা কি সস্তা আর দ্রুত করা যায়?


১. চারটা ধাপ

Vimeo-তে ভিডিও আপলোড হলে মূল প্রক্রিয়াটা চারটা ধাপ:

  1. ডিকোডিং — অডিও আর ভিডিও আলাদা করা, ভিডিওটা কাঁচা পিক্সেলে খোলা
  2. স্কেলিং — কয়েকটা “প্রোফাইল”-এ নামানো; প্রোফাইল মানে রেজলিউশন আর এনকোডার সেটিংয়ের একটা জোড়া, আলাদা আলাদা ডিভাইস আর ব্যান্ডউইথের জন্য
  3. কনভার্শন / টোন ম্যাপিং — দরকার হলে কালার স্পেস বদলানো, HDR সোর্সকে SDR ডিভাইসের জন্য টোন ম্যাপ করা
  4. এনকোডিং — প্রোফাইল অনুযায়ী কম্প্রেস করা

পর্ব ২-এ আমরা হাতে যা করেছিলাম, কাঠামোটা একই। পার্থক্যটা তৃতীয় ধাপে লুকিয়ে — HDR থেকে SDR টোন ম্যাপিংটা আলাদা ধাপ হিসেবে স্বীকৃত, কারণ যে কেউ যেকোনো ফাইল আপলোড করতে পারে।


২. Tron থেকে Falkor — পুরনো পাইপলাইন বদলানো

Vimeo-র পুরনো ট্রান্সকোডিং সিস্টেমটার নাম ছিল Tron — ২০১৩ সাল থেকে চলছে, তখন Vimeo নিজের ডেটা সেন্টারে ছিল, ক্লাউডে না। Tron প্রতিটা কাজে পুরো সোর্স ফাইল লোকাল ডিস্কে নামাত, ট্রান্সকোড করত, তারপর ফল আপলোড করত — আর আউটপুট দিত প্রোগ্রেসিভ MP4, অডিও-ভিডিও একসাথে।

২০১৯-এর শেষে ওরা Falkor বানানো শুরু করে। আর ২০২৩ সালেও Tron পুরোপুরি অবসরে যায়নি — কিছু edge case তখনো ওটাই সামলাত।

Falkor-এর ছয়টা মূল লক্ষ্য — প্রতিটাই একটা বাস্তব ব্যথা থেকে এসেছে:

১. সোর্স থেকেই কাজ করা। পুরনো ব্যবস্থায় প্রথমে একটা মধ্যবর্তী “মেজানিন” ফাইল বানিয়ে তারপর তা থেকে সব ধাপ বানানো হতো। এতে দুইবার কোয়ালিটির ক্ষতি হয় — একবার মেজানিন বানাতে, আরেকবার আসল এনকোডে। Falkor সরাসরি আসল আপলোড থেকে কাজ করে।

২. সমান্তরাল করা। ভিডিওকে প্রায় ১ মিনিটের চাংকে ভাগ করে একসাথে অনেক worker-এ এনকোড করা।

৩. ক্লাউড-নেটিভ হওয়া। Tron ক্লাউডের আগের ডিজাইন; ক্লাউড প্রোভাইডার যা যা দেয় তার পুরোটা কাজে লাগানো যাচ্ছিল না।

৪. স্পট ইনস্ট্যান্স ব্যবহার করা। এটাই খরচের গল্পের কেন্দ্র। স্পট (preemptible) মেশিনে গ্যারান্টি নেই — ক্লাউড যেকোনো সময় কেড়ে নিতে পারে — কিন্তু দাম দুই গুণ বা তার বেশি কম। এক ঘণ্টার ভিডিও এক টানে এনকোড করলে মাঝপথে মেশিন কেড়ে নিলে পুরোটা নষ্ট। কিন্তু ১ মিনিটের চাংকে ভাগ করা থাকলে শুধু ওই ছোট অংশটা আবার চালাতে হয় — তাই সমান্তরালতা আর সস্তা কম্পিউট আসলে একই সিদ্ধান্তের দুই দিক।

সাথে দ্বিতীয় একটা হিসাব, যেটা Vimeo স্পষ্ট করে লিখেছে: ক্লাউডে বিল হয় সেকেন্ড ধরে (প্রথম মিনিটের পর)। মানে একটা মেশিন এক ঘণ্টা চালানো আর দশটা মেশিন ছয় মিনিট করে চালানো — দাম মোটামুটি একই, অথচ দ্বিতীয়টা অনেক দ্রুত শেষ হয়। সমান্তরাল করাটা তাই কার্যত বিনামূল্যে গতি।

৫. অডিও আর ভিডিও আলাদা করা। আগে প্রতিটা rendition-এ অডিওটা আবার mux করে জুড়ে দেওয়া হতো — মানে একই অডিও প্রতিটা কোয়ালিটির জন্য আলাদা করে সংরক্ষিত। Falkor অডিও একবার রাখে, ফলে প্যাকেজারের কাজ সহজ হয় আর স্টোরেজ বাঁচে। সাথে ফ্র্যাগমেন্টেড MP4, যাতে চাংক করা সহজ হয়।

তবে এর একটা দাম আছে, আর Vimeo সেটা স্বীকার করেছে: প্রোগ্রেসিভ ফাইল সার্ভ করা কঠিন হয়ে যায়, কারণ সেগুলো এখন চলার পথে mux করতে হবে। এই সমস্যাটার সমাধানই Artax — যেটা নিয়ে একটু পরেই।

৬. পুরনো টুলিং ছাড়া। Python 2-র মতো বাতিল জিনিস বাদ দিয়ে টিমের বাকি অংশের সাথে মিল রাখা।

স্ট্যাক আর প্রবাহ

Falkor চলে Google Cloud-এ, তিনটা US রিজিয়নে, GKE (Kubernetes) দিয়ে অর্কেস্ট্রেট করা, সারির জন্য PubSub। API স্তর Go-তে, ট্রান্সকোডারগুলো C-তে।

Falkor — একটা ট্রান্সকোড কাজের পথ

দুটো ডিটেইল খেয়াল করার মতো:

  • প্রতিটা worker পুরো সোর্স ফাইল নামায় না — শুধু নিজের চাংকের বাইট রেঞ্জটা টানে। ২ গিগাবাইটের সোর্স ৬০টা worker নামালে ১২০ গিগাবাইট নেটওয়ার্ক ট্রাফিক; এভাবে সেটা এড়ানো যায়।
  • জোড়া লাগানোর কাজটা স্টোরেজেই হয়। Compose worker চাংকগুলোর হেডার দেখে আউটপুট ফাইলের হেডারটা (moov, sidx) বানায়, তারপর সেটা সব চাংকের সাথে জুড়ে চূড়ান্ত জায়গায় রাখে। Vimeo-র ভাষায় ওদের ক্লাউড প্রোভাইডারে এটা স্টোরেজের একটা API কল মাত্র — মানে বাইটগুলো কোনো worker-এর ভেতর দিয়ে যায়ই না।

Quickset — বিন-প্যাকিং স্কেডিউলার

Falkor-এর কাজ বিলি করে Quickset নামের নিজস্ব স্কেডিউলার, আর খরচ কমাতে এটা দুটো জিনিস করে:

  • CPU আর মেমরির ভেতর কাজগুলো এমনভাবে বসায় যাতে মেশিন অলস পড়ে না থাকে — তবু হঠাৎ চাপের জন্য কিছুটা জায়গা ফাঁকা রাখে
  • স্পট ইনস্ট্যান্সকে অগ্রাধিকার দিয়ে নোড অটোস্কেল করে আর কাজ বসায়; সত্যিই দরকার না হলে সাধারণ (দামি) মেশিনে যায় না

কিন্তু Quickset ভালোভাবে কাজ বিলি করতে পারবে তখনই, যখন কাজগুলো আকারে আর সময়ে মোটামুটি একরকম হয়। সেটা নিশ্চিত করতে কাজগুলো আলাদা সারিতে রাখা হয়:

  • অ্যানালাইসিস — ফাইলের আকার অনুযায়ী (এর চেয়ে ভালো আন্দাজ নেই)
  • অডিও — দৈর্ঘ্য আর কোডেক অনুযায়ী, কারণ অডিও চাংক করা হয় না, তাই দৈর্ঘ্য খুব এদিক-ওদিক হয়
  • ভিডিও — rendition আর কোডেক অনুযায়ী, কারণ চাংকের দৈর্ঘ্য তো এমনিতেই ধ্রুব, ~১ মিনিট

এক সারিতে ১০ সেকেন্ডের আর ১০ মিনিটের কাজ মিশে থাকলে কোনো স্কেডিউলারই ভালো করতে পারে না — এটাই মূল কথা।

নেটফ্লিক্সের “container shaping” আর Vimeo-র “সারি অনুযায়ী একরকম কাজ” — দুটো একই সমস্যার দুই রকম উত্তর। ভারী কাজের পাইপলাইনে এই প্যাটার্নটা বারবার ফিরে আসে।

ইনফ্রার যে সমস্যাগুলো পথে পড়ল

Vimeo সততার সাথে যেগুলো লিখে গেছে:

  • API আর worker আলাদা ক্লাস্টারে সরাতে হলো — GKE Ingress ১০০০-এর বেশি নোডের ক্লাস্টারে কাজ করত না (Vimeo জানিয়েছে, এখন আর এই সীমাটা নেই)
  • Cloud NAT বসাতে হলো। VPC-native ক্লাস্টারে প্রতিটা pod নিজস্ব IP পায়; pod-এর সংখ্যা বিশাল হওয়ায় এই ক্লাস্টারকে বাকি অবকাঠামোর সাথে peer করলে ভেতরের 10.x.x.x রেঞ্জের বড় একটা অংশ শেষ হয়ে যেত। তাই peer না করে Cloud NAT দিয়ে observability-র মতো সার্ভিসগুলোর সাথে কথা বলার ব্যবস্থা
  • স্টেট মেশিন নতুন করে ডিজাইন করতে হলো, কারণ PubSub at-least-once ডেলিভারি দেয় — মানে একই মেসেজ দুবার আসতে পারে, আর সিস্টেমকে সেটা সামলাতে হবে
  • মাল্টি-রিজিয়ন মানে রিজিয়নের মধ্যে ডেটা যাওয়ার খরচ, কিন্তু বিনিময়ে প্রাপ্যতা

পর্ব ৩-এ আমরা আইডেম্পোটেন্সি কী আর স্টেট মেশিন নিয়ে যা বলেছিলাম, এটা তারই বাস্তব রূপ। “সারি একবারই দেবে” ধরে নিয়ে সিস্টেম বানালে সেটা ভাঙবেই।

রোলআউটের কৌশলটা শেখার মতো

পুরনো পাইপলাইন থেকে নতুনে যাওয়ার ক্রমটা Vimeo বেছেছিল ঝুঁকির হিসাব করে:

  1. প্রথমে H.264 240p-এর সামান্য একটা অংশ — এই rendition UI বা API-তে ব্যবহারকারীকে দেখানোই হয় না, শুধু নিজেদের player আর external playlist ব্যবহার করে। তাই কিছু ভাঙলে প্রভাব ন্যূনতম, অথচ আসল ট্রাফিক দিয়ে scaling পরীক্ষা করা যায়
  2. পুরো 240p যাওয়ার পর AAC আর Opus অডিও
  3. তারপর H.264 1080p — সবচেয়ে বেশি দেখা rendition, তাই visual quality ঠিক আছে কিনা এখানেই সবচেয়ে দ্রুত যাচাই হয়
  4. তারপর একসাথে বাকি সব H.264 rendition — 4K, 2K, 720p, 360p; এর পরপরই 360° ভিডিও আর HDR10/Dolby Vision-এর জন্য HEVC

খেয়াল করো, তৃতীয় ধাপে ওরা ইচ্ছা করে সবচেয়ে বেশি দেখা জিনিসটায় গেল — কারণ লক্ষ্য ছিল সমস্যা লুকানো না, দ্রুত খুঁজে বের করা। Vimeo-র নিজের মন্তব্য: ভেতরে বারবার পরীক্ষা করার পরেও user-generated কনটেন্টে সবসময় নতুন edge case বেরোয়।

কিছু জিনিস তখনো migrate হয়নি: variable frame rate-এর সোর্স, AV1 (তখন শুধু Staff Picks-এ, কারণ একসাথে বেশি কিছু বদলাতে চায়নি), আর যেসব নেটওয়ার্ক সোর্সে সহজে seek করা যায় না — ওগুলোর জন্য আগে ডিস্কে নামাতেই হয়।

ফল: খরচ কমেছে, গতি বেড়েছে। লম্বা ভিডিও অনেক দ্রুত চলার জন্য প্রস্তুত হয়; তবে খুব ছোট ভিডিওতে পাইপলাইনের নিজস্ব ওভারহেডটা তুলনায় বড় হয়ে যায়।


৩. VMAF কীভাবে মাপা হয় — একটা চতুর কৌশল

এনকোডিংয়ের কোয়ালিটি মাপতে চাইলে সাধারণ পদ্ধতি হলো: এনকোড শেষ হোক, তারপর সোর্স আর আউটপুট দুটোই নামিয়ে, দুটোই ডিকোড করে, তুলনা করো।

Vimeo-র স্কেলে এটা অর্থহীন — সোর্স ফাইলটা আবার টানা আর আবার ডিকোড করা মানে প্রায় পুরো ট্রান্সকোডের সমান খরচ, শুধু একটা সংখ্যা পাওয়ার জন্য।

ওদের সমাধানটা সুন্দর: এনকোডারের ভেতর থেকেই মেপে ফেলো।

কারণ একটা এনকোডারকে এমনিতেই তার নিজের আউটপুট ডিকোড করে রাখতে হয় — পরের ফ্রেমগুলো ওই পুনর্গঠিত ছবিটার উপর ভিত্তি করেই predict করা হয়। এই জিনিসটার নাম reconstruction, আর সেটা এনকোডারের মেমরিতে এমনিতেই আছে। x264 আর x265-এ এটা বের করার API আগে থেকেই ছিল; rav1e-তে সমতুল্য API-টা Vimeo নিজে যোগ করেছে

তাহলে সোর্স ফ্রেম আর reconstruction পাশাপাশি রেখে libvmaf-এ পাঠিয়ে দেওয়া যায় — আলাদা করে ডিকোড করার ধাপটাই বাদ।

তিনটা ঝামেলা পথে পড়ল:

এনকোডারের lookahead। এনকোডার ইনপুট ফ্রেম পেয়ে সাথে সাথে আউটপুট দেয় না — বিটরেট বরাদ্দের পরিকল্পনা করতে প্রায় ৭০ ফ্রেম পর্যন্ত দেরি হয়। তাই ডিকোড করা সোর্স ফ্রেমগুলো ধরে রাখতে হয়, আর মেলানোর জন্য এনকোডারের ইনপুটের সাথে একটা পয়েন্টার জুড়ে দিয়ে আউটপুটে সেটা ফেরত নেওয়া হয়। খরচ: উল্লেখযোগ্য বাড়তি RAM — যেটা Vimeo আপাতত মেনে নিয়েছে।

ফ্রেমের ক্রম উল্টে যাওয়া। libvmaf-এ ফ্রেম ক্রমানুসারে ঢোকাতে হয়, কারণ VMAF-এর একটা temporal অংশ আছে। rav1e-তে সমস্যা নেই — AV1 এনকোডার ভেতরে ফ্রেম পুনর্বিন্যাস করলেও চূড়ান্ত আউটপুট ক্রমেই আসে। কিন্তু x264 আর x265-এর জন্য আরেকটা reordering queue লাগে।

রেজলিউশনের অমিল। ডিফল্টে VMAF মাপা হয় scaled সোর্সের সাথে — মানে 360p এনকোডকে 360p সোর্সের সাথে। এতে বোঝা যায় ওই ধাপটা নিজের মাপে কেমন, কিন্তু দুটো আলাদা রেজলিউশনের ধাপ একে অন্যের সাথে তুলনা করা যায় না। সেটা করতে হলে reconstruction-কে সোর্সের রেজলিউশনে upscale করে তবেই libvmaf-এ পাঠাতে হয়।

ডেটা দিয়ে যা বেরিয়ে এল

VMAF সব এনকোডে নয়, এলোমেলো একটা অংশে মাপা হয়, আর ফল যায় টাইম-সিরিজ ডেটাবেসে (Grafana ড্যাশবোর্ড) আর BigQuery-তে। তারপর প্রশ্ন করা যায়।

যা বেরিয়ে এল:

  • নিজের রেজলিউশনে মাপলে প্রোফাইলগুলোর কোয়ালিটি মোটামুটি সমান — তত্ত্বের সাথে মেলে
  • 360p, 1440p আর 4K-তে অস্বাভাবিক ফল, আর কারণটা ছিল বিটরেট ক্যাপ
  • 360p-র ক্যাপ ৫৫০ kbit/s থেকে বাড়ানোর পর নিচের পার্সেন্টাইলগুলোর স্কোর উঠে এল, মধ্যমা প্রায় একই রইল — মানে যে ভিডিওগুলো ক্যাপে আটকে যাচ্ছিল, শুধু সেগুলোই ভুগছিল
  • কিছু ক্ষেত্রে নিচের ধাপ উপরের ধাপের চেয়ে ভালো স্কোর করছিল — খুঁজে দেখা গেল কিছু অ্যাপ আপলোডের আগেই ভিডিও স্কেল করে পাঠাচ্ছে

শেষ দুটো আবিষ্কার VMAF ছাড়া কোনোদিন ধরা পড়ত না। কোয়ালিটি না মাপলে কোয়ালিটির বাগ দেখা যায় না — এটাই মূল কথা।


৪. কোডেক পরীক্ষার অবকাঠামো — AWCY

নতুন এনকোডার সংস্করণ এল, নাকি একটা সেটিং বদলাতে চাও — কীভাবে জানবে সেটা ভালো না খারাপ?

Vimeo ব্যবহার করে AWCY (“Are We Compressed Yet?”), Xiph.Org-এর ওপেন সোর্স টুল। এর অংশগুলো:

  • একটা ওয়েব ফ্রন্টএন্ড
  • rd_server — কেন্দ্রীয় স্কেডিউলার, কোডেক কম্পাইল করে আর কাজ বিলি করে
  • worker নোড — বিতরণ করা এনকোড/ডিকোড
  • একটা বিটস্ট্রিম অ্যানালাইজার — VP9/AV1 ফ্রেম ধরে ধরে দেখা

BD-rate — তুলনার আসল একক

এটা বোঝা জরুরি, কারণ কোডেকের সব তুলনায় এই সংখ্যাটাই ব্যবহার হয়।

সমস্যাটা হলো: দুটো এনকোডারের তুলনা করবে কীভাবে? একটা হয়তো কম বিটরেটে ভালো, আরেকটা বেশি বিটরেটে। একটা মাত্র বিটরেটে তুলনা করলে ফল বিভ্রান্তিকর।

BD-rate এর উত্তর: একই কনফিগারেশনে কয়েকটা কোয়ালিটি লেভেলে (সাধারণত কয়েকটা স্থির কোয়ান্টাইজার মানে) এনকোড চালাও। প্রতিটার বিটরেট আর কোয়ালিটি বসিয়ে দুটো বক্ররেখা আঁকো। তারপর হিসাব করো — একই কোয়ালিটি পেতে গড়ে কত শতাংশ কম (বা বেশি) বিট লাগছে

ঋণাত্মক BD-rate মানে উন্নতি। Vimeo-র উদাহরণে rav1e-র s3 কনফিগারেশনে s10-এর চেয়ে প্রায় ৩০% কম বিট লাগে একই কোয়ালিটির জন্য।

টেস্ট সেট

Vimeo vimeo-corpus-10s নামে একটা ডেটাসেট প্রকাশ করেছে — ১৫টা Creative Commons ভিডিও, ইচ্ছা করে বৈচিত্র্যময়: আলাদা রেজলিউশন, frame rate, কনটেন্টের ধরন, কোয়ালিটি। ভিডিওগুলো বেছে নেওয়াই হয়েছে এনকোড করা কঠিন বলে, আর তাতে scene cut আছে — যাতে rate control আর keyframe বসানোর জন্য scene detection পরীক্ষা করা যায়, যেগুলো প্রচলিত ডেটাসেটে সহজে যাচাই হয় না।

AWCY-র নির্ভরযোগ্যতার একটা ভালো প্রমাণ: AV1 স্ট্যান্ডার্ড বানানোর সময় Alliance for Open Media নিজেই এটা ব্যবহার করেছে, আর এখনো libaom, SVT-AV1 আর rav1e-র ডেভেলপাররা নিয়মিত ব্যবহার করে।

গতির খেসারতটাও AWCY দেখিয়ে দেয়: rav1e-র speed 10 আর speed 3 পাশাপাশি রাখলে দ্রুত সংস্করণে ব্যান্ডিং আর নয়েজ বেশি — সংখ্যা আর ছবি, দুটোই এক জায়গায়।


৫. Artax — ডাউনলোডের জন্য MP3 বানানোর মতো কাজ

এই অংশটা আমার প্রিয়, কারণ সমস্যাটা প্রায় সব ভিডিও প্ল্যাটফর্মেই আসে।

সমস্যা: আধুনিক পাইপলাইন আউটপুট দেয় ফ্র্যাগমেন্টেড ISOBMFF (CMAF) — অডিও আর ভিডিও আলাদা ফাইলে, ৬ সেকেন্ডের টুকরোয় ভাগ করা। ABR স্ট্রিমিংয়ের জন্য আদর্শ।

কিন্তু কিছু ব্যবহারকারীর দরকার একটা সাধারণ প্রোগ্রেসিভ MP4 — অফলাইন ডাউনলোড, সরল video ট্যাগে চালানো, বা এডিটিং সফটওয়্যারে ইমপোর্ট।

তিনটা সহজ উত্তর, তিনটাই খারাপ:

  • প্রতিটা এনকোডের দুটো mux রাখো (একটা progressive, একটা fragmented) → স্টোরেজ দ্বিগুণ। Vimeo-র ভাষায়, টাকাটা পুড়িয়ে ফেলাই সমান।
  • সব কিছু progressive হিসেবে রাখো, আর playback-এর জন্য CDN-এ প্রতিবার remux ও segment করো → বেশিরভাগ ট্রাফিক তো ABR playback-ই, তাই compute আর cache-এর ওভারহেড বিশাল। তবে এটাই সবচেয়ে সহজ পথ, আর বেশিরভাগ ভেন্ডর এটাই করে — Vimeo নিজেও বহু বছর এটাই করেছে।
  • ব্যবহারকারীকে ভোগাও → পণ্য খারাপ হয়

চতুর্থ পথ: একটা proxy লেখো, যেটা fragmented ফাইলগুলোর সেটকে HTTP-র উপর একটাই progressive MP4 হিসেবে দেখাবে — কিছু বানিয়ে বা জমিয়ে না রেখেই।

এটা কেন কঠিন, সেটা বোঝা দরকার। প্রথম request-এ পুরোটা ডিস্কে বানিয়ে ফেললে সহজ, কিন্তু time-to-first-byte ভয়ানক। moov বক্সটা ফাইলের শেষে রাখলেও সহজ, কিন্তু তখন পুরো ফাইল না আসা পর্যন্ত seek করা যায় না — মানে Content-Length নেই, range request নেই। আর HTML5 video ট্যাগে ব্যবহার করতে হলে moov সামনে চাই, Content-Length চাই, আর প্রথম request থেকেই range request চাই। এই কারণেই কেউ এটা করে না। Vimeo করল, নাম দিল Artax

আরেকটা কথা, যেটা বাস্তবে গুরুত্বপূর্ণ: এখানে সাধারণ muxer (FFmpeg, GPAC, L-SMASH) ব্যবহার করা যায় না। ওরা ইনপুটে ইচ্ছামতো seek করে আর ইচ্ছামতো লেখে — custom I/O callback দিলেও। কারণ ওরা demux করে আবার mux করে। এখানে যা দরকার সেটা আসলে deserialize করে সামান্য অন্যভাবে আংশিক serialize করা। দুটো এক জিনিস না।

কীভাবে, দুই ধাপে:

ধাপ ১ — সূচি বানানো। প্রথম request-এ ইনপুট ফাইলগুলোর ftyp, moov আর sidx বক্স সমান্তরালে টেনে আনা হয়। এখানে দুটো শর্ত:

  • fragmented ফাইলগুলোতে একটাই global sidx থাকতে হবে যেটা সব fragment ঢাকে (VOD style), fragment প্রতি একটা করে না (live style)। তাহলেই পুরো ফাইল না ঘেঁটে প্রতিটা fragment-এর অবস্থান জানা যায়।
  • fragmented ফাইলের moov-এ প্যাকেটের offset বা properties থাকে না — সেগুলো থাকে প্রতিটা fragment-এর moof-এ। তাই sidx থেকে সব moof-এর অবস্থান নিয়ে সেগুলোও একসাথে (connection pool দিয়ে) টেনে আনতে হয়।

এবার আউটপুটের সম্পূর্ণ moov বানানো যায়, আর আউটপুট ফাইলের আকারটাও হুবহু হিসাব করা যায় — তাই Content-Length দেওয়া যায়। mdat-এর আগের সব বক্সের আকার জানা থাকাটা range request সামলানোর জন্যও অপরিহার্য, কারণ প্রতিটা প্যাকেটের অবস্থান ওই পরিমাণ সরে যায়। এই কাঠামোটা ক্যাশ করে রাখা হয়, তাই কাজটা একবারই।

ধাপ ২ — রেঞ্জ রিকোয়েস্ট সামলানো। ব্যবহারকারী (বা ব্রাউজার) যখন একটা বাইট রেঞ্জ চায়, Artax হিসাব করে: ওই রেঞ্জে কোন কোন প্যাকেট পড়ে, সেগুলো ইনপুট ফাইলের ঠিক কোন জায়গায়, আংশিক প্যাকেটের ক্ষেত্রে কোন অফসেট থেকে, আর অডিও-ভিডিও কোন ক্রমে ইন্টারলিভ হবে (Vimeo-র ক্ষেত্রে ৫০০ মিলিসেকেন্ড পর পর)।

Artax — জমিয়ে না রেখে প্রোগ্রেসিভ MP4

প্রতিটা ইনপুট ফাইলের জন্য একটাই request দিয়ে দরকারি সব প্যাকেট আনা হয় — মাঝখানের moofগুলোও সাথে চলে আসে, সামান্য অপচয়, কিন্তু একটা request-এর লাভ ওই অপচয়ের চেয়ে বড়।

ফল: শুধু মেটাডেটার কাঠামোটা ক্যাশ হয়, প্যাকেটের ডেটা চাহিদামতো স্ট্রিম হয়। কোনো দ্বিতীয় কপি নেই।

CDN-এর সাথে মিলিয়ে কাজ করাটাও এর অংশ: Vimeo-র CDN পার্টনার একটা progressive সংস্করণের প্রথম request-এ সবসময় একটা 0-0 range request পাঠায় — সেই সুযোগে Artax ইনপুট বক্সগুলো deserialize করে ক্যাশ করে নেয় আর Content-Length ফেরত দেয়, ফলে CDN নিজের edge-এ ফাইলের অংশগুলো ক্যাশ করতে শুরু করে আর ব্যবহারকারীর ডাউনলোড সাথে সাথেই শুরু হয়।

চালু হয় ২০২২-এর জানুয়ারিতে, আর Vimeo-র নিজের ভাষায় — কেউ টেরই পায়নি, যেটা এই ধরনের লঞ্চের সবচেয়ে বড় প্রশংসা। (ওই সময় থেকে নামানো ফাইলগুলোর handler_name ফিল্ডে Vimeo Artax Video Handler লেখা থাকে।)

একটা বাড়তি লাভও হলো: পুরনো transcode সিস্টেমের যেসব অভ্যন্তরীণ ব্যবহারকারী progressive আউটপুট আশা করত, তাদেরও চুপচাপ এই সার্ভিসের মধ্য দিয়ে চালিয়ে দেওয়া গেল — ফলে Tron বন্ধ করার পথটা পরিষ্কার হলো।


৬. DRM-এর তিন যুগ

DRM নিয়ে Vimeo-র লেখাটা বিরল রকম সৎ — কারণ ওরা তিনবার আর্কিটেকচার বদলেছে, আর প্রতিবারের কারণটা লিখে গেছে।

আগে দুটো মৌলিক কথা:

  • DRM মানে দুটো জিনিস — এনক্রিপ্ট করা মিডিয়া স্যাম্পল, আর একটা লাইসেন্স যেটা কনটেন্ট প্রোটেকশন সিস্টেম দিয়ে তৈরি হয়
  • তিনটা বড় ইকোসিস্টেম: FairPlay (Apple), Widevine (Google), PlayReady (Microsoft)। এনক্রিপশন মানা হয় common encryption স্ট্যান্ডার্ড (ISO/IEC 23001-7)

প্রথম যুগ (২০১৬–২০২০): ইনগেস্টের সময় এনক্রিপশন

  • Skywarp — key ingestion সার্ভিস, ভিডিও আইডির সাথে encryption key ম্যাপ করত
  • DarkMatter — ট্রান্সকোডিংয়ের সময়ই কনটেন্ট এনক্রিপ্ট করত
  • license আর key storage ছিল একটা তৃতীয় পক্ষের ভেন্ডরের কাছে

কেন key সার্ভিস লাগে, সেটা মনে রাখার মতো: পুরো ক্যাটালগে একটাই master key রাখলে সেটা ফাঁস হওয়ামাত্র সব শেষ — আর ঠিক করতে হলে নতুন key দিয়ে পুরো ক্যাটালগ আবার transcode করতে হতে পারে। তাই প্রতিটা টাইটেলের (এমনকি প্রতিটা rendition-এর) আলাদা key, আর সেগুলোর নিরাপদ ম্যাপিং।

সমস্যা: এনক্রিপশনটা ingestion-এর সময় হওয়ায় একটা অস্বস্তিকর দোটানা তৈরি হলো — এনক্রিপ্টেড আর সাধারণ, দুই কপিই রাখবে? তাহলে স্টোরেজের বিল দ্বিগুণ। না রাখবে? তাহলে কোনো গ্রাহক DRM বন্ধ করতে চাইলে ভিডিওটা আবার transcode করতে হবে — আর মত বদলালে আবার। দুটোই ধীর আর দামি।

দ্বিতীয় যুগ (২০১৮–২০২৪): Fastly OTFP

২০১৮ নাগাদ দেখা গেল আসল DRM সমাধানটা নতুন গ্রাহক টানছেও না, ধরেও রাখছে না। একই সময়ে Vimeo-র OTT টিম নতুন সমাধান খুঁজছিল — এবং দ্রুত।

ভেতরে তখন নতুন একটা সার্ভিস বানাতে কেউ আগ্রহী ছিল না — Vimeo এটা লুকায়নি। কিন্তু Fastly-র On the Fly Packager ওরা আগে অন্য কাজে ব্যবহার করেছিল, তাই DRM-এর জন্য সেটা কনফিগার করাটা তুলনায় সামান্য কাজ। আর এটা transcode-এর সময় না, চাহিদামতো DRM দিত — মানে দোটানাটাও গেল।

সমস্যা: সব DRM request Fastly দিয়ে যেতে হতো, মানে DRM ট্রাফিকের জন্য অন্য CDN ব্যবহার করা যেত না। দ্বিতীয়ত, এটা কাজ করত একটা নির্দিষ্ট bucket-এর সাথে — তাই DRM চালু বা বন্ধ করতে হলে কনটেন্ট bucket-এ আনা-নেওয়া করতে হতো, যেটা আবারও ধীর আর দামি। আর সবচেয়ে বড় সীমাবদ্ধতা: এটা শুধু OTT ক্যাটালগে কাজ করত, Vimeo-র বাকি ভিডিওতে না।

তৃতীয় যুগ (২০২৩–এখন): নিজেদের রিয়েল-টাইম সমাধান

তিনটা অংশ:

  • Key Proxy — Vimeo-র ভিডিও আইডিকে প্রোভাইডারের কি-সার্ভিসে ম্যাপ করে
  • License Proxy — প্রোভাইডারের লাইসেন্সিং অবকাঠামোর সাথে যুক্ত
  • Media Proxy — চলার পথে সেগমেন্ট আবার প্যাকেজ করে এনক্রিপ্ট করে

প্রবাহটা: প্লেয়ার মেটাডেটা চায় → CDN ম্যানিফেস্ট আনে, key proxy তাতে DRM মেটাডেটা যোগ করে → প্লেয়ার সেগমেন্ট চায় → media proxy অরিজিনের মাধ্যমে এনক্রিপ্ট করা সেগমেন্ট দেয়।

লাভ: যেকোনো CDN-এ চলে, পুরো ক্যাটালগে প্রয়োগ করা যায় (শুধু OTT না), এনক্রিপ্টেড কপি আলাদা করে রাখতে হয় না।

এই তিন যুগের গল্পটা “বানাবো না কিনব” প্রশ্নের একটা বাস্তব উত্তর: সিদ্ধান্তটা চিরস্থায়ী না। প্রথমে নিজে বানাও, তারপর কেনা জিনিসে যাও যখন টিমের মনোযোগ নেই, তারপর আবার নিজে বানাও যখন কেনা জিনিসটার সীমা তোমার পথ আটকায়। প্রতিবারই সিদ্ধান্তটা তখনকার পরিস্থিতিতে ঠিক ছিল।


৭. স্টোরেজ — যেখানে টাকাটা আসলে যায়

Vimeo-র স্টোরেজ চলে Google Cloud Storage-এ, চারটা ক্লাস:

ক্লাসকীসের জন্যন্যূনতম রাখার সময়
Standardঘন ঘন ব্যবহার, সবচেয়ে দামি০ দিন
Nearlineমাসে একবারের মতো৩০ দিন
Coldlineতিন মাসে একবারের মতো৯০ দিন
Archiveদীর্ঘমেয়াদি সংরক্ষণ, সবচেয়ে সস্তা৩৬৫ দিন

ওই ন্যূনতম সময়টা ফাঁদ: নির্দিষ্ট সময়ের আগে সরালে বা মুছলে জরিমানা দিতে হয়। তাই “সব কিছু Archive-এ ফেলে দাও” কৌশলটা কাজ করে না — যদি কেউ ভিডিওটা কাল দেখতে চায়, তুমি হারলে।

Vimeo কাজটা দুই ভাগে করেছে, আর দুই ভাগে দুই রকম যন্ত্র ব্যবহার করেছে — এই পার্থক্যটাই সবচেয়ে শেখার মতো অংশ।

ভাগ ১ — source storage: linear programming

source মানে ব্যবহারকারীর আপলোড করা আসল কাঁচা ফাইল। এগুলো বিশাল, আর প্রায় কখনো ছোঁয়া হয় না — তাই চাহিদা যে কম, সেটা আগে থেকেই জানা। মানে এটা প্রেডিকশনের সমস্যা না, constrained optimization-এর সমস্যা।

Vimeo একটা cost function লিখল — চারটা storage class জুড়ে যোগফল, যাতে storage আর retrieval খরচ, ডেটার আকার, কতদিন থাকবে, আর ন্যূনতম দিনের সমন্বয় — সব ধরা আছে। তারপর linear programming দিয়ে হাজার হাজার নীতির সমন্বয়ের মধ্যে সবচেয়ে সস্তাটা খুঁজে বের করল।

ফল হিসেবে বেরিয়ে এল একদম সাধারণ দেখতে কিছু age-based নিয়ম, যেমন: ৩০ দিন সম্পাদনা হয়নি এবং ১৫ দিনে দেখা বা খোলা হয়নি → nearline-এ নামাও। এক বছরের বেশি পুরনো কিছু জিনিস → মুছে ফেলো।

দেখতে তুচ্ছ, কিন্তু এই নিয়মগুলোই source storage-এর খরচ প্রায় ৬০% কমিয়ে দিল

ভাগ ২ — compute storage: machine learning

দ্বিতীয় ভাগটা হলো transcode করা rendition-গুলো, যেগুলো দর্শক আসলে টানে। এখানে প্রশ্নটা সত্যিই প্রেডিকশনের: এই ভিডিওটা আবার দেখা হবে কি?

এখানে দুটো ক্লাস — hot মানে nearline, cold মানে coldline। মডেল: supervised-এ LightGBM, আর feature engineering ও dimensionality reduction-এ K-means clustering

ডেটা আসে storage log আর CDN log — দুটো মিলিয়ে বোঝা যায় কখন জিনিসটা cache থেকে গেছে আর কখন সরাসরি storage থেকে। ফিচারগুলোর মধ্যে আছে decay feature (সাম্প্রতিক ব্যবহারের ওজন বেশি), normalize করা retrieval অনুপাত, moving average (হঠাৎ বাড়া মানে CDN-এ cache miss), প্রথম access-এর সময়, প্রথম ও শেষ access-এর ব্যবধান, আর access-এর ওঠানামার হিসাব।

ground truth ঠিক করা হয়েছিল অন্তত ১২ মাসের log আছে এমন ভিডিও থেকে: retrieval আর read খরচ ধরে রাখার নিট খরচ ধনাত্মক হলে cold, ঋণাত্মক হলে hot

মডেলের পারফরম্যান্স — precision ৭৮%, recall ৮৪%, precision-recall curve-এর নিচে AUC ০.৮৩। inference চলে Kubeflow Pipelines-এ, আর ফল CSV হয়ে GCS-এর Days-Since-Custom_time হেডারে বসে যায়, যাতে transition আপনাআপনি হয়।

ফল: compute storage-এর খরচ প্রায় ২০% কম

দুটো সূক্ষ্ম প্রশ্ন

hot-এ থাকা জিনিস ঠান্ডা হয়ে গেলে? একটা সাধারণ age-based নীতি সেগুলোকে ধীরে ধীরে cold-এ নামায়।

cold-এ থাকা জিনিস হঠাৎ জনপ্রিয় হলে? এখানেই ন্যূনতম দিনের জরিমানাটা কামড় দেয়। তবু যদি জিনিসটা এতটাই জনপ্রিয় হয় যে জরিমানা দিয়ে ফিরিয়ে আনাই সস্তা, তখন ফিরিয়ে আনা হয়। Vimeo বলছে, এমন ঘটনা বিরল।


৮. Vimeo থেকে আমাদের নেওয়ার মতো কী

চাংকের আকার আর স্পট মেশিন একই সিদ্ধান্ত। ~১ মিনিটের চাংক মানে হারানো কাজ সর্বোচ্চ ১ মিনিট। এটাই সস্তা কম্পিউট ব্যবহার করার লাইসেন্স। ছোট প্রজেক্টেও একই যুক্তি — চাংক ছোট রাখলে preemptible/spot মেশিন ব্যবহার করা যায়, খরচ কয়েকগুণ কমে।

worker-কে পুরো ফাইল নামাতে দিও না। HTTP রেঞ্জ রিকোয়েস্ট দিয়ে শুধু নিজের অংশটা টানা — সহজ পরিবর্তন, বড় সাশ্রয়।

জোড়া লাগানোর কাজটা object storage-এ ঠেলে দাও। টুকরোগুলো জোড়া দেওয়ার জন্য ক্লাউড স্টোরেজের নিজস্ব API আছে (GCS-এর compose, S3-এর multipart copy) — ব্যবহার করলে বাইটগুলো তোমার worker-এর ভেতর দিয়ে যায়ই না।

অডিও একবার এনকোড করো। ছয়টা ভিডিও ধাপের প্রতিটায় একই অডিও জুড়ে রাখাটা সরাসরি অপচয় — CMAF-এ অডিও আলাদা ট্র্যাক, ওটাকে আলাদাই রাখো।

কোয়ালিটি মাপাকে সস্তা করো, নইলে মাপাই হবে না। এনকোডারের পুনর্গঠিত ফ্রেম ব্যবহার করার কৌশলটা প্রতিভাবান, কিন্তু সরল সংস্করণও চলে: সব ভিডিওতে না মেপে নমুনা নিয়ে মাপো, আর ফলটা এমন জায়গায় রাখো যেখানে প্রশ্ন করা যায়।

দুই কপি রাখার আগে ভাবো — চলার পথে বানানো যায় কি? Artax-এর শিক্ষা এটাই। DRM-এর দ্বিতীয় ও তৃতীয় যুগও একই শিক্ষা।

স্টোরেজ লাইফসাইকেলটা প্রথম দিন থেকেই ভাবো। পরে গিয়ে ১০ কোটি ফাইলের উপর নীতি প্রয়োগ করা অনেক কঠিন — আর ন্যূনতম রাখার সময়ের জরিমানাগুলো তখন কামড় দেয়।


সূত্র

সবগুলো Vimeo Engineering Blog থেকে:

পরের পর্বে — Mux। যারা এই পুরো জিনিসটা অন্য ডেভেলপারদের কাছে একটা API হিসেবে বিক্রি করে, আর তাই ওদের লড়াইটা সেকেন্ডের হিসাবে।