149 lines
5.0 KiB
HTML
149 lines
5.0 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="tr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>MWSE — Video Demo</title>
|
||
<script src="/script"></script>
|
||
<style>
|
||
* { box-sizing: border-box; }
|
||
body { font-family: sans-serif; margin: 0; background: #111; color: #eee; }
|
||
#header { padding: 10px 16px; background: #1a1a1a; display: flex; align-items: center; gap: 12px; }
|
||
#header h2 { margin: 0; font-size: 1.1rem; }
|
||
#status { font-size: .8rem; color: #aaa; }
|
||
#grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
|
||
gap: 6px; padding: 10px;
|
||
}
|
||
.tile {
|
||
background: #222; border-radius: 6px; overflow: hidden; position: relative;
|
||
aspect-ratio: 16/9;
|
||
}
|
||
.tile video { width: 100%; height: 100%; object-fit: cover; background: #000; }
|
||
.tile .label {
|
||
position: absolute; bottom: 6px; left: 6px;
|
||
font-size: .75rem; background: rgba(0,0,0,.6); padding: 2px 6px;
|
||
border-radius: 3px;
|
||
}
|
||
.tile .indicator {
|
||
position: absolute; top: 6px; right: 6px; width: 10px; height: 10px;
|
||
border-radius: 50%; background: #f44;
|
||
}
|
||
.tile.connected .indicator { background: #4c4; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="header">
|
||
<h2>MWSE Video Demo</h2>
|
||
<span id="status">Bağlanıyor…</span>
|
||
</div>
|
||
<div id="grid" id="grid"></div>
|
||
|
||
<script>
|
||
// Akış: MWSE bağlan → kamera/mikrofon aç → kendi videoyu göster →
|
||
// odaya katıl → her yeni eşle P2P WebRTC video kurulumu yap.
|
||
//
|
||
// Ölçek notu: WebRTC mesh topolojisi ~6–8 kişiye kadar makul çalışır.
|
||
// Daha büyük odalar (100–500 kişi) için SRS tabanlı SFU gereklidir (#39).
|
||
// Bu demo o SFU mimarisinin istemci tarafı API kalıplarını gösterir.
|
||
|
||
const mwse = new MWSE({ endpoint: location.origin.replace(/^http/, 'ws') });
|
||
let localStream;
|
||
const tiles = {}; // socketId → { el, rtc }
|
||
|
||
// ---- Kamera/mikrofon ------------------------------------------------
|
||
|
||
mwse.on('scope', async () => {
|
||
status(`Bağlandı: ${mwse.me.socketId}`);
|
||
|
||
try {
|
||
localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
|
||
} catch (err) {
|
||
status(`Kamera erişimi reddedildi: ${err}`);
|
||
addTile('ben (kamera yok)', null, true);
|
||
return;
|
||
}
|
||
|
||
// Kendi videoyu göster (sessiz — aksi hâlde geri besleme oluşur).
|
||
addTile(`ben (${mwse.me.socketId.slice(-6)})`, localStream, true);
|
||
|
||
// Odaya katıl.
|
||
const room = mwse.room({ name: 'video', joinType: 'free', ifexistsJoin: true });
|
||
await room.createRoom();
|
||
status(`Odada: video | ${mwse.me.socketId}`);
|
||
|
||
// Yeni katılan ile bağlantı başlat.
|
||
room.on('join', async peer => {
|
||
setTileState(peer.socketId, false);
|
||
await peer.requestPair();
|
||
});
|
||
|
||
// Otomatik eşleşme kabul.
|
||
mwse.me.on('request/pair', async peer => {
|
||
setTileState(peer.socketId, false);
|
||
await peer.acceptPair();
|
||
});
|
||
|
||
// Eşleme tamam — WebRTC video başlat.
|
||
mwse.me.on('accepted/pair', peer => startVideo(peer));
|
||
|
||
// Eş ayrıldı.
|
||
mwse.me.on('end/pair', id => removeTile(id));
|
||
});
|
||
|
||
// ---- WebRTC video kurulumu -----------------------------------------
|
||
|
||
function startVideo(peer) {
|
||
const rtc = peer.rtc;
|
||
rtc.connect();
|
||
|
||
// Kendi videomuzun tüm izlerini peer'a gönder.
|
||
if (localStream) rtc.sendStream(localStream, 'video', { kind: 'video' });
|
||
|
||
// Uzak video gelince tile'a yerleştir.
|
||
rtc.on('stream:added', ({ stream, name }) => {
|
||
if (name !== 'video') return;
|
||
const label = `${peer.socketId.slice(-6)}`;
|
||
addTile(label, stream, false);
|
||
setTileState(peer.socketId, true);
|
||
});
|
||
|
||
rtc.on('connected', () => setTileState(peer.socketId, true));
|
||
rtc.on('disconnected', () => setTileState(peer.socketId, false));
|
||
}
|
||
|
||
// ---- UI -----------------------------------------------------------
|
||
|
||
function addTile(label, stream, isMe) {
|
||
const id = isMe ? 'me' : label;
|
||
if (tiles[id]) return;
|
||
const div = document.createElement('div');
|
||
div.className = 'tile' + (isMe ? ' connected' : '');
|
||
div.innerHTML = `
|
||
<video autoplay playsinline ${isMe ? 'muted' : ''}></video>
|
||
<div class="label">${label}</div>
|
||
<div class="indicator"></div>`;
|
||
if (stream) div.querySelector('video').srcObject = stream;
|
||
document.getElementById('grid').appendChild(div);
|
||
tiles[id] = div;
|
||
}
|
||
|
||
function setTileState(peerId, connected) {
|
||
const tile = tiles[peerId.slice(-6)] || tiles[peerId];
|
||
if (tile) tile.classList.toggle('connected', connected);
|
||
}
|
||
|
||
function removeTile(peerId) {
|
||
const key = (peerId || '').slice(-6);
|
||
const tile = tiles[key] || tiles[peerId];
|
||
if (tile) { tile.remove(); delete tiles[key]; delete tiles[peerId]; }
|
||
}
|
||
|
||
function status(msg) {
|
||
document.getElementById('status').textContent = msg;
|
||
console.log('[video-demo]', msg);
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|