Чтобы встроить возможности совместного использования экрана в приложение для видеочата WebRTC, вы должны сначала иметь возможность захватывать контент на экране. В этом посте мы увидим, как получить доступ к функциям захвата экрана в Google Chrome из веб-приложения.
Что вам нужно
Для создания этого проекта вам понадобятся:
- "Гугл Хром"
- Текстовый редактор
- Локальный веб-сервер - мне нравится использовать servir для подобных вещей.
Совместное использование экрана
На данный момент Chrome не позволяет вам получать доступ к содержимому экрана через API `mediaDevices`. Существует черновик спецификации для метода getDisplayMedia, но ни один браузер еще не реализовал его. Совместное использование экрана в Интернете вызывает множество проблем с безопасностью для конечного пользователя, поэтому браузеры относятся к этой функции с беспокойством.
Что мы можем сделать прямо сейчас?
Что ж, в Chrome мы можем написать расширение, которое предоставит нам доступ к экрану, окнам приложений и вкладкам браузера. Расширения Chrome имеют расширенные разрешения и API-интерфейсы, которые позволяют получить доступ к ресурсам, недоступным для обычного JavaScript на странице.
Давайте напишем расширение, которое предоставит доступ к снимкам экрана, а затем покажет результаты этого на странице.
Создание расширения Chrome
Расширение, которое мы собираемся построить, очень простое. Он состоит из двух частей: файла `manifest.json`, описывающего расширение, и скрипта, который мы хотим запустить, который мы назовем` extension.js`.
Манифест расширения
Создайте новый каталог, в котором будет собираться наше приложение, каталог для самого расширения и файлы `manifest.json` и` extension.js`.
mkdir screen-capture cd screen-capture mkdir extension touch extension/manifest.json extension/extension.js
Откройте файл `manifest.json` в вашем любимом текстовом редакторе. Для начала нам нужно добавить несколько основных деталей в манифест: имя, описание, версию нашего расширения и версию требуемого формата файла манифеста, которая в данном случае равна 2.
{ "name": "Desktop Capture", "description": "Allows you to capture your desktop for use in video applications", "version": "0.1.0", "manifest_version": 2 }
Теперь нам нужно описать, как работает расширение. Добавьте в манифест следующее:
"manifest_version": 2, "background": { "scripts": ["extension.js"], "persistent": false }, "externally_connectable": { "matches": ["*://localhost/*"] }, "permissions": ["desktopCapture"] }
Это сообщает Chrome, что на самом деле делает расширение.
Во-первых, он запускает файл `extension.js` в фоновом режиме. Параметр persistant: false указывает, что мы создаем страницу событий. Это означает, что когда расширение не требуется, оно выгружается и не занимает системные ресурсы.
Наличие external_connectable означает, что это расширение может получать сообщения с веб-страницы по соответствующему URL-адресу. В данном случае это расширение разработки и будет работать только для localhost. Если вы хотите встроить это в расширение для своего сайта, вам нужно будет добавить сюда собственный код сопоставления домена.
Наконец, часть разрешений означает, что нам нужен доступ к desktopCapture API расширения Chrome. Мы увидим, как это работает, по мере написания кода для самого расширения.
Код расширения
Чтобы создать страницу события, нам нужно создать прослушиватель событий. Мы будем ждать сообщения с веб-страницы, поэтому откройте `extension.js` и начните с ввода:
chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => { });
Chrome.runtime - это API, который мы используем, чтобы реагировать на события, внешние по отношению к расширению, а также возвращать другие сведения о самом расширении. onMessageExternal запускается, когда сообщение получено извне расширения, поэтому нам нужно его прослушать. Когда вызывается наш слушатель, он получает три аргумента: сообщение, которое мы отправляем с нашей страницы, объект runtime.MessageSender и функцию, которую мы можем использовать не более одного раза для отправки сообщения обратно на страницу.
Как только это событие сработает, нам нужно использовать chrome.desktopCapture.chooseDesktopMedia`, чтобы отобразить выбор совместного использования экрана. Нам нужно передать массив источников для захвата. Это может быть« экран , окно , вкладка или звук », и мы будем передавать их с веб-страницы в отправляемом нами сообщении. Мы также передаем вкладку, отправившую сообщение, в расширение. Наконец, нам нужно передать обратный вызов, который будет вызываться, как только функция вернет streamId для потока, к которому мы запросили доступ. Чтобы мы могли использовать функцию sendResponse асинхронно, нам также необходимо вернуть true в конце функции слушателя.
chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => { const sources = message.sources; const tab = sender.tab; chrome.desktopCapture.chooseDesktopMedia(sources, tab, (streamId) => { }); return true; });
Как только мы получим ответ на обратный вызов, мы почти закончили с расширением. Все, что нам нужно сделать, это проверить, разрешил ли пользователь доступ к экрану, и отправить обратно либо неудачный ответ, либо идентификатор потока на веб-страницу с помощью sendResponse.
chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) =>; { const sources = message.sources; const tab = sender.tab; chrome.desktopCapture.chooseDesktopMedia(sources, tab, (streamId) => { if (!streamId) { sendResponse({ type: 'error', message: 'Failed to get stream ID' }); } else { sendResponse({ type: 'success', streamId: streamId }); } }); return true; });
Это все, что нам нужно написать для этой версии нашего расширения. Теперь нам нужно установить его, прежде чем мы сможем его использовать.
Установите расширение
Установить расширение, над которым вы работаете, в Chrome для тестирования - это легко и просто. Просто откройте страницу настроек расширений, набрав chrome: // extensions в адресной строке браузера.
Затем, чтобы установить расширение, вам нужно установить флажок «Режим разработчика», а затем выбрать «Загрузить распакованное расширение…». В диалоговом окне перейдите в каталог, в котором вы сохранили расширение, и выберите весь каталог.
После загрузки вы захотите записать свой идентификатор расширения. Нам это понадобится в следующей части.
Захват экрана здания
В оставшейся части этого поста мы увидим, как использовать наше новое расширение, чтобы получить доступ к захвату нашего экрана. Сейчас мы покажем это в элементе video на веб-странице. В другом посте мы рассмотрим использование потока в видеочате, чтобы мы могли удаленно делиться своим экраном.
Начните с создания каталога под названием `chrome` в каталоге вашего проекта и внутри него новой HTML-страницы с именем` index.html`. Добавьте следующую разметку:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Screen</title> </head> <body> <h1>Show my screen</h1> <video autoplay id="screen-view" width="50%"></video> <button id="get-screen">Get the screen</button> <button id="stop-screen" style="display:none">Stop the screen</button> <script> </script> </body> </html>
Это базовая HTML-страница с одним элементом «‹video›« внутри, чтобы показать наши результаты, двумя кнопками для запуска и остановки захвата экрана и блоком «‹script›«, где мы напишем остальную часть кода.
Мы начнем код со сбора элементов DOM, которые мы собираемся использовать. Мы также настраиваем объект для запроса, который мы сделаем расширению позже. Помните, что мы могли предоставить источники, из которых хотели выбирать. В этом приложении мы сможем выбрать весь экран, окно приложения или вкладку Chrome. Вам также понадобится идентификатор расширения, указанный при загрузке расширения в Chrome ранее. Добавьте это в свой блок `script`:
(() => { const EXTENSION_ID = 'YOUR_EXTENSION_ID'; const video = document.getElementById('screen-view’); const getScreen = document.getElementById('get-screen'); const stopScreen = document.getElementById('stop-screen'); const request = { sources: ['window', 'screen', 'tab'] }; let stream; })();
Теперь приступим к захвату рабочего стола. Когда мы нажимаем на кнопку, чтобы открыть экран, мы делаем запрос к расширению и получаем ответ в виде обратного вызова.
let stream; getScreen.addEventListener('click', event => { chrome.runtime.sendMessage(EXTENSION_ID, request, response => { }); }); })();
Получив ответ, мы проверяем его успешность. Если это так, мы можем взять идентификатор потока, возвращенный нам расширением, и передать его в API `mediaDevices`. Если нет, то мы регистрируем отказ в доступе.
chrome.runtime.sendMessage(EXTENSION_ID, request, response => { if (response && response.type === 'success') { navigator.mediaDevices.getUserMedia({ video: { mandatory: { chromeMediaSource: 'desktop', chromeMediaSourceId: response.streamId, } } }); } else { console.error('Could not get stream'); } });
В этом коде мы передаем параметры параметру видео для `mediaDevices.getUserMedia`. Для chromeMediaSource должно быть установлено значение «desktop», а chromeMediaSourceId - это идентификатор потока, который мы получили от расширения.
Остальной код, который нам нужно написать, ничем не отличается от обычного кода getUserMedia. Функция возвращает обещание, которое разрешается вместе с потоком, поэтому нам нужно установить поток в качестве источника для элемента «video» страницы. Мы также скроем кнопку получения экрана, покажем кнопку экрана остановки и перехватим все возможные ошибки.
navigator.mediaDevices.getUserMedia({ video: { mandatory: { chromeMediaSource: 'desktop', chromeMediaSourceId: response.streamId, } } }).then(returnedStream => { stream = returnedStream; video.src = URL.createObjectURL(stream); getScreen.style.display = "none"; stopScreen.style.display = "inline"; }).catch(err => { console.error('Could not get stream: ', err); });
Наконец, мы можем написать код для обработки остановки захвата экрана. Это будет прослушивать нажатия на кнопку, получать треки потока и останавливать их все. Он также удаляет «src» из «‹video›« и меняет местами видимые кнопки.
} else { console.error('Could not get stream'); } }); }); stopScreen.addEventListener('click', event => { stream.getTracks().forEach(track => track.stop()); video.src = ''; stopScreen.style.display = 'none'; getScreen.style.display = 'inline'; }); })();
Вот и весь код. Давай запустим это.
Захват экрана
Последнее, что нам нужно сделать, это разместить этот HTML-файл на localhost. Обычно я использую модуль npm под названием serveir. Если у вас установлены Node.js и npm, вы можете установить его с помощью
npm install servedir -g
Затем вы можете перейти с помощью командной строки в каталог, в котором вы сохранили файл, и обслужить его на localhost: 8000, введя:
serve .
Если у вас есть другой метод, который вы используете для обслуживания статических файлов на локальном хосте, вы также можете использовать его.
Откройте страницу, нажмите кнопку «Получить экран» и выберите экран, окно или вкладку, которыми хотите поделиться. Вы сняли свой экран!
Следующие шаги
Если вы не записывали весь этот код, вы также можете проверить результаты в этом репозитории GitHub.
Все, что мы здесь сделали, очень специфично для браузера Chrome, но оказывается, что вы можете получить доступ к захвату экрана и в Firefox. В идеале мы могли бы обернуть это функцией, которая без проблем работает для обоих браузеров. Следите за дальнейшими сообщениями в блоге о том, как добраться до этой стадии.
Мы реализовали минимальную версию необходимого расширения, чтобы получить доступ к снимкам экрана. Для более надежной версии ознакомьтесь с руководством по созданию снимков экрана в документации Twilio. Вы также найдете там дополнительную информацию о том, как использовать эту новую возможность с Twilio Video для демонстрации экрана во время видеозвонка.
У вас есть интересный пример использования захвата экрана в браузерах? Напишите мне свои идеи или вопросы в комментариях ниже. Вы также можете написать в Twitter по адресу @philnash или по электронной почте [email protected].
Снимок экрана в Google Chrome был первоначально размещен в блоге Twilio 12 октября 2017 г.