142 lines
4.5 KiB
TypeScript
142 lines
4.5 KiB
TypeScript
import type {
|
|
SfuConsumerDescriptor,
|
|
SfuMediaKind,
|
|
SfuProducerDescriptor,
|
|
SfuSessionDescriptor,
|
|
SfuSessionState,
|
|
SfuTransportDescriptor,
|
|
SfuTransportDirection,
|
|
} from './types';
|
|
|
|
type StoredSfuSession = SfuSessionDescriptor & {
|
|
updatedAt: string;
|
|
};
|
|
|
|
const nowIso = (): string => new Date().toISOString();
|
|
|
|
export class SfuSessionRegistry {
|
|
private readonly sessions = new Map<string, StoredSfuSession>();
|
|
private readonly transports = new Map<string, SfuTransportDescriptor>();
|
|
private readonly producers = new Map<string, SfuProducerDescriptor>();
|
|
private readonly consumers = new Map<string, SfuConsumerDescriptor>();
|
|
|
|
get(streamSessionId: string): SfuSessionDescriptor | null {
|
|
const found = this.sessions.get(streamSessionId);
|
|
if (!found) return null;
|
|
const { updatedAt: _updatedAt, ...descriptor } = found;
|
|
return descriptor;
|
|
}
|
|
|
|
set(session: SfuSessionDescriptor): SfuSessionDescriptor {
|
|
this.sessions.set(session.streamSessionId, { ...session, updatedAt: nowIso() });
|
|
return session;
|
|
}
|
|
|
|
updateState(streamSessionId: string, state: SfuSessionState): SfuSessionDescriptor | null {
|
|
const existing = this.sessions.get(streamSessionId);
|
|
if (!existing) return null;
|
|
|
|
const next: StoredSfuSession = { ...existing, state, updatedAt: nowIso() };
|
|
this.sessions.set(streamSessionId, next);
|
|
const { updatedAt: _updatedAt, ...descriptor } = next;
|
|
return descriptor;
|
|
}
|
|
|
|
list(): SfuSessionDescriptor[] {
|
|
return Array.from(this.sessions.values()).map(({ updatedAt: _updatedAt, ...descriptor }) => descriptor);
|
|
}
|
|
|
|
addTransport(input: {
|
|
transportId: string;
|
|
streamSessionId: string;
|
|
ownerDeviceId: string;
|
|
direction: SfuTransportDirection;
|
|
}): SfuTransportDescriptor {
|
|
const descriptor: SfuTransportDescriptor = {
|
|
transportId: input.transportId,
|
|
streamSessionId: input.streamSessionId,
|
|
ownerDeviceId: input.ownerDeviceId,
|
|
direction: input.direction,
|
|
state: 'new',
|
|
createdAt: nowIso(),
|
|
};
|
|
this.transports.set(descriptor.transportId, descriptor);
|
|
return descriptor;
|
|
}
|
|
|
|
getTransport(transportId: string): SfuTransportDescriptor | null {
|
|
return this.transports.get(transportId) ?? null;
|
|
}
|
|
|
|
listTransports(streamSessionId: string): SfuTransportDescriptor[] {
|
|
return Array.from(this.transports.values()).filter((transport) => transport.streamSessionId === streamSessionId);
|
|
}
|
|
|
|
connectTransport(transportId: string): SfuTransportDescriptor | null {
|
|
const existing = this.transports.get(transportId);
|
|
if (!existing) return null;
|
|
const next: SfuTransportDescriptor = { ...existing, state: 'connected' };
|
|
this.transports.set(transportId, next);
|
|
return next;
|
|
}
|
|
|
|
addProducer(input: {
|
|
producerId: string;
|
|
streamSessionId: string;
|
|
transportId: string;
|
|
cameraDeviceId: string;
|
|
kind: SfuMediaKind;
|
|
rtpParameters: Record<string, unknown>;
|
|
}): SfuProducerDescriptor {
|
|
const descriptor: SfuProducerDescriptor = {
|
|
producerId: input.producerId,
|
|
streamSessionId: input.streamSessionId,
|
|
transportId: input.transportId,
|
|
cameraDeviceId: input.cameraDeviceId,
|
|
kind: input.kind,
|
|
rtpParameters: input.rtpParameters,
|
|
createdAt: nowIso(),
|
|
};
|
|
this.producers.set(descriptor.producerId, descriptor);
|
|
return descriptor;
|
|
}
|
|
|
|
getProducer(producerId: string): SfuProducerDescriptor | null {
|
|
return this.producers.get(producerId) ?? null;
|
|
}
|
|
|
|
listProducers(streamSessionId: string): SfuProducerDescriptor[] {
|
|
return Array.from(this.producers.values())
|
|
.filter((producer) => producer.streamSessionId === streamSessionId)
|
|
.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
|
|
}
|
|
|
|
addConsumer(input: {
|
|
consumerId: string;
|
|
streamSessionId: string;
|
|
transportId: string;
|
|
viewerDeviceId: string;
|
|
producerId: string;
|
|
kind: SfuMediaKind;
|
|
rtpParameters: Record<string, unknown>;
|
|
}): SfuConsumerDescriptor {
|
|
const descriptor: SfuConsumerDescriptor = {
|
|
consumerId: input.consumerId,
|
|
streamSessionId: input.streamSessionId,
|
|
transportId: input.transportId,
|
|
viewerDeviceId: input.viewerDeviceId,
|
|
producerId: input.producerId,
|
|
kind: input.kind,
|
|
rtpParameters: input.rtpParameters,
|
|
createdAt: nowIso(),
|
|
};
|
|
this.consumers.set(descriptor.consumerId, descriptor);
|
|
return descriptor;
|
|
}
|
|
|
|
listConsumers(streamSessionId: string): SfuConsumerDescriptor[] {
|
|
return Array.from(this.consumers.values()).filter((consumer) => consumer.streamSessionId === streamSessionId);
|
|
}
|
|
}
|
|
|