Содержание статьи:
1.Состав плагина.
2.Использование модулей сторонних разработчиков.
3.Инструментарий разработчика.
4.Документация для разработки.
5.Цель разработки плагина.
6.Почему плагин бесплатный?
7.Отдельные моменты в разработке плагина.
8.Заключение.
1.Состав плагина.
Содержимое папки плагина после установки на сайт:
папка chart – в ней находится модуль создания диаграмм потоковых данных:
– smoothie.js от сторонних разработчиков (Smoothie Charts)
папка css – в ней находятся файлы CSS:
– dump.css определяет стиль внешнего файла-отчета, созданного нажатием кнопки Report плагина.
– webrtc2-dark.css создает темный стиль графического интерфейса плагина.
– webrtc2-light.css создает светлый стиль графического интерфейса плагина.
– webrtc2-backend.css – определяет стиль раздела “Users for Video chat” profile зарегистрированного пользователя сайта.
папка doc – в ней находится документация пользователя на русском и английском языках плагина.
папка images – в ней находятся картинки для графического интерфейса плагина.
папка includes – в ней находятся 6 файлов PHP:
– webrtc2-sign.php – выполняет функции сигнального сервера по технологии WebRTC.
– webrtc2-sse.php – определяет каждые 2-3сек. статус пользователей (on/off) по технологии SSE.
– webrtc2-profile-tbls.php – создается список контактов в профайле пользователя зарегистрированных пользователей сайта, с которыми данный пользователь хочет иметь видео связь (адресная книга).
– webrtc2-template.php – используется клиентским приложением (download: WebRTC2_desktop). Инструкция по использованию приложения WebRTC2_desktop: [ru]
Это недавно разработанное приложение видео-связи на java для десктопов на windows.
– webrtc2-create-tables.php – создает 3 пользовательские таблицы (webrtc2_countries, webrtc2_call_stat, webrtc2_stun_servers) в БД сайта.
– webrtc2-ip-info.php – берет на вход IP пользователя и возвращает информацию об этом IP.
папка js – в ней находятся 8 файлов JS:
– webrtc2-variables.js – перечень глобальный переменных для удобства программиста – собранные в один файл.
– webrtc2-init.js – инициирование объектов WebRTC с их параметрами из настроек плагина.
– webrtc2-interface.js – формирует графический интерфейс и управление им.
– webrtc2-service.js – дополнительные сервисные функции плагина.
– webrtc2-sign.js – вызов функций сигнального сервера на стороне клиента.
– webrtc2-modify-sdp.js – позволяет на лету изменять пропускную способность MediaBitrate в случае плохой видео-связи.
– webrtc2-profile.js – управляет списком контактов в профайле пользователя.
– webrtc2-meter-wrklt.js / webrtc2-meter-prcsr.js – обработка аудио потока (новое требование Google с 2020 года вместо устаревшего JavaScriptNode). Вызывается из модуля webrtc2-service.js
папка parser – в ней находится модуль определения браузера пользователя:
– detect.js от сторонних разработчиков (Darcy Clarke, Tobie Langel).
папка sound – в ней находятся 3 звуковых файла mp3:
– receive_call.mp3 – оповещение о вызове пользователя на видео-связь.
– receive_file – оповещение пользователя о приеме файла.
– receive_msg – оповещение пользователя о приеме сообщения.
файл class-webrtc2-core.php – формирует backend интерфейс плагина в административной панели управления сайтом.
файл class-webrtc2-shortcode.php – формирует frontend интерфейс плагина в браузере пользователя.
файл class-webrtc2-stun-client.php – проверяет работоспособность Stun серверов по списку.
файл class-webrtc2-tbl-stat.php – создает таблицу статистики видеозвонков в админке сайта.
файл class-webrtc2-tbl-srv.php – создает таблицу Stun серверов в админке сайта.
файл readme.txt – описание плагина по стандарту WordPress.
файл uninstall.php – удаление плагина по стандарту WordPress.
файл wp-webrtc2.php – основной файл плагина (регистрация, подключения CSS, JS).
2.Использование модулей сторонних разработчиков.
1. Модуль detect.js формирует информацию о браузере пользователя, которая передается плагином в шапку файла отчета, сформированного после нажатия кнопки Report плагина:
Использование модуля detect.js в плагине (модуль: webrtc2-service.js):
/** * Save to file.html selected content of win_messages. */ function webrtc2_msg_report() { var html = document.createElement("html"); var link = document.createElement("link"); var head = document.createElement("head"); var body = document.createElement("body"); var foot = document.createElement("foot"); var copyright = document.createElement("label"); var img_flag = document.createElement("img"); var title = document.createElement("h1"); var info = document.createElement("div"); var userName = document.createElement("label"); var browser = document.createElement("label"); var device = document.createElement("label"); var os = document.createElement("label"); var date_time = document.createElement("label"); var content = document.createElement("div"); var a = document.createElement("a"); var bl; //browser.family Имя браузера //browser.name Имя браузера и его версия //browser.version Полная версия браузер //browser.major Основной номер версии браузера //browser.minor Дополнительный номер версии браузера //browser.patch Номер патча браузера //device.family Имя устройства //device.name Имя устройства и версия //device.version Полная версия устройства //device.major Основной номер версии устройства //device.major Дополнительный номер версии устройства //device.patch Номер патча устройства //device.type Тип устройства (например, "Desktop" или "Mobile") //device.manufacturer Производитель устройства //os.family Название операционной системы //os.name Полне имя операционной системы //os.version Полная версия операционной системы //os.major Основной номер версии операционной системы //os.minor Дополнительный номер версии операционной системы //os.patch Номер патча операционной системы var agentInfo = detect.parse(navigator.userAgent); userName.innerHTML = "User name: " + webrtc2_hostId; userName.className = "info"; browser.innerHTML = "Browser: " + agentInfo.browser.family + " version " + agentInfo.browser.major + "." + agentInfo.browser.minor + "." + agentInfo.browser.patch; browser.className = "info"; device.innerHTML = "Device: " + agentInfo.device.type; device.className = "info"; os.innerHTML = "OS: " + agentInfo.os.name; os.className = "info"; date_time.innerHTML = "Date: " + new Date().toLocaleString(); date_time.className = "info"; if ("visible" == document.getElementById("fld_dump").style.visibility) { title.innerHTML = "WP-WebRTC2 protocol of dump"; } if ("visible" == document.getElementById("fld_chat").style.visibility) { title.innerHTML = "WP-WebRTC2 protocol of chat"; } img_flag.src = webrtc2_url + "doc/img/BY.gif"; copyright.innerHTML = "Belarus, Minsk © 2019. Developer: Oleg Klenitsky"; info.appendChild(userName); info.appendChild(browser); info.appendChild(device); info.appendChild(os); info.appendChild(date_time); link.rel = "stylesheet"; link.href = webrtc2_url + "css/dump.css"; link.type = "text/css"; head.appendChild(link); html.appendChild(head); html.appendChild(title); body.appendChild(info); if ("visible" == document.getElementById("fld_dump").style.visibility) { var fld_dump = document.createElement("div"); fld_dump.innerHTML = document.getElementById("fld_dump").innerHTML; fld_dump.id = "fld_dump_copy"; fld_dump.style = "margin-top: 5px;"; body.appendChild(fld_dump); } if ("visible" == document.getElementById("fld_chat").style.visibility) { var fld_chat = document.createElement("div"); fld_chat.innerHTML = document.getElementById("fld_chat").innerHTML; fld_chat.id = "fld_chat_copy"; fld_chat.style = "margin-top: 5px;"; body.appendChild(fld_chat); } html.appendChild(body); foot.appendChild(img_flag); foot.appendChild(copyright); html.appendChild(foot); html = [html.outerHTML]; bl = new Blob(html, {type: "text/html"}); a.href = URL.createObjectURL(bl); if ("visible" == document.getElementById("fld_dump").style.visibility) { a.download = "dump.html"; } if ("visible" == document.getElementById("fld_chat").style.visibility) { a.download = "chat.html"; } html = null; a.hidden = true; document.body.appendChild(a); a.click(); }
2. Модуль smoothie.js создания диаграмм потоковых данных формирует графики статистики WebRTC:
– кнопка Graph1: report.type == “inbound-rtp” && report.mediaType == “video”
– кнопка Graph2 report.type == “outbound-rtp” && report.mediaType == “video”
– кнопка Graph3: report.type == “transport”
– кнопка Graph4: report.type == “remote-inbound-rtp” && report.kind == “video”
Использование модуля smoothie.js в плагине (модуль: webrtc2-service.js) на примере одного графика:
/** * Display graph1. */ function webrtc2_graph1() { document.getElementById("graph_local").style.visibility = "visible"; document.getElementById("graph_remote").style.visibility = "visible"; // Нажата document.getElementById("btn_graph1").style.boxShadow = "inset -2px -2px 3px rgba(200, 200, 200, .6),inset 2px 2px 3px rgba(0, 0, 0, .6)"; // Отжата document.getElementById("btn_graph3").style.boxShadow = "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)"; document.getElementById("btn_graph2").style.boxShadow = "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)"; document.getElementById("btn_graph4").style.boxShadow = "inset 2px 2px 3px rgba(255, 255, 255, .6),inset -2px -2px 3px rgba(0, 0, 0, .6)"; graph = "graph1"; var smoothie = new SmoothieChart({grid:{sharpLines:true},tooltip:true,timestampFormatter:SmoothieChart.timeFormatter}); // Legends document.getElementById("local_legend1").innerHTML = "KbytesReceived/sec"; document.getElementById("local_legend2").innerHTML = "PacketsReceived/sec"; // Data var line1 = new TimeSeries(); var line2 = new TimeSeries(); // Add to SmoothieChart smoothie.addTimeSeries(line1, { strokeStyle:'rgb(0, 255, 0)', fillStyle:'rgba(0, 255, 0, 0.4)', lineWidth:2 }); smoothie.addTimeSeries(line2, { strokeStyle:'rgb(255, 0, 255)', fillStyle:'rgba(255, 0, 255, 0.3)', lineWidth:2 }); smoothie.streamTo(document.getElementById("canvas_local"), 500); // Add value to each line every second var graph1_interval_local = setInterval(function() { var pliCount = (!!inbound_rtp_video.pliCount) ? inbound_rtp_video.pliCount : "0"; var packetsLost = (!!inbound_rtp_video.packetsLost) ? inbound_rtp_video.packetsLost : "0"; document.getElementById("graph_title_local").innerHTML = "<b>Local: </b> [inbound-rtp]:video packetsLost: " + packetsLost + "; pliCount: " + pliCount + ";"; line1.append(inbound_rtp_video.timestamp, inbound_rtp_video.bytesReceivedPerSec); line2.append(inbound_rtp_video.timestamp, inbound_rtp_video.packetsReceivedPerSec); if (graph !== "graph1") { document.getElementById("graph_title_local").innerHTML = "<b>Local: Wait...</b>"; clearInterval(graph1_interval_local); } }, 1000); document.getElementById("graph_title_remote").innerHTML = "<b>Remote:</b> Wait..."; webrtc2_graph1_remote(); }
3.Инструментарий разработчика.
В процессе разработки плагина постоянно использовалась отладка WebRTC, встроенная в браузеры:
1. Для Chrome: chrome://webrtc-internals/
2. Для FireFox: about:webrtc
3. Для Opera: opera://webrtc-internals
4. Для Edge: edge://webrtc-internals/
5. Для Yandex: browser://webrtc-internals/
4.Документация для разработки.
Существует много интересной информации ознакомительного характера о технологии WebRTC. Ее можно без особого труда найти на просторах интернета. Поэтому я лишь приведу перечень документации, которая была полезна в конкретных случаях реализации функционала в плагине:
1. WebRTC 1.0
2. Getting Started WebRTC
3. Anatomy of a WebRTC SDP.
4. WebRTC-stats.
На мой личный взгляд, будущее WebRTC 1.0 – туманно, неоднозначно. С одной стороны – технология WebRTC – великолепна. Даже со своими текущими багами. Но ведь это проблемы роста. Обычное дело. Но вот что смущает: зреющий новый WebRTC 2.0 может стать могильщиком, а не продолжателем WebRTC 1.0. Звериный оскал капитализма не предполагает преемственности поколений, технологий. Это скорее похоже на тараканьи бега по вертикали. Кто первый, любой ценой. И если продолжать без аллегорий, то может однажды придут веселые дяди из Microsoft (Edge), Google, Hookflash и скажут: все что вы все сделали на основе WebRTC 1.0 – выбросить, теперь все будете работать по WebRTC 2.0.
Откуда такое настроение? Читайте сами, думайте сами:
1. OBJECT-RTC – IS IT WRTC 2.0?
2. What Developers Should Know About ORTC
5.Цель разработки плагина.
Когда-то, работая в одном учреждении, мы использовали Skype для организации видео-мостов между странами на высоком государственном уровне. К сожалению были обрывы связи, замедление трансляции. Да и смущал тот факт, что некоторые реплики, как впрочем и вся трансляция писалась на английских серверах без нашего на то решения. Потому, что Skype.
С тех пор прошло много времени. Перестройка, дружба с Западным “цивилизованным миром” в засос, развал СССР – в прошлом. Растет внучка 12 лет. И захотелось делать с ней уроки не по телефону, а по видео-связи. Вот так и появился WP-WebRTC2.
6.Почему плагин бесплатный?
Мне нравится философия WordPress: бесплатность и доступность для ВСЕХ к современным технологиям. Это не реклама. Это и мой жизненный принцип. Тут, что называется: нашли друг друга два одиночества. 🙂
7.Отдельные моменты в разработке плагина.
На мой взгляд интересным моментом в этом плагине видео-связи является вопрос:
1. как реализован принцип сигнализации в плагине?
Весь остальной функционал имеет пусть и нужное, но все-таки второстепенное значение. Итак:
Ниже представлен код основной функции сигнализации на стороне клиента (webrtc2-sign.js).
Сама по себе эта функция webrtc2_chat_init() создает соединение путем отправки по мере готовности определенной информации (sdp, ice-candidates) и приема соответствующей информации (sdp, ice-candidates) в ожидающем цикле setInterval().
/** * Peer to Peer video-chat. */ function webrtc2_chat_init() { let webrtc2_guestId = sessionStorage.getItem("webrtc2_guestId"); let timer_receive_answer; let timer_receive_ice; if ( "true" == sessionStorage.getItem("webrtc2_initiator") ) { // Init data channel for receive/send of messages and files. webrtc2_dataChannel = webrtc2_pc.createDataChannel("dataChannel"); webrtc2_dataChannel.binaryType = "arraybuffer"; webrtc2_datachannel_msg(); // Init data channel for statistics data of guestId. webrtc2_statChannel = webrtc2_pc.createDataChannel("statChannel"); webrtc2_statChannel.binaryType = "arraybuffer"; webrtc2_datachannel_stat(); webrtc2_pc.createOffer(webrtc2_offerOptions) // Modifying the SDP to check ice-ufrag, ice-pwd. .then(offer => { offer.sdp = webrtc2_chk_ufrag_pwd(webrtc2_hostId, "Offer", offer.sdp); return offer; }) .then(offer => { webrtc2_pc.setLocalDescription(offer); return offer; }) // modify the SDP after calling setLocalDescription. .then(offer => { offer.sdp = webrtc2_setMediaBitrates(webrtc2_hostId, "Offer", offer.sdp); return offer; }) .then(offer => { webrtc2_log_sdp(webrtc2_hostId, " -> Send [Offer] to " + webrtc2_guestId + ":", offer.sdp); webrtc2_send_sdp(JSON.stringify(offer), "offer"); }) // Receive answer sdp of webrtc2_guestId from server. .then(timer_receive_answer = setInterval(() => { webrtc2_receive_sdp("answer").then(answer => { if (answer) { webrtc2_log_sdp(webrtc2_hostId, " -> Receive [Answer] from " + webrtc2_guestId + ":", answer.sdp); webrtc2_pc.setRemoteDescription(answer); clearInterval(timer_receive_answer); } }) }, 10000)) // Receive ice-candidates of webrtc2_guestId from server. .then(timer_receive_ice = setInterval(() => { webrtc2_receive_ice().then(iceCandidates => { if (iceCandidates) { webrtc2_ice_remote(iceCandidates); for (let iceCandidate of iceCandidates) { webrtc2_pc.addIceCandidate(iceCandidate).catch(err => console.log(err)); } clearInterval(timer_receive_ice); } }) }, 10000)); }else{ // Receive offer sdp of webrtc2_guestId from server. let timer_receive_offer = setInterval(function () { webrtc2_receive_sdp("offer").then(offer => { if (offer) { webrtc2_log_sdp(webrtc2_hostId, " -> Receive [Offer] from " + webrtc2_guestId + " :", offer.sdp); webrtc2_pc.setRemoteDescription(offer).then(() => webrtc2_pc.createAnswer()) .then((answer) => { // Modifying the SDP to check ice-ufrag, ice-pwd. answer.sdp = webrtc2_chk_ufrag_pwd(webrtc2_hostId, "Answer", answer.sdp); return answer; }) .then(answer => { webrtc2_pc.setLocalDescription(answer); return answer; }) // modify the SDP after calling setLocalDescription. .then(answer => { answer.sdp = webrtc2_setMediaBitrates(webrtc2_hostId, "Answer", answer.sdp); return answer; }) .then(answer => { webrtc2_log_sdp(webrtc2_hostId, " -> Send [Answer] to " + webrtc2_guestId + ":", answer.sdp); webrtc2_send_sdp(JSON.stringify(answer), "answer"); }) // Receive ice-candidates of webrtc2_guestId from server. .then(timer_receive_ice = setInterval(() => { webrtc2_receive_ice().then(iceCandidates => { if (iceCandidates) { webrtc2_ice_remote(iceCandidates); for (let iceCandidate of iceCandidates) { webrtc2_pc.addIceCandidate(iceCandidate).catch(err => console.log(err)); } clearInterval(timer_receive_ice); } }) }, 10000)); clearInterval(timer_receive_offer); } }) }, 10000); } }
8.Заключение.
Предварительные испытания показали, что плагин WP-WebRTC2 работает удовлетворительно. Практическое использование покажет истинное состояние дел в работоспособности плагина.