WebRTC (Web Real-Time Communication) предоставляет браузерам возможность устанавливать прямое соединение для передачи аудио, видео и данных между клиентами без необходимости использования промежуточного сервера. Комбинация WebRTC с Lit позволяет создавать современные, реактивные веб-компоненты для коммуникационных приложений.
Lit предоставляет удобный синтаксис для создания кастомных элементов
через классы, декораторы и реактивные свойства. WebRTC-соединение обычно
строится на объектах RTCPeerConnection,
MediaStream и RTCDataChannel. Ключевым
моментом является управление состоянием соединения и реактивным
обновлением интерфейса.
import { LitElement, html, css } from 'lit';
import { property, state } from 'lit/decorators.js';
class WebRtcComponent extends LitElement {
static styles = css`
video {
width: 100%;
max-width: 400px;
margin-bottom: 1em;
border-radius: 8px;
background: black;
}
button {
padding: 0.5em 1em;
margin-right: 0.5em;
border: none;
border-radius: 4px;
background: #0078d4;
color: white;
cursor: pointer;
}
`;
@state() localStream = null;
@state() remoteStream = null;
@state() pc = null;
async firstUpdated() {
await this.initMedia();
this.setupPeerConnection();
}
async initMedia() {
try {
this.localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
this.requestUpdate();
} catch (err) {
console.error('Ошибка доступа к медиапотоку', err);
}
}
setupPeerConnection() {
this.pc = new RTCPeerConnection();
this.localStream.getTracks().forEach(track => this.pc.addTrack(track, this.localStream));
this.pc.ontr ack = (event) => {
this.remoteStream = event.streams[0];
this.requestUpdate();
};
}
async createOffer() {
const offer = await this.pc.createOffer();
await this.pc.setLocalDescription(offer);
console.log('Offer SDP:', offer.sdp);
}
async receiveAnswer(answer) {
await this.pc.setRemoteDescription(new RTCSessionDescription(answer));
}
render() {
return html`
<video autoplay ?muted=${true} .srcObject=${this.localStream}></video>
<video autoplay .srcObject=${this.remoteStream}></video>
<div>
<button @click=${this.createOffer}>Создать предложение</button>
</div>
`;
}
}
customElements.define('web-rtc-component', WebRtcComponent);
Использование декоратора @state() позволяет
автоматически обновлять интерфейс при изменении потоков или состояния
соединения. В контексте WebRTC это критично, так как:
localStream обновляется после получения доступа к
камере и микрофону;remoteStream изменяется при поступлении удаленного
потока;pc содержит текущую конфигурацию PeerConnection,
включая каналы данных и ICE-серверы.Реактивность Lit гарантирует, что элементы <video>
обновят свой srcObject при изменении потоков без
необходимости прямого манипулирования DOM.
Для успешного установления соединения через NAT и firewall необходим ICE-сервер. Обычно используется публичный STUN-сервер:
const pcConfig = {
iceServers: [
{ urls: 'stun:stun.l.google.com:19302' }
]
};
this.pc = new RTCPeerConnection(pcConfig);
Сигналинг (обмен SDP и ICE-кандидатами) не входит в WebRTC по умолчанию и реализуется отдельно через WebSocket или HTTP API. В Lit можно создавать реактивные методы для отправки и получения этих данных:
async handleRemoteOffer(offer) {
await this.pc.setRemoteDescription(new RTCSessionDescription(offer));
const answer = await this.pc.createAnswer();
await this.pc.setLocalDescription(answer);
sendAnswerToRemote(answer);
}
RTCDataChannel позволяет обмениваться текстовыми и
бинарными данными напрямую между браузерами. Для создания канала:
this.dataChannel = this.pc.createDataChannel('chat');
this.dataChannel.onmess age = (event) => {
console.log('Сообщение от партнёра:', event.data);
};
События можно привязывать к реактивным свойствам Lit для динамического отображения сообщений:
@state() messages = [];
this.dataChannel.onmess age = (event) => {
this.messages = [...this.messages, event.data];
};
Динамическое подключение нескольких удалённых
участников Создание массива PeerConnection и управление
отдельными <video> элементами через цикл
map():
this.peers.map(peer => html`<video autoplay .srcObject=${peer.stream}></video>`)Управление состояниями соединения Свойство
connectionState отслеживает iceConnectionState
и позволяет визуализировать статусы:
this.pc.oniceconnectionstatecha nge = () => {
console.log('Состояние ICE:', this.pc.iceConnectionState);
};Интеграция с Lit-реактивными формами
Использование @property для связывания с UI-компонентами,
например, выбора камеры или микрофона, обеспечивает мгновенное
обновление потоков при смене устройств.
requestUpdate() только после получения потоков, чтобы
избежать лишних перерасчетов DOM.track.stop().Lit и WebRTC вместе позволяют создавать полностью реактивные, модульные компоненты для аудио- и видеокоммуникаций, с управлением состоянием потоков, динамическим отображением видеопотоков и поддержкой обмена данными в реальном времени. Интеграция ICE-серверов, DataChannel и сигналинга обеспечивает полноценную работу P2P-приложений в браузере без необходимости сторонних плагинов.