Пример обмена ICE и SDP без использования вебсокетов для WebRTC

Большинство примеров в интернете для WebRTC используют socket.io при обмене ключами создания децентрализованного канала связи между участниками. Это не очень подходит для боевых проектов из-за потенциальной дыры в безопасности и невозможности масштабирования, логгирования. Подскажите, нет ли готовых примеров на основе Ajax?


Ответы (1 шт):

Автор решения: Трипольский Пётр

В результате длительного поиска оказалось проще написать самостоятельно

Исходный код бекенда опубликован в этом репозитории

<span></span>
<div id="root"></div>
<script src="./peer-to-peer.js"></script>
<script>
    (async function(global) {

        const root = document.querySelector('#root');
        const span = document.querySelector('span');
        const peerHub = new Map();

        global.peerHub = peerHub;

        const sleep = (timeout = 1000) => new Promise((res) => setTimeout(() => res(), timeout));

        const createVideo = (stream) => {
            const video = document.createElement('video');
            video.srcObject = stream;
            root.appendChild(video);
            video.play();
        };

        const request = async (url = '', params = {}) => {
            const builder = new URL(url, location.origin);
            Object.entries(params).forEach(([k, v]) => builder.searchParams.set(k, v));
            const data = await fetch(builder.toString());
            const json = await data.json();
            return json;
        };

        const currentStream = await navigator.mediaDevices.getUserMedia({
            video: { 
                facingMode: 'user',
                width: {
                    min: 0,
                    max: window.screen.width,
                },
                height: {
                    min: 0,
                    max: window.screen.height,
                },
            },
        });

        const currentUserId = await request('api/join-room');
        span.innerText = `Joined as user_${currentUserId}`;
        createVideo(currentStream);

        const createConnection = (toUserId = -1, initiator = false) => {
            const p2p = new PeerToPeer({
                mediaStream: currentStream,
                fromUserId: currentUserId,
                initiator,
                toUserId,
            });
            p2p.on('stream', ({ stream }) => createVideo(stream));
            p2p.on('sdp', ({ sdp, toUserId }) => {
                console.log(`Outgoing sdp ${toUserId} ${Date.now()}`, { sdp });
                request('api/create-sdp', {
                    currentUserId,
                    toUserId,
                    sdp,
                });
            });
            p2p.on('ice', ({ ice, toUserId }) => {
                console.log(`Outgoing ice ${toUserId} ${Date.now()}`, { ice });
                request('api/create-ice', {
                    currentUserId,
                    toUserId,
                    ice,
                });
            });
            peerHub.set(toUserId, p2p);
            return p2p;
        };

        const userList = await request('api/user-list');
        const targetList = userList.filter((id) => id !== currentUserId);
        targetList.forEach((toUserId) => createConnection(toUserId, true));

        const browseForIncomingSdp = async () => {
            const sdpList = await request('api/read-sdp', {
                currentUserId,
            });
            console.log({sdpList});
            await Promise.all(sdpList.map(async ({
                fromUserId,
                sdp,
            }) => {
                await request('api/mark-sdp', {
                    currentUserId,
                    fromUserId,
                });
                if (peerHub.has(fromUserId)) {
                    console.log(`Incoming answer sdp ${fromUserId} ${Date.now()}`, { sdp });
                    const p2p = peerHub.get(fromUserId);
                    p2p.setRemoteSdp(sdp);
                } else {
                    console.log(`Incoming offer sdp ${fromUserId} ${Date.now()}`, { sdp });
                    const p2p = createConnection(fromUserId, false);
                    p2p.setRemoteSdp(sdp);
                }
            }));
        };

        const browseForIncomingIce = async () => {
            const iceList = await request('api/read-ice', {
                currentUserId
            });
            console.log({iceList});
            await Promise.all(iceList.map(async ({
                fromUserId,
                ice,
            }) => {
                await request('api/mark-ice', {
                    currentUserId,
                    fromUserId,
                });
                console.log(`Incoming ice ${fromUserId} ${Date.now()}`, { ice });
                const p2p = peerHub.get(fromUserId);
                p2p.setRemoteIce(ice);
            }));
        };

        const tick = async () => {
            await browseForIncomingSdp();
            await browseForIncomingIce();
        };

        do {
            await tick();
            await sleep(5_000);
        } while (true);

        // button.addEventListener('click', tick);

    })(window);
</script>
→ Ссылка