هل تريد مشاركة الشاشة مع علامة تبويب متصفّح باستخدام HTML5؟

في العامين الماضيين، ساعدت بعض الشركات المختلفة في تحقيق وظائف شبيهة بمشاركة الشاشة باستخدام تقنيات المتصفح فقط. من واقع خبرتي، يمثّل تنفيذ VNC في تقنيات النظام الأساسي للويب فقط (أي بدون مكوّنات إضافية) مشكلة صعبة. هناك الكثير من الأشياء التي يجب مراعاتها والكثير من التحديات التي يجب التغلب عليها. تشمل هذه المشاكل وضع موضع مؤشر الماوس وإعادة توجيه ضغطات مفاتيح إعادة توجيه الألوان وإعادة تلوين ألوان 24 بت كاملة بمعدل 60 لقطة في الثانية.

جارٍ تسجيل محتوى علامة التبويب

إذا أزلنا تعقيدات مشاركة الشاشة التقليدية وركزنا على مشاركة محتويات علامة تبويب المتصفح، فإن المشكلة تبسط بشكل كبير إلى أ.) التقاط علامة التبويب المرئية في حالتها الحالية و ب.) إرسال هذا "الإطار" عبر السلك. بشكل أساسي، نحتاج إلى طريقة لتصوير نموذج العناصر في المستند (DOM) ومشاركته.

أما جزء المشاركة، فهو سهل. تقنية WebSocket قادرة جدًا على إرسال البيانات بتنسيقات مختلفة (سلسلة وJSON وثنائي). يمثل جزء أخذ لقطة شاشة أصعب بكثير. عالجت مشاريع مثل html2canvas تصوير الشاشة بلغة HTML من خلال إعادة تنفيذ محرك العرض في المتصفّح...في JavaScript. هناك مثال آخر هو تعليقات Google، على الرغم من أنها ليست مفتوحة المصدر. هذه الأنواع من المشاريع رائعة، ولكنها أيضًا بطيئة إلى حد كبير. سيكون محظوظًا أن تحصل على سرعة واحدة في الثانية لنقل البيانات، وهذا أقل بكثير من معدل سرعة 60 لقطة في الثانية.

تناقش هذه المقالة بعض الحلول المفضّلة لديّ لإثبات مفهوم "مشاركة الشاشة" لإحدى علامات التبويب.

الطريقة 1: أدوات مراقبة الطفرة + WebSocket

في وقت سابق من هذا العام، نشر رفائيل وينشتاين نهجًا واحدًا لإجراء النسخ المطابق لعلامة التبويب. ويستند أسلوبه إلى Mutation Monitorers وWebSocket.

وبشكل أساسي، ترصد علامة التبويب التي يشاركها مقدم العرض التغييرات التي تطرأ على الصفحة وترسل الاختلافات إلى العارض باستخدام websocket. بينما يتنقّل المستخدم في الصفحة أو يتفاعل معها، يختار الباحثون هذه التغييرات ويبلغون عنها مرة أخرى باستخدام مكتبة ملخّص التغييرات في رافاييل. فهذا يحافظ على أداء الأشياء. لا يتم إرسال الصفحة بأكملها لكل إطار.

وكما يشير رافايل في الفيديو، هذا مجرد دليل على المفهوم. ومع ذلك، أعتقد أنها طريقة أنيقة للجمع بين ميزة نظام أساسي أحدث مثل ميزة Mutation Monitorers وميزة قديمة مثل Websockets.

الطريقة 2: اتصال Blob من HTMLDocument + ثنائي WebSocket

الطريقة التالية هي الطريقة التي ظهرت لي مؤخرًا. هذا الأمر يشبه منهج "أداة مراقبة التغييرات"، ولكن بدلاً من إرسال ملخصات، يتم إنشاء نسخة طبق الأصل من دالة HTMLDocument وترسلها من خلال مقبس ويب ثنائي. في ما يلي طريقة الإعداد:

  1. أعِد كتابة جميع عناوين URL على الصفحة لتكون مطلقة. ويؤدي هذا الإجراء إلى منع مواد عرض الصور الثابتة ومواد عرض CSS من احتواء المحتوى على روابط معطّلة.
  2. استنساخ عنصر المستند الخاص بالصفحة: document.documentElement.cloneNode(true);
  3. جعل النسخة المحاكية للقراءة فقط وغير قابلة للاختيار، ومنع التمرير باستخدام CSS pointer-events: 'none';user-select:'none';overflow:hidden;
  4. سجِّل موضع التمرير الحالي للصفحة وأضِفها كسمات data-* على الصفحة المكرّرة.
  5. إنشاء new Blob() من .outerHTML للنسخة المكررة.

تبدو التعليمة البرمجية على هذا النحو (لقد أجريت تبسيطًا من المصدر الكامل):

function screenshotPage() {
    // 1. Rewrite current doc's imgs, css, and script URLs to be absolute before
    // we duplicate. This ensures no broken links when viewing the duplicate.
    urlsToAbsolute(document.images);
    urlsToAbsolute(document.querySelectorAll("link[rel='stylesheet']"));
    urlsToAbsolute(document.scripts);

    // 2. Duplicate entire document tree.
    var screenshot = document.documentElement.cloneNode(true);

    // 3. Screenshot should be readyonly, no scrolling, and no selections.
    screenshot.style.pointerEvents = 'none';
    screenshot.style.overflow = 'hidden';
    screenshot.style.userSelect = 'none'; // Note: need vendor prefixes

    // 4. … read on …

    // 5. Create a new .html file from the cloned content.
    var blob = new Blob([screenshot.outerHTML], {type: 'text/html'});

    // Open a popup to new file by creating a blob URL.
    window.open(window.URL.createObjectURL(blob));
}

يحتوي urlsToAbsolute() على تعبيرات عادية بسيطة لإعادة كتابة عناوين URL النسبية/الخالية من المخطط إلى عناوين URL مطلقة. وهذا ضروري كي لا تتعطّل الصور وCSS والخطوط والنصوص البرمجية عند عرضها في سياق عنوان URL ذي كائن ثنائي كبير (مثلاً، من مصدر مختلف).

آخر تعديل قمتُ به هو إضافة دعم التمرير. عندما يمرر مقدم العرض الصفحة، يجب أن يتابع المشاهد. لإجراء ذلك، أخزّن موضعَي scrollX وscrollY الحاليَين كسمات data-* في النسخة المكرّرة HTMLDocument. قبل إنشاء النقطة النهائية الأخيرة، يتم إدخال جزء من JavaScript يتم تنشيطه عند تحميل الصفحة:

// 4. Preserve current x,y scroll position of this page. See addOnPageLoad().
screenshot.dataset.scrollX = window.scrollX;
screenshot.dataset.scrollY = window.scrollY;

// 4.5. When screenshot loads (e.g. in blob URL), scroll it to the same location
// of this page. Do this by appending a window.onDOMContentLoaded listener
// which pulls out the screenshot (dupe's) saved scrollX/Y state on the DOM.
var script = document.createElement('script');
script.textContent = '(' + addOnPageLoad_.toString() + ')();'; // self calling.
screenshot.querySelector('body').appendChild(script);

// NOTE: Not to be invoked directly. When the screenshot loads, scroll it
// to the same x,y location of original page.
function addOnPageLoad() {
    window.addEventListener('DOMContentLoaded', function(e) {
    var scrollX = document.documentElement.dataset.scrollX || 0;
    var scrollY = document.documentElement.dataset.scrollY || 0;
    window.scrollTo(scrollX, scrollY);
    });

يُظهر تزييف التمرير أننا التقطنا لقطة شاشة لجزء من الصفحة الأصلية، في حين أننا في الواقع كررنا الصفحة بالكامل وأعدنا وضعها مرة فقط. #clever

الخصائص الديموغرافية

ولمشاركة علامة التبويب، نحتاج إلى تسجيل علامة التبويب وإرسالها إلى المشاهدين بشكل مستمر. من أجل ذلك، قمت بكتابة خادم وتطبيق Node websocket صغير وتطبيق وموجز يوضح التدفق. إذا لم يكن لديك اهتمام بخصوص الرمز، إليك فيديو قصيرًا يعرض الإجراءات المتّبعة:

التحسينات المستقبلية

لا يعني التحسين الواحد تكرار المستند بأكمله على كل إطار. هذا مضيعة للوقت وهو شيء يفعله مثال مراقب التغيُّرات جيدًا. هناك تحسين آخر وهو التعامل مع صور خلفية CSS النسبية في urlsToAbsolute(). هذا شيء لا يأخذه النص الحالي في الاعتبار.

الطريقة 3: واجهة برمجة تطبيقات إضافة Chrome + WebSocket الثنائي

في مؤتمر Google I/O 2012، أوضحتُ طريقة أخرى لمشاركة محتوى علامة تبويب في المتصفح. ومع ذلك، فإن هذا خداع. يتطلّب واجهة برمجة تطبيقات لإضافة Chrome: وليس محتوى HTML5 الساحر.

المصدر لهذا المصدر متوفر أيضًا على جيت هب، ولكن الجوهر هو:

  1. تسجيل علامة التبويب الحالية بتنسيق .png dataURL تتضمّن إضافات Chrome واجهة برمجة تطبيقات لذلك chrome.tabs.captureVisibleTab().
  2. حوِّل dataURL إلى عنوان Blob. يمكنك الاطّلاع على منصة مساعدة convertDataURIToBlob().
  3. أرسِل كل Blob (إطار) إلى العارض باستخدام مقبس ويب ثنائي من خلال ضبط socket.responseType='blob'.

مثال

إليك الرمز لأخذ لقطة شاشة لعلامة التبويب الحالية بتنسيق png وإرسال الإطار من خلال websocket:

var IMG_MIMETYPE = 'images/jpeg'; // Update to image/webp when crbug.com/112957 is fixed.
var IMG_QUALITY = 80; // [0-100]
var SEND_INTERVAL = 250; // ms

var ws = new WebSocket('ws://…', 'dumby-protocol');
ws.binaryType = 'blob';

function captureAndSendTab() {
    var opts = {format: IMG_MIMETYPE, quality: IMG_QUALITY};
    chrome.tabs.captureVisibleTab(null, opts, function(dataUrl) {
    // captureVisibleTab returns a dataURL. Decode it -> convert to blob -> send.
    ws.send(convertDataURIToBlob(dataUrl, IMG_MIMETYPE));
    });
}

var intervalId = setInterval(function() {
    if (ws.bufferedAmount == 0) {
    captureAndSendTab();
    }
}, SEND_INTERVAL);

التحسينات المستقبلية

إن معدل عرض الإطارات جيد بشكل مدهش لهذا الفيديو، ولكنه يمكن أن يكون أفضل من ذلك. يتمثل أحد التحسينات في إزالة النفقات العامة لتحويل عنوان URL الخاص بـ dataURL إلى دالة Blob. للأسف، لا يقدّم لنا chrome.tabs.captureVisibleTab() سوى dataURL. فإذا عرضت Blob أو صفيفًا مكتوبًا، فيمكننا إرسال ذلك مباشرة من خلال websocket بدلاً من إجراء التحويل إلى Blob بأنفسنا. لتحقيق ذلك، يُرجى تمييز crbug.com/32498 بنجمة.

الطريقة 4: WebRTC - المستقبل الحقيقي

أخيرًا وليس آخرًا.

سيساهم WebRTC في تحسين مشاركة الشاشة في المتصفِّح. في 14 آب (أغسطس) 2012، اقترح الفريق استخدام واجهة برمجة تطبيقات WebRTC Tab Content Capture لمشاركة محتوى علامة التبويب:

لنترك الطرق من 1 إلى 3 حتى يصبح هذا الرجل جاهزًا.

الخلاصة

لذا أصبح من الممكن مشاركة علامات تبويب المتصفح باستخدام تقنية الويب الحديثة!

لكن...يجب أن يستند هذا البيان إلى مقدار قليل من الملح. على الرغم من أنها رائعة، إلا أن الأساليب الواردة في هذه المقالة تفتقر إلى مشاركة تجربة المستخدم الرائعة بطريقة أو بأخرى. سيتغير كل ذلك نتيجة جهودنا المبذولة لتسجيل المحتوى في علامة التبويب WebRTC، ولكن إلى أن تصبح حقيقة واقعة، تبقى لدينا مكوّنات إضافية خاصة بالمتصفّح أو حلول محدودة، مثل تلك الواردة هنا.

هل لديك المزيد من التقنيات؟ نشر تعليق