Son birkaç yılda, birkaç farklı şirketin yalnızca tarayıcı teknolojilerini kullanarak ekran paylaşımına benzer işlevler elde etmesine yardımcı oldum. Deneyimlerime göre, VNC'yi yalnızca web platformu teknolojilerinde (yani eklenti olmadan) uygulamak zor bir sorun. Dikkate alınması gereken birçok şey ve aşılması gereken birçok zorluk vardır. Fare işaretçisi konumunu aktarma, tuş vuruşlarını iletme ve 60 fps'de tam 24 bit renkli yeniden boyama elde etme, bu sorunlardan yalnızca birkaçıdır.
Sekme içeriği yakalanıyor
Geleneksel ekran paylaşımının karmaşıklığını ortadan kaldırıp tarayıcı sekmesinin içeriğini paylaşmaya odaklanırsak sorun büyük ölçüde basitleşir: a.) Görünür sekmeyi mevcut durumunda yakalama ve b.) Bu "kareyi" kablo üzerinden gönderme. Temel olarak, DOM'un anlık görüntüsünü alıp paylaşmanın bir yoluna ihtiyacımız var.
Paylaşım kısmı kolaydır. WebSockets, farklı biçimlerde (dize, JSON, ikili) veri gönderme konusunda oldukça yeteneklidir. Anlık görüntü alma kısmı çok daha zor bir problem. html2canvas gibi projeler, tarayıcının oluşturma motorunu JavaScript'te yeniden uygulayarak HTML ekran görüntüsü alma sorununu çözdü. Açık kaynaklı olmasa da Google Geri Bildirim Aracı da başka bir örnektir. Bu tür projeler çok havalı olsa da son derece yavaş ilerler. 60 fps'lik bir performans elde etmek şöyle dursun, 1 fps'lik bir performans elde ederseniz şanslı sayılırsınız.
Bu makalede, bir sekmenin "ekran paylaşımı" için en sevdiğim kavram kanıtlama çözümlerinden birkaçını ele alacağım.
1. yöntem: MutationObserver + WebSocket
Sekme yansıtma için bir yaklaşım, bu yılın başlarında +Rafael Weinstein tarafından gösterilmişti. Tekniğinde Mutation Observer'lar ve bir WebSocket kullanılıyor.
Temel olarak, sunucunun paylaştığı sekme sayfadaki değişiklikleri izler ve bir web soketi kullanarak izleyiciye farkları gönderir. Kullanıcı sayfayı kaydırdıkça veya sayfayla etkileşimde bulundukça gözlemciler bu değişiklikleri algılar ve Rafael'in mutasyon özeti kitaplığını kullanarak görüntüleyiciye bildirir. Bu sayede performans korunur. Her kare için sayfanın tamamı gönderilmez.
Rafael'in videoda belirttiği gibi, bu yalnızca bir kavram kanıtlamadır. Yine de Mutation Observer gibi daha yeni bir platform özelliğini Websockets gibi daha eski bir özellikle birleştirmenin iyi bir yolu olduğunu düşünüyorum.
2. yöntem: HTMLDocument + ikili WebSocket'ten blob
Sıradaki yöntem, kısa süre önce aklıma geldi. Mutation Observers yaklaşımına benzer ancak özet farkları göndermek yerine tüm HTMLDocument öğesinin Blob klonunu oluşturur ve ikili websocket üzerinden gönderir. Kurulum adımları şunlardır:
- Sayfadaki tüm URL'leri mutlak olacak şekilde yeniden yazın. Bu sayede statik resim ve CSS öğelerinde bozuk bağlantılar bulunmaz.
- Sayfanın belge öğesini kopyalayın:
document.documentElement.cloneNode(true); - CSS kullanarak klonu salt okunur ve seçilemez hale getirin ve kaydırmayı engelleyin
pointer-events: 'none';user-select:'none';overflow:hidden; - Sayfanın mevcut kaydırma konumunu yakalayın ve bunları kopyaya
data-*özellikleri olarak ekleyin. - Kopyanın
.outerHTMLöğesinden birnew Blob()oluşturun.
Kod aşağıdaki gibi görünür (tam kaynakta basitleştirmeler yaptım):
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(), göreli/şemasız URL'leri mutlak URL'lere yeniden yazmak için basit normal ifadeler içerir. Bu, resimlerin, CSS'lerin, yazı tiplerinin ve komut dosyalarının bir blob URL'si bağlamında (ör. farklı bir kaynaktan) görüntülendiğinde bozulmaması için gereklidir.
Yaptığım son değişiklik, kaydırma desteği eklemek oldu. Sunucu sayfayı kaydırdığında izleyici de takip etmelidir. Bunun için mevcut scrollX ve scrollY konumlarını, kopya HTMLDocument üzerinde data-* özellikleri olarak saklıyorum. Nihai Blob oluşturulmadan önce, sayfa yüklendiğinde tetiklenen bir miktar JS eklenir:
// 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);
});
Kaydırma işlemini taklit etmek, orijinal sayfanın bir bölümünün ekran görüntüsünü aldığımız izlenimini verir. Oysa aslında her şeyi kopyalayıp yalnızca yeniden konumlandırdık. #clever
Demo
Ancak sekme paylaşımı için sekmeyi sürekli olarak yakalayıp izleyicilere göndermemiz gerekir. Bunun için akışı gösteren küçük bir Node websocket sunucusu, uygulaması ve yer işareti uygulaması yazdım. Kodla ilgilenmiyorsanız bu kısa videoda her şeyin nasıl çalıştığını görebilirsiniz:
Gelecekteki iyileştirmeler
Optimizasyonlardan biri, her karede belgenin tamamını kopyalamamaktır. Bu, kaynak israfına yol açar ve Mutation Observer örneğinin iyi yaptığı bir şeydir. Başka bir iyileştirme de urlsToAbsolute() içinde göreceli CSS arka plan resimlerini işlemek. Mevcut senaryoda
buna dikkat edilmemiş.
3. yöntem: Chrome uzantısı API + ikili WebSocket
Google I/O 2012'de, tarayıcı sekmesinin içeriğini ekran paylaşımıyla göstermenin başka bir yolunu tanıtmıştım. Ancak bu bir hile. Bu özellik için Chrome Uzantısı API'si gerekir. Özellik, saf HTML5 ile çalışmaz.
Bunun kaynağı da GitHub'da mevcut ancak özetle:
- Geçerli sekmeyi .png dataURL olarak yakalar. Chrome uzantılarında bunun için bir API vardır
chrome.tabs.captureVisibleTab(). - dataURL'yi
Blobbiçimine dönüştürün.convertDataURIToBlob()yardımcısına bakın. socket.responseType='blob'ayarını yaparak her Blob'u (kareyi) ikili websocket kullanarak görüntüleyiciye gönderin.
Örnek
Mevcut sekmenin ekran görüntüsünü PNG olarak alıp kareyi bir WebSocket üzerinden göndermek için kullanabileceğiniz kod:
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);
Gelecekteki iyileştirmeler
Bu oyunun kare hızı şaşırtıcı derecede iyi ancak daha da iyi olabilir. dataURL'yi Blob'a dönüştürme ek yükünü ortadan kaldırmak bir iyileştirme olabilir. Maalesef chrome.tabs.captureVisibleTab() bize yalnızca bir dataURL veriyor. Blob veya Typed Array döndürürse Blob'a dönüştürme işlemini kendimiz yapmak yerine doğrudan websocket üzerinden gönderebiliriz. Bunun gerçekleşmesi için lütfen crbug.com/32498 adresine yıldız ekleyin.
4. yöntem: WebRTC - Gerçek gelecek
Son olarak,
Tarayıcıda ekran paylaşımının geleceği WebRTC ile şekillenecek. 14 Ağustos 2012'de ekip, sekme içeriklerini paylaşmak için WebRTC Tab Content Capture API'sini önerdi:
Bu yöntem hazır olana kadar 1-3 yöntemlerini kullanabiliriz.
Sonuç
Yani günümüzün web teknolojisiyle tarayıcı sekmesi paylaşımı mümkün.
Ancak bu ifadeye şüpheyle yaklaşmak gerekir. Bu makaledeki teknikler her ne kadar iyi olsa da harika bir paylaşım kullanıcı deneyimi sunma konusunda yetersiz kalıyor. WebRTC Sekme İçeriği Yakalama çalışmasıyla tüm bunlar değişecek. Ancak bu gerçekleşene kadar tarayıcı eklentileri veya burada ele alınanlar gibi sınırlı çözümlerle yetinmek zorundayız.
Başka teknikleriniz var mı? Yorum yazın.