mirror of
git://f0xx.org/ac/ac-ms-sfu-signaling
synced 2026-07-29 08:38:45 +03:00
- JanusClient.php: HTTP REST client for Janus Gateway API - SfuRoomRepository.php: room + participant + stats persistence - bootstrap.php: PDO, auth, Janus client factory - public/index.php: front-controller routing - public/health.php: Janus online check - public/api/rooms.php: list/create/destroy rooms - public/api/join.php: SDP offer relay → Janus VideoRoom - public/api/stats.php: bitrate/packet-loss analytics ingest - public/dashboard.php: SFU rooms UI (common BE dark-theme styles) Co-authored-by: Cursor <cursoragent@cursor.com>
110 lines
3.9 KiB
PHP
110 lines
3.9 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
namespace AndroidCast\Sfu;
|
|
|
|
use PDO;
|
|
|
|
/**
|
|
* Persistent store for SFU rooms + participant sessions (MariaDB / SQLite).
|
|
* Schema managed by ac-platform-db sql/sfu/*.sql migrations.
|
|
*/
|
|
final class SfuRoomRepository
|
|
{
|
|
private PDO $pdo;
|
|
|
|
public function __construct(PDO $pdo)
|
|
{
|
|
$this->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]);
|
|
}
|
|
}
|