প্রতি-ভিডিও-ফ্রেম অপারেশনগুলি দক্ষভাবে সম্পাদন করুন

ব্রাউজারে ভিডিওগুলির সাথে আরও দক্ষতার সাথে কাজ করার জন্য requestVideoFrameCallback() কীভাবে ব্যবহার করবেন তা শিখুন।

প্রকাশিত: ৮ জানুয়ারী, ২০২৩

Browser Support

  • ক্রোম: ৮৩।
  • প্রান্ত: ৮৩।
  • ফায়ারফক্স: ১৩২।
  • সাফারি: ১৫.৪।

Source

HTMLVideoElement.requestVideoFrameCallback() পদ্ধতি ওয়েব লেখকদের একটি কলব্যাক নিবন্ধন করতে দেয় যা রেন্ডারিং ধাপে চলে যখন একটি নতুন ভিডিও ফ্রেম কম্পোজিটরে পাঠানো হয়। এটি ডেভেলপারদের ভিডিওতে দক্ষ প্রতি-ভিডিও-ফ্রেম ক্রিয়াকলাপ সম্পাদন করতে দেয়, যেমন ভিডিও প্রক্রিয়াকরণ এবং ক্যানভাসে পেইন্টিং, ভিডিও বিশ্লেষণ, অথবা বহিরাগত অডিও উৎসের সাথে সিঙ্ক্রোনাইজেশন।

requestAnimationFrame() এর সাথে পার্থক্য

এই API ব্যবহার করে করা অপারেশন, যেমন drawImage() ব্যবহার করে ক্যানভাসে একটি ভিডিও ফ্রেম আঁকা, স্ক্রিনে চলমান ভিডিওর ফ্রেম রেটের সাথে সর্বোত্তম প্রচেষ্টা হিসেবে সিঙ্ক্রোনাইজ করা হয়। এটি window.requestAnimationFrame() থেকে আলাদা, যা সাধারণত প্রতি সেকেন্ডে প্রায় 60 বার ফায়ার করে।

requestVideoFrameCallback() প্রকৃত ভিডিও ফ্রেম রেটের সাথে আবদ্ধ—একটি গুরুত্বপূর্ণ ব্যতিক্রম ছাড়া:

কলব্যাক চালানোর কার্যকর হার হল ভিডিওর হার এবং ব্রাউজারের হারের মধ্যে কম হার। এর অর্থ হল 60Hz এ রঙ করা ব্রাউজারে 25fps ভিডিও চালানোর ফলে 25Hz এ কলব্যাক শুরু হবে। একই 60Hz ব্রাউজারে 120fps ভিডিও চালানোর ফলে 60Hz এ কলব্যাক শুরু হবে।

বৈশিষ্ট্য সনাক্তকরণ

if ('requestVideoFrameCallback' in HTMLVideoElement.prototype) {
  // The API is supported!
}

পলিফিল

Window.requestAnimationFrame() এবং HTMLVideoElement.getVideoPlaybackQuality() এর উপর ভিত্তি করে requestVideoFrameCallback() পদ্ধতির জন্য একটি পলিফিল উপলব্ধ। এটি ব্যবহার করার আগে, README এ উল্লিখিত সীমাবদ্ধতাগুলি সম্পর্কে সচেতন থাকুন।

পদ্ধতিটি ব্যবহার করুন

আপনি যদি requestAnimationFrame() পদ্ধতি ব্যবহার করেন, তাহলে আপনি requestVideoFrameCallback() পদ্ধতিটি চিনতে পারবেন। একবার প্রাথমিক কলব্যাক নিবন্ধন করুন, এবং তারপর যখনই কলব্যাক শুরু হবে তখন পুনরায় নিবন্ধন করুন।

const doSomethingWithTheFrame = (now, metadata) => {
  // Do something with the frame.
  console.log(now, metadata);
  // Re-register the callback to be notified about the next frame.
  video.requestVideoFrameCallback(doSomethingWithTheFrame);
};
// Initially register the callback to be notified about the first frame.
video.requestVideoFrameCallback(doSomethingWithTheFrame);

কলব্যাকে, now একটি DOMHighResTimeStamp এবং metadata হল একটি VideoFrameMetadata অভিধান যার নিম্নলিখিত বৈশিষ্ট্য রয়েছে:

  • presentationTime , প্রকারের DOMHighResTimeStamp : ব্যবহারকারী এজেন্ট যে সময়ে রচনার জন্য ফ্রেম জমা দিয়েছে।
  • expectedDisplayTime , এর ধরণ DOMHighResTimeStamp : ব্যবহারকারী এজেন্ট যে সময়ে ফ্রেমটি দৃশ্যমান হবে বলে আশা করে।
  • width , ধরণের unsigned long : মিডিয়া পিক্সেলে ভিডিও ফ্রেমের প্রস্থ।
  • height , টাইপ unsigned long : মিডিয়া পিক্সেলে ভিডিও ফ্রেমের উচ্চতা।
  • mediaTime , double ধরণের : উপস্থাপিত ফ্রেমের কয়েক সেকেন্ডের মধ্যে মিডিয়া প্রেজেন্টেশন টাইমস্ট্যাম্প (PTS) (যেমন video.currentTime টাইমলাইনে এর টাইমস্ট্যাম্প)।
  • presentedFrames , ধরণের unsigned long : কম্পোজিশনের জন্য জমা দেওয়া ফ্রেমের সংখ্যার গণনা। ক্লায়েন্টদের VideoFrameRequestCallback এর উদাহরণগুলির মধ্যে ফ্রেম মিস হয়েছে কিনা তা নির্ধারণ করার অনুমতি দেয়।
  • processingDuration , ধরণের double : এই ফ্রেমের মতো একই প্রেজেন্টেশন টাইমস্ট্যাম্প ( mediaTime ) সহ এনকোডেড প্যাকেটটি ডিকোডারে জমা দেওয়ার পর থেকে ডিকোড করা ফ্রেমটি উপস্থাপনার জন্য প্রস্তুত না হওয়া পর্যন্ত সেকেন্ডে অতিবাহিত সময়কাল।

WebRTC অ্যাপ্লিকেশনের জন্য, অতিরিক্ত বৈশিষ্ট্য প্রদর্শিত হতে পারে:

  • captureTime , DOMHighResTimeStamp ধরণের: স্থানীয় বা দূরবর্তী উৎস থেকে আসা ভিডিও ফ্রেমের জন্য, এটি হল সেই সময় যখন ক্যামেরা দ্বারা ফ্রেমটি ধারণ করা হয়েছিল। দূরবর্তী উৎসের জন্য, RTP টাইমস্ট্যাম্পগুলিকে সময় ক্যাপচারে রূপান্তর করার জন্য ঘড়ির সিঙ্ক্রোনাইজেশন এবং RTCP প্রেরক রিপোর্ট ব্যবহার করে ক্যাপচার সময় অনুমান করা হয়।
  • receiveTime , যা DOMHighResTimeStamp এর মতো: দূরবর্তী উৎস থেকে আসা ভিডিও ফ্রেমের জন্য, এই সময়টি হল প্ল্যাটফর্ম দ্বারা এনকোডেড ফ্রেমটি গ্রহণ করা হয়েছিল, অর্থাৎ, যে সময়ে এই ফ্রেমের সাথে সম্পর্কিত শেষ প্যাকেটটি নেটওয়ার্কের মাধ্যমে গ্রহণ করা হয়েছিল।
  • rtpTimestamp , unsigned long ধরণের: এই ভিডিও ফ্রেমের সাথে সম্পর্কিত RTP টাইমস্ট্যাম্প।

এই তালিকার বিশেষ আকর্ষণ হলো mediaTime । Chromium এর বাস্তবায়নে অডিও ঘড়িকে সময় উৎস হিসেবে ব্যবহার করা হয় যা video.currentTime সমর্থন করে, যেখানে mediaTime সরাসরি ফ্রেমের presentationTimestamp দ্বারা পূর্ণ হয়। আপনি যদি সঠিকভাবে ফ্রেমগুলিকে পুনরুৎপাদনযোগ্য উপায়ে সনাক্ত করতে চান, যার মধ্যে আপনি কোন ফ্রেমগুলি মিস করেছেন তা সনাক্ত করাও অন্তর্ভুক্ত, তাহলে mediaTime হল আপনার ব্যবহার করা উচিত।

যদি জিনিসগুলো এক ফ্রেমের বাইরে মনে হয়

উল্লম্ব সিঙ্ক্রোনাইজেশন (অথবা শুধু vsync), হল একটি গ্রাফিক্স প্রযুক্তি যা একটি ভিডিওর ফ্রেম রেট এবং একটি মনিটরের রিফ্রেশ রেট সিঙ্ক্রোনাইজ করে। যেহেতু requestVideoFrameCallback() মূল থ্রেডে চলে, কিন্তু, হুডের নীচে, ভিডিও কম্পোজিটিং কম্পোজিটর থ্রেডে ঘটে, তাই এই API থেকে সবকিছুই একটি সর্বোত্তম প্রচেষ্টা, এবং ব্রাউজার কোনও কঠোর গ্যারান্টি দেয় না।

এটা সম্ভব যে APIটি একটি ভিডিও ফ্রেম রেন্ডার করার সময়ের তুলনায় এক vsync দেরিতে হয়। API এর মাধ্যমে ওয়েব পৃষ্ঠায় করা পরিবর্তনগুলি স্ক্রিনে প্রদর্শিত হতে এক vsync লাগে ( window.requestAnimationFrame() এর মতো)। তাই যদি আপনি আপনার ওয়েব পৃষ্ঠায় mediaTime বা ফ্রেম নম্বর আপডেট করতে থাকেন এবং সংখ্যাযুক্ত ভিডিও ফ্রেমের সাথে তুলনা করেন, তাহলে অবশেষে ভিডিওটি এক ফ্রেম এগিয়ে থাকার মতো দেখাবে।

আসলে যা ঘটছে তা হল ফ্রেমটি vsync x এ প্রস্তুত, কলব্যাকটি চালু করা হয়েছে এবং ফ্রেমটি vsync x+1 এ রেন্ডার করা হয়েছে, এবং কলব্যাকে করা পরিবর্তনগুলি vsync x+2 এ রেন্ডার করা হয়েছে। কলব্যাকটি vsync লেট কিনা তা পরীক্ষা করে আপনি পরীক্ষা করতে পারেন (এবং ফ্রেমটি ইতিমধ্যেই স্ক্রিনে রেন্ডার করা হয়েছে) metadata.expectedDisplayTime মোটামুটি now নাকি ভবিষ্যতে একটি vsync। যদি এটি now থেকে প্রায় পাঁচ থেকে দশ মাইক্রোসেকেন্ডের মধ্যে হয়, তাহলে ফ্রেমটি ইতিমধ্যেই রেন্ডার করা হয়েছে; যদি expectedDisplayTime ভবিষ্যতে প্রায় ষোল মিলিসেকেন্ড হয় (ধরে নিচ্ছি আপনার ব্রাউজারটি 60Hz এ রিফ্রেশ হচ্ছে), তাহলে আপনি ফ্রেমের সাথে সিঙ্কে আছেন।

ডেমো

আমি একটি ছোট ডেমো তৈরি করেছি যা দেখায় কিভাবে ভিডিওর ফ্রেম রেটে ক্যানভাসে ফ্রেম আঁকা হয় এবং ডিবাগিংয়ের জন্য ফ্রেম মেটাডেটা কোথায় লগ করা হয়।

let paintCount = 0;
let startTime = 0.0;

const updateCanvas = (now, metadata) => {
  if (startTime === 0.0) {
    startTime = now;
  }

  ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

  const elapsed = (now - startTime) / 1000.0;
  const fps = (++paintCount / elapsed).toFixed(3);
  fpsInfo.innerText = `video fps: ${fps}`;
  metadataInfo.innerText = JSON.stringify(metadata, null, 2);

  video.requestVideoFrameCallback(updateCanvas);
};

video.requestVideoFrameCallback(updateCanvas);

উপসংহার

মানুষ দীর্ঘদিন ধরে ফ্রেম-লেভেল প্রসেসিং করে আসছে—প্রকৃত ফ্রেমে অ্যাক্সেস না পেয়ে, শুধুমাত্র video.currentTime এর উপর ভিত্তি করে। requestVideoFrameCallback() পদ্ধতিটি এই সমাধানের ক্ষেত্রে অনেক উন্নত।

স্বীকৃতি

requestVideoFrameCallback API টি থমাস গিলবার্ট দ্বারা নির্দিষ্ট এবং বাস্তবায়িত করা হয়েছিল। এই পোস্টটি জো মেডলি এবং কেইস বাস্কেস দ্বারা পর্যালোচনা করা হয়েছিল।