उन्नत ब्राउज़र-आधारित वीडियो प्रोसेसिंग को अनलॉक करें। कस्टम इफ़ेक्ट्स और विश्लेषण के लिए WebCodecs API के साथ सीधे रॉ VideoFrame प्लेन डेटा तक पहुंचने और उसमें हेरफेर करना सीखें।
WebCodecs वीडियोफ़्रेम प्लेन एक्सेस: रॉ वीडियो डेटा मैनिपुलेशन का एक गहन विश्लेषण
वर्षों तक, वेब ब्राउज़र में उच्च-प्रदर्शन वीडियो प्रोसेसिंग एक दूर के सपने जैसा लगता था। डेवलपर्स अक्सर <video> एलिमेंट और 2D कैनवास एपीआई की सीमाओं तक ही सीमित थे, जो शक्तिशाली होने के बावजूद, प्रदर्शन में बाधाएं पैदा करते थे और अंतर्निहित रॉ वीडियो डेटा तक सीमित पहुंच प्रदान करते थे। WebCodecs API के आगमन ने इस परिदृश्य को मौलिक रूप से बदल दिया है, जो ब्राउज़र के अंतर्निहित मीडिया कोडेक्स तक निम्न-स्तरीय पहुंच प्रदान करता है। इसकी सबसे क्रांतिकारी विशेषताओं में से एक VideoFrame ऑब्जेक्ट के माध्यम से व्यक्तिगत वीडियो फ़्रेम के रॉ डेटा तक सीधे पहुंचने और उसमें हेरफेर करने की क्षमता है।
यह लेख उन डेवलपर्स के लिए एक व्यापक गाइड है जो साधारण वीडियो प्लेबैक से आगे बढ़ना चाहते हैं। हम VideoFrame प्लेन एक्सेस की जटिलताओं का पता लगाएंगे, कलर स्पेस और मेमोरी लेआउट जैसी अवधारणाओं को स्पष्ट करेंगे, और आपको रियल-टाइम फिल्टर से लेकर परिष्कृत कंप्यूटर विज़न कार्यों तक, इन-ब्राउज़र वीडियो एप्लिकेशन की अगली पीढ़ी बनाने के लिए सशक्त बनाने के लिए व्यावहारिक उदाहरण प्रदान करेंगे।
आवश्यक शर्तें
इस गाइड का अधिकतम लाभ उठाने के लिए, आपको निम्नलिखित की ठोस समझ होनी चाहिए:
- आधुनिक जावास्क्रिप्ट: जिसमें असिंक्रोनस प्रोग्रामिंग (
async/await, Promises) शामिल है। - बुनियादी वीडियो अवधारणाएँ: फ़्रेम, रिज़ॉल्यूशन और कोडेक्स जैसे शब्दों से परिचित होना सहायक है।
- ब्राउज़र एपीआई: कैनवास 2D या WebGL जैसे एपीआई का अनुभव फायदेमंद होगा लेकिन यह कड़ाई से आवश्यक नहीं है।
वीडियो फ़्रेम, कलर स्पेस और प्लेन्स को समझना
एपीआई में गोता लगाने से पहले, हमें पहले एक ठोस मानसिक मॉडल बनाना होगा कि वीडियो फ़्रेम का डेटा वास्तव में कैसा दिखता है। एक डिजिटल वीडियो स्थिर छवियों, या फ़्रेमों का एक क्रम है। प्रत्येक फ़्रेम पिक्सेल का एक ग्रिड है, और प्रत्येक पिक्सेल का एक रंग होता है। उस रंग को कैसे संग्रहीत किया जाता है यह कलर स्पेस और पिक्सेल प्रारूप द्वारा परिभाषित किया गया है।
RGBA: वेब की मूल भाषा
अधिकांश वेब डेवलपर्स RGBA कलर मॉडल से परिचित हैं। प्रत्येक पिक्सेल को चार घटकों द्वारा दर्शाया जाता है: लाल, हरा, नीला, और अल्फा (पारदर्शिता)। डेटा को आमतौर पर मेमोरी में इंटरलीव्ड रूप में संग्रहीत किया जाता है, जिसका अर्थ है कि एक पिक्सेल के लिए R, G, B, और A मान लगातार संग्रहीत होते हैं:
[R1, G1, B1, A1, R2, G2, B2, A2, ...]
इस मॉडल में, पूरी छवि को मेमोरी के एक ही, निरंतर ब्लॉक में संग्रहीत किया जाता है। हम इसे डेटा का एक "प्लेन" होने के रूप में सोच सकते हैं।
YUV: वीडियो कम्प्रेशन की भाषा
हालांकि, वीडियो कोडेक्स शायद ही कभी सीधे RGBA के साथ काम करते हैं। वे YUV (या अधिक सटीक रूप से, Y'CbCr) कलर स्पेस पसंद करते हैं। यह मॉडल छवि जानकारी को इसमें अलग करता है:
- Y (लूमा): चमक या ग्रेस्केल जानकारी। मानव आंख लूमा में परिवर्तन के प्रति सबसे अधिक संवेदनशील होती है।
- U (Cb) और V (Cr): क्रोमिनेंस या रंग-अंतर जानकारी। मानव आंख चमक विवरण की तुलना में रंग विवरण के प्रति कम संवेदनशील होती है।
यह पृथक्करण कुशल कम्प्रेशन की कुंजी है। U और V घटकों के रिज़ॉल्यूशन को कम करके—एक तकनीक जिसे क्रोमा सबसैंपलिंग कहा जाता है—हम गुणवत्ता में न्यूनतम बोधगम्य हानि के साथ फ़ाइल आकार को काफी कम कर सकते हैं। इससे प्लेनर पिक्सेल प्रारूप बनते हैं, जहां Y, U, और V घटकों को अलग-अलग मेमोरी ब्लॉक, या "प्लेन" में संग्रहीत किया जाता है।
एक सामान्य प्रारूप I420 (YUV 4:2:0 का एक प्रकार) है, जहां पिक्सेल के प्रत्येक 2x2 ब्लॉक के लिए, चार Y सैंपल होते हैं लेकिन केवल एक U और एक V सैंपल होता है। इसका मतलब है कि U और V प्लेन की चौड़ाई और ऊंचाई Y प्लेन की आधी होती है।
इस अंतर को समझना महत्वपूर्ण है क्योंकि WebCodecs आपको इन प्लेन्स तक सीधी पहुंच प्रदान करता है, ठीक वैसे ही जैसे डिकोडर उन्हें प्रदान करता है।
VideoFrame ऑब्जेक्ट: पिक्सेल डेटा का आपका प्रवेश द्वार
इस पहेली का केंद्रीय टुकड़ा VideoFrame ऑब्जेक्ट है। यह वीडियो के एक फ्रेम का प्रतिनिधित्व करता है और इसमें न केवल पिक्सेल डेटा होता है बल्कि महत्वपूर्ण मेटाडेटा भी होता है।
VideoFrame के मुख्य गुण
format: पिक्सेल प्रारूप को इंगित करने वाली एक स्ट्रिंग (जैसे, 'I420', 'NV12', 'RGBA')।codedWidth/codedHeight: मेमोरी में संग्रहीत फ्रेम के पूर्ण आयाम, जिसमें कोडेक द्वारा आवश्यक कोई भी पैडिंग शामिल है।displayWidth/displayHeight: वे आयाम जिनका उपयोग फ्रेम को प्रदर्शित करने के लिए किया जाना चाहिए।timestamp: माइक्रोसेकंड में फ्रेम का प्रेजेंटेशन टाइमस्टैम्प।duration: माइक्रोसेकंड में फ्रेम की अवधि।
जादुई मेथड: copyTo()
रॉ पिक्सेल डेटा तक पहुंचने का प्राथमिक तरीका videoFrame.copyTo(destination, options) है। यह असिंक्रोनस मेथड फ्रेम के प्लेन डेटा को आपके द्वारा प्रदान किए गए बफर में कॉपी करता है।
destination: एकArrayBufferया एक टाइप्ड ऐरे (जैसेUint8Array) जो डेटा को रखने के लिए पर्याप्त बड़ा हो।options: एक ऑब्जेक्ट जो निर्दिष्ट करता है कि कौन से प्लेन कॉपी करने हैं और उनका मेमोरी लेआउट क्या है। यदि छोड़ दिया जाता है, तो यह सभी प्लेन्स को एक ही सन्निहित बफर में कॉपी करता है।
यह मेथड एक प्रॉमिस लौटाता है जो PlaneLayout ऑब्जेक्ट्स की एक ऐरे के साथ रिज़ॉल्व होता है, प्रत्येक फ्रेम में प्रत्येक प्लेन के लिए एक। प्रत्येक PlaneLayout ऑब्जेक्ट में दो महत्वपूर्ण जानकारी होती है:
offset: बाइट ऑफ़सेट जहां इस प्लेन का डेटा डेस्टिनेशन बफर के भीतर शुरू होता है।stride: पिक्सेल की एक पंक्ति की शुरुआत और उस प्लेन के लिए अगली पंक्ति की शुरुआत के बीच बाइट्स की संख्या।
एक महत्वपूर्ण अवधारणा: स्ट्राइड बनाम विड्थ
यह निम्न-स्तरीय ग्राफिक्स प्रोग्रामिंग में नए डेवलपर्स के लिए भ्रम के सबसे आम स्रोतों में से एक है। आप यह नहीं मान सकते कि पिक्सेल डेटा की प्रत्येक पंक्ति एक के बाद एक कसकर पैक की गई है।
- विड्थ छवि की एक पंक्ति में पिक्सेल की संख्या है।
- स्ट्राइड (पिच या लाइन स्टेप भी कहा जाता है) मेमोरी में एक पंक्ति की शुरुआत से अगली की शुरुआत तक बाइट्स की संख्या है।
अक्सर, stride width * bytes_per_pixel से अधिक होगा। ऐसा इसलिए है क्योंकि मेमोरी को अक्सर सीपीयू या जीपीयू द्वारा तेज प्रोसेसिंग के लिए हार्डवेयर सीमाओं (जैसे, 32- या 64-बाइट सीमाएं) के साथ संरेखित करने के लिए पैड किया जाता है। आपको हमेशा एक विशिष्ट पंक्ति में एक पिक्सेल के मेमोरी पते की गणना करने के लिए स्ट्राइड का उपयोग करना चाहिए।
स्ट्राइड को अनदेखा करने से विकृत या तिरछी छवियां और गलत डेटा एक्सेस होगा।
व्यावहारिक उदाहरण 1: ग्रेस्केल प्लेन को एक्सेस और प्रदर्शित करना
आइए एक सरल लेकिन शक्तिशाली उदाहरण से शुरू करें। वेब पर अधिकांश वीडियो I420 जैसे YUV प्रारूप में एन्कोड किए जाते हैं। 'Y' प्लेन प्रभावी रूप से छवि का एक पूर्ण ग्रेस्केल प्रतिनिधित्व है। हम केवल इस प्लेन को निकाल सकते हैं और इसे कैनवास पर प्रस्तुत कर सकते हैं।
async function displayGrayscale(videoFrame) {
// हम मानते हैं कि वीडियोफ़्रेम 'I420' या 'NV12' जैसे YUV प्रारूप में है।
if (!videoFrame.format.startsWith('I4')) {
console.error('This example requires a YUV 4:2:0 planar format.');
videoFrame.close();
return;
}
const yPlaneInfo = videoFrame.layout[0]; // Y प्लेन हमेशा पहले होता है।
// केवल Y प्लेन डेटा रखने के लिए एक बफर बनाएं।
const yPlaneData = new Uint8Array(yPlaneInfo.stride * videoFrame.codedHeight);
// Y प्लेन को हमारे बफर में कॉपी करें।
await videoFrame.copyTo(yPlaneData, {
rect: { x: 0, y: 0, width: videoFrame.codedWidth, height: videoFrame.codedHeight },
layout: [yPlaneInfo]
});
// अब, yPlaneData में रॉ ग्रेस्केल पिक्सेल हैं।
// हमें इसे रेंडर करने की आवश्यकता है। हम कैनवास के लिए एक RGBA बफर बनाएंगे।
const canvas = document.getElementById('my-canvas');
canvas.width = videoFrame.displayWidth;
canvas.height = videoFrame.displayHeight;
const ctx = canvas.getContext('2d');
const imageData = ctx.createImageData(canvas.width, canvas.height);
// कैनवास पिक्सेल पर पुनरावृति करें और उन्हें Y प्लेन डेटा से भरें।
for (let y = 0; y < videoFrame.displayHeight; y++) {
for (let x = 0; x < videoFrame.displayWidth; x++) {
// महत्वपूर्ण: सही स्रोत इंडेक्स खोजने के लिए स्ट्राइड का उपयोग करें!
const yIndex = y * yPlaneInfo.stride + x;
const luma = yPlaneData[yIndex];
// RGBA ImageData बफर में डेस्टिनेशन इंडेक्स की गणना करें।
const rgbaIndex = (y * canvas.width + x) * 4;
imageData.data[rgbaIndex] = luma; // लाल
imageData.data[rgbaIndex + 1] = luma; // हरा
imageData.data[rgbaIndex + 2] = luma; // नीला
imageData.data[rgbaIndex + 3] = 255; // अल्फा
}
}
ctx.putImageData(imageData, 0, 0);
// महत्वपूर्ण: इसकी मेमोरी जारी करने के लिए हमेशा VideoFrame को बंद करें।
videoFrame.close();
}
यह उदाहरण कई प्रमुख चरणों पर प्रकाश डालता है: सही प्लेन लेआउट की पहचान करना, एक डेस्टिनेशन बफर आवंटित करना, डेटा निकालने के लिए copyTo का उपयोग करना, और एक नई छवि बनाने के लिए stride का उपयोग करके डेटा पर सही ढंग से पुनरावृति करना।
व्यावहारिक उदाहरण 2: इन-प्लेस मैनिपुलेशन (सेपिया फ़िल्टर)
अब चलिए एक सीधा डेटा मैनिपुलेशन करते हैं। एक सेपिया फ़िल्टर एक क्लासिक इफ़ेक्ट है जिसे लागू करना आसान है। इस उदाहरण के लिए, RGBA फ्रेम के साथ काम करना आसान है, जो आपको कैनवास या WebGL संदर्भ से मिल सकता है।
async function applySepiaFilter(videoFrame) {
// यह उदाहरण मानता है कि इनपुट फ्रेम 'RGBA' या 'BGRA' है।
if (videoFrame.format !== 'RGBA' && videoFrame.format !== 'BGRA') {
console.error('Sepia filter example requires an RGBA frame.');
videoFrame.close();
return null;
}
// पिक्सेल डेटा रखने के लिए एक बफर आवंटित करें।
const frameDataSize = videoFrame.allocationSize();
const frameData = new Uint8Array(frameDataSize);
await videoFrame.copyTo(frameData);
const layout = videoFrame.layout[0]; // RGBA एक सिंगल प्लेन है
// अब, बफर में डेटा में हेरफेर करें।
for (let y = 0; y < videoFrame.codedHeight; y++) {
for (let x = 0; x < videoFrame.codedWidth; x++) {
const pixelIndex = y * layout.stride + x * 4; // 4 बाइट्स प्रति पिक्सेल (R,G,B,A)
const r = frameData[pixelIndex];
const g = frameData[pixelIndex + 1];
const b = frameData[pixelIndex + 2];
const tr = 0.393 * r + 0.769 * g + 0.189 * b;
const tg = 0.349 * r + 0.686 * g + 0.168 * b;
const tb = 0.272 * r + 0.534 * g + 0.131 * b;
frameData[pixelIndex] = Math.min(255, tr);
frameData[pixelIndex + 1] = Math.min(255, tg);
frameData[pixelIndex + 2] = Math.min(255, tb);
// अल्फा (frameData[pixelIndex + 3]) अपरिवर्तित रहता है।
}
}
// संशोधित डेटा के साथ एक *नया* VideoFrame बनाएं।
const newFrame = new VideoFrame(frameData, {
format: videoFrame.format,
codedWidth: videoFrame.codedWidth,
codedHeight: videoFrame.codedHeight,
timestamp: videoFrame.timestamp,
duration: videoFrame.duration
});
// मूल फ्रेम को बंद करना न भूलें!
videoFrame.close();
return newFrame;
}
यह एक पूर्ण रीड-मॉडिफाई-राइट चक्र को प्रदर्शित करता है: डेटा को कॉपी करें, स्ट्राइड का उपयोग करके उसके माध्यम से लूप करें, प्रत्येक पिक्सेल पर एक गणितीय परिवर्तन लागू करें, और परिणामी डेटा के साथ एक नया VideoFrame बनाएं। इस नए फ्रेम को फिर कैनवास पर प्रस्तुत किया जा सकता है, एक VideoEncoder को भेजा जा सकता है, या किसी अन्य प्रसंस्करण चरण में पास किया जा सकता है।
प्रदर्शन मायने रखता है: जावास्क्रिप्ट बनाम वेबअसेंबली (WASM)
जावास्क्रिप्ट में हर फ्रेम के लिए लाखों पिक्सेल (एक 1080p फ्रेम में 2 मिलियन से अधिक पिक्सेल, या RGBA में 8 मिलियन डेटा पॉइंट होते हैं) पर पुनरावृति करना धीमा हो सकता है। जबकि आधुनिक JS इंजन अविश्वसनीय रूप से तेज हैं, उच्च-रिज़ॉल्यूशन वीडियो (HD, 4K) की रियल-टाइम प्रोसेसिंग के लिए, यह दृष्टिकोण आसानी से मुख्य थ्रेड पर भारी पड़ सकता है, जिससे एक रुका हुआ उपयोगकर्ता अनुभव हो सकता है।
यहीं पर वेबअसेंबली (WASM) एक आवश्यक उपकरण बन जाता है। WASM आपको C++, Rust, या Go जैसी भाषाओं में लिखे गए कोड को ब्राउज़र के अंदर लगभग-देशी गति से चलाने की अनुमति देता है। वीडियो प्रोसेसिंग के लिए वर्कफ़्लो बन जाता है:
- जावास्क्रिप्ट में: रॉ पिक्सेल डेटा को
ArrayBufferमें प्राप्त करने के लिएvideoFrame.copyTo()का उपयोग करें। - WASM को पास करें: इस बफर का एक संदर्भ अपने संकलित WASM मॉड्यूल में पास करें। यह एक बहुत तेज़ ऑपरेशन है क्योंकि इसमें डेटा की प्रतिलिपि बनाना शामिल नहीं है।
- WASM (C++/Rust) में: अपने अत्यधिक अनुकूलित छवि प्रसंस्करण एल्गोरिदम को सीधे मेमोरी बफर पर निष्पादित करें। यह जावास्क्रिप्ट लूप की तुलना में कई गुना तेज है।
- जावास्क्रिप्ट पर लौटें: एक बार जब WASM पूरा हो जाता है, तो नियंत्रण जावास्क्रिप्ट पर लौट आता है। फिर आप एक नया
VideoFrameबनाने के लिए संशोधित बफर का उपयोग कर सकते हैं।
किसी भी गंभीर, रियल-टाइम वीडियो मैनिपुलेशन एप्लिकेशन के लिए—जैसे कि वर्चुअल बैकग्राउंड, ऑब्जेक्ट डिटेक्शन, या जटिल फ़िल्टर—वेबअसेंबली का लाभ उठाना केवल एक विकल्प नहीं है; यह एक आवश्यकता है।
विभिन्न पिक्सेल प्रारूपों को संभालना (उदा., I420, NV12)
हालांकि RGBA सरल है, आपको अक्सर VideoDecoder से प्लेनर YUV प्रारूपों में फ्रेम प्राप्त होंगे। आइए देखें कि I420 जैसे पूरी तरह से प्लेनर प्रारूप को कैसे संभालें।
I420 प्रारूप में एक VideoFrame में अपनी layout ऐरे में तीन लेआउट डिस्क्रिप्टर होंगे:
layout[0]: Y प्लेन (लूमा)। आयामcodedWidthxcodedHeightहैं।layout[1]: U प्लेन (क्रोमा)। आयामcodedWidth/2xcodedHeight/2हैं।layout[2]: V प्लेन (क्रोमा)। आयामcodedWidth/2xcodedHeight/2हैं।
यहां बताया गया है कि आप तीनों प्लेन को एक ही बफर में कैसे कॉपी करेंगे:
async function extractI420Planes(videoFrame) {
const totalSize = videoFrame.allocationSize({ format: 'I420' });
const allPlanesData = new Uint8Array(totalSize);
const layouts = await videoFrame.copyTo(allPlanesData);
// लेआउट 3 PlaneLayout ऑब्जेक्ट्स की एक ऐरे है
console.log('Y Plane Layout:', layouts[0]); // { offset: 0, stride: ... }
console.log('U Plane Layout:', layouts[1]); // { offset: ..., stride: ... }
console.log('V Plane Layout:', layouts[2]); // { offset: ..., stride: ... }
// अब आप `allPlanesData` बफर के भीतर प्रत्येक प्लेन तक पहुंच सकते हैं
// इसके विशिष्ट ऑफ़सेट और स्ट्राइड का उपयोग करके।
const yPlaneView = new Uint8Array(
allPlanesData.buffer,
layouts[0].offset,
layouts[0].stride * videoFrame.codedHeight
);
// ध्यान दें कि क्रोमा आयाम आधे हो गए हैं!
const uPlaneView = new Uint8Array(
allPlanesData.buffer,
layouts[1].offset,
layouts[1].stride * (videoFrame.codedHeight / 2)
);
const vPlaneView = new Uint8Array(
allPlanesData.buffer,
layouts[2].offset,
layouts[2].stride * (videoFrame.codedHeight / 2)
);
console.log('Accessed Y plane size:', yPlaneView.byteLength);
console.log('Accessed U plane size:', uPlaneView.byteLength);
videoFrame.close();
}
एक और सामान्य प्रारूप NV12 है, जो सेमी-प्लेनर है। इसके दो प्लेन होते हैं: एक Y के लिए, और एक दूसरा प्लेन जहां U और V मान इंटरलीव्ड होते हैं (उदा., [U1, V1, U2, V2, ...])। WebCodecs API इसे पारदर्शी रूप से संभालता है; NV12 प्रारूप में एक VideoFrame में बस अपनी layout ऐरे में दो लेआउट होंगे।
चुनौतियाँ और सर्वोत्तम प्रथाएँ
इस निम्न स्तर पर काम करना शक्तिशाली है, लेकिन यह जिम्मेदारियों के साथ आता है।
मेमोरी प्रबंधन सर्वोपरि है
एक VideoFrame काफी मात्रा में मेमोरी रखता है, जिसे अक्सर जावास्क्रिप्ट कचरा कलेक्टर के हीप के बाहर प्रबंधित किया जाता है। यदि आप इस मेमोरी को स्पष्ट रूप से जारी नहीं करते हैं, तो आप एक मेमोरी लीक का कारण बनेंगे जो ब्राउज़र टैब को क्रैश कर सकता है।
हमेशा, हमेशा videoFrame.close() को कॉल करें जब आप एक फ्रेम के साथ काम पूरा कर लें।
असिंक्रोनस प्रकृति
सभी डेटा एक्सेस असिंक्रोनस है। आपके एप्लिकेशन की वास्तुकला को रेस की स्थितियों से बचने और एक सहज प्रसंस्करण पाइपलाइन सुनिश्चित करने के लिए प्रॉमिस और async/await के प्रवाह को ठीक से संभालना चाहिए।
ब्राउज़र संगतता
WebCodecs एक आधुनिक एपीआई है। हालांकि सभी प्रमुख ब्राउज़रों में समर्थित है, हमेशा इसकी उपलब्धता की जांच करें और किसी भी विक्रेता-विशिष्ट कार्यान्वयन विवरण या सीमाओं से अवगत रहें। एपीआई का उपयोग करने का प्रयास करने से पहले फ़ीचर डिटेक्शन का उपयोग करें।
निष्कर्ष: वेब वीडियो के लिए एक नया मोर्चा
WebCodecs API के माध्यम से VideoFrame के रॉ प्लेन डेटा तक सीधे पहुंचने और उसमें हेरफेर करने की क्षमता वेब-आधारित मीडिया अनुप्रयोगों के लिए एक आदर्श बदलाव है। यह <video> एलिमेंट के ब्लैक बॉक्स को हटा देता है और डेवलपर्स को वह दानेदार नियंत्रण देता है जो पहले देशी अनुप्रयोगों के लिए आरक्षित था।
वीडियो मेमोरी लेआउट की मूल बातें—प्लेन, स्ट्राइड, और कलर फॉर्मेट—को समझकर और प्रदर्शन-महत्वपूर्ण कार्यों के लिए वेबअसेंबली की शक्ति का लाभ उठाकर, अब आप सीधे ब्राउज़र में अविश्वसनीय रूप से परिष्कृत वीडियो प्रोसेसिंग टूल बना सकते हैं। रियल-टाइम कलर ग्रेडिंग और कस्टम विज़ुअल इफ़ेक्ट्स से लेकर क्लाइंट-साइड मशीन लर्निंग और वीडियो विश्लेषण तक, संभावनाएं विशाल हैं। वेब पर उच्च-प्रदर्शन, निम्न-स्तरीय वीडियो का युग वास्तव में शुरू हो गया है।