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

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

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

requestAnimationFrame() से अंतर

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

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

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

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

सुविधा का पता लगाना

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

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

Browser Support

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

Source

पॉलीफ़िल

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, of type unsigned long: मीडिया पिक्सल में वीडियो फ़्रेम की चौड़ाई.
  • height, of type unsigned long: The height of the video frame, in media pixels.
  • mediaTime, जिसका टाइप double है: यह मीडिया प्रज़ेंटेशन टाइमस्टैंप (पीटीएस) है. यह प्रज़ेंट किए गए फ़्रेम का टाइमस्टैंप होता है. इसे सेकंड में दिखाया जाता है. उदाहरण के लिए, video.currentTime टाइमलाइन पर इसका टाइमस्टैंप.
  • presentedFrames, जिसका टाइप unsigned long है: कंपोज़िशन के लिए सबमिट किए गए फ़्रेम की संख्या. इस कुकी की मदद से क्लाइंट यह तय कर पाते हैं कि VideoFrameRequestCallback के इंस्टेंस के बीच फ़्रेम छूट गए हैं या नहीं.
  • processingDuration, टाइप double: इस फ़्रेम (जैसे, mediaTime के जैसा) के साथ एन्कोड किए गए पैकेट को सबमिट करने से लेकर डिकोडर तक पहुंचने में लगा समय, सेकंड में. यह समय तब तक गिना जाता है, जब तक डिकोड किया गया फ़्रेम प्रज़ेंटेशन के लिए तैयार नहीं हो जाता.

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

  • captureTime, of type DOMHighResTimeStamp: For video frames coming from either a local or remote source, this is the time at which the frame was captured by the camera. रिमोट सोर्स के लिए, कैप्चर करने के समय का अनुमान लगाने के लिए, घड़ी के सिंक्रनाइज़ेशन और आरटीसीपी सेंडर रिपोर्ट का इस्तेमाल किया जाता है. इससे आरटीपी टाइमस्टैंप को कैप्चर करने के समय में बदला जा सकता है.
  • receiveTime, जिसका टाइप DOMHighResTimeStamp है: किसी रिमोट सोर्स से आने वाले वीडियो फ़्रेम के लिए, यह वह समय होता है जब एन्कोड किया गया फ़्रेम, प्लैटफ़ॉर्म को मिला था. इसका मतलब है कि यह वह समय होता है जब इस फ़्रेम से जुड़ा आखिरी पैकेट, नेटवर्क पर मिला था.
  • rtpTimestamp, टाइप unsigned long: इस वीडियो फ़्रेम से जुड़ा आरटीपी टाइमस्टैंप.

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

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

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

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

डेमो

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

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() तरीके से, इस समस्या को काफ़ी हद तक ठीक किया जा सकता है.

Acknowledgements

requestVideoFrameCallback एपीआई को थॉमस गिल्बर्ट ने बनाया और लागू किया था. इस पोस्ट की समीक्षा जो मेडली और केसी बास्क ने की है. Unsplash पर डेनिस जैंस की हीरो इमेज.