افتح إمكانات بث الفيديو عالي الجودة في المتصفح. تعلم كيفية تطبيق الترشيح الزمني المتقدم لتقليل الضوضاء باستخدام WebCodecs API ومعالجة VideoFrame.
إتقان WebCodecs: تحسين جودة الفيديو بتقنية تقليل الضوضاء الزمنية
في عالم اتصالات الفيديو عبر الويب، والبث المباشر، وتطبيقات الوقت الفعلي، تعد الجودة أمرًا بالغ الأهمية. يتوقع المستخدمون في جميع أنحاء العالم فيديو واضحًا ونقيًا، سواء كانوا في اجتماع عمل، أو يشاهدون حدثًا مباشرًا، أو يتفاعلون مع خدمة عن بُعد. ومع ذلك، غالبًا ما تعاني تدفقات الفيديو من عيب مستمر ومشتت للانتباه: الضوضاء. هذه الضوضاء الرقمية، التي تظهر غالبًا كنسيج محبب أو ثابت، يمكن أن تقلل من جودة تجربة المشاهدة، والمثير للدهشة، أنها تزيد من استهلاك النطاق الترددي. لحسن الحظ، تمنح واجهة برمجة تطبيقات المتصفح القوية، WebCodecs، المطورين تحكمًا غير مسبوق ومنخفض المستوى لمعالجة هذه المشكلة بشكل مباشر.
سيأخذك هذا الدليل الشامل في رحلة عميقة لاستخدام WebCodecs لتقنية معالجة فيديو محددة وعالية التأثير: تقليل الضوضاء الزمنية. سنستكشف ما هي ضوضاء الفيديو، ولماذا هي ضارة، وكيف يمكنك الاستفادة من كائن VideoFrame
لبناء خط أنابيب للترشيح مباشرة في المتصفح. سنغطي كل شيء بدءًا من النظرية الأساسية إلى التنفيذ العملي باستخدام جافاسكريبت، واعتبارات الأداء مع WebAssembly، والمفاهيم المتقدمة لتحقيق نتائج احترافية.
ما هي ضوضاء الفيديو ولماذا هي مهمة؟
قبل أن نتمكن من حل مشكلة ما، يجب علينا أولاً أن نفهمها. في الفيديو الرقمي، تشير الضوضاء إلى التغيرات العشوائية في معلومات السطوع أو اللون في إشارة الفيديو. إنها نتيجة ثانوية غير مرغوب فيها لعملية التقاط الصور ونقلها.
مصادر وأنواع الضوضاء
- ضوضاء المستشعر: المسبب الرئيسي. في ظروف الإضاءة المنخفضة، تقوم مستشعرات الكاميرا بتضخيم الإشارة الواردة لإنشاء صورة ساطعة بما فيه الكفاية. تعمل عملية التضخيم هذه أيضًا على تعزيز التقلبات الإلكترونية العشوائية، مما يؤدي إلى ظهور حبيبات مرئية.
- الضوضاء الحرارية: يمكن أن تسبب الحرارة الناتجة عن إلكترونيات الكاميرا حركة عشوائية للإلكترونات، مما يخلق ضوضاء مستقلة عن مستوى الضوء.
- ضوضاء التكميم (Quantization Noise): يتم إدخالها أثناء عمليات التحويل من التناظري إلى الرقمي والضغط، حيث يتم تعيين القيم المستمرة إلى مجموعة محدودة من المستويات المنفصلة.
تتجلى هذه الضوضاء عادةً في صورة ضوضاء غاوسية (Gaussian noise)، حيث تختلف شدة كل بكسل بشكل عشوائي حول قيمته الحقيقية، مما يخلق حبيبات دقيقة ومتلألئة عبر الإطار بأكمله.
التأثير المزدوج للضوضاء
ضوضاء الفيديو هي أكثر من مجرد مشكلة تجميلية؛ فلها عواقب تقنية وإدراكية كبيرة:
- تدهور تجربة المستخدم: التأثير الأكثر وضوحًا هو على الجودة البصرية. يبدو الفيديو المليء بالضوضاء غير احترافي، ومشتتًا للانتباه، ويمكن أن يجعل من الصعب تمييز التفاصيل المهمة. في تطبيقات مثل المؤتمرات عن بعد، يمكن أن تجعل المشاركين يظهرون بشكل محبب وغير واضح، مما ينتقص من الشعور بالوجود.
- انخفاض كفاءة الضغط: هذه هي المشكلة الأقل بديهية ولكنها بنفس القدر من الأهمية. تحقق برامج ترميز الفيديو الحديثة (مثل H.264، VP9، AV1) نسب ضغط عالية من خلال استغلال التكرار. تبحث عن أوجه التشابه بين الإطارات (التكرار الزمني) وداخل الإطار الواحد (التكرار المكاني). الضوضاء، بطبيعتها، عشوائية وغير متوقعة. إنها تكسر أنماط التكرار هذه. يرى المشفر الضوضاء العشوائية كتفاصيل عالية التردد يجب الحفاظ عليها، مما يجبره على تخصيص المزيد من البتات لترميز الضوضاء بدلاً من المحتوى الفعلي. يؤدي هذا إما إلى حجم ملف أكبر لنفس الجودة المدركة أو جودة أقل بنفس معدل البت.
عن طريق إزالة الضوضاء قبل الترميز، يمكننا جعل إشارة الفيديو أكثر قابلية للتنبؤ، مما يسمح للمشفر بالعمل بكفاءة أكبر. يؤدي هذا إلى جودة بصرية أفضل، واستهلاك نطاق ترددي أقل، وتجربة بث أكثر سلاسة للمستخدمين في كل مكان.
تقديم WebCodecs: قوة التحكم منخفض المستوى في الفيديو
لسنوات، كانت المعالجة المباشرة للفيديو في المتصفح محدودة. كان المطورون محصورين إلى حد كبير في إمكانيات عنصر <video>
وواجهة Canvas API، والتي غالبًا ما كانت تتضمن عمليات قراءة من وحدة معالجة الرسومات تقتل الأداء. يغير WebCodecs قواعد اللعبة بالكامل.
WebCodecs هي واجهة برمجة تطبيقات منخفضة المستوى توفر وصولاً مباشرًا إلى برامج ترميز الوسائط المدمجة في المتصفح. وهي مصممة للتطبيقات التي تتطلب تحكمًا دقيقًا في معالجة الوسائط، مثل برامج تحرير الفيديو، ومنصات الألعاب السحابية، وعملاء الاتصال المتقدمين في الوقت الفعلي.
المكون الأساسي الذي سنركز عليه هو كائن VideoFrame
. يمثل VideoFrame
إطارًا واحدًا من الفيديو كصورة، ولكنه أكثر بكثير من مجرد صورة نقطية بسيطة. إنه كائن فعال للغاية وقابل للنقل يمكنه الاحتفاظ ببيانات الفيديو بتنسيقات بكسل مختلفة (مثل RGBA، I420، NV12) ويحمل بيانات وصفية مهمة مثل:
timestamp
: وقت عرض الإطار بالميكروثانية.duration
: مدة الإطار بالميكروثانية.codedWidth
وcodedHeight
: أبعاد الإطار بالبكسل.format
: تنسيق بكسل البيانات (على سبيل المثال، 'I420'، 'RGBA').
بشكل حاسم، يوفر VideoFrame
طريقة تسمى copyTo()
، والتي تسمح لنا بنسخ بيانات البكسل الخام وغير المضغوطة إلى ArrayBuffer
. هذه هي نقطة دخولنا للتحليل والمعالجة. بمجرد حصولنا على البايتات الخام، يمكننا تطبيق خوارزمية تقليل الضوضاء الخاصة بنا ثم إنشاء VideoFrame
جديد من البيانات المعدلة لتمريره إلى أسفل خط أنابيب المعالجة (على سبيل المثال، إلى مشفر فيديو أو على لوحة canvas).
فهم الترشيح الزمني
يمكن تصنيف تقنيات تقليل الضوضاء على نطاق واسع إلى نوعين: مكاني وزمني.
- الترشيح المكاني: تعمل هذه التقنية على إطار واحد بمعزل عن غيره. تقوم بتحليل العلاقات بين البكسلات المجاورة لتحديد وتنعيم الضوضاء. مثال بسيط هو مرشح التمويه (blur filter). على الرغم من فعاليتها في تقليل الضوضاء، يمكن للمرشحات المكانية أيضًا أن تخفف من حدة التفاصيل والحواف المهمة، مما يؤدي إلى صورة أقل وضوحًا.
- الترشيح الزمني: هذا هو النهج الأكثر تطورًا الذي نركز عليه. يعمل عبر إطارات متعددة بمرور الوقت. المبدأ الأساسي هو أنه من المحتمل أن يكون محتوى المشهد الفعلي مرتبطًا من إطار إلى آخر، بينما تكون الضوضاء عشوائية وغير مرتبطة. من خلال مقارنة قيمة بكسل في موقع معين عبر عدة إطارات، يمكننا التمييز بين الإشارة المتسقة (الصورة الحقيقية) والتقلبات العشوائية (الضوضاء).
أبسط أشكال الترشيح الزمني هو المتوسط الزمني (temporal averaging). تخيل أن لديك الإطار الحالي والإطار السابق. بالنسبة لأي بكسل معين، من المحتمل أن تكون قيمته 'الحقيقية' في مكان ما بين قيمته في الإطار الحالي وقيمته في الإطار السابق. من خلال مزجها، يمكننا حساب متوسط الضوضاء العشوائية. يمكن حساب قيمة البكسل الجديدة بمتوسط مرجح بسيط:
new_pixel = (alpha * current_pixel) + ((1 - alpha) * previous_pixel)
هنا، alpha
هو عامل مزج بين 0 و 1. قيمة alpha
أعلى تعني أننا نثق في الإطار الحالي أكثر، مما يؤدي إلى تقليل أقل للضوضاء ولكن مع تشوهات حركية أقل. قيمة alpha
أقل توفر تقليلًا أقوى للضوضاء ولكن يمكن أن تسبب 'تخييلًا' (ghosting) أو مسارات في المناطق التي بها حركة. إيجاد التوازن الصحيح هو المفتاح.
تطبيق مرشح متوسط زمني بسيط
دعنا نبني تطبيقًا عمليًا لهذا المفهوم باستخدام WebCodecs. سيتكون خط الأنابيب الخاص بنا من ثلاث خطوات رئيسية:
- الحصول على تدفق من كائنات
VideoFrame
(على سبيل المثال، من كاميرا الويب). - لكل إطار، قم بتطبيق مرشحنا الزمني باستخدام بيانات الإطار السابق.
- إنشاء
VideoFrame
جديد ومنقح.
الخطوة 1: إعداد تدفق الإطارات
أسهل طريقة للحصول على تدفق مباشر من كائنات VideoFrame
هي باستخدام MediaStreamTrackProcessor
، الذي يستهلك MediaStreamTrack
(مثل واحد من getUserMedia
) ويعرض إطاراته كتدفق قابل للقراءة.
إعداد جافاسكريبت المفاهيمي:
async function setupVideoStream() {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
const track = stream.getVideoTracks()[0];
const trackProcessor = new MediaStreamTrackProcessor({ track });
const reader = trackProcessor.readable.getReader();
let previousFrameBuffer = null;
let previousFrameTimestamp = -1;
while (true) {
const { value: frame, done } = await reader.read();
if (done) break;
// Here is where we will process each 'frame'
const processedFrame = await applyTemporalFilter(frame, previousFrameBuffer);
// For the next iteration, we need to store the data of the *original* current frame
// You would copy the original frame's data to 'previousFrameBuffer' here before closing it.
// Don't forget to close frames to release memory!
frame.close();
// Do something with processedFrame (e.g., render to canvas, encode)
// ... and then close it too!
processedFrame.close();
}
}
الخطوة 2: خوارزمية الترشيح - العمل مع بيانات البكسل
هذا هو جوهر عملنا. داخل دالة applyTemporalFilter
الخاصة بنا، نحتاج إلى الوصول إلى بيانات البكسل للإطار الوارد. للتبسيط، دعنا نفترض أن إطاراتنا بتنسيق 'RGBA'. يتم تمثيل كل بكسل بـ 4 بايت: أحمر، أخضر، أزرق، وألفا (الشفافية).
async function applyTemporalFilter(currentFrame, previousFrameBuffer) {
// Define our blending factor. 0.8 means 80% of the new frame and 20% of the old.
const alpha = 0.8;
// Get the dimensions
const width = currentFrame.codedWidth;
const height = currentFrame.codedHeight;
// Allocate an ArrayBuffer to hold the pixel data of the current frame.
const currentFrameSize = width * height * 4; // 4 bytes per pixel for RGBA
const currentFrameBuffer = new Uint8Array(currentFrameSize);
await currentFrame.copyTo(currentFrameBuffer);
// If this is the first frame, there's no previous frame to blend with.
// Just return it as is, but store its buffer for the next iteration.
if (!previousFrameBuffer) {
const newFrameBuffer = new Uint8Array(currentFrameBuffer);
// We'll update our global 'previousFrameBuffer' with this one outside this function.
return { buffer: newFrameBuffer, frame: currentFrame };
}
// Create a new buffer for our output frame.
const outputFrameBuffer = new Uint8Array(currentFrameSize);
// The main processing loop.
for (let i = 0; i < currentFrameSize; i++) {
const currentPixelValue = currentFrameBuffer[i];
const previousPixelValue = previousFrameBuffer[i];
// Apply the temporal averaging formula for each color channel.
// We skip the alpha channel (every 4th byte).
if ((i + 1) % 4 !== 0) {
outputFrameBuffer[i] = Math.round(alpha * currentPixelValue + (1 - alpha) * previousPixelValue);
} else {
// Keep the alpha channel as is.
outputFrameBuffer[i] = currentPixelValue;
}
}
return { buffer: outputFrameBuffer, frame: currentFrame };
}
ملاحظة حول تنسيقات YUV (I420, NV12): بينما يسهل فهم RGBA، تتم معالجة معظم مقاطع الفيديو أصلاً في مساحات ألوان YUV لتحقيق الكفاءة. يعد التعامل مع YUV أكثر تعقيدًا حيث يتم تخزين معلومات اللون (U, V) والسطوع (Y) بشكل منفصل (في 'مستويات'). يظل منطق الترشيح كما هو، لكنك ستحتاج إلى التكرار فوق كل مستوى (Y و U و V) بشكل منفصل، مع مراعاة أبعاد كل منها (غالبًا ما تكون مستويات الألوان ذات دقة أقل، وهي تقنية تسمى chroma subsampling).
الخطوة 3: إنشاء `VideoFrame` الجديد المرشح
بعد انتهاء حلقتنا، يحتوي outputFrameBuffer
على بيانات البكسل لإطارنا الجديد والأنظف. نحتاج الآن إلى تغليف هذا في كائن VideoFrame
جديد، مع التأكد من نسخ البيانات الوصفية من الإطار الأصلي.
// Inside your main loop after calling applyTemporalFilter...
const { buffer: processedBuffer, frame: originalFrame } = await applyTemporalFilter(frame, previousFrameBuffer);
// Create a new VideoFrame from our processed buffer.
const newFrame = new VideoFrame(processedBuffer, {
format: 'RGBA',
codedWidth: originalFrame.codedWidth,
codedHeight: originalFrame.codedHeight,
timestamp: originalFrame.timestamp,
duration: originalFrame.duration
});
// IMPORTANT: Update the previous frame buffer for the next iteration.
// We need to copy the *original* frame's data, not the filtered data.
// A separate copy should be made before filtering.
previousFrameBuffer = new Uint8Array(originalFrameData);
// Now you can use 'newFrame'. Render it, encode it, etc.
// renderer.draw(newFrame);
// And critically, close it when you are done to prevent memory leaks.
newFrame.close();
إدارة الذاكرة أمر بالغ الأهمية: يمكن لكائنات VideoFrame
أن تحتفظ بكميات كبيرة من بيانات الفيديو غير المضغوطة وقد تكون مدعومة بذاكرة خارج كومة جافاسكريبت. يجب عليك استدعاء frame.close()
على كل إطار تنتهي من استخدامه. سيؤدي عدم القيام بذلك إلى استنفاد الذاكرة بسرعة وتعطل علامة التبويب.
اعتبارات الأداء: جافاسكريبت مقابل WebAssembly
التنفيذ النقي باستخدام جافاسكريبت المذكور أعلاه ممتاز للتعلم والعرض. ومع ذلك، بالنسبة لفيديو بدقة 1080p (1920x1080) وبمعدل 30 إطارًا في الثانية، تحتاج حلقتنا إلى إجراء أكثر من 248 مليون عملية حسابية في الثانية! (1920 * 1080 * 4 بايت * 30 إطارًا في الثانية). بينما تعد محركات جافاسكريبت الحديثة سريعة بشكل لا يصدق، فإن هذه المعالجة لكل بكسل هي حالة استخدام مثالية لتقنية أكثر توجهاً نحو الأداء: WebAssembly (Wasm).
نهج WebAssembly
يسمح لك WebAssembly بتشغيل التعليمات البرمجية المكتوبة بلغات مثل C++ أو Rust أو Go في المتصفح بسرعة شبه أصلية. منطق مرشحنا الزمني بسيط للتنفيذ في هذه اللغات. يمكنك كتابة دالة تأخذ مؤشرات إلى مخازن الإدخال والإخراج وتؤدي نفس عملية المزج التكرارية.
دالة C++ مفاهيمية لـ Wasm:
extern "C" {
void apply_temporal_filter(unsigned char* current_frame, unsigned char* previous_frame, unsigned char* output_frame, int buffer_size, float alpha) {
for (int i = 0; i < buffer_size; ++i) {
if ((i + 1) % 4 != 0) { // Skip alpha channel
output_frame[i] = (unsigned char)(alpha * current_frame[i] + (1.0 - alpha) * previous_frame[i]);
} else {
output_frame[i] = current_frame[i];
}
}
}
}
من جانب جافاسكريبت، ستقوم بتحميل وحدة Wasm المترجمة هذه. تأتي ميزة الأداء الرئيسية من مشاركة الذاكرة. يمكنك إنشاء ArrayBuffer
s في جافاسكريبت مدعومة بالذاكرة الخطية لوحدة Wasm. يتيح لك هذا تمرير بيانات الإطار إلى Wasm دون أي نسخ مكلف. ثم تعمل حلقة معالجة البكسل بأكملها كاستدعاء دالة Wasm واحد ومحسن للغاية، وهو أسرع بكثير من حلقة `for` في جافاسكريبت.
تقنيات الترشيح الزمني المتقدمة
المتوسط الزمني البسيط هو نقطة بداية رائعة، لكن له عيب كبير: إنه يُدخل ضبابية الحركة أو 'التخييل'. عندما يتحرك كائن، يتم مزج بكسلاته في الإطار الحالي مع بكسلات الخلفية من الإطار السابق، مما يخلق أثرًا. لبناء مرشح احترافي حقيقي، نحتاج إلى مراعاة الحركة.
الترشيح الزمني المعوض بالحركة (MCTF)
المعيار الذهبي لتقليل الضوضاء الزمنية هو الترشيح الزمني المعوض بالحركة. بدلاً من مزج بكسل بشكل أعمى مع البكسل الموجود في نفس الإحداثيات (x, y) في الإطار السابق، يحاول MCTF أولاً معرفة من أين أتى هذا البكسل.
تتضمن العملية ما يلي:
- تقدير الحركة: تقسم الخوارزمية الإطار الحالي إلى كتل (على سبيل المثال، 16x16 بكسل). لكل كتلة، تبحث في الإطار السابق للعثور على الكتلة الأكثر تشابهًا (على سبيل المثال، لديها أقل مجموع للفروق المطلقة). يسمى الإزاحة بين هاتين الكتلتين 'متجه الحركة'.
- تعويض الحركة: بعد ذلك، تبني نسخة 'معوضة بالحركة' من الإطار السابق عن طريق إزاحة الكتل وفقًا لمتجهات حركتها.
- الترشيح: أخيرًا، تقوم بإجراء المتوسط الزمني بين الإطار الحالي وهذا الإطار السابق الجديد المعوض بالحركة.
بهذه الطريقة، يتم مزج كائن متحرك مع نفسه من الإطار السابق، وليس مع الخلفية التي كشف عنها للتو. هذا يقلل بشكل كبير من تشوهات التخييل. يعد تنفيذ تقدير الحركة مكثفًا حسابيًا ومعقدًا، وغالبًا ما يتطلب خوارزميات متقدمة، وهو مهمة حصرية تقريبًا لـ WebAssembly أو حتى لمُظلِّلات حساب WebGPU.
الترشيح التكيفي
تحسين آخر هو جعل المرشح تكيفيًا. بدلاً من استخدام قيمة alpha
ثابتة للإطار بأكمله، يمكنك تغييرها بناءً على الظروف المحلية.
- التكيف مع الحركة: في المناطق التي تم فيها اكتشاف حركة عالية، يمكنك زيادة
alpha
(على سبيل المثال، إلى 0.95 أو 1.0) للاعتماد بشكل شبه كامل على الإطار الحالي، مما يمنع أي ضبابية حركة. في المناطق الثابتة (مثل جدار في الخلفية)، يمكنك تقليلalpha
(على سبيل المثال، إلى 0.5) لتقليل الضوضاء بشكل أقوى بكثير. - التكيف مع الإضاءة: غالبًا ما تكون الضوضاء أكثر وضوحًا في المناطق المظلمة من الصورة. يمكن جعل المرشح أكثر قوة في الظلال وأقل قوة في المناطق الساطعة للحفاظ على التفاصيل.
حالات الاستخدام والتطبيقات العملية
تفتح القدرة على إجراء تقليل ضوضاء عالي الجودة في المتصفح العديد من الإمكانيات:
- الاتصال في الوقت الحقيقي (WebRTC): المعالجة المسبقة لتغذية كاميرا الويب للمستخدم قبل إرسالها إلى مشفر الفيديو. هذا فوز كبير لمكالمات الفيديو في بيئات الإضاءة المنخفضة، مما يحسن الجودة البصرية ويقلل من النطاق الترددي المطلوب.
- تحرير الفيديو المستند إلى الويب: تقديم مرشح 'إزالة الضوضاء' كميزة في محرر فيديو داخل المتصفح، مما يسمح للمستخدمين بتنظيف لقطاتهم التي تم تحميلها دون معالجة من جانب الخادم.
- الألعاب السحابية وسطح المكتب البعيد: تنظيف تدفقات الفيديو الواردة لتقليل تشوهات الضغط وتوفير صورة أوضح وأكثر استقرارًا.
- المعالجة المسبقة للرؤية الحاسوبية: بالنسبة لتطبيقات الذكاء الاصطناعي / تعلم الآلة المستندة إلى الويب (مثل تتبع الكائنات أو التعرف على الوجه)، يمكن أن يؤدي تقليل ضوضاء الفيديو المدخل إلى استقرار البيانات وتحقيق نتائج أكثر دقة وموثوقية.
التحديات والتوجهات المستقبلية
على الرغم من قوته، لا يخلو هذا النهج من التحديات. يحتاج المطورون إلى الانتباه إلى:
- الأداء: المعالجة في الوقت الفعلي لفيديو HD أو 4K تتطلب الكثير. التنفيذ الفعال، عادةً مع WebAssembly، أمر لا بد منه.
- الذاكرة: تخزين إطار واحد أو أكثر من الإطارات السابقة كمخازن غير مضغوطة يستهلك قدرًا كبيرًا من ذاكرة الوصول العشوائي. الإدارة الدقيقة ضرورية.
- الكمون (Latency): كل خطوة معالجة تضيف كمونًا. بالنسبة للاتصال في الوقت الحقيقي، يجب تحسين خط الأنابيب هذا بشكل كبير لتجنب التأخير الملحوظ.
- المستقبل مع WebGPU: ستوفر واجهة WebGPU API الناشئة حدودًا جديدة لهذا النوع من العمل. ستسمح بتشغيل هذه الخوارزميات لكل بكسل كمُظلِّلات حساب متوازية للغاية على وحدة معالجة الرسومات في النظام، مما يوفر قفزة هائلة أخرى في الأداء حتى على WebAssembly على وحدة المعالجة المركزية.
الخاتمة
تمثل واجهة WebCodecs API حقبة جديدة للمعالجة المتقدمة للوسائط على الويب. إنها تهدم حواجز عنصر <video>
التقليدي الذي يعمل كصندوق أسود وتمنح المطورين التحكم الدقيق اللازم لبناء تطبيقات فيديو احترافية حقًا. يعد تقليل الضوضاء الزمنية مثالًا مثاليًا على قوتها: تقنية متطورة تعالج بشكل مباشر كلاً من الجودة التي يدركها المستخدم والكفاءة التقنية الأساسية.
لقد رأينا أنه من خلال اعتراض كائنات VideoFrame
الفردية، يمكننا تنفيذ منطق ترشيح قوي لتقليل الضوضاء، وتحسين قابلية الضغط، وتقديم تجربة فيديو فائقة. في حين أن التنفيذ البسيط باستخدام جافاسكريبت يعد نقطة انطلاق رائعة، فإن الطريق إلى حل جاهز للإنتاج وفي الوقت الفعلي يمر عبر أداء WebAssembly، وفي المستقبل، قوة المعالجة المتوازية لـ WebGPU.
في المرة القادمة التي ترى فيها فيديو محببًا في تطبيق ويب، تذكر أن الأدوات لإصلاحه أصبحت الآن، ولأول مرة، في أيدي مطوري الويب مباشرة. إنه وقت مثير للبناء باستخدام الفيديو على الويب.