Revert "feat(streams): add phase-2 SFU transport handshake and produce/consume APIs"
This reverts commit 498a7f838b7747470b220701505c4bfbd3ea8cff.
This commit is contained in:
@@ -40,23 +40,6 @@ const sfuTransportRequestSchema = z.object({
|
||||
role: z.enum(['camera', 'viewer']).optional(),
|
||||
});
|
||||
|
||||
const sfuTransportConnectSchema = z.object({
|
||||
transportId: z.string().min(1),
|
||||
dtlsParameters: z.record(z.string(), z.unknown()).default({}),
|
||||
});
|
||||
|
||||
const sfuProduceSchema = z.object({
|
||||
transportId: z.string().min(1),
|
||||
kind: z.enum(['audio', 'video']).default('video'),
|
||||
rtpParameters: z.record(z.string(), z.unknown()).default({}),
|
||||
});
|
||||
|
||||
const sfuConsumeSchema = z.object({
|
||||
transportId: z.string().min(1),
|
||||
producerId: z.string().min(1).optional(),
|
||||
rtpCapabilities: z.record(z.string(), z.unknown()).optional(),
|
||||
});
|
||||
|
||||
const listSchema = z.object({
|
||||
status: z.string().optional(),
|
||||
limit: z.coerce.number().int().min(1).max(100).default(25),
|
||||
@@ -105,14 +88,6 @@ const getOwnedStreamSession = async (streamSessionId: string, ownerUserId: strin
|
||||
where: and(eq(streamSessions.id, streamSessionId), eq(streamSessions.ownerUserId, ownerUserId)),
|
||||
});
|
||||
|
||||
const ensureSfuEnabled = (res: Parameters<typeof requireDeviceAuth>[1]) => {
|
||||
if (!sfuService) {
|
||||
res.status(409).json({ message: `SFU service disabled (MEDIA_MODE=${mediaMode})` });
|
||||
return null;
|
||||
}
|
||||
return sfuService;
|
||||
};
|
||||
|
||||
router.post('/request', requireDeviceAuth, async (req, res) => {
|
||||
const parsed = requestStreamSchema.safeParse(req.body ?? {});
|
||||
|
||||
@@ -471,8 +446,10 @@ router.get('/:streamSessionId/sfu/session', requireDeviceAuth, async (req, res)
|
||||
const deviceAuth = ensureDeviceAuth(req, res);
|
||||
if (!deviceAuth) return;
|
||||
|
||||
const sfu = ensureSfuEnabled(res);
|
||||
if (!sfu) return;
|
||||
if (!sfuService) {
|
||||
res.status(409).json({ message: `SFU service disabled (MEDIA_MODE=${mediaMode})` });
|
||||
return;
|
||||
}
|
||||
|
||||
const session = await getOwnedStreamSession(parsedParams.data.streamSessionId, deviceAuth.userId);
|
||||
if (!session) {
|
||||
@@ -486,58 +463,11 @@ router.get('/:streamSessionId/sfu/session', requireDeviceAuth, async (req, res)
|
||||
return;
|
||||
}
|
||||
|
||||
const [sfuSession, transports, producers, consumers] = await Promise.all([
|
||||
sfu.getSession(session.id),
|
||||
sfu.listTransports(session.id),
|
||||
sfu.listProducers(session.id),
|
||||
sfu.listConsumers(session.id),
|
||||
]);
|
||||
|
||||
const sfuSession = await sfuService.getSession(session.id);
|
||||
res.json({
|
||||
streamSessionId: session.id,
|
||||
mediaMode,
|
||||
sfuSession,
|
||||
transports,
|
||||
producers,
|
||||
consumers,
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/:streamSessionId/sfu/router-rtp-capabilities', requireDeviceAuth, async (req, res) => {
|
||||
const parsedParams = streamParamSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
res.status(400).json({ message: 'Invalid streamSessionId', errors: parsedParams.error.flatten() });
|
||||
return;
|
||||
}
|
||||
|
||||
const deviceAuth = ensureDeviceAuth(req, res);
|
||||
if (!deviceAuth) return;
|
||||
|
||||
const sfu = ensureSfuEnabled(res);
|
||||
if (!sfu) return;
|
||||
|
||||
const session = await getOwnedStreamSession(parsedParams.data.streamSessionId, deviceAuth.userId);
|
||||
if (!session) {
|
||||
res.status(404).json({ message: 'Stream session not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
const isParticipant = session.requesterDeviceId === deviceAuth.deviceId || session.cameraDeviceId === deviceAuth.deviceId;
|
||||
if (!isParticipant) {
|
||||
res.status(403).json({ message: 'Device cannot access SFU router capabilities for this stream' });
|
||||
return;
|
||||
}
|
||||
|
||||
const routerRtpCapabilities = await sfu.getRouterRtpCapabilities(session.id);
|
||||
if (!routerRtpCapabilities) {
|
||||
res.status(409).json({ message: 'SFU session is not initialized for this stream' });
|
||||
return;
|
||||
}
|
||||
|
||||
res.json({
|
||||
streamSessionId: session.id,
|
||||
mediaMode,
|
||||
routerRtpCapabilities,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -557,8 +487,10 @@ router.post('/:streamSessionId/sfu/publish-transport', requireDeviceAuth, async
|
||||
const deviceAuth = ensureDeviceAuth(req, res);
|
||||
if (!deviceAuth) return;
|
||||
|
||||
const sfu = ensureSfuEnabled(res);
|
||||
if (!sfu) return;
|
||||
if (!sfuService) {
|
||||
res.status(409).json({ message: `SFU service disabled (MEDIA_MODE=${mediaMode})` });
|
||||
return;
|
||||
}
|
||||
|
||||
const session = await getOwnedStreamSession(parsedParams.data.streamSessionId, deviceAuth.userId);
|
||||
if (!session) {
|
||||
@@ -576,11 +508,11 @@ router.post('/:streamSessionId/sfu/publish-transport', requireDeviceAuth, async
|
||||
return;
|
||||
}
|
||||
|
||||
const transport = await sfu.createPublishTransport({
|
||||
const transport = await sfuService.createPublishTransport({
|
||||
streamSessionId: session.id,
|
||||
cameraDeviceId: deviceAuth.deviceId,
|
||||
});
|
||||
await sfu.setSessionState(session.id, 'live');
|
||||
await sfuService.setSessionState(session.id, 'live');
|
||||
|
||||
res.json({
|
||||
streamSessionId: session.id,
|
||||
@@ -605,8 +537,10 @@ router.post('/:streamSessionId/sfu/subscribe-transport', requireDeviceAuth, asyn
|
||||
const deviceAuth = ensureDeviceAuth(req, res);
|
||||
if (!deviceAuth) return;
|
||||
|
||||
const sfu = ensureSfuEnabled(res);
|
||||
if (!sfu) return;
|
||||
if (!sfuService) {
|
||||
res.status(409).json({ message: `SFU service disabled (MEDIA_MODE=${mediaMode})` });
|
||||
return;
|
||||
}
|
||||
|
||||
const session = await getOwnedStreamSession(parsedParams.data.streamSessionId, deviceAuth.userId);
|
||||
if (!session) {
|
||||
@@ -625,11 +559,11 @@ router.post('/:streamSessionId/sfu/subscribe-transport', requireDeviceAuth, asyn
|
||||
return;
|
||||
}
|
||||
|
||||
const transport = await sfu.createSubscribeTransport({
|
||||
const transport = await sfuService.createSubscribeTransport({
|
||||
streamSessionId: session.id,
|
||||
viewerDeviceId: deviceAuth.deviceId,
|
||||
});
|
||||
await sfu.setSessionState(session.id, 'live');
|
||||
await sfuService.setSessionState(session.id, 'live');
|
||||
|
||||
res.json({
|
||||
streamSessionId: session.id,
|
||||
@@ -638,186 +572,6 @@ router.post('/:streamSessionId/sfu/subscribe-transport', requireDeviceAuth, asyn
|
||||
});
|
||||
});
|
||||
|
||||
router.post('/:streamSessionId/sfu/publish-transport/connect', requireDeviceAuth, async (req, res) => {
|
||||
const parsedParams = streamParamSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
res.status(400).json({ message: 'Invalid streamSessionId', errors: parsedParams.error.flatten() });
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedBody = sfuTransportConnectSchema.safeParse(req.body ?? {});
|
||||
if (!parsedBody.success) {
|
||||
res.status(400).json({ message: 'Invalid request body', errors: parsedBody.error.flatten() });
|
||||
return;
|
||||
}
|
||||
|
||||
const deviceAuth = ensureDeviceAuth(req, res);
|
||||
if (!deviceAuth) return;
|
||||
|
||||
const sfu = ensureSfuEnabled(res);
|
||||
if (!sfu) return;
|
||||
|
||||
const session = await getOwnedStreamSession(parsedParams.data.streamSessionId, deviceAuth.userId);
|
||||
if (!session) {
|
||||
res.status(404).json({ message: 'Stream session not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (session.cameraDeviceId !== deviceAuth.deviceId) {
|
||||
res.status(403).json({ message: 'Only camera device can connect publish transport' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const transport = await sfu.connectPublishTransport({
|
||||
streamSessionId: session.id,
|
||||
transportId: parsedBody.data.transportId,
|
||||
deviceId: deviceAuth.deviceId,
|
||||
dtlsParameters: parsedBody.data.dtlsParameters,
|
||||
});
|
||||
await sfu.setSessionState(session.id, 'live');
|
||||
res.json({ streamSessionId: session.id, mediaMode, transport });
|
||||
} catch (error) {
|
||||
res.status(409).json({ message: error instanceof Error ? error.message : 'Unable to connect publish transport' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/:streamSessionId/sfu/subscribe-transport/connect', requireDeviceAuth, async (req, res) => {
|
||||
const parsedParams = streamParamSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
res.status(400).json({ message: 'Invalid streamSessionId', errors: parsedParams.error.flatten() });
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedBody = sfuTransportConnectSchema.safeParse(req.body ?? {});
|
||||
if (!parsedBody.success) {
|
||||
res.status(400).json({ message: 'Invalid request body', errors: parsedBody.error.flatten() });
|
||||
return;
|
||||
}
|
||||
|
||||
const deviceAuth = ensureDeviceAuth(req, res);
|
||||
if (!deviceAuth) return;
|
||||
|
||||
const sfu = ensureSfuEnabled(res);
|
||||
if (!sfu) return;
|
||||
|
||||
const session = await getOwnedStreamSession(parsedParams.data.streamSessionId, deviceAuth.userId);
|
||||
if (!session) {
|
||||
res.status(404).json({ message: 'Stream session not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
const isParticipant = session.requesterDeviceId === deviceAuth.deviceId || session.cameraDeviceId === deviceAuth.deviceId;
|
||||
if (!isParticipant) {
|
||||
res.status(403).json({ message: 'Device cannot connect subscribe transport for this stream' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const transport = await sfu.connectSubscribeTransport({
|
||||
streamSessionId: session.id,
|
||||
transportId: parsedBody.data.transportId,
|
||||
deviceId: deviceAuth.deviceId,
|
||||
dtlsParameters: parsedBody.data.dtlsParameters,
|
||||
});
|
||||
await sfu.setSessionState(session.id, 'live');
|
||||
res.json({ streamSessionId: session.id, mediaMode, transport });
|
||||
} catch (error) {
|
||||
res.status(409).json({ message: error instanceof Error ? error.message : 'Unable to connect subscribe transport' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/:streamSessionId/sfu/produce', requireDeviceAuth, async (req, res) => {
|
||||
const parsedParams = streamParamSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
res.status(400).json({ message: 'Invalid streamSessionId', errors: parsedParams.error.flatten() });
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedBody = sfuProduceSchema.safeParse(req.body ?? {});
|
||||
if (!parsedBody.success) {
|
||||
res.status(400).json({ message: 'Invalid request body', errors: parsedBody.error.flatten() });
|
||||
return;
|
||||
}
|
||||
|
||||
const deviceAuth = ensureDeviceAuth(req, res);
|
||||
if (!deviceAuth) return;
|
||||
|
||||
const sfu = ensureSfuEnabled(res);
|
||||
if (!sfu) return;
|
||||
|
||||
const session = await getOwnedStreamSession(parsedParams.data.streamSessionId, deviceAuth.userId);
|
||||
if (!session) {
|
||||
res.status(404).json({ message: 'Stream session not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
if (session.cameraDeviceId !== deviceAuth.deviceId) {
|
||||
res.status(403).json({ message: 'Only camera device can publish media' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const producer = await sfu.produce({
|
||||
streamSessionId: session.id,
|
||||
transportId: parsedBody.data.transportId,
|
||||
cameraDeviceId: deviceAuth.deviceId,
|
||||
kind: parsedBody.data.kind,
|
||||
rtpParameters: parsedBody.data.rtpParameters,
|
||||
});
|
||||
await sfu.setSessionState(session.id, 'live');
|
||||
res.json({ streamSessionId: session.id, mediaMode, producer });
|
||||
} catch (error) {
|
||||
res.status(409).json({ message: error instanceof Error ? error.message : 'Unable to produce media' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/:streamSessionId/sfu/consume', requireDeviceAuth, async (req, res) => {
|
||||
const parsedParams = streamParamSchema.safeParse(req.params);
|
||||
if (!parsedParams.success) {
|
||||
res.status(400).json({ message: 'Invalid streamSessionId', errors: parsedParams.error.flatten() });
|
||||
return;
|
||||
}
|
||||
|
||||
const parsedBody = sfuConsumeSchema.safeParse(req.body ?? {});
|
||||
if (!parsedBody.success) {
|
||||
res.status(400).json({ message: 'Invalid request body', errors: parsedBody.error.flatten() });
|
||||
return;
|
||||
}
|
||||
|
||||
const deviceAuth = ensureDeviceAuth(req, res);
|
||||
if (!deviceAuth) return;
|
||||
|
||||
const sfu = ensureSfuEnabled(res);
|
||||
if (!sfu) return;
|
||||
|
||||
const session = await getOwnedStreamSession(parsedParams.data.streamSessionId, deviceAuth.userId);
|
||||
if (!session) {
|
||||
res.status(404).json({ message: 'Stream session not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
const isParticipant = session.requesterDeviceId === deviceAuth.deviceId || session.cameraDeviceId === deviceAuth.deviceId;
|
||||
if (!isParticipant) {
|
||||
res.status(403).json({ message: 'Device cannot consume media for this stream' });
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const consumer = await sfu.consume({
|
||||
streamSessionId: session.id,
|
||||
transportId: parsedBody.data.transportId,
|
||||
viewerDeviceId: deviceAuth.deviceId,
|
||||
producerId: parsedBody.data.producerId,
|
||||
rtpCapabilities: parsedBody.data.rtpCapabilities,
|
||||
});
|
||||
await sfu.setSessionState(session.id, 'live');
|
||||
res.json({ streamSessionId: session.id, mediaMode, consumer });
|
||||
} catch (error) {
|
||||
res.status(409).json({ message: error instanceof Error ? error.message : 'Unable to consume media' });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/:streamSessionId/end', requireDeviceAuth, async (req, res) => {
|
||||
const parsedParams = streamParamSchema.safeParse(req.params);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user