Содержание статьи:
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 работает удовлетворительно. Практическое использование покажет истинное состояние дел в работоспособности плагина.
