125 lines
4.2 KiB
HTML
125 lines
4.2 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="tr">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<title>MWSE — Video Demo</title>
|
||
<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 .dot { position: absolute; top: 6px; right: 6px; width: 10px; height: 10px;
|
||
border-radius: 50%; background: #f44; }
|
||
.tile.ok .dot { background: #4c4; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div id="header">
|
||
<h2>MWSE Video Demo</h2>
|
||
<span id="status">Bağlanıyor…</span>
|
||
</div>
|
||
<div id="grid"></div>
|
||
|
||
<script type="module">
|
||
import MWSE from '/sdk/index.js';
|
||
|
||
// Akış: bağlan → kamera aç → kendi tile'ı ekle → odaya katıl →
|
||
// her eşle P2P WebRTC bağlantısı kur.
|
||
// Ölçek notu: mesh topolojisi ~6–8 kişiye makul çalışır.
|
||
// Daha büyük odalar için SFU (SRS) gerekir — bkz. #39.
|
||
|
||
const mwse = new MWSE();
|
||
let localStream;
|
||
const tiles = {};
|
||
|
||
mwse.on('scope', async () => {
|
||
setStatus(`Bağlandı: ${mwse.me.socketId}`);
|
||
|
||
try {
|
||
localStream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
|
||
addTile('ben', localStream, true);
|
||
} catch (err) {
|
||
setStatus(`Kamera erişimi reddedildi: ${err.message}`);
|
||
}
|
||
|
||
const room = mwse.room({ name: 'video', joinType: 'free', ifexistsJoin: true });
|
||
await room.createRoom();
|
||
setStatus(`Odada: video | ${mwse.me.socketId.slice(-8)}`);
|
||
|
||
room.on('join', async peer => {
|
||
await peer.requestPair();
|
||
});
|
||
|
||
mwse.me.on('request/pair', async peer => {
|
||
await peer.acceptPair();
|
||
});
|
||
|
||
mwse.me.on('accepted/pair', peer => startVideo(peer));
|
||
mwse.me.on('end/pair', id => removeTile(id));
|
||
});
|
||
|
||
function startVideo(peer) {
|
||
const rtc = peer.rtc;
|
||
const polite = mwse.me.socketId < peer.socketId;
|
||
rtc.connect({ polite });
|
||
|
||
if (localStream) rtc.addStream('cam', localStream);
|
||
|
||
rtc.on('track', (track, streams) => {
|
||
if (track.kind !== 'video') return;
|
||
const stream = streams?.[0] ?? new MediaStream([track]);
|
||
addTile(peer.socketId.slice(-8), stream, false);
|
||
setOk(peer.socketId, true);
|
||
});
|
||
|
||
rtc.on('connected', () => setOk(peer.socketId, true));
|
||
rtc.on('disconnected', () => setOk(peer.socketId, false));
|
||
}
|
||
|
||
function addTile(label, stream, isMe) {
|
||
const key = isMe ? 'me' : label;
|
||
if (tiles[key]) return;
|
||
const div = document.createElement('div');
|
||
div.className = 'tile' + (isMe ? ' ok' : '');
|
||
const video = document.createElement('video');
|
||
video.autoplay = true;
|
||
video.playsInline = true;
|
||
if (isMe) video.muted = true;
|
||
if (stream) video.srcObject = stream;
|
||
const lbl = document.createElement('div');
|
||
lbl.className = 'label';
|
||
lbl.textContent = isMe ? `★ ${label}` : label;
|
||
const dot = document.createElement('div');
|
||
dot.className = 'dot';
|
||
div.append(video, lbl, dot);
|
||
document.getElementById('grid').appendChild(div);
|
||
tiles[key] = div;
|
||
}
|
||
|
||
function setOk(id, ok) {
|
||
const tile = tiles[id.slice(-8)] ?? tiles[id];
|
||
if (tile) tile.classList.toggle('ok', ok);
|
||
}
|
||
|
||
function removeTile(id) {
|
||
const key = (id || '').slice(-8);
|
||
for (const k of [key, id]) {
|
||
if (tiles[k]) { tiles[k].remove(); delete tiles[k]; }
|
||
}
|
||
}
|
||
|
||
function setStatus(msg) {
|
||
document.getElementById('status').textContent = msg;
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|