pdo = $pdo; } /* ── room lifecycle ─────────────────────────────────────────────── */ public function createRoom(int $janusRoomId, int $ownerId, string $name, string $purpose = 'screen_cast'): int { $stmt = $this->pdo->prepare( 'INSERT INTO sfu_rooms (janus_room_id, owner_id, name, purpose, created_at, updated_at) VALUES (:jrId, :oid, :name, :purpose, NOW(), NOW())' ); $stmt->execute([ ':jrId' => $janusRoomId, ':oid' => $ownerId, ':name' => $name, ':purpose' => $purpose, ]); return (int) $this->pdo->lastInsertId(); } public function getByJanusId(int $janusRoomId): ?array { $stmt = $this->pdo->prepare('SELECT * FROM sfu_rooms WHERE janus_room_id = :id LIMIT 1'); $stmt->execute([':id' => $janusRoomId]); $row = $stmt->fetch(PDO::FETCH_ASSOC); return $row ?: null; } public function listActive(): array { $stmt = $this->pdo->query( "SELECT r.*, COUNT(p.id) AS participant_count FROM sfu_rooms r LEFT JOIN sfu_participants p ON p.room_id = r.id AND p.left_at IS NULL WHERE r.closed_at IS NULL GROUP BY r.id ORDER BY r.created_at DESC" ); return $stmt->fetchAll(PDO::FETCH_ASSOC); } public function closeRoom(int $roomId): void { $stmt = $this->pdo->prepare('UPDATE sfu_rooms SET closed_at = NOW() WHERE id = :id'); $stmt->execute([':id' => $roomId]); } /* ── participant tracking ───────────────────────────────────────── */ public function joinRoom(int $roomId, int $userId, int $janusHandleId, string $role = 'publisher'): int { $stmt = $this->pdo->prepare( 'INSERT INTO sfu_participants (room_id, user_id, janus_handle_id, role, joined_at) VALUES (:room, :user, :handle, :role, NOW())' ); $stmt->execute([ ':room' => $roomId, ':user' => $userId, ':handle' => $janusHandleId, ':role' => $role, ]); return (int) $this->pdo->lastInsertId(); } public function leaveRoom(int $participantId): void { $stmt = $this->pdo->prepare('UPDATE sfu_participants SET left_at = NOW() WHERE id = :id'); $stmt->execute([':id' => $participantId]); } public function listParticipants(int $roomId): array { $stmt = $this->pdo->prepare( 'SELECT p.*, u.email FROM sfu_participants p JOIN users u ON u.id = p.user_id WHERE p.room_id = :room AND p.left_at IS NULL' ); $stmt->execute([':room' => $roomId]); return $stmt->fetchAll(PDO::FETCH_ASSOC); } /* ── stats hook ─────────────────────────────────────────────────── */ /** Record a bitrate/packet-loss sample for analytics. */ public function recordStats(int $participantId, float $bitrateKbps, float $packetLoss): void { $stmt = $this->pdo->prepare( 'INSERT INTO sfu_stats (participant_id, bitrate_kbps, packet_loss, sampled_at) VALUES (:pid, :bw, :pl, NOW())' ); $stmt->execute([':pid' => $participantId, ':bw' => $bitrateKbps, ':pl' => $packetLoss]); } }