requestVideoFrameCallback() की मदद से, हर वीडियो फ़्रेम के हिसाब से बेहतर कार्रवाइयां करें

ब्राउज़र में वीडियो के साथ बेहतर तरीके से काम करने के लिए, requestVideoFrameCallback() का इस्तेमाल करने का तरीका जानें.

HTMLVideoElement.requestVideoFrameCallback() तरीके की मदद से, वेब लेखक एक कॉलबैक रजिस्टर कर सकते हैं. यह कॉलबैक, कंपोजिटर को नया वीडियो फ़्रेम भेजे जाने पर, रेंडरिंग के चरणों में चलता है. इससे डेवलपर, वीडियो के हर फ़्रेम पर बेहतर तरीके से काम कर सकते हैं. जैसे, वीडियो को प्रोसेस करना और कैनवस पर पेंट करना, वीडियो का विश्लेषण करना या बाहरी ऑडियो सोर्स के साथ सिंक करना.

requestAnimationFrame() से अंतर

इस एपीआई का इस्तेमाल करके, कैनवस पर वीडियो फ़्रेम को ड्रॉ करने जैसे काम, स्क्रीन पर चल रहे वीडियो के फ़्रेम रेट के साथ सिंक किए जाएंगे.drawImage() window.requestAnimationFrame(), आम तौर पर हर सेकंड में करीब 60 बार ट्रिगर होता है. हालांकि, requestVideoFrameCallback(), वीडियो के असल फ़्रेम रेट के हिसाब से ट्रिगर होता है. हालांकि, इसमें एक खास बात है:

कॉलबैक जिस दर पर चलाए जाते हैं वह वीडियो की दर और ब्राउज़र की दर के बीच की कम दर होती है. इसका मतलब है कि 60Hz पर पेंट करने वाले ब्राउज़र में 25fps का वीडियो चलने पर, कॉलबैक 25Hz पर फ़ायर होंगे. उसी 60Hz ब्राउज़र में 120fps वीडियो, 60Hz पर कॉलबैक फ़ायर करेगा.

साइटमैप को क्या नाम दिया जाना चाहिए?

window.requestAnimationFrame() से मिलते-जुलते होने की वजह से, इस तरीके को शुरू में video.requestAnimationFrame() के तौर पर प्रस्तावित किया गया था और इसका नाम बदलकर requestVideoFrameCallback() कर दिया गया. इस पर लंबी चर्चा के बाद सहमति बनी.

फ़ीचर का पता लगाना

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

ब्राउज़र समर्थन

ब्राउज़र के इस्तेमाल से जुड़ी सहायता

  • Chrome: 83.
  • Edge: 83.
  • Firefox: 132.
  • Safari: 15.4.

सोर्स

Polyfill

Window.requestAnimationFrame() और HTMLVideoElement.getVideoPlaybackQuality() के आधार पर, requestVideoFrameCallback() तरीके के लिए पॉलीफ़िल उपलब्ध है. इसका इस्तेमाल करने से पहले, README में बताई गई सीमाओं के बारे में जानें.

requestVideoFrameCallback() तरीके का इस्तेमाल करना

अगर आपने कभी 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: दिखाए गए फ़्रेम का मीडिया प्रज़ेंटेशन टाइमस्टैंप (पीटीएस) सेकंड में. उदाहरण के लिए, video.currentTime टाइमलाइन पर इसका टाइमस्टैंप.
  • presentedFrames, unsigned long टाइप का: कॉम्पोज़िशन के लिए सबमिट किए गए फ़्रेम की संख्या. इससे क्लाइंट यह तय कर सकते हैं कि VideoFrameRequestCallback के इंस्टेंस के बीच फ़्रेम छूटे हैं या नहीं.
  • processingDuration, टाइप double: डिकोडर में इस फ़्रेम (उदाहरण के लिए, mediaTime जैसा) के प्रज़ेंटेशन टाइमस्टैंप (पीटीएस) के साथ एन्कोड किए गए पैकेट को सबमिट करने से लेकर, डिकोड किया गया फ़्रेम प्रज़ेंटेशन के लिए तैयार होने तक बीता समय, सेकंड में.

WebRTC ऐप्लिकेशन के लिए, अन्य प्रॉपर्टी दिख सकती हैं:

  • captureTime, टाइप DOMHighResTimeStamp: लोकल या रिमोट सोर्स से आने वाले वीडियो फ़्रेम के लिए, यह वह समय होता है जब कैमरे ने फ़्रेम कैप्चर किया था. रिमोट सोर्स के लिए, आरटीपी टाइमस्टैंप को कैप्चर टाइम में बदलने के लिए, क्लॉक सिंक्रोनाइज़ेशन और आरटीसीपी भेजने वाले की रिपोर्ट का इस्तेमाल करके, कैप्चर टाइम का अनुमान लगाया जाता है.
  • receiveTime, टाइप DOMHighResTimeStamp: रिमोट सोर्स से आने वाले वीडियो फ़्रेम के लिए, यह वह समय होता है जब प्लैटफ़ॉर्म को एन्कोड किया गया फ़्रेम मिलता है. इसका मतलब है कि यह वह समय होता है जब नेटवर्क पर इस फ़्रेम से जुड़ा आखिरी पैकेट मिलता है.
  • rtpTimestamp, टाइप unsigned long: इस वीडियो फ़्रेम से जुड़ा आरटीपी टाइमस्टैंप.

इस सूची में mediaTime खास तौर पर दिलचस्प है. Chromium में, ऑडियो क्लॉक को टाइम सोर्स के तौर पर इस्तेमाल किया जाता है, जो video.currentTime को बैक करता है. वहीं, mediaTime को सीधे फ़्रेम के presentationTimestamp से पॉप्युलेट किया जाता है. अगर आपको फ़्रेम की पहचान फिर से करने के तरीके के बारे में सटीक जानकारी चाहिए, तो mediaTime का इस्तेमाल करें. इसमें यह भी पता लगाया जा सकता है कि आपको कौनसे फ़्रेम नहीं मिले.

अगर आपको लगता है कि वीडियो एक फ़्रेम पीछे चल रहा है, तो…

वर्टिकल सिंक (या सिर्फ़ vsync), एक ऐसी ग्राफ़िक्स टेक्नोलॉजी है जो वीडियो के फ़्रेम रेट और मॉनिटर के रीफ़्रेश रेट को सिंक करती है. requestVideoFrameCallback() मुख्य थ्रेड पर चलता है, लेकिन वीडियो कॉम्पोज़ करने की प्रोसेस, कॉम्पोज़र थ्रेड पर होती है. इसलिए, इस एपीआई से मिलने वाली हर चीज़ को बेहतर बनाने की पूरी कोशिश की जाती है. हालांकि, ब्राउज़र इसकी कोई गारंटी नहीं देता. ऐसा हो सकता है कि वीडियो फ़्रेम रेंडर होने के मुकाबले, एपीआई एक vsync देर से हो. एपीआई की मदद से वेब पेज में किए गए बदलावों को स्क्रीन पर दिखने में एक vsync लगता है (जैसा कि window.requestAnimationFrame() में होता है). इसलिए, अगर अपने वेब पेज पर mediaTime या फ़्रेम नंबर को अपडेट करते रहते हैं और उसकी तुलना, नंबर वाले वीडियो फ़्रेम से करते हैं, तो वीडियो एक फ़्रेम आगे दिखेगा.

असल में, फ़्रेम vsync x पर तैयार होता है, कॉलबैक ट्रिगर होता है, और फ़्रेम vsync x+1 पर रेंडर होता है. साथ ही, कॉलबैक में किए गए बदलाव vsync x+2 पर रेंडर होते हैं. यह पता लगाया जा सकता है कि कॉलबैक, वीएसीपी के बाद हुआ है या नहीं (और फ़्रेम पहले से ही स्क्रीन पर रेंडर हो चुका है) इसके लिए, यह देखें कि metadata.expectedDisplayTime, now के आस-पास है या आने वाले समय में एक वीएसीपी है. अगर यह now के पांच से दस माइक्रोसेकंड के अंदर है, तो फ़्रेम पहले से रेंडर हो चुका है; अगर expectedDisplayTime आने वाले समय में करीब 16 मिलीसेकंड है (यह मानते हुए कि आपका ब्राउज़र/स्क्रीन 60Hz पर रीफ़्रेश हो रही है), तो इसका मतलब है कि आप फ़्रेम के साथ सिंक हैं.

डेमो

मैंने Glitch पर एक छोटा डेमो बनाया है. इसमें दिखाया गया है कि वीडियो के फ़्रेम रेट के हिसाब से, कैनवस पर फ़्रेम कैसे बनाए जाते हैं. साथ ही, यह भी बताया गया है कि डीबग करने के लिए, फ़्रेम का मेटाडेटा कहां लॉग किया जाता है.

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 एपीआई को थॉमस गिल्बर्ट ने तय और लागू किया था. इस पोस्ट की समीक्षा जो मेडली और केइस बेस्केस ने की है. Unsplash पर डेनिस जॉन्स की हीरो इमेज.