গত কয়েক বছরে, আমি কয়েকটি আলাদা কোম্পানিকে শুধুমাত্র ব্রাউজার প্রযুক্তি ব্যবহার করে স্ক্রিন শেয়ারিংয়ের মতো কার্যকারিতা অর্জন করতে সাহায্য করেছি। আমার অভিজ্ঞতা থেকে বলতে পারি, শুধুমাত্র ওয়েব প্ল্যাটফর্ম প্রযুক্তিতে (অর্থাৎ, কোনও প্লাগ-ইন ছাড়া) VNC প্রয়োগ করা একটি কঠিন সমস্যা। অনেক কিছু বিবেচনা করতে হবে এবং অনেক চ্যালেঞ্জ অতিক্রম করতে হবে। মাউস পয়েন্টারের পজিশন রিলে করা, কিস্ট্রোক ফরওয়ার্ড করা এবং ৬০fps-এ সম্পূর্ণ ২৪-বিট কালার রিপেন্ট করা হল কয়েকটি সমস্যা।
ট্যাবের কন্টেন্ট ক্যাপচার করা হচ্ছে
আমরা যদি স্ক্রিন শেয়ার করার চিরাচরিত জটিলতাগুলি সরিয়ে দিয়ে ব্রাউজার ট্যাবের কন্টেন্ট শেয়ার করার দিকে মনোযোগ দিই, তাহলে সমস্যাটি অনেক সহজ হয়ে যায়: ক) বর্তমান অবস্থায় দৃশ্যমান ট্যাব ক্যাপচার করা এবং খ) সেই "ফ্রেম" ওয়্যারের মাধ্যমে পাঠানো। মূলত, আমাদের DOM-এর স্ন্যাপশট নেওয়ার এবং তা শেয়ার করার একটি উপায় প্রয়োজন।
শেয়ার করার অংশটি সহজ। Websockets বিভিন্ন ফর্ম্যাটে (স্ট্রিং, JSON, বাইনারি) ডেটা পাঠাতে খুবই সক্ষম। স্ন্যাপশট নেওয়ার অংশটি অনেক বেশি কঠিন সমস্যা। html2canvas-এর মতো প্রোজেক্ট ব্রাউজারের রেন্ডারিং ইঞ্জিনকে জাভাস্ক্রিপ্টে আবার প্রয়োগ করে HTML স্ক্রিনক্যাপচারিংয়ের সমস্যা সমাধান করেছে! আরেকটি উদাহরণ হল Google Feedback, যদিও এটি ওপেন-সোর্স নয়। এই ধরনের প্রোজেক্ট খুব দারুণ, তবে এগুলি ভীষণ ধীর গতিতে চলে। আপনি ১fps থ্রুপুট পেলেই নিজেকে ভাগ্যবান মনে করবেন, কাঙ্খিত ৬০fps তো অনেক দূরের কথা।
এই নিবন্ধে ট্যাব "স্ক্রিন শেয়ার" করার জন্য আমার পছন্দের কয়েকটি এই আইডিয়াটি বাস্তবে ব্যবহার করা যাবে সংক্রান্ত সমাধান নিয়ে আলোচনা করা হয়েছে।
পদ্ধতি ১: মিউটেশন অবজারভার + WebSocket
ট্যাব মিরর করার একটি পদ্ধতি এই বছরের শুরুতে +Rafael Weinstein দেখিয়েছেন। তার টেকনিক মিউটেশন অবজারভার ও WebSocket ব্যবহার করে।
মূলত, উপস্থাপক যে ট্যাব শেয়ার করছেন সেটি পৃষ্ঠায় হওয়া পরিবর্তন লক্ষ্য করে এবং ওয়েব সকেট ব্যবহার করে দর্শকের কাছে ডিফারেন্স পাঠায়। ব্যবহারকারী স্ক্রল করলে বা পৃষ্ঠার সাথে ইন্টার্যাক্ট করলে, অবজারভার এইসব পরিবর্তন লক্ষ্য করে এবং Rafael-এর মিউটেশন সামারি লাইব্রেরি ব্যবহার করে ভিউয়ারকে রিপোর্ট করে। এটি পারফর্ম্যান্স বজায় রাখে। প্রতিটি ফ্রেমের জন্য সম্পূর্ণ পৃষ্ঠা পাঠানো হয় না।
ভিডিওতে রাফায়েল যেমন উল্লেখ করেছেন, এটি এখন বাস্তবে ব্যবহার করা যাবে। তবুও, আমি মনে করি যে মিউটেশন অবজার্ভারের মতো নতুন প্ল্যাটফর্ম ফিচারের সাথে ওয়েবসকেটের মতো পুরনো ফিচারকে একত্রিত করার এটি একটি চমৎকার উপায়।
পদ্ধতি ২: HTMLDocument + Binary WebSocket থেকে ব্লব
পরবর্তী পদ্ধতিটি আমি সম্প্রতি আবিষ্কার করেছি। এটি মিউটেশন অবজারভার অ্যাপ্রোচের মতোই, তবে সারসংক্ষেপের পার্থক্য পাঠানোর পরিবর্তে, এটি সম্পূর্ণ HTMLDocument-এর একটি ব্লব ক্লোন তৈরি করে এবং এটি একটি বাইনারি ওয়েবসকেটে পাঠায়। এখানে ধাপে ধাপে সেট-আপ করার পদ্ধতি দেওয়া হল:
- পৃষ্ঠার সব URL অ্যাবসোলিউট হিসেবে আবার লিখুন। এটি স্ট্যাটিক ছবি ও CSS অ্যাসেটে বিচ্ছিন্ন লিঙ্ক থাকা আটকায়।
- পৃষ্ঠার ডকুমেন্ট এলিমেন্ট ক্লোন করুন:
document.documentElement.cloneNode(true); - CSS
pointer-events: 'none';user-select:'none';overflow:hidden;ব্যবহার করে ক্লোনটি শুধু পাঠযোগ্য, বেছে নেওয়া যায় না এমন করুন এবং স্ক্রল করা আটকান - পৃষ্ঠার বর্তমান স্ক্রল পজিশন ক্যাপচার করুন এবং ডুপ্লিকেটে
data-*অ্যাট্রিবিউট হিসেবে যোগ করুন। - ডুপ্লিকেটের
.outerHTMLথেকে একটিnew Blob()তৈরি করুন।
কোডটি দেখতে অনেকটা এইরকম (আমি সম্পূর্ণ সোর্স থেকে এটিকে সরল করেছি):
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-এর (যেমন, অন্য কোনও উৎস থেকে) প্রসঙ্গে দেখার সময় ভেঙে না যায়।
আমি শেষ যে পরিবর্তনটি করেছি তা হল স্ক্রল করার সুবিধা যোগ করা। উপস্থাপক পৃষ্ঠা স্ক্রল করলে, দর্শককে তা অনুসরণ করতে হবে। এটি করার জন্য, আমি ডুপ্লিকেট HTMLDocument-এ data-* অ্যাট্রিবিউট হিসেবে বর্তমান scrollX ও scrollY পজিশন স্ট্যাশ করি। চূড়ান্ত ব্লব তৈরি করার আগে, কিছুটা JS ইনজেক্ট করা হয় যা পৃষ্ঠা লোড হওয়ার সময় ফায়ার করে:
// 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 সার্ভার, অ্যাপ ও বুকমার্কলেট লিখেছি যা ফ্লো দেখায়। কোড সম্পর্কে আপনার আগ্রহ না থাকলে, এখানে একটি ছোট ভিডিও দেওয়া হল যেখানে কোডটি কীভাবে কাজ করে তা দেখানো হয়েছে:
ভবিষ্যতে আরও উন্নত করা
একটি অপ্টিমাইজেশন হল প্রতিটি ফ্রেমে সম্পূর্ণ ডকুমেন্ট ডুপ্লিকেট না করা। এটি সময় ও রিসোর্স নষ্ট করে এবং Mutation Observer-এর উদাহরণ এই কাজটি ভালভাবে করে। অন্য একটি উন্নতি হল
urlsToAbsolute()-এ রিলেটিভ CSS ব্যাকগ্রাউন্ড ছবি ম্যানেজ করা। বর্তমান স্ক্রিপ্ট
এই বিষয়টি বিবেচনা করে না।
পদ্ধতি ৩: Chrome এক্সটেনশন API + বাইনারি WebSocket
Google I/O 2012-এ, আমি ব্রাউজার ট্যাবের কন্টেন্ট স্ক্রিনশেয়ার করার জন্য আরেকটি পদ্ধতি দেখিয়েছিলাম। তবে, এটি একটি প্রতারণা। এটির জন্য Chrome এক্সটেনশন API প্রয়োজন: শুধু HTML5 ম্যাজিক নয়।
এটির সোর্স Github-এও আছে, তবে সারমর্ম হল:
- বর্তমান ট্যাবটি .png dataURL হিসেবে ক্যাপচার করুন। Chrome এক্সটেনশনে এর জন্য একটি API আছে
chrome.tabs.captureVisibleTab()। - dataURL-কে
Blob-এ কনভার্ট করুন।convertDataURIToBlob()হেল্পার দেখুন। socket.responseType='blob'সেট করে বাইনারি ওয়েব সকেট ব্যবহার করে দর্শককে প্রতিটি ব্লব (ফ্রেম) পাঠান।
উদাহরণ
বর্তমান ট্যাবের স্ক্রিনশট png হিসেবে নিতে এবং ফ্রেমটি একটি ওয়েবসকেটের মাধ্যমে পাঠাতে এখানে কোড দেওয়া হল:
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);
ভবিষ্যতে আরও উন্নত করা
এটির ফ্রেমরেট আশ্চর্যজনকভাবে ভালো, তবে এটি আরও ভালো হতে পারে। একটি উন্নতি হল, dataURL-কে Blob-এ কনভার্ট করার ওভারহেড সরিয়ে দেওয়া। তবে, chrome.tabs.captureVisibleTab() আমাদের শুধু dataURL দেয়। এটি যদি Blob বা Typed Array রিটার্ন করে, তাহলে আমরা Blob-এ কনভার্ট করার পরিবর্তে সরাসরি Websocket-এর মাধ্যমে পাঠাতে পারি। এটি করতে crbug.com/32498-এ স্টার দিন!
পদ্ধতি ৪: WebRTC - আসল ভবিষ্যৎ
শেষ হলেও, কম গুরুত্বপূর্ণ নয়!
ব্রাউজারে স্ক্রিন শেয়ারিংয়ের ভবিষ্যৎ WebRTC-এর মাধ্যমে বাস্তবায়িত হবে। ১৪ আগস্ট, ২০১২-এ, টিম ট্যাবের কন্টেন্ট শেয়ার করার জন্য একটি WebRTC ট্যাব কন্টেন্ট ক্যাপচার API প্রস্তাব করে:
এটি রেডি না হওয়া পর্যন্ত আমাদের কাছে ১-৩ পদ্ধতিই আছে।
উপসংহার
তাহলে, আজকের ওয়েব প্রযুক্তির সাহায্যে ব্রাউজার ট্যাব শেয়ার করা সম্ভব!
কিন্তু…সেই বক্তব্যকে পুরোপুরি বিশ্বাস করা উচিত নয়। এই নিবন্ধে উল্লেখ করা কৌশলগুলি পরিষ্কার হলেও, কোনও না কোনওভাবে একটি দুর্দান্ত শেয়ারিং UX-এর থেকে কম পড়ে। WebRTC ট্যাব কন্টেন্ট ক্যাপচার করার প্রচেষ্টার মাধ্যমে এইসব কিছু পরিবর্তন হবে, তবে এটি বাস্তবে পরিণত না হওয়া পর্যন্ত, আমাদের কাছে ব্রাউজার প্লাগ-ইন বা এখানে কভার করা সীমিত সমাধান ছাড়া আর কোনও উপায় নেই।
আরও কৌশল আছে? কমেন্ট পোস্ট করুন!