mirror of
git://f0xx.org/ac/ac-ms-sfu-signaling
synced 2026-07-29 08:38:45 +03:00
sfu-signaling: real Janus signaling service implementation
- 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>
This commit is contained in:
109
src/SfuRoomRepository.php
Normal file
109
src/SfuRoomRepository.php
Normal file
@@ -0,0 +1,109 @@
|
||||
<?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]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user